assets.rs

  1use crate::{DevicePixels, Pixels, Result, SharedString, Size, size};
  2use smallvec::SmallVec;
  3
  4use image::{Delay, Frame};
  5use std::{
  6    borrow::Cow,
  7    fmt,
  8    hash::Hash,
  9    sync::atomic::{AtomicUsize, Ordering::SeqCst},
 10};
 11
 12/// A source of assets for this app to use.
 13pub trait AssetSource: 'static + Send + Sync {
 14    /// Load the given asset from the source path.
 15    fn load(&self, path: &str) -> Result<Option<Cow<'static, [u8]>>>;
 16
 17    /// List the assets at the given path.
 18    fn list(&self, path: &str) -> Result<Vec<SharedString>>;
 19}
 20
 21impl AssetSource for () {
 22    fn load(&self, _path: &str) -> Result<Option<Cow<'static, [u8]>>> {
 23        Ok(None)
 24    }
 25
 26    fn list(&self, _path: &str) -> Result<Vec<SharedString>> {
 27        Ok(vec![])
 28    }
 29}
 30
 31/// A unique identifier for the image cache
 32#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
 33pub struct ImageId(pub usize);
 34
 35#[derive(PartialEq, Eq, Hash, Clone)]
 36#[expect(missing_docs)]
 37pub struct RenderImageParams {
 38    pub image_id: ImageId,
 39    pub frame_index: usize,
 40}
 41
 42/// A cached and processed image, in BGRA format
 43pub struct RenderImage {
 44    /// The ID associated with this image
 45    pub id: ImageId,
 46    /// The scale factor of this image on render.
 47    pub(crate) scale_factor: f32,
 48    data: SmallVec<[Frame; 1]>,
 49}
 50
 51impl PartialEq for RenderImage {
 52    fn eq(&self, other: &Self) -> bool {
 53        self.id == other.id
 54    }
 55}
 56
 57impl Eq for RenderImage {}
 58
 59impl RenderImage {
 60    /// Create a new image from the given data.
 61    pub fn new(data: impl Into<SmallVec<[Frame; 1]>>) -> Self {
 62        static NEXT_ID: AtomicUsize = AtomicUsize::new(0);
 63
 64        Self {
 65            id: ImageId(NEXT_ID.fetch_add(1, SeqCst)),
 66            scale_factor: 1.0,
 67            data: data.into(),
 68        }
 69    }
 70
 71    /// Convert this image into a byte slice.
 72    pub fn as_bytes(&self, frame_index: usize) -> Option<&[u8]> {
 73        self.data
 74            .get(frame_index)
 75            .map(|frame| frame.buffer().as_raw().as_slice())
 76    }
 77
 78    /// Get the size of this image, in pixels.
 79    pub fn size(&self, frame_index: usize) -> Size<DevicePixels> {
 80        let (width, height) = self.data[frame_index].buffer().dimensions();
 81        size(width.into(), height.into())
 82    }
 83
 84    /// Get the size of this image, in pixels for display, adjusted for the scale factor.
 85    pub(crate) fn render_size(&self, frame_index: usize) -> Size<Pixels> {
 86        self.size(frame_index)
 87            .map(|v| (v.0 as f32 / self.scale_factor).into())
 88    }
 89
 90    /// Get the delay of this frame from the previous
 91    pub fn delay(&self, frame_index: usize) -> Delay {
 92        self.data[frame_index].delay()
 93    }
 94
 95    /// Get the number of frames for this image.
 96    pub fn frame_count(&self) -> usize {
 97        self.data.len()
 98    }
 99}
100
101impl fmt::Debug for RenderImage {
102    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
103        f.debug_struct("ImageData")
104            .field("id", &self.id)
105            .field("size", &self.size(0))
106            .finish()
107    }
108}