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)]
 36pub(crate) struct RenderImageParams {
 37    pub(crate) image_id: ImageId,
 38    pub(crate) frame_index: usize,
 39}
 40
 41/// A cached and processed image, in BGRA format
 42pub struct RenderImage {
 43    /// The ID associated with this image
 44    pub id: ImageId,
 45    /// The scale factor of this image on render.
 46    pub(crate) scale_factor: f32,
 47    data: SmallVec<[Frame; 1]>,
 48}
 49
 50impl PartialEq for RenderImage {
 51    fn eq(&self, other: &Self) -> bool {
 52        self.id == other.id
 53    }
 54}
 55
 56impl Eq for RenderImage {}
 57
 58impl RenderImage {
 59    /// Create a new image from the given data.
 60    pub fn new(data: impl Into<SmallVec<[Frame; 1]>>) -> Self {
 61        static NEXT_ID: AtomicUsize = AtomicUsize::new(0);
 62
 63        Self {
 64            id: ImageId(NEXT_ID.fetch_add(1, SeqCst)),
 65            scale_factor: 1.0,
 66            data: data.into(),
 67        }
 68    }
 69
 70    /// Convert this image into a byte slice.
 71    pub fn as_bytes(&self, frame_index: usize) -> Option<&[u8]> {
 72        self.data
 73            .get(frame_index)
 74            .map(|frame| frame.buffer().as_raw().as_slice())
 75    }
 76
 77    /// Get the size of this image, in pixels.
 78    pub fn size(&self, frame_index: usize) -> Size<DevicePixels> {
 79        let (width, height) = self.data[frame_index].buffer().dimensions();
 80        size(width.into(), height.into())
 81    }
 82
 83    /// Get the size of this image, in pixels for display, adjusted for the scale factor.
 84    pub(crate) fn render_size(&self, frame_index: usize) -> Size<Pixels> {
 85        self.size(frame_index)
 86            .map(|v| (v.0 as f32 / self.scale_factor).into())
 87    }
 88
 89    /// Get the delay of this frame from the previous
 90    pub fn delay(&self, frame_index: usize) -> Delay {
 91        self.data[frame_index].delay()
 92    }
 93
 94    /// Get the number of frames for this image.
 95    pub fn frame_count(&self) -> usize {
 96        self.data.len()
 97    }
 98}
 99
100impl fmt::Debug for RenderImage {
101    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
102        f.debug_struct("ImageData")
103            .field("id", &self.id)
104            .field("size", &self.size(0))
105            .finish()
106    }
107}