extern crate alloc;
use alloc::boxed::Box;
use core::marker::PhantomData;
use super::St77916Color;
use crate::{ControllerInterface, DriverError, DriverResult, ResetInterface, St77916};
pub enum Framebuffer {
Static(&'static mut [u8]),
Heap(Box<[u8]>),
}
impl Framebuffer {
pub fn heap<const N: usize>() -> Self {
Framebuffer::Heap(Box::new([0u8; N]))
}
pub fn as_mut_slice(&mut self) -> &mut [u8] {
match self {
Framebuffer::Static(arr) => arr,
Framebuffer::Heap(boxed) => boxed,
}
}
pub fn as_slice(&self) -> &[u8] {
match self {
Framebuffer::Static(arr) => arr,
Framebuffer::Heap(boxed) => boxed,
}
}
pub fn len(&self) -> usize {
self.as_slice().len()
}
pub fn is_empty(&self) -> bool {
self.as_slice().is_empty()
}
}
impl core::ops::Deref for Framebuffer {
type Target = [u8];
fn deref(&self) -> &Self::Target {
self.as_slice()
}
}
impl core::ops::DerefMut for Framebuffer {
fn deref_mut(&mut self) -> &mut Self::Target {
self.as_mut_slice()
}
}
pub const fn framebuffer_size(
display: crate::DisplaySize,
color: crate::ColorMode,
) -> usize {
(display.width as usize) * (display.height as usize) * color.bytes_per_pixel()
}
#[derive(Debug, Clone, Copy)]
pub(crate) struct DirtyBand {
pub(crate) y_min: u16,
pub(crate) y_max: u16,
}
impl DirtyBand {
pub fn clean() -> Self {
Self {
y_min: u16::MAX,
y_max: 0,
}
}
#[inline]
pub fn is_clean(&self) -> bool {
self.y_min > self.y_max
}
#[inline]
pub fn mark(&mut self, y_start: u16, y_end: u16) {
self.y_min = self.y_min.min(y_start);
self.y_max = self.y_max.max(y_end);
}
#[inline]
pub fn mark_all(&mut self, height: u16) {
self.y_min = 0;
self.y_max = height - 1;
}
#[inline]
pub fn reset(&mut self) {
*self = Self::clean();
}
}
pub struct Buffered<COLOR: St77916Color = embedded_graphics_core::pixelcolor::Rgb565> {
pub(crate) data: Framebuffer,
pub(crate) dirty: DirtyBand,
pub(crate) _color: PhantomData<COLOR>,
}
pub struct DoubleBuffered<COLOR: St77916Color = embedded_graphics_core::pixelcolor::Rgb565> {
pub(crate) bufs: [Framebuffer; 2],
pub(crate) back_idx: usize,
pub(crate) _color: PhantomData<COLOR>,
}
impl<COLOR: St77916Color> DoubleBuffered<COLOR> {
pub(crate) fn back(&self) -> &Framebuffer {
&self.bufs[self.back_idx]
}
pub(crate) fn back_mut(&mut self) -> &mut Framebuffer {
&mut self.bufs[self.back_idx]
}
pub(crate) fn front(&self) -> &Framebuffer {
&self.bufs[self.back_idx ^ 1]
}
}
pub struct Unbuffered<COLOR: St77916Color = embedded_graphics_core::pixelcolor::Rgb565> {
pub(crate) _color: PhantomData<COLOR>,
}
impl<IFACE, RST, COLOR: St77916Color> St77916<IFACE, RST, Buffered<COLOR>>
where
IFACE: ControllerInterface,
RST: ResetInterface,
{
pub fn flush(&mut self) -> DriverResult<(), IFACE, RST> {
if self.buffer.dirty.is_clean() {
return Ok(());
}
let y_min = self.buffer.dirty.y_min;
let y_max = self.buffer.dirty.y_max;
let bpp = COLOR::BYTES_PER_PIXEL;
let stride = self.config.width as usize * bpp;
self.set_window(0, y_min, self.config.width - 1, y_max)?;
let start = y_min as usize * stride;
let end = (y_max as usize + 1) * stride;
self.interface
.send_pixels(&self.buffer.data[start..end])
.map_err(DriverError::InterfaceError)?;
self.buffer.dirty.reset();
Ok(())
}
pub fn full_flush(&mut self) -> DriverResult<(), IFACE, RST> {
self.set_window(0, 0, self.config.width - 1, self.config.height - 1)?;
self.interface
.send_pixels(&self.buffer.data)
.map_err(DriverError::InterfaceError)?;
self.buffer.dirty.reset();
Ok(())
}
pub fn framebuffer_mut(&mut self) -> &mut [u8] {
self.buffer.data.as_mut_slice()
}
pub fn framebuffer(&self) -> &[u8] {
self.buffer.data.as_slice()
}
pub fn mark_dirty(&mut self, y_start: u16, y_end: u16) {
self.buffer.dirty.mark(y_start, y_end);
}
pub fn mark_all_dirty(&mut self) {
self.buffer.dirty.mark_all(self.config.height);
}
pub fn is_clean(&self) -> bool {
self.buffer.dirty.is_clean()
}
pub fn partial_flush(
&mut self,
x_start: u16,
x_end: u16,
y_start: u16,
y_end: u16,
) -> DriverResult<(), IFACE, RST> {
self.set_window(x_start, y_start, x_end, y_end)?;
let bpp = COLOR::BYTES_PER_PIXEL;
let fb_stride = self.config.width as usize * bpp;
let row_width = (x_end - x_start + 1) as usize * bpp;
for y in y_start..=y_end {
let offset = y as usize * fb_stride + x_start as usize * bpp;
let row_end = offset + row_width;
if row_end <= self.buffer.data.len() {
self.interface
.send_pixels(&self.buffer.data[offset..row_end])
.map_err(DriverError::InterfaceError)?;
} else {
return Err(DriverError::InvalidConfiguration(
"Framebuffer slice out of bounds",
));
}
}
Ok(())
}
}
impl<IFACE, RST, COLOR: St77916Color> St77916<IFACE, RST, DoubleBuffered<COLOR>>
where
IFACE: ControllerInterface,
RST: ResetInterface,
{
pub fn swap_buffers(&mut self) {
self.buffer.back_idx ^= 1;
}
pub fn flush_front(&mut self) -> DriverResult<(), IFACE, RST> {
self.set_window(0, 0, self.config.width - 1, self.config.height - 1)?;
self.interface
.send_pixels(self.buffer.front())
.map_err(DriverError::InterfaceError)
}
pub fn flush(&mut self) -> DriverResult<(), IFACE, RST> {
self.set_window(0, 0, self.config.width - 1, self.config.height - 1)?;
self.interface
.send_pixels(self.buffer.back())
.map_err(DriverError::InterfaceError)
}
pub fn back_buffer_mut(&mut self) -> &mut [u8] {
self.buffer.back_mut().as_mut_slice()
}
pub fn front_buffer(&self) -> &[u8] {
self.buffer.front().as_slice()
}
}