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    /// Draw a single pixel at the provided point using the given color.
24    pub fn draw_pixel(&mut self, point: Point, color: Color) {
25        let rgb = Rgb888::new(color.0, color.1, color.2);
26        let _ = self.inner.draw_iter(iter::once(Pixel(point, rgb)));
27    }
28
29    /// Return the raw color buffer of the canvas.
30    pub fn pixels(&self) -> Vec<Color> {
31        self.inner
32            .pixels
33            .iter()
34            .map(|p| match p {
35                Some(c) => Color(c.r(), c.g(), c.b()),
36                None => Color(0, 0, 0),
37            })
38            .collect()
39    }
40
41    /// Encode the canvas contents into a PNG image.
42    #[cfg(feature = "png")]
43    pub fn to_png(&self) -> Result<Vec<u8>, png::EncodingError> {
44        use png::{ColorType, Encoder};
45        let (w, h) = (self.inner.size().width, self.inner.size().height);
46        let mut buf = Vec::new();
47        {
48            let mut encoder = Encoder::new(&mut buf, w, h);
49            encoder.set_color(ColorType::Rgb);
50            let mut writer = encoder.write_header()?;
51            let data: Vec<u8> = self
52                .pixels()
53                .into_iter()
54                .flat_map(|c| vec![c.0, c.1, c.2])
55                .collect();
56            writer.write_image_data(&data)?;
57        }
58        Ok(buf)
59    }
60}
61
62#[cfg(test)]
63mod tests {
64    use super::*;
65
66    #[test]
67    fn draw_and_get_pixels() {
68        let mut canvas = Canvas::new(1, 1);
69        canvas.draw_pixel(Point::new(0, 0), Color(255, 0, 0));
70        assert_eq!(canvas.pixels(), vec![Color(255, 0, 0)]);
71    }
72
73    #[test]
74    fn blank_canvas_pixels() {
75        let canvas = Canvas::new(1, 1);
76        assert_eq!(canvas.pixels(), vec![Color(0, 0, 0)]);
77    }
78
79    #[cfg(feature = "png")]
80    #[test]
81    fn export_png() {
82        let mut canvas = Canvas::new(1, 1);
83        canvas.draw_pixel(Point::new(0, 0), Color(255, 0, 0));
84        let data = canvas.to_png().unwrap();
85        let (pixels, w, h) = crate::plugins::png::decode(&data).unwrap();
86        assert_eq!((w, h), (1, 1));
87        assert_eq!(pixels, vec![Color(255, 0, 0)]);
88    }
89}