1use crate::color::{FromRGB, Rgb16};
2use alloc::boxed::Box;
3use core::convert::Infallible;
4use core::marker::PhantomData;
5use embedded_graphics::pixelcolor::Gray4;
6use embedded_graphics::prelude::*;
7use embedded_graphics::primitives::Rectangle;
8
9pub const WIDTH: usize = 240;
10pub const HEIGHT: usize = 160;
11const BPP: usize = 4;
13const PPB: usize = 8 / BPP;
15const BUFFER_SIZE: usize = WIDTH * HEIGHT / PPB;
17
18const DEFAULT_PALETTE: [Rgb16; 16] = [
21 Rgb16::from_rgb(0x1a, 0x1c, 0x2c), Rgb16::from_rgb(0x5d, 0x27, 0x5d), Rgb16::from_rgb(0xb1, 0x3e, 0x53), Rgb16::from_rgb(0xef, 0x7d, 0x57), Rgb16::from_rgb(0xff, 0xcd, 0x75), Rgb16::from_rgb(0xa7, 0xf0, 0x70), Rgb16::from_rgb(0x38, 0xb7, 0x64), Rgb16::from_rgb(0x25, 0x71, 0x79), Rgb16::from_rgb(0x29, 0x36, 0x6f), Rgb16::from_rgb(0x3b, 0x5d, 0xc9), Rgb16::from_rgb(0x41, 0xa6, 0xf6), Rgb16::from_rgb(0x73, 0xef, 0xf7), Rgb16::from_rgb(0xf4, 0xf4, 0xf4), Rgb16::from_rgb(0x94, 0xb0, 0xc2), Rgb16::from_rgb(0x56, 0x6c, 0x86), Rgb16::from_rgb(0x33, 0x3c, 0x57), ];
38
39pub trait RenderFB {
40 type Error;
41 fn render_fb(&mut self, frame: &mut FrameBuffer) -> Result<(), Self::Error>;
42}
43
44pub struct FrameBuffer {
45 pub(crate) data: Box<[u8; BUFFER_SIZE]>,
47 pub(crate) palette: [Rgb16; 16],
49 pub(crate) dirty: bool,
50}
51
52impl FrameBuffer {
53 pub(crate) fn new() -> Self {
54 Self {
55 data: Box::new([0; BUFFER_SIZE]),
56 palette: DEFAULT_PALETTE,
57 dirty: false,
58 }
59 }
60
61 pub fn iter_pairs(&self) -> impl Iterator<Item = (Rgb16, Rgb16)> + use<'_> {
62 self.data.iter().map(|b| {
63 let right = self.palette[usize::from(b & 0xF)];
64 let left = self.palette[usize::from(b >> 4) & 0xF];
65 (right, left)
66 })
67 }
68
69 pub(crate) fn draw_hline(&mut self, x1: i32, x2: i32, y: i32, w: u32, c: Gray4) {
74 let mut left = x1;
75 let mut right = x2;
76 let mut y = y - (w / 2) as i32;
77 if left > right {
78 (left, right) = (right, left);
79 if w.is_multiple_of(2) {
80 y += 1;
81 }
82 }
83 let area = Rectangle {
84 top_left: Point::new(left, y),
85 size: Size::new((right - left + 1) as u32, w),
86 };
87 _ = self.fill_solid(&area, c);
88 }
89
90 pub(crate) fn draw_vline(&mut self, x: i32, y1: i32, y2: i32, w: u32, c: Gray4) {
95 let mut top = y1;
96 let mut down = y2;
97 let mut x = x - (w / 2) as i32;
98 if top > down {
99 (down, top) = (top, down);
100 if w.is_multiple_of(2) {
101 x += 1;
102 }
103 }
104 let area = Rectangle {
105 top_left: Point::new(x, top),
106 size: Size::new(w, (down - top + 1) as u32),
107 };
108 _ = self.fill_solid(&area, c);
109 }
110
111 fn draw_vline1(&mut self, x: usize, top_y: usize, bottom_y: usize, c: Gray4) {
115 let color = c.into_storage();
116 debug_assert!(color < 16);
117 let shift = if x.is_multiple_of(2) { 0 } else { 4 };
118 let mask = !(0b1111 << shift);
119 let start_i = (top_y * WIDTH + x) / PPB;
120 let end_i = (bottom_y * WIDTH + x) / PPB;
121 for pixel_index in (start_i..end_i).step_by(WIDTH / PPB) {
122 let byte_index = pixel_index;
123 let byte = unsafe { self.data.get_unchecked_mut(byte_index) };
126 *byte = (color << shift) | (*byte & mask);
127 }
128 }
129}
130
131impl OriginDimensions for FrameBuffer {
133 fn size(&self) -> Size {
134 Size::new(WIDTH as u32, HEIGHT as u32)
135 }
136}
137
138impl DrawTarget for FrameBuffer {
140 type Color = Gray4;
141 type Error = Infallible;
142
143 fn draw_iter<I>(&mut self, pixels: I) -> Result<(), Self::Error>
144 where
145 I: IntoIterator<Item = Pixel<Self::Color>>,
146 {
147 self.dirty = true;
148 for pixel in pixels {
149 let Pixel(point, color) = pixel;
150 self.set_pixel(point, color);
151 }
152 Ok(())
153 }
154
155 fn clear(&mut self, color: Self::Color) -> Result<(), Self::Error> {
156 self.dirty = true;
157 let new_byte = color_to_byte(&color);
158 self.data.fill(new_byte);
159 Ok(())
160 }
161
162 fn fill_solid(&mut self, area: &Rectangle, color: Self::Color) -> Result<(), Self::Error> {
163 self.dirty = true;
164 let new_byte = color_to_byte(&color);
165
166 let left_x = area.top_left.x.clamp(0, WIDTH as _) as usize;
167 let right_x = area.top_left.x + area.size.width as i32;
168 let right_x = right_x.clamp(0, WIDTH as _) as usize;
169
170 let top_y = area.top_left.y.clamp(0, HEIGHT as _) as usize;
171 let bottom_y = area.top_left.y + area.size.height as i32;
172 let bottom_y = bottom_y.clamp(0, HEIGHT as _) as usize;
173
174 if right_x - left_x <= 4 {
175 for x in left_x..right_x {
176 self.draw_vline1(x, top_y, bottom_y, color);
177 }
178 return Ok(());
179 }
180
181 let left_fract = left_x % 2 == 1;
182 let right_fract = right_x % 2 == 1;
183 let start_x = left_x + usize::from(left_fract);
184 let end_x = right_x - usize::from(right_fract);
185 let width = end_x - start_x;
186 debug_assert_eq!(width % 2, 0);
187
188 for y in top_y..bottom_y {
189 let start_i = y * WIDTH / 2 + start_x / 2;
190 let end_i = start_i + width / 2;
191 self.data[start_i..end_i].fill(new_byte);
192 }
193
194 if left_fract {
195 self.draw_vline1(left_x, top_y, bottom_y, color);
196 }
197 if right_fract {
198 self.draw_vline1(right_x - 1, top_y, bottom_y, color);
199 }
200 Ok(())
201 }
202
203 fn fill_contiguous<I>(&mut self, area: &Rectangle, colors: I) -> Result<(), Self::Error>
205 where
206 I: IntoIterator<Item = Self::Color>,
207 {
208 self.draw_iter(
209 area.points()
210 .zip(colors)
211 .map(|(pos, color)| Pixel(pos, color)),
212 )
213 }
214}
215
216impl FrameBuffer {
217 pub fn draw<D, C, E>(&mut self, target: &mut D) -> Result<(), E>
219 where
220 C: RgbColor + FromRGB,
221 D: DrawTarget<Color = C, Error = E>,
222 {
223 if !self.dirty {
224 return Ok(());
225 }
226 self.dirty = false;
227 let colors = ColorIter {
228 data: &self.data,
229 palette: &self.palette,
230 index: 0,
231 color: PhantomData,
232 };
233 let area = Rectangle::new(Point::zero(), Size::new(WIDTH as u32, HEIGHT as u32));
234 target.fill_contiguous(&area, colors)
235 }
236
237 pub(crate) fn set_pixel(&mut self, point: Point, color: Gray4) {
241 let x = point.x as usize;
244 let y = point.y as usize;
245 if y >= HEIGHT || x >= WIDTH {
246 return; }
248 let pixel_index = y * WIDTH + x;
249 let byte_index = pixel_index / PPB;
250 let shift = if pixel_index.is_multiple_of(2) { 0 } else { 4 };
251 let mask = !(0b1111 << shift);
252 let byte = unsafe { self.data.get_unchecked_mut(byte_index) };
255 let color = color.luma();
256 debug_assert!(color < 16);
257 *byte = (color << shift) | (*byte & mask);
258 }
259}
260
261struct ColorIter<'a, C>
262where
263 C: RgbColor + FromRGB,
264{
265 data: &'a [u8; BUFFER_SIZE],
266 palette: &'a [Rgb16; 16],
267 index: usize,
268 color: PhantomData<C>,
269}
270
271impl<C> Iterator for ColorIter<'_, C>
272where
273 C: RgbColor + FromRGB,
274{
275 type Item = C;
276
277 fn next(&mut self) -> Option<Self::Item> {
278 let byte_index = self.index / PPB;
279 let byte = self.data.get(byte_index)?;
280 let shift = self.index % PPB;
281 let luma = (byte >> (shift * BPP)) & 0b1111;
282 debug_assert!(luma < 16);
283 self.index += 1;
284 let rgb16 = self.palette[luma as usize];
285 Some(C::from_rgb(rgb16))
286 }
287}
288
289fn color_to_byte(c: &Gray4) -> u8 {
291 let luma = c.luma();
292 luma | (luma << 4)
293}