Skip to main content

gpui_authoring/
assets.rs

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