@@ -13,18 +13,183 @@ use std::time::{Duration, Instant, SystemTime};
|
||||
|
||||
use hex::ToHex;
|
||||
use sha2::Digest;
|
||||
use std::path;
|
||||
use std::path::PathBuf;
|
||||
use std::path::{self};
|
||||
use tracing::warn;
|
||||
|
||||
pub type MediaCacheValue = Promise<Result<TexturedImage>>;
|
||||
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 {
|
||||
Static(TextureHandle),
|
||||
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 first_frame: TextureFrame,
|
||||
pub other_frames: Vec<TextureFrame>,
|
||||
@@ -60,7 +225,7 @@ pub struct MediaCache {
|
||||
url_imgs: MediaCacheMap,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
#[derive(Debug, Eq, PartialEq, Clone, Copy)]
|
||||
pub enum MediaCacheType {
|
||||
Image,
|
||||
Gif,
|
||||
@@ -215,6 +380,20 @@ impl Images {
|
||||
self.static_imgs.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>;
|
||||
|
||||
@@ -41,8 +41,8 @@ pub use error::{Error, FilterError, ZapError};
|
||||
pub use filter::{FilterState, FilterStates, UnifiedSubscription};
|
||||
pub use fonts::NamedFontFamily;
|
||||
pub use imgcache::{
|
||||
Animation, GifState, GifStateMap, ImageFrame, Images, MediaCache, MediaCacheType,
|
||||
MediaCacheValue, TextureFrame, TexturedImage,
|
||||
Animation, GifState, GifStateMap, ImageFrame, Images, LoadableTextureState, MediaCache,
|
||||
MediaCacheType, TextureFrame, TextureState, TexturedImage, TexturesCache,
|
||||
};
|
||||
pub use job_pool::JobPool;
|
||||
pub use muted::{MuteFun, Muted};
|
||||
|
||||
Reference in New Issue
Block a user