|
| 1 | +use embedded_graphics::{ |
| 2 | + image::{Image, ImageRaw}, |
| 3 | + iterator::raw::RawDataSlice, |
| 4 | + pixelcolor::{raw::LittleEndian, Bgr888, Rgb555, Rgb565, Rgb888}, |
| 5 | + prelude::*, |
| 6 | +}; |
| 7 | +use tinybmp::Bmp; |
| 8 | + |
| 9 | +const WIDTH: usize = 20; |
| 10 | +const HEIGHT: usize = 20; |
| 11 | + |
| 12 | +// TODO: use e-g framebuffer when it's added |
| 13 | +#[derive(Debug, PartialEq)] |
| 14 | +struct Framebuffer<C> { |
| 15 | + pixels: [[C; WIDTH]; HEIGHT], |
| 16 | +} |
| 17 | + |
| 18 | +impl<C: PixelColor + From<Rgb888> + std::fmt::Debug> Framebuffer<C> { |
| 19 | + pub fn new() -> Self { |
| 20 | + let color = C::from(Rgb888::BLACK); |
| 21 | + |
| 22 | + Self { |
| 23 | + pixels: [[color; WIDTH]; HEIGHT], |
| 24 | + } |
| 25 | + } |
| 26 | + |
| 27 | + pub fn from_image(image: impl ImageDrawable<Color = C>) -> Self { |
| 28 | + let mut framebuffer = Framebuffer::<C>::new(); |
| 29 | + Image::new(&image, Point::zero()) |
| 30 | + .draw(&mut framebuffer) |
| 31 | + .unwrap(); |
| 32 | + framebuffer |
| 33 | + } |
| 34 | + |
| 35 | + pub fn pixels(&self) -> impl Iterator<Item = C> + '_ { |
| 36 | + self.pixels.iter().flatten().copied() |
| 37 | + } |
| 38 | + |
| 39 | + pub fn assert_eq(&self, expected: &Self) { |
| 40 | + let zipped = || self.pixels().zip(expected.pixels()); |
| 41 | + |
| 42 | + let errors = zipped().filter(|(a, b)| a != b).count(); |
| 43 | + let first_error = zipped() |
| 44 | + .enumerate() |
| 45 | + .find(|(_, (a, b))| a != b) |
| 46 | + .map(|(i, (a, b))| (Point::new((i % WIDTH) as i32, (i / WIDTH) as i32), a, b)); |
| 47 | + |
| 48 | + //let first_error = self.pixels() |
| 49 | + |
| 50 | + if self != expected { |
| 51 | + let first_error = first_error.unwrap(); |
| 52 | + panic!( |
| 53 | + "framebuffer differs from expected\n{} errors\nfirst error at ({}): {:?} (expected {:?})", |
| 54 | + errors, |
| 55 | + first_error.0, |
| 56 | + first_error.1, |
| 57 | + first_error.2, |
| 58 | + ); |
| 59 | + } |
| 60 | + } |
| 61 | +} |
| 62 | + |
| 63 | +impl<C: PixelColor> DrawTarget for Framebuffer<C> { |
| 64 | + type Color = C; |
| 65 | + type Error = std::convert::Infallible; |
| 66 | + |
| 67 | + fn draw_iter<I>(&mut self, pixels: I) -> Result<(), Self::Error> |
| 68 | + where |
| 69 | + I: IntoIterator<Item = embedded_graphics::Pixel<Self::Color>>, |
| 70 | + { |
| 71 | + for Pixel(p, c) in pixels { |
| 72 | + self.pixels[p.y as usize][p.x as usize] = c; |
| 73 | + } |
| 74 | + |
| 75 | + Ok(()) |
| 76 | + } |
| 77 | +} |
| 78 | + |
| 79 | +impl<C> OriginDimensions for Framebuffer<C> { |
| 80 | + fn size(&self) -> embedded_graphics::prelude::Size { |
| 81 | + Size::new(WIDTH as u32, HEIGHT as u32) |
| 82 | + } |
| 83 | +} |
| 84 | + |
| 85 | +fn draw_raw<C>(data: &[u8]) -> Framebuffer<C> |
| 86 | +where |
| 87 | + C: PixelColor + From<C::Raw> + From<Rgb888> + std::fmt::Debug, |
| 88 | + for<'a> RawDataSlice<'a, C::Raw, LittleEndian>: IntoIterator<Item = C::Raw>, |
| 89 | +{ |
| 90 | + let raw = ImageRaw::<C, LittleEndian>::new(data, WIDTH as u32); |
| 91 | + |
| 92 | + Framebuffer::from_image(raw) |
| 93 | +} |
| 94 | + |
| 95 | +fn draw_bmp<C>(data: &[u8]) -> Framebuffer<C> |
| 96 | +where |
| 97 | + C: PixelColor + From<Rgb555> + From<Rgb565> + From<Rgb888> + std::fmt::Debug, |
| 98 | +{ |
| 99 | + let bmp = Bmp::<C>::from_slice(data).unwrap(); |
| 100 | + |
| 101 | + Framebuffer::from_image(bmp) |
| 102 | +} |
| 103 | + |
| 104 | +#[test] |
| 105 | +fn ethernet_port() { |
| 106 | + let raw = draw_raw::<Bgr888>(include_bytes!("ethernet_port.raw")); |
| 107 | + let bmp = draw_bmp::<Bgr888>(include_bytes!("ethernet_port.bmp")); |
| 108 | + |
| 109 | + bmp.assert_eq(&raw); |
| 110 | +} |
0 commit comments