use crate::color::Color;
use crate::{HEIGHT, WIDTH};
use embedded_graphics::{pixelcolor::BinaryColor, prelude::*};
#[derive(Clone, Copy)]
pub enum DisplayRotation {
Rotate0,
Rotate90,
Rotate180,
Rotate270,
}
impl Default for DisplayRotation {
fn default() -> Self {
DisplayRotation::Rotate0
}
}
pub trait Display: DrawTarget<BinaryColor> {
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(())
}
}
pub struct Display1in54 {
buffer: [u8; WIDTH as usize * HEIGHT as usize / 8],
rotation: DisplayRotation,
is_inverted: bool,
}
impl Display1in54 {
pub fn bw() -> Self {
Display1in54 {
buffer: [Color::White.get_byte_value(); buffer_len(WIDTH as usize, HEIGHT as usize)],
rotation: DisplayRotation::default(),
is_inverted: true,
}
}
pub fn red() -> Self {
Display1in54 {
buffer: [Color::White.inverse().get_byte_value();
buffer_len(WIDTH as usize, HEIGHT as usize)],
rotation: DisplayRotation::default(),
is_inverted: false,
}
}
}
impl DrawTarget<BinaryColor> for Display1in54 {
type Error = core::convert::Infallible;
fn draw_pixel(&mut self, pixel: Pixel<BinaryColor>) -> Result<(), Self::Error> {
self.draw_helper(u32::from(WIDTH), u32::from(HEIGHT), pixel)
}
fn size(&self) -> Size {
Size::new(u32::from(WIDTH), u32::from(HEIGHT))
}
}
impl Display for Display1in54 {
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 is_inverted(&self) -> bool {
self.is_inverted
}
fn rotation(&self) -> DisplayRotation {
self.rotation
}
}
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 + 7) / 8) * ny,
0x80 >> (nx % 8),
)
}
#[must_use]
const fn buffer_len(width: usize, height: usize) -> usize {
(width + 7) / 8 * height
}
#[cfg(test)]
mod tests {
use super::{find_position, outside_display, Display, Display1in54, DisplayRotation};
use crate::color::Black;
use crate::color::Color;
use embedded_graphics::{prelude::*, primitives::Line, style::PrimitiveStyle};
#[test]
fn buffer_clear() {
let mut display = Display1in54::bw();
for &byte in display.buffer.iter() {
assert_eq!(byte, Color::White.get_byte_value());
}
display.clear_buffer(Color::White);
for &byte in display.buffer.iter() {
assert_eq!(byte, Color::Black.get_byte_value());
}
}
#[test]
fn rotation_overflow() {
use crate::{HEIGHT, WIDTH};
let width = WIDTH as u32;
let height = HEIGHT as u32;
test_rotation_overflow(width, height, DisplayRotation::Rotate0);
test_rotation_overflow(width, height, DisplayRotation::Rotate90);
test_rotation_overflow(width, height, DisplayRotation::Rotate180);
test_rotation_overflow(width, height, DisplayRotation::Rotate270);
}
fn test_rotation_overflow(width: u32, height: u32, rotation2: DisplayRotation) {
let max_value = width / 8 * height;
for x in 0..(width + height) {
for y in 0..(u32::max_value()) {
if outside_display(Point::new(x as i32, y as i32), width, height, rotation2) {
break;
} else {
let (idx, _) = find_position(x, y, width, height, rotation2);
assert!(idx < max_value);
}
}
}
}
#[test]
fn graphics_rotation_0() {
let mut display = Display1in54::bw();
let _ = Line::new(Point::new(0, 0), Point::new(7, 0))
.into_styled(PrimitiveStyle::with_stroke(Black, 1))
.draw(&mut display);
let buffer = display.buffer();
assert_eq!(buffer[0], Color::Black.get_byte_value());
for &byte in buffer.iter().skip(1) {
assert_eq!(byte, Color::White.get_byte_value());
}
}
}