pub mod muda;
pub mod tray_icon;
use std::hash::{Hash, Hasher};
use std::sync::{Arc, Mutex, OnceLock};
const ENCODE_CACHE_CAP: usize = 32;
#[allow(clippy::type_complexity)]
fn encode_cache() -> &'static Mutex<Vec<(u32, u32, u64, Arc<[u8]>)>> {
static CACHE: OnceLock<Mutex<Vec<(u32, u32, u64, Arc<[u8]>)>>> = OnceLock::new();
CACHE.get_or_init(|| Mutex::new(Vec::new()))
}
pub(crate) fn encode_rgba_cached(rgba: &[u8], width: u32, height: u32) -> Option<Arc<[u8]>> {
let key = {
let mut hasher = std::collections::hash_map::DefaultHasher::new();
rgba.hash(&mut hasher);
hasher.finish()
};
let Ok(mut cache) = encode_cache().lock() else {
return crate::render::encode_rgba_png(rgba, width, height).map(Arc::from);
};
if let Some((_, _, _, arc)) = cache
.iter()
.find(|(w, h, k, _)| *w == width && *h == height && *k == key)
{
return Some(Arc::clone(arc));
}
let arc: Arc<[u8]> = crate::render::encode_rgba_png(rgba, width, height)?.into();
if cache.len() >= ENCODE_CACHE_CAP {
cache.remove(0); }
cache.push((width, height, key, Arc::clone(&arc)));
Some(arc)
}
#[cfg(test)]
mod encode_cache_tests {
use super::*;
#[test]
fn identical_rgba_returns_the_same_arc() {
let rgba = vec![1, 2, 3, 255, 4, 5, 6, 128];
let a = encode_rgba_cached(&rgba, 2, 1).expect("encodes");
let b = encode_rgba_cached(&rgba.clone(), 2, 1).expect("encodes again");
assert!(
Arc::ptr_eq(&a, &b),
"identical RGBA must reuse the cached Arc"
);
let (decoded, w, h) = crate::render::decode_png(&a).expect("decodes");
assert_eq!((w, h), (2, 1));
assert_eq!(decoded, rgba);
}
#[test]
fn zero_dimension_returns_none() {
assert!(encode_rgba_cached(&[], 0, 1).is_none());
}
}