embedded_graphics_simulator/
display.rs

1use std::{convert::TryFrom, fs::File, io::BufReader, path::Path};
2
3use embedded_graphics::{
4    pixelcolor::{raw::ToBytes, BinaryColor, Gray8, Rgb888},
5    prelude::*,
6};
7
8use crate::{output_image::OutputImage, output_settings::OutputSettings};
9
10/// Simulator display.
11#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
12pub struct SimulatorDisplay<C> {
13    size: Size,
14    pub(crate) pixels: Box<[C]>,
15}
16
17impl<C: PixelColor> SimulatorDisplay<C> {
18    /// Creates a new display filled with a color.
19    ///
20    /// This constructor can be used if `C` doesn't implement `From<BinaryColor>` or another
21    /// default color is wanted.
22    pub fn with_default_color(size: Size, default_color: C) -> Self {
23        let pixel_count = size.width as usize * size.height as usize;
24        let pixels = vec![default_color; pixel_count].into_boxed_slice();
25
26        SimulatorDisplay { size, pixels }
27    }
28
29    /// Returns the color of the pixel at a point.
30    ///
31    /// # Panics
32    ///
33    /// Panics if `point` is outside the display.
34    pub fn get_pixel(&self, point: Point) -> C {
35        self.point_to_index(point)
36            .and_then(|index| self.pixels.get(index).copied())
37            .expect("can't get point outside of display")
38    }
39
40    fn point_to_index(&self, point: Point) -> Option<usize> {
41        if let Ok((x, y)) = <(u32, u32)>::try_from(point) {
42            if x < self.size.width && y < self.size.height {
43                return Some((x + y * self.size.width) as usize);
44            }
45        }
46
47        None
48    }
49
50    /// Compares the content of this display with another display.
51    ///
52    /// If both displays are equal `None` is returned, otherwise a difference image is returned.
53    /// All pixels that are different will be filled with `BinaryColor::On` and all equal pixels
54    /// with `BinaryColor::Off`.
55    ///
56    /// # Panics
57    ///
58    /// Panics if the both display don't have the same size.
59    pub fn diff(&self, other: &SimulatorDisplay<C>) -> Option<SimulatorDisplay<BinaryColor>> {
60        assert!(
61            self.size == other.size,
62            // TODO: use Display impl for Size
63            "both displays must have the same size (self: {}x{}, other: {}x{})",
64            self.size.width,
65            self.size.height,
66            other.size.width,
67            other.size.height,
68        );
69
70        let pixels = self
71            .bounding_box()
72            .points()
73            .map(|p| BinaryColor::from(self.get_pixel(p) != other.get_pixel(p)))
74            .collect::<Vec<_>>()
75            .into_boxed_slice();
76
77        if pixels.iter().any(|p| *p == BinaryColor::On) {
78            Some(SimulatorDisplay {
79                pixels,
80                size: self.size,
81            })
82        } else {
83            None
84        }
85    }
86}
87
88impl<C> SimulatorDisplay<C>
89where
90    C: PixelColor + From<BinaryColor>,
91{
92    /// Creates a new display.
93    ///
94    /// The display is filled with `C::from(BinaryColor::Off)`.
95    pub fn new(size: Size) -> Self {
96        Self::with_default_color(size, C::from(BinaryColor::Off))
97    }
98}
99
100impl<C> SimulatorDisplay<C>
101where
102    C: PixelColor + Into<Rgb888>,
103{
104    /// Converts the display contents into a RGB output image.
105    ///
106    /// # Examples
107    ///
108    /// ```rust
109    /// use embedded_graphics::{pixelcolor::Rgb888, prelude::*};
110    /// use embedded_graphics_simulator::{OutputSettingsBuilder, SimulatorDisplay};
111    ///
112    /// let output_settings = OutputSettingsBuilder::new().scale(2).build();
113    ///
114    /// let display = SimulatorDisplay::<Rgb888>::new(Size::new(128, 64));
115    ///
116    /// // draw something to the display
117    ///
118    /// let output_image = display.to_rgb_output_image(&output_settings);
119    /// assert_eq!(output_image.size(), Size::new(256, 128));
120    ///
121    /// // use output image:
122    /// // example: output_image.save_png("out.png")?;
123    /// ```
124    pub fn to_rgb_output_image(&self, output_settings: &OutputSettings) -> OutputImage<Rgb888> {
125        let mut output = OutputImage::new(self, output_settings);
126        output.update(self);
127
128        output
129    }
130
131    /// Converts the display contents into a grayscale output image.
132    ///
133    /// # Examples
134    ///
135    /// ```rust
136    /// use embedded_graphics::{pixelcolor::Gray8, prelude::*};
137    /// use embedded_graphics_simulator::{OutputSettingsBuilder, SimulatorDisplay};
138    ///
139    /// let output_settings = OutputSettingsBuilder::new().scale(2).build();
140    ///
141    /// let display = SimulatorDisplay::<Gray8>::new(Size::new(128, 64));
142    ///
143    /// // draw something to the display
144    ///
145    /// let output_image = display.to_grayscale_output_image(&output_settings);
146    /// assert_eq!(output_image.size(), Size::new(256, 128));
147    ///
148    /// // use output image:
149    /// // example: output_image.save_png("out.png")?;
150    /// ```
151    pub fn to_grayscale_output_image(
152        &self,
153        output_settings: &OutputSettings,
154    ) -> OutputImage<Gray8> {
155        let mut output = OutputImage::new(self, output_settings);
156        output.update(self);
157
158        output
159    }
160}
161
162impl<C> SimulatorDisplay<C>
163where
164    C: PixelColor + ToBytes,
165    <C as ToBytes>::Bytes: AsRef<[u8]>,
166{
167    /// Converts the display content to big endian raw data.
168    pub fn to_be_bytes(&self) -> Vec<u8> {
169        self.to_bytes(ToBytes::to_be_bytes)
170    }
171
172    /// Converts the display content to little endian raw data.
173    pub fn to_le_bytes(&self) -> Vec<u8> {
174        self.to_bytes(ToBytes::to_le_bytes)
175    }
176
177    /// Converts the display content to native endian raw data.
178    pub fn to_ne_bytes(&self) -> Vec<u8> {
179        self.to_bytes(ToBytes::to_ne_bytes)
180    }
181
182    fn to_bytes<F>(&self, pixel_to_bytes: F) -> Vec<u8>
183    where
184        F: Fn(C) -> C::Bytes,
185    {
186        let mut bytes = Vec::new();
187
188        if C::Raw::BITS_PER_PIXEL >= 8 {
189            for pixel in self.pixels.iter() {
190                bytes.extend_from_slice(pixel_to_bytes(*pixel).as_ref())
191            }
192        } else {
193            let pixels_per_byte = 8 / C::Raw::BITS_PER_PIXEL;
194
195            for row in self.pixels.chunks(self.size.width as usize) {
196                for byte_pixels in row.chunks(pixels_per_byte) {
197                    let mut value = 0;
198
199                    for pixel in byte_pixels {
200                        value <<= C::Raw::BITS_PER_PIXEL;
201                        value |= pixel.to_be_bytes().as_ref()[0];
202                    }
203
204                    value <<= C::Raw::BITS_PER_PIXEL * (pixels_per_byte - byte_pixels.len());
205
206                    bytes.push(value);
207                }
208            }
209        }
210
211        bytes
212    }
213}
214
215impl<C> SimulatorDisplay<C>
216where
217    C: PixelColor + From<Rgb888>,
218{
219    /// Loads a PNG file.
220    pub fn load_png<P: AsRef<Path>>(path: P) -> image::ImageResult<Self> {
221        let png_file = BufReader::new(File::open(path)?);
222        let image = image::load(png_file, image::ImageFormat::Png)?.to_rgb8();
223
224        let pixels = image
225            .pixels()
226            .map(|p| Rgb888::new(p[0], p[1], p[2]).into())
227            .collect();
228
229        Ok(Self {
230            size: Size::new(image.width(), image.height()),
231            pixels,
232        })
233    }
234}
235
236impl<C: PixelColor> DrawTarget for SimulatorDisplay<C> {
237    type Color = C;
238    type Error = core::convert::Infallible;
239
240    fn draw_iter<I>(&mut self, pixels: I) -> Result<(), Self::Error>
241    where
242        I: IntoIterator<Item = Pixel<Self::Color>>,
243    {
244        for Pixel(point, color) in pixels.into_iter() {
245            if let Some(index) = self.point_to_index(point) {
246                self.pixels[index] = color;
247            }
248        }
249
250        Ok(())
251    }
252}
253
254impl<C> OriginDimensions for SimulatorDisplay<C> {
255    fn size(&self) -> Size {
256        self.size
257    }
258}
259
260#[cfg(test)]
261mod tests {
262    use super::*;
263
264    use embedded_graphics::{
265        pixelcolor::{Gray2, Gray4, Rgb565},
266        primitives::{Circle, Line, PrimitiveStyle},
267    };
268
269    #[test]
270    fn rgb_output_image() {
271        let mut display = SimulatorDisplay::<BinaryColor>::new(Size::new(2, 4));
272
273        Line::new(Point::new(0, 0), Point::new(1, 3))
274            .into_styled(PrimitiveStyle::with_stroke(BinaryColor::On, 1))
275            .draw(&mut display)
276            .unwrap();
277
278        let image = display.to_rgb_output_image(&OutputSettings::default());
279        assert_eq!(image.size(), display.size());
280
281        let expected: &[u8] = &[
282            255, 255, 255, 0, 0, 0, //
283            255, 255, 255, 0, 0, 0, //
284            0, 0, 0, 255, 255, 255, //
285            0, 0, 0, 255, 255, 255, //
286        ];
287        assert_eq!(image.data.as_ref(), expected);
288    }
289
290    #[test]
291    fn grayscale_image_buffer() {
292        let mut display = SimulatorDisplay::<BinaryColor>::new(Size::new(2, 4));
293
294        Line::new(Point::new(0, 0), Point::new(1, 3))
295            .into_styled(PrimitiveStyle::with_stroke(BinaryColor::On, 1))
296            .draw(&mut display)
297            .unwrap();
298
299        let image = display.to_grayscale_output_image(&OutputSettings::default());
300        assert_eq!(image.size(), display.size());
301
302        let expected: &[u8] = &[
303            255, 0, //
304            255, 0, //
305            0, 255, //
306            0, 255, //
307        ];
308        assert_eq!(image.data.as_ref(), expected);
309    }
310
311    #[test]
312    fn to_bytes_u1() {
313        let display = SimulatorDisplay {
314            size: Size::new(9, 3),
315            pixels: [
316                1, 0, 0, 0, 0, 0, 0, 1, 0, //
317                0, 1, 0, 0, 0, 0, 1, 0, 1, //
318                0, 0, 1, 0, 0, 1, 0, 0, 0, //
319            ]
320            .iter()
321            .map(|c| BinaryColor::from(*c != 0))
322            .collect::<Vec<_>>()
323            .into_boxed_slice(),
324        };
325
326        let expected = [
327            0b10000001, 0b00000000, //
328            0b01000010, 0b10000000, //
329            0b00100100, 0b00000000, //
330        ];
331        assert_eq!(&display.to_be_bytes(), &expected);
332        assert_eq!(&display.to_le_bytes(), &expected);
333        assert_eq!(&display.to_ne_bytes(), &expected);
334    }
335
336    #[test]
337    fn to_bytes_u2() {
338        let display = SimulatorDisplay {
339            size: Size::new(5, 2),
340            pixels: [
341                0, 1, 2, 3, 0, //
342                1, 0, 3, 2, 1, //
343            ]
344            .iter()
345            .map(|c| Gray2::new(*c))
346            .collect::<Vec<_>>()
347            .into_boxed_slice(),
348        };
349
350        let expected = [
351            0b00011011, 0b00000000, //
352            0b01001110, 0b01000000, //
353        ];
354        assert_eq!(&display.to_be_bytes(), &expected);
355        assert_eq!(&display.to_le_bytes(), &expected);
356        assert_eq!(&display.to_ne_bytes(), &expected);
357    }
358
359    #[test]
360    fn to_bytes_u4() {
361        let display = SimulatorDisplay {
362            size: Size::new(5, 4),
363            pixels: [
364                0x0, 0x1, 0x2, 0x3, 0x4, //
365                0x5, 0x6, 0x7, 0x8, 0x9, //
366                0xA, 0xB, 0xC, 0xD, 0xE, //
367                0xF, 0x0, 0x0, 0x0, 0x0, //
368            ]
369            .iter()
370            .map(|c| Gray4::new(*c))
371            .collect::<Vec<_>>()
372            .into_boxed_slice(),
373        };
374
375        let expected = [
376            0x01, 0x23, 0x40, //
377            0x56, 0x78, 0x90, //
378            0xAB, 0xCD, 0xE0, //
379            0xF0, 0x00, 0x00, //
380        ];
381        assert_eq!(&display.to_be_bytes(), &expected);
382        assert_eq!(&display.to_le_bytes(), &expected);
383        assert_eq!(&display.to_ne_bytes(), &expected);
384    }
385
386    #[test]
387    fn to_bytes_u8() {
388        let expected = [
389            1, 2, 3, //
390            11, 12, 13, //
391        ];
392
393        let display = SimulatorDisplay {
394            size: Size::new(3, 2),
395            pixels: expected
396                .iter()
397                .copied()
398                .map(Gray8::new)
399                .collect::<Vec<_>>()
400                .into_boxed_slice(),
401        };
402
403        assert_eq!(&display.to_be_bytes(), &expected);
404        assert_eq!(&display.to_le_bytes(), &expected);
405        assert_eq!(&display.to_ne_bytes(), &expected);
406    }
407
408    #[test]
409    fn to_bytes_u16() {
410        let expected = vec![Rgb565::new(0x10, 0x00, 0x00), Rgb565::new(0x00, 0x00, 0x01)];
411
412        let display = SimulatorDisplay {
413            size: Size::new(2, 1),
414            pixels: expected.clone().into_boxed_slice(),
415        };
416
417        assert_eq!(&display.to_be_bytes(), &[0x80, 0x00, 0x00, 0x01]);
418        assert_eq!(&display.to_le_bytes(), &[0x00, 0x80, 0x01, 0x00]);
419    }
420
421    #[test]
422    fn to_bytes_u24() {
423        let expected = vec![Rgb888::new(0x80, 0x00, 0x00), Rgb888::new(0x00, 0x00, 0x01)];
424
425        let display = SimulatorDisplay {
426            size: Size::new(2, 1),
427            pixels: expected.clone().into_boxed_slice(),
428        };
429
430        assert_eq!(
431            &display.to_be_bytes(),
432            &[0x80, 0x00, 0x00, 0x00, 0x00, 0x01]
433        );
434        assert_eq!(
435            &display.to_le_bytes(),
436            &[0x00, 0x00, 0x80, 0x01, 0x00, 0x00]
437        );
438    }
439
440    #[test]
441    fn diff_equal() {
442        let display = SimulatorDisplay::<BinaryColor>::new(Size::new(4, 6));
443        let expected = display.clone();
444
445        assert_eq!(display.diff(&expected), None);
446    }
447
448    #[test]
449    fn diff_not_equal() {
450        let circle = Circle::new(Point::zero(), 3);
451
452        let mut display = SimulatorDisplay::<BinaryColor>::new(Size::new(4, 6));
453        let expected = display.clone();
454
455        circle
456            .into_styled(PrimitiveStyle::with_fill(BinaryColor::On))
457            .draw(&mut display)
458            .unwrap();
459
460        assert_eq!(display.diff(&expected), Some(display));
461    }
462
463    #[test]
464    #[should_panic(expected = "both displays must have the same size (self: 4x6, other: 4x5)")]
465    fn diff_wrong_size() {
466        let display = SimulatorDisplay::<BinaryColor>::new(Size::new(4, 6));
467        let expected = SimulatorDisplay::<BinaryColor>::new(Size::new(4, 5));
468
469        assert_eq!(display.diff(&expected), None);
470    }
471}