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;
30use embedded_graphics_core::pixelcolor::Rgb565;
31use embedded_graphics_core::pixelcolor::RgbColor;
32use embedded_graphics_core::prelude::Point;
33use heapless::Vec;
34
35use crate::DrawPrimitive;
36use crate::retro::{PaletteMode, ScreenTint, StippleMode, TextureMapping};
37
38#[cfg(feature = "aa")]
53pub trait ReadPixel {
54 fn read_pixel(&self, point: Point) -> Rgb565;
56}
57
58pub use embedded_draw_target::PixelRead;
64
65#[cfg(feature = "aa")]
66impl<T: PixelRead<Color = Rgb565>> ReadPixel for T {
67 #[inline]
68 fn read_pixel(&self, point: Point) -> Rgb565 {
69 self.get_pixel(point)
70 }
71}
72
73#[inline(always)]
77pub fn fast_blend_rgb565(bg: Rgb565, fg: Rgb565, alpha: u8) -> Rgb565 {
78 if alpha == 255 {
79 return fg;
80 }
81 if alpha == 0 {
82 return bg;
83 }
84 let a = alpha as u32;
85 let inv = 255 - a;
86 let r = (bg.r() as u32 * inv + fg.r() as u32 * a) / 255;
87 let g = (bg.g() as u32 * inv + fg.g() as u32 * a) / 255;
88 let b = (bg.b() as u32 * inv + fg.b() as u32 * a) / 255;
89 Rgb565::new(r as u8, g as u8, b as u8)
90}
91
92#[inline(always)]
96pub fn fast_blend_rgba8888(bg: [u8; 4], fg: [u8; 4]) -> [u8; 4] {
97 let a = fg[3] as u32;
98 if a == 255 {
99 return fg;
100 }
101 if a == 0 {
102 return bg;
103 }
104 let inv = 255 - a;
105 let r = (bg[0] as u32 * inv + fg[0] as u32 * a) / 255;
106 let g = (bg[1] as u32 * inv + fg[1] as u32 * a) / 255;
107 let b = (bg[2] as u32 * inv + fg[2] as u32 * a) / 255;
108 let out_a = fg[3] as u32 + (bg[3] as u32 * inv) / 255;
109 [r as u8, g as u8, b as u8, out_a as u8]
110}
111
112#[inline(always)]
116pub fn fast_blend_rgba8888_to_rgb565(bg: Rgb565, fg_rgba: [u8; 4]) -> Rgb565 {
117 let alpha = fg_rgba[3];
118 if alpha == 0 {
119 return bg;
120 }
121 let fg_r = fg_rgba[0] >> 3;
122 let fg_g = fg_rgba[1] >> 2;
123 let fg_b = fg_rgba[2] >> 3;
124 let fg_565 = Rgb565::new(fg_r, fg_g, fg_b);
125 fast_blend_rgb565(bg, fg_565, alpha)
126}
127
128#[inline(always)]
132pub fn reverse_color_rgb565(c: Rgb565) -> Rgb565 {
133 Rgb565::new(31 - c.r(), 63 - c.g(), 31 - c.b())
134}
135
136#[inline(always)]
140pub fn reverse_color_rgba8888(rgba: [u8; 4]) -> [u8; 4] {
141 [255 - rgba[0], 255 - rgba[1], 255 - rgba[2], rgba[3]]
142}
143
144#[cfg(feature = "aa")]
147#[inline(always)]
148fn blend_q8(bg: Rgb565, fg: Rgb565, coverage_q8: u32) -> Rgb565 {
149 let inv = 256 - coverage_q8;
150 let r = (bg.r() as u32 * inv + fg.r() as u32 * coverage_q8) >> 8;
151 let g = (bg.g() as u32 * inv + fg.g() as u32 * coverage_q8) >> 8;
152 let b = (bg.b() as u32 * inv + fg.b() as u32 * coverage_q8) >> 8;
153 Rgb565::new(r as u8, g as u8, b as u8)
154}
155
156#[cfg(feature = "aa-heuristic")]
168#[inline(always)]
169fn aa_pixel<D>(
170 fb: &mut D,
171 x: i32,
172 y: i32,
173 color: Rgb565,
174 z: u32,
175 zbuffer: &mut [crate::ZDepth],
176 width: usize,
177 coverage_q8: u32,
178) where
179 D: DrawTarget<Color = Rgb565> + ReadPixel,
180 <D as DrawTarget>::Error: Debug,
181{
182 if x < 0 || y < 0 || x >= width as i32 || coverage_q8 == 0 {
183 return;
184 }
185 let idx = y as usize * width + x as usize;
186 if idx >= zbuffer.len() {
187 return;
188 }
189 let z_depth = crate::to_zdepth(z);
190 if z_depth >= zbuffer[idx].saturating_add(crate::DEPTH_EPSILON) {
191 return;
192 }
193
194 let pixel_was_virgin = zbuffer[idx] == crate::Z_MAX_VALUE;
195 let final_color = if coverage_q8 >= 256 || !pixel_was_virgin {
196 color
197 } else {
198 let bg = fb.read_pixel(Point::new(x, y));
199 blend_q8(bg, color, coverage_q8)
200 };
201 zbuffer[idx] = z_depth;
202 fb.draw_iter([embedded_graphics_core::Pixel(Point::new(x, y), final_color)])
203 .unwrap();
204}
205
206#[derive(Debug, Clone, Copy)]
224pub struct FogConfig {
225 pub color: embedded_graphics_core::pixelcolor::Rgb565,
227 pub near: u32,
229 pub far: u32,
231}
232
233impl FogConfig {
234 pub fn new(color: embedded_graphics_core::pixelcolor::Rgb565, near: f32, far: f32) -> Self {
241 Self {
242 color,
243 near: (near * 65536.0) as u32,
244 far: (far * 65536.0) as u32,
245 }
246 }
247
248 #[inline]
250 pub fn apply(
251 &self,
252 base_color: embedded_graphics_core::pixelcolor::Rgb565,
253 depth: u32,
254 ) -> embedded_graphics_core::pixelcolor::Rgb565 {
255 let fog_factor = if depth <= self.near {
257 0u32
258 } else if depth >= self.far {
259 65536u32 } else {
261 let numerator = (depth - self.near) as u64;
263 let denominator = (self.far - self.near) as u64;
264 ((numerator * 65536) / denominator) as u32
265 };
266
267 let base_r = base_color.r() as u32;
270 let base_g = base_color.g() as u32;
271 let base_b = base_color.b() as u32;
272
273 let fog_r = self.color.r() as u32;
274 let fog_g = self.color.g() as u32;
275 let fog_b = self.color.b() as u32;
276
277 let r = ((base_r * (65536 - fog_factor) + fog_r * fog_factor) / 65536) as u8;
279 let g = ((base_g * (65536 - fog_factor) + fog_g * fog_factor) / 65536) as u8;
280 let b = ((base_b * (65536 - fog_factor) + fog_b * fog_factor) / 65536) as u8;
281
282 embedded_graphics_core::pixelcolor::Rgb565::new(r, g, b)
283 }
284}
285
286#[derive(Debug, Clone, Copy)]
288pub struct DitherConfig {
289 pub intensity: u8,
291}
292
293impl DitherConfig {
294 const BAYER_MATRIX: [[u8; 4]; 4] =
297 [[0, 8, 2, 10], [12, 4, 14, 6], [3, 11, 1, 9], [15, 7, 13, 5]];
298
299 pub fn new(intensity: u8) -> Self {
301 Self { intensity }
302 }
303
304 #[inline]
306 pub fn apply(
307 &self,
308 color: embedded_graphics_core::pixelcolor::Rgb565,
309 x: i32,
310 y: i32,
311 ) -> embedded_graphics_core::pixelcolor::Rgb565 {
312 if self.intensity == 0 {
313 return color;
314 }
315
316 let matrix_x = (x & 3) as usize;
318 let matrix_y = (y & 3) as usize;
319 let threshold = Self::BAYER_MATRIX[matrix_y][matrix_x];
320
321 let scaled_threshold = ((threshold as u16 * self.intensity as u16) / 15) as u8;
325
326 let r = color.r();
328 let g = color.g();
329 let b = color.b();
330
331 let r = if r > scaled_threshold {
333 r.saturating_sub(scaled_threshold / 2)
334 } else {
335 r.saturating_add(scaled_threshold / 2)
336 };
337
338 let g = if g > scaled_threshold {
339 g.saturating_sub(scaled_threshold / 2)
340 } else {
341 g.saturating_add(scaled_threshold / 2)
342 };
343
344 let b = if b > scaled_threshold {
345 b.saturating_sub(scaled_threshold / 2)
346 } else {
347 b.saturating_add(scaled_threshold / 2)
348 };
349
350 embedded_graphics_core::pixelcolor::Rgb565::new(r, g, b)
351 }
352}
353
354const FP_SHIFT: i64 = 16;
356
357#[inline(always)]
358fn fixed_to_i32(value: i64) -> i32 {
359 if value >= 0 {
360 (value >> FP_SHIFT) as i32
361 } else {
362 -((-value) >> FP_SHIFT) as i32
363 }
364}
365
366struct EdgeStepper {
367 x: i64,
368 step: i64,
369}
370
371impl EdgeStepper {
372 fn new(start: Point, end: Point, y: i32) -> Self {
373 let dy = (end.y - start.y) as i64;
374 let (step, x) = if dy != 0 {
375 let s = (((end.x - start.x) as i64) << FP_SHIFT) / dy;
376 let x = ((start.x as i64) << FP_SHIFT) + s * (y - start.y) as i64;
377 (s, x)
378 } else {
379 (0, (start.x as i64) << FP_SHIFT)
380 };
381 Self { x, step }
382 }
383
384 #[inline(always)]
385 fn current_x(&self) -> i32 {
386 fixed_to_i32(self.x)
387 }
388
389 #[inline(always)]
390 fn advance(&mut self) {
391 self.x += self.step;
392 }
393}
394
395#[inline(always)]
396pub fn fill_triangle<D: DrawTarget<Color = embedded_graphics_core::pixelcolor::Rgb565>>(
397 p1: Point,
398 p2: Point,
399 p3: Point,
400 color: embedded_graphics_core::pixelcolor::Rgb565,
401 fb: &mut D,
402) where
403 <D as DrawTarget>::Error: Debug,
404{
405 let area = (p2.x - p1.x) * (p3.y - p1.y) - (p2.y - p1.y) * (p3.x - p1.x);
406 if area == 0 {
407 return;
409 }
410
411 let bounds = fb.bounding_box();
412 let min_x = bounds.top_left.x;
413 let max_x = bounds.bottom_right().unwrap().x;
414
415 let mut pixel_row: [embedded_graphics_core::Pixel<embedded_graphics_core::pixelcolor::Rgb565>;
416 MAX_ROW_WIDTH] = [embedded_graphics_core::Pixel(
417 Point::new(0, 0),
418 embedded_graphics_core::pixelcolor::RgbColor::BLACK,
419 ); MAX_ROW_WIDTH];
420
421 if p2.y - p1.y > 0 {
423 let mut a = EdgeStepper::new(p1, p2, p1.y);
424 let mut b = EdgeStepper::new(p1, p3, p1.y);
425
426 for y in p1.y..p2.y {
427 let ax = a.current_x();
428 let bx = b.current_x();
429 let (start_x, end_x) = if ax < bx { (ax, bx) } else { (bx, ax) };
430 let start_x = start_x.clamp(min_x, max_x);
431 let end_x = end_x.clamp(min_x, max_x);
432 let mut x = start_x;
433 while x <= end_x {
434 let chunk_end = (x + MAX_ROW_WIDTH as i32 - 1).min(end_x);
435 let mut i = 0usize;
436 for sx in x..=chunk_end {
437 pixel_row[i] = embedded_graphics_core::Pixel(Point::new(sx, y), color);
438 i += 1;
439 }
440 fb.draw_iter(pixel_row[..i].iter().copied()).unwrap();
441 x = chunk_end + 1;
442 }
443 a.advance();
444 b.advance();
445 }
446 }
447
448 if p3.y - p2.y > 0 {
450 let mut a = EdgeStepper::new(p2, p3, p2.y);
451 let mut b = EdgeStepper::new(p1, p3, p2.y);
452
453 for y in p2.y..=p3.y {
454 let ax = a.current_x();
455 let bx = b.current_x();
456 let (start_x, end_x) = if ax < bx { (ax, bx) } else { (bx, ax) };
457 let start_x = start_x.clamp(min_x, max_x);
458 let end_x = end_x.clamp(min_x, max_x);
459 let mut x = start_x;
460 while x <= end_x {
461 let chunk_end = (x + MAX_ROW_WIDTH as i32 - 1).min(end_x);
462 let mut i = 0usize;
463 for sx in x..=chunk_end {
464 pixel_row[i] = embedded_graphics_core::Pixel(Point::new(sx, y), color);
465 i += 1;
466 }
467 fb.draw_iter(pixel_row[..i].iter().copied()).unwrap();
468 x = chunk_end + 1;
469 }
470 a.advance();
471 b.advance();
472 }
473 }
474}
475
476#[allow(dead_code)]
477fn fill_bottom_flat_triangle<D: DrawTarget<Color = embedded_graphics_core::pixelcolor::Rgb565>>(
478 p1: Point,
479 p2: Point,
480 p3: Point,
481 color: embedded_graphics_core::pixelcolor::Rgb565,
482 fb: &mut D,
483) where
484 <D as DrawTarget>::Error: Debug,
485{
486 let mut edge1 = EdgeStepper::new(p1, p2, p1.y);
487 let mut edge2 = EdgeStepper::new(p1, p3, p1.y);
488
489 for scanline_y in p1.y..=p2.y {
490 draw_horizontal_line(
491 Point::new(edge1.current_x(), scanline_y),
492 Point::new(edge2.current_x(), scanline_y),
493 color,
494 fb,
495 );
496 edge1.advance();
497 edge2.advance();
498 }
499}
500
501#[allow(dead_code)]
502fn fill_top_flat_triangle<D: DrawTarget<Color = embedded_graphics_core::pixelcolor::Rgb565>>(
503 p1: Point,
504 p2: Point,
505 p3: Point,
506 color: embedded_graphics_core::pixelcolor::Rgb565,
507 fb: &mut D,
508) where
509 <D as DrawTarget>::Error: Debug,
510{
511 let mut edge1 = EdgeStepper::new(p1, p3, p1.y);
513 let mut edge2 = EdgeStepper::new(p2, p3, p1.y);
514
515 for scanline_y in p1.y..=p3.y {
516 draw_horizontal_line(
517 Point::new(edge1.current_x(), scanline_y),
518 Point::new(edge2.current_x(), scanline_y),
519 color,
520 fb,
521 );
522 edge1.advance();
523 edge2.advance();
524 }
525}
526
527#[allow(dead_code)]
528fn draw_horizontal_line<D: DrawTarget<Color = embedded_graphics_core::pixelcolor::Rgb565>>(
529 p1: Point,
530 p2: Point,
531 color: embedded_graphics_core::pixelcolor::Rgb565,
532 fb: &mut D,
533) where
534 <D as DrawTarget>::Error: Debug,
535{
536 let start = p1.x.min(p2.x);
537 let end = p1.x.max(p2.x);
538
539 for x in start..=end {
540 fb.draw_iter([embedded_graphics_core::Pixel(Point::new(x, p1.y), color)])
541 .unwrap();
542 }
543}
544
545#[derive(Clone, Copy, Default)]
546struct ScreenVert {
547 x: f32,
548 y: f32,
549}
550
551#[inline]
552fn clip_polygon_plane_2d(
553 input: &[ScreenVert],
554 output: &mut [ScreenVert; 8],
555 dist: impl Fn(ScreenVert) -> f32,
556) -> usize {
557 let n = input.len();
558 let mut m = 0usize;
559 for i in 0..n {
560 let prev = input[(n + i - 1) % n];
561 let curr = input[i];
562 let d_prev = dist(prev);
563 let d_curr = dist(curr);
564 if d_curr >= 0.0 {
565 if d_prev < 0.0 {
566 let t = d_prev / (d_prev - d_curr);
567 if m < 8 {
568 output[m] = ScreenVert {
569 x: prev.x + (curr.x - prev.x) * t,
570 y: prev.y + (curr.y - prev.y) * t,
571 };
572 m += 1;
573 }
574 }
575 if m < 8 {
576 output[m] = curr;
577 m += 1;
578 }
579 } else if d_prev >= 0.0 {
580 let t = d_prev / (d_prev - d_curr);
581 if m < 8 {
582 output[m] = ScreenVert {
583 x: prev.x + (curr.x - prev.x) * t,
584 y: prev.y + (curr.y - prev.y) * t,
585 };
586 m += 1;
587 }
588 }
589 }
590 m
591}
592
593#[inline]
594fn tri_area2(a: Point, b: Point, c: Point) -> i32 {
595 (b.x - a.x) * (c.y - a.y) - (b.y - a.y) * (c.x - a.x)
596}
597
598#[inline]
599fn round_to_i32(v: f32) -> i32 {
600 if v >= 0.0 {
601 (v + 0.5) as i32
602 } else {
603 (v - 0.5) as i32
604 }
605}
606
607#[inline]
608fn fill_triangle_screen_clipped<D: DrawTarget<Color = embedded_graphics_core::pixelcolor::Rgb565>>(
609 p1: Point,
610 p2: Point,
611 p3: Point,
612 color: embedded_graphics_core::pixelcolor::Rgb565,
613 fb: &mut D,
614) where
615 <D as DrawTarget>::Error: Debug,
616{
617 let bounds = fb.bounding_box();
618 let max_x = bounds.size.width.saturating_sub(1) as f32;
619 let max_y = bounds.size.height.saturating_sub(1) as f32;
620 if max_x < 0.0 || max_y < 0.0 {
621 return;
622 }
623
624 let mut a = [ScreenVert::default(); 8];
625 let mut b = [ScreenVert::default(); 8];
626 a[0] = ScreenVert {
627 x: p1.x as f32,
628 y: p1.y as f32,
629 };
630 a[1] = ScreenVert {
631 x: p2.x as f32,
632 y: p2.y as f32,
633 };
634 a[2] = ScreenVert {
635 x: p3.x as f32,
636 y: p3.y as f32,
637 };
638
639 let n = clip_polygon_plane_2d(&a[..3], &mut b, |v| v.x); if n < 3 {
641 return;
642 }
643 let n = clip_polygon_plane_2d(&b[..n], &mut a, |v| max_x - v.x); if n < 3 {
645 return;
646 }
647 let n = clip_polygon_plane_2d(&a[..n], &mut b, |v| v.y); if n < 3 {
649 return;
650 }
651 let n = clip_polygon_plane_2d(&b[..n], &mut a, |v| max_y - v.y); if n < 3 {
653 return;
654 }
655
656 for i in 1..n - 1 {
657 let t0 = Point::new(round_to_i32(a[0].x), round_to_i32(a[0].y));
658 let t1 = Point::new(round_to_i32(a[i].x), round_to_i32(a[i].y));
659 let t2 = Point::new(round_to_i32(a[i + 1].x), round_to_i32(a[i + 1].y));
660 if tri_area2(t0, t1, t2) == 0 {
661 continue;
662 }
663 fill_triangle(t0, t1, t2, color, fb);
664 }
665}
666
667#[inline]
668pub fn draw<D: DrawTarget<Color = embedded_graphics_core::pixelcolor::Rgb565>>(
669 primitive: DrawPrimitive,
670 fb: &mut D,
671) where
672 <D as DrawTarget>::Error: Debug,
673{
674 match primitive {
675 DrawPrimitive::Line([p1, p2], color) => {
676 fb.draw_iter(
677 line_drawing::Bresenham::new((p1.x, p1.y), (p2.x, p2.y))
678 .map(|(x, y)| embedded_graphics_core::Pixel(Point::new(x, y), color)),
679 )
680 .unwrap();
681 }
682 DrawPrimitive::ColoredPoint(p, c) => {
683 let p = embedded_graphics_core::geometry::Point::new(p.x, p.y);
684
685 fb.draw_iter([embedded_graphics_core::Pixel(p, c)]).unwrap();
686 }
687 DrawPrimitive::ColoredTriangle(mut vertices, color) => {
688 vertices.as_mut_slice().sort_unstable_by_key(|a| a.y);
690
691 let [p1, p2, p3] = [
692 Point::new(vertices[0].x, vertices[0].y),
693 Point::new(vertices[1].x, vertices[1].y),
694 Point::new(vertices[2].x, vertices[2].y),
695 ];
696 fill_triangle_screen_clipped(p1, p2, p3, color, fb);
697 }
698 DrawPrimitive::ColoredTriangleWithDepth {
699 points,
700 depths: _,
701 color,
702 }
703 | DrawPrimitive::TranslucentTriangleWithDepth {
704 points,
705 depths: _,
706 color,
707 alpha: _,
708 } => {
709 let mut vertices = points;
712 if vertices[0].y > vertices[1].y {
713 vertices.swap(0, 1);
714 }
715 if vertices[0].y > vertices[2].y {
716 vertices.swap(0, 2);
717 }
718 if vertices[1].y > vertices[2].y {
719 vertices.swap(1, 2);
720 }
721
722 let mut buf: Vec<_, 3> = Vec::new();
723 for p in vertices.iter() {
724 buf.push(embedded_graphics_core::geometry::Point::new(p.x, p.y))
725 .unwrap();
726 }
727 let [p1, p2, p3] = buf.into_array().unwrap();
728 fill_triangle_screen_clipped(p1, p2, p3, color, fb);
729 }
730 DrawPrimitive::GouraudTriangle {
731 mut points,
732 mut colors,
733 } => {
734 if points[0].y > points[1].y {
736 points.swap(0, 1);
737 colors.swap(0, 1);
738 }
739 if points[0].y > points[2].y {
740 points.swap(0, 2);
741 colors.swap(0, 2);
742 }
743 if points[1].y > points[2].y {
744 points.swap(1, 2);
745 colors.swap(1, 2);
746 }
747
748 let mut buf: Vec<_, 3> = Vec::new();
749 for p in points.iter() {
750 buf.push(embedded_graphics_core::geometry::Point::new(p.x, p.y))
751 .unwrap();
752 }
753 let [p1, p2, p3] = buf.into_array().unwrap();
754 let [c1, c2, c3] = colors;
755
756 let bounds = fb.bounding_box();
758 let scr_w = bounds.size.width as i32;
759 let scr_h = bounds.size.height as i32;
760 if p1.x < 0 && p2.x < 0 && p3.x < 0 {
761 return;
762 }
763 if p1.x >= scr_w && p2.x >= scr_w && p3.x >= scr_w {
764 return;
765 }
766 if p1.y < 0 && p2.y < 0 && p3.y < 0 {
767 return;
768 }
769 if p1.y >= scr_h && p2.y >= scr_h && p3.y >= scr_h {
770 return;
771 }
772
773 if p2.y == p3.y {
774 fill_bottom_flat_gouraud(p1, p2, p3, c1, c2, c3, fb);
775 } else if p1.y == p2.y {
776 fill_top_flat_gouraud(p1, p2, p3, c1, c2, c3, fb);
777 } else {
778 let t = (p2.y - p1.y) as f32 / (p3.y - p1.y) as f32;
780 let p4 = Point::new((p1.x as f32 + t * (p3.x - p1.x) as f32) as i32, p2.y);
781 let c4 = interpolate_color(c1, c3, t);
783
784 fill_bottom_flat_gouraud(p1, p2, p4, c1, c2, c4, fb);
785 fill_top_flat_gouraud(p2, p4, p3, c2, c4, c3, fb);
786 }
787 }
788 DrawPrimitive::GouraudTriangleWithDepth {
789 points,
790 depths: _,
791 colors,
792 } => {
793 let prim = DrawPrimitive::GouraudTriangle { points, colors };
796 draw(prim, fb);
797 }
798 DrawPrimitive::TexturedTriangle { .. }
799 | DrawPrimitive::TexturedTriangleWithDepth { .. }
800 | DrawPrimitive::TexturedGouraudTriangleWithDepth { .. }
801 | DrawPrimitive::LightmappedTriangle { .. } => {
802 }
805 }
806}
807
808#[inline]
810fn interpolate_color(
811 c1: embedded_graphics_core::pixelcolor::Rgb565,
812 c2: embedded_graphics_core::pixelcolor::Rgb565,
813 t: f32,
814) -> embedded_graphics_core::pixelcolor::Rgb565 {
815 let r1 = c1.r() as f32;
816 let g1 = c1.g() as f32;
817 let b1 = c1.b() as f32;
818
819 let r2 = c2.r() as f32;
820 let g2 = c2.g() as f32;
821 let b2 = c2.b() as f32;
822
823 let r = (r1 + t * (r2 - r1)) as u8;
824 let g = (g1 + t * (g2 - g1)) as u8;
825 let b = (b1 + t * (b2 - b1)) as u8;
826
827 embedded_graphics_core::pixelcolor::Rgb565::new(r, g, b)
828}
829
830fn fill_bottom_flat_gouraud<D: DrawTarget<Color = embedded_graphics_core::pixelcolor::Rgb565>>(
832 p1: Point,
833 p2: Point,
834 p3: Point,
835 c1: embedded_graphics_core::pixelcolor::Rgb565,
836 c2: embedded_graphics_core::pixelcolor::Rgb565,
837 c3: embedded_graphics_core::pixelcolor::Rgb565,
838 fb: &mut D,
839) where
840 <D as DrawTarget>::Error: Debug,
841{
842 let height = (p2.y - p1.y) as f32;
843 if height == 0.0 {
844 return;
845 }
846
847 let mut edge1 = EdgeStepper::new(p1, p2, p1.y);
848 let mut edge2 = EdgeStepper::new(p1, p3, p1.y);
849
850 for scanline_y in p1.y..=p2.y {
851 let t = (scanline_y - p1.y) as f32 / height;
852 let color_left = interpolate_color(c1, c2, t);
853 let color_right = interpolate_color(c1, c3, t);
854
855 draw_horizontal_line_gouraud(
856 Point::new(edge1.current_x(), scanline_y),
857 Point::new(edge2.current_x(), scanline_y),
858 color_left,
859 color_right,
860 fb,
861 );
862
863 edge1.advance();
864 edge2.advance();
865 }
866}
867
868fn fill_top_flat_gouraud<D: DrawTarget<Color = embedded_graphics_core::pixelcolor::Rgb565>>(
870 p1: Point,
871 p2: Point,
872 p3: Point,
873 c1: embedded_graphics_core::pixelcolor::Rgb565,
874 c2: embedded_graphics_core::pixelcolor::Rgb565,
875 c3: embedded_graphics_core::pixelcolor::Rgb565,
876 fb: &mut D,
877) where
878 <D as DrawTarget>::Error: Debug,
879{
880 let height = (p3.y - p1.y) as f32;
882 if height == 0.0 {
883 return;
884 }
885
886 let mut edge1 = EdgeStepper::new(p1, p3, p1.y);
887 let mut edge2 = EdgeStepper::new(p2, p3, p1.y);
888
889 for scanline_y in p1.y..=p3.y {
890 let t = (scanline_y - p1.y) as f32 / height;
891 let color_left = interpolate_color(c1, c3, t);
892 let color_right = interpolate_color(c2, c3, t);
893
894 draw_horizontal_line_gouraud(
895 Point::new(edge1.current_x(), scanline_y),
896 Point::new(edge2.current_x(), scanline_y),
897 color_left,
898 color_right,
899 fb,
900 );
901
902 edge1.advance();
903 edge2.advance();
904 }
905}
906
907fn draw_horizontal_line_gouraud<D: DrawTarget<Color = embedded_graphics_core::pixelcolor::Rgb565>>(
909 p1: Point,
910 p2: Point,
911 color1: embedded_graphics_core::pixelcolor::Rgb565,
912 color2: embedded_graphics_core::pixelcolor::Rgb565,
913 fb: &mut D,
914) where
915 <D as DrawTarget>::Error: Debug,
916{
917 let start = p1.x.min(p2.x);
918 let end = p1.x.max(p2.x);
919 let width = (end - start) as f32;
920
921 if width == 0.0 {
922 fb.draw_iter([embedded_graphics_core::Pixel(
923 Point::new(start, p1.y),
924 color1,
925 )])
926 .unwrap();
927 return;
928 }
929
930 for x in start..=end {
931 let t = (x - start) as f32 / width;
932 let color = interpolate_color(color1, color2, t);
933 fb.draw_iter([embedded_graphics_core::Pixel(Point::new(x, p1.y), color)])
934 .unwrap();
935 }
936}
937
938#[inline]
941pub fn draw_zbuffered<D: DrawTarget<Color = embedded_graphics_core::pixelcolor::Rgb565>>(
942 primitive: DrawPrimitive,
943 fb: &mut D,
944 zbuffer: &mut [crate::ZDepth],
945 width: usize,
946) where
947 <D as DrawTarget>::Error: Debug,
948{
949 draw_zbuffered_with_effects(primitive, fb, zbuffer, width, None, None);
951}
952
953#[cfg(feature = "aa-heuristic")]
962#[inline]
963pub fn draw_zbuffered_aa<D>(
964 primitive: DrawPrimitive,
965 fb: &mut D,
966 zbuffer: &mut [crate::ZDepth],
967 width: usize,
968) where
969 D: DrawTarget<Color = Rgb565> + ReadPixel,
970 <D as DrawTarget>::Error: Debug,
971{
972 match primitive {
973 DrawPrimitive::ColoredTriangleWithDepth {
974 mut points,
975 mut depths,
976 color,
977 } => {
978 if points[0].y > points[1].y {
979 points.swap(0, 1);
980 depths.swap(0, 1);
981 }
982 if points[0].y > points[2].y {
983 points.swap(0, 2);
984 depths.swap(0, 2);
985 }
986 if points[1].y > points[2].y {
987 points.swap(1, 2);
988 depths.swap(1, 2);
989 }
990 let [p1, p2, p3] = points;
991 let [z1, z2, z3] = depths;
992
993 let scr_w = width as i32;
995 let scr_h = (zbuffer.len() / width) as i32;
996 if p1.x < 0 && p2.x < 0 && p3.x < 0 {
997 return;
998 }
999 if p1.x >= scr_w && p2.x >= scr_w && p3.x >= scr_w {
1000 return;
1001 }
1002 if p1.y < 0 && p2.y < 0 && p3.y < 0 {
1003 return;
1004 }
1005 if p1.y >= scr_h && p2.y >= scr_h && p3.y >= scr_h {
1006 return;
1007 }
1008
1009 fill_triangle_zbuffered_aa(p1, p2, p3, z1, z2, z3, color, fb, zbuffer, width);
1010 }
1011 DrawPrimitive::Line([p1, p2], color) => {
1012 draw_line_aa(p1.x, p1.y, p2.x, p2.y, color, fb);
1013 }
1014 other => draw_zbuffered(other, fb, zbuffer, width),
1017 }
1018}
1019
1020#[cfg(feature = "aa")]
1022#[inline]
1023pub fn draw_zbuffered_2xssaa<D>(
1024 primitive: DrawPrimitive,
1025 fb: &mut D,
1026 zbuffer: &mut [crate::ZDepth],
1027 width: usize,
1028) where
1029 D: DrawTarget<Color = Rgb565> + ReadPixel,
1030 <D as DrawTarget>::Error: Debug,
1031{
1032 match primitive {
1033 DrawPrimitive::ColoredTriangleWithDepth {
1034 mut points,
1035 mut depths,
1036 color,
1037 } => {
1038 if points[0].y > points[1].y {
1039 points.swap(0, 1);
1040 depths.swap(0, 1);
1041 }
1042 if points[0].y > points[2].y {
1043 points.swap(0, 2);
1044 depths.swap(0, 2);
1045 }
1046 if points[1].y > points[2].y {
1047 points.swap(1, 2);
1048 depths.swap(1, 2);
1049 }
1050 let [p1, p2, p3] = points;
1051 let [z1, z2, z3] = depths;
1052
1053 let scr_w = width as i32;
1054 let scr_h = (zbuffer.len() / width) as i32;
1055 if p1.x < 0 && p2.x < 0 && p3.x < 0 {
1056 return;
1057 }
1058 if p1.x >= scr_w && p2.x >= scr_w && p3.x >= scr_w {
1059 return;
1060 }
1061 if p1.y < 0 && p2.y < 0 && p3.y < 0 {
1062 return;
1063 }
1064 if p1.y >= scr_h && p2.y >= scr_h && p3.y >= scr_h {
1065 return;
1066 }
1067
1068 fill_triangle_zbuffered_2xssaa(p1, p2, p3, z1, z2, z3, color, fb, zbuffer, width);
1069 }
1070 DrawPrimitive::Line([p1, p2], color) => {
1071 draw_line_aa(p1.x, p1.y, p2.x, p2.y, color, fb);
1072 }
1073 other => draw_zbuffered(other, fb, zbuffer, width),
1074 }
1075}
1076
1077#[cfg(feature = "aa")]
1078#[inline(always)]
1079fn fill_triangle_zbuffered_2xssaa<D>(
1080 p1: nalgebra::Point2<i32>,
1081 p2: nalgebra::Point2<i32>,
1082 p3: nalgebra::Point2<i32>,
1083 z1: f32,
1084 z2: f32,
1085 z3: f32,
1086 color: Rgb565,
1087 fb: &mut D,
1088 zbuffer: &mut [crate::ZDepth],
1089 width: usize,
1090) where
1091 D: DrawTarget<Color = Rgb565> + ReadPixel,
1092 <D as DrawTarget>::Error: Debug,
1093{
1094 let p1_eg = Point::new(p1.x, p1.y);
1095 let p2_eg = Point::new(p2.x, p2.y);
1096 let p3_eg = Point::new(p3.x, p3.y);
1097
1098 let z1_int = (z1 * 65536.0) as u32;
1099 let z2_int = (z2 * 65536.0) as u32;
1100 let z3_int = (z3 * 65536.0) as u32;
1101
1102 if p2_eg.y == p3_eg.y {
1103 fill_bottom_flat_2xssaa(
1104 p1_eg, p2_eg, p3_eg, z1_int, z2_int, z3_int, color, fb, zbuffer, width,
1105 );
1106 } else if p1_eg.y == p2_eg.y {
1107 fill_top_flat_2xssaa(
1108 p1_eg, p2_eg, p3_eg, z1_int, z2_int, z3_int, color, fb, zbuffer, width,
1109 );
1110 } else {
1111 let t = (p2_eg.y - p1_eg.y) as f32 / (p3_eg.y - p1_eg.y) as f32;
1112 let p4 = Point::new(
1113 (p1_eg.x as f32 + t * (p3_eg.x - p1_eg.x) as f32) as i32,
1114 p2_eg.y,
1115 );
1116 let z4_int = (z1_int as i64 + (t * (z3_int as i64 - z1_int as i64) as f32) as i64) as u32;
1117 fill_bottom_flat_2xssaa(
1118 p1_eg, p2_eg, p4, z1_int, z2_int, z4_int, color, fb, zbuffer, width,
1119 );
1120 fill_top_flat_2xssaa(
1121 p2_eg, p4, p3_eg, z2_int, z4_int, z3_int, color, fb, zbuffer, width,
1122 );
1123 }
1124}
1125
1126#[cfg(feature = "aa")]
1127#[inline(always)]
1128fn fill_bottom_flat_2xssaa<D>(
1129 p1: Point,
1130 p2: Point,
1131 p3: Point,
1132 z1: u32,
1133 z2: u32,
1134 z3: u32,
1135 color: Rgb565,
1136 fb: &mut D,
1137 zbuffer: &mut [crate::ZDepth],
1138 width: usize,
1139) where
1140 D: DrawTarget<Color = Rgb565> + ReadPixel,
1141 <D as DrawTarget>::Error: Debug,
1142{
1143 let height = p2.y - p1.y;
1144 if height == 0 {
1145 return;
1146 }
1147 let invslope1 = ((p2.x - p1.x) << 16) / height;
1148 let invslope2 = ((p3.x - p1.x) << 16) / height;
1149
1150 let mut curx1 = p1.x << 16;
1151 let mut curx2 = p1.x << 16;
1152
1153 for scanline_y in p1.y..=p2.y {
1154 let dy = scanline_y - p1.y;
1155 let z_left = (z1 as i64 + ((z2 as i64 - z1 as i64) * dy as i64 / height as i64)) as u32;
1156 let z_right = (z1 as i64 + ((z3 as i64 - z1 as i64) * dy as i64 / height as i64)) as u32;
1157
1158 ssaa2x_scanline(
1159 curx1, curx2, scanline_y, z_left, z_right, color, fb, zbuffer, width,
1160 );
1161
1162 curx1 += invslope1;
1163 curx2 += invslope2;
1164 }
1165}
1166
1167#[cfg(feature = "aa")]
1168#[inline(always)]
1169fn fill_top_flat_2xssaa<D>(
1170 p1: Point,
1171 p2: Point,
1172 p3: Point,
1173 z1: u32,
1174 z2: u32,
1175 z3: u32,
1176 color: Rgb565,
1177 fb: &mut D,
1178 zbuffer: &mut [crate::ZDepth],
1179 width: usize,
1180) where
1181 D: DrawTarget<Color = Rgb565> + ReadPixel,
1182 <D as DrawTarget>::Error: Debug,
1183{
1184 let height = p3.y - p1.y;
1185 if height == 0 {
1186 return;
1187 }
1188 let invslope1 = ((p3.x - p1.x) << 16) / height;
1189 let invslope2 = ((p3.x - p2.x) << 16) / height;
1190
1191 let mut curx1 = p3.x << 16;
1192 let mut curx2 = p3.x << 16;
1193
1194 for scanline_y in (p1.y..=p3.y).rev() {
1195 let dy = scanline_y - p1.y;
1196 let z_left = (z1 as i64 + ((z3 as i64 - z1 as i64) * dy as i64 / height as i64)) as u32;
1197 let z_right = (z2 as i64 + ((z3 as i64 - z2 as i64) * dy as i64 / height as i64)) as u32;
1198
1199 ssaa2x_scanline(
1200 curx1, curx2, scanline_y, z_left, z_right, color, fb, zbuffer, width,
1201 );
1202
1203 curx1 -= invslope1;
1204 curx2 -= invslope2;
1205 }
1206}
1207
1208#[cfg(feature = "aa")]
1209#[inline(always)]
1210fn ssaa2x_scanline<D>(
1211 cx1: i32,
1212 cx2: i32,
1213 y: i32,
1214 z_left: u32,
1215 z_right: u32,
1216 color: Rgb565,
1217 fb: &mut D,
1218 zbuffer: &mut [crate::ZDepth],
1219 width: usize,
1220) where
1221 D: DrawTarget<Color = Rgb565> + ReadPixel,
1222 <D as DrawTarget>::Error: Debug,
1223{
1224 let (left_fx, right_fx, z_l, z_r) = if cx1 <= cx2 {
1225 (cx1, cx2, z_left, z_right)
1226 } else {
1227 (cx2, cx1, z_right, z_left)
1228 };
1229
1230 let l_int = left_fx >> 16;
1231 let r_int = right_fx >> 16;
1232 let span = r_int - l_int;
1233
1234 let eval_subsamples = |x: i32| -> u32 {
1235 let fx1 = (x << 16) | 16384;
1236 let fx2 = (x << 16) | 49152;
1237 let s1 = (fx1 >= left_fx && fx1 <= right_fx) as u32;
1238 let s2 = (fx2 >= left_fx && fx2 <= right_fx) as u32;
1239 s1 + s2
1240 };
1241
1242 if l_int == r_int {
1243 let samples = eval_subsamples(l_int);
1244 if samples > 0 {
1245 let cov_q8 = samples * 128;
1246 aa_pixel(fb, l_int, y, color, z_l, zbuffer, width, cov_q8.min(256));
1247 }
1248 return;
1249 }
1250
1251 let left_samples = eval_subsamples(l_int);
1252 if left_samples > 0 {
1253 let cov_q8 = left_samples * 128;
1254 aa_pixel(fb, l_int, y, color, z_l, zbuffer, width, cov_q8.min(256));
1255 }
1256
1257 if span > 1 {
1258 for x in (l_int + 1)..r_int {
1259 let t = (x - l_int) as f32 / span as f32;
1260 let z = (z_l as f32 + t * (z_r as f32 - z_l as f32)) as u32;
1261 aa_pixel(fb, x, y, color, z, zbuffer, width, 256);
1262 }
1263 }
1264
1265 let right_samples = eval_subsamples(r_int);
1266 if right_samples > 0 {
1267 let cov_q8 = right_samples * 128;
1268 aa_pixel(fb, r_int, y, color, z_r, zbuffer, width, cov_q8.min(256));
1269 }
1270}
1271
1272#[cfg(feature = "aa-heuristic")]
1273#[inline(always)]
1274fn fill_triangle_zbuffered_aa<D>(
1275 p1: nalgebra::Point2<i32>,
1276 p2: nalgebra::Point2<i32>,
1277 p3: nalgebra::Point2<i32>,
1278 z1: f32,
1279 z2: f32,
1280 z3: f32,
1281 color: Rgb565,
1282 fb: &mut D,
1283 zbuffer: &mut [crate::ZDepth],
1284 width: usize,
1285) where
1286 D: DrawTarget<Color = Rgb565> + ReadPixel,
1287 <D as DrawTarget>::Error: Debug,
1288{
1289 let p1_eg = Point::new(p1.x, p1.y);
1290 let p2_eg = Point::new(p2.x, p2.y);
1291 let p3_eg = Point::new(p3.x, p3.y);
1292
1293 let z1_int = (z1 * 65536.0) as u32;
1294 let z2_int = (z2 * 65536.0) as u32;
1295 let z3_int = (z3 * 65536.0) as u32;
1296
1297 if p2_eg.y == p3_eg.y {
1298 fill_bottom_flat_aa(
1299 p1_eg, p2_eg, p3_eg, z1_int, z2_int, z3_int, color, fb, zbuffer, width,
1300 );
1301 } else if p1_eg.y == p2_eg.y {
1302 fill_top_flat_aa(
1303 p1_eg, p2_eg, p3_eg, z1_int, z2_int, z3_int, color, fb, zbuffer, width,
1304 );
1305 } else {
1306 let t = (p2_eg.y - p1_eg.y) as f32 / (p3_eg.y - p1_eg.y) as f32;
1307 let p4 = Point::new(
1308 (p1_eg.x as f32 + t * (p3_eg.x - p1_eg.x) as f32) as i32,
1309 p2_eg.y,
1310 );
1311 let z4_int = (z1_int as i64 + (t * (z3_int as i64 - z1_int as i64) as f32) as i64) as u32;
1312 fill_bottom_flat_aa(
1313 p1_eg, p2_eg, p4, z1_int, z2_int, z4_int, color, fb, zbuffer, width,
1314 );
1315 fill_top_flat_aa(
1316 p2_eg, p4, p3_eg, z2_int, z4_int, z3_int, color, fb, zbuffer, width,
1317 );
1318 }
1319}
1320
1321#[cfg(feature = "aa-heuristic")]
1322#[inline(always)]
1323fn fill_bottom_flat_aa<D>(
1324 p1: Point,
1325 p2: Point,
1326 p3: Point,
1327 z1: u32,
1328 z2: u32,
1329 z3: u32,
1330 color: Rgb565,
1331 fb: &mut D,
1332 zbuffer: &mut [crate::ZDepth],
1333 width: usize,
1334) where
1335 D: DrawTarget<Color = Rgb565> + ReadPixel,
1336 <D as DrawTarget>::Error: Debug,
1337{
1338 let height = p2.y - p1.y;
1339 if height == 0 {
1340 return;
1341 }
1342 let invslope1 = ((p2.x - p1.x) << 16) / height;
1343 let invslope2 = ((p3.x - p1.x) << 16) / height;
1344
1345 let mut curx1 = p1.x << 16;
1346 let mut curx2 = p1.x << 16;
1347
1348 for scanline_y in p1.y..=p2.y {
1349 let dy = scanline_y - p1.y;
1350 let z_left = (z1 as i64 + ((z2 as i64 - z1 as i64) * dy as i64 / height as i64)) as u32;
1351 let z_right = (z1 as i64 + ((z3 as i64 - z1 as i64) * dy as i64 / height as i64)) as u32;
1352
1353 aa_scanline(
1354 curx1, curx2, scanline_y, z_left, z_right, color, fb, zbuffer, width,
1355 );
1356
1357 curx1 += invslope1;
1358 curx2 += invslope2;
1359 }
1360}
1361
1362#[cfg(feature = "aa-heuristic")]
1363#[inline(always)]
1364fn fill_top_flat_aa<D>(
1365 p1: Point,
1366 p2: Point,
1367 p3: Point,
1368 z1: u32,
1369 z2: u32,
1370 z3: u32,
1371 color: Rgb565,
1372 fb: &mut D,
1373 zbuffer: &mut [crate::ZDepth],
1374 width: usize,
1375) where
1376 D: DrawTarget<Color = Rgb565> + ReadPixel,
1377 <D as DrawTarget>::Error: Debug,
1378{
1379 let height = p3.y - p1.y;
1380 if height == 0 {
1381 return;
1382 }
1383 let invslope1 = ((p3.x - p1.x) << 16) / height;
1384 let invslope2 = ((p3.x - p2.x) << 16) / height;
1385
1386 let mut curx1 = p3.x << 16;
1387 let mut curx2 = p3.x << 16;
1388
1389 for scanline_y in (p1.y..=p3.y).rev() {
1390 let dy = scanline_y - p1.y;
1391 let z_left = (z1 as i64 + ((z3 as i64 - z1 as i64) * dy as i64 / height as i64)) as u32;
1392 let z_right = (z2 as i64 + ((z3 as i64 - z2 as i64) * dy as i64 / height as i64)) as u32;
1393
1394 aa_scanline(
1395 curx1, curx2, scanline_y, z_left, z_right, color, fb, zbuffer, width,
1396 );
1397
1398 curx1 -= invslope1;
1399 curx2 -= invslope2;
1400 }
1401}
1402
1403#[cfg(feature = "aa-heuristic")]
1409#[inline(always)]
1410fn aa_scanline<D>(
1411 cx1: i32,
1412 cx2: i32,
1413 y: i32,
1414 z_left: u32,
1415 z_right: u32,
1416 color: Rgb565,
1417 fb: &mut D,
1418 zbuffer: &mut [crate::ZDepth],
1419 width: usize,
1420) where
1421 D: DrawTarget<Color = Rgb565> + ReadPixel,
1422 <D as DrawTarget>::Error: Debug,
1423{
1424 let (left_fx, right_fx, z_l, z_r) = if cx1 <= cx2 {
1426 (cx1, cx2, z_left, z_right)
1427 } else {
1428 (cx2, cx1, z_right, z_left)
1429 };
1430
1431 let l_int = left_fx >> 16;
1432 let r_int = right_fx >> 16;
1433 let l_frac_q16 = (left_fx & 0xFFFF) as u32;
1434 let r_frac_q16 = (right_fx & 0xFFFF) as u32;
1435
1436 let span = r_int - l_int;
1438
1439 if l_int == r_int {
1440 let cov_q16 = r_frac_q16.saturating_sub(l_frac_q16);
1443 aa_pixel(fb, l_int, y, color, z_l, zbuffer, width, cov_q16 >> 8);
1444 return;
1445 }
1446
1447 let left_cov_q8 = 256 - (l_frac_q16 >> 8);
1449 aa_pixel(fb, l_int, y, color, z_l, zbuffer, width, left_cov_q8);
1450
1451 if span > 1 {
1454 for x in (l_int + 1)..r_int {
1455 if x < 0 {
1456 continue;
1457 }
1458 let idx = y as usize * width + x as usize;
1459 if idx >= zbuffer.len() {
1460 continue;
1461 }
1462 let t_num = (x - l_int) as i64;
1464 let t_den = span as i64;
1465 let z = (z_l as i64 + ((z_r as i64 - z_l as i64) * t_num / t_den)) as u32;
1466 let z_depth = crate::to_zdepth(z);
1467 if z_depth < zbuffer[idx].saturating_add(crate::DEPTH_EPSILON) {
1468 zbuffer[idx] = z_depth;
1469 fb.draw_iter([embedded_graphics_core::Pixel(Point::new(x, y), color)])
1470 .unwrap();
1471 }
1472 }
1473 }
1474
1475 if r_frac_q16 > 0 {
1477 let right_cov_q8 = r_frac_q16 >> 8;
1478 aa_pixel(fb, r_int, y, color, z_r, zbuffer, width, right_cov_q8);
1479 }
1480}
1481
1482#[cfg(feature = "aa-coverage")]
1499#[inline]
1500pub fn draw_zbuffered_aa_coverage<D>(
1501 primitive: DrawPrimitive,
1502 fb: &mut D,
1503 zbuffer: &mut [crate::ZDepth],
1504 coverage: &mut [u8],
1505 width: usize,
1506) where
1507 D: DrawTarget<Color = Rgb565> + ReadPixel,
1508 <D as DrawTarget>::Error: Debug,
1509{
1510 match primitive {
1511 DrawPrimitive::ColoredTriangleWithDepth {
1512 mut points,
1513 mut depths,
1514 color,
1515 } => {
1516 if points[0].y > points[1].y {
1517 points.swap(0, 1);
1518 depths.swap(0, 1);
1519 }
1520 if points[0].y > points[2].y {
1521 points.swap(0, 2);
1522 depths.swap(0, 2);
1523 }
1524 if points[1].y > points[2].y {
1525 points.swap(1, 2);
1526 depths.swap(1, 2);
1527 }
1528 let [p1, p2, p3] = points;
1529 let [z1, z2, z3] = depths;
1530 fill_triangle_zbuffered_aa_cov(
1531 p1, p2, p3, z1, z2, z3, color, fb, zbuffer, coverage, width,
1532 );
1533 }
1534 DrawPrimitive::Line([p1, p2], color) => {
1535 draw_line_aa_coverage(p1.x, p1.y, p2.x, p2.y, color, fb, coverage, width);
1538 }
1539 other => draw_zbuffered(other, fb, zbuffer, width),
1540 }
1541}
1542
1543#[cfg(feature = "aa-coverage")]
1547pub fn draw_line_aa_coverage<D>(
1548 x0: i32,
1549 y0: i32,
1550 x1: i32,
1551 y1: i32,
1552 color: Rgb565,
1553 fb: &mut D,
1554 coverage: &mut [u8],
1555 width: usize,
1556) where
1557 D: DrawTarget<Color = Rgb565> + ReadPixel,
1558 <D as DrawTarget>::Error: Debug,
1559{
1560 let dx = (x1 - x0).abs();
1561 let dy = (y1 - y0).abs();
1562 let steep = dy > dx;
1563 let (x0, y0, x1, y1) = if steep {
1564 (y0, x0, y1, x1)
1565 } else {
1566 (x0, y0, x1, y1)
1567 };
1568 let (x0, y0, x1, y1) = if x0 > x1 {
1569 (x1, y1, x0, y0)
1570 } else {
1571 (x0, y0, x1, y1)
1572 };
1573 let dx = x1 - x0;
1574 let dy = y1 - y0;
1575 if dx == 0 {
1576 let (px, py) = if steep { (y0, x0) } else { (x0, y0) };
1577 plot_aa_cov(fb, px, py, color, coverage, width, 256);
1578 return;
1579 }
1580 let gradient: i32 = ((dy as i64) << 16) as i32 / dx;
1581 let mut intery: i32 = y0 << 16;
1582 for x in x0..=x1 {
1583 let y_int = intery >> 16;
1584 let frac_q16 = (intery & 0xFFFF) as u32;
1585 let cov_top = 256 - (frac_q16 >> 8);
1586 let cov_bot = frac_q16 >> 8;
1587 if steep {
1588 plot_aa_cov(fb, y_int, x, color, coverage, width, cov_top);
1589 plot_aa_cov(fb, y_int + 1, x, color, coverage, width, cov_bot);
1590 } else {
1591 plot_aa_cov(fb, x, y_int, color, coverage, width, cov_top);
1592 plot_aa_cov(fb, x, y_int + 1, color, coverage, width, cov_bot);
1593 }
1594 intery += gradient;
1595 }
1596}
1597
1598#[cfg(feature = "aa-coverage")]
1601#[inline(always)]
1602fn plot_aa_cov<D>(
1603 fb: &mut D,
1604 x: i32,
1605 y: i32,
1606 color: Rgb565,
1607 coverage: &mut [u8],
1608 width: usize,
1609 coverage_q8: u32,
1610) where
1611 D: DrawTarget<Color = Rgb565> + ReadPixel,
1612 <D as DrawTarget>::Error: Debug,
1613{
1614 if x < 0 || y < 0 || x >= width as i32 || coverage_q8 == 0 {
1615 return;
1616 }
1617 let idx = y as usize * width + x as usize;
1618 if idx >= coverage.len() {
1619 return;
1620 }
1621 let p = Point::new(x, y);
1622
1623 if coverage_q8 >= 256 {
1624 coverage[idx] = 255;
1625 fb.draw_iter([embedded_graphics_core::Pixel(p, color)])
1626 .unwrap();
1627 return;
1628 }
1629
1630 let prev_cov = coverage[idx] as u32;
1631
1632 if prev_cov == 0 {
1633 let claim_255 = (coverage_q8 * 255) >> 8;
1634 coverage[idx] = claim_255 as u8;
1635 fb.draw_iter([embedded_graphics_core::Pixel(p, color)])
1636 .unwrap();
1637 return;
1638 }
1639
1640 if prev_cov >= 255 {
1641 let existing = fb.read_pixel(p);
1642 let result = blend_q8(existing, color, coverage_q8);
1643 fb.draw_iter([embedded_graphics_core::Pixel(p, result)])
1644 .unwrap();
1645 return;
1646 }
1647
1648 let remaining = 255 - prev_cov;
1649 let claim_255 = ((coverage_q8 * 255) >> 8).min(remaining);
1650 if claim_255 == 0 {
1651 return;
1652 }
1653 let new_total = prev_cov + claim_255;
1654 let existing = fb.read_pixel(p);
1655 let blend_factor = (claim_255 * 256) / new_total;
1656 let result = blend_q8(existing, color, blend_factor);
1657 coverage[idx] = new_total as u8;
1658 fb.draw_iter([embedded_graphics_core::Pixel(p, result)])
1659 .unwrap();
1660}
1661
1662#[cfg(feature = "aa-coverage")]
1666pub fn composite_aa_background<D>(
1667 fb: &mut D,
1668 coverage: &[u8],
1669 bg: Rgb565,
1670 width: usize,
1671 height: usize,
1672) where
1673 D: DrawTarget<Color = Rgb565> + ReadPixel,
1674 <D as DrawTarget>::Error: Debug,
1675{
1676 for y in 0..height {
1677 for x in 0..width {
1678 let idx = y * width + x;
1679 let cov = coverage[idx];
1680 if cov == 255 {
1681 continue; }
1683 let p = Point::new(x as i32, y as i32);
1684 let final_color = if cov == 0 {
1685 bg
1686 } else {
1687 let tri_color = fb.read_pixel(p);
1691 let cov_q8 = ((cov as u32) * 256) / 255;
1693 blend_q8(bg, tri_color, cov_q8)
1694 };
1695 fb.draw_iter([embedded_graphics_core::Pixel(p, final_color)])
1696 .unwrap();
1697 }
1698 }
1699}
1700
1701#[cfg(feature = "aa-coverage")]
1716#[inline(always)]
1717fn aa_pixel_cov<D>(
1718 fb: &mut D,
1719 x: i32,
1720 y: i32,
1721 color: Rgb565,
1722 z: u32,
1723 zbuffer: &mut [crate::ZDepth],
1724 coverage: &mut [u8],
1725 width: usize,
1726 coverage_q8: u32,
1727) where
1728 D: DrawTarget<Color = Rgb565> + ReadPixel,
1729 <D as DrawTarget>::Error: Debug,
1730{
1731 if x < 0 || y < 0 || x >= width as i32 || coverage_q8 == 0 {
1732 return;
1733 }
1734 let idx = y as usize * width + x as usize;
1735 if idx >= zbuffer.len() {
1736 return;
1737 }
1738 let z_depth = crate::to_zdepth(z);
1739 if z_depth >= zbuffer[idx].saturating_add(crate::DEPTH_EPSILON) {
1740 return;
1741 }
1742
1743 let p = Point::new(x, y);
1744
1745 if coverage_q8 >= 256 {
1747 coverage[idx] = 255;
1748 zbuffer[idx] = z_depth;
1749 fb.draw_iter([embedded_graphics_core::Pixel(p, color)])
1750 .unwrap();
1751 return;
1752 }
1753
1754 let prev_cov = coverage[idx] as u32;
1755
1756 if prev_cov == 0 {
1757 let claim_255 = (coverage_q8 * 255) >> 8;
1760 coverage[idx] = claim_255 as u8;
1761 zbuffer[idx] = z_depth;
1762 fb.draw_iter([embedded_graphics_core::Pixel(p, color)])
1763 .unwrap();
1764 return;
1765 }
1766
1767 if prev_cov >= 255 {
1768 let existing = fb.read_pixel(p);
1773 let result = blend_q8(existing, color, coverage_q8);
1774 zbuffer[idx] = z_depth;
1775 fb.draw_iter([embedded_graphics_core::Pixel(p, result)])
1776 .unwrap();
1777 return;
1778 }
1779
1780 let remaining = 255 - prev_cov;
1783 let claim_255 = ((coverage_q8 * 255) >> 8).min(remaining);
1784 if claim_255 == 0 {
1785 return;
1786 }
1787 let new_total = prev_cov + claim_255;
1788 let existing = fb.read_pixel(p);
1789 let blend_factor = (claim_255 * 256) / new_total;
1790 let result = blend_q8(existing, color, blend_factor);
1791 coverage[idx] = new_total as u8;
1792 zbuffer[idx] = z_depth;
1793 fb.draw_iter([embedded_graphics_core::Pixel(p, result)])
1794 .unwrap();
1795}
1796
1797#[cfg(feature = "aa-coverage")]
1798#[inline(always)]
1799fn fill_triangle_zbuffered_aa_cov<D>(
1800 p1: nalgebra::Point2<i32>,
1801 p2: nalgebra::Point2<i32>,
1802 p3: nalgebra::Point2<i32>,
1803 z1: f32,
1804 z2: f32,
1805 z3: f32,
1806 color: Rgb565,
1807 fb: &mut D,
1808 zbuffer: &mut [crate::ZDepth],
1809 coverage: &mut [u8],
1810 width: usize,
1811) where
1812 D: DrawTarget<Color = Rgb565> + ReadPixel,
1813 <D as DrawTarget>::Error: Debug,
1814{
1815 let p1_eg = Point::new(p1.x, p1.y);
1816 let p2_eg = Point::new(p2.x, p2.y);
1817 let p3_eg = Point::new(p3.x, p3.y);
1818
1819 let z1_int = (z1 * 65536.0) as u32;
1820 let z2_int = (z2 * 65536.0) as u32;
1821 let z3_int = (z3 * 65536.0) as u32;
1822
1823 if p2_eg.y == p3_eg.y {
1824 fill_bottom_flat_aa_cov(
1825 p1_eg, p2_eg, p3_eg, z1_int, z2_int, z3_int, color, fb, zbuffer, coverage, width,
1826 );
1827 } else if p1_eg.y == p2_eg.y {
1828 fill_top_flat_aa_cov(
1829 p1_eg, p2_eg, p3_eg, z1_int, z2_int, z3_int, color, fb, zbuffer, coverage, width,
1830 );
1831 } else {
1832 let t = (p2_eg.y - p1_eg.y) as f32 / (p3_eg.y - p1_eg.y) as f32;
1833 let p4 = Point::new(
1834 (p1_eg.x as f32 + t * (p3_eg.x - p1_eg.x) as f32) as i32,
1835 p2_eg.y,
1836 );
1837 let z4_int = (z1_int as i64 + (t * (z3_int as i64 - z1_int as i64) as f32) as i64) as u32;
1838 fill_bottom_flat_aa_cov(
1839 p1_eg, p2_eg, p4, z1_int, z2_int, z4_int, color, fb, zbuffer, coverage, width,
1840 );
1841 fill_top_flat_aa_cov(
1842 p2_eg, p4, p3_eg, z2_int, z4_int, z3_int, color, fb, zbuffer, coverage, width,
1843 );
1844 }
1845}
1846
1847#[cfg(feature = "aa-coverage")]
1848#[inline(always)]
1849fn fill_bottom_flat_aa_cov<D>(
1850 p1: Point,
1851 p2: Point,
1852 p3: Point,
1853 z1: u32,
1854 z2: u32,
1855 z3: u32,
1856 color: Rgb565,
1857 fb: &mut D,
1858 zbuffer: &mut [crate::ZDepth],
1859 coverage: &mut [u8],
1860 width: usize,
1861) where
1862 D: DrawTarget<Color = Rgb565> + ReadPixel,
1863 <D as DrawTarget>::Error: Debug,
1864{
1865 let height = p2.y - p1.y;
1866 if height == 0 {
1867 return;
1868 }
1869 let invslope1 = ((p2.x - p1.x) << 16) / height;
1870 let invslope2 = ((p3.x - p1.x) << 16) / height;
1871
1872 let mut curx1 = p1.x << 16;
1873 let mut curx2 = p1.x << 16;
1874
1875 for scanline_y in p1.y..=p2.y {
1876 let dy = scanline_y - p1.y;
1877 let z_left = (z1 as i64 + ((z2 as i64 - z1 as i64) * dy as i64 / height as i64)) as u32;
1878 let z_right = (z1 as i64 + ((z3 as i64 - z1 as i64) * dy as i64 / height as i64)) as u32;
1879
1880 aa_scanline_cov(
1881 curx1, curx2, scanline_y, z_left, z_right, color, fb, zbuffer, coverage, width,
1882 );
1883
1884 curx1 += invslope1;
1885 curx2 += invslope2;
1886 }
1887}
1888
1889#[cfg(feature = "aa-coverage")]
1890#[inline(always)]
1891fn fill_top_flat_aa_cov<D>(
1892 p1: Point,
1893 p2: Point,
1894 p3: Point,
1895 z1: u32,
1896 z2: u32,
1897 z3: u32,
1898 color: Rgb565,
1899 fb: &mut D,
1900 zbuffer: &mut [crate::ZDepth],
1901 coverage: &mut [u8],
1902 width: usize,
1903) where
1904 D: DrawTarget<Color = Rgb565> + ReadPixel,
1905 <D as DrawTarget>::Error: Debug,
1906{
1907 let height = p3.y - p1.y;
1908 if height == 0 {
1909 return;
1910 }
1911 let invslope1 = ((p3.x - p1.x) << 16) / height;
1912 let invslope2 = ((p3.x - p2.x) << 16) / height;
1913
1914 let mut curx1 = p3.x << 16;
1915 let mut curx2 = p3.x << 16;
1916
1917 for scanline_y in (p1.y..=p3.y).rev() {
1918 let dy = scanline_y - p1.y;
1919 let z_left = (z1 as i64 + ((z3 as i64 - z1 as i64) * dy as i64 / height as i64)) as u32;
1920 let z_right = (z2 as i64 + ((z3 as i64 - z2 as i64) * dy as i64 / height as i64)) as u32;
1921
1922 aa_scanline_cov(
1923 curx1, curx2, scanline_y, z_left, z_right, color, fb, zbuffer, coverage, width,
1924 );
1925
1926 curx1 -= invslope1;
1927 curx2 -= invslope2;
1928 }
1929}
1930
1931#[cfg(feature = "aa-coverage")]
1932#[inline(always)]
1933fn aa_scanline_cov<D>(
1934 cx1: i32,
1935 cx2: i32,
1936 y: i32,
1937 z_left: u32,
1938 z_right: u32,
1939 color: Rgb565,
1940 fb: &mut D,
1941 zbuffer: &mut [crate::ZDepth],
1942 coverage: &mut [u8],
1943 width: usize,
1944) where
1945 D: DrawTarget<Color = Rgb565> + ReadPixel,
1946 <D as DrawTarget>::Error: Debug,
1947{
1948 let (left_fx, right_fx, z_l, z_r) = if cx1 <= cx2 {
1949 (cx1, cx2, z_left, z_right)
1950 } else {
1951 (cx2, cx1, z_right, z_left)
1952 };
1953
1954 let l_int = left_fx >> 16;
1955 let r_int = right_fx >> 16;
1956 let l_frac_q16 = (left_fx & 0xFFFF) as u32;
1957 let r_frac_q16 = (right_fx & 0xFFFF) as u32;
1958 let span = r_int - l_int;
1959
1960 if l_int == r_int {
1961 let cov_q16 = r_frac_q16.saturating_sub(l_frac_q16);
1962 aa_pixel_cov(
1963 fb,
1964 l_int,
1965 y,
1966 color,
1967 z_l,
1968 zbuffer,
1969 coverage,
1970 width,
1971 cov_q16 >> 8,
1972 );
1973 return;
1974 }
1975
1976 let left_cov_q8 = 256 - (l_frac_q16 >> 8);
1977 aa_pixel_cov(
1978 fb,
1979 l_int,
1980 y,
1981 color,
1982 z_l,
1983 zbuffer,
1984 coverage,
1985 width,
1986 left_cov_q8,
1987 );
1988
1989 if span > 1 {
1990 for x in (l_int + 1)..r_int {
1991 let t_num = (x - l_int) as i64;
1994 let t_den = span as i64;
1995 let z = (z_l as i64 + ((z_r as i64 - z_l as i64) * t_num / t_den)) as u32;
1996 aa_pixel_cov(fb, x, y, color, z, zbuffer, coverage, width, 256);
1997 }
1998 }
1999
2000 if r_frac_q16 > 0 {
2001 let right_cov_q8 = r_frac_q16 >> 8;
2002 aa_pixel_cov(
2003 fb,
2004 r_int,
2005 y,
2006 color,
2007 z_r,
2008 zbuffer,
2009 coverage,
2010 width,
2011 right_cov_q8,
2012 );
2013 }
2014}
2015
2016#[cfg(feature = "aa")]
2021pub fn draw_line_aa<D>(x0: i32, y0: i32, x1: i32, y1: i32, color: Rgb565, fb: &mut D)
2022where
2023 D: DrawTarget<Color = Rgb565> + ReadPixel,
2024 <D as DrawTarget>::Error: Debug,
2025{
2026 let dx = (x1 - x0).abs();
2027 let dy = (y1 - y0).abs();
2028 let steep = dy > dx;
2029 let (x0, y0, x1, y1) = if steep {
2030 (y0, x0, y1, x1)
2031 } else {
2032 (x0, y0, x1, y1)
2033 };
2034 let (x0, y0, x1, y1) = if x0 > x1 {
2035 (x1, y1, x0, y0)
2036 } else {
2037 (x0, y0, x1, y1)
2038 };
2039 let dx = x1 - x0;
2040 let dy = y1 - y0;
2041 if dx == 0 {
2042 let (px, py) = if steep { (y0, x0) } else { (x0, y0) };
2044 plot_aa(fb, px, py, color, 256);
2045 return;
2046 }
2047 let gradient: i32 = ((dy as i64) << 16) as i32 / dx;
2049 let mut intery: i32 = y0 << 16;
2051 for x in x0..=x1 {
2052 let y_int = intery >> 16;
2053 let frac_q16 = (intery & 0xFFFF) as u32;
2054 let cov_top = 256 - (frac_q16 >> 8); let cov_bot = frac_q16 >> 8; if steep {
2057 plot_aa(fb, y_int, x, color, cov_top);
2058 plot_aa(fb, y_int + 1, x, color, cov_bot);
2059 } else {
2060 plot_aa(fb, x, y_int, color, cov_top);
2061 plot_aa(fb, x, y_int + 1, color, cov_bot);
2062 }
2063 intery += gradient;
2064 }
2065}
2066
2067#[cfg(feature = "aa")]
2068#[inline(always)]
2069fn plot_aa<D>(fb: &mut D, x: i32, y: i32, color: Rgb565, coverage_q8: u32)
2070where
2071 D: DrawTarget<Color = Rgb565> + ReadPixel,
2072 <D as DrawTarget>::Error: Debug,
2073{
2074 if coverage_q8 == 0 {
2075 return;
2076 }
2077 let final_color = if coverage_q8 >= 256 {
2078 color
2079 } else {
2080 let bg = fb.read_pixel(Point::new(x, y));
2081 blend_q8(bg, color, coverage_q8)
2082 };
2083 fb.draw_iter([embedded_graphics_core::Pixel(Point::new(x, y), final_color)])
2084 .unwrap();
2085}
2086
2087#[inline]
2090pub fn draw_zbuffered_with_effects<
2091 D: DrawTarget<Color = embedded_graphics_core::pixelcolor::Rgb565>,
2092>(
2093 primitive: DrawPrimitive,
2094 fb: &mut D,
2095 zbuffer: &mut [crate::ZDepth],
2096 width: usize,
2097 fog_config: Option<&FogConfig>,
2098 dither_config: Option<&DitherConfig>,
2099) where
2100 <D as DrawTarget>::Error: Debug,
2101{
2102 match primitive {
2103 DrawPrimitive::ColoredTriangleWithDepth {
2104 mut points,
2105 mut depths,
2106 color,
2107 } => {
2108 if points[0].y > points[1].y {
2110 points.swap(0, 1);
2111 depths.swap(0, 1);
2112 }
2113 if points[0].y > points[2].y {
2114 points.swap(0, 2);
2115 depths.swap(0, 2);
2116 }
2117 if points[1].y > points[2].y {
2118 points.swap(1, 2);
2119 depths.swap(1, 2);
2120 }
2121
2122 let [p1, p2, p3] = points;
2123 let [z1, z2, z3] = depths;
2124
2125 let scr_w = width as i32;
2127 let scr_h = (zbuffer.len() / width) as i32;
2128 if p1.x < 0 && p2.x < 0 && p3.x < 0 {
2129 return;
2130 }
2131 if p1.x >= scr_w && p2.x >= scr_w && p3.x >= scr_w {
2132 return;
2133 }
2134 if p1.y < 0 && p2.y < 0 && p3.y < 0 {
2135 return;
2136 }
2137 if p1.y >= scr_h && p2.y >= scr_h && p3.y >= scr_h {
2138 return;
2139 }
2140
2141 fill_triangle_zbuffered(
2142 p1,
2143 p2,
2144 p3,
2145 z1,
2146 z2,
2147 z3,
2148 color,
2149 fb,
2150 zbuffer,
2151 width,
2152 fog_config,
2153 dither_config,
2154 );
2155 }
2156 DrawPrimitive::TranslucentTriangleWithDepth {
2157 mut points,
2158 mut depths,
2159 color,
2160 alpha,
2161 } => {
2162 if points[0].y > points[1].y {
2163 points.swap(0, 1);
2164 depths.swap(0, 1);
2165 }
2166 if points[0].y > points[2].y {
2167 points.swap(0, 2);
2168 depths.swap(0, 2);
2169 }
2170 if points[1].y > points[2].y {
2171 points.swap(1, 2);
2172 depths.swap(1, 2);
2173 }
2174
2175 let [p1, p2, p3] = points;
2176 let [z1, z2, z3] = depths;
2177
2178 let scr_w = width as i32;
2179 let scr_h = (zbuffer.len() / width) as i32;
2180 if p1.x < 0 && p2.x < 0 && p3.x < 0 {
2181 return;
2182 }
2183 if p1.x >= scr_w && p2.x >= scr_w && p3.x >= scr_w {
2184 return;
2185 }
2186 if p1.y < 0 && p2.y < 0 && p3.y < 0 {
2187 return;
2188 }
2189 if p1.y >= scr_h && p2.y >= scr_h && p3.y >= scr_h {
2190 return;
2191 }
2192
2193 fill_triangle_zbuffered_translucent(
2194 p1, p2, p3, z1, z2, z3, color, alpha, fb, zbuffer, width,
2195 );
2196 }
2197 DrawPrimitive::GouraudTriangleWithDepth {
2198 mut points,
2199 mut depths,
2200 mut colors,
2201 } => {
2202 if points[0].y > points[1].y {
2204 points.swap(0, 1);
2205 depths.swap(0, 1);
2206 colors.swap(0, 1);
2207 }
2208 if points[0].y > points[2].y {
2209 points.swap(0, 2);
2210 depths.swap(0, 2);
2211 colors.swap(0, 2);
2212 }
2213 if points[1].y > points[2].y {
2214 points.swap(1, 2);
2215 depths.swap(1, 2);
2216 colors.swap(1, 2);
2217 }
2218
2219 let [p1, p2, p3] = points;
2220 let [z1, z2, z3] = depths;
2221 let [c1, c2, c3] = colors;
2222
2223 let scr_w = width as i32;
2225 let scr_h = (zbuffer.len() / width) as i32;
2226 if p1.x < 0 && p2.x < 0 && p3.x < 0 {
2227 return;
2228 }
2229 if p1.x >= scr_w && p2.x >= scr_w && p3.x >= scr_w {
2230 return;
2231 }
2232 if p1.y < 0 && p2.y < 0 && p3.y < 0 {
2233 return;
2234 }
2235 if p1.y >= scr_h && p2.y >= scr_h && p3.y >= scr_h {
2236 return;
2237 }
2238
2239 fill_triangle_zbuffered_gouraud(
2240 p1,
2241 p2,
2242 p3,
2243 z1,
2244 z2,
2245 z3,
2246 c1,
2247 c2,
2248 c3,
2249 fb,
2250 zbuffer,
2251 width,
2252 fog_config,
2253 dither_config,
2254 );
2255 }
2256 DrawPrimitive::TexturedTriangle { .. }
2258 | DrawPrimitive::TexturedTriangleWithDepth { .. }
2259 | DrawPrimitive::TexturedGouraudTriangleWithDepth { .. }
2260 | DrawPrimitive::LightmappedTriangle { .. } => {
2261 }
2263 _ => draw(primitive, fb),
2265 }
2266}
2267
2268#[inline(always)]
2269fn interpolate_uv(
2270 t: f32,
2271 w1: f32,
2272 w2: f32,
2273 uv1: [f32; 2],
2274 uv2: [f32; 2],
2275 texture_mapping: TextureMapping,
2276) -> [f32; 2] {
2277 match texture_mapping {
2278 TextureMapping::PerspectiveCorrect => {
2279 let ow1 = 1.0 / w1;
2280 let ow2 = 1.0 / w2;
2281 let one_over_w = ow1 + t * (ow2 - ow1);
2282 [
2283 (uv1[0] * ow1 + t * (uv2[0] * ow2 - uv1[0] * ow1)) / one_over_w,
2284 (uv1[1] * ow1 + t * (uv2[1] * ow2 - uv1[1] * ow1)) / one_over_w,
2285 ]
2286 }
2287 TextureMapping::Affine => [
2288 uv1[0] + t * (uv2[0] - uv1[0]),
2289 uv1[1] + t * (uv2[1] - uv1[1]),
2290 ],
2291 }
2292}
2293
2294#[inline(always)]
2295fn should_skip_stipple(x: i32, y: i32, stipple_mode: StippleMode) -> bool {
2296 matches!(stipple_mode, StippleMode::Checkerboard) && ((x ^ y) & 1) != 0
2297}
2298
2299#[inline]
2301pub fn draw_zbuffered_with_textures<
2302 D: DrawTarget<Color = embedded_graphics_core::pixelcolor::Rgb565>,
2303 const N: usize,
2304>(
2305 primitive: DrawPrimitive,
2306 fb: &mut D,
2307 zbuffer: &mut [crate::ZDepth],
2308 width: usize,
2309 texture_manager: &crate::texture::TextureManager<N>,
2310 fog_config: Option<&FogConfig>,
2311 dither_config: Option<&DitherConfig>,
2312) where
2313 <D as DrawTarget>::Error: Debug,
2314{
2315 draw_zbuffered_with_textures_mapped(
2316 primitive,
2317 fb,
2318 zbuffer,
2319 width,
2320 texture_manager,
2321 fog_config,
2322 dither_config,
2323 TextureMapping::PerspectiveCorrect,
2324 StippleMode::Off,
2325 None,
2326 PaletteMode::Off,
2327 );
2328}
2329
2330#[inline]
2331pub fn draw_zbuffered_with_textures_mapped<
2332 D: DrawTarget<Color = embedded_graphics_core::pixelcolor::Rgb565>,
2333 const N: usize,
2334>(
2335 primitive: DrawPrimitive,
2336 fb: &mut D,
2337 zbuffer: &mut [crate::ZDepth],
2338 width: usize,
2339 texture_manager: &crate::texture::TextureManager<N>,
2340 fog_config: Option<&FogConfig>,
2341 dither_config: Option<&DitherConfig>,
2342 texture_mapping: TextureMapping,
2343 stipple_mode: StippleMode,
2344 screen_tint: Option<ScreenTint>,
2345 palette_mode: PaletteMode,
2346) where
2347 <D as DrawTarget>::Error: Debug,
2348{
2349 match primitive {
2350 DrawPrimitive::TexturedTriangleWithDepth {
2351 mut points,
2352 mut depths,
2353 mut ws,
2354 mut uvs,
2355 texture_id,
2356 } => {
2357 if let Some(texture) = texture_manager.get(texture_id) {
2359 if points[0].y > points[1].y {
2361 points.swap(0, 1);
2362 depths.swap(0, 1);
2363 ws.swap(0, 1);
2364 uvs.swap(0, 1);
2365 }
2366 if points[0].y > points[2].y {
2367 points.swap(0, 2);
2368 depths.swap(0, 2);
2369 ws.swap(0, 2);
2370 uvs.swap(0, 2);
2371 }
2372 if points[1].y > points[2].y {
2373 points.swap(1, 2);
2374 depths.swap(1, 2);
2375 ws.swap(1, 2);
2376 uvs.swap(1, 2);
2377 }
2378
2379 let [p1, p2, p3] = points;
2380 let [z1, z2, z3] = depths;
2381 let [w1, w2, w3] = ws;
2382 let [uv1, uv2, uv3] = uvs;
2383
2384 let scr_w = width as i32;
2386 let scr_h = (zbuffer.len() / width) as i32;
2387 if p1.x < 0 && p2.x < 0 && p3.x < 0 {
2388 return;
2389 }
2390 if p1.x >= scr_w && p2.x >= scr_w && p3.x >= scr_w {
2391 return;
2392 }
2393 if p1.y < 0 && p2.y < 0 && p3.y < 0 {
2394 return;
2395 }
2396 if p1.y >= scr_h && p2.y >= scr_h && p3.y >= scr_h {
2397 return;
2398 }
2399
2400 fill_triangle_zbuffered_textured(
2401 p1,
2402 p2,
2403 p3,
2404 z1,
2405 z2,
2406 z3,
2407 w1,
2408 w2,
2409 w3,
2410 uv1,
2411 uv2,
2412 uv3,
2413 texture,
2414 fb,
2415 zbuffer,
2416 width,
2417 fog_config,
2418 dither_config,
2419 texture_mapping,
2420 stipple_mode,
2421 screen_tint,
2422 palette_mode,
2423 );
2424 }
2425 }
2426 DrawPrimitive::TexturedGouraudTriangleWithDepth {
2427 mut points,
2428 mut depths,
2429 mut ws,
2430 mut uvs,
2431 mut colors,
2432 texture_id,
2433 } => {
2434 if let Some(texture) = texture_manager.get(texture_id) {
2435 if points[0].y > points[1].y {
2436 points.swap(0, 1);
2437 depths.swap(0, 1);
2438 ws.swap(0, 1);
2439 uvs.swap(0, 1);
2440 colors.swap(0, 1);
2441 }
2442 if points[0].y > points[2].y {
2443 points.swap(0, 2);
2444 depths.swap(0, 2);
2445 ws.swap(0, 2);
2446 uvs.swap(0, 2);
2447 colors.swap(0, 2);
2448 }
2449 if points[1].y > points[2].y {
2450 points.swap(1, 2);
2451 depths.swap(1, 2);
2452 ws.swap(1, 2);
2453 uvs.swap(1, 2);
2454 colors.swap(1, 2);
2455 }
2456
2457 let [p1, p2, p3] = points;
2458 let [z1, z2, z3] = depths;
2459 let [w1, w2, w3] = ws;
2460 let [uv1, uv2, uv3] = uvs;
2461 let [c1, c2, c3] = colors;
2462
2463 let scr_w = width as i32;
2464 let scr_h = (zbuffer.len() / width) as i32;
2465 if p1.x < 0 && p2.x < 0 && p3.x < 0 {
2466 return;
2467 }
2468 if p1.x >= scr_w && p2.x >= scr_w && p3.x >= scr_w {
2469 return;
2470 }
2471 if p1.y < 0 && p2.y < 0 && p3.y < 0 {
2472 return;
2473 }
2474 if p1.y >= scr_h && p2.y >= scr_h && p3.y >= scr_h {
2475 return;
2476 }
2477
2478 fill_triangle_zbuffered_textured_gouraud(
2479 p1,
2480 p2,
2481 p3,
2482 z1,
2483 z2,
2484 z3,
2485 w1,
2486 w2,
2487 w3,
2488 uv1,
2489 uv2,
2490 uv3,
2491 c1,
2492 c2,
2493 c3,
2494 texture,
2495 fb,
2496 zbuffer,
2497 width,
2498 fog_config,
2499 dither_config,
2500 texture_mapping,
2501 stipple_mode,
2502 screen_tint,
2503 palette_mode,
2504 );
2505 }
2506 }
2507 _ => draw_zbuffered_with_effects(primitive, fb, zbuffer, width, fog_config, dither_config),
2509 }
2510}
2511
2512pub fn draw_zbuffered_lightmapped<
2526 D: DrawTarget<Color = embedded_graphics_core::pixelcolor::Rgb565>,
2527 const N: usize,
2528>(
2529 points: [nalgebra::Point2<i32>; 3],
2530 depths: [f32; 3],
2531 ws: [f32; 3],
2532 surface_uvs: [[f32; 2]; 3],
2533 lm_uvs: [[f32; 2]; 3],
2534 texture_id: u32,
2535 lightmap_id: u32,
2536 brightness: u8,
2537 dynamic_tint: embedded_graphics_core::pixelcolor::Rgb565,
2538 fog_config: Option<&FogConfig>,
2539 texture_manager: &crate::texture::TextureManager<N>,
2540 fb: &mut D,
2541 zbuffer: &mut [crate::ZDepth],
2542 width: usize,
2543) where
2544 <D as DrawTarget>::Error: core::fmt::Debug,
2545{
2546 draw_zbuffered_lightmapped_mapped(
2547 points,
2548 depths,
2549 ws,
2550 surface_uvs,
2551 lm_uvs,
2552 texture_id,
2553 lightmap_id,
2554 brightness,
2555 dynamic_tint,
2556 fog_config,
2557 texture_manager,
2558 fb,
2559 zbuffer,
2560 width,
2561 TextureMapping::PerspectiveCorrect,
2562 StippleMode::Off,
2563 None,
2564 PaletteMode::Off,
2565 );
2566}
2567
2568pub fn draw_zbuffered_lightmapped_mapped<
2569 D: DrawTarget<Color = embedded_graphics_core::pixelcolor::Rgb565>,
2570 const N: usize,
2571>(
2572 mut points: [nalgebra::Point2<i32>; 3],
2573 mut depths: [f32; 3],
2574 mut ws: [f32; 3],
2575 mut surface_uvs: [[f32; 2]; 3],
2576 mut lm_uvs: [[f32; 2]; 3],
2577 texture_id: u32,
2578 lightmap_id: u32,
2579 brightness: u8,
2580 dynamic_tint: embedded_graphics_core::pixelcolor::Rgb565,
2581 fog_config: Option<&FogConfig>,
2582 texture_manager: &crate::texture::TextureManager<N>,
2583 fb: &mut D,
2584 zbuffer: &mut [crate::ZDepth],
2585 width: usize,
2586 texture_mapping: TextureMapping,
2587 stipple_mode: StippleMode,
2588 screen_tint: Option<ScreenTint>,
2589 palette_mode: PaletteMode,
2590) where
2591 <D as DrawTarget>::Error: core::fmt::Debug,
2592{
2593 let surf = match texture_manager.get(texture_id) {
2594 Some(t) => t,
2595 None => return,
2596 };
2597 let lm = if lightmap_id == u32::MAX {
2598 None
2599 } else {
2600 texture_manager.get(lightmap_id)
2601 };
2602
2603 macro_rules! swap_all {
2605 ($i:expr, $j:expr) => {
2606 points.swap($i, $j);
2607 depths.swap($i, $j);
2608 ws.swap($i, $j);
2609 surface_uvs.swap($i, $j);
2610 lm_uvs.swap($i, $j);
2611 };
2612 }
2613 if points[0].y > points[1].y {
2614 swap_all!(0, 1);
2615 }
2616 if points[0].y > points[2].y {
2617 swap_all!(0, 2);
2618 }
2619 if points[1].y > points[2].y {
2620 swap_all!(1, 2);
2621 }
2622
2623 let [p1, p2, p3] = points;
2624 let [z1, z2, z3] = depths;
2625 let [w1, w2, w3] = ws;
2626 let [uv1, uv2, uv3] = surface_uvs;
2627 let [luv1, luv2, luv3] = lm_uvs;
2628
2629 let scr_w = width as i32;
2630 let scr_h = (zbuffer.len() / width) as i32;
2631 if p1.x < 0 && p2.x < 0 && p3.x < 0 {
2632 return;
2633 }
2634 if p1.x >= scr_w && p2.x >= scr_w && p3.x >= scr_w {
2635 return;
2636 }
2637 if p1.y < 0 && p2.y < 0 && p3.y < 0 {
2638 return;
2639 }
2640 if p1.y >= scr_h && p2.y >= scr_h && p3.y >= scr_h {
2641 return;
2642 }
2643
2644 let z1_int = (z1 * 65536.0) as u32;
2645 let z2_int = (z2 * 65536.0) as u32;
2646 let z3_int = (z3 * 65536.0) as u32;
2647
2648 if p2.y == p3.y {
2650 fill_lm_bottom_flat(
2651 p1,
2652 p2,
2653 p3,
2654 z1_int,
2655 z2_int,
2656 z3_int,
2657 w1,
2658 w2,
2659 w3,
2660 uv1,
2661 uv2,
2662 uv3,
2663 luv1,
2664 luv2,
2665 luv3,
2666 dynamic_tint,
2667 fog_config,
2668 surf,
2669 lm,
2670 fb,
2671 zbuffer,
2672 width,
2673 texture_mapping,
2674 stipple_mode,
2675 screen_tint,
2676 palette_mode,
2677 brightness,
2678 );
2679 } else if p1.y == p2.y {
2680 fill_lm_top_flat(
2681 p1,
2682 p2,
2683 p3,
2684 z1_int,
2685 z2_int,
2686 z3_int,
2687 w1,
2688 w2,
2689 w3,
2690 uv1,
2691 uv2,
2692 uv3,
2693 luv1,
2694 luv2,
2695 luv3,
2696 dynamic_tint,
2697 fog_config,
2698 surf,
2699 lm,
2700 fb,
2701 zbuffer,
2702 width,
2703 texture_mapping,
2704 stipple_mode,
2705 screen_tint,
2706 palette_mode,
2707 brightness,
2708 );
2709 } else {
2710 let dy31 = (p3.y - p1.y) as f32;
2712 let dy21 = (p2.y - p1.y) as f32;
2713 let t = dy21 / dy31;
2714 let p4x = p1.x + ((p3.x - p1.x) as f32 * t) as i32;
2715 let p4 = embedded_graphics_core::prelude::Point::new(p4x, p2.y);
2716 let z4_int = (z1_int as f32 + (z3_int as f32 - z1_int as f32) * t) as u32;
2717 let w4 = w1 + (w3 - w1) * t;
2718 let uv4 = [
2719 uv1[0] + (uv3[0] - uv1[0]) * t,
2720 uv1[1] + (uv3[1] - uv1[1]) * t,
2721 ];
2722 let luv4 = [
2723 luv1[0] + (luv3[0] - luv1[0]) * t,
2724 luv1[1] + (luv3[1] - luv1[1]) * t,
2725 ];
2726 let p4_2 = nalgebra::Point2::new(p4.x, p4.y);
2727 fill_lm_bottom_flat(
2728 p1,
2729 p2,
2730 p4_2,
2731 z1_int,
2732 z2_int,
2733 z4_int,
2734 w1,
2735 w2,
2736 w4,
2737 uv1,
2738 uv2,
2739 uv4,
2740 luv1,
2741 luv2,
2742 luv4,
2743 dynamic_tint,
2744 fog_config,
2745 surf,
2746 lm,
2747 fb,
2748 zbuffer,
2749 width,
2750 texture_mapping,
2751 stipple_mode,
2752 screen_tint,
2753 palette_mode,
2754 brightness,
2755 );
2756 fill_lm_top_flat(
2757 p2,
2758 p4_2,
2759 p3,
2760 z2_int,
2761 z4_int,
2762 z3_int,
2763 w2,
2764 w4,
2765 w3,
2766 uv2,
2767 uv4,
2768 uv3,
2769 luv2,
2770 luv4,
2771 luv3,
2772 dynamic_tint,
2773 fog_config,
2774 surf,
2775 lm,
2776 fb,
2777 zbuffer,
2778 width,
2779 texture_mapping,
2780 stipple_mode,
2781 screen_tint,
2782 palette_mode,
2783 brightness,
2784 );
2785 }
2786}
2787
2788#[inline(always)]
2789#[allow(clippy::too_many_arguments)]
2790fn fill_lm_bottom_flat<D: DrawTarget<Color = embedded_graphics_core::pixelcolor::Rgb565>>(
2791 p1: nalgebra::Point2<i32>,
2792 p2: nalgebra::Point2<i32>,
2793 p3: nalgebra::Point2<i32>,
2794 z1: u32,
2795 z2: u32,
2796 z3: u32,
2797 w1: f32,
2798 w2: f32,
2799 w3: f32,
2800 uv1: [f32; 2],
2801 uv2: [f32; 2],
2802 uv3: [f32; 2],
2803 luv1: [f32; 2],
2804 luv2: [f32; 2],
2805 luv3: [f32; 2],
2806 dynamic_tint: embedded_graphics_core::pixelcolor::Rgb565,
2807 fog_config: Option<&FogConfig>,
2808 surf: &crate::texture::Texture,
2809 lm: Option<&crate::texture::Texture>,
2810 fb: &mut D,
2811 zbuffer: &mut [crate::ZDepth],
2812 width: usize,
2813 texture_mapping: TextureMapping,
2814 stipple_mode: StippleMode,
2815 screen_tint: Option<ScreenTint>,
2816 palette_mode: PaletteMode,
2817 brightness: u8,
2818) where
2819 <D as DrawTarget>::Error: core::fmt::Debug,
2820{
2821 let height = p2.y - p1.y;
2822 if height == 0 {
2823 return;
2824 }
2825 let invslope1 = ((p2.x - p1.x) << 16) / height;
2826 let invslope2 = ((p3.x - p1.x) << 16) / height;
2827 let mut curx1 = p1.x << 16;
2828 let mut curx2 = p1.x << 16;
2829 for scanline_y in p1.y..=p2.y {
2830 let dy = scanline_y - p1.y;
2831 let t = dy as f32 / height as f32;
2832 let z_l = (z1 as i64 + (z2 as i64 - z1 as i64) * dy as i64 / height as i64) as u32;
2833 let z_r = (z1 as i64 + (z3 as i64 - z1 as i64) * dy as i64 / height as i64) as u32;
2834 let wl = w1 + t * (w2 - w1);
2835 let wr = w1 + t * (w3 - w1);
2836 let uvl = [
2837 uv1[0] + t * (uv2[0] - uv1[0]),
2838 uv1[1] + t * (uv2[1] - uv1[1]),
2839 ];
2840 let uvr = [
2841 uv1[0] + t * (uv3[0] - uv1[0]),
2842 uv1[1] + t * (uv3[1] - uv1[1]),
2843 ];
2844 let luvl = [
2845 luv1[0] + t * (luv2[0] - luv1[0]),
2846 luv1[1] + t * (luv2[1] - luv1[1]),
2847 ];
2848 let luvr = [
2849 luv1[0] + t * (luv3[0] - luv1[0]),
2850 luv1[1] + t * (luv3[1] - luv1[1]),
2851 ];
2852 draw_scanline_lm(
2853 curx1 >> 16,
2854 curx2 >> 16,
2855 scanline_y,
2856 z_l,
2857 z_r,
2858 wl,
2859 wr,
2860 uvl,
2861 uvr,
2862 luvl,
2863 luvr,
2864 dynamic_tint,
2865 fog_config,
2866 surf,
2867 lm,
2868 fb,
2869 zbuffer,
2870 width,
2871 texture_mapping,
2872 stipple_mode,
2873 screen_tint,
2874 palette_mode,
2875 brightness,
2876 );
2877 curx1 += invslope1;
2878 curx2 += invslope2;
2879 }
2880}
2881
2882#[inline(always)]
2883#[allow(clippy::too_many_arguments)]
2884fn fill_lm_top_flat<D: DrawTarget<Color = embedded_graphics_core::pixelcolor::Rgb565>>(
2885 p1: nalgebra::Point2<i32>,
2886 p2: nalgebra::Point2<i32>,
2887 p3: nalgebra::Point2<i32>,
2888 z1: u32,
2889 z2: u32,
2890 z3: u32,
2891 w1: f32,
2892 w2: f32,
2893 w3: f32,
2894 uv1: [f32; 2],
2895 uv2: [f32; 2],
2896 uv3: [f32; 2],
2897 luv1: [f32; 2],
2898 luv2: [f32; 2],
2899 luv3: [f32; 2],
2900 dynamic_tint: embedded_graphics_core::pixelcolor::Rgb565,
2901 fog_config: Option<&FogConfig>,
2902 surf: &crate::texture::Texture,
2903 lm: Option<&crate::texture::Texture>,
2904 fb: &mut D,
2905 zbuffer: &mut [crate::ZDepth],
2906 width: usize,
2907 texture_mapping: TextureMapping,
2908 stipple_mode: StippleMode,
2909 screen_tint: Option<ScreenTint>,
2910 palette_mode: PaletteMode,
2911 brightness: u8,
2912) where
2913 <D as DrawTarget>::Error: core::fmt::Debug,
2914{
2915 let height = p3.y - p1.y;
2916 if height == 0 {
2917 return;
2918 }
2919 let invslope1 = ((p3.x - p1.x) << 16) / height;
2920 let invslope2 = ((p3.x - p2.x) << 16) / height;
2921 let mut curx1 = p3.x << 16;
2922 let mut curx2 = p3.x << 16;
2923 for scanline_y in (p1.y..=p3.y).rev() {
2924 let dy = scanline_y - p1.y;
2925 let t = dy as f32 / height as f32;
2926 let z_l = (z1 as i64 + (z3 as i64 - z1 as i64) * dy as i64 / height as i64) as u32;
2927 let z_r = (z2 as i64 + (z3 as i64 - z2 as i64) * dy as i64 / height as i64) as u32;
2928 let wl = w1 + t * (w3 - w1);
2929 let wr = w2 + t * (w3 - w2);
2930 let uvl = [
2931 uv1[0] + t * (uv3[0] - uv1[0]),
2932 uv1[1] + t * (uv3[1] - uv1[1]),
2933 ];
2934 let uvr = [
2935 uv2[0] + t * (uv3[0] - uv2[0]),
2936 uv2[1] + t * (uv3[1] - uv2[1]),
2937 ];
2938 let luvl = [
2939 luv1[0] + t * (luv3[0] - luv1[0]),
2940 luv1[1] + t * (luv3[1] - luv1[1]),
2941 ];
2942 let luvr = [
2943 luv2[0] + t * (luv3[0] - luv2[0]),
2944 luv2[1] + t * (luv3[1] - luv2[1]),
2945 ];
2946 draw_scanline_lm(
2947 curx1 >> 16,
2948 curx2 >> 16,
2949 scanline_y,
2950 z_l,
2951 z_r,
2952 wl,
2953 wr,
2954 uvl,
2955 uvr,
2956 luvl,
2957 luvr,
2958 dynamic_tint,
2959 fog_config,
2960 surf,
2961 lm,
2962 fb,
2963 zbuffer,
2964 width,
2965 texture_mapping,
2966 stipple_mode,
2967 screen_tint,
2968 palette_mode,
2969 brightness,
2970 );
2971 curx1 -= invslope1;
2972 curx2 -= invslope2;
2973 }
2974}
2975
2976#[inline(always)]
2977#[allow(clippy::too_many_arguments)]
2978fn draw_scanline_lm<D: DrawTarget<Color = embedded_graphics_core::pixelcolor::Rgb565>>(
2979 x1: i32,
2980 x2: i32,
2981 y: i32,
2982 z1: u32,
2983 z2: u32,
2984 w1: f32,
2985 w2: f32,
2986 uv1: [f32; 2],
2987 uv2: [f32; 2],
2988 luv1: [f32; 2],
2989 luv2: [f32; 2],
2990 dynamic_tint: embedded_graphics_core::pixelcolor::Rgb565,
2991 fog_config: Option<&FogConfig>,
2992 surf: &crate::texture::Texture,
2993 lm: Option<&crate::texture::Texture>,
2994 fb: &mut D,
2995 zbuffer: &mut [crate::ZDepth],
2996 width: usize,
2997 texture_mapping: TextureMapping,
2998 stipple_mode: StippleMode,
2999 screen_tint: Option<ScreenTint>,
3000 palette_mode: PaletteMode,
3001 brightness: u8,
3002) where
3003 <D as DrawTarget>::Error: core::fmt::Debug,
3004{
3005 use embedded_graphics_core::pixelcolor::RgbColor;
3006 use embedded_graphics_core::prelude::Point;
3007
3008 if y < 0 {
3009 return;
3010 }
3011 let height = zbuffer.len() / width;
3012 if y as usize >= height {
3013 return;
3014 }
3015
3016 let (left_x, right_x, z_left, z_right, w_left, w_right, uv_left, uv_right, luv_left, luv_right) =
3017 if x1 <= x2 {
3018 (x1, x2, z1, z2, w1, w2, uv1, uv2, luv1, luv2)
3019 } else {
3020 (x2, x1, z2, z1, w2, w1, uv2, uv1, luv2, luv1)
3021 };
3022
3023 let start_x = left_x.max(0);
3024 let end_x = right_x.min(width as i32 - 1);
3025 if start_x > end_x {
3026 return;
3027 }
3028
3029 let span = right_x - left_x;
3030 let inv_span = if span > 0 { 1.0 / span as f32 } else { 0.0 };
3031 let z_step = if span > 0 {
3032 (((z_right as i64 - z_left as i64) << 16) / span as i64) as i32
3033 } else {
3034 0
3035 };
3036
3037 let left_clip = start_x - left_x;
3038 let mut z_curr = ((z_left as i64) << 16) + (left_clip as i64 * z_step as i64);
3039 let mut zbuf_idx = y as usize * width + start_x as usize;
3040
3041 for x in start_x..=end_x {
3042 if should_skip_stipple(x, y, stipple_mode) {
3043 z_curr += z_step as i64;
3044 zbuf_idx += 1;
3045 continue;
3046 }
3047
3048 let z = (z_curr >> 16) as u32;
3049 z_curr += z_step as i64;
3050 let z_depth = crate::to_zdepth(z);
3051
3052 if z_depth >= zbuffer[zbuf_idx].saturating_add(crate::DEPTH_EPSILON) {
3053 zbuf_idx += 1;
3054 continue;
3055 }
3056 zbuffer[zbuf_idx] = z_depth;
3057
3058 let t = (x - left_x) as f32 * inv_span;
3059 let [su, sv] = interpolate_uv(t, w_left, w_right, uv_left, uv_right, texture_mapping);
3060 let surf_c = surf.sample(su, sv);
3061
3062 let lit_c = if let Some(lm_tex) = lm {
3063 let [lu, lv] = interpolate_uv(t, w_left, w_right, luv_left, luv_right, texture_mapping);
3064 let lm_c = lm_tex.sample(lu, lv);
3065 let r = ((surf_c.r() as u32 * lm_c.r() as u32) / 31).min(31) as u8;
3066 let g = ((surf_c.g() as u32 * lm_c.g() as u32) / 63).min(63) as u8;
3067 let b = ((surf_c.b() as u32 * lm_c.b() as u32) / 31).min(31) as u8;
3068 embedded_graphics_core::pixelcolor::Rgb565::new(r, g, b)
3069 } else {
3070 surf_c
3071 };
3072
3073 let lit_c = if brightness < 255 {
3074 let scale = brightness as u32;
3075 let r = ((lit_c.r() as u32 * scale) / 255) as u8;
3076 let g = ((lit_c.g() as u32 * scale) / 255) as u8;
3077 let b = ((lit_c.b() as u32 * scale) / 255) as u8;
3078 embedded_graphics_core::pixelcolor::Rgb565::new(r, g, b)
3079 } else {
3080 lit_c
3081 };
3082
3083 let tinted_c = embedded_graphics_core::pixelcolor::Rgb565::new(
3084 (lit_c.r() as u16 + dynamic_tint.r() as u16).min(31) as u8,
3085 (lit_c.g() as u16 + dynamic_tint.g() as u16).min(63) as u8,
3086 (lit_c.b() as u16 + dynamic_tint.b() as u16).min(31) as u8,
3087 );
3088
3089 let mut final_c = if let Some(fog) = fog_config {
3090 fog.apply(tinted_c, z)
3091 } else {
3092 tinted_c
3093 };
3094
3095 if let Some(tint) = screen_tint {
3096 final_c = tint.apply(final_c);
3097 }
3098 final_c = palette_mode.apply(final_c);
3099
3100 fb.draw_iter([embedded_graphics_core::Pixel(Point::new(x, y), final_c)])
3101 .unwrap();
3102 }
3103}
3104
3105pub fn draw_bsp_coverage<
3115 D: DrawTarget<Color = embedded_graphics_core::pixelcolor::Rgb565>,
3116 const N: usize,
3117>(
3118 mut points: [nalgebra::Point2<i32>; 3],
3119 mut ws: [f32; 3],
3120 mut uvs: [[f32; 2]; 3],
3121 texture_id: u32,
3122 texture_manager: &crate::texture::TextureManager<N>,
3123 fb: &mut D,
3124 coverage: &mut crate::bsp::coverage::CoverageBuffer<'_>,
3125 texture_mapping: TextureMapping,
3126 stipple_mode: StippleMode,
3127 screen_tint: Option<ScreenTint>,
3128 palette_mode: PaletteMode,
3129) where
3130 <D as DrawTarget>::Error: core::fmt::Debug,
3131{
3132 let tex = match texture_manager.get(texture_id) {
3133 Some(t) => t,
3134 None => return,
3135 };
3136
3137 if points[0].y > points[1].y {
3139 points.swap(0, 1);
3140 ws.swap(0, 1);
3141 uvs.swap(0, 1);
3142 }
3143 if points[0].y > points[2].y {
3144 points.swap(0, 2);
3145 ws.swap(0, 2);
3146 uvs.swap(0, 2);
3147 }
3148 if points[1].y > points[2].y {
3149 points.swap(1, 2);
3150 ws.swap(1, 2);
3151 uvs.swap(1, 2);
3152 }
3153
3154 let [p1, p2, p3] = points;
3155 let [w1, w2, w3] = ws;
3156 let [uv1, uv2, uv3] = uvs;
3157
3158 let w = coverage.width as i32;
3159 let h = coverage.height as i32;
3160 if p1.x < 0 && p2.x < 0 && p3.x < 0 {
3161 return;
3162 }
3163 if p1.x >= w && p2.x >= w && p3.x >= w {
3164 return;
3165 }
3166 if p1.y < 0 && p2.y < 0 && p3.y < 0 {
3167 return;
3168 }
3169 if p1.y >= h && p2.y >= h && p3.y >= h {
3170 return;
3171 }
3172
3173 let rasterize_span =
3174 |x1: i32,
3175 x2: i32,
3176 y: i32,
3177 wl: f32,
3178 wr: f32,
3179 uvl: [f32; 2],
3180 uvr: [f32; 2],
3181 fb: &mut D,
3182 coverage: &mut crate::bsp::coverage::CoverageBuffer<'_>| {
3183 let start = x1.min(x2);
3184 let end = x1.max(x2);
3185 let span = end - start;
3186 for x in start..=end {
3187 if x < 0 || y < 0 || x >= w || y >= h {
3188 continue;
3189 }
3190 if coverage.is_covered(x as usize, y as usize) {
3191 continue;
3192 }
3193 if should_skip_stipple(x, y, stipple_mode) {
3194 continue;
3195 }
3196 let t = if span > 0 {
3197 (x - start) as f32 / span as f32
3198 } else {
3199 0.0
3200 };
3201 let [su, sv] = interpolate_uv(t, wl, wr, uvl, uvr, texture_mapping);
3202 let mut color = tex.sample(su, sv);
3203 if let Some(tint) = screen_tint {
3204 color = tint.apply(color);
3205 }
3206 color = palette_mode.apply(color);
3207 coverage.mark_covered(x as usize, y as usize);
3208 fb.draw_iter([embedded_graphics_core::Pixel(
3209 embedded_graphics_core::prelude::Point::new(x, y),
3210 color,
3211 )])
3212 .unwrap();
3213 }
3214 };
3215
3216 let draw_flat_bottom =
3218 |p1: nalgebra::Point2<i32>,
3219 p2: nalgebra::Point2<i32>,
3220 p3: nalgebra::Point2<i32>,
3221 w1: f32,
3222 w2: f32,
3223 w3: f32,
3224 uv1: [f32; 2],
3225 uv2: [f32; 2],
3226 uv3: [f32; 2],
3227 fb: &mut D,
3228 coverage: &mut crate::bsp::coverage::CoverageBuffer<'_>| {
3229 let height = p2.y - p1.y;
3230 if height == 0 {
3231 return;
3232 }
3233 let invslope1 = ((p2.x - p1.x) << 16) / height;
3234 let invslope2 = ((p3.x - p1.x) << 16) / height;
3235 let mut cx1 = p1.x << 16;
3236 let mut cx2 = p1.x << 16;
3237 for sy in p1.y..=p2.y {
3238 let dy = sy - p1.y;
3239 let t = dy as f32 / height as f32;
3240 let wl = w1 + t * (w2 - w1);
3241 let wr = w1 + t * (w3 - w1);
3242 let uvl = [
3243 uv1[0] + t * (uv2[0] - uv1[0]),
3244 uv1[1] + t * (uv2[1] - uv1[1]),
3245 ];
3246 let uvr = [
3247 uv1[0] + t * (uv3[0] - uv1[0]),
3248 uv1[1] + t * (uv3[1] - uv1[1]),
3249 ];
3250 rasterize_span(cx1 >> 16, cx2 >> 16, sy, wl, wr, uvl, uvr, fb, coverage);
3251 cx1 += invslope1;
3252 cx2 += invslope2;
3253 }
3254 };
3255
3256 let draw_flat_top =
3257 |p1: nalgebra::Point2<i32>,
3258 p2: nalgebra::Point2<i32>,
3259 p3: nalgebra::Point2<i32>,
3260 w1: f32,
3261 w2: f32,
3262 w3: f32,
3263 uv1: [f32; 2],
3264 uv2: [f32; 2],
3265 uv3: [f32; 2],
3266 fb: &mut D,
3267 coverage: &mut crate::bsp::coverage::CoverageBuffer<'_>| {
3268 let height = p3.y - p1.y;
3269 if height == 0 {
3270 return;
3271 }
3272 let invslope1 = ((p3.x - p1.x) << 16) / height;
3273 let invslope2 = ((p3.x - p2.x) << 16) / height;
3274 let mut cx1 = p3.x << 16;
3275 let mut cx2 = p3.x << 16;
3276 for sy in (p1.y..=p3.y).rev() {
3277 let dy = sy - p1.y;
3278 let t = dy as f32 / height as f32;
3279 let wl = w1 + t * (w3 - w1);
3280 let wr = w2 + t * (w3 - w2);
3281 let uvl = [
3282 uv1[0] + t * (uv3[0] - uv1[0]),
3283 uv1[1] + t * (uv3[1] - uv1[1]),
3284 ];
3285 let uvr = [
3286 uv2[0] + t * (uv3[0] - uv2[0]),
3287 uv2[1] + t * (uv3[1] - uv2[1]),
3288 ];
3289 rasterize_span(cx1 >> 16, cx2 >> 16, sy, wl, wr, uvl, uvr, fb, coverage);
3290 cx1 -= invslope1;
3291 cx2 -= invslope2;
3292 }
3293 };
3294
3295 if p2.y == p3.y {
3296 draw_flat_bottom(p1, p2, p3, w1, w2, w3, uv1, uv2, uv3, fb, coverage);
3297 } else if p1.y == p2.y {
3298 draw_flat_top(p1, p2, p3, w1, w2, w3, uv1, uv2, uv3, fb, coverage);
3299 } else {
3300 let dy31 = (p3.y - p1.y) as f32;
3301 let dy21 = (p2.y - p1.y) as f32;
3302 let t = dy21 / dy31;
3303 let p4x = p1.x + ((p3.x - p1.x) as f32 * t) as i32;
3304 let p4 = nalgebra::Point2::new(p4x, p2.y);
3305 let w4 = w1 + (w3 - w1) * t;
3306 let uv4 = [
3307 uv1[0] + (uv3[0] - uv1[0]) * t,
3308 uv1[1] + (uv3[1] - uv1[1]) * t,
3309 ];
3310 draw_flat_bottom(p1, p2, p4, w1, w2, w4, uv1, uv2, uv4, fb, coverage);
3311 draw_flat_top(p2, p4, p3, w2, w4, w3, uv2, uv4, uv3, fb, coverage);
3312 }
3313}
3314
3315#[inline(always)]
3316fn fill_triangle_zbuffered<D: DrawTarget<Color = embedded_graphics_core::pixelcolor::Rgb565>>(
3317 p1: nalgebra::Point2<i32>,
3318 p2: nalgebra::Point2<i32>,
3319 p3: nalgebra::Point2<i32>,
3320 z1: f32,
3321 z2: f32,
3322 z3: f32,
3323 color: embedded_graphics_core::pixelcolor::Rgb565,
3324 fb: &mut D,
3325 zbuffer: &mut [crate::ZDepth],
3326 width: usize,
3327 fog_config: Option<&FogConfig>,
3328 dither_config: Option<&DitherConfig>,
3329) where
3330 <D as DrawTarget>::Error: Debug,
3331{
3332 let p1_eg = Point::new(p1.x, p1.y);
3334 let p2_eg = Point::new(p2.x, p2.y);
3335 let p3_eg = Point::new(p3.x, p3.y);
3336
3337 let z1_int = (z1 * 65536.0) as u32;
3340 let z2_int = (z2 * 65536.0) as u32;
3341 let z3_int = (z3 * 65536.0) as u32;
3342
3343 if p2_eg.y == p3_eg.y {
3345 fill_bottom_flat_triangle_zbuffered(
3346 p1_eg,
3347 p2_eg,
3348 p3_eg,
3349 z1_int,
3350 z2_int,
3351 z3_int,
3352 color,
3353 fb,
3354 zbuffer,
3355 width,
3356 fog_config,
3357 dither_config,
3358 );
3359 } else if p1_eg.y == p2_eg.y {
3360 fill_top_flat_triangle_zbuffered(
3361 p1_eg,
3362 p2_eg,
3363 p3_eg,
3364 z1_int,
3365 z2_int,
3366 z3_int,
3367 color,
3368 fb,
3369 zbuffer,
3370 width,
3371 fog_config,
3372 dither_config,
3373 );
3374 } else {
3375 let t = (p2_eg.y - p1_eg.y) as f32 / (p3_eg.y - p1_eg.y) as f32;
3377 let p4 = Point::new(
3378 (p1_eg.x as f32 + t * (p3_eg.x - p1_eg.x) as f32) as i32,
3379 p2_eg.y,
3380 );
3381 let z4_int = (z1_int as i64 + (t * (z3_int as i64 - z1_int as i64) as f32) as i64) as u32;
3382
3383 fill_bottom_flat_triangle_zbuffered(
3384 p1_eg,
3385 p2_eg,
3386 p4,
3387 z1_int,
3388 z2_int,
3389 z4_int,
3390 color,
3391 fb,
3392 zbuffer,
3393 width,
3394 fog_config,
3395 dither_config,
3396 );
3397 fill_top_flat_triangle_zbuffered(
3398 p2_eg,
3399 p4,
3400 p3_eg,
3401 z2_int,
3402 z4_int,
3403 z3_int,
3404 color,
3405 fb,
3406 zbuffer,
3407 width,
3408 fog_config,
3409 dither_config,
3410 );
3411 }
3412}
3413
3414#[inline(always)]
3416fn fill_triangle_zbuffered_gouraud<
3417 D: DrawTarget<Color = embedded_graphics_core::pixelcolor::Rgb565>,
3418>(
3419 p1: nalgebra::Point2<i32>,
3420 p2: nalgebra::Point2<i32>,
3421 p3: nalgebra::Point2<i32>,
3422 z1: f32,
3423 z2: f32,
3424 z3: f32,
3425 c1: embedded_graphics_core::pixelcolor::Rgb565,
3426 c2: embedded_graphics_core::pixelcolor::Rgb565,
3427 c3: embedded_graphics_core::pixelcolor::Rgb565,
3428 fb: &mut D,
3429 zbuffer: &mut [crate::ZDepth],
3430 width: usize,
3431 fog_config: Option<&FogConfig>,
3432 dither_config: Option<&DitherConfig>,
3433) where
3434 <D as DrawTarget>::Error: Debug,
3435{
3436 let p1_eg = Point::new(p1.x, p1.y);
3438 let p2_eg = Point::new(p2.x, p2.y);
3439 let p3_eg = Point::new(p3.x, p3.y);
3440
3441 let z1_int = (z1 * 65536.0) as u32;
3443 let z2_int = (z2 * 65536.0) as u32;
3444 let z3_int = (z3 * 65536.0) as u32;
3445
3446 if p2_eg.y == p3_eg.y {
3448 fill_bottom_flat_triangle_zbuffered_gouraud(
3449 p1_eg,
3450 p2_eg,
3451 p3_eg,
3452 z1_int,
3453 z2_int,
3454 z3_int,
3455 c1,
3456 c2,
3457 c3,
3458 fb,
3459 zbuffer,
3460 width,
3461 fog_config,
3462 dither_config,
3463 );
3464 } else if p1_eg.y == p2_eg.y {
3465 fill_top_flat_triangle_zbuffered_gouraud(
3466 p1_eg,
3467 p2_eg,
3468 p3_eg,
3469 z1_int,
3470 z2_int,
3471 z3_int,
3472 c1,
3473 c2,
3474 c3,
3475 fb,
3476 zbuffer,
3477 width,
3478 fog_config,
3479 dither_config,
3480 );
3481 } else {
3482 let t = (p2_eg.y - p1_eg.y) as f32 / (p3_eg.y - p1_eg.y) as f32;
3484 let p4 = Point::new(
3485 (p1_eg.x as f32 + t * (p3_eg.x - p1_eg.x) as f32) as i32,
3486 p2_eg.y,
3487 );
3488 let z4_int = (z1_int as i64 + (t * (z3_int as i64 - z1_int as i64) as f32) as i64) as u32;
3489 let c4 = interpolate_color(c1, c3, t);
3490
3491 fill_bottom_flat_triangle_zbuffered_gouraud(
3492 p1_eg,
3493 p2_eg,
3494 p4,
3495 z1_int,
3496 z2_int,
3497 z4_int,
3498 c1,
3499 c2,
3500 c4,
3501 fb,
3502 zbuffer,
3503 width,
3504 fog_config,
3505 dither_config,
3506 );
3507 fill_top_flat_triangle_zbuffered_gouraud(
3508 p2_eg,
3509 p4,
3510 p3_eg,
3511 z2_int,
3512 z4_int,
3513 z3_int,
3514 c2,
3515 c4,
3516 c3,
3517 fb,
3518 zbuffer,
3519 width,
3520 fog_config,
3521 dither_config,
3522 );
3523 }
3524}
3525
3526#[inline(always)]
3527fn fill_bottom_flat_triangle_zbuffered<
3528 D: DrawTarget<Color = embedded_graphics_core::pixelcolor::Rgb565>,
3529>(
3530 p1: Point,
3531 p2: Point,
3532 p3: Point,
3533 z1: u32,
3534 z2: u32,
3535 z3: u32,
3536 color: embedded_graphics_core::pixelcolor::Rgb565,
3537 fb: &mut D,
3538 zbuffer: &mut [crate::ZDepth],
3539 width: usize,
3540 fog_config: Option<&FogConfig>,
3541 dither_config: Option<&DitherConfig>,
3542) where
3543 <D as DrawTarget>::Error: Debug,
3544{
3545 let height = p2.y - p1.y;
3546 if height == 0 {
3547 return;
3548 }
3549
3550 let invslope1 = ((p2.x - p1.x) << 16) / height;
3553 let invslope2 = ((p3.x - p1.x) << 16) / height;
3554
3555 let mut curx1 = p1.x << 16; let mut curx2 = p1.x << 16; let scr_h = (zbuffer.len() / width) as i32;
3561 let y_skip = (0_i32 - p1.y).max(0);
3562 curx1 = curx1.wrapping_add(invslope1.wrapping_mul(y_skip));
3563 curx2 = curx2.wrapping_add(invslope2.wrapping_mul(y_skip));
3564 let y_start = p1.y.max(0);
3565 let y_end = p2.y.min(scr_h - 1);
3566
3567 for scanline_y in y_start..=y_end {
3568 let dy = scanline_y - p1.y;
3569 let z_left = if height > 0 {
3571 (z1 as i64 + ((z2 as i64 - z1 as i64) * dy as i64 / height as i64)) as u32
3572 } else {
3573 z1
3574 };
3575 let z_right = if height > 0 {
3576 (z1 as i64 + ((z3 as i64 - z1 as i64) * dy as i64 / height as i64)) as u32
3577 } else {
3578 z1
3579 };
3580
3581 draw_scanline_zbuffered(
3582 curx1 >> 16, curx2 >> 16, scanline_y,
3585 z_left,
3586 z_right,
3587 color,
3588 fb,
3589 zbuffer,
3590 width,
3591 fog_config,
3592 dither_config,
3593 );
3594
3595 curx1 = curx1.wrapping_add(invslope1);
3596 curx2 = curx2.wrapping_add(invslope2);
3597 }
3598}
3599
3600#[inline(always)]
3601fn fill_top_flat_triangle_zbuffered<
3602 D: DrawTarget<Color = embedded_graphics_core::pixelcolor::Rgb565>,
3603>(
3604 p1: Point,
3605 p2: Point,
3606 p3: Point,
3607 z1: u32,
3608 z2: u32,
3609 z3: u32,
3610 color: embedded_graphics_core::pixelcolor::Rgb565,
3611 fb: &mut D,
3612 zbuffer: &mut [crate::ZDepth],
3613 width: usize,
3614 fog_config: Option<&FogConfig>,
3615 dither_config: Option<&DitherConfig>,
3616) where
3617 <D as DrawTarget>::Error: Debug,
3618{
3619 let height = p3.y - p1.y;
3620 if height == 0 {
3621 return;
3622 }
3623
3624 let invslope1 = ((p3.x - p1.x) << 16) / height;
3626 let invslope2 = ((p3.x - p2.x) << 16) / height;
3627
3628 let mut curx1 = p3.x << 16; let mut curx2 = p3.x << 16; let scr_h = (zbuffer.len() / width) as i32;
3637 let y_skip_bot = (p3.y - (scr_h - 1)).max(0);
3638 curx1 = curx1.wrapping_sub(invslope1.wrapping_mul(y_skip_bot));
3639 curx2 = curx2.wrapping_sub(invslope2.wrapping_mul(y_skip_bot));
3640 let y_start = p1.y.max(0);
3641 let y_end = p3.y.min(scr_h - 1);
3642
3643 for scanline_y in (y_start..=y_end).rev() {
3644 let dy = scanline_y - p1.y;
3645 let z_left = if height > 0 {
3647 (z1 as i64 + ((z3 as i64 - z1 as i64) * dy as i64 / height as i64)) as u32
3648 } else {
3649 z1
3650 };
3651 let z_right = if height > 0 {
3652 (z2 as i64 + ((z3 as i64 - z2 as i64) * dy as i64 / height as i64)) as u32
3653 } else {
3654 z2
3655 };
3656
3657 draw_scanline_zbuffered(
3658 curx1 >> 16, curx2 >> 16, scanline_y,
3661 z_left,
3662 z_right,
3663 color,
3664 fb,
3665 zbuffer,
3666 width,
3667 fog_config,
3668 dither_config,
3669 );
3670
3671 curx1 = curx1.wrapping_sub(invslope1);
3672 curx2 = curx2.wrapping_sub(invslope2);
3673 }
3674}
3675
3676#[inline(always)]
3678fn fill_bottom_flat_triangle_zbuffered_gouraud<
3679 D: DrawTarget<Color = embedded_graphics_core::pixelcolor::Rgb565>,
3680>(
3681 p1: Point,
3682 p2: Point,
3683 p3: Point,
3684 z1: u32,
3685 z2: u32,
3686 z3: u32,
3687 c1: embedded_graphics_core::pixelcolor::Rgb565,
3688 c2: embedded_graphics_core::pixelcolor::Rgb565,
3689 c3: embedded_graphics_core::pixelcolor::Rgb565,
3690 fb: &mut D,
3691 zbuffer: &mut [crate::ZDepth],
3692 width: usize,
3693 fog_config: Option<&FogConfig>,
3694 dither_config: Option<&DitherConfig>,
3695) where
3696 <D as DrawTarget>::Error: Debug,
3697{
3698 let height = p2.y - p1.y;
3699 if height == 0 {
3700 return;
3701 }
3702
3703 let invslope1 = ((p2.x - p1.x) << 16) / height;
3704 let invslope2 = ((p3.x - p1.x) << 16) / height;
3705
3706 let mut curx1 = p1.x << 16;
3707 let mut curx2 = p1.x << 16;
3708
3709 for scanline_y in p1.y..=p2.y {
3710 let dy = scanline_y - p1.y;
3711 let t = dy as f32 / height as f32;
3712
3713 let z_left = if height > 0 {
3715 (z1 as i64 + ((z2 as i64 - z1 as i64) * dy as i64 / height as i64)) as u32
3716 } else {
3717 z1
3718 };
3719 let z_right = if height > 0 {
3720 (z1 as i64 + ((z3 as i64 - z1 as i64) * dy as i64 / height as i64)) as u32
3721 } else {
3722 z1
3723 };
3724
3725 let color_left = interpolate_color(c1, c2, t);
3727 let color_right = interpolate_color(c1, c3, t);
3728
3729 draw_scanline_zbuffered_gouraud(
3730 curx1 >> 16,
3731 curx2 >> 16,
3732 scanline_y,
3733 z_left,
3734 z_right,
3735 color_left,
3736 color_right,
3737 fb,
3738 zbuffer,
3739 width,
3740 fog_config,
3741 dither_config,
3742 );
3743
3744 curx1 += invslope1;
3745 curx2 += invslope2;
3746 }
3747}
3748
3749#[inline(always)]
3751fn fill_top_flat_triangle_zbuffered_gouraud<
3752 D: DrawTarget<Color = embedded_graphics_core::pixelcolor::Rgb565>,
3753>(
3754 p1: Point,
3755 p2: Point,
3756 p3: Point,
3757 z1: u32,
3758 z2: u32,
3759 z3: u32,
3760 c1: embedded_graphics_core::pixelcolor::Rgb565,
3761 c2: embedded_graphics_core::pixelcolor::Rgb565,
3762 c3: embedded_graphics_core::pixelcolor::Rgb565,
3763 fb: &mut D,
3764 zbuffer: &mut [crate::ZDepth],
3765 width: usize,
3766 fog_config: Option<&FogConfig>,
3767 dither_config: Option<&DitherConfig>,
3768) where
3769 <D as DrawTarget>::Error: Debug,
3770{
3771 let height = p3.y - p1.y;
3772 if height == 0 {
3773 return;
3774 }
3775
3776 let invslope1 = ((p3.x - p1.x) << 16) / height;
3777 let invslope2 = ((p3.x - p2.x) << 16) / height;
3778
3779 let mut curx1 = p3.x << 16;
3780 let mut curx2 = p3.x << 16;
3781
3782 for scanline_y in (p1.y..=p3.y).rev() {
3783 let dy = scanline_y - p1.y;
3784 let t = dy as f32 / height as f32;
3785
3786 let z_left = if height > 0 {
3788 (z1 as i64 + ((z3 as i64 - z1 as i64) * dy as i64 / height as i64)) as u32
3789 } else {
3790 z1
3791 };
3792 let z_right = if height > 0 {
3793 (z2 as i64 + ((z3 as i64 - z2 as i64) * dy as i64 / height as i64)) as u32
3794 } else {
3795 z2
3796 };
3797
3798 let color_left = interpolate_color(c1, c3, t);
3800 let color_right = interpolate_color(c2, c3, t);
3801
3802 draw_scanline_zbuffered_gouraud(
3803 curx1 >> 16,
3804 curx2 >> 16,
3805 scanline_y,
3806 z_left,
3807 z_right,
3808 color_left,
3809 color_right,
3810 fb,
3811 zbuffer,
3812 width,
3813 fog_config,
3814 dither_config,
3815 );
3816
3817 curx1 -= invslope1;
3818 curx2 -= invslope2;
3819 }
3820}
3821
3822#[inline(always)]
3824fn draw_scanline_zbuffered_gouraud<
3825 D: DrawTarget<Color = embedded_graphics_core::pixelcolor::Rgb565>,
3826>(
3827 x1: i32,
3828 x2: i32,
3829 y: i32,
3830 z1: u32,
3831 z2: u32,
3832 color1: embedded_graphics_core::pixelcolor::Rgb565,
3833 color2: embedded_graphics_core::pixelcolor::Rgb565,
3834 fb: &mut D,
3835 zbuffer: &mut [crate::ZDepth],
3836 width: usize,
3837 fog_config: Option<&FogConfig>,
3838 dither_config: Option<&DitherConfig>,
3839) where
3840 <D as DrawTarget>::Error: Debug,
3841{
3842 if y < 0 {
3843 return;
3844 }
3845 let height = zbuffer.len() / width;
3846 if y as usize >= height {
3847 return;
3848 }
3849
3850 let (left_x, right_x, z_left, z_right, c_left, c_right) = if x1 <= x2 {
3851 (x1, x2, z1, z2, color1, color2)
3852 } else {
3853 (x2, x1, z2, z1, color2, color1)
3854 };
3855
3856 let start_x = left_x.max(0);
3857 let end_x = right_x.min(width as i32 - 1);
3858 if start_x > end_x {
3859 return;
3860 }
3861
3862 let span = right_x - left_x;
3863 let inv_span = if span > 0 { 1.0 / span as f32 } else { 0.0 };
3864 let z_step = if span > 0 {
3865 (((z_right as i64 - z_left as i64) << 16) / span as i64) as i32
3866 } else {
3867 0
3868 };
3869
3870 let left_clip = start_x - left_x;
3871 let mut z_curr = ((z_left as i64) << 16) + (left_clip as i64 * z_step as i64);
3872 let mut zbuf_idx = y as usize * width + start_x as usize;
3873
3874 for x in start_x..=end_x {
3875 let z = (z_curr >> 16) as u32;
3876 z_curr += z_step as i64;
3877 let z_depth = crate::to_zdepth(z);
3878
3879 if z_depth < zbuffer[zbuf_idx].saturating_add(crate::DEPTH_EPSILON) {
3880 zbuffer[zbuf_idx] = z_depth;
3881
3882 let t = (x - left_x) as f32 * inv_span;
3883 let mut final_color = interpolate_color(c_left, c_right, t);
3884
3885 if let Some(fog) = fog_config {
3886 final_color = fog.apply(final_color, z);
3887 }
3888
3889 if let Some(dither) = dither_config {
3890 final_color = dither.apply(final_color, x, y);
3891 }
3892
3893 fb.draw_iter([embedded_graphics_core::Pixel(Point::new(x, y), final_color)])
3894 .unwrap();
3895 }
3896 zbuf_idx += 1;
3897 }
3898}
3899
3900#[inline(always)]
3901fn draw_scanline_zbuffered<D: DrawTarget<Color = embedded_graphics_core::pixelcolor::Rgb565>>(
3902 x1: i32,
3903 x2: i32,
3904 y: i32,
3905 z1: u32,
3906 z2: u32,
3907 color: embedded_graphics_core::pixelcolor::Rgb565,
3908 fb: &mut D,
3909 zbuffer: &mut [crate::ZDepth],
3910 width: usize,
3911 fog_config: Option<&FogConfig>,
3912 dither_config: Option<&DitherConfig>,
3913) where
3914 <D as DrawTarget>::Error: Debug,
3915{
3916 if y < 0 {
3917 return;
3918 }
3919 let height = zbuffer.len() / width;
3920 if y as usize >= height {
3921 return;
3922 }
3923
3924 let (left_x, right_x, z_left, z_right) = if x1 <= x2 {
3925 (x1, x2, z1, z2)
3926 } else {
3927 (x2, x1, z2, z1)
3928 };
3929
3930 let start_x = left_x.max(0);
3931 let end_x = right_x.min(width as i32 - 1);
3932 if start_x > end_x {
3933 return;
3934 }
3935
3936 let span = right_x - left_x;
3937 let z_step = if span > 0 {
3938 (((z_right as i64 - z_left as i64) << 16) / span as i64) as i32
3939 } else {
3940 0
3941 };
3942
3943 let left_clip = start_x - left_x;
3944 let mut z_curr = ((z_left as i64) << 16) + (left_clip as i64 * z_step as i64);
3945 let mut zbuf_idx = y as usize * width + start_x as usize;
3946
3947 for x in start_x..=end_x {
3948 let z = (z_curr >> 16) as u32;
3949 z_curr += z_step as i64;
3950 let z_depth = crate::to_zdepth(z);
3951
3952 if z_depth < zbuffer[zbuf_idx].saturating_add(crate::DEPTH_EPSILON) {
3953 zbuffer[zbuf_idx] = z_depth;
3954
3955 let mut final_color = color;
3956
3957 if let Some(fog) = fog_config {
3958 final_color = fog.apply(final_color, z);
3959 }
3960
3961 if let Some(dither) = dither_config {
3962 final_color = dither.apply(final_color, x, y);
3963 }
3964
3965 fb.draw_iter([embedded_graphics_core::Pixel(Point::new(x, y), final_color)])
3966 .unwrap();
3967 }
3968 zbuf_idx += 1;
3969 }
3970}
3971
3972#[inline(always)]
3974fn fill_triangle_zbuffered_textured<
3975 D: DrawTarget<Color = embedded_graphics_core::pixelcolor::Rgb565>,
3976>(
3977 p1: nalgebra::Point2<i32>,
3978 p2: nalgebra::Point2<i32>,
3979 p3: nalgebra::Point2<i32>,
3980 z1: f32,
3981 z2: f32,
3982 z3: f32,
3983 w1: f32,
3984 w2: f32,
3985 w3: f32,
3986 uv1: [f32; 2],
3987 uv2: [f32; 2],
3988 uv3: [f32; 2],
3989 texture: &crate::texture::Texture,
3990 fb: &mut D,
3991 zbuffer: &mut [crate::ZDepth],
3992 width: usize,
3993 fog_config: Option<&FogConfig>,
3994 dither_config: Option<&DitherConfig>,
3995 texture_mapping: TextureMapping,
3996 stipple_mode: StippleMode,
3997 screen_tint: Option<ScreenTint>,
3998 palette_mode: PaletteMode,
3999) where
4000 <D as DrawTarget>::Error: Debug,
4001{
4002 let p1_eg = Point::new(p1.x, p1.y);
4004 let p2_eg = Point::new(p2.x, p2.y);
4005 let p3_eg = Point::new(p3.x, p3.y);
4006
4007 let z1_int = (z1 * 65536.0) as u32;
4009 let z2_int = (z2 * 65536.0) as u32;
4010 let z3_int = (z3 * 65536.0) as u32;
4011
4012 if p2_eg.y == p3_eg.y {
4014 fill_bottom_flat_triangle_zbuffered_textured(
4015 p1_eg,
4016 p2_eg,
4017 p3_eg,
4018 z1_int,
4019 z2_int,
4020 z3_int,
4021 w1,
4022 w2,
4023 w3,
4024 uv1,
4025 uv2,
4026 uv3,
4027 texture,
4028 fb,
4029 zbuffer,
4030 width,
4031 fog_config,
4032 dither_config,
4033 texture_mapping,
4034 stipple_mode,
4035 screen_tint,
4036 palette_mode,
4037 );
4038 } else if p1_eg.y == p2_eg.y {
4039 fill_top_flat_triangle_zbuffered_textured(
4040 p1_eg,
4041 p2_eg,
4042 p3_eg,
4043 z1_int,
4044 z2_int,
4045 z3_int,
4046 w1,
4047 w2,
4048 w3,
4049 uv1,
4050 uv2,
4051 uv3,
4052 texture,
4053 fb,
4054 zbuffer,
4055 width,
4056 fog_config,
4057 dither_config,
4058 texture_mapping,
4059 stipple_mode,
4060 screen_tint,
4061 palette_mode,
4062 );
4063 } else {
4064 let t = (p2_eg.y - p1_eg.y) as f32 / (p3_eg.y - p1_eg.y) as f32;
4066 let p4 = Point::new(
4067 (p1_eg.x as f32 + t * (p3_eg.x - p1_eg.x) as f32) as i32,
4068 p2_eg.y,
4069 );
4070 let z4_int = (z1_int as i64 + (t * (z3_int as i64 - z1_int as i64) as f32) as i64) as u32;
4071 let w4 = w1 + t * (w3 - w1);
4073 let uv4 = [
4075 uv1[0] + t * (uv3[0] - uv1[0]),
4076 uv1[1] + t * (uv3[1] - uv1[1]),
4077 ];
4078
4079 fill_bottom_flat_triangle_zbuffered_textured(
4080 p1_eg,
4081 p2_eg,
4082 p4,
4083 z1_int,
4084 z2_int,
4085 z4_int,
4086 w1,
4087 w2,
4088 w4,
4089 uv1,
4090 uv2,
4091 uv4,
4092 texture,
4093 fb,
4094 zbuffer,
4095 width,
4096 fog_config,
4097 dither_config,
4098 texture_mapping,
4099 stipple_mode,
4100 screen_tint,
4101 palette_mode,
4102 );
4103 fill_top_flat_triangle_zbuffered_textured(
4104 p2_eg,
4105 p4,
4106 p3_eg,
4107 z2_int,
4108 z4_int,
4109 z3_int,
4110 w2,
4111 w4,
4112 w3,
4113 uv2,
4114 uv4,
4115 uv3,
4116 texture,
4117 fb,
4118 zbuffer,
4119 width,
4120 fog_config,
4121 dither_config,
4122 texture_mapping,
4123 stipple_mode,
4124 screen_tint,
4125 palette_mode,
4126 );
4127 }
4128}
4129
4130#[inline(always)]
4132fn fill_bottom_flat_triangle_zbuffered_textured<
4133 D: DrawTarget<Color = embedded_graphics_core::pixelcolor::Rgb565>,
4134>(
4135 p1: Point,
4136 p2: Point,
4137 p3: Point,
4138 z1: u32,
4139 z2: u32,
4140 z3: u32,
4141 w1: f32,
4142 w2: f32,
4143 w3: f32,
4144 uv1: [f32; 2],
4145 uv2: [f32; 2],
4146 uv3: [f32; 2],
4147 texture: &crate::texture::Texture,
4148 fb: &mut D,
4149 zbuffer: &mut [crate::ZDepth],
4150 width: usize,
4151 fog_config: Option<&FogConfig>,
4152 dither_config: Option<&DitherConfig>,
4153 texture_mapping: TextureMapping,
4154 stipple_mode: StippleMode,
4155 screen_tint: Option<ScreenTint>,
4156 palette_mode: PaletteMode,
4157) where
4158 <D as DrawTarget>::Error: Debug,
4159{
4160 let height = p2.y - p1.y;
4161 if height == 0 {
4162 return;
4163 }
4164
4165 let invslope1 = ((p2.x - p1.x) << 16) / height;
4166 let invslope2 = ((p3.x - p1.x) << 16) / height;
4167
4168 let mut curx1 = p1.x << 16;
4169 let mut curx2 = p1.x << 16;
4170
4171 for scanline_y in p1.y..=p2.y {
4172 let dy = scanline_y - p1.y;
4173 let t = dy as f32 / height as f32;
4174
4175 let z_left = if height > 0 {
4177 (z1 as i64 + ((z2 as i64 - z1 as i64) * dy as i64 / height as i64)) as u32
4178 } else {
4179 z1
4180 };
4181 let z_right = if height > 0 {
4182 (z1 as i64 + ((z3 as i64 - z1 as i64) * dy as i64 / height as i64)) as u32
4183 } else {
4184 z1
4185 };
4186
4187 let w_left = w1 + t * (w2 - w1);
4189 let w_right = w1 + t * (w3 - w1);
4190
4191 let uv_left = [
4193 uv1[0] + t * (uv2[0] - uv1[0]),
4194 uv1[1] + t * (uv2[1] - uv1[1]),
4195 ];
4196 let uv_right = [
4197 uv1[0] + t * (uv3[0] - uv1[0]),
4198 uv1[1] + t * (uv3[1] - uv1[1]),
4199 ];
4200
4201 draw_scanline_zbuffered_textured(
4202 curx1 >> 16,
4203 curx2 >> 16,
4204 scanline_y,
4205 z_left,
4206 z_right,
4207 w_left,
4208 w_right,
4209 uv_left,
4210 uv_right,
4211 texture,
4212 fb,
4213 zbuffer,
4214 width,
4215 fog_config,
4216 dither_config,
4217 texture_mapping,
4218 stipple_mode,
4219 screen_tint,
4220 palette_mode,
4221 );
4222
4223 curx1 += invslope1;
4224 curx2 += invslope2;
4225 }
4226}
4227
4228#[inline(always)]
4230fn fill_top_flat_triangle_zbuffered_textured<
4231 D: DrawTarget<Color = embedded_graphics_core::pixelcolor::Rgb565>,
4232>(
4233 p1: Point,
4234 p2: Point,
4235 p3: Point,
4236 z1: u32,
4237 z2: u32,
4238 z3: u32,
4239 w1: f32,
4240 w2: f32,
4241 w3: f32,
4242 uv1: [f32; 2],
4243 uv2: [f32; 2],
4244 uv3: [f32; 2],
4245 texture: &crate::texture::Texture,
4246 fb: &mut D,
4247 zbuffer: &mut [crate::ZDepth],
4248 width: usize,
4249 fog_config: Option<&FogConfig>,
4250 dither_config: Option<&DitherConfig>,
4251 texture_mapping: TextureMapping,
4252 stipple_mode: StippleMode,
4253 screen_tint: Option<ScreenTint>,
4254 palette_mode: PaletteMode,
4255) where
4256 <D as DrawTarget>::Error: Debug,
4257{
4258 let height = p3.y - p1.y;
4259 if height == 0 {
4260 return;
4261 }
4262
4263 let invslope1 = ((p3.x - p1.x) << 16) / height;
4264 let invslope2 = ((p3.x - p2.x) << 16) / height;
4265
4266 let mut curx1 = p3.x << 16;
4267 let mut curx2 = p3.x << 16;
4268
4269 for scanline_y in (p1.y..=p3.y).rev() {
4270 let dy = scanline_y - p1.y;
4271 let t = dy as f32 / height as f32;
4272
4273 let z_left = if height > 0 {
4275 (z1 as i64 + ((z3 as i64 - z1 as i64) * dy as i64 / height as i64)) as u32
4276 } else {
4277 z1
4278 };
4279 let z_right = if height > 0 {
4280 (z2 as i64 + ((z3 as i64 - z2 as i64) * dy as i64 / height as i64)) as u32
4281 } else {
4282 z2
4283 };
4284
4285 let w_left = w1 + t * (w3 - w1);
4287 let w_right = w2 + t * (w3 - w2);
4288
4289 let uv_left = [
4291 uv1[0] + t * (uv3[0] - uv1[0]),
4292 uv1[1] + t * (uv3[1] - uv1[1]),
4293 ];
4294 let uv_right = [
4295 uv2[0] + t * (uv3[0] - uv2[0]),
4296 uv2[1] + t * (uv3[1] - uv2[1]),
4297 ];
4298
4299 draw_scanline_zbuffered_textured(
4300 curx1 >> 16,
4301 curx2 >> 16,
4302 scanline_y,
4303 z_left,
4304 z_right,
4305 w_left,
4306 w_right,
4307 uv_left,
4308 uv_right,
4309 texture,
4310 fb,
4311 zbuffer,
4312 width,
4313 fog_config,
4314 dither_config,
4315 texture_mapping,
4316 stipple_mode,
4317 screen_tint,
4318 palette_mode,
4319 );
4320
4321 curx1 -= invslope1;
4322 curx2 -= invslope2;
4323 }
4324}
4325
4326#[inline(always)]
4328fn draw_scanline_zbuffered_textured<
4329 D: DrawTarget<Color = embedded_graphics_core::pixelcolor::Rgb565>,
4330>(
4331 x1: i32,
4332 x2: i32,
4333 y: i32,
4334 z1: u32,
4335 z2: u32,
4336 w1: f32,
4337 w2: f32,
4338 uv1: [f32; 2],
4339 uv2: [f32; 2],
4340 texture: &crate::texture::Texture,
4341 fb: &mut D,
4342 zbuffer: &mut [crate::ZDepth],
4343 width: usize,
4344 fog_config: Option<&FogConfig>,
4345 dither_config: Option<&DitherConfig>,
4346 texture_mapping: TextureMapping,
4347 stipple_mode: StippleMode,
4348 screen_tint: Option<ScreenTint>,
4349 palette_mode: PaletteMode,
4350) where
4351 <D as DrawTarget>::Error: Debug,
4352{
4353 if y < 0 {
4354 return;
4355 }
4356 let height = zbuffer.len() / width;
4357 if y as usize >= height {
4358 return;
4359 }
4360
4361 let (left_x, right_x, z_left, z_right, w_left, w_right, uv_left, uv_right) = if x1 <= x2 {
4362 (x1, x2, z1, z2, w1, w2, uv1, uv2)
4363 } else {
4364 (x2, x1, z2, z1, w2, w1, uv2, uv1)
4365 };
4366
4367 let start_x = left_x.max(0);
4368 let end_x = right_x.min(width as i32 - 1);
4369 if start_x > end_x {
4370 return;
4371 }
4372
4373 let span = right_x - left_x;
4374 let inv_span = if span > 0 { 1.0 / span as f32 } else { 0.0 };
4375 let z_step = if span > 0 {
4376 (((z_right as i64 - z_left as i64) << 16) / span as i64) as i32
4377 } else {
4378 0
4379 };
4380
4381 let left_clip = start_x - left_x;
4382 let mut z_curr = ((z_left as i64) << 16) + (left_clip as i64 * z_step as i64);
4383 let mut zbuf_idx = y as usize * width + start_x as usize;
4384
4385 const SUB_SPAN_SIZE: i32 = 16;
4388
4389 let mut span_x = start_x;
4390 while span_x <= end_x {
4391 let next_span_x = (span_x + SUB_SPAN_SIZE).min(end_x + 1);
4392 let span_len = next_span_x - span_x;
4393
4394 let t_start = (span_x - left_x) as f32 * inv_span;
4395 let t_end = (next_span_x - 1 - left_x) as f32 * inv_span;
4396
4397 let [u_start, v_start] =
4398 interpolate_uv(t_start, w_left, w_right, uv_left, uv_right, texture_mapping);
4399 let [u_end, v_end] =
4400 interpolate_uv(t_end, w_left, w_right, uv_left, uv_right, texture_mapping);
4401
4402 let inv_sub = if span_len > 1 {
4403 1.0 / (span_len - 1) as f32
4404 } else {
4405 0.0
4406 };
4407 let du = (u_end - u_start) * inv_sub;
4408 let dv = (v_end - v_start) * inv_sub;
4409
4410 let mut curr_u = u_start;
4411 let mut curr_v = v_start;
4412
4413 for x in span_x..next_span_x {
4414 if should_skip_stipple(x, y, stipple_mode) {
4415 z_curr += z_step as i64;
4416 zbuf_idx += 1;
4417 curr_u += du;
4418 curr_v += dv;
4419 continue;
4420 }
4421
4422 let z = (z_curr >> 16) as u32;
4423 z_curr += z_step as i64;
4424 let z_depth = crate::to_zdepth(z);
4425
4426 if z_depth < zbuffer[zbuf_idx].saturating_add(crate::DEPTH_EPSILON) {
4427 zbuffer[zbuf_idx] = z_depth;
4428
4429 let mut final_color = texture.sample(curr_u, curr_v);
4431
4432 if let Some(fog) = fog_config {
4434 final_color = fog.apply(final_color, z);
4435 }
4436
4437 if let Some(dither) = dither_config {
4438 final_color = dither.apply(final_color, x, y);
4439 }
4440 if let Some(tint) = screen_tint {
4441 final_color = tint.apply(final_color);
4442 }
4443 final_color = palette_mode.apply(final_color);
4444
4445 fb.draw_iter([embedded_graphics_core::Pixel(Point::new(x, y), final_color)])
4446 .unwrap();
4447 }
4448 zbuf_idx += 1;
4449 curr_u += du;
4450 curr_v += dv;
4451 }
4452
4453 span_x = next_span_x;
4454 }
4455}
4456
4457#[inline(always)]
4458fn fill_triangle_zbuffered_translucent<D: DrawTarget<Color = Rgb565>>(
4459 p1: nalgebra::Point2<i32>,
4460 p2: nalgebra::Point2<i32>,
4461 p3: nalgebra::Point2<i32>,
4462 z1: f32,
4463 z2: f32,
4464 z3: f32,
4465 color: Rgb565,
4466 alpha: u8,
4467 fb: &mut D,
4468 zbuffer: &mut [crate::ZDepth],
4469 width: usize,
4470) where
4471 <D as DrawTarget>::Error: Debug,
4472{
4473 let p1_eg = Point::new(p1.x, p1.y);
4474 let p2_eg = Point::new(p2.x, p2.y);
4475 let p3_eg = Point::new(p3.x, p3.y);
4476
4477 let z1_int = (z1 * 65536.0) as u32;
4478 let z2_int = (z2 * 65536.0) as u32;
4479 let z3_int = (z3 * 65536.0) as u32;
4480
4481 if p2_eg.y == p3_eg.y {
4482 fill_bottom_flat_translucent(
4483 p1_eg, p2_eg, p3_eg, z1_int, z2_int, z3_int, color, alpha, fb, zbuffer, width,
4484 );
4485 } else if p1_eg.y == p2_eg.y {
4486 fill_top_flat_translucent(
4487 p1_eg, p2_eg, p3_eg, z1_int, z2_int, z3_int, color, alpha, fb, zbuffer, width,
4488 );
4489 } else {
4490 let t = (p2_eg.y - p1_eg.y) as f32 / (p3_eg.y - p1_eg.y) as f32;
4491 let p4 = Point::new(
4492 (p1_eg.x as f32 + t * (p3_eg.x - p1_eg.x) as f32) as i32,
4493 p2_eg.y,
4494 );
4495 let z4_int = (z1_int as i64 + (t * (z3_int as i64 - z1_int as i64) as f32) as i64) as u32;
4496 fill_bottom_flat_translucent(
4497 p1_eg, p2_eg, p4, z1_int, z2_int, z4_int, color, alpha, fb, zbuffer, width,
4498 );
4499 fill_top_flat_translucent(
4500 p2_eg, p4, p3_eg, z2_int, z4_int, z3_int, color, alpha, fb, zbuffer, width,
4501 );
4502 }
4503}
4504
4505#[inline(always)]
4506fn fill_bottom_flat_translucent<D: DrawTarget<Color = Rgb565>>(
4507 p1: Point,
4508 p2: Point,
4509 p3: Point,
4510 z1: u32,
4511 z2: u32,
4512 z3: u32,
4513 color: Rgb565,
4514 alpha: u8,
4515 fb: &mut D,
4516 zbuffer: &mut [crate::ZDepth],
4517 width: usize,
4518) where
4519 <D as DrawTarget>::Error: Debug,
4520{
4521 let height = p2.y - p1.y;
4522 if height == 0 {
4523 return;
4524 }
4525 let invslope1 = ((p2.x - p1.x) << 16) / height;
4526 let invslope2 = ((p3.x - p1.x) << 16) / height;
4527
4528 let mut curx1 = p1.x << 16;
4529 let mut curx2 = p1.x << 16;
4530
4531 for scanline_y in p1.y..=p2.y {
4532 let dy = scanline_y - p1.y;
4533 let z_left = (z1 as i64 + ((z2 as i64 - z1 as i64) * dy as i64 / height as i64)) as u32;
4534 let z_right = (z1 as i64 + ((z3 as i64 - z1 as i64) * dy as i64 / height as i64)) as u32;
4535
4536 let (left_x, right_x, z_l, z_r) = if curx1 <= curx2 {
4537 (curx1 >> 16, curx2 >> 16, z_left, z_right)
4538 } else {
4539 (curx2 >> 16, curx1 >> 16, z_right, z_left)
4540 };
4541
4542 let span = right_x - left_x;
4543 for x in left_x..=right_x {
4544 if x < 0 || scanline_y < 0 || x >= width as i32 {
4545 continue;
4546 }
4547 let idx = (scanline_y as usize) * width + (x as usize);
4548 if idx >= zbuffer.len() {
4549 continue;
4550 }
4551
4552 let z = if span > 0 {
4553 let t = (x - left_x) as f32 / span as f32;
4554 (z_l as f32 + t * (z_r as f32 - z_l as f32)) as u32
4555 } else {
4556 z_l
4557 };
4558 let z_depth = crate::to_zdepth(z);
4559
4560 if z_depth < zbuffer[idx] {
4561 zbuffer[idx] = z_depth;
4562 let draw_color = fast_blend_rgb565(Rgb565::BLACK, color, alpha);
4563 let _ = fb.draw_iter([embedded_graphics_core::Pixel(
4564 Point::new(x, scanline_y),
4565 draw_color,
4566 )]);
4567 }
4568 }
4569
4570 curx1 += invslope1;
4571 curx2 += invslope2;
4572 }
4573}
4574
4575#[inline(always)]
4576fn fill_top_flat_translucent<D: DrawTarget<Color = Rgb565>>(
4577 p1: Point,
4578 p2: Point,
4579 p3: Point,
4580 z1: u32,
4581 z2: u32,
4582 z3: u32,
4583 color: Rgb565,
4584 alpha: u8,
4585 fb: &mut D,
4586 zbuffer: &mut [crate::ZDepth],
4587 width: usize,
4588) where
4589 <D as DrawTarget>::Error: Debug,
4590{
4591 let height = p3.y - p1.y;
4592 if height == 0 {
4593 return;
4594 }
4595 let invslope1 = ((p3.x - p1.x) << 16) / height;
4596 let invslope2 = ((p3.x - p2.x) << 16) / height;
4597
4598 let mut curx1 = p3.x << 16;
4599 let mut curx2 = p3.x << 16;
4600
4601 for scanline_y in (p1.y..=p3.y).rev() {
4602 let dy = scanline_y - p1.y;
4603 let z_left = (z1 as i64 + ((z3 as i64 - z1 as i64) * dy as i64 / height as i64)) as u32;
4604 let z_right = (z2 as i64 + ((z3 as i64 - z2 as i64) * dy as i64 / height as i64)) as u32;
4605
4606 let (left_x, right_x, z_l, z_r) = if curx1 <= curx2 {
4607 (curx1 >> 16, curx2 >> 16, z_left, z_right)
4608 } else {
4609 (curx2 >> 16, curx1 >> 16, z_right, z_left)
4610 };
4611
4612 let span = right_x - left_x;
4613 for x in left_x..=right_x {
4614 if x < 0 || scanline_y < 0 || x >= width as i32 {
4615 continue;
4616 }
4617 let idx = (scanline_y as usize) * width + (x as usize);
4618 if idx >= zbuffer.len() {
4619 continue;
4620 }
4621
4622 let z = if span > 0 {
4623 let t = (x - left_x) as f32 / span as f32;
4624 (z_l as f32 + t * (z_r as f32 - z_l as f32)) as u32
4625 } else {
4626 z_l
4627 };
4628 let z_depth = crate::to_zdepth(z);
4629
4630 if z_depth < zbuffer[idx] {
4631 zbuffer[idx] = z_depth;
4632 let draw_color = fast_blend_rgb565(Rgb565::BLACK, color, alpha);
4633 let _ = fb.draw_iter([embedded_graphics_core::Pixel(
4634 Point::new(x, scanline_y),
4635 draw_color,
4636 )]);
4637 }
4638 }
4639
4640 curx1 -= invslope1;
4641 curx2 -= invslope2;
4642 }
4643}
4644
4645pub fn fill_triangle_zbuffered_textured_gouraud<
4646 D: DrawTarget<Color = embedded_graphics_core::pixelcolor::Rgb565>,
4647>(
4648 p1: nalgebra::Point2<i32>,
4649 p2: nalgebra::Point2<i32>,
4650 p3: nalgebra::Point2<i32>,
4651 z1: f32,
4652 z2: f32,
4653 z3: f32,
4654 w1: f32,
4655 w2: f32,
4656 w3: f32,
4657 uv1: [f32; 2],
4658 uv2: [f32; 2],
4659 uv3: [f32; 2],
4660 c1: embedded_graphics_core::pixelcolor::Rgb565,
4661 c2: embedded_graphics_core::pixelcolor::Rgb565,
4662 c3: embedded_graphics_core::pixelcolor::Rgb565,
4663 texture: &crate::texture::Texture,
4664 fb: &mut D,
4665 zbuffer: &mut [crate::ZDepth],
4666 width: usize,
4667 fog_config: Option<&FogConfig>,
4668 dither_config: Option<&DitherConfig>,
4669 texture_mapping: TextureMapping,
4670 stipple_mode: StippleMode,
4671 screen_tint: Option<ScreenTint>,
4672 palette_mode: PaletteMode,
4673) where
4674 <D as DrawTarget>::Error: Debug,
4675{
4676 let p1_eg = Point::new(p1.x, p1.y);
4677 let p2_eg = Point::new(p2.x, p2.y);
4678 let p3_eg = Point::new(p3.x, p3.y);
4679
4680 let z1_int = (z1 * 65536.0) as u32;
4681 let z2_int = (z2 * 65536.0) as u32;
4682 let z3_int = (z3 * 65536.0) as u32;
4683
4684 if p2_eg.y == p3_eg.y {
4685 fill_bottom_flat_triangle_zbuffered_textured_gouraud(
4686 p1_eg,
4687 p2_eg,
4688 p3_eg,
4689 z1_int,
4690 z2_int,
4691 z3_int,
4692 w1,
4693 w2,
4694 w3,
4695 uv1,
4696 uv2,
4697 uv3,
4698 c1,
4699 c2,
4700 c3,
4701 texture,
4702 fb,
4703 zbuffer,
4704 width,
4705 fog_config,
4706 dither_config,
4707 texture_mapping,
4708 stipple_mode,
4709 screen_tint,
4710 palette_mode,
4711 );
4712 } else if p1_eg.y == p2_eg.y {
4713 fill_top_flat_triangle_zbuffered_textured_gouraud(
4714 p1_eg,
4715 p2_eg,
4716 p3_eg,
4717 z1_int,
4718 z2_int,
4719 z3_int,
4720 w1,
4721 w2,
4722 w3,
4723 uv1,
4724 uv2,
4725 uv3,
4726 c1,
4727 c2,
4728 c3,
4729 texture,
4730 fb,
4731 zbuffer,
4732 width,
4733 fog_config,
4734 dither_config,
4735 texture_mapping,
4736 stipple_mode,
4737 screen_tint,
4738 palette_mode,
4739 );
4740 } else {
4741 let t = (p2_eg.y - p1_eg.y) as f32 / (p3_eg.y - p1_eg.y) as f32;
4742 let split_x = (p1_eg.x as f32 + t * (p3_eg.x - p1_eg.x) as f32) as i32;
4743 let p_split = Point::new(split_x, p2_eg.y);
4744
4745 let z_split = (z1_int as f64 + (z3_int as f64 - z1_int as f64) * t as f64) as u32;
4746 let w_split = w1 + t * (w3 - w1);
4747 let uv_split = [
4748 uv1[0] + t * (uv3[0] - uv1[0]),
4749 uv1[1] + t * (uv3[1] - uv1[1]),
4750 ];
4751 let color_split = interpolate_color(c1, c3, t);
4752
4753 fill_bottom_flat_triangle_zbuffered_textured_gouraud(
4754 p1_eg,
4755 p2_eg,
4756 p_split,
4757 z1_int,
4758 z2_int,
4759 z_split,
4760 w1,
4761 w2,
4762 w_split,
4763 uv1,
4764 uv2,
4765 uv_split,
4766 c1,
4767 c2,
4768 color_split,
4769 texture,
4770 fb,
4771 zbuffer,
4772 width,
4773 fog_config,
4774 dither_config,
4775 texture_mapping,
4776 stipple_mode,
4777 screen_tint,
4778 palette_mode,
4779 );
4780
4781 fill_top_flat_triangle_zbuffered_textured_gouraud(
4782 p2_eg,
4783 p_split,
4784 p3_eg,
4785 z2_int,
4786 z_split,
4787 z3_int,
4788 w2,
4789 w_split,
4790 w3,
4791 uv2,
4792 uv_split,
4793 uv3,
4794 c2,
4795 color_split,
4796 c3,
4797 texture,
4798 fb,
4799 zbuffer,
4800 width,
4801 fog_config,
4802 dither_config,
4803 texture_mapping,
4804 stipple_mode,
4805 screen_tint,
4806 palette_mode,
4807 );
4808 }
4809}
4810
4811fn fill_bottom_flat_triangle_zbuffered_textured_gouraud<
4812 D: DrawTarget<Color = embedded_graphics_core::pixelcolor::Rgb565>,
4813>(
4814 p1: Point,
4815 p2: Point,
4816 p3: Point,
4817 z1: u32,
4818 z2: u32,
4819 z3: u32,
4820 w1: f32,
4821 w2: f32,
4822 w3: f32,
4823 uv1: [f32; 2],
4824 uv2: [f32; 2],
4825 uv3: [f32; 2],
4826 c1: Rgb565,
4827 c2: Rgb565,
4828 c3: Rgb565,
4829 texture: &crate::texture::Texture,
4830 fb: &mut D,
4831 zbuffer: &mut [crate::ZDepth],
4832 width: usize,
4833 fog_config: Option<&FogConfig>,
4834 dither_config: Option<&DitherConfig>,
4835 texture_mapping: TextureMapping,
4836 stipple_mode: StippleMode,
4837 screen_tint: Option<ScreenTint>,
4838 palette_mode: PaletteMode,
4839) where
4840 <D as DrawTarget>::Error: Debug,
4841{
4842 let height = p2.y - p1.y;
4843 if height == 0 {
4844 return;
4845 }
4846
4847 let invslope1 = ((p2.x - p1.x) << 16) / height;
4848 let invslope2 = ((p3.x - p1.x) << 16) / height;
4849
4850 let mut curx1 = p1.x << 16;
4851 let mut curx2 = p1.x << 16;
4852
4853 for scanline_y in p1.y..=p2.y {
4854 let dy = scanline_y - p1.y;
4855 let t = dy as f32 / height as f32;
4856
4857 let z_left = if height > 0 {
4858 (z1 as i64 + ((z2 as i64 - z1 as i64) * dy as i64 / height as i64)) as u32
4859 } else {
4860 z1
4861 };
4862 let z_right = if height > 0 {
4863 (z1 as i64 + ((z3 as i64 - z1 as i64) * dy as i64 / height as i64)) as u32
4864 } else {
4865 z1
4866 };
4867
4868 let w_left = w1 + t * (w2 - w1);
4869 let w_right = w1 + t * (w3 - w1);
4870
4871 let uv_left = [
4872 uv1[0] + t * (uv2[0] - uv1[0]),
4873 uv1[1] + t * (uv2[1] - uv1[1]),
4874 ];
4875 let uv_right = [
4876 uv1[0] + t * (uv3[0] - uv1[0]),
4877 uv1[1] + t * (uv3[1] - uv1[1]),
4878 ];
4879
4880 let color_left = interpolate_color(c1, c2, t);
4881 let color_right = interpolate_color(c1, c3, t);
4882
4883 draw_scanline_zbuffered_textured_gouraud(
4884 curx1 >> 16,
4885 curx2 >> 16,
4886 scanline_y,
4887 z_left,
4888 z_right,
4889 w_left,
4890 w_right,
4891 uv_left,
4892 uv_right,
4893 color_left,
4894 color_right,
4895 texture,
4896 fb,
4897 zbuffer,
4898 width,
4899 fog_config,
4900 dither_config,
4901 texture_mapping,
4902 stipple_mode,
4903 screen_tint,
4904 palette_mode,
4905 );
4906
4907 curx1 += invslope1;
4908 curx2 += invslope2;
4909 }
4910}
4911
4912fn fill_top_flat_triangle_zbuffered_textured_gouraud<
4913 D: DrawTarget<Color = embedded_graphics_core::pixelcolor::Rgb565>,
4914>(
4915 p1: Point,
4916 p2: Point,
4917 p3: Point,
4918 z1: u32,
4919 z2: u32,
4920 z3: u32,
4921 w1: f32,
4922 w2: f32,
4923 w3: f32,
4924 uv1: [f32; 2],
4925 uv2: [f32; 2],
4926 uv3: [f32; 2],
4927 c1: Rgb565,
4928 c2: Rgb565,
4929 c3: Rgb565,
4930 texture: &crate::texture::Texture,
4931 fb: &mut D,
4932 zbuffer: &mut [crate::ZDepth],
4933 width: usize,
4934 fog_config: Option<&FogConfig>,
4935 dither_config: Option<&DitherConfig>,
4936 texture_mapping: TextureMapping,
4937 stipple_mode: StippleMode,
4938 screen_tint: Option<ScreenTint>,
4939 palette_mode: PaletteMode,
4940) where
4941 <D as DrawTarget>::Error: Debug,
4942{
4943 let height = p3.y - p1.y;
4944 if height == 0 {
4945 return;
4946 }
4947
4948 let invslope1 = ((p3.x - p1.x) << 16) / height;
4949 let invslope2 = ((p3.x - p2.x) << 16) / height;
4950
4951 let mut curx1 = p3.x << 16;
4952 let mut curx2 = p3.x << 16;
4953
4954 for scanline_y in (p1.y..=p3.y).rev() {
4955 let dy = p3.y - scanline_y;
4956 let t = dy as f32 / height as f32;
4957
4958 let z_left = if height > 0 {
4959 (z3 as i64 + ((z1 as i64 - z3 as i64) * dy as i64 / height as i64)) as u32
4960 } else {
4961 z3
4962 };
4963 let z_right = if height > 0 {
4964 (z3 as i64 + ((z2 as i64 - z3 as i64) * dy as i64 / height as i64)) as u32
4965 } else {
4966 z3
4967 };
4968
4969 let w_left = w3 + t * (w1 - w3);
4970 let w_right = w3 + t * (w2 - w3);
4971
4972 let uv_left = [
4973 uv3[0] + t * (uv1[0] - uv3[0]),
4974 uv3[1] + t * (uv1[1] - uv3[1]),
4975 ];
4976 let uv_right = [
4977 uv3[0] + t * (uv2[0] - uv3[0]),
4978 uv3[1] + t * (uv2[1] - uv3[1]),
4979 ];
4980
4981 let color_left = interpolate_color(c3, c1, t);
4982 let color_right = interpolate_color(c3, c2, t);
4983
4984 draw_scanline_zbuffered_textured_gouraud(
4985 curx1 >> 16,
4986 curx2 >> 16,
4987 scanline_y,
4988 z_left,
4989 z_right,
4990 w_left,
4991 w_right,
4992 uv_left,
4993 uv_right,
4994 color_left,
4995 color_right,
4996 texture,
4997 fb,
4998 zbuffer,
4999 width,
5000 fog_config,
5001 dither_config,
5002 texture_mapping,
5003 stipple_mode,
5004 screen_tint,
5005 palette_mode,
5006 );
5007
5008 curx1 -= invslope1;
5009 curx2 -= invslope2;
5010 }
5011}
5012
5013fn draw_scanline_zbuffered_textured_gouraud<
5014 D: DrawTarget<Color = embedded_graphics_core::pixelcolor::Rgb565>,
5015>(
5016 x1: i32,
5017 x2: i32,
5018 y: i32,
5019 z1: u32,
5020 z2: u32,
5021 w1: f32,
5022 w2: f32,
5023 uv1: [f32; 2],
5024 uv2: [f32; 2],
5025 color1: Rgb565,
5026 color2: Rgb565,
5027 texture: &crate::texture::Texture,
5028 fb: &mut D,
5029 zbuffer: &mut [crate::ZDepth],
5030 width: usize,
5031 fog_config: Option<&FogConfig>,
5032 dither_config: Option<&DitherConfig>,
5033 texture_mapping: TextureMapping,
5034 stipple_mode: StippleMode,
5035 screen_tint: Option<ScreenTint>,
5036 palette_mode: PaletteMode,
5037) where
5038 <D as DrawTarget>::Error: Debug,
5039{
5040 if y < 0 {
5041 return;
5042 }
5043 let height = zbuffer.len() / width;
5044 if y as usize >= height {
5045 return;
5046 }
5047
5048 let (
5049 left_x,
5050 right_x,
5051 z_left,
5052 z_right,
5053 w_left,
5054 w_right,
5055 uv_left,
5056 uv_right,
5057 color_left,
5058 color_right,
5059 ) = if x1 <= x2 {
5060 (x1, x2, z1, z2, w1, w2, uv1, uv2, color1, color2)
5061 } else {
5062 (x2, x1, z2, z1, w2, w1, uv2, uv1, color2, color1)
5063 };
5064
5065 let start_x = left_x.max(0);
5066 let end_x = right_x.min(width as i32 - 1);
5067 if start_x > end_x {
5068 return;
5069 }
5070
5071 let span = right_x - left_x;
5072 let inv_span = if span > 0 { 1.0 / span as f32 } else { 0.0 };
5073 let z_step = if span > 0 {
5074 (((z_right as i64 - z_left as i64) << 16) / span as i64) as i32
5075 } else {
5076 0
5077 };
5078
5079 let left_clip = start_x - left_x;
5080 let mut z_curr = ((z_left as i64) << 16) + (left_clip as i64 * z_step as i64);
5081 let mut zbuf_idx = y as usize * width + start_x as usize;
5082
5083 const SUB_SPAN_SIZE: i32 = 16;
5084 let mut span_x = start_x;
5085
5086 while span_x <= end_x {
5087 let next_span_x = (span_x + SUB_SPAN_SIZE).min(end_x + 1);
5088 let span_len = next_span_x - span_x;
5089
5090 let t_start = (span_x - left_x) as f32 * inv_span;
5091 let t_end = (next_span_x - 1 - left_x) as f32 * inv_span;
5092
5093 let [u_start, v_start] =
5094 interpolate_uv(t_start, w_left, w_right, uv_left, uv_right, texture_mapping);
5095 let [u_end, v_end] =
5096 interpolate_uv(t_end, w_left, w_right, uv_left, uv_right, texture_mapping);
5097
5098 let inv_sub = if span_len > 1 {
5099 1.0 / (span_len - 1) as f32
5100 } else {
5101 0.0
5102 };
5103
5104 let du = (u_end - u_start) * inv_sub;
5105 let dv = (v_end - v_start) * inv_sub;
5106
5107 let mut curr_u = u_start;
5108 let mut curr_v = v_start;
5109
5110 for x in span_x..next_span_x {
5111 if should_skip_stipple(x, y, stipple_mode) {
5112 z_curr += z_step as i64;
5113 zbuf_idx += 1;
5114 curr_u += du;
5115 curr_v += dv;
5116 continue;
5117 }
5118
5119 let z = (z_curr >> 16) as u32;
5120 z_curr += z_step as i64;
5121 let z_depth = crate::to_zdepth(z);
5122
5123 if z_depth < zbuffer[zbuf_idx].saturating_add(crate::DEPTH_EPSILON) {
5124 zbuffer[zbuf_idx] = z_depth;
5125
5126 let tx = (x - left_x) as f32 * inv_span;
5127 let c_interp = interpolate_color(color_left, color_right, tx);
5128 let tex_color = texture.sample(curr_u, curr_v);
5129
5130 let r = ((tex_color.r() as u16 * c_interp.r() as u16) / 31) as u8;
5131 let g = ((tex_color.g() as u16 * c_interp.g() as u16) / 63) as u8;
5132 let b_val = ((tex_color.b() as u16 * c_interp.b() as u16) / 31) as u8;
5133 let mut final_color = Rgb565::new(r, g, b_val);
5134
5135 if let Some(fog) = fog_config {
5136 final_color = fog.apply(final_color, z);
5137 }
5138 if let Some(dither) = dither_config {
5139 final_color = dither.apply(final_color, x, y);
5140 }
5141 if let Some(tint) = screen_tint {
5142 final_color = tint.apply(final_color);
5143 }
5144 final_color = palette_mode.apply(final_color);
5145
5146 fb.draw_iter([embedded_graphics_core::Pixel(Point::new(x, y), final_color)])
5147 .unwrap();
5148 }
5149 zbuf_idx += 1;
5150 curr_u += du;
5151 curr_v += dv;
5152 }
5153
5154 span_x = next_span_x;
5155 }
5156}
5157
5158#[cfg(test)]
5159mod tests {
5160 extern crate std;
5161 use super::*;
5162 use embedded_graphics_core::pixelcolor::Rgb565;
5163 use embedded_graphics_core::prelude::*;
5164 use nalgebra::Point2;
5165
5166 struct MockFramebuffer {
5168 pixels: std::vec::Vec<(i32, i32, Rgb565)>,
5169 }
5170
5171 impl MockFramebuffer {
5172 fn new() -> Self {
5173 Self {
5174 pixels: std::vec::Vec::new(),
5175 }
5176 }
5177
5178 fn contains_pixel(&self, x: i32, y: i32) -> bool {
5179 self.pixels.iter().any(|(px, py, _)| *px == x && *py == y)
5180 }
5181
5182 fn pixel_count(&self) -> usize {
5183 self.pixels.len()
5184 }
5185 }
5186
5187 impl DrawTarget for MockFramebuffer {
5188 type Color = Rgb565;
5189 type Error = core::convert::Infallible;
5190
5191 fn draw_iter<I>(&mut self, pixels: I) -> Result<(), Self::Error>
5192 where
5193 I: IntoIterator<Item = embedded_graphics_core::Pixel<Self::Color>>,
5194 {
5195 for pixel in pixels {
5196 self.pixels.push((pixel.0.x, pixel.0.y, pixel.1));
5197 }
5198 Ok(())
5199 }
5200 }
5201
5202 impl OriginDimensions for MockFramebuffer {
5203 fn size(&self) -> Size {
5204 Size::new(640, 480)
5205 }
5206 }
5207
5208 #[test]
5209 fn test_draw_point() {
5210 let mut fb = MockFramebuffer::new();
5211 let point = Point2::new(10, 20);
5212 let color = Rgb565::CSS_RED;
5213
5214 draw(DrawPrimitive::ColoredPoint(point, color), &mut fb);
5215
5216 assert_eq!(fb.pixel_count(), 1);
5217 assert!(fb.contains_pixel(10, 20));
5218 }
5219
5220 #[test]
5221 fn test_draw_line_horizontal() {
5222 let mut fb = MockFramebuffer::new();
5223 let p1 = Point2::new(10, 20);
5224 let p2 = Point2::new(20, 20);
5225 let color = Rgb565::CSS_GREEN;
5226
5227 draw(DrawPrimitive::Line([p1, p2], color), &mut fb);
5228
5229 assert!(fb.pixel_count() >= 10); assert!(fb.contains_pixel(10, 20));
5232 assert!(fb.contains_pixel(20, 20));
5233 }
5234
5235 #[test]
5236 fn test_draw_line_vertical() {
5237 let mut fb = MockFramebuffer::new();
5238 let p1 = Point2::new(10, 10);
5239 let p2 = Point2::new(10, 20);
5240 let color = Rgb565::CSS_BLUE;
5241
5242 draw(DrawPrimitive::Line([p1, p2], color), &mut fb);
5243
5244 assert!(fb.pixel_count() >= 10);
5246 assert!(fb.contains_pixel(10, 10));
5247 assert!(fb.contains_pixel(10, 20));
5248 }
5249
5250 #[test]
5251 fn test_draw_line_diagonal() {
5252 let mut fb = MockFramebuffer::new();
5253 let p1 = Point2::new(0, 0);
5254 let p2 = Point2::new(10, 10);
5255 let color = Rgb565::CSS_WHITE;
5256
5257 draw(DrawPrimitive::Line([p1, p2], color), &mut fb);
5258
5259 assert!(fb.pixel_count() >= 10);
5261 assert!(fb.contains_pixel(0, 0));
5262 assert!(fb.contains_pixel(10, 10));
5263 }
5264
5265 #[test]
5266 fn test_draw_triangle_flat_bottom() {
5267 let mut fb = MockFramebuffer::new();
5268 let vertices = [
5269 Point2::new(50, 10), Point2::new(30, 30), Point2::new(70, 30), ];
5273 let color = Rgb565::CSS_YELLOW;
5274
5275 draw(DrawPrimitive::ColoredTriangle(vertices, color), &mut fb);
5276
5277 let count = fb.pixel_count();
5279 assert!(count > 0, "Expected pixels to be drawn, got {}", count);
5280 assert!(fb.contains_pixel(50, 10));
5282 }
5283
5284 #[test]
5285 fn test_draw_triangle_flat_top() {
5286 let mut fb = MockFramebuffer::new();
5287 let vertices = [
5288 Point2::new(30, 10), Point2::new(70, 10), Point2::new(50, 30), ];
5292 let color = Rgb565::CSS_CYAN;
5293
5294 draw(DrawPrimitive::ColoredTriangle(vertices, color), &mut fb);
5295
5296 assert!(fb.pixel_count() > 20);
5298 assert!(fb.contains_pixel(50, 30));
5299 }
5300
5301 #[test]
5302 fn test_draw_triangle_general() {
5303 let mut fb = MockFramebuffer::new();
5304 let vertices = [
5305 Point2::new(50, 10),
5306 Point2::new(30, 30),
5307 Point2::new(80, 40),
5308 ];
5309 let color = Rgb565::CSS_MAGENTA;
5310
5311 draw(DrawPrimitive::ColoredTriangle(vertices, color), &mut fb);
5312
5313 assert!(fb.pixel_count() > 30);
5315 }
5316
5317 #[test]
5318 fn test_triangle_vertex_sorting() {
5319 let mut fb = MockFramebuffer::new();
5320 let vertices = [
5322 Point2::new(50, 30), Point2::new(30, 10), Point2::new(70, 20), ];
5326 let color = Rgb565::CSS_WHITE;
5327
5328 draw(DrawPrimitive::ColoredTriangle(vertices, color), &mut fb);
5330
5331 assert!(fb.pixel_count() > 10);
5332 }
5333
5334 #[test]
5335 fn test_draw_multiple_primitives() {
5336 let mut fb = MockFramebuffer::new();
5337
5338 draw(
5339 DrawPrimitive::ColoredPoint(Point2::new(5, 5), Rgb565::CSS_RED),
5340 &mut fb,
5341 );
5342 draw(
5343 DrawPrimitive::Line(
5344 [Point2::new(10, 10), Point2::new(20, 20)],
5345 Rgb565::CSS_GREEN,
5346 ),
5347 &mut fb,
5348 );
5349
5350 assert!(fb.pixel_count() > 11); assert!(fb.contains_pixel(5, 5));
5353 }
5354
5355 #[test]
5356 fn test_scanline_z_linear_interpolation_correctness() {
5357 let width = 100;
5358 let mut zbuffer = std::vec![crate::Z_MAX_VALUE; width * 10];
5359 let mut fb = MockFramebuffer::new();
5360
5361 let x1 = 10;
5362 let x2 = 90;
5363 let y = 5;
5364 let z1 = 10000u32;
5365 let z2 = 90000u32;
5366
5367 draw_scanline_zbuffered(
5368 x1,
5369 x2,
5370 y,
5371 z1,
5372 z2,
5373 Rgb565::CSS_BLUE,
5374 &mut fb,
5375 &mut zbuffer,
5376 width,
5377 None,
5378 None,
5379 );
5380
5381 let span = (x2 - x1) as f64;
5383 for x in x1..=x2 {
5384 let idx = y as usize * width + x as usize;
5385 let actual_z = zbuffer[idx];
5386 let expected_z = (z1 as f64 + (x - x1) as f64 * (z2 - z1) as f64 / span) as u32;
5387 let expected_z_depth = crate::to_zdepth(expected_z);
5388
5389 let diff = (actual_z as i64 - expected_z_depth as i64).abs();
5390 assert!(
5391 diff <= 2,
5392 "Z interpolation error at x={}: actual={}, expected={}, diff={}",
5393 x,
5394 actual_z,
5395 expected_z_depth,
5396 diff
5397 );
5398 }
5399 }
5400
5401 #[test]
5402 fn test_zbuffer_depth_occlusion_correctness() {
5403 let width = 50;
5404 let mut zbuffer = std::vec![crate::Z_MAX_VALUE; width * 5];
5405 let mut fb = MockFramebuffer::new();
5406
5407 draw_scanline_zbuffered(
5409 10,
5410 20,
5411 2,
5412 40000 << 16,
5413 40000 << 16,
5414 Rgb565::CSS_RED,
5415 &mut fb,
5416 &mut zbuffer,
5417 width,
5418 None,
5419 None,
5420 );
5421
5422 draw_scanline_zbuffered(
5424 10,
5425 20,
5426 2,
5427 20000 << 16,
5428 20000 << 16,
5429 Rgb565::CSS_GREEN,
5430 &mut fb,
5431 &mut zbuffer,
5432 width,
5433 None,
5434 None,
5435 );
5436
5437 for x in 10..=20 {
5439 let idx = 2 * width + x;
5440 assert_eq!(zbuffer[idx], crate::to_zdepth(20000 << 16));
5441 }
5442
5443 draw_scanline_zbuffered(
5445 10,
5446 20,
5447 2,
5448 60000 << 16,
5449 60000 << 16,
5450 Rgb565::CSS_BLUE,
5451 &mut fb,
5452 &mut zbuffer,
5453 width,
5454 None,
5455 None,
5456 );
5457
5458 for x in 10..=20 {
5460 let idx = 2 * width + x;
5461 assert_eq!(zbuffer[idx], crate::to_zdepth(20000 << 16));
5462 }
5463 }
5464
5465 #[test]
5466 fn test_sub_span_textured_scanline_correctness() {
5467 let width = 100;
5468 let mut zbuffer = std::vec![crate::Z_MAX_VALUE; width * 10];
5469 let mut fb = MockFramebuffer::new();
5470
5471 static TEX_DATA: [Rgb565; 4] = [
5473 Rgb565::CSS_RED,
5474 Rgb565::CSS_GREEN,
5475 Rgb565::CSS_BLUE,
5476 Rgb565::CSS_YELLOW,
5477 ];
5478 let texture = crate::texture::Texture::new(&TEX_DATA, 2, 2);
5479
5480 draw_scanline_zbuffered_textured(
5482 10,
5483 73,
5484 4,
5485 1000 << 16,
5486 1000 << 16,
5487 1.0,
5488 1.0,
5489 [0.0, 0.0],
5490 [1.0, 1.0],
5491 &texture,
5492 &mut fb,
5493 &mut zbuffer,
5494 width,
5495 None,
5496 None,
5497 TextureMapping::Affine,
5498 StippleMode::Off,
5499 None,
5500 PaletteMode::Off,
5501 );
5502
5503 for x in 10..=73 {
5505 let idx = 4 * width + x as usize;
5506 assert_eq!(zbuffer[idx], crate::to_zdepth(1000 << 16));
5507 }
5508 assert!(fb.pixel_count() >= 64);
5509 }
5510
5511 #[test]
5512 fn test_fast_blend_rgb565() {
5513 let bg = Rgb565::BLACK;
5514 let fg = Rgb565::WHITE;
5515 assert_eq!(fast_blend_rgb565(bg, fg, 0), bg);
5516 assert_eq!(fast_blend_rgb565(bg, fg, 255), fg);
5517
5518 let blended = fast_blend_rgb565(bg, fg, 128);
5519 assert!(blended.r() > 0 && blended.r() < 31);
5520 }
5521
5522 #[test]
5523 fn test_fast_blend_rgba8888() {
5524 let bg = [0, 0, 0, 255];
5525 let fg = [255, 255, 255, 128];
5526 let out = fast_blend_rgba8888(bg, fg);
5527 assert!(out[0] > 0 && out[0] < 255);
5528 }
5529
5530 #[test]
5531 fn test_fast_blend_rgba8888_to_rgb565() {
5532 let bg = Rgb565::BLACK;
5533 let fg = [255, 0, 0, 255];
5534 let out = fast_blend_rgba8888_to_rgb565(bg, fg);
5535 assert_eq!(out, Rgb565::CSS_RED);
5536 }
5537}
5538
5539#[cfg(feature = "fixed-raster")]
5540pub fn fill_triangle_fixed<D: DrawTarget<Color = embedded_graphics_core::pixelcolor::Rgb565>>(
5541 mut p1: Point,
5542 mut p2: Point,
5543 mut p3: Point,
5544 color: embedded_graphics_core::pixelcolor::Rgb565,
5545 fb: &mut D,
5546) where
5547 <D as DrawTarget>::Error: Debug,
5548{
5549 if p1.y > p2.y {
5550 core::mem::swap(&mut p1, &mut p2);
5551 }
5552 if p1.y > p3.y {
5553 core::mem::swap(&mut p1, &mut p3);
5554 }
5555 if p2.y > p3.y {
5556 core::mem::swap(&mut p2, &mut p3);
5557 }
5558
5559 if p1.y == p3.y {
5560 return;
5561 }
5562
5563 let bounds = fb.bounding_box();
5564 let min_x = bounds.top_left.x;
5565 let max_x = bounds.bottom_right().unwrap().x;
5566 let min_y = bounds.top_left.y;
5567 let max_y = bounds.bottom_right().unwrap().y;
5568
5569 let dy12 = p2.y - p1.y;
5570 let dy13 = p3.y - p1.y;
5571 let dy23 = p3.y - p2.y;
5572
5573 let dx12_step = if dy12 > 0 {
5574 ((p2.x - p1.x) << 16) / dy12
5575 } else {
5576 0
5577 };
5578 let dx13_step = if dy13 > 0 {
5579 ((p3.x - p1.x) << 16) / dy13
5580 } else {
5581 0
5582 };
5583 let dx23_step = if dy23 > 0 {
5584 ((p3.x - p2.x) << 16) / dy23
5585 } else {
5586 0
5587 };
5588
5589 let mut x13_fp = (p1.x << 16) + 0x8000;
5590 let mut x12_fp = x13_fp;
5591
5592 for y in p1.y..p2.y {
5593 if y >= min_y && y <= max_y {
5594 let xa = (x12_fp >> 16).clamp(min_x, max_x);
5595 let xb = (x13_fp >> 16).clamp(min_x, max_x);
5596 let (start_x, end_x) = if xa <= xb { (xa, xb) } else { (xb, xa) };
5597 for x in start_x..=end_x {
5598 let _ = fb.draw_iter(core::iter::once(embedded_graphics_core::Pixel(
5599 Point::new(x, y),
5600 color,
5601 )));
5602 }
5603 }
5604 x12_fp += dx12_step;
5605 x13_fp += dx13_step;
5606 }
5607
5608 let mut x23_fp = (p2.x << 16) + 0x8000;
5609 for y in p2.y..=p3.y {
5610 if y >= min_y && y <= max_y {
5611 let xa = (x23_fp >> 16).clamp(min_x, max_x);
5612 let xb = (x13_fp >> 16).clamp(min_x, max_x);
5613 let (start_x, end_x) = if xa <= xb { (xa, xb) } else { (xb, xa) };
5614 for x in start_x..=end_x {
5615 let _ = fb.draw_iter(core::iter::once(embedded_graphics_core::Pixel(
5616 Point::new(x, y),
5617 color,
5618 )));
5619 }
5620 }
5621 x23_fp += dx23_step;
5622 x13_fp += dx13_step;
5623 }
5624}
5625
5626#[cfg(feature = "fixed-raster")]
5627pub fn fill_triangle_zbuffered_fixed<
5628 D: DrawTarget<Color = embedded_graphics_core::pixelcolor::Rgb565>,
5629>(
5630 mut p1: Point,
5631 mut p2: Point,
5632 mut p3: Point,
5633 mut z1: u32,
5634 mut z2: u32,
5635 mut z3: u32,
5636 color: embedded_graphics_core::pixelcolor::Rgb565,
5637 fb: &mut D,
5638 zbuffer: &mut [crate::ZDepth],
5639 width: usize,
5640) where
5641 <D as DrawTarget>::Error: Debug,
5642{
5643 if p1.y > p2.y {
5644 core::mem::swap(&mut p1, &mut p2);
5645 core::mem::swap(&mut z1, &mut z2);
5646 }
5647 if p1.y > p3.y {
5648 core::mem::swap(&mut p1, &mut p3);
5649 core::mem::swap(&mut z1, &mut z3);
5650 }
5651 if p2.y > p3.y {
5652 core::mem::swap(&mut p2, &mut p3);
5653 core::mem::swap(&mut z2, &mut z3);
5654 }
5655
5656 if p1.y == p3.y {
5657 return;
5658 }
5659
5660 let bounds = fb.bounding_box();
5661 let min_x = bounds.top_left.x;
5662 let max_x = bounds.bottom_right().unwrap().x;
5663 let min_y = bounds.top_left.y;
5664 let max_y = bounds.bottom_right().unwrap().y;
5665
5666 let dy12 = p2.y - p1.y;
5667 let dy13 = p3.y - p1.y;
5668 let dy23 = p3.y - p2.y;
5669
5670 let dx12_step = if dy12 > 0 {
5671 ((p2.x - p1.x) << 16) / dy12
5672 } else {
5673 0
5674 };
5675 let dx13_step = if dy13 > 0 {
5676 ((p3.x - p1.x) << 16) / dy13
5677 } else {
5678 0
5679 };
5680 let dx23_step = if dy23 > 0 {
5681 ((p3.x - p2.x) << 16) / dy23
5682 } else {
5683 0
5684 };
5685
5686 let dz12_step = if dy12 > 0 {
5687 ((z2 as i64 - z1 as i64) << 16) / dy12 as i64
5688 } else {
5689 0
5690 };
5691 let dz13_step = if dy13 > 0 {
5692 ((z3 as i64 - z1 as i64) << 16) / dy13 as i64
5693 } else {
5694 0
5695 };
5696 let dz23_step = if dy23 > 0 {
5697 ((z3 as i64 - z2 as i64) << 16) / dy23 as i64
5698 } else {
5699 0
5700 };
5701
5702 let mut x13_fp = (p1.x << 16) + 0x8000;
5703 let mut x12_fp = x13_fp;
5704 let mut z13_fp = (z1 as i64) << 16;
5705 let mut z12_fp = z13_fp;
5706
5707 for y in p1.y..p2.y {
5708 if y >= min_y && y <= max_y {
5709 let xa = x12_fp >> 16;
5710 let xb = x13_fp >> 16;
5711 let (start_x, end_x, za_fp, zb_fp) = if xa <= xb {
5712 (xa, xb, z12_fp, z13_fp)
5713 } else {
5714 (xb, xa, z13_fp, z12_fp)
5715 };
5716 let span_dx = end_x - start_x;
5717 let dz_span_step = if span_dx > 0 {
5718 (zb_fp - za_fp) / span_dx as i64
5719 } else {
5720 0
5721 };
5722 let mut z_curr_fp = za_fp;
5723
5724 for x in start_x..=end_x {
5725 if x >= min_x && x <= max_x {
5726 let z_val = (z_curr_fp >> 16) as u32;
5727 let zdepth = crate::to_zdepth(z_val);
5728 let idx = (y as usize) * width + (x as usize);
5729 if idx < zbuffer.len() && zdepth < zbuffer[idx] {
5730 zbuffer[idx] = zdepth;
5731 let _ = fb.draw_iter(core::iter::once(embedded_graphics_core::Pixel(
5732 Point::new(x, y),
5733 color,
5734 )));
5735 }
5736 }
5737 z_curr_fp += dz_span_step;
5738 }
5739 }
5740 x12_fp += dx12_step;
5741 x13_fp += dx13_step;
5742 z12_fp += dz12_step;
5743 z13_fp += dz13_step;
5744 }
5745
5746 let mut x23_fp = ((p1.x << 16) + 0x8000) + dx12_step * dy12;
5747 let mut z23_fp = ((z1 as i64) << 16) + dz12_step * dy12 as i64;
5748 for y in p2.y..=p3.y {
5749 if y >= min_y && y <= max_y {
5750 let xa = x23_fp >> 16;
5751 let xb = x13_fp >> 16;
5752 let (start_x, end_x, za_fp, zb_fp) = if xa <= xb {
5753 (xa, xb, z23_fp, z13_fp)
5754 } else {
5755 (xb, xa, z13_fp, z23_fp)
5756 };
5757 let span_dx = end_x - start_x;
5758 let dz_span_step = if span_dx > 0 {
5759 (zb_fp - za_fp) / span_dx as i64
5760 } else {
5761 0
5762 };
5763 let mut z_curr_fp = za_fp;
5764
5765 for x in start_x..=end_x {
5766 if x >= min_x && x <= max_x {
5767 let z_val = (z_curr_fp >> 16) as u32;
5768 let zdepth = crate::to_zdepth(z_val);
5769 let idx = (y as usize) * width + (x as usize);
5770 if idx < zbuffer.len() && zdepth < zbuffer[idx] {
5771 zbuffer[idx] = zdepth;
5772 let _ = fb.draw_iter(core::iter::once(embedded_graphics_core::Pixel(
5773 Point::new(x, y),
5774 color,
5775 )));
5776 }
5777 }
5778 z_curr_fp += dz_span_step;
5779 }
5780 }
5781 x23_fp += dx23_step;
5782 x13_fp += dx13_step;
5783 z23_fp += dz23_step;
5784 z13_fp += dz13_step;
5785 }
5786}