add TexturesCache

Signed-off-by: kernelkind <kernelkind@gmail.com>
This commit is contained in:
kernelkind
2025-04-29 10:48:07 -04:00
parent faec75e1b6
commit 7f01f3623d
4 changed files with 188 additions and 9 deletions

View File

@@ -13,18 +13,183 @@ use std::time::{Duration, Instant, SystemTime};
use hex::ToHex; use hex::ToHex;
use sha2::Digest; use sha2::Digest;
use std::path;
use std::path::PathBuf; use std::path::PathBuf;
use std::path::{self};
use tracing::warn; use tracing::warn;
pub type MediaCacheValue = Promise<Result<TexturedImage>>; pub type MediaCacheValue = Promise<Result<TexturedImage>>;
pub type MediaCacheMap = HashMap<String, MediaCacheValue>; pub type MediaCacheMap = HashMap<String, MediaCacheValue>;
#[derive(Default)]
pub struct TexturesCache {
cache: hashbrown::HashMap<String, TextureStateInternal>,
}
impl TexturesCache {
pub fn handle_and_get_or_insert_loadable(
&mut self,
url: &str,
closure: impl FnOnce() -> Promise<Option<Result<TexturedImage>>>,
) -> LoadableTextureState {
let internal = self.handle_and_get_state_internal(url, true, closure);
internal.into()
}
pub fn handle_and_get_or_insert(
&mut self,
url: &str,
closure: impl FnOnce() -> Promise<Option<Result<TexturedImage>>>,
) -> TextureState {
let internal = self.handle_and_get_state_internal(url, false, closure);
internal.into()
}
fn handle_and_get_state_internal(
&mut self,
url: &str,
use_loading: bool,
closure: impl FnOnce() -> Promise<Option<Result<TexturedImage>>>,
) -> &mut TextureStateInternal {
let state = match self.cache.raw_entry_mut().from_key(url) {
hashbrown::hash_map::RawEntryMut::Occupied(entry) => {
let state = entry.into_mut();
handle_occupied(state, use_loading);
state
}
hashbrown::hash_map::RawEntryMut::Vacant(entry) => {
let res = closure();
let (_, state) = entry.insert(url.to_owned(), TextureStateInternal::Pending(res));
state
}
};
state
}
pub fn insert_pending(&mut self, url: &str, promise: Promise<Option<Result<TexturedImage>>>) {
self.cache
.insert(url.to_owned(), TextureStateInternal::Pending(promise));
}
pub fn move_to_loaded(&mut self, url: &str) {
let hashbrown::hash_map::RawEntryMut::Occupied(entry) =
self.cache.raw_entry_mut().from_key(url)
else {
return;
};
entry.replace_entry_with(|_, v| {
let TextureStateInternal::Loading(textured) = v else {
return None;
};
Some(TextureStateInternal::Loaded(textured))
});
}
pub fn get_and_handle(&mut self, url: &str) -> Option<LoadableTextureState> {
self.cache.get_mut(url).map(|state| {
handle_occupied(state, true);
state.into()
})
}
}
fn handle_occupied(state: &mut TextureStateInternal, use_loading: bool) {
let TextureStateInternal::Pending(promise) = state else {
return;
};
let Some(res) = promise.ready_mut() else {
return;
};
let Some(res) = res.take() else {
tracing::error!("Failed to take the promise");
*state =
TextureStateInternal::Error(crate::Error::Generic("Promise already taken".to_owned()));
return;
};
match res {
Ok(textured) => {
*state = if use_loading {
TextureStateInternal::Loading(textured)
} else {
TextureStateInternal::Loaded(textured)
}
}
Err(e) => *state = TextureStateInternal::Error(e),
}
}
pub enum LoadableTextureState<'a> {
Pending,
Error(&'a crate::Error),
Loading {
actual_image_tex: &'a mut TexturedImage,
}, // the texture is in the loading state, for transitioning between the pending and loaded states
Loaded(&'a mut TexturedImage),
}
pub enum TextureState<'a> {
Pending,
Error(&'a crate::Error),
Loaded(&'a mut TexturedImage),
}
impl<'a> From<&'a mut TextureStateInternal> for TextureState<'a> {
fn from(value: &'a mut TextureStateInternal) -> Self {
match value {
TextureStateInternal::Pending(_) => TextureState::Pending,
TextureStateInternal::Error(error) => TextureState::Error(error),
TextureStateInternal::Loading(textured_image) => TextureState::Loaded(textured_image),
TextureStateInternal::Loaded(textured_image) => TextureState::Loaded(textured_image),
}
}
}
pub enum TextureStateInternal {
Pending(Promise<Option<Result<TexturedImage>>>),
Error(crate::Error),
Loading(TexturedImage), // the image is in the loading state, for transitioning between blur and image
Loaded(TexturedImage),
}
impl<'a> From<&'a mut TextureStateInternal> for LoadableTextureState<'a> {
fn from(value: &'a mut TextureStateInternal) -> Self {
match value {
TextureStateInternal::Pending(_) => LoadableTextureState::Pending,
TextureStateInternal::Error(error) => LoadableTextureState::Error(error),
TextureStateInternal::Loading(textured_image) => LoadableTextureState::Loading {
actual_image_tex: textured_image,
},
TextureStateInternal::Loaded(textured_image) => {
LoadableTextureState::Loaded(textured_image)
}
}
}
}
pub enum TexturedImage { pub enum TexturedImage {
Static(TextureHandle), Static(TextureHandle),
Animated(Animation), Animated(Animation),
} }
impl TexturedImage {
pub fn get_first_texture(&self) -> &TextureHandle {
match self {
TexturedImage::Static(texture_handle) => texture_handle,
TexturedImage::Animated(animation) => &animation.first_frame.texture,
}
}
}
pub struct Animation { pub struct Animation {
pub first_frame: TextureFrame, pub first_frame: TextureFrame,
pub other_frames: Vec<TextureFrame>, pub other_frames: Vec<TextureFrame>,
@@ -60,7 +225,7 @@ pub struct MediaCache {
url_imgs: MediaCacheMap, url_imgs: MediaCacheMap,
} }
#[derive(Clone)] #[derive(Debug, Eq, PartialEq, Clone, Copy)]
pub enum MediaCacheType { pub enum MediaCacheType {
Image, Image,
Gif, Gif,
@@ -215,6 +380,20 @@ impl Images {
self.static_imgs.migrate_v0()?; self.static_imgs.migrate_v0()?;
self.gifs.migrate_v0() self.gifs.migrate_v0()
} }
pub fn get_cache(&self, cache_type: MediaCacheType) -> &MediaCache {
match cache_type {
MediaCacheType::Image => &self.static_imgs,
MediaCacheType::Gif => &self.gifs,
}
}
pub fn get_cache_mut(&mut self, cache_type: MediaCacheType) -> &mut MediaCache {
match cache_type {
MediaCacheType::Image => &mut self.static_imgs,
MediaCacheType::Gif => &mut self.gifs,
}
}
} }
pub type GifStateMap = HashMap<String, GifState>; pub type GifStateMap = HashMap<String, GifState>;

View File

@@ -41,8 +41,8 @@ pub use error::{Error, FilterError, ZapError};
pub use filter::{FilterState, FilterStates, UnifiedSubscription}; pub use filter::{FilterState, FilterStates, UnifiedSubscription};
pub use fonts::NamedFontFamily; pub use fonts::NamedFontFamily;
pub use imgcache::{ pub use imgcache::{
Animation, GifState, GifStateMap, ImageFrame, Images, MediaCache, MediaCacheType, Animation, GifState, GifStateMap, ImageFrame, Images, LoadableTextureState, MediaCache,
MediaCacheValue, TextureFrame, TexturedImage, MediaCacheType, TextureFrame, TextureState, TexturedImage, TexturesCache,
}; };
pub use job_pool::JobPool; pub use job_pool::JobPool;
pub use muted::{MuteFun, Muted}; pub use muted::{MuteFun, Muted};

View File

@@ -468,7 +468,7 @@ fn render_media_cache(
let m_cached_promise = cache.map().get(url); let m_cached_promise = cache.map().get(url);
if m_cached_promise.is_none() { if m_cached_promise.is_none() {
let res = crate::images::fetch_img(cache, ui.ctx(), url, img_type, cache_type.clone()); let res = crate::images::fetch_img(cache, ui.ctx(), url, img_type, cache_type);
cache.map_mut().insert(url.to_owned(), res); cache.map_mut().insert(url.to_owned(), res);
} }

View File

@@ -318,7 +318,7 @@ fn image_carousel(
ui.ctx().memory(|mem| { ui.ctx().memory(|mem| {
mem.data mem.data
.get_temp::<(String, MediaCacheType)>(carousel_id.with("current_image")) .get_temp::<(String, MediaCacheType)>(carousel_id.with("current_image"))
.unwrap_or_else(|| (images[0].0.clone(), images[0].1.clone())) .unwrap_or_else(|| (images[0].0.clone(), images[0].1))
}) })
}); });
@@ -333,7 +333,7 @@ fn image_carousel(
img_cache, img_cache,
&image, &image,
ImageType::Content, ImageType::Content,
cache_type.clone(), cache_type,
|ui| { |ui| {
ui.allocate_space(egui::vec2(spinsz, spinsz)); ui.allocate_space(egui::vec2(spinsz, spinsz));
}, },
@@ -360,7 +360,7 @@ fn image_carousel(
mem.data.insert_temp(carousel_id.with("show_popup"), true); mem.data.insert_temp(carousel_id.with("show_popup"), true);
mem.data.insert_temp( mem.data.insert_temp(
carousel_id.with("current_image"), carousel_id.with("current_image"),
(image.clone(), cache_type.clone()), (image.clone(), cache_type),
); );
}); });
} }
@@ -438,7 +438,7 @@ fn image_carousel(
img_cache, img_cache,
&image, &image,
ImageType::Content, ImageType::Content,
cache_type.clone(), cache_type,
|ui| { |ui| {
ui.allocate_space(egui::vec2(spinsz, spinsz)); ui.allocate_space(egui::vec2(spinsz, spinsz));
}, },