Skip to main content

firefly_rust/graphics/
canvas.rs

1use crate::*;
2#[cfg(feature = "alloc")]
3use alloc::boxed::Box;
4#[cfg(feature = "alloc")]
5use alloc::vec;
6
7// /// Canvas is an [`Image`] that can be drawn upon.
8// #[cfg(feature = "alloc")]
9// pub struct CanvasStatic<const SIZE: usize> {
10//     pub(crate) raw: [u8; SIZE],
11// }
12
13// impl<const SIZE: usize> CanvasStatic<SIZE> {
14//     /// Create new [`CanvasStatic`].
15//     #[must_use]
16//     pub const fn new(width: usize) -> Self {
17//         const HEADER_SIZE: usize = 4;
18//         let mut raw = [0u8; SIZE];
19//         #[expect(clippy::cast_possible_wrap)]
20//         prepare_slice(&mut raw, width as i32);
21//         Self { raw }
22//     }
23// }
24
25/// Canvas is an [`Image`] that can be drawn upon.
26///
27/// [`CanvasBuf`] is the same as [`Canvas`] but holds the ownership of the underlying slice.
28#[cfg(feature = "alloc")]
29pub struct CanvasBuf {
30    pub(crate) raw: Box<[u8]>,
31}
32
33#[cfg(feature = "alloc")]
34impl CanvasBuf {
35    /// Create new empty canvas.
36    #[must_use]
37    #[expect(clippy::cast_sign_loss)]
38    pub fn new(s: Size) -> Self {
39        const HEADER_SIZE: usize = 4;
40        let body_size = s.width * s.height / 2;
41        let mut raw = vec![0; HEADER_SIZE + body_size as usize];
42        prepare_slice(&mut raw, s.width);
43        Self {
44            raw: raw.into_boxed_slice(),
45        }
46    }
47
48    #[must_use]
49    pub fn into_image(self) -> ImageBuf {
50        self.into()
51    }
52}
53
54#[cfg(feature = "alloc")]
55impl Canvas for CanvasBuf {
56    unsafe fn as_bytes(&self) -> &[u8] {
57        &self.raw
58    }
59}
60
61/// Canvas is an [`Image`] that can be drawn upon.
62pub struct CanvasRef<'a> {
63    pub(crate) raw: &'a [u8],
64}
65
66impl<'a> CanvasRef<'a> {
67    /// Create new empty canvas using the given slice.
68    ///
69    /// Returns [`None`] if the slice is too small for the given image size.
70    /// A bigger slice than needed is fine.
71    #[must_use]
72    #[expect(clippy::cast_sign_loss)]
73    pub fn new(s: Size, raw: &'a mut [u8]) -> Option<Self> {
74        const HEADER_SIZE: usize = 4;
75        let body_size = s.width * s.height / 2;
76        let exp_size = HEADER_SIZE + body_size as usize;
77        if raw.len() < exp_size {
78            return None;
79        }
80        prepare_slice(raw, s.width);
81        Some(Self {
82            raw: &raw[..exp_size],
83        })
84    }
85
86    #[must_use]
87    pub fn into_image(self) -> ImageRef<'a> {
88        self.into()
89    }
90}
91
92impl Canvas for CanvasRef<'_> {
93    unsafe fn as_bytes(&self) -> &[u8] {
94        self.raw
95    }
96}
97
98/// Canvas is an [`Image`] that can be drawn upon.
99pub trait Canvas {
100    /// Get the raw canvas representation.
101    ///
102    /// # Safety
103    ///
104    /// Don't use it. The internal canvas representation might change
105    /// in the future and so should not be relied upon.
106    unsafe fn as_bytes(&self) -> &[u8];
107}
108
109#[expect(clippy::cast_sign_loss)]
110const fn prepare_slice(raw: &mut [u8], width: i32) {
111    raw[0] = 0x22; // magic number
112    raw[1] = width as u8; // width
113    raw[2] = (width >> 8) as u8; // width
114    raw[3] = 255; // transparency
115}
116
117impl<T: Canvas> Image for T {
118    unsafe fn as_bytes(&self) -> &[u8] {
119        unsafe { Canvas::as_bytes(self) }
120    }
121}