rlvgl_core/plugins/
canvas.rs

1//! Canvas wrapper based on `embedded_canvas`.
2use crate::widget::Color;
3use alloc::vec;
4use alloc::vec::Vec;
5use core::iter;
6use embedded_canvas::Canvas as EcCanvas;
7use embedded_graphics::pixelcolor::Rgb888;
8use embedded_graphics::prelude::{DrawTarget, OriginDimensions, Pixel, Point, RgbColor, Size};
9
10/// In-memory pixel buffer that can be drawn on using [`embedded_canvas`].
11pub struct Canvas {
12    inner: EcCanvas<Rgb888>,
13}
14
15impl Canvas {
16    /// Create a new canvas with the given dimensions in pixels.
17    pub fn new(width: u32, height: u32) -> Self {
18        Self {
19            inner: EcCanvas::new(Size::new(width, height)),
20        }
21    }
22
23    /// Return the width and height of the canvas in pixels.
24    pub fn size(&self) -> (u32, u32) {
25        let s = self.inner.size();
26        (s.width, s.height)
27    }
28
29    /// Draw a single pixel at the provided point using the given color.
30    pub fn draw_pixel(&mut self, point: Point, color: Color) {
31        let rgb = Rgb888::new(color.0, color.1, color.2);
32        let _ = self.inner.draw_iter(iter::once(Pixel(point, rgb)));
33    }
34
35    /// Return the raw color buffer of the canvas.
36    pub fn pixels(&self) -> Vec<Color> {
37        self.inner
38            .pixels
39            .iter()
40            .map(|p| match p {
41                Some(c) => Color(c.r(), c.g(), c.b(), 255),
42                None => Color(0, 0, 0, 255),
43            })
44            .collect()
45    }
46
47    /// Encode the canvas contents into a PNG image.
48    #[cfg(feature = "png")]
49    pub fn to_png(&self) -> Result<Vec<u8>, png::EncodingError> {
50        use png::{ColorType, Encoder};
51        let (w, h) = (self.inner.size().width, self.inner.size().height);
52        let mut buf = Vec::new();
53        {
54            let mut encoder = Encoder::new(&mut buf, w, h);
55            encoder.set_color(ColorType::Rgb);
56            let mut writer = encoder.write_header()?;
57            let data: Vec<u8> = self
58                .pixels()
59                .into_iter()
60                .flat_map(|c| vec![c.0, c.1, c.2])
61                .collect();
62            writer.write_image_data(&data)?;
63        }
64        Ok(buf)
65    }
66}
67
68#[cfg(test)]
69mod tests {
70    use super::*;
71
72    #[test]
73    fn draw_and_get_pixels() {
74        let mut canvas = Canvas::new(1, 1);
75        canvas.draw_pixel(Point::new(0, 0), Color(255, 0, 0, 255));
76        assert_eq!(canvas.pixels(), vec![Color(255, 0, 0, 255)]);
77    }
78
79    #[test]
80    fn blank_canvas_pixels() {
81        let canvas = Canvas::new(1, 1);
82        assert_eq!(canvas.pixels(), vec![Color(0, 0, 0, 255)]);
83    }
84
85    #[cfg(feature = "png")]
86    #[test]
87    fn export_png() {
88        let mut canvas = Canvas::new(1, 1);
89        canvas.draw_pixel(Point::new(0, 0), Color(255, 0, 0, 255));
90        let data = canvas.to_png().unwrap();
91        let (pixels, w, h) = crate::plugins::png::decode(&data).unwrap();
92        assert_eq!((w, h), (1, 1));
93        assert_eq!(pixels, vec![Color(255, 0, 0, 255)]);
94    }
95}