epd_spectra/
graphics.rs

1//! Specific display buffers for each EPDs and `embedded_graphics` related implementations
2
3use core::cmp::{max, min};
4use embedded_graphics::{
5    draw_target::DrawTarget,
6    geometry::{OriginDimensions, Size},
7    pixelcolor::{
8        raw::{RawData, RawU2},
9        BinaryColor, PixelColor, Rgb888, RgbColor,
10    },
11    Pixel,
12};
13
14/// Colors supported by the e-paper displays
15#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)]
16pub enum TriColor {
17    #[default]
18    White,
19    Black,
20    Red,
21}
22
23impl PixelColor for TriColor {
24    type Raw = RawU2;
25}
26
27impl From<RawU2> for TriColor {
28    fn from(data: RawU2) -> Self {
29        let data = data.into_inner();
30        if data & 0b01 != 0 {
31            TriColor::Black
32        } else if data & 0b10 != 0 {
33            TriColor::Red
34        } else {
35            TriColor::White
36        }
37    }
38}
39
40impl From<BinaryColor> for TriColor {
41    fn from(b: BinaryColor) -> TriColor {
42        match b {
43            BinaryColor::On => Self::Black,
44            BinaryColor::Off => Self::White,
45        }
46    }
47}
48
49impl From<TriColor> for Rgb888 {
50    fn from(b: TriColor) -> Self {
51        match b {
52            TriColor::White => Self::new(u8::MAX, u8::MAX, u8::MAX),
53            TriColor::Black => Self::new(0, 0, 0),
54            TriColor::Red => Self::new(u8::MAX, 0, 0),
55        }
56    }
57}
58
59impl From<Rgb888> for TriColor {
60    fn from(p: Rgb888) -> TriColor {
61        let min = min(min(p.r(), p.g()), p.b());
62        let max = max(max(p.r(), p.g()), p.b());
63        let chroma = max - min;
64        let brightness = max;
65        if chroma > u8::MAX / 3 && p.r() > p.g() && p.r() > p.b() {
66            TriColor::Red
67        } else if brightness > u8::MAX / 2 {
68            TriColor::White
69        } else {
70            TriColor::Black
71        }
72    }
73}
74
75/// Display rotation, only 90° increments supported
76#[derive(Clone, Copy, Default)]
77pub enum DisplayRotation {
78    /// No rotation
79    #[default]
80    Rotate0,
81    /// Rotate by 90 degrees clockwise
82    Rotate90,
83    /// Rotate by 180 degrees clockwise
84    Rotate180,
85    /// Rotate 270 degrees clockwise
86    Rotate270,
87}
88
89pub trait DisplayBuffer {
90    fn get_buffer_black(&self) -> &[u8];
91    fn get_buffer_red(&self) -> &[u8];
92}
93
94/// Display buffer used for drawing with `embedded_graphics`.
95/// The concrete types are dependent on the size.
96/// Examples: `Display1in54`, `Display2in13`, ...
97pub struct Display<const SIZE_V: u32, const SIZE_H: u32, const IMAGE_SIZE: usize> {
98    buffer_black: [u8; IMAGE_SIZE],
99    buffer_red: [u8; IMAGE_SIZE],
100    rotation: DisplayRotation,
101}
102
103impl<const SIZE_V: u32, const SIZE_H: u32, const IMAGE_SIZE: usize>
104    Display<SIZE_V, SIZE_H, IMAGE_SIZE>
105{
106    pub fn set_rotation(&mut self, rotation: DisplayRotation) {
107        self.rotation = rotation;
108    }
109    #[must_use]
110    pub fn rotation(&self) -> DisplayRotation {
111        self.rotation
112    }
113}
114
115impl<const SIZE_V: u32, const SIZE_H: u32, const IMAGE_SIZE: usize> DisplayBuffer
116    for Display<SIZE_V, SIZE_H, IMAGE_SIZE>
117{
118    fn get_buffer_black(&self) -> &[u8] {
119        &self.buffer_black
120    }
121    fn get_buffer_red(&self) -> &[u8] {
122        &self.buffer_red
123    }
124}
125
126impl<const SIZE_V: u32, const SIZE_H: u32, const IMAGE_SIZE: usize> Default
127    for Display<SIZE_V, SIZE_H, IMAGE_SIZE>
128{
129    fn default() -> Self {
130        Self {
131            buffer_black: [0; IMAGE_SIZE],
132            buffer_red: [0; IMAGE_SIZE],
133            rotation: DisplayRotation::default(),
134        }
135    }
136}
137
138impl<const SIZE_V: u32, const SIZE_H: u32, const IMAGE_SIZE: usize> OriginDimensions
139    for Display<SIZE_V, SIZE_H, IMAGE_SIZE>
140{
141    fn size(&self) -> Size {
142        match self.rotation {
143            DisplayRotation::Rotate0 | DisplayRotation::Rotate180 => Size::new(SIZE_H, SIZE_V),
144            DisplayRotation::Rotate90 | DisplayRotation::Rotate270 => Size::new(SIZE_V, SIZE_H),
145        }
146    }
147}
148
149impl<const SIZE_V: u32, const SIZE_H: u32, const IMAGE_SIZE: usize> DrawTarget
150    for Display<SIZE_V, SIZE_H, IMAGE_SIZE>
151{
152    type Color = TriColor;
153    type Error = core::convert::Infallible;
154
155    #[allow(clippy::cast_sign_loss, clippy::cast_possible_wrap)]
156    fn draw_iter<I>(&mut self, pixels: I) -> Result<(), Self::Error>
157    where
158        I: IntoIterator<Item = Pixel<Self::Color>>,
159    {
160        for pixel in pixels {
161            let Pixel(p, color) = pixel;
162
163            let (x, y) = match self.rotation {
164                DisplayRotation::Rotate0 => (p.x, p.y),
165                DisplayRotation::Rotate90 => (SIZE_H as i32 - 1 - p.y, p.x),
166                DisplayRotation::Rotate180 => (SIZE_H as i32 - 1 - p.x, SIZE_V as i32 - 1 - p.y),
167                DisplayRotation::Rotate270 => (p.y, SIZE_V as i32 - 1 - p.x),
168            };
169
170            if (x < 0) || (x >= SIZE_H as i32) || (y < 0) || y >= SIZE_V as i32 {
171                continue;
172            }
173
174            let mask: u8 = 1 << (7 - (x % 8));
175            let index = y as usize * SIZE_H as usize / 8 + x as usize / 8;
176            assert!(index < IMAGE_SIZE);
177
178            match color {
179                TriColor::White => {
180                    self.buffer_black[index] &= !mask;
181                    self.buffer_red[index] &= !mask;
182                }
183                TriColor::Black => {
184                    self.buffer_black[index] |= mask;
185                    self.buffer_red[index] &= !mask;
186                }
187                TriColor::Red => {
188                    self.buffer_black[index] &= !mask;
189                    self.buffer_red[index] |= mask;
190                }
191            }
192        }
193        Ok(())
194    }
195}
196
197macro_rules! display_type {
198    ($a:expr, $b:expr) => {
199        Display<$a, $b, {$a * ($b / 8)}>
200    };
201}
202pub type Display1in54 = display_type!(152, 152);
203pub type Display2in13 = display_type!(212, 104);
204pub type Display2in66 = display_type!(296, 152);
205pub type Display2in71 = display_type!(264, 176);
206pub type Display2in87 = display_type!(296, 128);
207pub type Display3in70 = display_type!(416, 240);
208pub type Display4in17 = display_type!(300, 400);
209pub type Display4in37 = display_type!(480, 176);
210pub type Display2in9 = display_type!(384, 168);