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