From c25d9f18d323cb689b26594410c390632045f03e Mon Sep 17 00:00:00 2001 From: Reinier Balt Date: Sun, 30 May 2021 14:20:02 +0200 Subject: [PATCH] Enable drawing in three colors for epd2in13 Move from BinaryColor to TriColor: use one Display fo drawing --- examples/epd2in13bc.rs | 53 +++++++++++++----------- src/color.rs | 22 ++++++++++ src/epd2in13bc/graphics.rs | 33 +++++++++++---- src/epd2in13bc/mod.rs | 15 +++---- src/graphics.rs | 85 +++++++++++++++++++++++++++++++++++++- 5 files changed, 166 insertions(+), 42 deletions(-) diff --git a/examples/epd2in13bc.rs b/examples/epd2in13bc.rs index 45a48a3..69a7ffc 100644 --- a/examples/epd2in13bc.rs +++ b/examples/epd2in13bc.rs @@ -11,7 +11,7 @@ use embedded_hal::prelude::*; use epd_waveshare::{ color::*, epd2in13bc::{Display2in13bc, Epd2in13bc}, - graphics::{Display, DisplayRotation}, + graphics::{DisplayRotation, TriDisplay}, prelude::*, }; use linux_embedded_hal::{ @@ -77,7 +77,7 @@ fn main() -> Result<(), std::io::Error> { println!("Test all the rotations"); let mut display = Display2in13bc::default(); - let mut display_chromatic = Display2in13bc::default(); + display.clear_buffer(TriColor::White); display.set_rotation(DisplayRotation::Rotate0); draw_text(&mut display, "Rotation 0!", 5, 50); @@ -91,59 +91,62 @@ fn main() -> Result<(), std::io::Error> { display.set_rotation(DisplayRotation::Rotate270); draw_text(&mut display, "Rotation 270!", 5, 50); + // Since we only used black and white, we can resort to updating only + // the bw-buffer of this tri-color screen + epd2in13 - .update_and_display_frame(&mut spi, &display.buffer(), &mut delay) + .update_and_display_frame(&mut spi, &display.bw_buffer(), &mut delay) .expect("display frame new graphics"); println!("First frame done. Waiting 5s"); delay.delay_ms(5000u16); - println!("Now test new graphics with default rotation:"); - display.clear_buffer(Color::White); - display_chromatic.clear_buffer(Color::White); - // keep both displays on same rotation - display_chromatic.set_rotation(DisplayRotation::Rotate270); + println!("Now test new graphics with default rotation and three colors:"); + display.clear_buffer(TriColor::White); - // draw a analog clock + // draw a analog clock in black let _ = Circle::new(Point::new(64, 64), 40) - .into_styled(PrimitiveStyle::with_stroke(Black, 1)) + .into_styled(PrimitiveStyle::with_stroke(TriColor::Black, 1)) .draw(&mut display); let _ = Line::new(Point::new(64, 64), Point::new(30, 40)) - .into_styled(PrimitiveStyle::with_stroke(Black, 4)) + .into_styled(PrimitiveStyle::with_stroke(TriColor::Black, 4)) .draw(&mut display); let _ = Line::new(Point::new(64, 64), Point::new(80, 40)) - .into_styled(PrimitiveStyle::with_stroke(Black, 1)) + .into_styled(PrimitiveStyle::with_stroke(TriColor::Black, 1)) .draw(&mut display); - // draw text white on Red background by using the chromatic buffer + // draw text white on chromatic (red or yellow) background let _ = Text::new("It's working-WoB!", Point::new(90, 10)) .into_styled(text_style!( font = Font6x8, - text_color = White, - background_color = Black + text_color = TriColor::White, + background_color = TriColor::Chromatic )) - .draw(&mut display_chromatic); + .draw(&mut display); // use bigger/different font let _ = Text::new("It's working-WoB!", Point::new(90, 40)) .into_styled(text_style!( font = Font12x16, - text_color = White, - background_color = Black + text_color = TriColor::White, + background_color = TriColor::Chromatic )) - .draw(&mut display_chromatic); + .draw(&mut display); - epd2in13.update_color_frame(&mut spi, &display.buffer(), &display_chromatic.buffer())?; + // we used three colors, so we need to update both bw-buffer and chromatic-buffer + + epd2in13.update_color_frame(&mut spi, display.bw_buffer(), display.chromatic_buffer())?; epd2in13 .display_frame(&mut spi, &mut delay) .expect("display frame new graphics"); + println!("Second frame done. Waiting 5s"); delay.delay_ms(5000u16); - display.clear_buffer(Color::White); - display_chromatic.clear_buffer(Color::White); - epd2in13.update_color_frame(&mut spi, &display.buffer(), &display_chromatic.buffer())?; + // clear both bw buffer and chromatic buffer + display.clear_buffer(TriColor::White); + epd2in13.update_color_frame(&mut spi, display.bw_buffer(), display.chromatic_buffer())?; epd2in13.display_frame(&mut spi, &mut delay)?; println!("Finished tests - going to sleep"); @@ -154,8 +157,8 @@ fn draw_text(display: &mut Display2in13bc, text: &str, x: i32, y: i32) { let _ = Text::new(text, Point::new(x, y)) .into_styled(text_style!( font = Font6x8, - text_color = Black, - background_color = White + text_color = TriColor::Black, + background_color = TriColor::White )) .draw(display); } diff --git a/src/color.rs b/src/color.rs index 754b411..6918dcb 100644 --- a/src/color.rs +++ b/src/color.rs @@ -169,6 +169,28 @@ impl From for Color { } } +impl TriColor { + /// Get the color encoding of the color for one bit + pub fn get_bit_value(self) -> u8 { + match self { + TriColor::White => 1u8, + TriColor::Black | TriColor::Chromatic => 0u8, + } + } + + /// Gets a full byte of black or white pixels + pub fn get_byte_value(self) -> u8 { + match self { + TriColor::White => 0xff, + TriColor::Black | TriColor::Chromatic => 0x00, + } + } +} + +impl PixelColor for TriColor { + type Raw = (); +} + #[cfg(test)] mod tests { use super::*; diff --git a/src/epd2in13bc/graphics.rs b/src/epd2in13bc/graphics.rs index 10ca19a..80ce6d1 100644 --- a/src/epd2in13bc/graphics.rs +++ b/src/epd2in13bc/graphics.rs @@ -1,30 +1,33 @@ +use crate::color::TriColor; use crate::epd2in13bc::{DEFAULT_BACKGROUND_COLOR, HEIGHT, NUM_DISPLAY_BITS, WIDTH}; -use crate::graphics::{Display, DisplayRotation}; -use embedded_graphics::pixelcolor::BinaryColor; +use crate::graphics::{DisplayRotation, TriDisplay}; use embedded_graphics::prelude::*; -/// Full size buffer for use with the 2in13b/c EPD +/// Full size buffer for use with the 2.13" b/c EPD /// /// Can also be manually constructed and be used together with VarDisplay pub struct Display2in13bc { - buffer: [u8; NUM_DISPLAY_BITS as usize], + // one buffer for both b/w and for chromatic: + // * &buffer[0..NUM_DISPLAY_BITS] for b/w buffer and + // * &buffer[NUM_DISPLAY_BITS..2*NUM_DISPLAY_BITS] for chromatic buffer + buffer: [u8; 2 * NUM_DISPLAY_BITS as usize], rotation: DisplayRotation, } impl Default for Display2in13bc { fn default() -> Self { Display2in13bc { - buffer: [DEFAULT_BACKGROUND_COLOR.get_byte_value(); NUM_DISPLAY_BITS as usize], + buffer: [DEFAULT_BACKGROUND_COLOR.get_byte_value(); 2 * NUM_DISPLAY_BITS as usize], rotation: DisplayRotation::default(), } } } -impl DrawTarget for Display2in13bc { +impl DrawTarget for Display2in13bc { type Error = core::convert::Infallible; - fn draw_pixel(&mut self, pixel: Pixel) -> Result<(), Self::Error> { - self.draw_helper(WIDTH, HEIGHT, pixel) + fn draw_pixel(&mut self, pixel: Pixel) -> Result<(), Self::Error> { + self.draw_helper_tri(WIDTH, HEIGHT, pixel) } fn size(&self) -> Size { @@ -32,7 +35,7 @@ impl DrawTarget for Display2in13bc { } } -impl Display for Display2in13bc { +impl TriDisplay for Display2in13bc { fn buffer(&self) -> &[u8] { &self.buffer } @@ -48,4 +51,16 @@ impl Display for Display2in13bc { fn rotation(&self) -> DisplayRotation { self.rotation } + + fn chromatic_offset(&self) -> usize { + NUM_DISPLAY_BITS as usize + } + + fn bw_buffer(&self) -> &[u8] { + &self.buffer[0..self.chromatic_offset()] + } + + fn chromatic_buffer(&self) -> &[u8] { + &self.buffer[self.chromatic_offset()..] + } } diff --git a/src/epd2in13bc/mod.rs b/src/epd2in13bc/mod.rs index a9265e2..9c11cbf 100644 --- a/src/epd2in13bc/mod.rs +++ b/src/epd2in13bc/mod.rs @@ -70,8 +70,9 @@ pub const WIDTH: u32 = 104; /// Height of epd2in13bc in pixels pub const HEIGHT: u32 = 212; /// Default background color (white) of epd2in13bc display -pub const DEFAULT_BACKGROUND_COLOR: Color = Color::White; +pub const DEFAULT_BACKGROUND_COLOR: TriColor = TriColor::White; +/// Number of bits for b/w buffer and same for chromatic buffer const NUM_DISPLAY_BITS: u32 = WIDTH * HEIGHT / 8; const IS_BUSY_LOW: bool = true; @@ -81,7 +82,7 @@ const BLACK_BORDER: u8 = 0x30; const CHROMATIC_BORDER: u8 = 0xb0; const FLOATING_BORDER: u8 = 0xF0; -use crate::color::{Color, TriColor}; +use crate::color::TriColor; pub(crate) mod command; use self::command::Command; @@ -91,10 +92,10 @@ mod graphics; #[cfg(feature = "graphics")] pub use self::graphics::Display2in13bc; -/// Epd2in9bc driver +/// Epd2in13bc driver pub struct Epd2in13bc { interface: DisplayInterface, - color: Color, + color: TriColor, } impl InternalWiAdditions @@ -196,7 +197,7 @@ where RST: OutputPin, DELAY: DelayMs, { - type DisplayColor = Color; + type DisplayColor = TriColor; fn new( spi: &mut SPI, cs: CS, @@ -236,11 +237,11 @@ where self.init(spi, delay) } - fn set_background_color(&mut self, color: Color) { + fn set_background_color(&mut self, color: TriColor) { self.color = color; } - fn background_color(&self) -> &Color { + fn background_color(&self) -> &TriColor { &self.color } diff --git a/src/graphics.rs b/src/graphics.rs index c7b752e..a752e58 100644 --- a/src/graphics.rs +++ b/src/graphics.rs @@ -1,7 +1,7 @@ //! Graphics Support for EPDs use crate::buffer_len; -use crate::color::{Color, OctColor}; +use crate::color::{Color, OctColor, TriColor}; use embedded_graphics::{pixelcolor::BinaryColor, prelude::*}; /// Displayrotation @@ -85,6 +85,89 @@ pub trait Display: DrawTarget { } } +/// Necessary traits for all displays to implement for drawing +/// +/// Adds support for: +/// - Drawing (With the help of DrawTarget/Embedded Graphics) +/// - Rotations +/// - Clearing +pub trait TriDisplay: DrawTarget { + /// Clears the buffer of the display with the chosen background color + fn clear_buffer(&mut self, background_color: TriColor) { + for elem in self.get_mut_buffer().iter_mut() { + *elem = background_color.get_byte_value(); + } + } + + /// Returns the buffer + fn buffer(&self) -> &[u8]; + + /// Returns a mutable buffer + fn get_mut_buffer(&mut self) -> &mut [u8]; + + /// Sets the rotation of the display + fn set_rotation(&mut self, rotation: DisplayRotation); + + /// Get the current rotation of the display + fn rotation(&self) -> DisplayRotation; + + /// Get the offset into buffer where chromatic data starts + fn chromatic_offset(&self) -> usize; + + /// return the b/w part of the buffer + fn bw_buffer(&self) -> &[u8]; + + /// return the chromatic part of the buffer + fn chromatic_buffer(&self) -> &[u8]; + + /// Helperfunction for the Embedded Graphics draw trait + /// + /// Becomes uneccesary when const_generics become stablised + fn draw_helper_tri( + &mut self, + width: u32, + height: u32, + pixel: Pixel, + ) -> Result<(), Self::Error> { + let rotation = self.rotation(); + + let Pixel(point, color) = pixel; + if outside_display(point, width, height, rotation) { + return Ok(()); + } + + // Give us index inside the buffer and the bit-position in that u8 which needs to be changed + let (index, bit) = find_position(point.x as u32, point.y as u32, width, height, rotation); + let index = index as usize; + let offset = self.chromatic_offset(); + + let buffer = self.get_mut_buffer(); + + // "Draw" the Pixel on that bit + match color { + TriColor::Black => { + // clear bit in bw-buffer -> black + buffer[index] &= !bit; + // set bit in chromatic-buffer -> white + buffer[index+offset] |= bit; + } + TriColor::White => { + // set bit in bw-buffer -> white + buffer[index] |= bit; + // set bit in chromatic-buffer -> white + buffer[index+offset] |= bit; + } + TriColor::Chromatic => { + // set bit in b/w buffer (white) + buffer[index] |= bit; + // clear bit in chromatic buffer -> chromatic + buffer[index+offset] &= !bit; + } + } + Ok(()) + } +} + /// Necessary traits for all displays to implement for drawing /// /// Adds support for: