1use 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#[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#[derive(Clone, Copy, Default)]
77pub enum DisplayRotation {
78 #[default]
80 Rotate0,
81 Rotate90,
83 Rotate180,
85 Rotate270,
87}
88
89pub trait DisplayBuffer {
90 fn get_buffer_black(&self) -> &[u8];
91 fn get_buffer_red(&self) -> &[u8];
92}
93
94pub 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);