use embedded_graphics::pixelcolor::Rgb565;
use embedded_graphics::prelude::*;
use embedded_graphics::primitives::Rectangle;
use embedded_hal::digital::OutputPin;
use lcd_async::Display;
use lcd_async::interface::Interface;
use lcd_async::models::Model;
use lcd_async::raw_framebuf::RawFrameBuf;
use super::super::DisplayDriver;
pub struct LcdAsyncDisplay<DI, MOD, RST, BUF, const W: usize, const H: usize>
where
DI: Interface<Word = u8>,
MOD: Model<ColorFormat = Rgb565>,
RST: OutputPin,
BUF: AsMut<[u8]> + AsRef<[u8]>,
{
display: Display<DI, MOD, RST>,
buffer: BUF,
dirty: Option<Rectangle>,
staging: Option<&'static mut [u8]>,
}
impl<DI, MOD, RST, BUF, const W: usize, const H: usize> LcdAsyncDisplay<DI, MOD, RST, BUF, W, H>
where
DI: Interface<Word = u8>,
MOD: Model<ColorFormat = Rgb565>,
RST: OutputPin,
BUF: AsMut<[u8]> + AsRef<[u8]>,
{
pub fn new(display: Display<DI, MOD, RST>, buffer: BUF) -> Self {
debug_assert_eq!(
buffer.as_ref().len(),
W * H * 2,
"framebuffer length must equal W * H * 2 (Rgb565)",
);
Self {
display,
buffer,
dirty: Some(Rectangle::new(Point::zero(), Size::new(W as u32, H as u32))),
staging: None,
}
}
pub fn with_staging(mut self, staging: &'static mut [u8]) -> Self {
self.staging = Some(staging);
self
}
pub fn display(&mut self) -> &mut Display<DI, MOD, RST> {
&mut self.display
}
fn mark_dirty(&mut self, area: &Rectangle) {
let screen = Rectangle::new(Point::zero(), Size::new(W as u32, H as u32));
let area = area.intersection(&screen);
let Some(area_br) = area.bottom_right() else {
return;
};
self.dirty = Some(match self.dirty {
None => area,
Some(d) => Rectangle::with_corners(
d.top_left.component_min(area.top_left),
d.bottom_right().unwrap_or(d.top_left).component_max(area_br),
),
});
}
}
impl<DI, MOD, RST, BUF, const W: usize, const H: usize> DisplayDriver for LcdAsyncDisplay<DI, MOD, RST, BUF, W, H>
where
DI: Interface<Word = u8>,
MOD: Model<ColorFormat = Rgb565>,
RST: OutputPin,
BUF: AsMut<[u8]> + AsRef<[u8]>,
{
async fn init(&mut self) {}
async fn flush(&mut self) {
let Some(dirty) = self.dirty else {
return;
};
let x0 = dirty.top_left.x as usize;
let y0 = dirty.top_left.y as usize;
let w = dirty.size.width as usize;
let h = dirty.size.height as usize;
match self.staging.as_deref_mut() {
Some(staging) if w < W && staging.len() >= w * 2 => {
let rows_per_pass = staging.len() / (w * 2);
let buffer = self.buffer.as_ref();
let mut y = y0;
while y < y0 + h {
let count = rows_per_pass.min(y0 + h - y);
for n in 0..count {
let from = ((y + n) * W + x0) * 2;
staging[n * w * 2..(n + 1) * w * 2].copy_from_slice(&buffer[from..from + w * 2]);
}
if self
.display
.show_raw_data(x0 as u16, y as u16, w as u16, count as u16, &staging[..count * w * 2])
.await
.is_err()
{
return;
}
y += count;
}
}
_ => {
let rows = &self.buffer.as_ref()[y0 * W * 2..(y0 + h) * W * 2];
if self
.display
.show_raw_data(0, y0 as u16, W as u16, h as u16, rows)
.await
.is_err()
{
return;
}
}
}
self.dirty = None;
}
}
impl<DI, MOD, RST, BUF, const W: usize, const H: usize> OriginDimensions for LcdAsyncDisplay<DI, MOD, RST, BUF, W, H>
where
DI: Interface<Word = u8>,
MOD: Model<ColorFormat = Rgb565>,
RST: OutputPin,
BUF: AsMut<[u8]> + AsRef<[u8]>,
{
fn size(&self) -> Size {
Size::new(W as u32, H as u32)
}
}
impl<DI, MOD, RST, BUF, const W: usize, const H: usize> DrawTarget for LcdAsyncDisplay<DI, MOD, RST, BUF, W, H>
where
DI: Interface<Word = u8>,
MOD: Model<ColorFormat = Rgb565>,
RST: OutputPin,
BUF: AsMut<[u8]> + AsRef<[u8]>,
{
type Color = Rgb565;
type Error = core::convert::Infallible;
fn draw_iter<I>(&mut self, pixels: I) -> Result<(), Self::Error>
where
I: IntoIterator<Item = Pixel<Rgb565>>,
{
let mut min = Point::new(i32::MAX, i32::MAX);
let mut max = Point::new(i32::MIN, i32::MIN);
{
let mut fb = RawFrameBuf::<Rgb565, _>::new(self.buffer.as_mut(), W, H);
fb.draw_iter(pixels.into_iter().inspect(|Pixel(pos, _)| {
min = min.component_min(*pos);
max = max.component_max(*pos);
}))
.ok();
}
if min.x <= max.x {
self.mark_dirty(&Rectangle::with_corners(min, max));
}
Ok(())
}
fn fill_contiguous<I>(&mut self, area: &Rectangle, colors: I) -> Result<(), Self::Error>
where
I: IntoIterator<Item = Rgb565>,
{
{
let mut fb = RawFrameBuf::<Rgb565, _>::new(self.buffer.as_mut(), W, H);
fb.fill_contiguous(area, colors).ok();
}
self.mark_dirty(area);
Ok(())
}
fn fill_solid(&mut self, area: &Rectangle, color: Rgb565) -> Result<(), Self::Error> {
{
let mut fb = RawFrameBuf::<Rgb565, _>::new(self.buffer.as_mut(), W, H);
fb.fill_solid(area, color).ok();
}
self.mark_dirty(area);
Ok(())
}
fn clear(&mut self, color: Rgb565) -> Result<(), Self::Error> {
{
let mut fb = RawFrameBuf::<Rgb565, _>::new(self.buffer.as_mut(), W, H);
fb.clear(color).ok();
}
self.mark_dirty(&Rectangle::new(Point::zero(), Size::new(W as u32, H as u32)));
Ok(())
}
}