1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
use crate::{framebuffer::Framebuffer, output_settings::OutputSettings};
use embedded_graphics::{
    drawable::Pixel,
    geometry::{Point, Size},
    pixelcolor::{BinaryColor, PixelColor, Rgb888},
    DrawTarget,
};
use image::{ImageBuffer, Rgb};
use std::convert::TryFrom;

/// Simulator display.
pub struct SimulatorDisplay<C> {
    size: Size,
    pixels: Box<[C]>,
}

impl<C> SimulatorDisplay<C>
where
    C: PixelColor,
{
    /// Creates a new display filled with a color.
    ///
    /// This constructor can be used if `C` doesn't implement `From<BinaryColor>` or another
    /// default color is wanted.
    pub fn with_default_color(size: Size, default_color: C) -> Self {
        let pixel_count = size.width as usize * size.height as usize;
        let pixels = vec![default_color; pixel_count].into_boxed_slice();

        SimulatorDisplay { size, pixels }
    }

    /// Returns the color of the pixel at a point.
    ///
    /// # Panics
    ///
    /// Panics if `point` is outside the display.
    pub fn get_pixel(&self, point: Point) -> C {
        self.point_to_index(point)
            .and_then(|index| self.pixels.get(index).copied())
            .expect("can't get point outside of display")
    }

    fn point_to_index(&self, point: Point) -> Option<usize> {
        if let Ok((x, y)) = <(u32, u32)>::try_from(point) {
            if x < self.size.width && y < self.size.height {
                return Some((x + y * self.size.width) as usize);
            }
        }

        None
    }
}

impl<C> SimulatorDisplay<C>
where
    C: PixelColor + From<BinaryColor>,
{
    /// Creates a new display.
    ///
    /// The display is filled with `C::from(BinaryColor::Off)`.
    pub fn new(size: Size) -> Self {
        Self::with_default_color(size, C::from(BinaryColor::Off))
    }
}

impl<C> SimulatorDisplay<C>
where
    C: PixelColor + Into<Rgb888>,
{
    /// Converts the display contents into an image.rs `ImageBuffer`.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use embedded_graphics::{prelude::*, pixelcolor::Rgb888};
    /// use embedded_graphics_simulator::{OutputSettingsBuilder, SimulatorDisplay};
    ///
    /// let output_settings = OutputSettingsBuilder::new().scale(2).build();
    ///
    /// let display: SimulatorDisplay<Rgb888> = SimulatorDisplay::new(Size::new(128, 64));
    ///
    /// // draw something to the display
    ///
    /// let image_buffer = display.to_image_buffer(&output_settings);
    /// assert_eq!(image_buffer.width(), 256);
    /// assert_eq!(image_buffer.height(), 128);
    ///
    /// // use image buffer
    /// // example: image_buffer.save
    /// ```
    pub fn to_image_buffer(
        &self,
        output_settings: &OutputSettings,
    ) -> ImageBuffer<Rgb<u8>, Box<[u8]>> {
        let framebuffer = Framebuffer::new(self, output_settings);
        framebuffer.into_image_buffer()
    }
}

impl<C> DrawTarget<C> for SimulatorDisplay<C>
where
    C: PixelColor,
{
    type Error = core::convert::Infallible;

    fn draw_pixel(&mut self, pixel: Pixel<C>) -> Result<(), Self::Error> {
        let Pixel(point, color) = pixel;

        if let Some(index) = self.point_to_index(point) {
            self.pixels[index] = color;
        }

        Ok(())
    }

    fn size(&self) -> Size {
        self.size
    }
}