epd_waveshare/
graphics.rs

1//! Graphics Support for EPDs
2
3use crate::color::{ColorType, TriColor};
4use core::marker::PhantomData;
5use embedded_graphics_core::prelude::*;
6
7/// Display rotation, only 90° increments supported
8#[derive(Clone, Copy, Default)]
9pub enum DisplayRotation {
10    /// No rotation
11    #[default]
12    Rotate0,
13    /// Rotate by 90 degrees clockwise
14    Rotate90,
15    /// Rotate by 180 degrees clockwise
16    Rotate180,
17    /// Rotate 270 degrees clockwise
18    Rotate270,
19}
20
21/// count the number of bytes per line knowing that it may contains padding bits
22const fn line_bytes(width: u32, bits_per_pixel: usize) -> usize {
23    // round to upper 8 bit count
24    (width as usize * bits_per_pixel + 7) / 8
25}
26
27/// Display buffer used for drawing with embedded graphics
28/// This can be rendered on EPD using ...
29///
30/// - WIDTH: width in pixel when display is not rotated
31/// - HEIGHT: height in pixel when display is not rotated
32/// - BWRBIT: mandatory value of the B/W when chromatic bit is set, can be any value for non
33///           tricolor epd
34/// - COLOR: color type used by the target display
35/// - BYTECOUNT: This is redundant with previous data and should be removed when const generic
36///              expressions are stabilized
37///
38/// More on BWRBIT:
39///
40/// Different chromatic displays differently treat the bits in chromatic color planes.
41/// Some of them ([crate::epd2in13bc]) will render a color pixel if bit is set for that pixel,
42/// which is a `BWRBIT = true` mode.
43///
44/// Other displays, like [crate::epd5in83b_v2] in opposite, will draw color pixel if bit is
45/// cleared for that pixel, which is a `BWRBIT = false` mode.
46///
47/// BWRBIT=true: chromatic doesn't override white, white bit cleared for black, white bit set for white, both bits set for chromatic
48/// BWRBIT=false: chromatic does override white, both bits cleared for black, white bit set for white, red bit set for black
49pub struct Display<
50    const WIDTH: u32,
51    const HEIGHT: u32,
52    const BWRBIT: bool,
53    const BYTECOUNT: usize,
54    COLOR: ColorType + PixelColor,
55> {
56    buffer: [u8; BYTECOUNT],
57    rotation: DisplayRotation,
58    _color: PhantomData<COLOR>,
59}
60
61impl<
62        const WIDTH: u32,
63        const HEIGHT: u32,
64        const BWRBIT: bool,
65        const BYTECOUNT: usize,
66        COLOR: ColorType + PixelColor,
67    > Default for Display<WIDTH, HEIGHT, BWRBIT, BYTECOUNT, COLOR>
68{
69    /// Initialize display with the color '0', which may not be the same on all device.
70    /// Many devices have a bit parameter polarity that should be changed if this is not the right
71    /// one.
72    /// However, every device driver should implement a DEFAULT_COLOR constant to indicate which
73    /// color this represents (TODO)
74    ///
75    /// If you want a specific default color, you can still call clear() to set one.
76    // inline is necessary here to allow heap allocation via Box on stack limited programs
77    #[inline(always)]
78    fn default() -> Self {
79        Self {
80            // default color must be 0 for every bit in a pixel to make this work everywere
81            buffer: [0u8; BYTECOUNT],
82            rotation: DisplayRotation::default(),
83            _color: PhantomData,
84        }
85    }
86}
87
88/// For use with embedded_grahics
89impl<
90        const WIDTH: u32,
91        const HEIGHT: u32,
92        const BWRBIT: bool,
93        const BYTECOUNT: usize,
94        COLOR: ColorType + PixelColor,
95    > DrawTarget for Display<WIDTH, HEIGHT, BWRBIT, BYTECOUNT, COLOR>
96{
97    type Color = COLOR;
98    type Error = core::convert::Infallible;
99
100    fn draw_iter<I>(&mut self, pixels: I) -> Result<(), Self::Error>
101    where
102        I: IntoIterator<Item = Pixel<Self::Color>>,
103    {
104        for pixel in pixels {
105            self.set_pixel(pixel);
106        }
107        Ok(())
108    }
109}
110
111/// For use with embedded_grahics
112impl<
113        const WIDTH: u32,
114        const HEIGHT: u32,
115        const BWRBIT: bool,
116        const BYTECOUNT: usize,
117        COLOR: ColorType + PixelColor,
118    > OriginDimensions for Display<WIDTH, HEIGHT, BWRBIT, BYTECOUNT, COLOR>
119{
120    fn size(&self) -> Size {
121        match self.rotation {
122            DisplayRotation::Rotate0 | DisplayRotation::Rotate180 => Size::new(WIDTH, HEIGHT),
123            DisplayRotation::Rotate90 | DisplayRotation::Rotate270 => Size::new(HEIGHT, WIDTH),
124        }
125    }
126}
127
128impl<
129        const WIDTH: u32,
130        const HEIGHT: u32,
131        const BWRBIT: bool,
132        const BYTECOUNT: usize,
133        COLOR: ColorType + PixelColor,
134    > Display<WIDTH, HEIGHT, BWRBIT, BYTECOUNT, COLOR>
135{
136    /// get internal buffer to use it (to draw in epd)
137    pub fn buffer(&self) -> &[u8] {
138        &self.buffer
139    }
140
141    /// Set the display rotation.
142    ///
143    /// This only concerns future drawing made to it. Anything aready drawn
144    /// stays as it is in the buffer.
145    pub fn set_rotation(&mut self, rotation: DisplayRotation) {
146        self.rotation = rotation;
147    }
148
149    /// Get current rotation
150    pub fn rotation(&self) -> DisplayRotation {
151        self.rotation
152    }
153
154    /// Set a specific pixel color on this display
155    pub fn set_pixel(&mut self, pixel: Pixel<COLOR>) {
156        set_pixel(
157            &mut self.buffer,
158            WIDTH,
159            HEIGHT,
160            self.rotation,
161            BWRBIT,
162            pixel,
163        );
164    }
165}
166
167/// Some Tricolor specifics
168impl<const WIDTH: u32, const HEIGHT: u32, const BWRBIT: bool, const BYTECOUNT: usize>
169    Display<WIDTH, HEIGHT, BWRBIT, BYTECOUNT, TriColor>
170{
171    /// get black/white internal buffer to use it (to draw in epd)
172    pub fn bw_buffer(&self) -> &[u8] {
173        &self.buffer[..self.buffer.len() / 2]
174    }
175
176    /// get chromatic internal buffer to use it (to draw in epd)
177    pub fn chromatic_buffer(&self) -> &[u8] {
178        &self.buffer[self.buffer.len() / 2..]
179    }
180}
181
182/// Same as `Display`, except that its characteristics are defined at runtime.
183/// See display for documentation as everything is the same except that default
184/// is replaced by a `new` method.
185pub struct VarDisplay<'a, COLOR: ColorType + PixelColor> {
186    width: u32,
187    height: u32,
188    bwrbit: bool,
189    buffer: &'a mut [u8],
190    rotation: DisplayRotation,
191    _color: PhantomData<COLOR>,
192}
193
194/// For use with embedded_grahics
195impl<'a, COLOR: ColorType + PixelColor> DrawTarget for VarDisplay<'a, COLOR> {
196    type Color = COLOR;
197    type Error = core::convert::Infallible;
198
199    fn draw_iter<I>(&mut self, pixels: I) -> Result<(), Self::Error>
200    where
201        I: IntoIterator<Item = Pixel<Self::Color>>,
202    {
203        for pixel in pixels {
204            self.set_pixel(pixel);
205        }
206        Ok(())
207    }
208}
209
210/// For use with embedded_grahics
211impl<'a, COLOR: ColorType + PixelColor> OriginDimensions for VarDisplay<'a, COLOR> {
212    fn size(&self) -> Size {
213        match self.rotation {
214            DisplayRotation::Rotate0 | DisplayRotation::Rotate180 => {
215                Size::new(self.width, self.height)
216            }
217            DisplayRotation::Rotate90 | DisplayRotation::Rotate270 => {
218                Size::new(self.height, self.width)
219            }
220        }
221    }
222}
223
224/// Error found during usage of VarDisplay
225#[derive(Debug)]
226pub enum VarDisplayError {
227    /// The provided buffer was too small
228    BufferTooSmall,
229}
230
231impl<'a, COLOR: ColorType + PixelColor> VarDisplay<'a, COLOR> {
232    /// You must allocate the buffer by yourself, it must be large enough to contain all pixels.
233    ///
234    /// Parameters are documented in `Display` as they are the same as the const generics there.
235    /// bwrbit should be false for non tricolor displays
236    pub fn new(
237        width: u32,
238        height: u32,
239        buffer: &'a mut [u8],
240        bwrbit: bool,
241    ) -> Result<Self, VarDisplayError> {
242        let myself = Self {
243            width,
244            height,
245            bwrbit,
246            buffer,
247            rotation: DisplayRotation::default(),
248            _color: PhantomData,
249        };
250        // enfore some constraints dynamicly
251        if myself.buffer_size() > myself.buffer.len() {
252            return Err(VarDisplayError::BufferTooSmall);
253        }
254        Ok(myself)
255    }
256
257    /// get the number of used bytes in the buffer
258    fn buffer_size(&self) -> usize {
259        self.height as usize
260            * line_bytes(
261                self.width,
262                COLOR::BITS_PER_PIXEL_PER_BUFFER * COLOR::BUFFER_COUNT,
263            )
264    }
265
266    /// get internal buffer to use it (to draw in epd)
267    pub fn buffer(&self) -> &[u8] {
268        &self.buffer[..self.buffer_size()]
269    }
270
271    /// Set the display rotation.
272    ///
273    /// This only concerns future drawing made to it. Anything aready drawn
274    /// stays as it is in the buffer.
275    pub fn set_rotation(&mut self, rotation: DisplayRotation) {
276        self.rotation = rotation;
277    }
278
279    /// Get current rotation
280    pub fn rotation(&self) -> DisplayRotation {
281        self.rotation
282    }
283
284    /// Set a specific pixel color on this display
285    pub fn set_pixel(&mut self, pixel: Pixel<COLOR>) {
286        let size = self.buffer_size();
287        set_pixel(
288            &mut self.buffer[..size],
289            self.width,
290            self.height,
291            self.rotation,
292            self.bwrbit,
293            pixel,
294        );
295    }
296}
297
298/// Some Tricolor specifics
299impl<'a> VarDisplay<'a, TriColor> {
300    /// get black/white internal buffer to use it (to draw in epd)
301    pub fn bw_buffer(&self) -> &[u8] {
302        &self.buffer[..self.buffer_size() / 2]
303    }
304
305    /// get chromatic internal buffer to use it (to draw in epd)
306    pub fn chromatic_buffer(&self) -> &[u8] {
307        &self.buffer[self.buffer_size() / 2..self.buffer_size()]
308    }
309}
310
311// This is a function to share code between `Display` and `VarDisplay`
312// It sets a specific pixel in a buffer to a given color.
313// The big number of parameters is due to the fact that it is an internal function to both
314// strctures.
315fn set_pixel<COLOR: ColorType + PixelColor>(
316    buffer: &mut [u8],
317    width: u32,
318    height: u32,
319    rotation: DisplayRotation,
320    bwrbit: bool,
321    pixel: Pixel<COLOR>,
322) {
323    let Pixel(point, color) = pixel;
324
325    // final coordinates
326    let (x, y) = match rotation {
327        // as i32 = never use more than 2 billion pixel per line or per column
328        DisplayRotation::Rotate0 => (point.x, point.y),
329        DisplayRotation::Rotate90 => (width as i32 - 1 - point.y, point.x),
330        DisplayRotation::Rotate180 => (width as i32 - 1 - point.x, height as i32 - 1 - point.y),
331        DisplayRotation::Rotate270 => (point.y, height as i32 - 1 - point.x),
332    };
333
334    // Out of range check
335    if (x < 0) || (x >= width as i32) || (y < 0) || (y >= height as i32) {
336        // don't do anything in case of out of range
337        return;
338    }
339
340    let index = x as usize * COLOR::BITS_PER_PIXEL_PER_BUFFER / 8
341        + y as usize * line_bytes(width, COLOR::BITS_PER_PIXEL_PER_BUFFER);
342    let (mask, bits) = color.bitmask(bwrbit, x as u32);
343
344    if COLOR::BUFFER_COUNT == 2 {
345        // split buffer is for tricolor displays that use 2 buffer for 2 bits per pixel
346        buffer[index] = buffer[index] & mask | (bits & 0xFF) as u8;
347        let index = index + buffer.len() / 2;
348        buffer[index] = buffer[index] & mask | (bits >> 8) as u8;
349    } else {
350        buffer[index] = buffer[index] & mask | bits as u8;
351    }
352}
353
354#[cfg(test)]
355mod tests {
356    use super::*;
357    use crate::color::*;
358    use embedded_graphics::{
359        prelude::*,
360        primitives::{Line, PrimitiveStyle},
361    };
362
363    // test buffer length
364    #[test]
365    fn graphics_size() {
366        // example definition taken from epd1in54
367        let display = Display::<200, 200, false, { 200 * 200 / 8 }, Color>::default();
368        assert_eq!(display.buffer().len(), 5000);
369    }
370
371    // test default background color on all bytes
372    #[test]
373    fn graphics_default() {
374        let display = Display::<200, 200, false, { 200 * 200 / 8 }, Color>::default();
375        for &byte in display.buffer() {
376            assert_eq!(byte, 0);
377        }
378    }
379
380    #[test]
381    fn graphics_rotation_0() {
382        let mut display = Display::<200, 200, false, { 200 * 200 / 8 }, Color>::default();
383        let _ = Line::new(Point::new(0, 0), Point::new(7, 0))
384            .into_styled(PrimitiveStyle::with_stroke(Color::Black, 1))
385            .draw(&mut display);
386
387        let buffer = display.buffer();
388
389        assert_eq!(buffer[0], Color::Black.get_byte_value());
390
391        for &byte in buffer.iter().skip(1) {
392            assert_eq!(byte, 0);
393        }
394    }
395
396    #[test]
397    fn graphics_rotation_90() {
398        let mut display = Display::<200, 200, false, { 200 * 200 / 8 }, Color>::default();
399        display.set_rotation(DisplayRotation::Rotate90);
400        let _ = Line::new(Point::new(0, 192), Point::new(0, 199))
401            .into_styled(PrimitiveStyle::with_stroke(Color::Black, 1))
402            .draw(&mut display);
403
404        let buffer = display.buffer();
405
406        assert_eq!(buffer[0], Color::Black.get_byte_value());
407
408        for &byte in buffer.iter().skip(1) {
409            assert_eq!(byte, 0);
410        }
411    }
412
413    #[test]
414    fn graphics_rotation_180() {
415        let mut display = Display::<200, 200, false, { 200 * 200 / 8 }, Color>::default();
416        display.set_rotation(DisplayRotation::Rotate180);
417        let _ = Line::new(Point::new(192, 199), Point::new(199, 199))
418            .into_styled(PrimitiveStyle::with_stroke(Color::Black, 1))
419            .draw(&mut display);
420
421        let buffer = display.buffer();
422
423        extern crate std;
424        std::println!("{:?}", buffer);
425
426        assert_eq!(buffer[0], Color::Black.get_byte_value());
427
428        for &byte in buffer.iter().skip(1) {
429            assert_eq!(byte, 0);
430        }
431    }
432
433    #[test]
434    fn graphics_rotation_270() {
435        let mut display = Display::<200, 200, false, { 200 * 200 / 8 }, Color>::default();
436        display.set_rotation(DisplayRotation::Rotate270);
437        let _ = Line::new(Point::new(199, 0), Point::new(199, 7))
438            .into_styled(PrimitiveStyle::with_stroke(Color::Black, 1))
439            .draw(&mut display);
440
441        let buffer = display.buffer();
442
443        extern crate std;
444        std::println!("{:?}", buffer);
445
446        assert_eq!(buffer[0], Color::Black.get_byte_value());
447
448        for &byte in buffer.iter().skip(1) {
449            assert_eq!(byte, 0);
450        }
451    }
452}