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}