1#[cfg(feature = "row_width_320")]
3const MAX_ROW_WIDTH: usize = 320;
4#[cfg(all(feature = "row_width_240", not(feature = "row_width_320")))]
5const MAX_ROW_WIDTH: usize = 240;
6#[cfg(all(
7 feature = "row_width_160",
8 not(feature = "row_width_240"),
9 not(feature = "row_width_320"),
10 not(feature = "row_width_96")
11))]
12const MAX_ROW_WIDTH: usize = 160;
13#[cfg(all(
14 feature = "row_width_96",
15 not(feature = "row_width_160"),
16 not(feature = "row_width_240"),
17 not(feature = "row_width_320")
18))]
19const MAX_ROW_WIDTH: usize = 96;
20#[cfg(not(any(
21 feature = "row_width_320",
22 feature = "row_width_240",
23 feature = "row_width_160",
24 feature = "row_width_96"
25)))]
26const MAX_ROW_WIDTH: usize = 100;
27
28use core::fmt::Debug;
29use embedded_graphics_core::draw_target::DrawTarget;
30#[cfg(feature = "aa")]
31use embedded_graphics_core::pixelcolor::Rgb565;
32use embedded_graphics_core::pixelcolor::RgbColor;
33use embedded_graphics_core::prelude::Point;
34use heapless::Vec;
35
36use crate::DrawPrimitive;
37
38#[cfg(feature = "aa")]
46pub trait ReadPixel {
47 fn read_pixel(&self, point: Point) -> Rgb565;
48}
49
50#[cfg(feature = "aa")]
53#[inline(always)]
54fn blend_q8(bg: Rgb565, fg: Rgb565, coverage_q8: u32) -> Rgb565 {
55 let inv = 256 - coverage_q8;
56 let r = (bg.r() as u32 * inv + fg.r() as u32 * coverage_q8) >> 8;
57 let g = (bg.g() as u32 * inv + fg.g() as u32 * coverage_q8) >> 8;
58 let b = (bg.b() as u32 * inv + fg.b() as u32 * coverage_q8) >> 8;
59 Rgb565::new(r as u8, g as u8, b as u8)
60}
61
62#[cfg(feature = "aa-heuristic")]
74#[inline(always)]
75fn aa_pixel<D>(
76 fb: &mut D,
77 x: i32,
78 y: i32,
79 color: Rgb565,
80 z: u32,
81 zbuffer: &mut [u32],
82 width: usize,
83 coverage_q8: u32,
84) where
85 D: DrawTarget<Color = Rgb565> + ReadPixel,
86 <D as DrawTarget>::Error: Debug,
87{
88 if x < 0 || y < 0 || coverage_q8 == 0 {
89 return;
90 }
91 let idx = y as usize * width + x as usize;
92 if idx >= zbuffer.len() {
93 return;
94 }
95 if z >= zbuffer[idx].saturating_add(DEPTH_EPSILON) {
96 return;
97 }
98
99 let pixel_was_virgin = zbuffer[idx] == u32::MAX;
100 let final_color = if coverage_q8 >= 256 || !pixel_was_virgin {
101 color
102 } else {
103 let bg = fb.read_pixel(Point::new(x, y));
104 blend_q8(bg, color, coverage_q8)
105 };
106 zbuffer[idx] = z;
107 fb.draw_iter([embedded_graphics_core::Pixel(Point::new(x, y), final_color)])
108 .unwrap();
109}
110
111const DEPTH_EPSILON: u32 = 128;
127
128#[derive(Debug, Clone, Copy)]
130pub struct FogConfig {
131 pub color: embedded_graphics_core::pixelcolor::Rgb565,
133 pub near: u32,
135 pub far: u32,
137}
138
139impl FogConfig {
140 pub fn new(color: embedded_graphics_core::pixelcolor::Rgb565, near: f32, far: f32) -> Self {
147 Self {
148 color,
149 near: (near * 65536.0) as u32,
150 far: (far * 65536.0) as u32,
151 }
152 }
153
154 #[inline]
156 pub fn apply(
157 &self,
158 base_color: embedded_graphics_core::pixelcolor::Rgb565,
159 depth: u32,
160 ) -> embedded_graphics_core::pixelcolor::Rgb565 {
161 let fog_factor = if depth <= self.near {
163 0u32
164 } else if depth >= self.far {
165 65536u32 } else {
167 let numerator = (depth - self.near) as u64;
169 let denominator = (self.far - self.near) as u64;
170 ((numerator * 65536) / denominator) as u32
171 };
172
173 let base_r = base_color.r() as u32;
176 let base_g = base_color.g() as u32;
177 let base_b = base_color.b() as u32;
178
179 let fog_r = self.color.r() as u32;
180 let fog_g = self.color.g() as u32;
181 let fog_b = self.color.b() as u32;
182
183 let r = ((base_r * (65536 - fog_factor) + fog_r * fog_factor) / 65536) as u8;
185 let g = ((base_g * (65536 - fog_factor) + fog_g * fog_factor) / 65536) as u8;
186 let b = ((base_b * (65536 - fog_factor) + fog_b * fog_factor) / 65536) as u8;
187
188 embedded_graphics_core::pixelcolor::Rgb565::new(r, g, b)
189 }
190}
191
192#[derive(Debug, Clone, Copy)]
194pub struct DitherConfig {
195 pub intensity: u8,
197}
198
199impl DitherConfig {
200 const BAYER_MATRIX: [[u8; 4]; 4] =
203 [[0, 8, 2, 10], [12, 4, 14, 6], [3, 11, 1, 9], [15, 7, 13, 5]];
204
205 pub fn new(intensity: u8) -> Self {
207 Self { intensity }
208 }
209
210 #[inline]
212 pub fn apply(
213 &self,
214 color: embedded_graphics_core::pixelcolor::Rgb565,
215 x: i32,
216 y: i32,
217 ) -> embedded_graphics_core::pixelcolor::Rgb565 {
218 if self.intensity == 0 {
219 return color;
220 }
221
222 let matrix_x = (x & 3) as usize;
224 let matrix_y = (y & 3) as usize;
225 let threshold = Self::BAYER_MATRIX[matrix_y][matrix_x];
226
227 let scaled_threshold = ((threshold as u16 * self.intensity as u16) / 15) as u8;
231
232 let r = color.r();
234 let g = color.g();
235 let b = color.b();
236
237 let r = if r > scaled_threshold {
239 r.saturating_sub(scaled_threshold / 2)
240 } else {
241 r.saturating_add(scaled_threshold / 2)
242 };
243
244 let g = if g > scaled_threshold {
245 g.saturating_sub(scaled_threshold / 2)
246 } else {
247 g.saturating_add(scaled_threshold / 2)
248 };
249
250 let b = if b > scaled_threshold {
251 b.saturating_sub(scaled_threshold / 2)
252 } else {
253 b.saturating_add(scaled_threshold / 2)
254 };
255
256 embedded_graphics_core::pixelcolor::Rgb565::new(r, g, b)
257 }
258}
259
260struct Interpolator {
261 x: i32,
262 dx: i32,
263 dy: i32,
264 error: i32,
265}
266
267impl Interpolator {
268 fn new(p_start: Point, p_end: Point) -> Self {
269 Self {
270 x: p_start.x,
271 dx: p_end.x - p_start.x,
272 dy: p_end.y - p_start.y,
273 error: 0,
274 }
275 }
276
277 fn next(&mut self) -> i32 {
278 self.x += self.dx / self.dy;
279 self.error += self.dx % self.dy;
280 if self.error >= self.dy {
281 self.x += 1;
282 self.error -= self.dy;
283 }
284 self.x
285 }
286}
287
288const FP_SHIFT: i64 = 16;
290
291#[inline(always)]
292fn fixed_to_i32(value: i64) -> i32 {
293 if value >= 0 {
294 (value >> FP_SHIFT) as i32
295 } else {
296 -((-value) >> FP_SHIFT) as i32
297 }
298}
299
300struct EdgeStepper {
301 x: i64,
302 step: i64,
303}
304
305impl EdgeStepper {
306 fn new(start: Point, end: Point, y: i32) -> Self {
307 let dy = (end.y - start.y) as i64;
308 let (step, x) = if dy != 0 {
309 let s = (((end.x - start.x) as i64) << FP_SHIFT) / dy;
310 let x = ((start.x as i64) << FP_SHIFT) + s * (y - start.y) as i64;
311 (s, x)
312 } else {
313 (0, (start.x as i64) << FP_SHIFT)
314 };
315 Self { x, step }
316 }
317
318 #[inline(always)]
319 fn current_x(&self) -> i32 {
320 fixed_to_i32(self.x)
321 }
322
323 #[inline(always)]
324 fn advance(&mut self) {
325 self.x += self.step;
326 }
327}
328
329#[inline(always)]
330fn fill_triangle<D: DrawTarget<Color = embedded_graphics_core::pixelcolor::Rgb565>>(
331 p1: Point,
332 p2: Point,
333 p3: Point,
334 color: embedded_graphics_core::pixelcolor::Rgb565,
335 fb: &mut D,
336) where
337 <D as DrawTarget>::Error: Debug,
338{
339 let area = (p2.x - p1.x) * (p3.y - p1.y) - (p2.y - p1.y) * (p3.x - p1.x);
340 if area == 0 {
341 return;
343 }
344
345 let bounds = fb.bounding_box();
346 let min_x = bounds.top_left.x;
347 let max_x = bounds.bottom_right().unwrap().x;
348
349 let mut pixel_row: [embedded_graphics_core::Pixel<embedded_graphics_core::pixelcolor::Rgb565>;
350 MAX_ROW_WIDTH] = [embedded_graphics_core::Pixel(
351 Point::new(0, 0),
352 embedded_graphics_core::pixelcolor::RgbColor::BLACK,
353 ); MAX_ROW_WIDTH];
354
355 if p2.y - p1.y > 0 {
357 let mut a = Interpolator::new(p1, p2);
358 let mut b = Interpolator::new(p1, p3);
359
360 for y in p1.y..p2.y {
361 let ax = a.next();
362 let bx = b.next();
363 let (start_x, end_x) = if ax < bx { (ax, bx) } else { (bx, ax) };
364 let start_x = start_x.clamp(min_x, max_x);
365 let end_x = end_x.clamp(min_x, max_x);
366
367 let mut i = 0;
368 for x in start_x..=end_x {
369 pixel_row[i] = embedded_graphics_core::Pixel(Point::new(x, y), color);
370 i += 1;
371 }
372
373 fb.draw_iter(pixel_row[..(end_x - start_x + 1) as usize].iter().copied())
374 .unwrap();
375 }
376 }
377
378 if p3.y - p2.y > 0 {
380 let mut a = Interpolator::new(p2, p3);
381 let mut b = Interpolator::new(p1, p3);
382
383 for y in p2.y..=p3.y {
384 let ax = a.next();
385 let bx = b.next();
386 let (start_x, end_x) = if ax < bx { (ax, bx) } else { (bx, ax) };
387 let start_x = start_x.clamp(min_x, max_x);
388 let end_x = end_x.clamp(min_x, max_x);
389
390 let mut i = 0;
391 for x in start_x..=end_x {
392 pixel_row[i] = embedded_graphics_core::Pixel(Point::new(x, y), color);
393 i += 1;
394 }
395
396 fb.draw_iter(pixel_row[..(end_x - start_x + 1) as usize].iter().copied())
397 .unwrap();
398 }
399 }
400}
401
402fn fill_bottom_flat_triangle<D: DrawTarget<Color = embedded_graphics_core::pixelcolor::Rgb565>>(
403 p1: Point,
404 p2: Point,
405 p3: Point,
406 color: embedded_graphics_core::pixelcolor::Rgb565,
407 fb: &mut D,
408) where
409 <D as DrawTarget>::Error: Debug,
410{
411 let mut edge1 = EdgeStepper::new(p1, p2, p1.y);
412 let mut edge2 = EdgeStepper::new(p1, p3, p1.y);
413
414 for scanline_y in p1.y..=p2.y {
415 draw_horizontal_line(
416 Point::new(edge1.current_x(), scanline_y),
417 Point::new(edge2.current_x(), scanline_y),
418 color,
419 fb,
420 );
421 edge1.advance();
422 edge2.advance();
423 }
424}
425
426fn fill_top_flat_triangle<D: DrawTarget<Color = embedded_graphics_core::pixelcolor::Rgb565>>(
427 p1: Point,
428 p2: Point,
429 p3: Point,
430 color: embedded_graphics_core::pixelcolor::Rgb565,
431 fb: &mut D,
432) where
433 <D as DrawTarget>::Error: Debug,
434{
435 let mut edge1 = EdgeStepper::new(p1, p3, p1.y);
437 let mut edge2 = EdgeStepper::new(p2, p3, p1.y);
438
439 for scanline_y in p1.y..=p3.y {
440 draw_horizontal_line(
441 Point::new(edge1.current_x(), scanline_y),
442 Point::new(edge2.current_x(), scanline_y),
443 color,
444 fb,
445 );
446 edge1.advance();
447 edge2.advance();
448 }
449}
450
451fn draw_horizontal_line<D: DrawTarget<Color = embedded_graphics_core::pixelcolor::Rgb565>>(
452 p1: Point,
453 p2: Point,
454 color: embedded_graphics_core::pixelcolor::Rgb565,
455 fb: &mut D,
456) where
457 <D as DrawTarget>::Error: Debug,
458{
459 let start = p1.x.min(p2.x);
460 let end = p1.x.max(p2.x);
461
462 for x in start..=end {
463 fb.draw_iter([embedded_graphics_core::Pixel(Point::new(x, p1.y), color)])
464 .unwrap();
465 }
466}
467
468#[inline]
469pub fn draw<D: DrawTarget<Color = embedded_graphics_core::pixelcolor::Rgb565>>(
470 primitive: DrawPrimitive,
471 fb: &mut D,
472) where
473 <D as DrawTarget>::Error: Debug,
474{
475 match primitive {
476 DrawPrimitive::Line([p1, p2], color) => {
477 fb.draw_iter(
478 line_drawing::Bresenham::new((p1.x, p1.y), (p2.x, p2.y))
479 .map(|(x, y)| embedded_graphics_core::Pixel(Point::new(x, y), color)),
480 )
481 .unwrap();
482 }
483 DrawPrimitive::ColoredPoint(p, c) => {
484 let p = embedded_graphics_core::geometry::Point::new(p.x, p.y);
485
486 fb.draw_iter([embedded_graphics_core::Pixel(p, c)]).unwrap();
487 }
488 DrawPrimitive::ColoredTriangle(mut vertices, color) => {
489 vertices
491 .as_mut_slice()
492 .sort_unstable_by(|a, b| a.y.cmp(&b.y));
493
494 let [p1, p2, p3] = [
495 Point::new(vertices[0].x, vertices[0].y),
496 Point::new(vertices[1].x, vertices[1].y),
497 Point::new(vertices[2].x, vertices[2].y),
498 ];
499
500 let bounds = fb.bounding_box();
502 let scr_w = bounds.size.width as i32;
503 let scr_h = bounds.size.height as i32;
504 if p1.x < 0 && p2.x < 0 && p3.x < 0 {
505 return;
506 }
507 if p1.x >= scr_w && p2.x >= scr_w && p3.x >= scr_w {
508 return;
509 }
510 if p1.y < 0 && p2.y < 0 && p3.y < 0 {
511 return;
512 }
513 if p1.y >= scr_h && p2.y >= scr_h && p3.y >= scr_h {
514 return;
515 }
516
517 fill_triangle(p1, p2, p3, color, fb);
518 }
519 DrawPrimitive::ColoredTriangleWithDepth {
520 points,
521 depths: _,
522 color,
523 } => {
524 let mut vertices = points;
527 if vertices[0].y > vertices[1].y {
528 vertices.swap(0, 1);
529 }
530 if vertices[0].y > vertices[2].y {
531 vertices.swap(0, 2);
532 }
533 if vertices[1].y > vertices[2].y {
534 vertices.swap(1, 2);
535 }
536
537 let mut buf: Vec<_, 3> = Vec::new();
538 for p in vertices.iter() {
539 buf.push(embedded_graphics_core::geometry::Point::new(p.x, p.y))
540 .unwrap();
541 }
542 let [p1, p2, p3] = buf.into_array().unwrap();
543
544 let bounds = fb.bounding_box();
546 let scr_w = bounds.size.width as i32;
547 let scr_h = bounds.size.height as i32;
548 if p1.x < 0 && p2.x < 0 && p3.x < 0 {
549 return;
550 }
551 if p1.x >= scr_w && p2.x >= scr_w && p3.x >= scr_w {
552 return;
553 }
554 if p1.y < 0 && p2.y < 0 && p3.y < 0 {
555 return;
556 }
557 if p1.y >= scr_h && p2.y >= scr_h && p3.y >= scr_h {
558 return;
559 }
560
561 if p2.y == p3.y {
562 fill_bottom_flat_triangle(p1, p2, p3, color, fb);
563 } else if p1.y == p2.y {
564 fill_top_flat_triangle(p1, p2, p3, color, fb);
565 } else {
566 let p4 = Point::new(
567 (p1.x as f32
568 + ((p2.y - p1.y) as f32 / (p3.y - p1.y) as f32) * (p3.x - p1.x) as f32)
569 as i32,
570 p2.y,
571 );
572
573 fill_bottom_flat_triangle(p1, p2, p4, color, fb);
574 fill_top_flat_triangle(p2, p4, p3, color, fb);
575 }
576 }
577 DrawPrimitive::GouraudTriangle {
578 mut points,
579 mut colors,
580 } => {
581 if points[0].y > points[1].y {
583 points.swap(0, 1);
584 colors.swap(0, 1);
585 }
586 if points[0].y > points[2].y {
587 points.swap(0, 2);
588 colors.swap(0, 2);
589 }
590 if points[1].y > points[2].y {
591 points.swap(1, 2);
592 colors.swap(1, 2);
593 }
594
595 let mut buf: Vec<_, 3> = Vec::new();
596 for p in points.iter() {
597 buf.push(embedded_graphics_core::geometry::Point::new(p.x, p.y))
598 .unwrap();
599 }
600 let [p1, p2, p3] = buf.into_array().unwrap();
601 let [c1, c2, c3] = colors;
602
603 let bounds = fb.bounding_box();
605 let scr_w = bounds.size.width as i32;
606 let scr_h = bounds.size.height as i32;
607 if p1.x < 0 && p2.x < 0 && p3.x < 0 {
608 return;
609 }
610 if p1.x >= scr_w && p2.x >= scr_w && p3.x >= scr_w {
611 return;
612 }
613 if p1.y < 0 && p2.y < 0 && p3.y < 0 {
614 return;
615 }
616 if p1.y >= scr_h && p2.y >= scr_h && p3.y >= scr_h {
617 return;
618 }
619
620 if p2.y == p3.y {
621 fill_bottom_flat_gouraud(p1, p2, p3, c1, c2, c3, fb);
622 } else if p1.y == p2.y {
623 fill_top_flat_gouraud(p1, p2, p3, c1, c2, c3, fb);
624 } else {
625 let t = (p2.y - p1.y) as f32 / (p3.y - p1.y) as f32;
627 let p4 = Point::new((p1.x as f32 + t * (p3.x - p1.x) as f32) as i32, p2.y);
628 let c4 = interpolate_color(c1, c3, t);
630
631 fill_bottom_flat_gouraud(p1, p2, p4, c1, c2, c4, fb);
632 fill_top_flat_gouraud(p2, p4, p3, c2, c4, c3, fb);
633 }
634 }
635 DrawPrimitive::GouraudTriangleWithDepth {
636 points,
637 depths: _,
638 colors,
639 } => {
640 let prim = DrawPrimitive::GouraudTriangle { points, colors };
643 draw(prim, fb);
644 }
645 DrawPrimitive::TexturedTriangle { .. }
646 | DrawPrimitive::TexturedTriangleWithDepth { .. } => {
647 }
651 }
652}
653
654#[inline]
656fn interpolate_color(
657 c1: embedded_graphics_core::pixelcolor::Rgb565,
658 c2: embedded_graphics_core::pixelcolor::Rgb565,
659 t: f32,
660) -> embedded_graphics_core::pixelcolor::Rgb565 {
661 let r1 = c1.r() as f32;
662 let g1 = c1.g() as f32;
663 let b1 = c1.b() as f32;
664
665 let r2 = c2.r() as f32;
666 let g2 = c2.g() as f32;
667 let b2 = c2.b() as f32;
668
669 let r = (r1 + t * (r2 - r1)) as u8;
670 let g = (g1 + t * (g2 - g1)) as u8;
671 let b = (b1 + t * (b2 - b1)) as u8;
672
673 embedded_graphics_core::pixelcolor::Rgb565::new(r, g, b)
674}
675
676fn fill_bottom_flat_gouraud<D: DrawTarget<Color = embedded_graphics_core::pixelcolor::Rgb565>>(
678 p1: Point,
679 p2: Point,
680 p3: Point,
681 c1: embedded_graphics_core::pixelcolor::Rgb565,
682 c2: embedded_graphics_core::pixelcolor::Rgb565,
683 c3: embedded_graphics_core::pixelcolor::Rgb565,
684 fb: &mut D,
685) where
686 <D as DrawTarget>::Error: Debug,
687{
688 let height = (p2.y - p1.y) as f32;
689 if height == 0.0 {
690 return;
691 }
692
693 let mut edge1 = EdgeStepper::new(p1, p2, p1.y);
694 let mut edge2 = EdgeStepper::new(p1, p3, p1.y);
695
696 for scanline_y in p1.y..=p2.y {
697 let t = (scanline_y - p1.y) as f32 / height;
698 let color_left = interpolate_color(c1, c2, t);
699 let color_right = interpolate_color(c1, c3, t);
700
701 draw_horizontal_line_gouraud(
702 Point::new(edge1.current_x(), scanline_y),
703 Point::new(edge2.current_x(), scanline_y),
704 color_left,
705 color_right,
706 fb,
707 );
708
709 edge1.advance();
710 edge2.advance();
711 }
712}
713
714fn fill_top_flat_gouraud<D: DrawTarget<Color = embedded_graphics_core::pixelcolor::Rgb565>>(
716 p1: Point,
717 p2: Point,
718 p3: Point,
719 c1: embedded_graphics_core::pixelcolor::Rgb565,
720 c2: embedded_graphics_core::pixelcolor::Rgb565,
721 c3: embedded_graphics_core::pixelcolor::Rgb565,
722 fb: &mut D,
723) where
724 <D as DrawTarget>::Error: Debug,
725{
726 let height = (p3.y - p1.y) as f32;
728 if height == 0.0 {
729 return;
730 }
731
732 let mut edge1 = EdgeStepper::new(p1, p3, p1.y);
733 let mut edge2 = EdgeStepper::new(p2, p3, p1.y);
734
735 for scanline_y in p1.y..=p3.y {
736 let t = (scanline_y - p1.y) as f32 / height;
737 let color_left = interpolate_color(c1, c3, t);
738 let color_right = interpolate_color(c2, c3, t);
739
740 draw_horizontal_line_gouraud(
741 Point::new(edge1.current_x(), scanline_y),
742 Point::new(edge2.current_x(), scanline_y),
743 color_left,
744 color_right,
745 fb,
746 );
747
748 edge1.advance();
749 edge2.advance();
750 }
751}
752
753fn draw_horizontal_line_gouraud<D: DrawTarget<Color = embedded_graphics_core::pixelcolor::Rgb565>>(
755 p1: Point,
756 p2: Point,
757 color1: embedded_graphics_core::pixelcolor::Rgb565,
758 color2: embedded_graphics_core::pixelcolor::Rgb565,
759 fb: &mut D,
760) where
761 <D as DrawTarget>::Error: Debug,
762{
763 let start = p1.x.min(p2.x);
764 let end = p1.x.max(p2.x);
765 let width = (end - start) as f32;
766
767 if width == 0.0 {
768 fb.draw_iter([embedded_graphics_core::Pixel(
769 Point::new(start, p1.y),
770 color1,
771 )])
772 .unwrap();
773 return;
774 }
775
776 for x in start..=end {
777 let t = (x - start) as f32 / width;
778 let color = interpolate_color(color1, color2, t);
779 fb.draw_iter([embedded_graphics_core::Pixel(Point::new(x, p1.y), color)])
780 .unwrap();
781 }
782}
783
784#[inline]
787pub fn draw_zbuffered<D: DrawTarget<Color = embedded_graphics_core::pixelcolor::Rgb565>>(
788 primitive: DrawPrimitive,
789 fb: &mut D,
790 zbuffer: &mut [u32],
791 width: usize,
792) where
793 <D as DrawTarget>::Error: Debug,
794{
795 draw_zbuffered_with_effects(primitive, fb, zbuffer, width, None, None);
797}
798
799#[cfg(feature = "aa-heuristic")]
808#[inline]
809pub fn draw_zbuffered_aa<D>(primitive: DrawPrimitive, fb: &mut D, zbuffer: &mut [u32], width: usize)
810where
811 D: DrawTarget<Color = Rgb565> + ReadPixel,
812 <D as DrawTarget>::Error: Debug,
813{
814 match primitive {
815 DrawPrimitive::ColoredTriangleWithDepth {
816 mut points,
817 mut depths,
818 color,
819 } => {
820 if points[0].y > points[1].y {
821 points.swap(0, 1);
822 depths.swap(0, 1);
823 }
824 if points[0].y > points[2].y {
825 points.swap(0, 2);
826 depths.swap(0, 2);
827 }
828 if points[1].y > points[2].y {
829 points.swap(1, 2);
830 depths.swap(1, 2);
831 }
832 let [p1, p2, p3] = points;
833 let [z1, z2, z3] = depths;
834
835 let scr_w = width as i32;
837 let scr_h = (zbuffer.len() / width) as i32;
838 if p1.x < 0 && p2.x < 0 && p3.x < 0 {
839 return;
840 }
841 if p1.x >= scr_w && p2.x >= scr_w && p3.x >= scr_w {
842 return;
843 }
844 if p1.y < 0 && p2.y < 0 && p3.y < 0 {
845 return;
846 }
847 if p1.y >= scr_h && p2.y >= scr_h && p3.y >= scr_h {
848 return;
849 }
850
851 fill_triangle_zbuffered_aa(p1, p2, p3, z1, z2, z3, color, fb, zbuffer, width);
852 }
853 DrawPrimitive::Line([p1, p2], color) => {
854 draw_line_aa(p1.x, p1.y, p2.x, p2.y, color, fb);
855 }
856 other => draw_zbuffered(other, fb, zbuffer, width),
859 }
860}
861
862#[cfg(feature = "aa-heuristic")]
863#[inline(always)]
864fn fill_triangle_zbuffered_aa<D>(
865 p1: nalgebra::Point2<i32>,
866 p2: nalgebra::Point2<i32>,
867 p3: nalgebra::Point2<i32>,
868 z1: f32,
869 z2: f32,
870 z3: f32,
871 color: Rgb565,
872 fb: &mut D,
873 zbuffer: &mut [u32],
874 width: usize,
875) where
876 D: DrawTarget<Color = Rgb565> + ReadPixel,
877 <D as DrawTarget>::Error: Debug,
878{
879 let p1_eg = Point::new(p1.x, p1.y);
880 let p2_eg = Point::new(p2.x, p2.y);
881 let p3_eg = Point::new(p3.x, p3.y);
882
883 let z1_int = (z1 * 65536.0) as u32;
884 let z2_int = (z2 * 65536.0) as u32;
885 let z3_int = (z3 * 65536.0) as u32;
886
887 if p2_eg.y == p3_eg.y {
888 fill_bottom_flat_aa(
889 p1_eg, p2_eg, p3_eg, z1_int, z2_int, z3_int, color, fb, zbuffer, width,
890 );
891 } else if p1_eg.y == p2_eg.y {
892 fill_top_flat_aa(
893 p1_eg, p2_eg, p3_eg, z1_int, z2_int, z3_int, color, fb, zbuffer, width,
894 );
895 } else {
896 let t = (p2_eg.y - p1_eg.y) as f32 / (p3_eg.y - p1_eg.y) as f32;
897 let p4 = Point::new(
898 (p1_eg.x as f32 + t * (p3_eg.x - p1_eg.x) as f32) as i32,
899 p2_eg.y,
900 );
901 let z4_int = (z1_int as i64 + (t * (z3_int as i64 - z1_int as i64) as f32) as i64) as u32;
902 fill_bottom_flat_aa(
903 p1_eg, p2_eg, p4, z1_int, z2_int, z4_int, color, fb, zbuffer, width,
904 );
905 fill_top_flat_aa(
906 p2_eg, p4, p3_eg, z2_int, z4_int, z3_int, color, fb, zbuffer, width,
907 );
908 }
909}
910
911#[cfg(feature = "aa-heuristic")]
912#[inline(always)]
913fn fill_bottom_flat_aa<D>(
914 p1: Point,
915 p2: Point,
916 p3: Point,
917 z1: u32,
918 z2: u32,
919 z3: u32,
920 color: Rgb565,
921 fb: &mut D,
922 zbuffer: &mut [u32],
923 width: usize,
924) where
925 D: DrawTarget<Color = Rgb565> + ReadPixel,
926 <D as DrawTarget>::Error: Debug,
927{
928 let height = p2.y - p1.y;
929 if height == 0 {
930 return;
931 }
932 let invslope1 = ((p2.x - p1.x) << 16) / height;
933 let invslope2 = ((p3.x - p1.x) << 16) / height;
934
935 let mut curx1 = p1.x << 16;
936 let mut curx2 = p1.x << 16;
937
938 for scanline_y in p1.y..=p2.y {
939 let dy = scanline_y - p1.y;
940 let z_left = (z1 as i64 + ((z2 as i64 - z1 as i64) * dy as i64 / height as i64)) as u32;
941 let z_right = (z1 as i64 + ((z3 as i64 - z1 as i64) * dy as i64 / height as i64)) as u32;
942
943 aa_scanline(
944 curx1, curx2, scanline_y, z_left, z_right, color, fb, zbuffer, width,
945 );
946
947 curx1 += invslope1;
948 curx2 += invslope2;
949 }
950}
951
952#[cfg(feature = "aa-heuristic")]
953#[inline(always)]
954fn fill_top_flat_aa<D>(
955 p1: Point,
956 p2: Point,
957 p3: Point,
958 z1: u32,
959 z2: u32,
960 z3: u32,
961 color: Rgb565,
962 fb: &mut D,
963 zbuffer: &mut [u32],
964 width: usize,
965) where
966 D: DrawTarget<Color = Rgb565> + ReadPixel,
967 <D as DrawTarget>::Error: Debug,
968{
969 let height = p3.y - p1.y;
970 if height == 0 {
971 return;
972 }
973 let invslope1 = ((p3.x - p1.x) << 16) / height;
974 let invslope2 = ((p3.x - p2.x) << 16) / height;
975
976 let mut curx1 = p3.x << 16;
977 let mut curx2 = p3.x << 16;
978
979 for scanline_y in (p1.y..=p3.y).rev() {
980 let dy = scanline_y - p1.y;
981 let z_left = (z1 as i64 + ((z3 as i64 - z1 as i64) * dy as i64 / height as i64)) as u32;
982 let z_right = (z2 as i64 + ((z3 as i64 - z2 as i64) * dy as i64 / height as i64)) as u32;
983
984 aa_scanline(
985 curx1, curx2, scanline_y, z_left, z_right, color, fb, zbuffer, width,
986 );
987
988 curx1 -= invslope1;
989 curx2 -= invslope2;
990 }
991}
992
993#[cfg(feature = "aa-heuristic")]
999#[inline(always)]
1000fn aa_scanline<D>(
1001 cx1: i32,
1002 cx2: i32,
1003 y: i32,
1004 z_left: u32,
1005 z_right: u32,
1006 color: Rgb565,
1007 fb: &mut D,
1008 zbuffer: &mut [u32],
1009 width: usize,
1010) where
1011 D: DrawTarget<Color = Rgb565> + ReadPixel,
1012 <D as DrawTarget>::Error: Debug,
1013{
1014 let (left_fx, right_fx, z_l, z_r) = if cx1 <= cx2 {
1016 (cx1, cx2, z_left, z_right)
1017 } else {
1018 (cx2, cx1, z_right, z_left)
1019 };
1020
1021 let l_int = left_fx >> 16;
1022 let r_int = right_fx >> 16;
1023 let l_frac_q16 = (left_fx & 0xFFFF) as u32;
1024 let r_frac_q16 = (right_fx & 0xFFFF) as u32;
1025
1026 let span = r_int - l_int;
1028
1029 if l_int == r_int {
1030 let cov_q16 = r_frac_q16.saturating_sub(l_frac_q16);
1033 aa_pixel(fb, l_int, y, color, z_l, zbuffer, width, cov_q16 >> 8);
1034 return;
1035 }
1036
1037 let left_cov_q8 = 256 - (l_frac_q16 >> 8);
1039 aa_pixel(fb, l_int, y, color, z_l, zbuffer, width, left_cov_q8);
1040
1041 if span > 1 {
1044 for x in (l_int + 1)..r_int {
1045 if x < 0 {
1046 continue;
1047 }
1048 let idx = y as usize * width + x as usize;
1049 if idx >= zbuffer.len() {
1050 continue;
1051 }
1052 let t_num = (x - l_int) as i64;
1054 let t_den = span as i64;
1055 let z = (z_l as i64 + ((z_r as i64 - z_l as i64) * t_num / t_den)) as u32;
1056 if z < zbuffer[idx].saturating_add(DEPTH_EPSILON) {
1057 zbuffer[idx] = z;
1058 fb.draw_iter([embedded_graphics_core::Pixel(Point::new(x, y), color)])
1059 .unwrap();
1060 }
1061 }
1062 }
1063
1064 if r_frac_q16 > 0 {
1066 let right_cov_q8 = r_frac_q16 >> 8;
1067 aa_pixel(fb, r_int, y, color, z_r, zbuffer, width, right_cov_q8);
1068 }
1069}
1070
1071#[cfg(feature = "aa-coverage")]
1088#[inline]
1089pub fn draw_zbuffered_aa_coverage<D>(
1090 primitive: DrawPrimitive,
1091 fb: &mut D,
1092 zbuffer: &mut [u32],
1093 coverage: &mut [u8],
1094 width: usize,
1095) where
1096 D: DrawTarget<Color = Rgb565> + ReadPixel,
1097 <D as DrawTarget>::Error: Debug,
1098{
1099 match primitive {
1100 DrawPrimitive::ColoredTriangleWithDepth {
1101 mut points,
1102 mut depths,
1103 color,
1104 } => {
1105 if points[0].y > points[1].y {
1106 points.swap(0, 1);
1107 depths.swap(0, 1);
1108 }
1109 if points[0].y > points[2].y {
1110 points.swap(0, 2);
1111 depths.swap(0, 2);
1112 }
1113 if points[1].y > points[2].y {
1114 points.swap(1, 2);
1115 depths.swap(1, 2);
1116 }
1117 let [p1, p2, p3] = points;
1118 let [z1, z2, z3] = depths;
1119 fill_triangle_zbuffered_aa_cov(
1120 p1, p2, p3, z1, z2, z3, color, fb, zbuffer, coverage, width,
1121 );
1122 }
1123 DrawPrimitive::Line([p1, p2], color) => {
1124 draw_line_aa_coverage(p1.x, p1.y, p2.x, p2.y, color, fb, coverage, width);
1127 }
1128 other => draw_zbuffered(other, fb, zbuffer, width),
1129 }
1130}
1131
1132#[cfg(feature = "aa-coverage")]
1136pub fn draw_line_aa_coverage<D>(
1137 x0: i32,
1138 y0: i32,
1139 x1: i32,
1140 y1: i32,
1141 color: Rgb565,
1142 fb: &mut D,
1143 coverage: &mut [u8],
1144 width: usize,
1145) where
1146 D: DrawTarget<Color = Rgb565> + ReadPixel,
1147 <D as DrawTarget>::Error: Debug,
1148{
1149 let dx = (x1 - x0).abs();
1150 let dy = (y1 - y0).abs();
1151 let steep = dy > dx;
1152 let (x0, y0, x1, y1) = if steep {
1153 (y0, x0, y1, x1)
1154 } else {
1155 (x0, y0, x1, y1)
1156 };
1157 let (x0, y0, x1, y1) = if x0 > x1 {
1158 (x1, y1, x0, y0)
1159 } else {
1160 (x0, y0, x1, y1)
1161 };
1162 let dx = x1 - x0;
1163 let dy = y1 - y0;
1164 if dx == 0 {
1165 let (px, py) = if steep { (y0, x0) } else { (x0, y0) };
1166 plot_aa_cov(fb, px, py, color, coverage, width, 256);
1167 return;
1168 }
1169 let gradient: i32 = ((dy as i64) << 16) as i32 / dx;
1170 let mut intery: i32 = y0 << 16;
1171 for x in x0..=x1 {
1172 let y_int = intery >> 16;
1173 let frac_q16 = (intery & 0xFFFF) as u32;
1174 let cov_top = 256 - (frac_q16 >> 8);
1175 let cov_bot = frac_q16 >> 8;
1176 if steep {
1177 plot_aa_cov(fb, y_int, x, color, coverage, width, cov_top);
1178 plot_aa_cov(fb, y_int + 1, x, color, coverage, width, cov_bot);
1179 } else {
1180 plot_aa_cov(fb, x, y_int, color, coverage, width, cov_top);
1181 plot_aa_cov(fb, x, y_int + 1, color, coverage, width, cov_bot);
1182 }
1183 intery += gradient;
1184 }
1185}
1186
1187#[cfg(feature = "aa-coverage")]
1190#[inline(always)]
1191fn plot_aa_cov<D>(
1192 fb: &mut D,
1193 x: i32,
1194 y: i32,
1195 color: Rgb565,
1196 coverage: &mut [u8],
1197 width: usize,
1198 coverage_q8: u32,
1199) where
1200 D: DrawTarget<Color = Rgb565> + ReadPixel,
1201 <D as DrawTarget>::Error: Debug,
1202{
1203 if x < 0 || y < 0 || coverage_q8 == 0 {
1204 return;
1205 }
1206 let idx = y as usize * width + x as usize;
1207 if idx >= coverage.len() {
1208 return;
1209 }
1210 let p = Point::new(x, y);
1211
1212 if coverage_q8 >= 256 {
1213 coverage[idx] = 255;
1214 fb.draw_iter([embedded_graphics_core::Pixel(p, color)])
1215 .unwrap();
1216 return;
1217 }
1218
1219 let prev_cov = coverage[idx] as u32;
1220
1221 if prev_cov == 0 {
1222 let claim_255 = (coverage_q8 * 255) >> 8;
1223 coverage[idx] = claim_255 as u8;
1224 fb.draw_iter([embedded_graphics_core::Pixel(p, color)])
1225 .unwrap();
1226 return;
1227 }
1228
1229 if prev_cov >= 255 {
1230 let existing = fb.read_pixel(p);
1231 let result = blend_q8(existing, color, coverage_q8);
1232 fb.draw_iter([embedded_graphics_core::Pixel(p, result)])
1233 .unwrap();
1234 return;
1235 }
1236
1237 let remaining = 255 - prev_cov;
1238 let claim_255 = ((coverage_q8 * 255) >> 8).min(remaining);
1239 if claim_255 == 0 {
1240 return;
1241 }
1242 let new_total = prev_cov + claim_255;
1243 let existing = fb.read_pixel(p);
1244 let blend_factor = (claim_255 * 256) / new_total;
1245 let result = blend_q8(existing, color, blend_factor);
1246 coverage[idx] = new_total as u8;
1247 fb.draw_iter([embedded_graphics_core::Pixel(p, result)])
1248 .unwrap();
1249}
1250
1251#[cfg(feature = "aa-coverage")]
1255pub fn composite_aa_background<D>(
1256 fb: &mut D,
1257 coverage: &[u8],
1258 bg: Rgb565,
1259 width: usize,
1260 height: usize,
1261) where
1262 D: DrawTarget<Color = Rgb565> + ReadPixel,
1263 <D as DrawTarget>::Error: Debug,
1264{
1265 for y in 0..height {
1266 for x in 0..width {
1267 let idx = y * width + x;
1268 let cov = coverage[idx];
1269 if cov == 255 {
1270 continue; }
1272 let p = Point::new(x as i32, y as i32);
1273 let final_color = if cov == 0 {
1274 bg
1275 } else {
1276 let tri_color = fb.read_pixel(p);
1280 let cov_q8 = ((cov as u32) * 256) / 255;
1282 blend_q8(bg, tri_color, cov_q8)
1283 };
1284 fb.draw_iter([embedded_graphics_core::Pixel(p, final_color)])
1285 .unwrap();
1286 }
1287 }
1288}
1289
1290#[cfg(feature = "aa-coverage")]
1305#[inline(always)]
1306fn aa_pixel_cov<D>(
1307 fb: &mut D,
1308 x: i32,
1309 y: i32,
1310 color: Rgb565,
1311 z: u32,
1312 zbuffer: &mut [u32],
1313 coverage: &mut [u8],
1314 width: usize,
1315 coverage_q8: u32,
1316) where
1317 D: DrawTarget<Color = Rgb565> + ReadPixel,
1318 <D as DrawTarget>::Error: Debug,
1319{
1320 if x < 0 || y < 0 || coverage_q8 == 0 {
1321 return;
1322 }
1323 let idx = y as usize * width + x as usize;
1324 if idx >= zbuffer.len() {
1325 return;
1326 }
1327 if z >= zbuffer[idx].saturating_add(DEPTH_EPSILON) {
1328 return;
1329 }
1330
1331 let p = Point::new(x, y);
1332
1333 if coverage_q8 >= 256 {
1335 coverage[idx] = 255;
1336 zbuffer[idx] = z;
1337 fb.draw_iter([embedded_graphics_core::Pixel(p, color)])
1338 .unwrap();
1339 return;
1340 }
1341
1342 let prev_cov = coverage[idx] as u32;
1343
1344 if prev_cov == 0 {
1345 let claim_255 = (coverage_q8 * 255) >> 8;
1348 coverage[idx] = claim_255 as u8;
1349 zbuffer[idx] = z;
1350 fb.draw_iter([embedded_graphics_core::Pixel(p, color)])
1351 .unwrap();
1352 return;
1353 }
1354
1355 if prev_cov >= 255 {
1356 let existing = fb.read_pixel(p);
1361 let result = blend_q8(existing, color, coverage_q8);
1362 zbuffer[idx] = z;
1363 fb.draw_iter([embedded_graphics_core::Pixel(p, result)])
1364 .unwrap();
1365 return;
1366 }
1367
1368 let remaining = 255 - prev_cov;
1371 let claim_255 = ((coverage_q8 * 255) >> 8).min(remaining);
1372 if claim_255 == 0 {
1373 return;
1374 }
1375 let new_total = prev_cov + claim_255;
1376 let existing = fb.read_pixel(p);
1377 let blend_factor = (claim_255 * 256) / new_total;
1378 let result = blend_q8(existing, color, blend_factor);
1379 coverage[idx] = new_total as u8;
1380 zbuffer[idx] = z;
1381 fb.draw_iter([embedded_graphics_core::Pixel(p, result)])
1382 .unwrap();
1383}
1384
1385#[cfg(feature = "aa-coverage")]
1386#[inline(always)]
1387fn fill_triangle_zbuffered_aa_cov<D>(
1388 p1: nalgebra::Point2<i32>,
1389 p2: nalgebra::Point2<i32>,
1390 p3: nalgebra::Point2<i32>,
1391 z1: f32,
1392 z2: f32,
1393 z3: f32,
1394 color: Rgb565,
1395 fb: &mut D,
1396 zbuffer: &mut [u32],
1397 coverage: &mut [u8],
1398 width: usize,
1399) where
1400 D: DrawTarget<Color = Rgb565> + ReadPixel,
1401 <D as DrawTarget>::Error: Debug,
1402{
1403 let p1_eg = Point::new(p1.x, p1.y);
1404 let p2_eg = Point::new(p2.x, p2.y);
1405 let p3_eg = Point::new(p3.x, p3.y);
1406
1407 let z1_int = (z1 * 65536.0) as u32;
1408 let z2_int = (z2 * 65536.0) as u32;
1409 let z3_int = (z3 * 65536.0) as u32;
1410
1411 if p2_eg.y == p3_eg.y {
1412 fill_bottom_flat_aa_cov(
1413 p1_eg, p2_eg, p3_eg, z1_int, z2_int, z3_int, color, fb, zbuffer, coverage, width,
1414 );
1415 } else if p1_eg.y == p2_eg.y {
1416 fill_top_flat_aa_cov(
1417 p1_eg, p2_eg, p3_eg, z1_int, z2_int, z3_int, color, fb, zbuffer, coverage, width,
1418 );
1419 } else {
1420 let t = (p2_eg.y - p1_eg.y) as f32 / (p3_eg.y - p1_eg.y) as f32;
1421 let p4 = Point::new(
1422 (p1_eg.x as f32 + t * (p3_eg.x - p1_eg.x) as f32) as i32,
1423 p2_eg.y,
1424 );
1425 let z4_int = (z1_int as i64 + (t * (z3_int as i64 - z1_int as i64) as f32) as i64) as u32;
1426 fill_bottom_flat_aa_cov(
1427 p1_eg, p2_eg, p4, z1_int, z2_int, z4_int, color, fb, zbuffer, coverage, width,
1428 );
1429 fill_top_flat_aa_cov(
1430 p2_eg, p4, p3_eg, z2_int, z4_int, z3_int, color, fb, zbuffer, coverage, width,
1431 );
1432 }
1433}
1434
1435#[cfg(feature = "aa-coverage")]
1436#[inline(always)]
1437fn fill_bottom_flat_aa_cov<D>(
1438 p1: Point,
1439 p2: Point,
1440 p3: Point,
1441 z1: u32,
1442 z2: u32,
1443 z3: u32,
1444 color: Rgb565,
1445 fb: &mut D,
1446 zbuffer: &mut [u32],
1447 coverage: &mut [u8],
1448 width: usize,
1449) where
1450 D: DrawTarget<Color = Rgb565> + ReadPixel,
1451 <D as DrawTarget>::Error: Debug,
1452{
1453 let height = p2.y - p1.y;
1454 if height == 0 {
1455 return;
1456 }
1457 let invslope1 = ((p2.x - p1.x) << 16) / height;
1458 let invslope2 = ((p3.x - p1.x) << 16) / height;
1459
1460 let mut curx1 = p1.x << 16;
1461 let mut curx2 = p1.x << 16;
1462
1463 for scanline_y in p1.y..=p2.y {
1464 let dy = scanline_y - p1.y;
1465 let z_left = (z1 as i64 + ((z2 as i64 - z1 as i64) * dy as i64 / height as i64)) as u32;
1466 let z_right = (z1 as i64 + ((z3 as i64 - z1 as i64) * dy as i64 / height as i64)) as u32;
1467
1468 aa_scanline_cov(
1469 curx1, curx2, scanline_y, z_left, z_right, color, fb, zbuffer, coverage, width,
1470 );
1471
1472 curx1 += invslope1;
1473 curx2 += invslope2;
1474 }
1475}
1476
1477#[cfg(feature = "aa-coverage")]
1478#[inline(always)]
1479fn fill_top_flat_aa_cov<D>(
1480 p1: Point,
1481 p2: Point,
1482 p3: Point,
1483 z1: u32,
1484 z2: u32,
1485 z3: u32,
1486 color: Rgb565,
1487 fb: &mut D,
1488 zbuffer: &mut [u32],
1489 coverage: &mut [u8],
1490 width: usize,
1491) where
1492 D: DrawTarget<Color = Rgb565> + ReadPixel,
1493 <D as DrawTarget>::Error: Debug,
1494{
1495 let height = p3.y - p1.y;
1496 if height == 0 {
1497 return;
1498 }
1499 let invslope1 = ((p3.x - p1.x) << 16) / height;
1500 let invslope2 = ((p3.x - p2.x) << 16) / height;
1501
1502 let mut curx1 = p3.x << 16;
1503 let mut curx2 = p3.x << 16;
1504
1505 for scanline_y in (p1.y..=p3.y).rev() {
1506 let dy = scanline_y - p1.y;
1507 let z_left = (z1 as i64 + ((z3 as i64 - z1 as i64) * dy as i64 / height as i64)) as u32;
1508 let z_right = (z2 as i64 + ((z3 as i64 - z2 as i64) * dy as i64 / height as i64)) as u32;
1509
1510 aa_scanline_cov(
1511 curx1, curx2, scanline_y, z_left, z_right, color, fb, zbuffer, coverage, width,
1512 );
1513
1514 curx1 -= invslope1;
1515 curx2 -= invslope2;
1516 }
1517}
1518
1519#[cfg(feature = "aa-coverage")]
1520#[inline(always)]
1521fn aa_scanline_cov<D>(
1522 cx1: i32,
1523 cx2: i32,
1524 y: i32,
1525 z_left: u32,
1526 z_right: u32,
1527 color: Rgb565,
1528 fb: &mut D,
1529 zbuffer: &mut [u32],
1530 coverage: &mut [u8],
1531 width: usize,
1532) where
1533 D: DrawTarget<Color = Rgb565> + ReadPixel,
1534 <D as DrawTarget>::Error: Debug,
1535{
1536 let (left_fx, right_fx, z_l, z_r) = if cx1 <= cx2 {
1537 (cx1, cx2, z_left, z_right)
1538 } else {
1539 (cx2, cx1, z_right, z_left)
1540 };
1541
1542 let l_int = left_fx >> 16;
1543 let r_int = right_fx >> 16;
1544 let l_frac_q16 = (left_fx & 0xFFFF) as u32;
1545 let r_frac_q16 = (right_fx & 0xFFFF) as u32;
1546 let span = r_int - l_int;
1547
1548 if l_int == r_int {
1549 let cov_q16 = r_frac_q16.saturating_sub(l_frac_q16);
1550 aa_pixel_cov(
1551 fb,
1552 l_int,
1553 y,
1554 color,
1555 z_l,
1556 zbuffer,
1557 coverage,
1558 width,
1559 cov_q16 >> 8,
1560 );
1561 return;
1562 }
1563
1564 let left_cov_q8 = 256 - (l_frac_q16 >> 8);
1565 aa_pixel_cov(
1566 fb,
1567 l_int,
1568 y,
1569 color,
1570 z_l,
1571 zbuffer,
1572 coverage,
1573 width,
1574 left_cov_q8,
1575 );
1576
1577 if span > 1 {
1578 for x in (l_int + 1)..r_int {
1579 let t_num = (x - l_int) as i64;
1582 let t_den = span as i64;
1583 let z = (z_l as i64 + ((z_r as i64 - z_l as i64) * t_num / t_den)) as u32;
1584 aa_pixel_cov(fb, x, y, color, z, zbuffer, coverage, width, 256);
1585 }
1586 }
1587
1588 if r_frac_q16 > 0 {
1589 let right_cov_q8 = r_frac_q16 >> 8;
1590 aa_pixel_cov(
1591 fb,
1592 r_int,
1593 y,
1594 color,
1595 z_r,
1596 zbuffer,
1597 coverage,
1598 width,
1599 right_cov_q8,
1600 );
1601 }
1602}
1603
1604#[cfg(feature = "aa")]
1609pub fn draw_line_aa<D>(x0: i32, y0: i32, x1: i32, y1: i32, color: Rgb565, fb: &mut D)
1610where
1611 D: DrawTarget<Color = Rgb565> + ReadPixel,
1612 <D as DrawTarget>::Error: Debug,
1613{
1614 let dx = (x1 - x0).abs();
1615 let dy = (y1 - y0).abs();
1616 let steep = dy > dx;
1617 let (x0, y0, x1, y1) = if steep {
1618 (y0, x0, y1, x1)
1619 } else {
1620 (x0, y0, x1, y1)
1621 };
1622 let (x0, y0, x1, y1) = if x0 > x1 {
1623 (x1, y1, x0, y0)
1624 } else {
1625 (x0, y0, x1, y1)
1626 };
1627 let dx = x1 - x0;
1628 let dy = y1 - y0;
1629 if dx == 0 {
1630 let (px, py) = if steep { (y0, x0) } else { (x0, y0) };
1632 plot_aa(fb, px, py, color, 256);
1633 return;
1634 }
1635 let gradient: i32 = ((dy as i64) << 16) as i32 / dx;
1637 let mut intery: i32 = y0 << 16;
1639 for x in x0..=x1 {
1640 let y_int = intery >> 16;
1641 let frac_q16 = (intery & 0xFFFF) as u32;
1642 let cov_top = 256 - (frac_q16 >> 8); let cov_bot = frac_q16 >> 8; if steep {
1645 plot_aa(fb, y_int, x, color, cov_top);
1646 plot_aa(fb, y_int + 1, x, color, cov_bot);
1647 } else {
1648 plot_aa(fb, x, y_int, color, cov_top);
1649 plot_aa(fb, x, y_int + 1, color, cov_bot);
1650 }
1651 intery += gradient;
1652 }
1653}
1654
1655#[cfg(feature = "aa")]
1656#[inline(always)]
1657fn plot_aa<D>(fb: &mut D, x: i32, y: i32, color: Rgb565, coverage_q8: u32)
1658where
1659 D: DrawTarget<Color = Rgb565> + ReadPixel,
1660 <D as DrawTarget>::Error: Debug,
1661{
1662 if coverage_q8 == 0 {
1663 return;
1664 }
1665 let final_color = if coverage_q8 >= 256 {
1666 color
1667 } else {
1668 let bg = fb.read_pixel(Point::new(x, y));
1669 blend_q8(bg, color, coverage_q8)
1670 };
1671 fb.draw_iter([embedded_graphics_core::Pixel(Point::new(x, y), final_color)])
1672 .unwrap();
1673}
1674
1675#[inline]
1678pub fn draw_zbuffered_with_effects<
1679 D: DrawTarget<Color = embedded_graphics_core::pixelcolor::Rgb565>,
1680>(
1681 primitive: DrawPrimitive,
1682 fb: &mut D,
1683 zbuffer: &mut [u32],
1684 width: usize,
1685 fog_config: Option<&FogConfig>,
1686 dither_config: Option<&DitherConfig>,
1687) where
1688 <D as DrawTarget>::Error: Debug,
1689{
1690 match primitive {
1691 DrawPrimitive::ColoredTriangleWithDepth {
1692 mut points,
1693 mut depths,
1694 color,
1695 } => {
1696 if points[0].y > points[1].y {
1698 points.swap(0, 1);
1699 depths.swap(0, 1);
1700 }
1701 if points[0].y > points[2].y {
1702 points.swap(0, 2);
1703 depths.swap(0, 2);
1704 }
1705 if points[1].y > points[2].y {
1706 points.swap(1, 2);
1707 depths.swap(1, 2);
1708 }
1709
1710 let [p1, p2, p3] = points;
1711 let [z1, z2, z3] = depths;
1712
1713 let scr_w = width as i32;
1715 let scr_h = (zbuffer.len() / width) as i32;
1716 if p1.x < 0 && p2.x < 0 && p3.x < 0 {
1717 return;
1718 }
1719 if p1.x >= scr_w && p2.x >= scr_w && p3.x >= scr_w {
1720 return;
1721 }
1722 if p1.y < 0 && p2.y < 0 && p3.y < 0 {
1723 return;
1724 }
1725 if p1.y >= scr_h && p2.y >= scr_h && p3.y >= scr_h {
1726 return;
1727 }
1728
1729 fill_triangle_zbuffered(
1730 p1,
1731 p2,
1732 p3,
1733 z1,
1734 z2,
1735 z3,
1736 color,
1737 fb,
1738 zbuffer,
1739 width,
1740 fog_config,
1741 dither_config,
1742 );
1743 }
1744 DrawPrimitive::GouraudTriangleWithDepth {
1745 mut points,
1746 mut depths,
1747 mut colors,
1748 } => {
1749 if points[0].y > points[1].y {
1751 points.swap(0, 1);
1752 depths.swap(0, 1);
1753 colors.swap(0, 1);
1754 }
1755 if points[0].y > points[2].y {
1756 points.swap(0, 2);
1757 depths.swap(0, 2);
1758 colors.swap(0, 2);
1759 }
1760 if points[1].y > points[2].y {
1761 points.swap(1, 2);
1762 depths.swap(1, 2);
1763 colors.swap(1, 2);
1764 }
1765
1766 let [p1, p2, p3] = points;
1767 let [z1, z2, z3] = depths;
1768 let [c1, c2, c3] = colors;
1769
1770 let scr_w = width as i32;
1772 let scr_h = (zbuffer.len() / width) as i32;
1773 if p1.x < 0 && p2.x < 0 && p3.x < 0 {
1774 return;
1775 }
1776 if p1.x >= scr_w && p2.x >= scr_w && p3.x >= scr_w {
1777 return;
1778 }
1779 if p1.y < 0 && p2.y < 0 && p3.y < 0 {
1780 return;
1781 }
1782 if p1.y >= scr_h && p2.y >= scr_h && p3.y >= scr_h {
1783 return;
1784 }
1785
1786 fill_triangle_zbuffered_gouraud(
1787 p1,
1788 p2,
1789 p3,
1790 z1,
1791 z2,
1792 z3,
1793 c1,
1794 c2,
1795 c3,
1796 fb,
1797 zbuffer,
1798 width,
1799 fog_config,
1800 dither_config,
1801 );
1802 }
1803 DrawPrimitive::TexturedTriangle { .. }
1805 | DrawPrimitive::TexturedTriangleWithDepth { .. } => {
1806 }
1809 _ => draw(primitive, fb),
1811 }
1812}
1813
1814#[inline]
1816pub fn draw_zbuffered_with_textures<
1817 D: DrawTarget<Color = embedded_graphics_core::pixelcolor::Rgb565>,
1818 const N: usize,
1819>(
1820 primitive: DrawPrimitive,
1821 fb: &mut D,
1822 zbuffer: &mut [u32],
1823 width: usize,
1824 texture_manager: &crate::texture::TextureManager<N>,
1825 fog_config: Option<&FogConfig>,
1826 dither_config: Option<&DitherConfig>,
1827) where
1828 <D as DrawTarget>::Error: Debug,
1829{
1830 match primitive {
1831 DrawPrimitive::TexturedTriangleWithDepth {
1832 mut points,
1833 mut depths,
1834 mut ws,
1835 mut uvs,
1836 texture_id,
1837 } => {
1838 if let Some(texture) = texture_manager.get(texture_id) {
1840 if points[0].y > points[1].y {
1842 points.swap(0, 1);
1843 depths.swap(0, 1);
1844 ws.swap(0, 1);
1845 uvs.swap(0, 1);
1846 }
1847 if points[0].y > points[2].y {
1848 points.swap(0, 2);
1849 depths.swap(0, 2);
1850 ws.swap(0, 2);
1851 uvs.swap(0, 2);
1852 }
1853 if points[1].y > points[2].y {
1854 points.swap(1, 2);
1855 depths.swap(1, 2);
1856 ws.swap(1, 2);
1857 uvs.swap(1, 2);
1858 }
1859
1860 let [p1, p2, p3] = points;
1861 let [z1, z2, z3] = depths;
1862 let [w1, w2, w3] = ws;
1863 let [uv1, uv2, uv3] = uvs;
1864
1865 let scr_w = width as i32;
1867 let scr_h = (zbuffer.len() / width) as i32;
1868 if p1.x < 0 && p2.x < 0 && p3.x < 0 {
1869 return;
1870 }
1871 if p1.x >= scr_w && p2.x >= scr_w && p3.x >= scr_w {
1872 return;
1873 }
1874 if p1.y < 0 && p2.y < 0 && p3.y < 0 {
1875 return;
1876 }
1877 if p1.y >= scr_h && p2.y >= scr_h && p3.y >= scr_h {
1878 return;
1879 }
1880
1881 fill_triangle_zbuffered_textured(
1882 p1,
1883 p2,
1884 p3,
1885 z1,
1886 z2,
1887 z3,
1888 w1,
1889 w2,
1890 w3,
1891 uv1,
1892 uv2,
1893 uv3,
1894 texture,
1895 fb,
1896 zbuffer,
1897 width,
1898 fog_config,
1899 dither_config,
1900 );
1901 }
1902 }
1903 _ => draw_zbuffered_with_effects(primitive, fb, zbuffer, width, fog_config, dither_config),
1905 }
1906}
1907
1908#[inline(always)]
1909fn fill_triangle_zbuffered<D: DrawTarget<Color = embedded_graphics_core::pixelcolor::Rgb565>>(
1910 p1: nalgebra::Point2<i32>,
1911 p2: nalgebra::Point2<i32>,
1912 p3: nalgebra::Point2<i32>,
1913 z1: f32,
1914 z2: f32,
1915 z3: f32,
1916 color: embedded_graphics_core::pixelcolor::Rgb565,
1917 fb: &mut D,
1918 zbuffer: &mut [u32],
1919 width: usize,
1920 fog_config: Option<&FogConfig>,
1921 dither_config: Option<&DitherConfig>,
1922) where
1923 <D as DrawTarget>::Error: Debug,
1924{
1925 let p1_eg = Point::new(p1.x, p1.y);
1927 let p2_eg = Point::new(p2.x, p2.y);
1928 let p3_eg = Point::new(p3.x, p3.y);
1929
1930 let z1_int = (z1 * 65536.0) as u32;
1933 let z2_int = (z2 * 65536.0) as u32;
1934 let z3_int = (z3 * 65536.0) as u32;
1935
1936 if p2_eg.y == p3_eg.y {
1938 fill_bottom_flat_triangle_zbuffered(
1939 p1_eg,
1940 p2_eg,
1941 p3_eg,
1942 z1_int,
1943 z2_int,
1944 z3_int,
1945 color,
1946 fb,
1947 zbuffer,
1948 width,
1949 fog_config,
1950 dither_config,
1951 );
1952 } else if p1_eg.y == p2_eg.y {
1953 fill_top_flat_triangle_zbuffered(
1954 p1_eg,
1955 p2_eg,
1956 p3_eg,
1957 z1_int,
1958 z2_int,
1959 z3_int,
1960 color,
1961 fb,
1962 zbuffer,
1963 width,
1964 fog_config,
1965 dither_config,
1966 );
1967 } else {
1968 let t = (p2_eg.y - p1_eg.y) as f32 / (p3_eg.y - p1_eg.y) as f32;
1970 let p4 = Point::new(
1971 (p1_eg.x as f32 + t * (p3_eg.x - p1_eg.x) as f32) as i32,
1972 p2_eg.y,
1973 );
1974 let z4_int = (z1_int as i64 + (t * (z3_int as i64 - z1_int as i64) as f32) as i64) as u32;
1975
1976 fill_bottom_flat_triangle_zbuffered(
1977 p1_eg,
1978 p2_eg,
1979 p4,
1980 z1_int,
1981 z2_int,
1982 z4_int,
1983 color,
1984 fb,
1985 zbuffer,
1986 width,
1987 fog_config,
1988 dither_config,
1989 );
1990 fill_top_flat_triangle_zbuffered(
1991 p2_eg,
1992 p4,
1993 p3_eg,
1994 z2_int,
1995 z4_int,
1996 z3_int,
1997 color,
1998 fb,
1999 zbuffer,
2000 width,
2001 fog_config,
2002 dither_config,
2003 );
2004 }
2005}
2006
2007#[inline(always)]
2009fn fill_triangle_zbuffered_gouraud<
2010 D: DrawTarget<Color = embedded_graphics_core::pixelcolor::Rgb565>,
2011>(
2012 p1: nalgebra::Point2<i32>,
2013 p2: nalgebra::Point2<i32>,
2014 p3: nalgebra::Point2<i32>,
2015 z1: f32,
2016 z2: f32,
2017 z3: f32,
2018 c1: embedded_graphics_core::pixelcolor::Rgb565,
2019 c2: embedded_graphics_core::pixelcolor::Rgb565,
2020 c3: embedded_graphics_core::pixelcolor::Rgb565,
2021 fb: &mut D,
2022 zbuffer: &mut [u32],
2023 width: usize,
2024 fog_config: Option<&FogConfig>,
2025 dither_config: Option<&DitherConfig>,
2026) where
2027 <D as DrawTarget>::Error: Debug,
2028{
2029 let p1_eg = Point::new(p1.x, p1.y);
2031 let p2_eg = Point::new(p2.x, p2.y);
2032 let p3_eg = Point::new(p3.x, p3.y);
2033
2034 let z1_int = (z1 * 65536.0) as u32;
2036 let z2_int = (z2 * 65536.0) as u32;
2037 let z3_int = (z3 * 65536.0) as u32;
2038
2039 if p2_eg.y == p3_eg.y {
2041 fill_bottom_flat_triangle_zbuffered_gouraud(
2042 p1_eg,
2043 p2_eg,
2044 p3_eg,
2045 z1_int,
2046 z2_int,
2047 z3_int,
2048 c1,
2049 c2,
2050 c3,
2051 fb,
2052 zbuffer,
2053 width,
2054 fog_config,
2055 dither_config,
2056 );
2057 } else if p1_eg.y == p2_eg.y {
2058 fill_top_flat_triangle_zbuffered_gouraud(
2059 p1_eg,
2060 p2_eg,
2061 p3_eg,
2062 z1_int,
2063 z2_int,
2064 z3_int,
2065 c1,
2066 c2,
2067 c3,
2068 fb,
2069 zbuffer,
2070 width,
2071 fog_config,
2072 dither_config,
2073 );
2074 } else {
2075 let t = (p2_eg.y - p1_eg.y) as f32 / (p3_eg.y - p1_eg.y) as f32;
2077 let p4 = Point::new(
2078 (p1_eg.x as f32 + t * (p3_eg.x - p1_eg.x) as f32) as i32,
2079 p2_eg.y,
2080 );
2081 let z4_int = (z1_int as i64 + (t * (z3_int as i64 - z1_int as i64) as f32) as i64) as u32;
2082 let c4 = interpolate_color(c1, c3, t);
2083
2084 fill_bottom_flat_triangle_zbuffered_gouraud(
2085 p1_eg,
2086 p2_eg,
2087 p4,
2088 z1_int,
2089 z2_int,
2090 z4_int,
2091 c1,
2092 c2,
2093 c4,
2094 fb,
2095 zbuffer,
2096 width,
2097 fog_config,
2098 dither_config,
2099 );
2100 fill_top_flat_triangle_zbuffered_gouraud(
2101 p2_eg,
2102 p4,
2103 p3_eg,
2104 z2_int,
2105 z4_int,
2106 z3_int,
2107 c2,
2108 c4,
2109 c3,
2110 fb,
2111 zbuffer,
2112 width,
2113 fog_config,
2114 dither_config,
2115 );
2116 }
2117}
2118
2119#[inline(always)]
2120fn fill_bottom_flat_triangle_zbuffered<
2121 D: DrawTarget<Color = embedded_graphics_core::pixelcolor::Rgb565>,
2122>(
2123 p1: Point,
2124 p2: Point,
2125 p3: Point,
2126 z1: u32,
2127 z2: u32,
2128 z3: u32,
2129 color: embedded_graphics_core::pixelcolor::Rgb565,
2130 fb: &mut D,
2131 zbuffer: &mut [u32],
2132 width: usize,
2133 fog_config: Option<&FogConfig>,
2134 dither_config: Option<&DitherConfig>,
2135) where
2136 <D as DrawTarget>::Error: Debug,
2137{
2138 let height = p2.y - p1.y;
2139 if height == 0 {
2140 return;
2141 }
2142
2143 let invslope1 = ((p2.x - p1.x) << 16) / height;
2146 let invslope2 = ((p3.x - p1.x) << 16) / height;
2147
2148 let mut curx1 = p1.x << 16; let mut curx2 = p1.x << 16; for scanline_y in p1.y..=p2.y {
2152 let dy = scanline_y - p1.y;
2153 let z_left = if height > 0 {
2155 (z1 as i64 + ((z2 as i64 - z1 as i64) * dy as i64 / height as i64)) as u32
2156 } else {
2157 z1
2158 };
2159 let z_right = if height > 0 {
2160 (z1 as i64 + ((z3 as i64 - z1 as i64) * dy as i64 / height as i64)) as u32
2161 } else {
2162 z1
2163 };
2164
2165 draw_scanline_zbuffered(
2166 curx1 >> 16, curx2 >> 16, scanline_y,
2169 z_left,
2170 z_right,
2171 color,
2172 fb,
2173 zbuffer,
2174 width,
2175 fog_config,
2176 dither_config,
2177 );
2178
2179 curx1 += invslope1;
2180 curx2 += invslope2;
2181 }
2182}
2183
2184#[inline(always)]
2185fn fill_top_flat_triangle_zbuffered<
2186 D: DrawTarget<Color = embedded_graphics_core::pixelcolor::Rgb565>,
2187>(
2188 p1: Point,
2189 p2: Point,
2190 p3: Point,
2191 z1: u32,
2192 z2: u32,
2193 z3: u32,
2194 color: embedded_graphics_core::pixelcolor::Rgb565,
2195 fb: &mut D,
2196 zbuffer: &mut [u32],
2197 width: usize,
2198 fog_config: Option<&FogConfig>,
2199 dither_config: Option<&DitherConfig>,
2200) where
2201 <D as DrawTarget>::Error: Debug,
2202{
2203 let height = p3.y - p1.y;
2204 if height == 0 {
2205 return;
2206 }
2207
2208 let invslope1 = ((p3.x - p1.x) << 16) / height;
2210 let invslope2 = ((p3.x - p2.x) << 16) / height;
2211
2212 let mut curx1 = p3.x << 16; let mut curx2 = p3.x << 16; for scanline_y in (p1.y..=p3.y).rev() {
2216 let dy = scanline_y - p1.y;
2217 let z_left = if height > 0 {
2219 (z1 as i64 + ((z3 as i64 - z1 as i64) * dy as i64 / height as i64)) as u32
2220 } else {
2221 z1
2222 };
2223 let z_right = if height > 0 {
2224 (z2 as i64 + ((z3 as i64 - z2 as i64) * dy as i64 / height as i64)) as u32
2225 } else {
2226 z2
2227 };
2228
2229 draw_scanline_zbuffered(
2230 curx1 >> 16, curx2 >> 16, scanline_y,
2233 z_left,
2234 z_right,
2235 color,
2236 fb,
2237 zbuffer,
2238 width,
2239 fog_config,
2240 dither_config,
2241 );
2242
2243 curx1 -= invslope1;
2244 curx2 -= invslope2;
2245 }
2246}
2247
2248#[inline(always)]
2250fn fill_bottom_flat_triangle_zbuffered_gouraud<
2251 D: DrawTarget<Color = embedded_graphics_core::pixelcolor::Rgb565>,
2252>(
2253 p1: Point,
2254 p2: Point,
2255 p3: Point,
2256 z1: u32,
2257 z2: u32,
2258 z3: u32,
2259 c1: embedded_graphics_core::pixelcolor::Rgb565,
2260 c2: embedded_graphics_core::pixelcolor::Rgb565,
2261 c3: embedded_graphics_core::pixelcolor::Rgb565,
2262 fb: &mut D,
2263 zbuffer: &mut [u32],
2264 width: usize,
2265 fog_config: Option<&FogConfig>,
2266 dither_config: Option<&DitherConfig>,
2267) where
2268 <D as DrawTarget>::Error: Debug,
2269{
2270 let height = p2.y - p1.y;
2271 if height == 0 {
2272 return;
2273 }
2274
2275 let invslope1 = ((p2.x - p1.x) << 16) / height;
2276 let invslope2 = ((p3.x - p1.x) << 16) / height;
2277
2278 let mut curx1 = p1.x << 16;
2279 let mut curx2 = p1.x << 16;
2280
2281 for scanline_y in p1.y..=p2.y {
2282 let dy = scanline_y - p1.y;
2283 let t = dy as f32 / height as f32;
2284
2285 let z_left = if height > 0 {
2287 (z1 as i64 + ((z2 as i64 - z1 as i64) * dy as i64 / height as i64)) as u32
2288 } else {
2289 z1
2290 };
2291 let z_right = if height > 0 {
2292 (z1 as i64 + ((z3 as i64 - z1 as i64) * dy as i64 / height as i64)) as u32
2293 } else {
2294 z1
2295 };
2296
2297 let color_left = interpolate_color(c1, c2, t);
2299 let color_right = interpolate_color(c1, c3, t);
2300
2301 draw_scanline_zbuffered_gouraud(
2302 curx1 >> 16,
2303 curx2 >> 16,
2304 scanline_y,
2305 z_left,
2306 z_right,
2307 color_left,
2308 color_right,
2309 fb,
2310 zbuffer,
2311 width,
2312 fog_config,
2313 dither_config,
2314 );
2315
2316 curx1 += invslope1;
2317 curx2 += invslope2;
2318 }
2319}
2320
2321#[inline(always)]
2323fn fill_top_flat_triangle_zbuffered_gouraud<
2324 D: DrawTarget<Color = embedded_graphics_core::pixelcolor::Rgb565>,
2325>(
2326 p1: Point,
2327 p2: Point,
2328 p3: Point,
2329 z1: u32,
2330 z2: u32,
2331 z3: u32,
2332 c1: embedded_graphics_core::pixelcolor::Rgb565,
2333 c2: embedded_graphics_core::pixelcolor::Rgb565,
2334 c3: embedded_graphics_core::pixelcolor::Rgb565,
2335 fb: &mut D,
2336 zbuffer: &mut [u32],
2337 width: usize,
2338 fog_config: Option<&FogConfig>,
2339 dither_config: Option<&DitherConfig>,
2340) where
2341 <D as DrawTarget>::Error: Debug,
2342{
2343 let height = p3.y - p1.y;
2344 if height == 0 {
2345 return;
2346 }
2347
2348 let invslope1 = ((p3.x - p1.x) << 16) / height;
2349 let invslope2 = ((p3.x - p2.x) << 16) / height;
2350
2351 let mut curx1 = p3.x << 16;
2352 let mut curx2 = p3.x << 16;
2353
2354 for scanline_y in (p1.y..=p3.y).rev() {
2355 let dy = scanline_y - p1.y;
2356 let t = dy as f32 / height as f32;
2357
2358 let z_left = if height > 0 {
2360 (z1 as i64 + ((z3 as i64 - z1 as i64) * dy as i64 / height as i64)) as u32
2361 } else {
2362 z1
2363 };
2364 let z_right = if height > 0 {
2365 (z2 as i64 + ((z3 as i64 - z2 as i64) * dy as i64 / height as i64)) as u32
2366 } else {
2367 z2
2368 };
2369
2370 let color_left = interpolate_color(c1, c3, t);
2372 let color_right = interpolate_color(c2, c3, t);
2373
2374 draw_scanline_zbuffered_gouraud(
2375 curx1 >> 16,
2376 curx2 >> 16,
2377 scanline_y,
2378 z_left,
2379 z_right,
2380 color_left,
2381 color_right,
2382 fb,
2383 zbuffer,
2384 width,
2385 fog_config,
2386 dither_config,
2387 );
2388
2389 curx1 -= invslope1;
2390 curx2 -= invslope2;
2391 }
2392}
2393
2394#[inline(always)]
2396fn draw_scanline_zbuffered_gouraud<
2397 D: DrawTarget<Color = embedded_graphics_core::pixelcolor::Rgb565>,
2398>(
2399 x1: i32,
2400 x2: i32,
2401 y: i32,
2402 z1: u32,
2403 z2: u32,
2404 color1: embedded_graphics_core::pixelcolor::Rgb565,
2405 color2: embedded_graphics_core::pixelcolor::Rgb565,
2406 fb: &mut D,
2407 zbuffer: &mut [u32],
2408 width: usize,
2409 fog_config: Option<&FogConfig>,
2410 dither_config: Option<&DitherConfig>,
2411) where
2412 <D as DrawTarget>::Error: Debug,
2413{
2414 let start = x1.min(x2);
2415 let end = x1.max(x2);
2416 let span_width = end - start;
2417
2418 for x in start..=end {
2419 if x < 0 || y < 0 {
2420 continue;
2421 }
2422
2423 let zbuffer_idx = y as usize * width + x as usize;
2424 if zbuffer_idx >= zbuffer.len() {
2425 continue;
2426 }
2427
2428 let t = if span_width > 0 {
2430 (x - start) as f32 / span_width as f32
2431 } else {
2432 0.0
2433 };
2434 let z = if span_width > 0 {
2435 (z1 as i64 + ((z2 as i64 - z1 as i64) * (x - start) as i64 / span_width as i64)) as u32
2436 } else {
2437 z1
2438 };
2439
2440 if z < zbuffer[zbuffer_idx].saturating_add(DEPTH_EPSILON) {
2443 zbuffer[zbuffer_idx] = z;
2444
2445 let mut final_color = interpolate_color(color1, color2, t);
2447
2448 if let Some(fog) = fog_config {
2450 final_color = fog.apply(final_color, z);
2451 }
2452
2453 if let Some(dither) = dither_config {
2454 final_color = dither.apply(final_color, x, y);
2455 }
2456
2457 fb.draw_iter([embedded_graphics_core::Pixel(Point::new(x, y), final_color)])
2458 .unwrap();
2459 }
2460 }
2461}
2462
2463#[inline(always)]
2464fn draw_scanline_zbuffered<D: DrawTarget<Color = embedded_graphics_core::pixelcolor::Rgb565>>(
2465 x1: i32,
2466 x2: i32,
2467 y: i32,
2468 z1: u32,
2469 z2: u32,
2470 color: embedded_graphics_core::pixelcolor::Rgb565,
2471 fb: &mut D,
2472 zbuffer: &mut [u32],
2473 width: usize,
2474 fog_config: Option<&FogConfig>,
2475 dither_config: Option<&DitherConfig>,
2476) where
2477 <D as DrawTarget>::Error: Debug,
2478{
2479 let start = x1.min(x2);
2480 let end = x1.max(x2);
2481
2482 for x in start..=end {
2483 if x < 0 || y < 0 {
2484 continue;
2485 }
2486
2487 let zbuffer_idx = y as usize * width + x as usize;
2488 if zbuffer_idx >= zbuffer.len() {
2489 continue;
2490 }
2491
2492 let span = end - start;
2495 let z = if span == 0 {
2496 z1
2497 } else {
2498 let t_num = (x - start) as i64;
2500 let t_den = span as i64;
2501 let z_diff = z2 as i64 - z1 as i64;
2502 (z1 as i64 + (t_num * z_diff / t_den)) as u32
2503 };
2504
2505 if z < zbuffer[zbuffer_idx].saturating_add(DEPTH_EPSILON) {
2508 zbuffer[zbuffer_idx] = z;
2509
2510 let mut final_color = color;
2512
2513 if let Some(fog) = fog_config {
2514 final_color = fog.apply(final_color, z);
2515 }
2516
2517 if let Some(dither) = dither_config {
2518 final_color = dither.apply(final_color, x, y);
2519 }
2520
2521 fb.draw_iter([embedded_graphics_core::Pixel(Point::new(x, y), final_color)])
2522 .unwrap();
2523 }
2524 }
2525}
2526
2527#[inline(always)]
2529fn fill_triangle_zbuffered_textured<
2530 D: DrawTarget<Color = embedded_graphics_core::pixelcolor::Rgb565>,
2531>(
2532 p1: nalgebra::Point2<i32>,
2533 p2: nalgebra::Point2<i32>,
2534 p3: nalgebra::Point2<i32>,
2535 z1: f32,
2536 z2: f32,
2537 z3: f32,
2538 w1: f32,
2539 w2: f32,
2540 w3: f32,
2541 uv1: [f32; 2],
2542 uv2: [f32; 2],
2543 uv3: [f32; 2],
2544 texture: &crate::texture::Texture,
2545 fb: &mut D,
2546 zbuffer: &mut [u32],
2547 width: usize,
2548 fog_config: Option<&FogConfig>,
2549 dither_config: Option<&DitherConfig>,
2550) where
2551 <D as DrawTarget>::Error: Debug,
2552{
2553 let p1_eg = Point::new(p1.x, p1.y);
2555 let p2_eg = Point::new(p2.x, p2.y);
2556 let p3_eg = Point::new(p3.x, p3.y);
2557
2558 let z1_int = (z1 * 65536.0) as u32;
2560 let z2_int = (z2 * 65536.0) as u32;
2561 let z3_int = (z3 * 65536.0) as u32;
2562
2563 if p2_eg.y == p3_eg.y {
2565 fill_bottom_flat_triangle_zbuffered_textured(
2566 p1_eg,
2567 p2_eg,
2568 p3_eg,
2569 z1_int,
2570 z2_int,
2571 z3_int,
2572 w1,
2573 w2,
2574 w3,
2575 uv1,
2576 uv2,
2577 uv3,
2578 texture,
2579 fb,
2580 zbuffer,
2581 width,
2582 fog_config,
2583 dither_config,
2584 );
2585 } else if p1_eg.y == p2_eg.y {
2586 fill_top_flat_triangle_zbuffered_textured(
2587 p1_eg,
2588 p2_eg,
2589 p3_eg,
2590 z1_int,
2591 z2_int,
2592 z3_int,
2593 w1,
2594 w2,
2595 w3,
2596 uv1,
2597 uv2,
2598 uv3,
2599 texture,
2600 fb,
2601 zbuffer,
2602 width,
2603 fog_config,
2604 dither_config,
2605 );
2606 } else {
2607 let t = (p2_eg.y - p1_eg.y) as f32 / (p3_eg.y - p1_eg.y) as f32;
2609 let p4 = Point::new(
2610 (p1_eg.x as f32 + t * (p3_eg.x - p1_eg.x) as f32) as i32,
2611 p2_eg.y,
2612 );
2613 let z4_int = (z1_int as i64 + (t * (z3_int as i64 - z1_int as i64) as f32) as i64) as u32;
2614 let w4 = w1 + t * (w3 - w1);
2616 let uv4 = [
2618 uv1[0] + t * (uv3[0] - uv1[0]),
2619 uv1[1] + t * (uv3[1] - uv1[1]),
2620 ];
2621
2622 fill_bottom_flat_triangle_zbuffered_textured(
2623 p1_eg,
2624 p2_eg,
2625 p4,
2626 z1_int,
2627 z2_int,
2628 z4_int,
2629 w1,
2630 w2,
2631 w4,
2632 uv1,
2633 uv2,
2634 uv4,
2635 texture,
2636 fb,
2637 zbuffer,
2638 width,
2639 fog_config,
2640 dither_config,
2641 );
2642 fill_top_flat_triangle_zbuffered_textured(
2643 p2_eg,
2644 p4,
2645 p3_eg,
2646 z2_int,
2647 z4_int,
2648 z3_int,
2649 w2,
2650 w4,
2651 w3,
2652 uv2,
2653 uv4,
2654 uv3,
2655 texture,
2656 fb,
2657 zbuffer,
2658 width,
2659 fog_config,
2660 dither_config,
2661 );
2662 }
2663}
2664
2665#[inline(always)]
2667fn fill_bottom_flat_triangle_zbuffered_textured<
2668 D: DrawTarget<Color = embedded_graphics_core::pixelcolor::Rgb565>,
2669>(
2670 p1: Point,
2671 p2: Point,
2672 p3: Point,
2673 z1: u32,
2674 z2: u32,
2675 z3: u32,
2676 w1: f32,
2677 w2: f32,
2678 w3: f32,
2679 uv1: [f32; 2],
2680 uv2: [f32; 2],
2681 uv3: [f32; 2],
2682 texture: &crate::texture::Texture,
2683 fb: &mut D,
2684 zbuffer: &mut [u32],
2685 width: usize,
2686 fog_config: Option<&FogConfig>,
2687 dither_config: Option<&DitherConfig>,
2688) where
2689 <D as DrawTarget>::Error: Debug,
2690{
2691 let height = p2.y - p1.y;
2692 if height == 0 {
2693 return;
2694 }
2695
2696 let invslope1 = ((p2.x - p1.x) << 16) / height;
2697 let invslope2 = ((p3.x - p1.x) << 16) / height;
2698
2699 let mut curx1 = p1.x << 16;
2700 let mut curx2 = p1.x << 16;
2701
2702 for scanline_y in p1.y..=p2.y {
2703 let dy = scanline_y - p1.y;
2704 let t = dy as f32 / height as f32;
2705
2706 let z_left = if height > 0 {
2708 (z1 as i64 + ((z2 as i64 - z1 as i64) * dy as i64 / height as i64)) as u32
2709 } else {
2710 z1
2711 };
2712 let z_right = if height > 0 {
2713 (z1 as i64 + ((z3 as i64 - z1 as i64) * dy as i64 / height as i64)) as u32
2714 } else {
2715 z1
2716 };
2717
2718 let w_left = w1 + t * (w2 - w1);
2720 let w_right = w1 + t * (w3 - w1);
2721
2722 let uv_left = [
2724 uv1[0] + t * (uv2[0] - uv1[0]),
2725 uv1[1] + t * (uv2[1] - uv1[1]),
2726 ];
2727 let uv_right = [
2728 uv1[0] + t * (uv3[0] - uv1[0]),
2729 uv1[1] + t * (uv3[1] - uv1[1]),
2730 ];
2731
2732 draw_scanline_zbuffered_textured(
2733 curx1 >> 16,
2734 curx2 >> 16,
2735 scanline_y,
2736 z_left,
2737 z_right,
2738 w_left,
2739 w_right,
2740 uv_left,
2741 uv_right,
2742 texture,
2743 fb,
2744 zbuffer,
2745 width,
2746 fog_config,
2747 dither_config,
2748 );
2749
2750 curx1 += invslope1;
2751 curx2 += invslope2;
2752 }
2753}
2754
2755#[inline(always)]
2757fn fill_top_flat_triangle_zbuffered_textured<
2758 D: DrawTarget<Color = embedded_graphics_core::pixelcolor::Rgb565>,
2759>(
2760 p1: Point,
2761 p2: Point,
2762 p3: Point,
2763 z1: u32,
2764 z2: u32,
2765 z3: u32,
2766 w1: f32,
2767 w2: f32,
2768 w3: f32,
2769 uv1: [f32; 2],
2770 uv2: [f32; 2],
2771 uv3: [f32; 2],
2772 texture: &crate::texture::Texture,
2773 fb: &mut D,
2774 zbuffer: &mut [u32],
2775 width: usize,
2776 fog_config: Option<&FogConfig>,
2777 dither_config: Option<&DitherConfig>,
2778) where
2779 <D as DrawTarget>::Error: Debug,
2780{
2781 let height = p3.y - p1.y;
2782 if height == 0 {
2783 return;
2784 }
2785
2786 let invslope1 = ((p3.x - p1.x) << 16) / height;
2787 let invslope2 = ((p3.x - p2.x) << 16) / height;
2788
2789 let mut curx1 = p3.x << 16;
2790 let mut curx2 = p3.x << 16;
2791
2792 for scanline_y in (p1.y..=p3.y).rev() {
2793 let dy = scanline_y - p1.y;
2794 let t = dy as f32 / height as f32;
2795
2796 let z_left = if height > 0 {
2798 (z1 as i64 + ((z3 as i64 - z1 as i64) * dy as i64 / height as i64)) as u32
2799 } else {
2800 z1
2801 };
2802 let z_right = if height > 0 {
2803 (z2 as i64 + ((z3 as i64 - z2 as i64) * dy as i64 / height as i64)) as u32
2804 } else {
2805 z2
2806 };
2807
2808 let w_left = w1 + t * (w3 - w1);
2810 let w_right = w2 + t * (w3 - w2);
2811
2812 let uv_left = [
2814 uv1[0] + t * (uv3[0] - uv1[0]),
2815 uv1[1] + t * (uv3[1] - uv1[1]),
2816 ];
2817 let uv_right = [
2818 uv2[0] + t * (uv3[0] - uv2[0]),
2819 uv2[1] + t * (uv3[1] - uv2[1]),
2820 ];
2821
2822 draw_scanline_zbuffered_textured(
2823 curx1 >> 16,
2824 curx2 >> 16,
2825 scanline_y,
2826 z_left,
2827 z_right,
2828 w_left,
2829 w_right,
2830 uv_left,
2831 uv_right,
2832 texture,
2833 fb,
2834 zbuffer,
2835 width,
2836 fog_config,
2837 dither_config,
2838 );
2839
2840 curx1 -= invslope1;
2841 curx2 -= invslope2;
2842 }
2843}
2844
2845#[inline(always)]
2847fn draw_scanline_zbuffered_textured<
2848 D: DrawTarget<Color = embedded_graphics_core::pixelcolor::Rgb565>,
2849>(
2850 x1: i32,
2851 x2: i32,
2852 y: i32,
2853 z1: u32,
2854 z2: u32,
2855 w1: f32,
2856 w2: f32,
2857 uv1: [f32; 2],
2858 uv2: [f32; 2],
2859 texture: &crate::texture::Texture,
2860 fb: &mut D,
2861 zbuffer: &mut [u32],
2862 width: usize,
2863 fog_config: Option<&FogConfig>,
2864 dither_config: Option<&DitherConfig>,
2865) where
2866 <D as DrawTarget>::Error: Debug,
2867{
2868 let start = x1.min(x2);
2869 let end = x1.max(x2);
2870 let span_width = end - start;
2871
2872 for x in start..=end {
2873 if x < 0 || y < 0 {
2874 continue;
2875 }
2876
2877 let zbuffer_idx = y as usize * width + x as usize;
2878 if zbuffer_idx >= zbuffer.len() {
2879 continue;
2880 }
2881
2882 let t = if span_width > 0 {
2884 (x - start) as f32 / span_width as f32
2885 } else {
2886 0.0
2887 };
2888 let z = if span_width > 0 {
2889 (z1 as i64 + ((z2 as i64 - z1 as i64) * (x - start) as i64 / span_width as i64)) as u32
2890 } else {
2891 z1
2892 };
2893
2894 if z < zbuffer[zbuffer_idx].saturating_add(DEPTH_EPSILON) {
2897 zbuffer[zbuffer_idx] = z;
2898
2899 let ow1 = 1.0 / w1;
2901 let ow2 = 1.0 / w2;
2902 let one_over_w = ow1 + t * (ow2 - ow1);
2903 let u = (uv1[0] * ow1 + t * (uv2[0] * ow2 - uv1[0] * ow1)) / one_over_w;
2904 let v = (uv1[1] * ow1 + t * (uv2[1] * ow2 - uv1[1] * ow1)) / one_over_w;
2905
2906 let mut final_color = texture.sample(u, v);
2908
2909 if let Some(fog) = fog_config {
2911 final_color = fog.apply(final_color, z);
2912 }
2913
2914 if let Some(dither) = dither_config {
2915 final_color = dither.apply(final_color, x, y);
2916 }
2917
2918 fb.draw_iter([embedded_graphics_core::Pixel(Point::new(x, y), final_color)])
2919 .unwrap();
2920 }
2921 }
2922}
2923
2924#[cfg(test)]
2925mod tests {
2926 extern crate std;
2927 use super::*;
2928 use embedded_graphics_core::pixelcolor::Rgb565;
2929 use embedded_graphics_core::prelude::*;
2930 use nalgebra::Point2;
2931
2932 struct MockFramebuffer {
2934 pixels: std::vec::Vec<(i32, i32, Rgb565)>,
2935 }
2936
2937 impl MockFramebuffer {
2938 fn new() -> Self {
2939 Self {
2940 pixels: std::vec::Vec::new(),
2941 }
2942 }
2943
2944 fn contains_pixel(&self, x: i32, y: i32) -> bool {
2945 self.pixels.iter().any(|(px, py, _)| *px == x && *py == y)
2946 }
2947
2948 fn pixel_count(&self) -> usize {
2949 self.pixels.len()
2950 }
2951 }
2952
2953 impl DrawTarget for MockFramebuffer {
2954 type Color = Rgb565;
2955 type Error = core::convert::Infallible;
2956
2957 fn draw_iter<I>(&mut self, pixels: I) -> Result<(), Self::Error>
2958 where
2959 I: IntoIterator<Item = embedded_graphics_core::Pixel<Self::Color>>,
2960 {
2961 for pixel in pixels {
2962 self.pixels.push((pixel.0.x, pixel.0.y, pixel.1));
2963 }
2964 Ok(())
2965 }
2966 }
2967
2968 impl OriginDimensions for MockFramebuffer {
2969 fn size(&self) -> Size {
2970 Size::new(640, 480)
2971 }
2972 }
2973
2974 #[test]
2975 fn test_draw_point() {
2976 let mut fb = MockFramebuffer::new();
2977 let point = Point2::new(10, 20);
2978 let color = Rgb565::CSS_RED;
2979
2980 draw(DrawPrimitive::ColoredPoint(point, color), &mut fb);
2981
2982 assert_eq!(fb.pixel_count(), 1);
2983 assert!(fb.contains_pixel(10, 20));
2984 }
2985
2986 #[test]
2987 fn test_draw_line_horizontal() {
2988 let mut fb = MockFramebuffer::new();
2989 let p1 = Point2::new(10, 20);
2990 let p2 = Point2::new(20, 20);
2991 let color = Rgb565::CSS_GREEN;
2992
2993 draw(DrawPrimitive::Line([p1, p2], color), &mut fb);
2994
2995 assert!(fb.pixel_count() >= 10); assert!(fb.contains_pixel(10, 20));
2998 assert!(fb.contains_pixel(20, 20));
2999 }
3000
3001 #[test]
3002 fn test_draw_line_vertical() {
3003 let mut fb = MockFramebuffer::new();
3004 let p1 = Point2::new(10, 10);
3005 let p2 = Point2::new(10, 20);
3006 let color = Rgb565::CSS_BLUE;
3007
3008 draw(DrawPrimitive::Line([p1, p2], color), &mut fb);
3009
3010 assert!(fb.pixel_count() >= 10);
3012 assert!(fb.contains_pixel(10, 10));
3013 assert!(fb.contains_pixel(10, 20));
3014 }
3015
3016 #[test]
3017 fn test_draw_line_diagonal() {
3018 let mut fb = MockFramebuffer::new();
3019 let p1 = Point2::new(0, 0);
3020 let p2 = Point2::new(10, 10);
3021 let color = Rgb565::CSS_WHITE;
3022
3023 draw(DrawPrimitive::Line([p1, p2], color), &mut fb);
3024
3025 assert!(fb.pixel_count() >= 10);
3027 assert!(fb.contains_pixel(0, 0));
3028 assert!(fb.contains_pixel(10, 10));
3029 }
3030
3031 #[test]
3032 fn test_draw_triangle_flat_bottom() {
3033 let mut fb = MockFramebuffer::new();
3034 let vertices = [
3035 Point2::new(50, 10), Point2::new(30, 30), Point2::new(70, 30), ];
3039 let color = Rgb565::CSS_YELLOW;
3040
3041 draw(DrawPrimitive::ColoredTriangle(vertices, color), &mut fb);
3042
3043 let count = fb.pixel_count();
3045 assert!(count > 0, "Expected pixels to be drawn, got {}", count);
3046 assert!(fb.contains_pixel(50, 10));
3048 }
3049
3050 #[test]
3051 fn test_draw_triangle_flat_top() {
3052 let mut fb = MockFramebuffer::new();
3053 let vertices = [
3054 Point2::new(30, 10), Point2::new(70, 10), Point2::new(50, 30), ];
3058 let color = Rgb565::CSS_CYAN;
3059
3060 draw(DrawPrimitive::ColoredTriangle(vertices, color), &mut fb);
3061
3062 assert!(fb.pixel_count() > 20);
3064 assert!(fb.contains_pixel(50, 30));
3065 }
3066
3067 #[test]
3068 fn test_draw_triangle_general() {
3069 let mut fb = MockFramebuffer::new();
3070 let vertices = [
3071 Point2::new(50, 10),
3072 Point2::new(30, 30),
3073 Point2::new(80, 40),
3074 ];
3075 let color = Rgb565::CSS_MAGENTA;
3076
3077 draw(DrawPrimitive::ColoredTriangle(vertices, color), &mut fb);
3078
3079 assert!(fb.pixel_count() > 30);
3081 }
3082
3083 #[test]
3084 fn test_triangle_vertex_sorting() {
3085 let mut fb = MockFramebuffer::new();
3086 let vertices = [
3088 Point2::new(50, 30), Point2::new(30, 10), Point2::new(70, 20), ];
3092 let color = Rgb565::CSS_WHITE;
3093
3094 draw(DrawPrimitive::ColoredTriangle(vertices, color), &mut fb);
3096
3097 assert!(fb.pixel_count() > 10);
3098 }
3099
3100 #[test]
3101 fn test_draw_multiple_primitives() {
3102 let mut fb = MockFramebuffer::new();
3103
3104 draw(
3105 DrawPrimitive::ColoredPoint(Point2::new(5, 5), Rgb565::CSS_RED),
3106 &mut fb,
3107 );
3108 draw(
3109 DrawPrimitive::Line(
3110 [Point2::new(10, 10), Point2::new(20, 20)],
3111 Rgb565::CSS_GREEN,
3112 ),
3113 &mut fb,
3114 );
3115
3116 assert!(fb.pixel_count() > 11); assert!(fb.contains_pixel(5, 5));
3119 }
3120}