assets.rs

 1use anyhow::{anyhow, Result};
 2use image::ImageFormat;
 3use std::{borrow::Cow, cell::RefCell, collections::HashMap, sync::Arc};
 4
 5use crate::ImageData;
 6
 7pub trait AssetSource: 'static + Send + Sync {
 8    fn load(&self, path: &str) -> Result<Cow<[u8]>>;
 9    fn list(&self, path: &str) -> Vec<Cow<'static, str>>;
10}
11
12impl AssetSource for () {
13    fn load(&self, path: &str) -> Result<Cow<[u8]>> {
14        Err(anyhow!(
15            "get called on empty asset provider with \"{}\"",
16            path
17        ))
18    }
19
20    fn list(&self, _: &str) -> Vec<Cow<'static, str>> {
21        vec![]
22    }
23}
24
25pub struct AssetCache {
26    source: Box<dyn AssetSource>,
27    svgs: RefCell<HashMap<String, usvg::Tree>>,
28    pngs: RefCell<HashMap<String, Arc<ImageData>>>,
29}
30
31impl AssetCache {
32    pub fn new(source: impl AssetSource) -> Self {
33        Self {
34            source: Box::new(source),
35            svgs: RefCell::new(HashMap::new()),
36            pngs: RefCell::new(HashMap::new()),
37        }
38    }
39
40    pub fn svg(&self, path: &str) -> Result<usvg::Tree> {
41        let mut svgs = self.svgs.borrow_mut();
42        if let Some(svg) = svgs.get(path) {
43            Ok(svg.clone())
44        } else {
45            let bytes = self.source.load(path)?;
46            let svg = usvg::Tree::from_data(&bytes, &usvg::Options::default())?;
47            svgs.insert(path.to_string(), svg.clone());
48            Ok(svg)
49        }
50    }
51
52    pub fn png(&self, path: &str) -> Result<Arc<ImageData>> {
53        let mut pngs = self.pngs.borrow_mut();
54        if let Some(png) = pngs.get(path) {
55            Ok(png.clone())
56        } else {
57            let bytes = self.source.load(path)?;
58            let image = ImageData::new(
59                image::load_from_memory_with_format(&bytes, ImageFormat::Png)?.into_bgra8(),
60            );
61            pngs.insert(path.to_string(), image.clone());
62            Ok(image)
63        }
64    }
65}