1pub mod display290_gray4_mfgn;
3pub mod display290_gray4_t5;
4pub mod display290_mono;
5
6use crate::color::Color;
7use embedded_graphics::{pixelcolor::BinaryColor, prelude::*};
8
9pub trait Display: DrawTarget {
16 fn clear_buffer(&mut self, background_color: Color) {
18 let fill_color = if self.is_inverted() {
19 background_color.inverse().get_byte_value()
20 } else {
21 background_color.get_byte_value()
22 };
23
24 for elem in self.get_mut_buffer().iter_mut() {
25 *elem = fill_color
26 }
27 }
28
29 fn buffer(&self) -> &[u8];
31
32 fn get_mut_buffer(&mut self) -> &mut [u8];
34
35 fn set_rotation(&mut self, rotation: DisplayRotation);
37
38 fn rotation(&self) -> DisplayRotation;
40
41 fn is_inverted(&self) -> bool;
43
44 fn draw_helper(
46 &mut self,
47 width: u32,
48 height: u32,
49 pixel: Pixel<BinaryColor>,
50 ) -> Result<(), Self::Error> {
51 let rotation = self.rotation();
52 let is_inverted = self.is_inverted();
53 let buffer = self.get_mut_buffer();
54
55 let Pixel(point, color) = pixel;
56 if outside_display(point, width, height, rotation) {
57 return Ok(());
58 }
59
60 let (index, bit) = find_position(point.x as u32, point.y as u32, width, height, rotation);
62 let index = index as usize;
63
64 match color {
66 BinaryColor::On => {
68 if is_inverted {
69 buffer[index] &= !bit;
70 } else {
71 buffer[index] |= bit;
72 }
73 }
74 BinaryColor::Off => {
76 if is_inverted {
77 buffer[index] |= bit;
78 } else {
79 buffer[index] &= !bit;
80 }
81 }
82 }
83 Ok(())
84 }
85}
86
87#[derive(Clone, Copy, Default)]
89pub enum DisplayRotation {
90 #[default]
92 Rotate0,
93 Rotate90,
95 Rotate180,
97 Rotate270,
99}
100
101fn outside_display(p: Point, width: u32, height: u32, rotation: DisplayRotation) -> bool {
103 if p.x < 0 || p.y < 0 {
104 return true;
105 }
106 let (x, y) = (p.x as u32, p.y as u32);
107 match rotation {
108 DisplayRotation::Rotate0 | DisplayRotation::Rotate180 => {
109 if x >= width || y >= height {
110 return true;
111 }
112 }
113 DisplayRotation::Rotate90 | DisplayRotation::Rotate270 => {
114 if y >= width || x >= height {
115 return true;
116 }
117 }
118 }
119 false
120}
121
122fn find_rotation(x: u32, y: u32, width: u32, height: u32, rotation: DisplayRotation) -> (u32, u32) {
123 let nx;
124 let ny;
125 match rotation {
126 DisplayRotation::Rotate0 => {
127 nx = x;
128 ny = y;
129 }
130 DisplayRotation::Rotate90 => {
131 nx = width - 1 - y;
132 ny = x;
133 }
134 DisplayRotation::Rotate180 => {
135 nx = width - 1 - x;
136 ny = height - 1 - y;
137 }
138 DisplayRotation::Rotate270 => {
139 nx = y;
140 ny = height - 1 - x;
141 }
142 }
143 (nx, ny)
144}
145
146#[rustfmt::skip]
147fn find_position(x: u32, y: u32, width: u32, height: u32, rotation: DisplayRotation) -> (u32, u8) {
149 let (nx, ny) = find_rotation(x, y, width, height, rotation);
150 (
151 nx / 8 + width.div_ceil(8) * ny,
152 0x80 >> (nx % 8)
153 )
154}
155
156#[must_use]
159const fn buffer_len(width: usize, height: usize) -> usize {
160 width.div_ceil(8) * height
161}
162
163#[cfg(test)]
164mod tests {
165 use super::*;
166 use core::convert::Infallible;
167 use embedded_graphics::pixelcolor::BinaryColor;
168 use embedded_graphics::prelude::{Pixel, Point};
169
170 struct DummyDisplay {
172 buffer: [u8; 4736],
173 rotation: DisplayRotation,
174 inverted: bool,
175 width: u32,
176 height: u32,
177 }
178
179 impl DummyDisplay {
180 fn new(width: u32, height: u32, rotation: DisplayRotation, inverted: bool) -> Self {
181 Self {
182 buffer: [0u8; 4736],
183 rotation,
184 inverted,
185 width,
186 height,
187 }
188 }
189 }
190
191 impl embedded_graphics::geometry::OriginDimensions for DummyDisplay {
192 fn size(&self) -> embedded_graphics::prelude::Size {
193 match self.rotation {
194 DisplayRotation::Rotate0 | DisplayRotation::Rotate180 => {
195 embedded_graphics::prelude::Size::new(self.width, self.height)
196 }
197 DisplayRotation::Rotate90 | DisplayRotation::Rotate270 => {
198 embedded_graphics::prelude::Size::new(self.height, self.width)
199 }
200 }
201 }
202 }
203
204 impl embedded_graphics::draw_target::DrawTarget for DummyDisplay {
205 type Error = Infallible;
206 type Color = BinaryColor;
207
208 fn draw_iter<I>(&mut self, _pixels: I) -> Result<(), Self::Error>
209 where
210 I: IntoIterator<Item = Pixel<Self::Color>>,
211 {
212 for _ in _pixels.into_iter() {}
214 Ok(())
215 }
216 }
217
218 impl Display for DummyDisplay {
219 fn buffer(&self) -> &[u8] {
220 &self.buffer
221 }
222
223 fn get_mut_buffer(&mut self) -> &mut [u8] {
224 &mut self.buffer
225 }
226
227 fn set_rotation(&mut self, rotation: DisplayRotation) {
228 self.rotation = rotation;
229 }
230
231 fn rotation(&self) -> DisplayRotation {
232 self.rotation
233 }
234
235 fn is_inverted(&self) -> bool {
236 self.inverted
237 }
238 }
239
240 #[test]
241 fn draw_helper_sets_msb_first_bits() {
242 let mut d = DummyDisplay::new(296, 128, DisplayRotation::Rotate0, false);
244
245 for x in 0..8 {
247 let p = Pixel(Point::new(x, 0), BinaryColor::On);
248 let w = d.width;
249 let h = d.height;
250 Display::draw_helper(&mut d, w, h, p).unwrap();
251 }
252
253 assert_eq!(d.buffer[0], 0xFF);
254
255 for &b in d.buffer.iter().skip(1) {
257 assert_eq!(b, 0);
258 }
259 }
260
261 #[test]
262 fn draw_helper_sets_last_byte_in_buffer() {
263 let width = 296u32;
264 let height = 128u32;
265 let mut d = DummyDisplay::new(width, height, DisplayRotation::Rotate0, false);
266
267 let x = width - 1;
269 let y = height - 1;
270 Display::draw_helper(
271 &mut d,
272 width,
273 height,
274 Pixel(Point::new(x as i32, y as i32), BinaryColor::On),
275 )
276 .unwrap();
277
278 let bytes_per_row = width.div_ceil(8);
279 let idx = (x / 8 + bytes_per_row * y) as usize;
280 let bit = 0x80u8 >> (x % 8);
281
282 assert_eq!(d.buffer[idx] & bit, bit);
284
285 assert_eq!(idx, (bytes_per_row * height - 1) as usize);
287 }
288
289 #[test]
290 fn draw_helper_respects_rotation() {
291 let mut d = DummyDisplay::new(296, 128, DisplayRotation::Rotate90, false);
292
293 let logical_x = 1u32;
295 let logical_y = 1u32;
296
297 let w = d.width;
298 let h = d.height;
299 Display::draw_helper(
300 &mut d,
301 w,
302 h,
303 Pixel(
304 Point::new(logical_x as i32, logical_y as i32),
305 BinaryColor::On,
306 ),
307 )
308 .unwrap();
309
310 let (nx, ny) = super::find_rotation(
311 logical_x,
312 logical_y,
313 d.width,
314 d.height,
315 DisplayRotation::Rotate90,
316 );
317 let bytes_per_row = 296u32.div_ceil(8);
318 let idx = (nx / 8 + bytes_per_row * ny) as usize;
319 let bit = 0x80u8 >> (nx % 8);
320
321 assert_eq!(d.buffer[idx] & bit, bit);
322 }
323
324 #[test]
325 fn draw_helper_inversion_behavior() {
326 let mut d = DummyDisplay::new(8, 1, DisplayRotation::Rotate0, true);
327
328 for b in d.buffer.iter_mut() {
330 *b = 0xFF;
331 }
332
333 let w = d.width;
335 let h = d.height;
336 Display::draw_helper(&mut d, w, h, Pixel(Point::new(0, 0), BinaryColor::On)).unwrap();
337 assert_eq!(d.buffer[0] & 0x80, 0);
338
339 let w = d.width;
341 let h = d.height;
342 Display::draw_helper(&mut d, w, h, Pixel(Point::new(1, 0), BinaryColor::Off)).unwrap();
343 assert_eq!(d.buffer[0] & 0x40, 0x40);
344 }
345}