pub mod display290_gray4_mfgn;
pub mod display290_gray4_t5;
pub mod display290_mono;
use crate::color::Color;
use embedded_graphics::{pixelcolor::BinaryColor, prelude::*};
pub trait Display: DrawTarget {
fn clear_buffer(&mut self, background_color: Color) {
let fill_color = if self.is_inverted() {
background_color.inverse().get_byte_value()
} else {
background_color.get_byte_value()
};
for elem in self.get_mut_buffer().iter_mut() {
*elem = fill_color
}
}
fn buffer(&self) -> &[u8];
fn get_mut_buffer(&mut self) -> &mut [u8];
fn set_rotation(&mut self, rotation: DisplayRotation);
fn rotation(&self) -> DisplayRotation;
fn is_inverted(&self) -> bool;
fn draw_helper(
&mut self,
width: u32,
height: u32,
pixel: Pixel<BinaryColor>,
) -> Result<(), Self::Error> {
let rotation = self.rotation();
let is_inverted = self.is_inverted();
let buffer = self.get_mut_buffer();
let Pixel(point, color) = pixel;
if outside_display(point, width, height, rotation) {
return Ok(());
}
let (index, bit) = find_position(point.x as u32, point.y as u32, width, height, rotation);
let index = index as usize;
match color {
BinaryColor::On => {
if is_inverted {
buffer[index] &= !bit;
} else {
buffer[index] |= bit;
}
}
BinaryColor::Off => {
if is_inverted {
buffer[index] |= bit;
} else {
buffer[index] &= !bit;
}
}
}
Ok(())
}
}
#[derive(Clone, Copy, Default)]
pub enum DisplayRotation {
#[default]
Rotate0,
Rotate90,
Rotate180,
Rotate270,
}
fn outside_display(p: Point, width: u32, height: u32, rotation: DisplayRotation) -> bool {
if p.x < 0 || p.y < 0 {
return true;
}
let (x, y) = (p.x as u32, p.y as u32);
match rotation {
DisplayRotation::Rotate0 | DisplayRotation::Rotate180 => {
if x >= width || y >= height {
return true;
}
}
DisplayRotation::Rotate90 | DisplayRotation::Rotate270 => {
if y >= width || x >= height {
return true;
}
}
}
false
}
fn find_rotation(x: u32, y: u32, width: u32, height: u32, rotation: DisplayRotation) -> (u32, u32) {
let nx;
let ny;
match rotation {
DisplayRotation::Rotate0 => {
nx = x;
ny = y;
}
DisplayRotation::Rotate90 => {
nx = width - 1 - y;
ny = x;
}
DisplayRotation::Rotate180 => {
nx = width - 1 - x;
ny = height - 1 - y;
}
DisplayRotation::Rotate270 => {
nx = y;
ny = height - 1 - x;
}
}
(nx, ny)
}
#[rustfmt::skip]
fn find_position(x: u32, y: u32, width: u32, height: u32, rotation: DisplayRotation) -> (u32, u8) {
let (nx, ny) = find_rotation(x, y, width, height, rotation);
(
nx / 8 + width.div_ceil(8) * ny,
0x80 >> (nx % 8)
)
}
#[must_use]
const fn buffer_len(width: usize, height: usize) -> usize {
width.div_ceil(8) * height
}
#[cfg(test)]
mod tests {
use super::*;
use core::convert::Infallible;
use embedded_graphics::pixelcolor::BinaryColor;
use embedded_graphics::prelude::{Pixel, Point};
struct DummyDisplay {
buffer: [u8; 4736],
rotation: DisplayRotation,
inverted: bool,
width: u32,
height: u32,
}
impl DummyDisplay {
fn new(width: u32, height: u32, rotation: DisplayRotation, inverted: bool) -> Self {
Self {
buffer: [0u8; 4736],
rotation,
inverted,
width,
height,
}
}
}
impl embedded_graphics::geometry::OriginDimensions for DummyDisplay {
fn size(&self) -> embedded_graphics::prelude::Size {
match self.rotation {
DisplayRotation::Rotate0 | DisplayRotation::Rotate180 => {
embedded_graphics::prelude::Size::new(self.width, self.height)
}
DisplayRotation::Rotate90 | DisplayRotation::Rotate270 => {
embedded_graphics::prelude::Size::new(self.height, self.width)
}
}
}
}
impl embedded_graphics::draw_target::DrawTarget for DummyDisplay {
type Error = Infallible;
type Color = BinaryColor;
fn draw_iter<I>(&mut self, _pixels: I) -> Result<(), Self::Error>
where
I: IntoIterator<Item = Pixel<Self::Color>>,
{
for _ in _pixels.into_iter() {}
Ok(())
}
}
impl Display for DummyDisplay {
fn buffer(&self) -> &[u8] {
&self.buffer
}
fn get_mut_buffer(&mut self) -> &mut [u8] {
&mut self.buffer
}
fn set_rotation(&mut self, rotation: DisplayRotation) {
self.rotation = rotation;
}
fn rotation(&self) -> DisplayRotation {
self.rotation
}
fn is_inverted(&self) -> bool {
self.inverted
}
}
#[test]
fn draw_helper_sets_msb_first_bits() {
let mut d = DummyDisplay::new(296, 128, DisplayRotation::Rotate0, false);
for x in 0..8 {
let p = Pixel(Point::new(x, 0), BinaryColor::On);
let w = d.width;
let h = d.height;
Display::draw_helper(&mut d, w, h, p).unwrap();
}
assert_eq!(d.buffer[0], 0xFF);
for &b in d.buffer.iter().skip(1) {
assert_eq!(b, 0);
}
}
#[test]
fn draw_helper_sets_last_byte_in_buffer() {
let width = 296u32;
let height = 128u32;
let mut d = DummyDisplay::new(width, height, DisplayRotation::Rotate0, false);
let x = width - 1;
let y = height - 1;
Display::draw_helper(
&mut d,
width,
height,
Pixel(Point::new(x as i32, y as i32), BinaryColor::On),
)
.unwrap();
let bytes_per_row = width.div_ceil(8);
let idx = (x / 8 + bytes_per_row * y) as usize;
let bit = 0x80u8 >> (x % 8);
assert_eq!(d.buffer[idx] & bit, bit);
assert_eq!(idx, (bytes_per_row * height - 1) as usize);
}
#[test]
fn draw_helper_respects_rotation() {
let mut d = DummyDisplay::new(296, 128, DisplayRotation::Rotate90, false);
let logical_x = 1u32;
let logical_y = 1u32;
let w = d.width;
let h = d.height;
Display::draw_helper(
&mut d,
w,
h,
Pixel(
Point::new(logical_x as i32, logical_y as i32),
BinaryColor::On,
),
)
.unwrap();
let (nx, ny) = super::find_rotation(
logical_x,
logical_y,
d.width,
d.height,
DisplayRotation::Rotate90,
);
let bytes_per_row = 296u32.div_ceil(8);
let idx = (nx / 8 + bytes_per_row * ny) as usize;
let bit = 0x80u8 >> (nx % 8);
assert_eq!(d.buffer[idx] & bit, bit);
}
#[test]
fn draw_helper_inversion_behavior() {
let mut d = DummyDisplay::new(8, 1, DisplayRotation::Rotate0, true);
for b in d.buffer.iter_mut() {
*b = 0xFF;
}
let w = d.width;
let h = d.height;
Display::draw_helper(&mut d, w, h, Pixel(Point::new(0, 0), BinaryColor::On)).unwrap();
assert_eq!(d.buffer[0] & 0x80, 0);
let w = d.width;
let h = d.height;
Display::draw_helper(&mut d, w, h, Pixel(Point::new(1, 0), BinaryColor::Off)).unwrap();
assert_eq!(d.buffer[0] & 0x40, 0x40);
}
}