1use embedded_graphics_core::{
13 Pixel,
14 draw_target::DrawTarget,
15 geometry::{OriginDimensions, Point, Size},
16 pixelcolor::{Gray8, GrayColor, Rgb565, RgbColor},
17 primitives::Rectangle,
18};
19
20use crate::geometry::Rect;
21use crate::render::PixelRead;
22
23#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
25pub struct Rgba8888 {
26 pub r: u8,
27 pub g: u8,
28 pub b: u8,
29 pub a: u8,
30}
31
32impl Rgba8888 {
33 pub const BLACK: Self = Self {
34 r: 0,
35 g: 0,
36 b: 0,
37 a: 255,
38 };
39 pub const WHITE: Self = Self {
40 r: 255,
41 g: 255,
42 b: 255,
43 a: 255,
44 };
45 pub const TRANSPARENT: Self = Self {
46 r: 0,
47 g: 0,
48 b: 0,
49 a: 0,
50 };
51
52 pub const fn new(r: u8, g: u8, b: u8, a: u8) -> Self {
53 Self { r, g, b, a }
54 }
55
56 pub fn from_rgb565(rgb: Rgb565, alpha: u8) -> Self {
57 let r5 = rgb.r();
58 let g6 = rgb.g();
59 let b5 = rgb.b();
60 let r8 = (r5 << 3) | (r5 >> 2);
61 let g8 = (g6 << 2) | (g6 >> 4);
62 let b8 = (b5 << 3) | (b5 >> 2);
63 Self {
64 r: r8,
65 g: g8,
66 b: b8,
67 a: alpha,
68 }
69 }
70
71 pub fn to_rgb565(self) -> Rgb565 {
72 Rgb565::new(self.r >> 3, self.g >> 2, self.b >> 3)
73 }
74}
75
76impl embedded_graphics_core::pixelcolor::PixelColor for Rgba8888 {
77 type Raw = embedded_graphics_core::pixelcolor::raw::RawU32;
78}
79
80#[derive(Clone, Debug, PartialEq, Eq)]
83pub struct Framebuffer<const N: usize> {
84 pixels: [Rgb565; N],
85 width: u32,
86 height: u32,
87}
88
89impl<const N: usize> Framebuffer<N> {
90 pub fn new(width: u32, height: u32) -> Self {
94 assert!(
95 (width as usize) * (height as usize) <= N,
96 "Framebuffer backing array too small for width * height",
97 );
98 Self {
99 pixels: [Rgb565::BLACK; N],
100 width,
101 height,
102 }
103 }
104
105 pub fn clear_color(&mut self, color: Rgb565) {
107 let len = self.len();
108 for p in self.pixels[..len].iter_mut() {
109 *p = color;
110 }
111 }
112
113 pub fn pixels(&self) -> &[Rgb565] {
116 &self.pixels[..self.len()]
117 }
118
119 pub fn pixels_mut(&mut self) -> &mut [Rgb565] {
121 let len = self.len();
122 &mut self.pixels[..len]
123 }
124
125 pub const fn width(&self) -> u32 {
126 self.width
127 }
128
129 pub const fn height(&self) -> u32 {
130 self.height
131 }
132
133 #[inline]
134 fn len(&self) -> usize {
135 (self.width as usize) * (self.height as usize)
136 }
137
138 #[inline]
139 fn index(&self, x: i32, y: i32) -> Option<usize> {
140 if x < 0 || y < 0 || x as u32 >= self.width || y as u32 >= self.height {
141 return None;
142 }
143 Some((y as usize) * (self.width as usize) + (x as usize))
144 }
145
146 pub fn apply_iir_blur(&mut self, blur_degree: u8) {
148 let rect = Rect::new(0, 0, self.width, self.height);
149 self.blur_rect(rect, blur_degree);
150 }
151
152 pub fn blur_rect(&mut self, rect: Rect, blur_degree: u8) {
154 if blur_degree == 0 || self.width == 0 || self.height == 0 {
155 return;
156 }
157
158 let x0 = rect.x.max(0) as u32;
159 let y0 = rect.y.max(0) as u32;
160 let x1 = (rect.right() as u32).min(self.width);
161 let y1 = (rect.bottom() as u32).min(self.height);
162
163 if x0 >= x1 || y0 >= y1 {
164 return;
165 }
166
167 let alpha = 256 - (blur_degree as i32);
168 let w = self.width as usize;
169
170 for y in y0..y1 {
172 let row_start = y as usize * w;
173 let idx0 = row_start + x0 as usize;
174 let raw0 = self.pixels[idx0];
175 let (r5, g6, b5) = (raw0.r(), raw0.g(), raw0.b());
176 let mut acc_r = (((r5 << 3) | (r5 >> 2)) as i32) << 8;
177 let mut acc_g = (((g6 << 2) | (g6 >> 4)) as i32) << 8;
178 let mut acc_b = (((b5 << 3) | (b5 >> 2)) as i32) << 8;
179
180 for x in x0..x1 {
181 let idx = row_start + x as usize;
182 let raw = self.pixels[idx];
183 let (r5, g6, b5) = (raw.r(), raw.g(), raw.b());
184 let r8 = ((r5 << 3) | (r5 >> 2)) as i32;
185 let g8 = ((g6 << 2) | (g6 >> 4)) as i32;
186 let b8 = ((b5 << 3) | (b5 >> 2)) as i32;
187
188 acc_r += (((r8 << 8) - acc_r) * alpha) >> 8;
189 acc_g += (((g8 << 8) - acc_g) * alpha) >> 8;
190 acc_b += (((b8 << 8) - acc_b) * alpha) >> 8;
191
192 let r_out = ((acc_r >> 8).clamp(0, 255) as u8) >> 3;
193 let g_out = ((acc_g >> 8).clamp(0, 255) as u8) >> 2;
194 let b_out = ((acc_b >> 8).clamp(0, 255) as u8) >> 3;
195 self.pixels[idx] = Rgb565::new(r_out, g_out, b_out);
196 }
197 }
198
199 for y in y0..y1 {
201 let row_start = y as usize * w;
202 let idx_last = row_start + (x1 - 1) as usize;
203 let raw_last = self.pixels[idx_last];
204 let (r5, g6, b5) = (raw_last.r(), raw_last.g(), raw_last.b());
205 let mut acc_r = (((r5 << 3) | (r5 >> 2)) as i32) << 8;
206 let mut acc_g = (((g6 << 2) | (g6 >> 4)) as i32) << 8;
207 let mut acc_b = (((b5 << 3) | (b5 >> 2)) as i32) << 8;
208
209 for x in (x0..x1).rev() {
210 let idx = row_start + x as usize;
211 let raw = self.pixels[idx];
212 let (r5, g6, b5) = (raw.r(), raw.g(), raw.b());
213 let r8 = ((r5 << 3) | (r5 >> 2)) as i32;
214 let g8 = ((g6 << 2) | (g6 >> 4)) as i32;
215 let b8 = ((b5 << 3) | (b5 >> 2)) as i32;
216
217 acc_r += (((r8 << 8) - acc_r) * alpha) >> 8;
218 acc_g += (((g8 << 8) - acc_g) * alpha) >> 8;
219 acc_b += (((b8 << 8) - acc_b) * alpha) >> 8;
220
221 let r_out = ((acc_r >> 8).clamp(0, 255) as u8) >> 3;
222 let g_out = ((acc_g >> 8).clamp(0, 255) as u8) >> 2;
223 let b_out = ((acc_b >> 8).clamp(0, 255) as u8) >> 3;
224 self.pixels[idx] = Rgb565::new(r_out, g_out, b_out);
225 }
226 }
227
228 for x in x0..x1 {
230 let idx0 = y0 as usize * w + x as usize;
231 let raw0 = self.pixels[idx0];
232 let (r5, g6, b5) = (raw0.r(), raw0.g(), raw0.b());
233 let mut acc_r = (((r5 << 3) | (r5 >> 2)) as i32) << 8;
234 let mut acc_g = (((g6 << 2) | (g6 >> 4)) as i32) << 8;
235 let mut acc_b = (((b5 << 3) | (b5 >> 2)) as i32) << 8;
236
237 for y in y0..y1 {
238 let idx = y as usize * w + x as usize;
239 let raw = self.pixels[idx];
240 let (r5, g6, b5) = (raw.r(), raw.g(), raw.b());
241 let r8 = ((r5 << 3) | (r5 >> 2)) as i32;
242 let g8 = ((g6 << 2) | (g6 >> 4)) as i32;
243 let b8 = ((b5 << 3) | (b5 >> 2)) as i32;
244
245 acc_r += (((r8 << 8) - acc_r) * alpha) >> 8;
246 acc_g += (((g8 << 8) - acc_g) * alpha) >> 8;
247 acc_b += (((b8 << 8) - acc_b) * alpha) >> 8;
248
249 let r_out = ((acc_r >> 8).clamp(0, 255) as u8) >> 3;
250 let g_out = ((acc_g >> 8).clamp(0, 255) as u8) >> 2;
251 let b_out = ((acc_b >> 8).clamp(0, 255) as u8) >> 3;
252 self.pixels[idx] = Rgb565::new(r_out, g_out, b_out);
253 }
254 }
255
256 for x in x0..x1 {
258 let idx_last = (y1 - 1) as usize * w + x as usize;
259 let raw_last = self.pixels[idx_last];
260 let (r5, g6, b5) = (raw_last.r(), raw_last.g(), raw_last.b());
261 let mut acc_r = (((r5 << 3) | (r5 >> 2)) as i32) << 8;
262 let mut acc_g = (((g6 << 2) | (g6 >> 4)) as i32) << 8;
263 let mut acc_b = (((b5 << 3) | (b5 >> 2)) as i32) << 8;
264
265 for y in (y0..y1).rev() {
266 let idx = y as usize * w + x as usize;
267 let raw = self.pixels[idx];
268 let (r5, g6, b5) = (raw.r(), raw.g(), raw.b());
269 let r8 = ((r5 << 3) | (r5 >> 2)) as i32;
270 let g8 = ((g6 << 2) | (g6 >> 4)) as i32;
271 let b8 = ((b5 << 3) | (b5 >> 2)) as i32;
272
273 acc_r += (((r8 << 8) - acc_r) * alpha) >> 8;
274 acc_g += (((g8 << 8) - acc_g) * alpha) >> 8;
275 acc_b += (((b8 << 8) - acc_b) * alpha) >> 8;
276
277 let r_out = ((acc_r >> 8).clamp(0, 255) as u8) >> 3;
278 let g_out = ((acc_g >> 8).clamp(0, 255) as u8) >> 2;
279 let b_out = ((acc_b >> 8).clamp(0, 255) as u8) >> 3;
280 self.pixels[idx] = Rgb565::new(r_out, g_out, b_out);
281 }
282 }
283 }
284
285 pub fn reverse_colour_rect(&mut self, rect: Rect) {
287 let x0 = rect.x.max(0) as u32;
288 let y0 = rect.y.max(0) as u32;
289 let x1 = (rect.right() as u32).min(self.width);
290 let y1 = (rect.bottom() as u32).min(self.height);
291
292 if x0 >= x1 || y0 >= y1 {
293 return;
294 }
295
296 let w = self.width as usize;
297 for y in y0..y1 {
298 let row_start = y as usize * w;
299 for x in x0..x1 {
300 let idx = row_start + x as usize;
301 let c = self.pixels[idx];
302 self.pixels[idx] = Rgb565::new(31 - c.r(), 63 - c.g(), 31 - c.b());
303 }
304 }
305 }
306
307 pub fn apply_reverse_colour(&mut self) {
309 let rect = Rect::new(0, 0, self.width, self.height);
310 self.reverse_colour_rect(rect);
311 }
312}
313
314impl<const N: usize> OriginDimensions for Framebuffer<N> {
315 fn size(&self) -> Size {
316 Size::new(self.width, self.height)
317 }
318}
319
320impl<const N: usize> DrawTarget for Framebuffer<N> {
321 type Color = Rgb565;
322 type Error = core::convert::Infallible;
323
324 fn draw_iter<I>(&mut self, pixels: I) -> Result<(), Self::Error>
325 where
326 I: IntoIterator<Item = Pixel<Self::Color>>,
327 {
328 for Pixel(point, color) in pixels {
329 if let Some(idx) = self.index(point.x, point.y) {
330 self.pixels[idx] = color;
331 }
332 }
333 Ok(())
334 }
335
336 fn fill_solid(&mut self, area: &Rectangle, color: Self::Color) -> Result<(), Self::Error> {
337 let intersect = area.intersection(&Rectangle::new(Point::zero(), self.size()));
338 if intersect.is_zero_sized() {
339 return Ok(());
340 }
341 let x0 = intersect.top_left.x as usize;
342 let y0 = intersect.top_left.y as usize;
343 let w = intersect.size.width as usize;
344 let h = intersect.size.height as usize;
345 let stride = self.width as usize;
346
347 for row in 0..h {
348 let start = (y0 + row) * stride + x0;
349 if start + w <= N {
350 self.pixels[start..start + w].fill(color);
351 }
352 }
353 Ok(())
354 }
355
356 fn fill_contiguous<I>(&mut self, area: &Rectangle, colors: I) -> Result<(), Self::Error>
357 where
358 I: IntoIterator<Item = Self::Color>,
359 {
360 let intersect = area.intersection(&Rectangle::new(Point::zero(), self.size()));
361 if intersect.is_zero_sized() {
362 return Ok(());
363 }
364 let mut colors = colors.into_iter();
365 let stride = self.width as usize;
366 let x_end = area.top_left.x + area.size.width as i32;
367 let y_end = area.top_left.y + area.size.height as i32;
368
369 for y in area.top_left.y..y_end {
370 for x in area.top_left.x..x_end {
371 if let Some(c) = colors.next() {
372 if x >= 0 && y >= 0 && (x as u32) < self.width && (y as u32) < self.height {
373 let idx = (y as usize) * stride + (x as usize);
374 if idx < N {
375 self.pixels[idx] = c;
376 }
377 }
378 } else {
379 return Ok(());
380 }
381 }
382 }
383 Ok(())
384 }
385
386 fn clear(&mut self, color: Self::Color) -> Result<(), Self::Error> {
387 let len = self.len();
388 self.pixels[..len].fill(color);
389 Ok(())
390 }
391}
392
393impl<const N: usize> PixelRead for Framebuffer<N> {
394 fn get_pixel(&self, point: Point) -> Rgb565 {
395 match self.index(point.x, point.y) {
396 Some(idx) => self.pixels[idx],
397 None => Rgb565::BLACK,
398 }
399 }
400}
401
402#[derive(Clone, Debug, PartialEq, Eq)]
404pub struct FramebufferRgba8888<const N: usize> {
405 pixels: [Rgba8888; N],
406 width: u32,
407 height: u32,
408}
409
410impl<const N: usize> FramebufferRgba8888<N> {
411 pub fn new(width: u32, height: u32) -> Self {
412 assert!(
413 (width as usize) * (height as usize) <= N,
414 "Framebuffer backing array too small for width * height",
415 );
416 Self {
417 pixels: [Rgba8888::BLACK; N],
418 width,
419 height,
420 }
421 }
422
423 pub fn clear_color(&mut self, color: Rgba8888) {
424 let len = self.len();
425 for p in self.pixels[..len].iter_mut() {
426 *p = color;
427 }
428 }
429
430 pub fn pixels(&self) -> &[Rgba8888] {
431 &self.pixels[..self.len()]
432 }
433
434 pub fn pixels_mut(&mut self) -> &mut [Rgba8888] {
435 let len = self.len();
436 &mut self.pixels[..len]
437 }
438
439 pub const fn width(&self) -> u32 {
440 self.width
441 }
442
443 pub const fn height(&self) -> u32 {
444 self.height
445 }
446
447 #[inline]
448 fn len(&self) -> usize {
449 (self.width as usize) * (self.height as usize)
450 }
451
452 #[inline]
453 fn index(&self, x: i32, y: i32) -> Option<usize> {
454 if x < 0 || y < 0 || x as u32 >= self.width || y as u32 >= self.height {
455 return None;
456 }
457 Some((y as usize) * (self.width as usize) + (x as usize))
458 }
459
460 pub fn apply_iir_blur(&mut self, blur_degree: u8) {
461 let rect = Rect::new(0, 0, self.width, self.height);
462 self.blur_rect(rect, blur_degree);
463 }
464
465 pub fn blur_rect(&mut self, rect: Rect, blur_degree: u8) {
466 if blur_degree == 0 || self.width == 0 || self.height == 0 {
467 return;
468 }
469 let x0 = rect.x.max(0) as u32;
470 let y0 = rect.y.max(0) as u32;
471 let x1 = (rect.right() as u32).min(self.width);
472 let y1 = (rect.bottom() as u32).min(self.height);
473
474 if x0 >= x1 || y0 >= y1 {
475 return;
476 }
477 let alpha = 256 - (blur_degree as i32);
478 let w = self.width as usize;
479
480 for y in y0..y1 {
482 let row_start = y as usize * w;
483 let p0 = self.pixels[row_start + x0 as usize];
484 let mut acc_r = (p0.r as i32) << 8;
485 let mut acc_g = (p0.g as i32) << 8;
486 let mut acc_b = (p0.b as i32) << 8;
487 let mut acc_a = (p0.a as i32) << 8;
488
489 for x in x0..x1 {
490 let idx = row_start + x as usize;
491 let p = self.pixels[idx];
492 acc_r += ((((p.r as i32) << 8) - acc_r) * alpha) >> 8;
493 acc_g += ((((p.g as i32) << 8) - acc_g) * alpha) >> 8;
494 acc_b += ((((p.b as i32) << 8) - acc_b) * alpha) >> 8;
495 acc_a += ((((p.a as i32) << 8) - acc_a) * alpha) >> 8;
496
497 self.pixels[idx] = Rgba8888 {
498 r: (acc_r >> 8).clamp(0, 255) as u8,
499 g: (acc_g >> 8).clamp(0, 255) as u8,
500 b: (acc_b >> 8).clamp(0, 255) as u8,
501 a: (acc_a >> 8).clamp(0, 255) as u8,
502 };
503 }
504 }
505
506 for y in y0..y1 {
508 let row_start = y as usize * w;
509 let p_last = self.pixels[row_start + (x1 - 1) as usize];
510 let mut acc_r = (p_last.r as i32) << 8;
511 let mut acc_g = (p_last.g as i32) << 8;
512 let mut acc_b = (p_last.b as i32) << 8;
513 let mut acc_a = (p_last.a as i32) << 8;
514
515 for x in (x0..x1).rev() {
516 let idx = row_start + x as usize;
517 let p = self.pixels[idx];
518 acc_r += ((((p.r as i32) << 8) - acc_r) * alpha) >> 8;
519 acc_g += ((((p.g as i32) << 8) - acc_g) * alpha) >> 8;
520 acc_b += ((((p.b as i32) << 8) - acc_b) * alpha) >> 8;
521 acc_a += ((((p.a as i32) << 8) - acc_a) * alpha) >> 8;
522
523 self.pixels[idx] = Rgba8888 {
524 r: (acc_r >> 8).clamp(0, 255) as u8,
525 g: (acc_g >> 8).clamp(0, 255) as u8,
526 b: (acc_b >> 8).clamp(0, 255) as u8,
527 a: (acc_a >> 8).clamp(0, 255) as u8,
528 };
529 }
530 }
531
532 for x in x0..x1 {
534 let p0 = self.pixels[y0 as usize * w + x as usize];
535 let mut acc_r = (p0.r as i32) << 8;
536 let mut acc_g = (p0.g as i32) << 8;
537 let mut acc_b = (p0.b as i32) << 8;
538 let mut acc_a = (p0.a as i32) << 8;
539
540 for y in y0..y1 {
541 let idx = y as usize * w + x as usize;
542 let p = self.pixels[idx];
543 acc_r += ((((p.r as i32) << 8) - acc_r) * alpha) >> 8;
544 acc_g += ((((p.g as i32) << 8) - acc_g) * alpha) >> 8;
545 acc_b += ((((p.b as i32) << 8) - acc_b) * alpha) >> 8;
546 acc_a += ((((p.a as i32) << 8) - acc_a) * alpha) >> 8;
547
548 self.pixels[idx] = Rgba8888 {
549 r: (acc_r >> 8).clamp(0, 255) as u8,
550 g: (acc_g >> 8).clamp(0, 255) as u8,
551 b: (acc_b >> 8).clamp(0, 255) as u8,
552 a: (acc_a >> 8).clamp(0, 255) as u8,
553 };
554 }
555 }
556
557 for x in x0..x1 {
559 let p_last = self.pixels[(y1 - 1) as usize * w + x as usize];
560 let mut acc_r = (p_last.r as i32) << 8;
561 let mut acc_g = (p_last.g as i32) << 8;
562 let mut acc_b = (p_last.b as i32) << 8;
563 let mut acc_a = (p_last.a as i32) << 8;
564
565 for y in (y0..y1).rev() {
566 let idx = y as usize * w + x as usize;
567 let p = self.pixels[idx];
568 acc_r += ((((p.r as i32) << 8) - acc_r) * alpha) >> 8;
569 acc_g += ((((p.g as i32) << 8) - acc_g) * alpha) >> 8;
570 acc_b += ((((p.b as i32) << 8) - acc_b) * alpha) >> 8;
571 acc_a += ((((p.a as i32) << 8) - acc_a) * alpha) >> 8;
572
573 self.pixels[idx] = Rgba8888 {
574 r: (acc_r >> 8).clamp(0, 255) as u8,
575 g: (acc_g >> 8).clamp(0, 255) as u8,
576 b: (acc_b >> 8).clamp(0, 255) as u8,
577 a: (acc_a >> 8).clamp(0, 255) as u8,
578 };
579 }
580 }
581 }
582
583 pub fn reverse_colour_rect(&mut self, rect: Rect) {
585 let x0 = rect.x.max(0) as u32;
586 let y0 = rect.y.max(0) as u32;
587 let x1 = (rect.right() as u32).min(self.width);
588 let y1 = (rect.bottom() as u32).min(self.height);
589
590 if x0 >= x1 || y0 >= y1 {
591 return;
592 }
593
594 let w = self.width as usize;
595 for y in y0..y1 {
596 let row_start = y as usize * w;
597 for x in x0..x1 {
598 let idx = row_start + x as usize;
599 let p = self.pixels[idx];
600 self.pixels[idx] = Rgba8888 {
601 r: 255 - p.r,
602 g: 255 - p.g,
603 b: 255 - p.b,
604 a: p.a,
605 };
606 }
607 }
608 }
609
610 pub fn apply_reverse_colour(&mut self) {
612 let rect = Rect::new(0, 0, self.width, self.height);
613 self.reverse_colour_rect(rect);
614 }
615}
616
617impl<const N: usize> OriginDimensions for FramebufferRgba8888<N> {
618 fn size(&self) -> Size {
619 Size::new(self.width, self.height)
620 }
621}
622
623impl<const N: usize> DrawTarget for FramebufferRgba8888<N> {
624 type Color = Rgba8888;
625 type Error = core::convert::Infallible;
626
627 fn draw_iter<I>(&mut self, pixels: I) -> Result<(), Self::Error>
628 where
629 I: IntoIterator<Item = Pixel<Self::Color>>,
630 {
631 for Pixel(point, color) in pixels {
632 if let Some(idx) = self.index(point.x, point.y) {
633 self.pixels[idx] = color;
634 }
635 }
636 Ok(())
637 }
638
639 fn fill_solid(&mut self, area: &Rectangle, color: Self::Color) -> Result<(), Self::Error> {
640 let intersect = area.intersection(&Rectangle::new(Point::zero(), self.size()));
641 if intersect.is_zero_sized() {
642 return Ok(());
643 }
644 let x0 = intersect.top_left.x as usize;
645 let y0 = intersect.top_left.y as usize;
646 let w = intersect.size.width as usize;
647 let h = intersect.size.height as usize;
648 let stride = self.width as usize;
649
650 for row in 0..h {
651 let start = (y0 + row) * stride + x0;
652 if start + w <= N {
653 self.pixels[start..start + w].fill(color);
654 }
655 }
656 Ok(())
657 }
658
659 fn fill_contiguous<I>(&mut self, area: &Rectangle, colors: I) -> Result<(), Self::Error>
660 where
661 I: IntoIterator<Item = Self::Color>,
662 {
663 let intersect = area.intersection(&Rectangle::new(Point::zero(), self.size()));
664 if intersect.is_zero_sized() {
665 return Ok(());
666 }
667 let mut colors = colors.into_iter();
668 let stride = self.width as usize;
669 let x_end = area.top_left.x + area.size.width as i32;
670 let y_end = area.top_left.y + area.size.height as i32;
671
672 for y in area.top_left.y..y_end {
673 for x in area.top_left.x..x_end {
674 if let Some(c) = colors.next() {
675 if x >= 0 && y >= 0 && (x as u32) < self.width && (y as u32) < self.height {
676 let idx = (y as usize) * stride + (x as usize);
677 if idx < N {
678 self.pixels[idx] = c;
679 }
680 }
681 } else {
682 return Ok(());
683 }
684 }
685 }
686 Ok(())
687 }
688
689 fn clear(&mut self, color: Self::Color) -> Result<(), Self::Error> {
690 let len = self.len();
691 self.pixels[..len].fill(color);
692 Ok(())
693 }
694}
695
696impl<const N: usize> PixelRead for FramebufferRgba8888<N> {
697 fn get_pixel(&self, point: Point) -> Rgba8888 {
698 match self.index(point.x, point.y) {
699 Some(idx) => self.pixels[idx],
700 None => Rgba8888::TRANSPARENT,
701 }
702 }
703}
704
705#[derive(Clone, Debug, PartialEq, Eq)]
707pub struct FramebufferGray8<const N: usize> {
708 pixels: [Gray8; N],
709 width: u32,
710 height: u32,
711}
712
713impl<const N: usize> FramebufferGray8<N> {
714 pub fn new(width: u32, height: u32) -> Self {
715 assert!(
716 (width as usize) * (height as usize) <= N,
717 "Framebuffer backing array too small for width * height",
718 );
719 Self {
720 pixels: [Gray8::new(0); N],
721 width,
722 height,
723 }
724 }
725
726 pub fn clear_color(&mut self, color: Gray8) {
727 let len = self.len();
728 for p in self.pixels[..len].iter_mut() {
729 *p = color;
730 }
731 }
732
733 pub fn pixels(&self) -> &[Gray8] {
734 &self.pixels[..self.len()]
735 }
736
737 pub fn pixels_mut(&mut self) -> &mut [Gray8] {
738 let len = self.len();
739 &mut self.pixels[..len]
740 }
741
742 pub const fn width(&self) -> u32 {
743 self.width
744 }
745
746 pub const fn height(&self) -> u32 {
747 self.height
748 }
749
750 #[inline]
751 fn len(&self) -> usize {
752 (self.width as usize) * (self.height as usize)
753 }
754
755 #[inline]
756 fn index(&self, x: i32, y: i32) -> Option<usize> {
757 if x < 0 || y < 0 || x as u32 >= self.width || y as u32 >= self.height {
758 return None;
759 }
760 Some((y as usize) * (self.width as usize) + (x as usize))
761 }
762
763 pub fn apply_iir_blur(&mut self, blur_degree: u8) {
764 let rect = Rect::new(0, 0, self.width, self.height);
765 self.blur_rect(rect, blur_degree);
766 }
767
768 pub fn blur_rect(&mut self, rect: Rect, blur_degree: u8) {
769 if blur_degree == 0 || self.width == 0 || self.height == 0 {
770 return;
771 }
772 let x0 = rect.x.max(0) as u32;
773 let y0 = rect.y.max(0) as u32;
774 let x1 = (rect.right() as u32).min(self.width);
775 let y1 = (rect.bottom() as u32).min(self.height);
776
777 if x0 >= x1 || y0 >= y1 {
778 return;
779 }
780 let alpha = 256 - (blur_degree as i32);
781 let w = self.width as usize;
782
783 for y in y0..y1 {
785 let row_start = y as usize * w;
786 let p0 = self.pixels[row_start + x0 as usize].luma();
787 let mut acc = (p0 as i32) << 8;
788
789 for x in x0..x1 {
790 let idx = row_start + x as usize;
791 let luma = self.pixels[idx].luma();
792 acc += ((((luma as i32) << 8) - acc) * alpha) >> 8;
793 self.pixels[idx] = Gray8::new((acc >> 8).clamp(0, 255) as u8);
794 }
795 }
796
797 for y in y0..y1 {
799 let row_start = y as usize * w;
800 let p_last = self.pixels[row_start + (x1 - 1) as usize].luma();
801 let mut acc = (p_last as i32) << 8;
802
803 for x in (x0..x1).rev() {
804 let idx = row_start + x as usize;
805 let luma = self.pixels[idx].luma();
806 acc += ((((luma as i32) << 8) - acc) * alpha) >> 8;
807 self.pixels[idx] = Gray8::new((acc >> 8).clamp(0, 255) as u8);
808 }
809 }
810
811 for x in x0..x1 {
813 let p0 = self.pixels[y0 as usize * w + x as usize].luma();
814 let mut acc = (p0 as i32) << 8;
815
816 for y in y0..y1 {
817 let idx = y as usize * w + x as usize;
818 let luma = self.pixels[idx].luma();
819 acc += ((((luma as i32) << 8) - acc) * alpha) >> 8;
820 self.pixels[idx] = Gray8::new((acc >> 8).clamp(0, 255) as u8);
821 }
822 }
823
824 for x in x0..x1 {
826 let p_last = self.pixels[(y1 - 1) as usize * w + x as usize].luma();
827 let mut acc = (p_last as i32) << 8;
828
829 for y in (y0..y1).rev() {
830 let idx = y as usize * w + x as usize;
831 let luma = self.pixels[idx].luma();
832 acc += ((((luma as i32) << 8) - acc) * alpha) >> 8;
833 self.pixels[idx] = Gray8::new((acc >> 8).clamp(0, 255) as u8);
834 }
835 }
836 }
837}
838
839impl<const N: usize> OriginDimensions for FramebufferGray8<N> {
840 fn size(&self) -> Size {
841 Size::new(self.width, self.height)
842 }
843}
844
845impl<const N: usize> DrawTarget for FramebufferGray8<N> {
846 type Color = Gray8;
847 type Error = core::convert::Infallible;
848
849 fn draw_iter<I>(&mut self, pixels: I) -> Result<(), Self::Error>
850 where
851 I: IntoIterator<Item = Pixel<Self::Color>>,
852 {
853 for Pixel(point, color) in pixels {
854 if let Some(idx) = self.index(point.x, point.y) {
855 self.pixels[idx] = color;
856 }
857 }
858 Ok(())
859 }
860
861 fn fill_solid(&mut self, area: &Rectangle, color: Self::Color) -> Result<(), Self::Error> {
862 let intersect = area.intersection(&Rectangle::new(Point::zero(), self.size()));
863 if intersect.is_zero_sized() {
864 return Ok(());
865 }
866 let x0 = intersect.top_left.x as usize;
867 let y0 = intersect.top_left.y as usize;
868 let w = intersect.size.width as usize;
869 let h = intersect.size.height as usize;
870 let stride = self.width as usize;
871
872 for row in 0..h {
873 let start = (y0 + row) * stride + x0;
874 if start + w <= N {
875 self.pixels[start..start + w].fill(color);
876 }
877 }
878 Ok(())
879 }
880
881 fn fill_contiguous<I>(&mut self, area: &Rectangle, colors: I) -> Result<(), Self::Error>
882 where
883 I: IntoIterator<Item = Self::Color>,
884 {
885 let intersect = area.intersection(&Rectangle::new(Point::zero(), self.size()));
886 if intersect.is_zero_sized() {
887 return Ok(());
888 }
889 let mut colors = colors.into_iter();
890 let stride = self.width as usize;
891 let x_end = area.top_left.x + area.size.width as i32;
892 let y_end = area.top_left.y + area.size.height as i32;
893
894 for y in area.top_left.y..y_end {
895 for x in area.top_left.x..x_end {
896 if let Some(c) = colors.next() {
897 if x >= 0 && y >= 0 && (x as u32) < self.width && (y as u32) < self.height {
898 let idx = (y as usize) * stride + (x as usize);
899 if idx < N {
900 self.pixels[idx] = c;
901 }
902 }
903 } else {
904 return Ok(());
905 }
906 }
907 }
908 Ok(())
909 }
910
911 fn clear(&mut self, color: Self::Color) -> Result<(), Self::Error> {
912 let len = self.len();
913 self.pixels[..len].fill(color);
914 Ok(())
915 }
916}
917
918impl<const N: usize> PixelRead for FramebufferGray8<N> {
919 fn get_pixel(&self, point: Point) -> Gray8 {
920 match self.index(point.x, point.y) {
921 Some(idx) => self.pixels[idx],
922 None => Gray8::new(0),
923 }
924 }
925}
926
927#[cfg(test)]
928mod tests {
929 use super::*;
930
931 #[test]
932 fn test_iir_blur_rgb565() {
933 let mut fb = Framebuffer::<100>::new(10, 10);
934 fb.clear_color(Rgb565::WHITE);
935 for y in 3..7 {
936 for x in 3..7 {
937 fb.pixels_mut()[y * 10 + x] = Rgb565::BLACK;
938 }
939 }
940 fb.apply_iir_blur(128);
941 let center_pixel = fb.pixels()[5 * 10 + 5];
942 assert_ne!(center_pixel, Rgb565::BLACK);
943 }
944
945 #[test]
946 fn test_iir_blur_rgba8888() {
947 let mut fb = FramebufferRgba8888::<100>::new(10, 10);
948 fb.clear_color(Rgba8888::WHITE);
949 for y in 3..7 {
950 for x in 3..7 {
951 fb.pixels_mut()[y * 10 + x] = Rgba8888::BLACK;
952 }
953 }
954 fb.apply_iir_blur(128);
955 let center_pixel = fb.pixels()[5 * 10 + 5];
956 assert_ne!(center_pixel, Rgba8888::BLACK);
957 assert!(center_pixel.r > 0);
958 }
959
960 #[test]
961 fn test_iir_blur_gray8() {
962 let mut fb = FramebufferGray8::<100>::new(10, 10);
963 fb.clear_color(Gray8::new(255));
964 for y in 3..7 {
965 for x in 3..7 {
966 fb.pixels_mut()[y * 10 + x] = Gray8::new(0);
967 }
968 }
969 fb.apply_iir_blur(128);
970 let center_pixel = fb.pixels()[5 * 10 + 5].luma();
971 assert!(center_pixel > 0 && center_pixel < 255);
972 }
973}