use std::borrow::Cow;
use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::{Arc, Mutex, PoisonError, Weak};
use crate::media::{ImageData, ImageMetadata};
static PIXEL: [u8; 4] = [0; 4];
pub(crate) struct Markers<T> {
prefix: &'static str,
next: AtomicU64,
entries: Mutex<Vec<(Weak<usvgr::PreloadedImageData>, Arc<T>)>>,
}
impl<T> Markers<T> {
pub(crate) const fn new(prefix: &'static str) -> Self {
Self {
prefix,
next: AtomicU64::new(0),
entries: Mutex::new(Vec::new()),
}
}
pub(crate) fn create(&self, value: T, width: u32, height: u32) -> ImageData<'static> {
let image = self.image();
let mut entries = self.entries.lock().unwrap_or_else(PoisonError::into_inner);
entries.retain(|(image, _)| image.strong_count() > 0);
entries.push((Arc::downgrade(&image), Arc::new(value)));
Self::image_data(image, width, height)
}
#[cfg(feature = "wgpu")]
pub(crate) fn empty(&self, width: u32, height: u32) -> ImageData<'static> {
Self::image_data(self.image(), width, height)
}
pub(crate) fn resolve(&self, image: &Arc<usvgr::PreloadedImageData>) -> Option<Arc<T>> {
if !image.id.starts_with(self.prefix) {
return None;
}
let entries = self.entries.lock().unwrap_or_else(PoisonError::into_inner);
entries
.iter()
.find(|(weak, _)| weak.as_ptr() == Arc::as_ptr(image))
.map(|(_, value)| Arc::clone(value))
}
#[cfg(feature = "wgpu")]
pub(crate) fn retain(&self, keep: impl Fn(&T) -> bool) {
let mut entries = self.entries.lock().unwrap_or_else(PoisonError::into_inner);
entries.retain(|(_, value)| keep(value));
}
fn image(&self) -> Arc<usvgr::PreloadedImageData> {
Arc::new(usvgr::PreloadedImageData {
data: Cow::Borrowed(&PIXEL),
width: 1,
height: 1,
id: format!(
"{}{}",
self.prefix,
self.next.fetch_add(1, Ordering::Relaxed)
),
})
}
fn image_data(
image: Arc<usvgr::PreloadedImageData>,
width: u32,
height: u32,
) -> ImageData<'static> {
let id = image.id.clone();
ImageData::new_from_raw_data(image, id, ImageMetadata { width, height })
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn markers_resolve_while_their_image_lives() {
static MARKERS: Markers<u32> = Markers::new("test:");
let marker = MARKERS.create(7, 4, 4);
let image = marker.href();
assert!(image.id.starts_with("test:"));
assert_eq!(MARKERS.resolve(&image).as_deref(), Some(&7));
let plain = Arc::new(usvgr::PreloadedImageData {
data: Cow::Owned(vec![255; 4]),
width: 1,
height: 1,
id: "photo.png".to_owned(),
});
assert!(MARKERS.resolve(&plain).is_none());
drop(marker);
drop(image);
let _next = MARKERS.create(8, 1, 1);
assert_eq!(MARKERS.entries.lock().unwrap().len(), 1);
}
}