weact_studio_epd/
graphics.rs

1use core::convert::Infallible;
2
3use embedded_graphics::{
4    draw_target::DrawTarget,
5    geometry::{OriginDimensions, Point, Size},
6    pixelcolor::PixelColor,
7    Pixel,
8};
9
10use crate::color::{Color, ColorType, TriColor};
11
12/// Rotation of the display.
13#[derive(Debug, Clone, Copy, Default)]
14pub enum DisplayRotation {
15    /// No rotation.
16    #[default]
17    Rotate0,
18    /// Rotate by 90 degrees clockwise.
19    Rotate90,
20    /// Rotate by 180 degrees clockwise.
21    Rotate180,
22    /// Rotate 270 degrees clockwise.
23    Rotate270,
24}
25
26/// Computes the needed buffer length. Takes care of rounding up in case `width`
27/// is not divisible by 8.
28pub const fn buffer_len<C>(width: usize, height: usize) -> usize
29where
30    C: ColorType,
31{
32    (width + 7) / 8 * height * C::BUFFER_COUNT
33}
34
35/// In-memory display buffer to render to using `embedded-graphics`.
36///
37/// `BUFFER_SIZE` can be calculated using [`buffer_len`].
38pub struct Display<const WIDTH: u32, const HEIGHT: u32, const BUFFER_SIZE: usize, C> {
39    buffer: [u8; BUFFER_SIZE],
40    rotation: DisplayRotation,
41    _color: core::marker::PhantomData<C>,
42}
43
44/// Display buffer for the WeAct Studio 2.9 inch B/W display.
45pub type Display290BlackWhite = Display<128, 296, { buffer_len::<Color>(128, 296) }, Color>;
46/// Display buffer for the WeAct Studio 2.9 inch tri-color display.
47pub type Display290TriColor = Display<128, 296, { buffer_len::<TriColor>(128, 296) }, TriColor>;
48/// Display buffer for the WeAct Studio 2.13 inch B/W display.
49///
50/// The screen uses a 128 pixel wide buffer but only 122 pixels are visible.
51pub type Display213BlackWhite = Display<128, 250, { buffer_len::<Color>(128, 250) }, Color>;
52/// Display buffer for the WeAct Studio 2.13 inch tri-color display.
53///
54/// The screen uses a 128 pixel wide buffer but only 122 pixels are visible.
55pub type Display213TriColor = Display<128, 250, { buffer_len::<TriColor>(128, 250) }, TriColor>;
56
57/// Generically-sized B/W display buffer.
58///
59/// `WIDTH` must be a multiple of 8. `BUFFER_SIZE` can be calculated using [`buffer_len`].
60pub type DisplayBlackWhite<const WIDTH: u32, const HEIGHT: u32, const BUFFER_SIZE: usize> =
61    Display<WIDTH, HEIGHT, BUFFER_SIZE, Color>;
62
63/// Generically-sized tri-color display buffer.
64///
65/// `WIDTH` must be a multiple of 8. `BUFFER_SIZE` can be calculated using [`buffer_len`].
66pub type DisplayTriColor<const WIDTH: u32, const HEIGHT: u32, const BUFFER_SIZE: usize> =
67    Display<WIDTH, HEIGHT, BUFFER_SIZE, TriColor>;
68
69impl<const WIDTH: u32, const HEIGHT: u32, const BUFFER_SIZE: usize>
70    Display<WIDTH, HEIGHT, BUFFER_SIZE, Color>
71{
72    /// Creates a new display buffer filled with the default color.
73    pub fn new() -> Self {
74        Self {
75            buffer: [Color::default().byte_value().0; BUFFER_SIZE],
76            rotation: Default::default(),
77            _color: core::marker::PhantomData,
78        }
79    }
80
81    /// Get the internal buffer.
82    pub fn buffer(&self) -> &[u8] {
83        &self.buffer
84    }
85
86    /// Clear the display buffer with the given color.
87    pub fn clear(&mut self, color: Color) {
88        self.buffer.fill(color.byte_value().0);
89    }
90}
91
92impl<const WIDTH: u32, const HEIGHT: u32, const BUFFER_SIZE: usize> Default
93    for Display<WIDTH, HEIGHT, BUFFER_SIZE, Color>
94{
95    fn default() -> Self {
96        Self::new()
97    }
98}
99
100impl<const WIDTH: u32, const HEIGHT: u32, const BUFFER_SIZE: usize>
101    Display<WIDTH, HEIGHT, BUFFER_SIZE, TriColor>
102{
103    /// Creates a new display buffer filled with the default color.
104    pub fn new() -> Self {
105        let background_color = TriColor::default();
106
107        let mut buffer = [0; BUFFER_SIZE];
108        buffer[..(BUFFER_SIZE / 2)].fill(background_color.byte_value().0);
109        buffer[(BUFFER_SIZE / 2)..].fill(background_color.byte_value().1);
110
111        Self {
112            buffer,
113            rotation: Default::default(),
114            _color: core::marker::PhantomData,
115        }
116    }
117
118    /// Get the internal B/W buffer.
119    pub fn bw_buffer(&self) -> &[u8] {
120        &self.buffer[..(BUFFER_SIZE / 2)]
121    }
122
123    /// Get the internal red buffer.
124    pub fn red_buffer(&self) -> &[u8] {
125        &self.buffer[(BUFFER_SIZE / 2)..]
126    }
127
128    /// Clear the display buffer with the given color.
129    pub fn clear(&mut self, color: TriColor) {
130        self.buffer[..(BUFFER_SIZE / 2)].fill(color.byte_value().0);
131        self.buffer[(BUFFER_SIZE / 2)..].fill(color.byte_value().1);
132    }
133}
134
135impl<const WIDTH: u32, const HEIGHT: u32, const BUFFER_SIZE: usize> Default
136    for Display<WIDTH, HEIGHT, BUFFER_SIZE, TriColor>
137{
138    fn default() -> Self {
139        Self::new()
140    }
141}
142
143impl<const WIDTH: u32, const HEIGHT: u32, const BUFFER_SIZE: usize, C>
144    Display<WIDTH, HEIGHT, BUFFER_SIZE, C>
145where
146    C: ColorType + PixelColor,
147{
148    /// Get the current rotation of the display.
149    pub fn rotation(&self) -> DisplayRotation {
150        self.rotation
151    }
152
153    /// Sets the rotation of the display.
154    pub fn set_rotation(&mut self, rotation: DisplayRotation) {
155        self.rotation = rotation;
156    }
157
158    fn set_pixel(&mut self, pixel: Pixel<C>) {
159        // let rotation = self.rotation;
160        let Pixel(point, color) = pixel;
161        let Point { x, y } = point;
162
163        if outside_display(point, WIDTH, HEIGHT, self.rotation) {
164            return;
165        }
166
167        let (index, bit) =
168            pixel_position_in_buffer(x as u32, y as u32, WIDTH, HEIGHT, self.rotation);
169        let index = index as usize;
170        let (bw_bit, red_bit) = color.bit_value();
171
172        #[allow(clippy::collapsible_else_if)]
173        if C::BUFFER_COUNT == 2 {
174            if red_bit == 1 {
175                // Red buffer takes precendence over B/W buffer so no need to update B/W buffer.
176                self.buffer[index + BUFFER_SIZE / 2] |= bit;
177            } else {
178                if bw_bit == 1 {
179                    self.buffer[index] |= bit;
180                } else {
181                    self.buffer[index] &= !bit;
182                }
183                self.buffer[index + BUFFER_SIZE / 2] &= !bit;
184            }
185        } else {
186            if bw_bit == 1 {
187                self.buffer[index] |= bit;
188            } else {
189                self.buffer[index] &= !bit;
190            }
191        }
192    }
193}
194
195impl<const WIDTH: u32, const HEIGHT: u32, const BUFFER_SIZE: usize> DrawTarget
196    for Display<WIDTH, HEIGHT, BUFFER_SIZE, Color>
197{
198    type Color = Color;
199    type Error = Infallible;
200
201    fn draw_iter<I>(&mut self, pixels: I) -> Result<(), Self::Error>
202    where
203        I: IntoIterator<Item = Pixel<Self::Color>>,
204    {
205        for p in pixels.into_iter() {
206            self.set_pixel(p);
207        }
208        Ok(())
209    }
210
211    fn clear(&mut self, color: Self::Color) -> Result<(), Self::Error> {
212        self.clear(color);
213        Ok(())
214    }
215}
216
217impl<const WIDTH: u32, const HEIGHT: u32, const BUFFER_SIZE: usize> DrawTarget
218    for Display<WIDTH, HEIGHT, BUFFER_SIZE, TriColor>
219{
220    type Color = TriColor;
221    type Error = Infallible;
222
223    fn draw_iter<I>(&mut self, pixels: I) -> Result<(), Self::Error>
224    where
225        I: IntoIterator<Item = Pixel<Self::Color>>,
226    {
227        for p in pixels.into_iter() {
228            self.set_pixel(p);
229        }
230        Ok(())
231    }
232
233    fn clear(&mut self, color: Self::Color) -> Result<(), Self::Error> {
234        self.clear(color);
235        Ok(())
236    }
237}
238
239impl PixelColor for Color {
240    type Raw = ();
241}
242
243impl PixelColor for TriColor {
244    type Raw = ();
245}
246
247impl<const WIDTH: u32, const HEIGHT: u32, const BUFFER_SIZE: usize, C> OriginDimensions
248    for Display<WIDTH, HEIGHT, BUFFER_SIZE, C>
249where
250    C: PixelColor + ColorType,
251{
252    fn size(&self) -> Size {
253        //if display is rotated 90 deg or 270 then swap height and width
254        match self.rotation() {
255            DisplayRotation::Rotate0 | DisplayRotation::Rotate180 => Size::new(WIDTH, HEIGHT),
256            DisplayRotation::Rotate90 | DisplayRotation::Rotate270 => Size::new(HEIGHT, WIDTH),
257        }
258    }
259}
260
261fn outside_display(p: Point, width: u32, height: u32, rotation: DisplayRotation) -> bool {
262    if p.x < 0 || p.y < 0 {
263        return true;
264    }
265    let (x, y) = (p.x as u32, p.y as u32);
266    match rotation {
267        DisplayRotation::Rotate0 | DisplayRotation::Rotate180 if x >= width || y >= height => true,
268        DisplayRotation::Rotate90 | DisplayRotation::Rotate270 if y >= width || x >= height => true,
269        _ => false,
270    }
271}
272
273/// Returns the position of the pixel in the (single color) buffer.
274///
275/// Return type is (byte index, bit)
276fn pixel_position_in_buffer(
277    x: u32,
278    y: u32,
279    width: u32,
280    height: u32,
281    rotation: DisplayRotation,
282) -> (u32, u8) {
283    let (nx, ny) = find_rotation(x, y, width, height, rotation);
284    (nx / 8 + bytes_per_line(width) * ny, 0x80 >> (nx % 8))
285}
286
287fn find_rotation(x: u32, y: u32, width: u32, height: u32, rotation: DisplayRotation) -> (u32, u32) {
288    match rotation {
289        DisplayRotation::Rotate0 => (x, y),
290        DisplayRotation::Rotate90 => (width - 1 - y, x),
291        DisplayRotation::Rotate180 => (width - 1 - x, height - 1 - y),
292        DisplayRotation::Rotate270 => (y, height - 1 - x),
293    }
294}
295
296const fn bytes_per_line(width: u32) -> u32 {
297    (width + 7) / 8
298}
299
300#[cfg(test)]
301mod tests {
302    use super::*;
303
304    #[test]
305    fn pixels_are_set_correctly_in_both_buffers_when_creating_new_tri_color_display() {
306        let display = Display::<8, 1, 2, TriColor>::new();
307        assert_eq!(display.buffer.len(), 2);
308
309        assert_eq!(
310            display.buffer[0], 0b1111_1111,
311            "B/W buffer has incorrect value"
312        );
313        assert_eq!(
314            display.buffer[1], 0b0000_0000,
315            "Red buffer has incorrect value"
316        );
317    }
318
319    #[test]
320    fn pixel_is_set_in_bw_buffer_when_drawing_black() {
321        let mut display = Display::<8, 1, 2, TriColor>::new();
322
323        display.set_pixel(Pixel(Point::new(0, 0), TriColor::Black));
324
325        assert_eq!(
326            display.buffer[0], 0b0111_1111,
327            "B/W buffer has incorrect value"
328        );
329        assert_eq!(
330            display.buffer[1], 0b0000_0000,
331            "Red buffer has incorrect value"
332        );
333    }
334
335    #[test]
336    fn pixel_is_set_in_both_buffers_when_drawing_red() {
337        let mut display = Display::<8, 1, 2, TriColor>::new();
338
339        display.set_pixel(Pixel(Point::new(0, 0), TriColor::Red));
340
341        assert_eq!(
342            display.buffer[0], 0b1111_1111,
343            "B/W buffer has incorrect value"
344        );
345        assert_eq!(
346            display.buffer[1], 0b1000_0000,
347            "Red buffer has incorrect value"
348        );
349    }
350
351    #[test]
352    fn pixel_is_set_in_both_buffers_when_drawing_red_then_black() {
353        let mut display = Display::<8, 1, 2, TriColor>::new();
354
355        display.set_pixel(Pixel(Point::new(0, 0), TriColor::Red));
356        display.set_pixel(Pixel(Point::new(0, 0), TriColor::Black));
357
358        assert_eq!(
359            display.buffer[0], 0b0111_1111,
360            "B/W buffer has incorrect value"
361        );
362        assert_eq!(
363            display.buffer[1], 0b0000_0000,
364            "Red buffer has incorrect value"
365        );
366    }
367
368    #[test]
369    fn pixel_is_set_in_both_buffers_when_drawing_red_black_red() {
370        let mut display = Display::<8, 1, 2, TriColor>::new();
371
372        display.set_pixel(Pixel(Point::new(0, 0), TriColor::Red));
373        display.set_pixel(Pixel(Point::new(0, 0), TriColor::Black));
374        display.set_pixel(Pixel(Point::new(0, 0), TriColor::Red));
375
376        assert_eq!(
377            display.buffer[0], 0b0111_1111,
378            "B/W buffer has incorrect value"
379        );
380        assert_eq!(
381            display.buffer[1], 0b1000_0000,
382            "Red buffer has incorrect value"
383        );
384    }
385
386    #[test]
387    fn clear_sets_both_buffers() {
388        let mut display = Display::<8, 1, 2, TriColor>::new();
389
390        display.set_pixel(Pixel(Point::new(0, 0), TriColor::Black));
391        display.set_pixel(Pixel(Point::new(0, 0), TriColor::Red));
392
393        display.clear(TriColor::White);
394        assert_eq!(
395            display.buffer[0], 0b1111_1111,
396            "B/W buffer has incorrect value"
397        );
398        assert_eq!(
399            display.buffer[1], 0b0000_0000,
400            "Red buffer has incorrect value"
401        );
402
403        display.clear(TriColor::Red);
404        assert_eq!(
405            display.buffer[0], 0b0000_0000,
406            "B/W buffer has incorrect value"
407        );
408        assert_eq!(
409            display.buffer[1], 0b1111_1111,
410            "Red buffer has incorrect value"
411        );
412    }
413}