1use crate::{size, DevicePixels, Result, SharedString, Size};
2use anyhow::anyhow;
3use image::{Bgra, ImageBuffer};
4use std::{
5 borrow::Cow,
6 fmt,
7 hash::Hash,
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<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<Cow<'static, [u8]>> {
22 Err(anyhow!(
23 "get called on empty asset provider with \"{}\"",
24 path
25 ))
26 }
27
28 fn list(&self, _path: &str) -> Result<Vec<SharedString>> {
29 Ok(vec![])
30 }
31}
32
33/// A unique identifier for the image cache
34#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
35pub struct ImageId(usize);
36
37/// A cached and processed image.
38pub struct ImageData {
39 /// The ID associated with this image
40 pub id: ImageId,
41 data: ImageBuffer<Bgra<u8>, Vec<u8>>,
42}
43
44impl ImageData {
45 /// Create a new image from the given data.
46 pub fn new(data: ImageBuffer<Bgra<u8>, Vec<u8>>) -> Self {
47 static NEXT_ID: AtomicUsize = AtomicUsize::new(0);
48
49 Self {
50 id: ImageId(NEXT_ID.fetch_add(1, SeqCst)),
51 data,
52 }
53 }
54
55 /// Convert this image into a byte slice.
56 pub fn as_bytes(&self) -> &[u8] {
57 &self.data
58 }
59
60 /// Get the size of this image, in pixels
61 pub fn size(&self) -> Size<DevicePixels> {
62 let (width, height) = self.data.dimensions();
63 size(width.into(), height.into())
64 }
65}
66
67impl fmt::Debug for ImageData {
68 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
69 f.debug_struct("ImageData")
70 .field("id", &self.id)
71 .field("size", &self.data.dimensions())
72 .finish()
73 }
74}