// Uses too much RAM to be useful ): // 320 * 240 * 2 * 2 bytes/pixel = 307KB of RAM use core::convert::Infallible; use embedded_graphics::{ draw_target::DrawTarget, geometry::{Dimensions, Point}, pixelcolor::PixelColor, Pixel, }; pub struct BufferedDisplay<const L: usize, C: 'static, E, DT: DrawTarget<Color = C, Error = E>> { target: DT, width: usize, height: usize, last: &'static mut [C; L], cur: &'static mut [C; L], } impl<const L: usize, C: PixelColor, E, DT: DrawTarget<Color = C, Error = E>> BufferedDisplay<L, C, E, DT> { pub fn new( mut target: DT, color: C, last: &'static mut [C; L], cur: &'static mut [C; L], ) -> Result<Self, E> { let size = target.bounding_box().size; let width = size.width.try_into().unwrap(); let height = size.height.try_into().unwrap(); assert_eq!(L, width * height); target.fill_solid(&target.bounding_box(), color)?; Ok(Self { target, width, height, last, cur, }) } pub fn flush(&mut self) -> Result<(), E> { let iter = BufferedDisplayIter::new(self.width, self.height, self.last, self.cur); self.target.draw_iter(iter)?; Ok(()) } #[inline(always)] fn index<X: TryInto<usize>, Y: TryInto<usize>>(&self, x: X, y: Y) -> Option<usize> { let x: usize = x.try_into().ok()?; let y: usize = y.try_into().ok()?; if y >= self.height || x >= self.width { return None; } Some(x + y * self.width) } } impl<const L: usize, C: PixelColor, E, DT: DrawTarget<Color = C, Error = E>> Dimensions for BufferedDisplay<L, C, E, DT> { fn bounding_box(&self) -> embedded_graphics::primitives::Rectangle { self.target.bounding_box() } } impl<const L: usize, C: PixelColor, E, DT: DrawTarget<Color = C, Error = E>> DrawTarget for BufferedDisplay<L, C, E, DT> { type Color = C; type Error = Infallible; fn draw_iter<I>(&mut self, pixels: I) -> Result<(), Self::Error> where I: IntoIterator<Item = embedded_graphics::prelude::Pixel<Self::Color>>, { for Pixel(p, c) in pixels { if let Some(idx) = self.index(p.x, p.y) { self.cur[idx] = c; } } Ok(()) } } struct BufferedDisplayIter<'a, C: PixelColor> { width: usize, height: usize, last: &'a mut [C], cur: &'a mut [C], i: usize, } impl<'a, C: PixelColor> BufferedDisplayIter<'a, C> { fn new(width: usize, height: usize, last: &'a mut [C], cur: &'a mut [C]) -> Self { Self { width, height, last, cur, i: 0, } } #[inline(always)] fn get_xy(&self, idx: usize) -> (i32, i32) { let y = (idx / self.width).try_into().unwrap(); let x = (idx % self.width).try_into().unwrap(); (x, y) } } impl<'a, C: PixelColor> Iterator for BufferedDisplayIter<'a, C> { type Item = Pixel<C>; fn next(&mut self) -> Option<Self::Item> { loop { if self.i >= self.cur.len() { return None; } let c = self.cur[self.i]; if self.last[self.i] != c { self.last[self.i] = c; let (x, y) = self.get_xy(self.i); return Some(Pixel(Point::new(x, y), c)); } self.i += 1; } } }