Skip to main content

firefly_rust/graphics/
image.rs

1use crate::*;
2
3/// Owned version of [`Image`].
4///
5/// Can be loaded as [`FileBuf`] from ROM with [`load_file_buf`]
6/// and then cast using [`Into::into`].
7#[cfg(feature = "alloc")]
8pub struct ImageBuf {
9    pub(crate) raw: alloc::boxed::Box<[u8]>,
10}
11
12#[cfg(feature = "alloc")]
13impl Image for ImageBuf {
14    unsafe fn as_bytes(&self) -> &[u8] {
15        &self.raw
16    }
17}
18
19#[cfg(feature = "alloc")]
20impl From<FileBuf> for ImageBuf {
21    fn from(value: FileBuf) -> Self {
22        Self { raw: value.raw }
23    }
24}
25
26#[cfg(feature = "alloc")]
27impl From<CanvasBuf> for ImageBuf {
28    fn from(value: CanvasBuf) -> Self {
29        Self { raw: value.raw }
30    }
31}
32
33/// A loaded image file.
34///
35/// Can be loaded as [`FileRef`] from ROM with [`load_file`]
36/// and then cast using [`Into`].
37pub struct ImageRef<'a> {
38    raw: &'a [u8],
39}
40
41impl<'a> ImageRef<'a> {
42    /// Reinterpret raw bytes as an image.
43    ///
44    /// # Safety
45    ///
46    /// Using this function requires a good understanding of the internal
47    /// Firefly Zero binary image format. In 99% cases, you should not construct
48    /// a raw image but instead load it from a [`File`] or generate using [`Canvas`].
49    #[must_use]
50    pub unsafe fn from_bytes(raw: &'a [u8]) -> Self {
51        Self { raw }
52    }
53}
54
55impl Image for ImageRef<'_> {
56    unsafe fn as_bytes(&self) -> &[u8] {
57        self.raw
58    }
59}
60
61impl<'a> From<FileRef<'a>> for ImageRef<'a> {
62    fn from(value: FileRef<'a>) -> Self {
63        Self { raw: value.raw }
64    }
65}
66
67impl<'a> From<CanvasRef<'a>> for ImageRef<'a> {
68    fn from(value: CanvasRef<'a>) -> Self {
69        Self { raw: value.raw }
70    }
71}
72
73/// A loaded image file.
74pub trait Image {
75    /// Get the raw image representation.
76    ///
77    /// # Safety
78    ///
79    /// Don't use it. The internal image representation might change
80    /// in the future and so should not be relied upon.
81    unsafe fn as_bytes(&self) -> &[u8];
82
83    /// Get a rectangle subregion of the image.
84    #[must_use]
85    fn sub(&self, p: Point, s: Size) -> SubImage<'_> {
86        let raw = unsafe { self.as_bytes() };
87        SubImage {
88            point: p,
89            size: s,
90            raw,
91        }
92    }
93
94    /// The color used for transparency. If no transparency, returns [`Color::None`].
95    #[must_use]
96    fn transparency(&self) -> Color {
97        let raw = unsafe { self.as_bytes() };
98        Color::from(raw[3] + 1)
99    }
100
101    /// The number of pixels the image has.
102    #[must_use]
103    fn pixels(&self) -> usize {
104        const HEADER_SIZE: usize = 4;
105        const PPB: usize = 2;
106        let raw = unsafe { self.as_bytes() };
107        (raw.len() - HEADER_SIZE) * PPB
108    }
109
110    /// The image width in pixels.
111    #[must_use]
112    fn width(&self) -> u16 {
113        let raw = unsafe { self.as_bytes() };
114        let big = u16::from(raw[1]);
115        let little = u16::from(raw[2]);
116        big | (little << 8)
117    }
118
119    /// The image height in pixels.
120    #[must_use]
121    fn height(&self) -> u16 {
122        let p = self.pixels();
123        let w = usize::from(self.width());
124        p.checked_div(w).unwrap_or(0) as u16
125    }
126
127    /// The image size in pixels.
128    #[must_use]
129    fn size(&self) -> Size {
130        Size {
131            width: i32::from(self.width()),
132            height: i32::from(self.height()),
133        }
134    }
135}
136
137/// A subregion of an image. Constructed using [`Image::sub`].
138pub struct SubImage<'a> {
139    pub(crate) point: Point,
140    pub(crate) size: Size,
141    pub(crate) raw: &'a [u8],
142}