use crate::color::{ColorType, TriColor};
use core::marker::PhantomData;
use embedded_graphics_core::prelude::*;
#[derive(Clone, Copy, Default)]
pub enum DisplayRotation {
#[default]
Rotate0,
Rotate90,
Rotate180,
Rotate270,
}
const fn line_bytes(width: u32, bits_per_pixel: usize) -> usize {
(width as usize * bits_per_pixel + 7) / 8
}
pub struct Display<
const WIDTH: u32,
const HEIGHT: u32,
const BWRBIT: bool,
const BYTECOUNT: usize,
COLOR: ColorType + PixelColor,
> {
buffer: [u8; BYTECOUNT],
rotation: DisplayRotation,
_color: PhantomData<COLOR>,
}
impl<
const WIDTH: u32,
const HEIGHT: u32,
const BWRBIT: bool,
const BYTECOUNT: usize,
COLOR: ColorType + PixelColor,
> Default for Display<WIDTH, HEIGHT, BWRBIT, BYTECOUNT, COLOR>
{
#[inline(always)]
fn default() -> Self {
Self {
buffer: [0u8; BYTECOUNT],
rotation: DisplayRotation::default(),
_color: PhantomData,
}
}
}
impl<
const WIDTH: u32,
const HEIGHT: u32,
const BWRBIT: bool,
const BYTECOUNT: usize,
COLOR: ColorType + PixelColor,
> DrawTarget for Display<WIDTH, HEIGHT, BWRBIT, BYTECOUNT, COLOR>
{
type Color = COLOR;
type Error = core::convert::Infallible;
fn draw_iter<I>(&mut self, pixels: I) -> Result<(), Self::Error>
where
I: IntoIterator<Item = Pixel<Self::Color>>,
{
for pixel in pixels {
self.set_pixel(pixel);
}
Ok(())
}
}
impl<
const WIDTH: u32,
const HEIGHT: u32,
const BWRBIT: bool,
const BYTECOUNT: usize,
COLOR: ColorType + PixelColor,
> OriginDimensions for Display<WIDTH, HEIGHT, BWRBIT, BYTECOUNT, COLOR>
{
fn size(&self) -> Size {
match self.rotation {
DisplayRotation::Rotate0 | DisplayRotation::Rotate180 => Size::new(WIDTH, HEIGHT),
DisplayRotation::Rotate90 | DisplayRotation::Rotate270 => Size::new(HEIGHT, WIDTH),
}
}
}
impl<
const WIDTH: u32,
const HEIGHT: u32,
const BWRBIT: bool,
const BYTECOUNT: usize,
COLOR: ColorType + PixelColor,
> Display<WIDTH, HEIGHT, BWRBIT, BYTECOUNT, COLOR>
{
pub fn buffer(&self) -> &[u8] {
&self.buffer
}
pub fn set_rotation(&mut self, rotation: DisplayRotation) {
self.rotation = rotation;
}
pub fn rotation(&self) -> DisplayRotation {
self.rotation
}
pub fn set_pixel(&mut self, pixel: Pixel<COLOR>) {
set_pixel(
&mut self.buffer,
WIDTH,
HEIGHT,
self.rotation,
BWRBIT,
pixel,
);
}
}
impl<const WIDTH: u32, const HEIGHT: u32, const BWRBIT: bool, const BYTECOUNT: usize>
Display<WIDTH, HEIGHT, BWRBIT, BYTECOUNT, TriColor>
{
pub fn bw_buffer(&self) -> &[u8] {
&self.buffer[..self.buffer.len() / 2]
}
pub fn chromatic_buffer(&self) -> &[u8] {
&self.buffer[self.buffer.len() / 2..]
}
}
pub struct VarDisplay<'a, COLOR: ColorType + PixelColor> {
width: u32,
height: u32,
bwrbit: bool,
buffer: &'a mut [u8],
rotation: DisplayRotation,
_color: PhantomData<COLOR>,
}
impl<'a, COLOR: ColorType + PixelColor> DrawTarget for VarDisplay<'a, COLOR> {
type Color = COLOR;
type Error = core::convert::Infallible;
fn draw_iter<I>(&mut self, pixels: I) -> Result<(), Self::Error>
where
I: IntoIterator<Item = Pixel<Self::Color>>,
{
for pixel in pixels {
self.set_pixel(pixel);
}
Ok(())
}
}
impl<'a, COLOR: ColorType + PixelColor> OriginDimensions for VarDisplay<'a, COLOR> {
fn size(&self) -> Size {
match self.rotation {
DisplayRotation::Rotate0 | DisplayRotation::Rotate180 => {
Size::new(self.width, self.height)
}
DisplayRotation::Rotate90 | DisplayRotation::Rotate270 => {
Size::new(self.height, self.width)
}
}
}
}
#[derive(Debug)]
pub enum VarDisplayError {
BufferTooSmall,
}
impl<'a, COLOR: ColorType + PixelColor> VarDisplay<'a, COLOR> {
pub fn new(
width: u32,
height: u32,
buffer: &'a mut [u8],
bwrbit: bool,
) -> Result<Self, VarDisplayError> {
let myself = Self {
width,
height,
bwrbit,
buffer,
rotation: DisplayRotation::default(),
_color: PhantomData,
};
if myself.buffer_size() > myself.buffer.len() {
return Err(VarDisplayError::BufferTooSmall);
}
Ok(myself)
}
fn buffer_size(&self) -> usize {
self.height as usize
* line_bytes(
self.width,
COLOR::BITS_PER_PIXEL_PER_BUFFER * COLOR::BUFFER_COUNT,
)
}
pub fn buffer(&self) -> &[u8] {
&self.buffer[..self.buffer_size()]
}
pub fn set_rotation(&mut self, rotation: DisplayRotation) {
self.rotation = rotation;
}
pub fn rotation(&self) -> DisplayRotation {
self.rotation
}
pub fn set_pixel(&mut self, pixel: Pixel<COLOR>) {
let size = self.buffer_size();
set_pixel(
&mut self.buffer[..size],
self.width,
self.height,
self.rotation,
self.bwrbit,
pixel,
);
}
}
impl<'a> VarDisplay<'a, TriColor> {
pub fn bw_buffer(&self) -> &[u8] {
&self.buffer[..self.buffer_size() / 2]
}
pub fn chromatic_buffer(&self) -> &[u8] {
&self.buffer[self.buffer_size() / 2..self.buffer_size()]
}
}
fn set_pixel<COLOR: ColorType + PixelColor>(
buffer: &mut [u8],
width: u32,
height: u32,
rotation: DisplayRotation,
bwrbit: bool,
pixel: Pixel<COLOR>,
) {
let Pixel(point, color) = pixel;
let (x, y) = match rotation {
DisplayRotation::Rotate0 => (point.x, point.y),
DisplayRotation::Rotate90 => (width as i32 - 1 - point.y, point.x),
DisplayRotation::Rotate180 => (width as i32 - 1 - point.x, height as i32 - 1 - point.y),
DisplayRotation::Rotate270 => (point.y, height as i32 - 1 - point.x),
};
if (x < 0) || (x >= width as i32) || (y < 0) || (y >= height as i32) {
return;
}
let index = x as usize * COLOR::BITS_PER_PIXEL_PER_BUFFER / 8
+ y as usize * line_bytes(width, COLOR::BITS_PER_PIXEL_PER_BUFFER);
let (mask, bits) = color.bitmask(bwrbit, x as u32);
if COLOR::BUFFER_COUNT == 2 {
buffer[index] = buffer[index] & mask | (bits & 0xFF) as u8;
let index = index + buffer.len() / 2;
buffer[index] = buffer[index] & mask | (bits >> 8) as u8;
} else {
buffer[index] = buffer[index] & mask | bits as u8;
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::color::*;
use embedded_graphics::{
prelude::*,
primitives::{Line, PrimitiveStyle},
};
#[test]
fn graphics_size() {
let display = Display::<200, 200, false, { 200 * 200 / 8 }, Color>::default();
assert_eq!(display.buffer().len(), 5000);
}
#[test]
fn graphics_default() {
let display = Display::<200, 200, false, { 200 * 200 / 8 }, Color>::default();
for &byte in display.buffer() {
assert_eq!(byte, 0);
}
}
#[test]
fn graphics_rotation_0() {
let mut display = Display::<200, 200, false, { 200 * 200 / 8 }, Color>::default();
let _ = Line::new(Point::new(0, 0), Point::new(7, 0))
.into_styled(PrimitiveStyle::with_stroke(Color::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, 0);
}
}
#[test]
fn graphics_rotation_90() {
let mut display = Display::<200, 200, false, { 200 * 200 / 8 }, Color>::default();
display.set_rotation(DisplayRotation::Rotate90);
let _ = Line::new(Point::new(0, 192), Point::new(0, 199))
.into_styled(PrimitiveStyle::with_stroke(Color::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, 0);
}
}
#[test]
fn graphics_rotation_180() {
let mut display = Display::<200, 200, false, { 200 * 200 / 8 }, Color>::default();
display.set_rotation(DisplayRotation::Rotate180);
let _ = Line::new(Point::new(192, 199), Point::new(199, 199))
.into_styled(PrimitiveStyle::with_stroke(Color::Black, 1))
.draw(&mut display);
let buffer = display.buffer();
extern crate std;
std::println!("{:?}", buffer);
assert_eq!(buffer[0], Color::Black.get_byte_value());
for &byte in buffer.iter().skip(1) {
assert_eq!(byte, 0);
}
}
#[test]
fn graphics_rotation_270() {
let mut display = Display::<200, 200, false, { 200 * 200 / 8 }, Color>::default();
display.set_rotation(DisplayRotation::Rotate270);
let _ = Line::new(Point::new(199, 0), Point::new(199, 7))
.into_styled(PrimitiveStyle::with_stroke(Color::Black, 1))
.draw(&mut display);
let buffer = display.buffer();
extern crate std;
std::println!("{:?}", buffer);
assert_eq!(buffer[0], Color::Black.get_byte_value());
for &byte in buffer.iter().skip(1) {
assert_eq!(byte, 0);
}
}
}