1use embedded_graphics_core::{
13 Pixel,
14 draw_target::DrawTarget,
15 geometry::{OriginDimensions, Point, Size},
16 pixelcolor::{Gray8, GrayColor, Rgb565, RgbColor},
17};
18
19use crate::geometry::Rect;
20use crate::render::PixelRead;
21
22#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
24pub struct Rgba8888 {
25 pub r: u8,
26 pub g: u8,
27 pub b: u8,
28 pub a: u8,
29}
30
31impl Rgba8888 {
32 pub const BLACK: Self = Self {
33 r: 0,
34 g: 0,
35 b: 0,
36 a: 255,
37 };
38 pub const WHITE: Self = Self {
39 r: 255,
40 g: 255,
41 b: 255,
42 a: 255,
43 };
44 pub const TRANSPARENT: Self = Self {
45 r: 0,
46 g: 0,
47 b: 0,
48 a: 0,
49 };
50
51 pub const fn new(r: u8, g: u8, b: u8, a: u8) -> Self {
52 Self { r, g, b, a }
53 }
54
55 pub fn from_rgb565(rgb: Rgb565, alpha: u8) -> Self {
56 let r5 = rgb.r();
57 let g6 = rgb.g();
58 let b5 = rgb.b();
59 let r8 = (r5 << 3) | (r5 >> 2);
60 let g8 = (g6 << 2) | (g6 >> 4);
61 let b8 = (b5 << 3) | (b5 >> 2);
62 Self {
63 r: r8,
64 g: g8,
65 b: b8,
66 a: alpha,
67 }
68 }
69
70 pub fn to_rgb565(self) -> Rgb565 {
71 Rgb565::new(self.r >> 3, self.g >> 2, self.b >> 3)
72 }
73}
74
75impl embedded_graphics_core::pixelcolor::PixelColor for Rgba8888 {
76 type Raw = embedded_graphics_core::pixelcolor::raw::RawU32;
77}
78
79#[derive(Clone, Debug, PartialEq, Eq)]
82pub struct Framebuffer<const N: usize> {
83 pixels: [Rgb565; N],
84 width: u32,
85 height: u32,
86}
87
88impl<const N: usize> Framebuffer<N> {
89 pub fn new(width: u32, height: u32) -> Self {
93 assert!(
94 (width as usize) * (height as usize) <= N,
95 "Framebuffer backing array too small for width * height",
96 );
97 Self {
98 pixels: [Rgb565::BLACK; N],
99 width,
100 height,
101 }
102 }
103
104 pub fn clear_color(&mut self, color: Rgb565) {
106 let len = self.len();
107 for p in self.pixels[..len].iter_mut() {
108 *p = color;
109 }
110 }
111
112 pub fn pixels(&self) -> &[Rgb565] {
115 &self.pixels[..self.len()]
116 }
117
118 pub fn pixels_mut(&mut self) -> &mut [Rgb565] {
120 let len = self.len();
121 &mut self.pixels[..len]
122 }
123
124 pub const fn width(&self) -> u32 {
125 self.width
126 }
127
128 pub const fn height(&self) -> u32 {
129 self.height
130 }
131
132 #[inline]
133 fn len(&self) -> usize {
134 (self.width as usize) * (self.height as usize)
135 }
136
137 #[inline]
138 fn index(&self, x: i32, y: i32) -> Option<usize> {
139 if x < 0 || y < 0 || x as u32 >= self.width || y as u32 >= self.height {
140 return None;
141 }
142 Some((y as usize) * (self.width as usize) + (x as usize))
143 }
144
145 pub fn apply_iir_blur(&mut self, blur_degree: u8) {
147 let rect = Rect::new(0, 0, self.width, self.height);
148 self.blur_rect(rect, blur_degree);
149 }
150
151 pub fn blur_rect(&mut self, rect: Rect, blur_degree: u8) {
153 if blur_degree == 0 || self.width == 0 || self.height == 0 {
154 return;
155 }
156
157 let x0 = rect.x.max(0) as u32;
158 let y0 = rect.y.max(0) as u32;
159 let x1 = (rect.right() as u32).min(self.width);
160 let y1 = (rect.bottom() as u32).min(self.height);
161
162 if x0 >= x1 || y0 >= y1 {
163 return;
164 }
165
166 let alpha = 256 - (blur_degree as i32);
167 let w = self.width as usize;
168
169 for y in y0..y1 {
171 let row_start = y as usize * w;
172 let idx0 = row_start + x0 as usize;
173 let raw0 = self.pixels[idx0];
174 let (r5, g6, b5) = (raw0.r(), raw0.g(), raw0.b());
175 let mut acc_r = (((r5 << 3) | (r5 >> 2)) as i32) << 8;
176 let mut acc_g = (((g6 << 2) | (g6 >> 4)) as i32) << 8;
177 let mut acc_b = (((b5 << 3) | (b5 >> 2)) as i32) << 8;
178
179 for x in x0..x1 {
180 let idx = row_start + x as usize;
181 let raw = self.pixels[idx];
182 let (r5, g6, b5) = (raw.r(), raw.g(), raw.b());
183 let r8 = ((r5 << 3) | (r5 >> 2)) as i32;
184 let g8 = ((g6 << 2) | (g6 >> 4)) as i32;
185 let b8 = ((b5 << 3) | (b5 >> 2)) as i32;
186
187 acc_r += (((r8 << 8) - acc_r) * alpha) >> 8;
188 acc_g += (((g8 << 8) - acc_g) * alpha) >> 8;
189 acc_b += (((b8 << 8) - acc_b) * alpha) >> 8;
190
191 let r_out = ((acc_r >> 8).clamp(0, 255) as u8) >> 3;
192 let g_out = ((acc_g >> 8).clamp(0, 255) as u8) >> 2;
193 let b_out = ((acc_b >> 8).clamp(0, 255) as u8) >> 3;
194 self.pixels[idx] = Rgb565::new(r_out, g_out, b_out);
195 }
196 }
197
198 for y in y0..y1 {
200 let row_start = y as usize * w;
201 let idx_last = row_start + (x1 - 1) as usize;
202 let raw_last = self.pixels[idx_last];
203 let (r5, g6, b5) = (raw_last.r(), raw_last.g(), raw_last.b());
204 let mut acc_r = (((r5 << 3) | (r5 >> 2)) as i32) << 8;
205 let mut acc_g = (((g6 << 2) | (g6 >> 4)) as i32) << 8;
206 let mut acc_b = (((b5 << 3) | (b5 >> 2)) as i32) << 8;
207
208 for x in (x0..x1).rev() {
209 let idx = row_start + x as usize;
210 let raw = self.pixels[idx];
211 let (r5, g6, b5) = (raw.r(), raw.g(), raw.b());
212 let r8 = ((r5 << 3) | (r5 >> 2)) as i32;
213 let g8 = ((g6 << 2) | (g6 >> 4)) as i32;
214 let b8 = ((b5 << 3) | (b5 >> 2)) as i32;
215
216 acc_r += (((r8 << 8) - acc_r) * alpha) >> 8;
217 acc_g += (((g8 << 8) - acc_g) * alpha) >> 8;
218 acc_b += (((b8 << 8) - acc_b) * alpha) >> 8;
219
220 let r_out = ((acc_r >> 8).clamp(0, 255) as u8) >> 3;
221 let g_out = ((acc_g >> 8).clamp(0, 255) as u8) >> 2;
222 let b_out = ((acc_b >> 8).clamp(0, 255) as u8) >> 3;
223 self.pixels[idx] = Rgb565::new(r_out, g_out, b_out);
224 }
225 }
226
227 for x in x0..x1 {
229 let idx0 = y0 as usize * w + x as usize;
230 let raw0 = self.pixels[idx0];
231 let (r5, g6, b5) = (raw0.r(), raw0.g(), raw0.b());
232 let mut acc_r = (((r5 << 3) | (r5 >> 2)) as i32) << 8;
233 let mut acc_g = (((g6 << 2) | (g6 >> 4)) as i32) << 8;
234 let mut acc_b = (((b5 << 3) | (b5 >> 2)) as i32) << 8;
235
236 for y in y0..y1 {
237 let idx = y as usize * w + x as usize;
238 let raw = self.pixels[idx];
239 let (r5, g6, b5) = (raw.r(), raw.g(), raw.b());
240 let r8 = ((r5 << 3) | (r5 >> 2)) as i32;
241 let g8 = ((g6 << 2) | (g6 >> 4)) as i32;
242 let b8 = ((b5 << 3) | (b5 >> 2)) as i32;
243
244 acc_r += (((r8 << 8) - acc_r) * alpha) >> 8;
245 acc_g += (((g8 << 8) - acc_g) * alpha) >> 8;
246 acc_b += (((b8 << 8) - acc_b) * alpha) >> 8;
247
248 let r_out = ((acc_r >> 8).clamp(0, 255) as u8) >> 3;
249 let g_out = ((acc_g >> 8).clamp(0, 255) as u8) >> 2;
250 let b_out = ((acc_b >> 8).clamp(0, 255) as u8) >> 3;
251 self.pixels[idx] = Rgb565::new(r_out, g_out, b_out);
252 }
253 }
254
255 for x in x0..x1 {
257 let idx_last = (y1 - 1) as usize * w + x as usize;
258 let raw_last = self.pixels[idx_last];
259 let (r5, g6, b5) = (raw_last.r(), raw_last.g(), raw_last.b());
260 let mut acc_r = (((r5 << 3) | (r5 >> 2)) as i32) << 8;
261 let mut acc_g = (((g6 << 2) | (g6 >> 4)) as i32) << 8;
262 let mut acc_b = (((b5 << 3) | (b5 >> 2)) as i32) << 8;
263
264 for y in (y0..y1).rev() {
265 let idx = y as usize * w + x as usize;
266 let raw = self.pixels[idx];
267 let (r5, g6, b5) = (raw.r(), raw.g(), raw.b());
268 let r8 = ((r5 << 3) | (r5 >> 2)) as i32;
269 let g8 = ((g6 << 2) | (g6 >> 4)) as i32;
270 let b8 = ((b5 << 3) | (b5 >> 2)) as i32;
271
272 acc_r += (((r8 << 8) - acc_r) * alpha) >> 8;
273 acc_g += (((g8 << 8) - acc_g) * alpha) >> 8;
274 acc_b += (((b8 << 8) - acc_b) * alpha) >> 8;
275
276 let r_out = ((acc_r >> 8).clamp(0, 255) as u8) >> 3;
277 let g_out = ((acc_g >> 8).clamp(0, 255) as u8) >> 2;
278 let b_out = ((acc_b >> 8).clamp(0, 255) as u8) >> 3;
279 self.pixels[idx] = Rgb565::new(r_out, g_out, b_out);
280 }
281 }
282 }
283
284 pub fn reverse_colour_rect(&mut self, rect: Rect) {
286 let x0 = rect.x.max(0) as u32;
287 let y0 = rect.y.max(0) as u32;
288 let x1 = (rect.right() as u32).min(self.width);
289 let y1 = (rect.bottom() as u32).min(self.height);
290
291 if x0 >= x1 || y0 >= y1 {
292 return;
293 }
294
295 let w = self.width as usize;
296 for y in y0..y1 {
297 let row_start = y as usize * w;
298 for x in x0..x1 {
299 let idx = row_start + x as usize;
300 let c = self.pixels[idx];
301 self.pixels[idx] = Rgb565::new(31 - c.r(), 63 - c.g(), 31 - c.b());
302 }
303 }
304 }
305
306 pub fn apply_reverse_colour(&mut self) {
308 let rect = Rect::new(0, 0, self.width, self.height);
309 self.reverse_colour_rect(rect);
310 }
311}
312
313impl<const N: usize> OriginDimensions for Framebuffer<N> {
314 fn size(&self) -> Size {
315 Size::new(self.width, self.height)
316 }
317}
318
319impl<const N: usize> DrawTarget for Framebuffer<N> {
320 type Color = Rgb565;
321 type Error = core::convert::Infallible;
322
323 fn draw_iter<I>(&mut self, pixels: I) -> Result<(), Self::Error>
324 where
325 I: IntoIterator<Item = Pixel<Self::Color>>,
326 {
327 for Pixel(point, color) in pixels {
328 if let Some(idx) = self.index(point.x, point.y) {
329 self.pixels[idx] = color;
330 }
331 }
332 Ok(())
333 }
334}
335
336impl<const N: usize> PixelRead for Framebuffer<N> {
337 fn get_pixel(&self, point: Point) -> Rgb565 {
338 match self.index(point.x, point.y) {
339 Some(idx) => self.pixels[idx],
340 None => Rgb565::BLACK,
341 }
342 }
343}
344
345#[derive(Clone, Debug, PartialEq, Eq)]
347pub struct FramebufferRgba8888<const N: usize> {
348 pixels: [Rgba8888; N],
349 width: u32,
350 height: u32,
351}
352
353impl<const N: usize> FramebufferRgba8888<N> {
354 pub fn new(width: u32, height: u32) -> Self {
355 assert!(
356 (width as usize) * (height as usize) <= N,
357 "Framebuffer backing array too small for width * height",
358 );
359 Self {
360 pixels: [Rgba8888::BLACK; N],
361 width,
362 height,
363 }
364 }
365
366 pub fn clear_color(&mut self, color: Rgba8888) {
367 let len = self.len();
368 for p in self.pixels[..len].iter_mut() {
369 *p = color;
370 }
371 }
372
373 pub fn pixels(&self) -> &[Rgba8888] {
374 &self.pixels[..self.len()]
375 }
376
377 pub fn pixels_mut(&mut self) -> &mut [Rgba8888] {
378 let len = self.len();
379 &mut self.pixels[..len]
380 }
381
382 pub const fn width(&self) -> u32 {
383 self.width
384 }
385
386 pub const fn height(&self) -> u32 {
387 self.height
388 }
389
390 #[inline]
391 fn len(&self) -> usize {
392 (self.width as usize) * (self.height as usize)
393 }
394
395 #[inline]
396 fn index(&self, x: i32, y: i32) -> Option<usize> {
397 if x < 0 || y < 0 || x as u32 >= self.width || y as u32 >= self.height {
398 return None;
399 }
400 Some((y as usize) * (self.width as usize) + (x as usize))
401 }
402
403 pub fn apply_iir_blur(&mut self, blur_degree: u8) {
404 let rect = Rect::new(0, 0, self.width, self.height);
405 self.blur_rect(rect, blur_degree);
406 }
407
408 pub fn blur_rect(&mut self, rect: Rect, blur_degree: u8) {
409 if blur_degree == 0 || self.width == 0 || self.height == 0 {
410 return;
411 }
412 let x0 = rect.x.max(0) as u32;
413 let y0 = rect.y.max(0) as u32;
414 let x1 = (rect.right() as u32).min(self.width);
415 let y1 = (rect.bottom() as u32).min(self.height);
416
417 if x0 >= x1 || y0 >= y1 {
418 return;
419 }
420 let alpha = 256 - (blur_degree as i32);
421 let w = self.width as usize;
422
423 for y in y0..y1 {
425 let row_start = y as usize * w;
426 let p0 = self.pixels[row_start + x0 as usize];
427 let mut acc_r = (p0.r as i32) << 8;
428 let mut acc_g = (p0.g as i32) << 8;
429 let mut acc_b = (p0.b as i32) << 8;
430 let mut acc_a = (p0.a as i32) << 8;
431
432 for x in x0..x1 {
433 let idx = row_start + x as usize;
434 let p = self.pixels[idx];
435 acc_r += ((((p.r as i32) << 8) - acc_r) * alpha) >> 8;
436 acc_g += ((((p.g as i32) << 8) - acc_g) * alpha) >> 8;
437 acc_b += ((((p.b as i32) << 8) - acc_b) * alpha) >> 8;
438 acc_a += ((((p.a as i32) << 8) - acc_a) * alpha) >> 8;
439
440 self.pixels[idx] = Rgba8888 {
441 r: (acc_r >> 8).clamp(0, 255) as u8,
442 g: (acc_g >> 8).clamp(0, 255) as u8,
443 b: (acc_b >> 8).clamp(0, 255) as u8,
444 a: (acc_a >> 8).clamp(0, 255) as u8,
445 };
446 }
447 }
448
449 for y in y0..y1 {
451 let row_start = y as usize * w;
452 let p_last = self.pixels[row_start + (x1 - 1) as usize];
453 let mut acc_r = (p_last.r as i32) << 8;
454 let mut acc_g = (p_last.g as i32) << 8;
455 let mut acc_b = (p_last.b as i32) << 8;
456 let mut acc_a = (p_last.a as i32) << 8;
457
458 for x in (x0..x1).rev() {
459 let idx = row_start + x as usize;
460 let p = self.pixels[idx];
461 acc_r += ((((p.r as i32) << 8) - acc_r) * alpha) >> 8;
462 acc_g += ((((p.g as i32) << 8) - acc_g) * alpha) >> 8;
463 acc_b += ((((p.b as i32) << 8) - acc_b) * alpha) >> 8;
464 acc_a += ((((p.a as i32) << 8) - acc_a) * alpha) >> 8;
465
466 self.pixels[idx] = Rgba8888 {
467 r: (acc_r >> 8).clamp(0, 255) as u8,
468 g: (acc_g >> 8).clamp(0, 255) as u8,
469 b: (acc_b >> 8).clamp(0, 255) as u8,
470 a: (acc_a >> 8).clamp(0, 255) as u8,
471 };
472 }
473 }
474
475 for x in x0..x1 {
477 let p0 = self.pixels[y0 as usize * w + x as usize];
478 let mut acc_r = (p0.r as i32) << 8;
479 let mut acc_g = (p0.g as i32) << 8;
480 let mut acc_b = (p0.b as i32) << 8;
481 let mut acc_a = (p0.a as i32) << 8;
482
483 for y in y0..y1 {
484 let idx = y as usize * w + x as usize;
485 let p = self.pixels[idx];
486 acc_r += ((((p.r as i32) << 8) - acc_r) * alpha) >> 8;
487 acc_g += ((((p.g as i32) << 8) - acc_g) * alpha) >> 8;
488 acc_b += ((((p.b as i32) << 8) - acc_b) * alpha) >> 8;
489 acc_a += ((((p.a as i32) << 8) - acc_a) * alpha) >> 8;
490
491 self.pixels[idx] = Rgba8888 {
492 r: (acc_r >> 8).clamp(0, 255) as u8,
493 g: (acc_g >> 8).clamp(0, 255) as u8,
494 b: (acc_b >> 8).clamp(0, 255) as u8,
495 a: (acc_a >> 8).clamp(0, 255) as u8,
496 };
497 }
498 }
499
500 for x in x0..x1 {
502 let p_last = self.pixels[(y1 - 1) as usize * w + x as usize];
503 let mut acc_r = (p_last.r as i32) << 8;
504 let mut acc_g = (p_last.g as i32) << 8;
505 let mut acc_b = (p_last.b as i32) << 8;
506 let mut acc_a = (p_last.a as i32) << 8;
507
508 for y in (y0..y1).rev() {
509 let idx = y as usize * w + x as usize;
510 let p = self.pixels[idx];
511 acc_r += ((((p.r as i32) << 8) - acc_r) * alpha) >> 8;
512 acc_g += ((((p.g as i32) << 8) - acc_g) * alpha) >> 8;
513 acc_b += ((((p.b as i32) << 8) - acc_b) * alpha) >> 8;
514 acc_a += ((((p.a as i32) << 8) - acc_a) * alpha) >> 8;
515
516 self.pixels[idx] = Rgba8888 {
517 r: (acc_r >> 8).clamp(0, 255) as u8,
518 g: (acc_g >> 8).clamp(0, 255) as u8,
519 b: (acc_b >> 8).clamp(0, 255) as u8,
520 a: (acc_a >> 8).clamp(0, 255) as u8,
521 };
522 }
523 }
524 }
525
526 pub fn reverse_colour_rect(&mut self, rect: Rect) {
528 let x0 = rect.x.max(0) as u32;
529 let y0 = rect.y.max(0) as u32;
530 let x1 = (rect.right() as u32).min(self.width);
531 let y1 = (rect.bottom() as u32).min(self.height);
532
533 if x0 >= x1 || y0 >= y1 {
534 return;
535 }
536
537 let w = self.width as usize;
538 for y in y0..y1 {
539 let row_start = y as usize * w;
540 for x in x0..x1 {
541 let idx = row_start + x as usize;
542 let p = self.pixels[idx];
543 self.pixels[idx] = Rgba8888 {
544 r: 255 - p.r,
545 g: 255 - p.g,
546 b: 255 - p.b,
547 a: p.a,
548 };
549 }
550 }
551 }
552
553 pub fn apply_reverse_colour(&mut self) {
555 let rect = Rect::new(0, 0, self.width, self.height);
556 self.reverse_colour_rect(rect);
557 }
558}
559
560impl<const N: usize> OriginDimensions for FramebufferRgba8888<N> {
561 fn size(&self) -> Size {
562 Size::new(self.width, self.height)
563 }
564}
565
566impl<const N: usize> DrawTarget for FramebufferRgba8888<N> {
567 type Color = Rgba8888;
568 type Error = core::convert::Infallible;
569
570 fn draw_iter<I>(&mut self, pixels: I) -> Result<(), Self::Error>
571 where
572 I: IntoIterator<Item = Pixel<Self::Color>>,
573 {
574 for Pixel(point, color) in pixels {
575 if let Some(idx) = self.index(point.x, point.y) {
576 self.pixels[idx] = color;
577 }
578 }
579 Ok(())
580 }
581}
582
583impl<const N: usize> PixelRead for FramebufferRgba8888<N> {
584 fn get_pixel(&self, point: Point) -> Rgba8888 {
585 match self.index(point.x, point.y) {
586 Some(idx) => self.pixels[idx],
587 None => Rgba8888::TRANSPARENT,
588 }
589 }
590}
591
592#[derive(Clone, Debug, PartialEq, Eq)]
594pub struct FramebufferGray8<const N: usize> {
595 pixels: [Gray8; N],
596 width: u32,
597 height: u32,
598}
599
600impl<const N: usize> FramebufferGray8<N> {
601 pub fn new(width: u32, height: u32) -> Self {
602 assert!(
603 (width as usize) * (height as usize) <= N,
604 "Framebuffer backing array too small for width * height",
605 );
606 Self {
607 pixels: [Gray8::new(0); N],
608 width,
609 height,
610 }
611 }
612
613 pub fn clear_color(&mut self, color: Gray8) {
614 let len = self.len();
615 for p in self.pixels[..len].iter_mut() {
616 *p = color;
617 }
618 }
619
620 pub fn pixels(&self) -> &[Gray8] {
621 &self.pixels[..self.len()]
622 }
623
624 pub fn pixels_mut(&mut self) -> &mut [Gray8] {
625 let len = self.len();
626 &mut self.pixels[..len]
627 }
628
629 pub const fn width(&self) -> u32 {
630 self.width
631 }
632
633 pub const fn height(&self) -> u32 {
634 self.height
635 }
636
637 #[inline]
638 fn len(&self) -> usize {
639 (self.width as usize) * (self.height as usize)
640 }
641
642 #[inline]
643 fn index(&self, x: i32, y: i32) -> Option<usize> {
644 if x < 0 || y < 0 || x as u32 >= self.width || y as u32 >= self.height {
645 return None;
646 }
647 Some((y as usize) * (self.width as usize) + (x as usize))
648 }
649
650 pub fn apply_iir_blur(&mut self, blur_degree: u8) {
651 let rect = Rect::new(0, 0, self.width, self.height);
652 self.blur_rect(rect, blur_degree);
653 }
654
655 pub fn blur_rect(&mut self, rect: Rect, blur_degree: u8) {
656 if blur_degree == 0 || self.width == 0 || self.height == 0 {
657 return;
658 }
659 let x0 = rect.x.max(0) as u32;
660 let y0 = rect.y.max(0) as u32;
661 let x1 = (rect.right() as u32).min(self.width);
662 let y1 = (rect.bottom() as u32).min(self.height);
663
664 if x0 >= x1 || y0 >= y1 {
665 return;
666 }
667 let alpha = 256 - (blur_degree as i32);
668 let w = self.width as usize;
669
670 for y in y0..y1 {
672 let row_start = y as usize * w;
673 let p0 = self.pixels[row_start + x0 as usize].luma();
674 let mut acc = (p0 as i32) << 8;
675
676 for x in x0..x1 {
677 let idx = row_start + x as usize;
678 let luma = self.pixels[idx].luma();
679 acc += ((((luma as i32) << 8) - acc) * alpha) >> 8;
680 self.pixels[idx] = Gray8::new((acc >> 8).clamp(0, 255) as u8);
681 }
682 }
683
684 for y in y0..y1 {
686 let row_start = y as usize * w;
687 let p_last = self.pixels[row_start + (x1 - 1) as usize].luma();
688 let mut acc = (p_last as i32) << 8;
689
690 for x in (x0..x1).rev() {
691 let idx = row_start + x as usize;
692 let luma = self.pixels[idx].luma();
693 acc += ((((luma as i32) << 8) - acc) * alpha) >> 8;
694 self.pixels[idx] = Gray8::new((acc >> 8).clamp(0, 255) as u8);
695 }
696 }
697
698 for x in x0..x1 {
700 let p0 = self.pixels[y0 as usize * w + x as usize].luma();
701 let mut acc = (p0 as i32) << 8;
702
703 for y in y0..y1 {
704 let idx = y as usize * w + x as usize;
705 let luma = self.pixels[idx].luma();
706 acc += ((((luma as i32) << 8) - acc) * alpha) >> 8;
707 self.pixels[idx] = Gray8::new((acc >> 8).clamp(0, 255) as u8);
708 }
709 }
710
711 for x in x0..x1 {
713 let p_last = self.pixels[(y1 - 1) as usize * w + x as usize].luma();
714 let mut acc = (p_last as i32) << 8;
715
716 for y in (y0..y1).rev() {
717 let idx = y as usize * w + x as usize;
718 let luma = self.pixels[idx].luma();
719 acc += ((((luma as i32) << 8) - acc) * alpha) >> 8;
720 self.pixels[idx] = Gray8::new((acc >> 8).clamp(0, 255) as u8);
721 }
722 }
723 }
724}
725
726impl<const N: usize> OriginDimensions for FramebufferGray8<N> {
727 fn size(&self) -> Size {
728 Size::new(self.width, self.height)
729 }
730}
731
732impl<const N: usize> DrawTarget for FramebufferGray8<N> {
733 type Color = Gray8;
734 type Error = core::convert::Infallible;
735
736 fn draw_iter<I>(&mut self, pixels: I) -> Result<(), Self::Error>
737 where
738 I: IntoIterator<Item = Pixel<Self::Color>>,
739 {
740 for Pixel(point, color) in pixels {
741 if let Some(idx) = self.index(point.x, point.y) {
742 self.pixels[idx] = color;
743 }
744 }
745 Ok(())
746 }
747}
748
749impl<const N: usize> PixelRead for FramebufferGray8<N> {
750 fn get_pixel(&self, point: Point) -> Gray8 {
751 match self.index(point.x, point.y) {
752 Some(idx) => self.pixels[idx],
753 None => Gray8::new(0),
754 }
755 }
756}
757
758#[cfg(test)]
759mod tests {
760 use super::*;
761
762 #[test]
763 fn test_iir_blur_rgb565() {
764 let mut fb = Framebuffer::<100>::new(10, 10);
765 fb.clear_color(Rgb565::WHITE);
766 for y in 3..7 {
767 for x in 3..7 {
768 fb.pixels_mut()[y * 10 + x] = Rgb565::BLACK;
769 }
770 }
771 fb.apply_iir_blur(128);
772 let center_pixel = fb.pixels()[5 * 10 + 5];
773 assert_ne!(center_pixel, Rgb565::BLACK);
774 }
775
776 #[test]
777 fn test_iir_blur_rgba8888() {
778 let mut fb = FramebufferRgba8888::<100>::new(10, 10);
779 fb.clear_color(Rgba8888::WHITE);
780 for y in 3..7 {
781 for x in 3..7 {
782 fb.pixels_mut()[y * 10 + x] = Rgba8888::BLACK;
783 }
784 }
785 fb.apply_iir_blur(128);
786 let center_pixel = fb.pixels()[5 * 10 + 5];
787 assert_ne!(center_pixel, Rgba8888::BLACK);
788 assert!(center_pixel.r > 0);
789 }
790
791 #[test]
792 fn test_iir_blur_gray8() {
793 let mut fb = FramebufferGray8::<100>::new(10, 10);
794 fb.clear_color(Gray8::new(255));
795 for y in 3..7 {
796 for x in 3..7 {
797 fb.pixels_mut()[y * 10 + x] = Gray8::new(0);
798 }
799 }
800 fb.apply_iir_blur(128);
801 let center_pixel = fb.pixels()[5 * 10 + 5].luma();
802 assert!(center_pixel > 0 && center_pixel < 255);
803 }
804}