mod pixel_map;
mod rotation;
mod window;
use core::{convert::Infallible, marker::PhantomData};
use embedded_graphics_core::{
Pixel,
draw_target::DrawTarget,
geometry::{OriginDimensions, Point, Size},
pixelcolor::BinaryColor,
primitives::Rectangle,
};
pub use pixel_map::{Landscape, PixelMap};
pub use rotation::Rotation;
pub use window::Window;
pub struct FrameBuffer<M: PixelMap, const N: usize> {
buf: [u8; N],
rotation: Rotation,
map: PhantomData<M>,
}
impl<M: PixelMap, const N: usize> FrameBuffer<M, N> {
pub const fn new() -> Self {
const { assert!(N == M::BUFFER_LEN) };
Self {
buf: [0x00; N],
rotation: Rotation::Deg0,
map: PhantomData,
}
}
#[must_use]
pub const fn with_rotation(mut self, rotation: Rotation) -> Self {
self.rotation = rotation;
self
}
pub const fn rotation(&self) -> Rotation {
self.rotation
}
pub fn set_rotation(&mut self, rotation: Rotation) {
self.rotation = rotation;
}
pub const fn logical_size(&self) -> (u32, u32) {
self.rotation.size(M::WIDTH, M::HEIGHT)
}
fn locate(&self, x: u32, y: u32) -> Option<(usize, u8)> {
let (xn, yn) = self.rotation.to_native(M::WIDTH, M::HEIGHT, x, y)?;
M::locate(xn, yn)
}
pub fn set_pixel(&mut self, x: u32, y: u32, on: bool) {
let Some((i, bit)) = self.locate(x, y) else {
return;
};
if on {
self.buf[i] |= 1 << bit;
} else {
self.buf[i] &= !(1 << bit);
}
}
pub fn get_pixel(&self, x: u32, y: u32) -> Option<bool> {
let (i, bit) = self.locate(x, y)?;
Some((self.buf[i] >> bit) & 0b1 == 1)
}
pub fn fill(&mut self, on: bool) {
self.buf.fill(if on { 0xFF } else { 0x00 });
}
pub fn as_bytes(&self) -> &[u8] {
&self.buf
}
pub fn window(&self, area: Rectangle) -> Option<Window> {
let (w, h) = self.logical_size();
let area = area.intersection(&Rectangle::new(Point::zero(), Size::new(w, h)));
let bottom_right = area.bottom_right()?;
let to_native = |p: Point| {
self.rotation
.to_native(M::WIDTH, M::HEIGHT, p.x as u32, p.y as u32)
.map(|(x, y)| Point::new(x as i32, y as i32))
};
let (a, b) = (to_native(area.top_left)?, to_native(bottom_right)?);
Window::covering::<M>(Rectangle::with_corners(
Point::new(a.x.min(b.x), a.y.min(b.y)),
Point::new(a.x.max(b.x), a.y.max(b.y)),
))
}
pub fn copy_window<'a>(&self, window: &Window, out: &'a mut [u8]) -> &'a [u8] {
window.copy_from(&self.buf, M::HEIGHT, out)
}
pub fn as_bytes_mut(&mut self) -> &mut [u8] {
&mut self.buf
}
pub fn fill_with(&mut self, mut f: impl FnMut() -> u8) {
self.buf.iter_mut().for_each(|b| *b = f());
}
pub fn read_row(&self, y: u32, out: &mut [bool]) -> bool {
let (w, h) = self.logical_size();
assert!(y < h && out.len() == w as usize);
match self.rotation {
Rotation::Deg0 => M::read_row(&self.buf, y, out),
Rotation::Deg180 => {
let any = M::read_row(&self.buf, M::HEIGHT - 1 - y, out);
out.reverse();
any
}
Rotation::Deg90 => M::read_col(&self.buf, M::WIDTH - 1 - y, out),
Rotation::Deg270 => {
let any = M::read_col(&self.buf, y, out);
out.reverse();
any
}
}
}
pub fn write_row(&mut self, y: u32, row: &[bool]) {
let (w, h) = self.logical_size();
assert!(y < h && row.len() == w as usize);
match self.rotation {
Rotation::Deg0 => M::write_row(&mut self.buf, y, row),
Rotation::Deg180 => M::write_row_rev(&mut self.buf, M::HEIGHT - 1 - y, row),
Rotation::Deg90 => M::write_col(&mut self.buf, M::WIDTH - 1 - y, row),
Rotation::Deg270 => M::write_col_rev(&mut self.buf, y, row),
}
}
}
impl<M: PixelMap, const N: usize> core::fmt::Debug for FrameBuffer<M, N> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("FrameBuffer")
.field("width", &M::WIDTH)
.field("height", &M::HEIGHT)
.field("rotation", &self.rotation)
.finish_non_exhaustive()
}
}
impl<M: PixelMap, const N: usize> Default for FrameBuffer<M, N> {
fn default() -> Self {
Self::new()
}
}
impl<M: PixelMap, const N: usize> OriginDimensions for FrameBuffer<M, N> {
fn size(&self) -> Size {
let (w, h) = self.logical_size();
Size::new(w, h)
}
}
impl<M: PixelMap, const N: usize> DrawTarget for FrameBuffer<M, N> {
type Color = BinaryColor;
type Error = Infallible;
fn draw_iter<I>(&mut self, pixels: I) -> Result<(), Self::Error>
where
I: IntoIterator<Item = Pixel<Self::Color>>,
{
for Pixel(Point { x, y }, color) in pixels {
if let (Ok(x), Ok(y)) = (u32::try_from(x), u32::try_from(y)) {
self.set_pixel(x, y, color.is_on());
}
}
Ok(())
}
fn clear(&mut self, color: Self::Color) -> Result<(), Self::Error> {
self.fill(color.is_on());
Ok(())
}
}