#[cfg(feature = "graphics")]
use embedded_graphics_core::{
geometry::{Point, Size},
pixelcolor::BinaryColor,
prelude::*,
primitives::Rectangle,
};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum DisplayRotation {
#[default]
Rotate0,
Rotate90,
Rotate180,
Rotate270,
}
pub struct PageBuffer<'a> {
buffer: &'a mut [u8],
width: u32,
height: u32,
stride: usize,
y_offset: u32,
rotation: DisplayRotation,
}
impl<'a> PageBuffer<'a> {
pub fn new(buffer: &'a mut [u8], width: u32, height: u32, y_offset: u32) -> Self {
Self {
buffer,
width,
height,
stride: width.div_ceil(8) as usize,
y_offset,
rotation: DisplayRotation::Rotate0,
}
}
pub fn stride(&self) -> usize {
self.stride
}
pub fn set_rotation(&mut self, rotation: DisplayRotation) {
self.rotation = rotation;
}
pub fn rotation(&self) -> DisplayRotation {
self.rotation
}
pub fn clear_byte(&mut self, val: u8) {
self.buffer.fill(val);
}
pub fn as_slice(&self) -> &[u8] {
self.buffer
}
pub fn as_mut_slice(&mut self) -> &mut [u8] {
self.buffer
}
pub fn width(&self) -> u32 {
self.width
}
pub fn height(&self) -> u32 {
self.height
}
pub fn y_offset(&self) -> u32 {
self.y_offset
}
pub fn set_pixel(&mut self, x: u32, y: u32, black: bool) {
let (mapped_x, mapped_y) = match self.rotation {
DisplayRotation::Rotate0 => (x, y),
DisplayRotation::Rotate90 => (self.width.saturating_sub(1).saturating_sub(y), x),
DisplayRotation::Rotate180 => (
self.width.saturating_sub(1).saturating_sub(x),
self.height.saturating_sub(1).saturating_sub(y),
),
DisplayRotation::Rotate270 => (y, self.height.saturating_sub(1).saturating_sub(x)),
};
if mapped_x >= self.width
|| mapped_y < self.y_offset
|| mapped_y >= self.y_offset + self.height
{
return;
}
let local_y = mapped_y - self.y_offset;
let index = local_y as usize * self.stride + (mapped_x / 8) as usize;
let bit = 7 - (mapped_x % 8);
if index < self.buffer.len() {
if black {
self.buffer[index] &= !(1 << bit);
} else {
self.buffer[index] |= 1 << bit;
}
}
}
}
#[cfg(feature = "graphics")]
#[cfg_attr(docsrs, doc(cfg(feature = "graphics")))]
impl<'a> Dimensions for PageBuffer<'a> {
fn bounding_box(&self) -> Rectangle {
Rectangle::new(
Point::new(0, self.y_offset as i32),
Size::new(self.width, self.height),
)
}
}
#[cfg(feature = "graphics")]
#[cfg_attr(docsrs, doc(cfg(feature = "graphics")))]
impl<'a> DrawTarget for PageBuffer<'a> {
type Color = BinaryColor;
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(point, color) in pixels {
if point.x >= 0 && point.y >= 0 {
let x = point.x as u32;
let y = point.y as u32;
let black = match color {
BinaryColor::On => true,
BinaryColor::Off => false,
};
self.set_pixel(x, y, black);
}
}
Ok(())
}
}