firefly_rust/graphics/
image.rs

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