133 lines · 3.7 KB · Rust
RawDownload
use crate::{DevicePixels, Pixels, Result, SharedString, Size, size};use smallvec::SmallVec; use image::{Delay, Frame};use std::{    borrow::Cow,    fmt,    hash::Hash,    sync::atomic::{AtomicUsize, Ordering::SeqCst},}; /// A source of assets for this app to use.pub trait AssetSource: 'static + Send + Sync {    /// Load the given asset from the source path.    fn load(&self, path: &str) -> Result<Option<Cow<'static, [u8]>>>;     /// List the assets at the given path.    fn list(&self, path: &str) -> Result<Vec<SharedString>>;} impl AssetSource for () {    fn load(&self, _path: &str) -> Result<Option<Cow<'static, [u8]>>> {        Ok(None)    }     fn list(&self, _path: &str) -> Result<Vec<SharedString>> {        Ok(vec![])    }} /// A unique identifier for the image cache#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]pub struct ImageId(pub usize); #[derive(PartialEq, Eq, Hash, Clone)]#[expect(missing_docs)]pub struct RenderImageParams {    pub image_id: ImageId,    pub frame_index: usize,} /// A cached and processed image, in BGRA formatpub struct RenderImage {    /// The ID associated with this image    pub id: ImageId,    /// The scale factor of this image on render.    pub(crate) scale_factor: f32,    data: SmallVec<[Frame; 1]>,} impl PartialEq for RenderImage {    fn eq(&self, other: &Self) -> bool {        self.id == other.id    }} impl Eq for RenderImage {} impl RenderImage {    /// Create a new image from the given data.    pub fn new(data: impl Into<SmallVec<[Frame; 1]>>) -> Self {        static NEXT_ID: AtomicUsize = AtomicUsize::new(0);         Self {            id: ImageId(NEXT_ID.fetch_add(1, SeqCst)),            scale_factor: 1.0,            data: data.into(),        }    }     /// Convert this image into a byte slice.    pub fn as_bytes(&self, frame_index: usize) -> Option<&[u8]> {        self.data            .get(frame_index)            .map(|frame| frame.buffer().as_raw().as_slice())    }     /// Get the size of this image, in pixels.    pub fn size(&self, frame_index: usize) -> Size<DevicePixels> {        self.data            .get(frame_index)            .map(|frame| {                let (width, height) = frame.buffer().dimensions();                size(width.into(), height.into())            })            .unwrap_or_default()    }     /// Get the size of this image, in pixels for display, adjusted for the scale factor.    pub(crate) fn render_size(&self, frame_index: usize) -> Size<Pixels> {        self.size(frame_index)            .map(|v| (v.0 as f32 / self.scale_factor).into())    }     /// Get the delay of this frame from the previous    pub fn delay(&self, frame_index: usize) -> Delay {        self.data            .get(frame_index)            .map(|frame| frame.delay())            .unwrap_or(Delay::from_numer_denom_ms(100, 1))    }     /// Get the number of frames for this image.    pub fn frame_count(&self) -> usize {        self.data.len()    }} impl fmt::Debug for RenderImage {    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {        f.debug_struct("ImageData")            .field("id", &self.id)            .field("size", &self.data.first().map(|f| f.buffer().dimensions()))            .finish()    }} #[cfg(test)]mod tests {    use super::*;    use smallvec::SmallVec;     #[test]    fn empty_render_image_does_not_panic() {        let image = RenderImage::new(SmallVec::new());        assert_eq!(image.frame_count(), 0);        assert_eq!(image.size(0), Size::default());        assert_eq!(image.as_bytes(0), None);        assert_eq!(image.render_size(0), Size::default());        assert_eq!(image.delay(0), Delay::from_numer_denom_ms(100, 1));        let _ = format!("{image:?}");    }}