embedded_graphics_simulator/
display.rs

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