1use crate::geometry::RectU16;
7use crate::kurbo::{Affine, BezPath, PathEl};
8use crate::strip::Strip;
9use crate::strip_generator::{GenerationMode, StripGenerator, StripStorage};
10use crate::tile::Tile;
11use crate::util::{Clear, Pool, normalized_mul_u8x16, strip_bbox};
12use alloc::vec;
13use alloc::vec::Vec;
14use core::ops::Range;
15use fearless_simd::{Level, Simd, SimdBase, dispatch, u8x16};
16use peniko::Fill;
17
18#[derive(Debug)]
19struct ClipData {
20 alpha_start: u32,
21 strip_start: u32,
22
23 bbox: RectU16,
27}
28
29impl ClipData {
30 fn to_path_data_ref<'a>(&self, storage: &'a StripStorage) -> PathDataRef<'a> {
31 PathDataRef {
32 strips: storage
33 .strips
34 .get(self.strip_start as usize..)
35 .unwrap_or(&[]),
36 alphas: storage
37 .alphas
38 .get(self.alpha_start as usize..)
39 .unwrap_or(&[]),
40 bbox: self.bbox,
41 }
42 }
43}
44
45#[derive(Debug)]
47pub struct ClipContext {
48 storage: StripStorage,
49 temp_storage: StripStorage,
50 clip_stack: Vec<ClipData>,
51}
52
53impl Default for ClipContext {
54 fn default() -> Self {
55 Self::new()
56 }
57}
58
59impl ClipContext {
60 #[inline]
62 pub fn new() -> Self {
63 let mut main_storage = StripStorage::default();
64 main_storage.set_generation_mode(GenerationMode::Append);
65 Self {
66 storage: main_storage,
67 temp_storage: StripStorage::default(),
68 clip_stack: vec![],
69 }
70 }
71
72 #[inline]
74 pub fn reset(&mut self) {
75 self.clip_stack.clear();
76 self.storage.clear();
77 self.temp_storage.clear();
78 }
79
80 #[inline]
82 pub fn get(&self) -> Option<PathDataRef<'_>> {
83 self.clip_stack
84 .last()
85 .map(|c| c.to_path_data_ref(&self.storage))
86 }
87
88 #[inline]
90 pub fn push_clip(
91 &mut self,
92 clip_path: impl IntoIterator<Item = PathEl>,
93 strip_generator: &mut StripGenerator,
94 fill_rule: Fill,
95 transform: Affine,
96 aliasing_threshold: Option<u8>,
97 ) {
98 self.temp_storage.clear();
99
100 let alpha_start = self.storage.alphas.len() as u32;
101 let strip_start = self.storage.strips.len() as u32;
102
103 let existing_clip = self
104 .clip_stack
105 .last()
106 .map(|c| c.to_path_data_ref(&self.storage));
107
108 strip_generator.generate_filled_path(
109 clip_path,
110 fill_rule,
111 transform,
112 aliasing_threshold,
113 &mut self.temp_storage,
114 existing_clip,
115 );
116
117 let bbox = strip_bbox(&self.temp_storage.strips).unwrap_or(RectU16::ZERO);
118 let clip_data = ClipData {
119 alpha_start,
120 strip_start,
121 bbox,
122 };
123
124 self.storage.extend(&self.temp_storage);
125 self.clip_stack.push(clip_data);
126 }
127
128 #[inline]
130 pub fn pop_clip(&mut self) {
131 let data = self.clip_stack.pop().expect("clip stack underflowed");
132 self.storage.strips.truncate(data.strip_start as usize);
133 self.storage.alphas.truncate(data.alpha_start as usize);
134 }
135}
136
137#[derive(Debug)]
139struct RawClip {
140 path: Range<usize>,
142 fill_rule: Fill,
143 transform: Affine,
144 aliasing_threshold: Option<u8>,
145}
146
147#[derive(Debug)]
149struct ClipFrame {
150 parent_context: ClipContext,
152 source_shift: Affine,
154 clip_revision: u64,
156}
157
158#[derive(Debug)]
169pub struct ClipState {
170 context: ClipContext,
172 context_pool: Pool<ClipContext>,
174 path_elements: Vec<PathEl>,
176 raw_clips: Vec<RawClip>,
178 frames: Vec<ClipFrame>,
180 revision: u64,
182}
183
184impl Clear for ClipContext {
185 fn clear(&mut self) {
186 self.reset();
187 }
188}
189
190impl Default for ClipState {
191 fn default() -> Self {
192 Self::new()
193 }
194}
195
196impl ClipState {
197 pub fn new() -> Self {
199 Self {
200 context: ClipContext::new(),
201 context_pool: Pool::default(),
202 path_elements: Vec::new(),
203 raw_clips: Vec::new(),
204 frames: Vec::new(),
205 revision: 0,
206 }
207 }
208
209 pub fn get(&self) -> Option<PathDataRef<'_>> {
211 self.context.get()
212 }
213
214 pub fn push_root_viewport(
216 &mut self,
217 source_shift: (u16, u16),
218 strip_generator: &mut StripGenerator,
219 ) {
220 let parent_context = core::mem::replace(&mut self.context, self.context_pool.take());
221 let source_shift =
222 Affine::translate((f64::from(source_shift.0), f64::from(source_shift.1)))
223 * self.active_shift();
224 self.frames.push(ClipFrame {
225 parent_context,
226 source_shift,
227 clip_revision: self.revision,
228 });
229 self.rebuild_context(strip_generator);
230 }
231
232 pub fn pop_root_viewport(&mut self, strip_generator: &mut StripGenerator) {
234 let frame = self.frames.pop().expect("filter clip stack underflow");
235 let filter_context = core::mem::replace(&mut self.context, frame.parent_context);
236 self.context_pool.submit(filter_context);
237 if self.revision == frame.clip_revision {
238 } else {
240 self.rebuild_context(strip_generator);
241 }
242 }
243
244 pub fn push_clip(
246 &mut self,
247 path: &BezPath,
248 strip_generator: &mut StripGenerator,
249 fill_rule: Fill,
250 transform: Affine,
251 aliasing_threshold: Option<u8>,
252 ) {
253 let path_start = self.path_elements.len();
254 self.path_elements.extend(path.iter());
255 let path = path_start..self.path_elements.len();
256 let clip_transform = self.active_shift() * transform;
257
258 self.context.push_clip(
259 self.path_elements[path.clone()].iter().copied(),
260 strip_generator,
261 fill_rule,
262 clip_transform,
263 aliasing_threshold,
264 );
265 self.raw_clips.push(RawClip {
266 path,
267 fill_rule,
268 transform,
269 aliasing_threshold,
270 });
271 self.revision = self.revision.wrapping_add(1);
272 }
273
274 pub fn pop_clip(&mut self) {
276 let raw_clip = self.raw_clips.pop().expect("clip stack underflowed");
277 self.path_elements.truncate(raw_clip.path.start);
278 self.context.pop_clip();
279 self.revision = self.revision.wrapping_add(1);
280 }
281
282 pub fn reset(&mut self) {
284 self.context.reset();
285 for frame in self.frames.drain(..) {
286 self.context_pool.submit(frame.parent_context);
287 }
288 self.path_elements.clear();
289 self.raw_clips.clear();
290 self.revision = 0;
291 }
292
293 fn active_shift(&self) -> Affine {
294 self.frames
295 .last()
296 .map_or(Affine::IDENTITY, |frame| frame.source_shift)
297 }
298
299 fn rebuild_context(&mut self, strip_generator: &mut StripGenerator) {
300 self.context.reset();
301 let active_shift = self.active_shift();
302 for raw_clip in &self.raw_clips {
303 self.context.push_clip(
304 self.path_elements[raw_clip.path.clone()].iter().copied(),
305 strip_generator,
306 raw_clip.fill_rule,
307 active_shift * raw_clip.transform,
308 raw_clip.aliasing_threshold,
309 );
310 }
311 }
312}
313
314#[derive(Clone, Copy, Debug)]
316pub struct PathDataRef<'a> {
317 pub strips: &'a [Strip],
319 pub alphas: &'a [u8],
321 pub bbox: RectU16,
325}
326
327pub fn intersect(
331 level: Level,
332 path_1: PathDataRef<'_>,
333 path_2: PathDataRef<'_>,
334 target: &mut StripStorage,
335) {
336 dispatch!(level, simd => intersect_impl(simd, path_1, path_2, target));
337}
338
339#[inline(always)]
351fn intersect_impl<S: Simd>(
352 simd: S,
353 path_1: PathDataRef<'_>,
354 path_2: PathDataRef<'_>,
355 target: &mut StripStorage,
356) {
357 if path_1.strips.is_empty() || path_2.strips.is_empty() {
359 return;
360 }
361
362 let path_1_start_y = path_1.strips[0].strip_y();
365 let path_2_start_y = path_2.strips[0].strip_y();
366 let mut cur_y = path_1_start_y.max(path_2_start_y);
367 let end_y = path_1.strips[path_1.strips.len() - 1]
368 .strip_y()
369 .min(path_2.strips[path_2.strips.len() - 1].strip_y());
370
371 let mut path_1_idx = 0;
372 let mut path_2_idx = 0;
373
374 if path_1_start_y < cur_y {
379 path_1_idx = first_strip_at_or_after(path_1.strips, cur_y);
380 } else if path_2_start_y < cur_y {
381 path_2_idx = first_strip_at_or_after(path_2.strips, cur_y);
382 }
383
384 let mut strip_state = None;
385
386 while cur_y <= end_y {
388 let mut p1_iter = RowIterator::new(path_1, &mut path_1_idx, cur_y);
391 let mut p2_iter = RowIterator::new(path_2, &mut path_2_idx, cur_y);
392
393 let mut p1_region = p1_iter.next();
394 let mut p2_region = p2_iter.next();
395
396 while let (Some(region_1), Some(region_2)) = (p1_region, p2_region) {
402 match region_1.overlap_relationship(®ion_2) {
403 OverlapRelationship::Advance(advance) => {
406 match advance {
407 Advance::Left => p1_region = p1_iter.next(),
408 Advance::Right => p2_region = p2_iter.next(),
409 };
410
411 continue;
412 }
413 OverlapRelationship::Overlap(overlap) => {
415 match (region_1, region_2) {
416 (Region::Fill(_), Region::Fill(_)) => {
421 flush_strip(&mut strip_state, &mut target.strips, cur_y);
422 start_strip(&mut strip_state, &target.alphas, overlap.end, true);
423 }
424 (Region::Strip(s), Region::Fill(_))
426 | (Region::Fill(_), Region::Strip(s)) => {
427 if should_create_new_strip(&strip_state, &target.alphas, overlap.start)
429 {
430 flush_strip(&mut strip_state, &mut target.strips, cur_y);
431 start_strip(&mut strip_state, &target.alphas, overlap.start, false);
432 }
433
434 let s_alphas = &s.alphas[(overlap.start - s.start) as usize * 4..]
435 [..overlap.width() as usize * 4];
436 target.alphas.extend_from_slice(s_alphas);
437 }
438 (Region::Strip(s_region_1), Region::Strip(s_region_2)) => {
440 if should_create_new_strip(&strip_state, &target.alphas, overlap.start)
442 {
443 flush_strip(&mut strip_state, &mut target.strips, cur_y);
444 start_strip(&mut strip_state, &target.alphas, overlap.start, false);
445 }
446
447 let num_blocks = overlap.width() / Tile::HEIGHT;
448
449 let s1_alphas = s_region_1.alphas
451 [(overlap.start - s_region_1.start) as usize * 4..]
452 .chunks_exact(16)
453 .take(num_blocks as usize);
454 let s2_alphas = s_region_2.alphas
455 [(overlap.start - s_region_2.start) as usize * 4..]
456 .chunks_exact(16)
457 .take(num_blocks as usize);
458
459 for (s1_alpha, s2_alpha) in s1_alphas.zip(s2_alphas) {
460 let s1 = u8x16::from_slice(simd, s1_alpha);
461 let s2 = u8x16::from_slice(simd, s2_alpha);
462
463 let res = simd.narrow_u16x16(normalized_mul_u8x16(s1, s2));
465 target.alphas.extend(res.as_slice());
466 }
467 }
468 }
469
470 match overlap.advance {
472 Advance::Left => p1_region = p1_iter.next(),
473 Advance::Right => p2_region = p2_iter.next(),
474 };
475 }
476 }
477 }
478
479 flush_strip(&mut strip_state, &mut target.strips, cur_y);
481 cur_y += 1;
482 }
483
484 if !target.strips.is_empty() {
486 target.strips.push(Strip::sentinel(
487 end_y * Tile::HEIGHT,
488 target.alphas.len() as u32,
489 ));
490 }
491}
492
493#[inline(always)]
494fn first_strip_at_or_after(strips: &[Strip], strip_y: u16) -> usize {
495 strips.partition_point(|strip| strip.strip_y() < strip_y)
498}
499
500struct Overlap {
502 start: u16,
504 end: u16,
506 advance: Advance,
508}
509
510impl Overlap {
511 fn width(&self) -> u16 {
512 self.end - self.start
513 }
514}
515
516enum Advance {
517 Left,
518 Right,
519}
520
521enum OverlapRelationship {
523 Advance(Advance),
525 Overlap(Overlap),
527}
528
529#[derive(Debug, Clone, Copy)]
530struct FillRegion {
531 start: u16,
532 width: u16,
533}
534
535#[derive(Debug, Clone, Copy)]
536struct StripRegion<'a> {
537 start: u16,
538 width: u16,
539 alphas: &'a [u8],
540}
541
542#[derive(Debug, Clone, Copy)]
543enum Region<'a> {
544 Fill(FillRegion),
545 Strip(StripRegion<'a>),
546}
547
548impl Region<'_> {
549 #[inline(always)]
550 fn start(&self) -> u16 {
551 match self {
552 Region::Fill(fill) => fill.start,
553 Region::Strip(strip) => strip.start,
554 }
555 }
556
557 #[inline(always)]
558 fn width(&self) -> u16 {
559 match self {
560 Region::Fill(fill) => fill.width,
561 Region::Strip(strip) => strip.width,
562 }
563 }
564
565 #[inline(always)]
566 fn end(&self) -> u16 {
567 self.start() + self.width()
568 }
569
570 fn overlap_relationship(&self, other: &Region<'_>) -> OverlapRelationship {
571 if self.end() <= other.start() {
572 OverlapRelationship::Advance(Advance::Left)
573 } else if self.start() >= other.end() {
574 OverlapRelationship::Advance(Advance::Right)
575 } else {
576 let start = self.start().max(other.start());
577 let end = self.end().min(other.end());
578
579 let shift = if self.end() <= other.end() {
580 Advance::Left
581 } else {
582 Advance::Right
583 };
584
585 OverlapRelationship::Overlap(Overlap {
586 advance: shift,
587 start,
588 end,
589 })
590 }
591 }
592}
593
594struct RowIterator<'a> {
596 input: PathDataRef<'a>,
598 strip_y: u16,
600 cur_idx: &'a mut usize,
602 on_strip: bool,
607}
608
609impl<'a> RowIterator<'a> {
610 fn new(input: PathDataRef<'a>, cur_idx: &'a mut usize, strip_y: u16) -> Self {
611 while input.strips[*cur_idx].strip_y() < strip_y {
613 *cur_idx += 1;
614 }
615
616 Self {
617 input,
618 cur_idx,
619 strip_y,
620 on_strip: true,
621 }
622 }
623
624 #[inline(always)]
625 fn cur_strip(&self) -> &Strip {
626 &self.input.strips[*self.cur_idx]
627 }
628
629 #[inline(always)]
630 fn next_strip(&self) -> &Strip {
631 &self.input.strips[*self.cur_idx + 1]
632 }
633
634 #[inline(always)]
635 fn cur_strip_width(&self) -> u16 {
636 let cur = self.cur_strip();
637 let next = self.next_strip();
638 ((next.alpha_idx() - cur.alpha_idx()) / Tile::HEIGHT as u32) as u16
639 }
640
641 #[inline(always)]
642 fn cur_strip_alphas(&self) -> &'a [u8] {
643 let cur = self.cur_strip();
644 let next = self.next_strip();
645 &self.input.alphas[cur.alpha_idx() as usize..next.alpha_idx() as usize]
646 }
647
648 fn cur_strip_fill_area(&self) -> Option<FillRegion> {
649 let next = self.next_strip();
650
651 if next.fill_gap() {
654 let cur = self.cur_strip();
655 let x = cur.x + self.cur_strip_width();
656 let width = next.x - x;
657
658 (width > 0).then_some(FillRegion { start: x, width })
659 } else {
660 None
661 }
662 }
663}
664
665impl<'a> Iterator for RowIterator<'a> {
666 type Item = Region<'a>;
667
668 #[inline(always)]
669 fn next(&mut self) -> Option<Self::Item> {
670 loop {
671 if !self.on_strip {
673 self.on_strip = true;
675
676 if let Some(fill_area) = self.cur_strip_fill_area() {
680 *self.cur_idx += 1;
681
682 return Some(Region::Fill(fill_area));
683 } else {
684 *self.cur_idx += 1;
685 }
686 }
687
688 self.on_strip = false;
691
692 if self.cur_strip().is_sentinel() || self.cur_strip().strip_y() != self.strip_y {
694 return None;
695 }
696
697 let x = self.cur_strip().x;
699 let width = self.cur_strip_width();
700
701 if width == 0 {
706 debug_assert!(
707 self.next_strip().is_sentinel() || self.next_strip().strip_y() != self.strip_y,
708 "zero-width strips must only appear at the end of a row"
709 );
710
711 continue;
712 }
713
714 let alphas = self.cur_strip_alphas();
715
716 return Some(Region::Strip(StripRegion {
717 start: x,
718 width,
719 alphas,
720 }));
721 }
722 }
723}
724
725struct StripState {
727 x: u16,
728 alpha_idx: u32,
729 fill_gap: bool,
730}
731
732fn flush_strip(strip_state: &mut Option<StripState>, strips: &mut Vec<Strip>, cur_y: u16) {
733 if let Some(state) = core::mem::take(strip_state) {
734 strips.push(Strip::new(
735 state.x,
736 cur_y * Tile::HEIGHT,
737 state.alpha_idx,
738 state.fill_gap,
739 ));
740 }
741}
742
743#[inline(always)]
744fn start_strip(strip_data: &mut Option<StripState>, alphas: &[u8], x: u16, fill_gap: bool) {
745 *strip_data = Some(StripState {
746 x,
747 alpha_idx: alphas.len() as u32,
748 fill_gap,
749 });
750}
751
752fn should_create_new_strip(
753 strip_state: &Option<StripState>,
754 alphas: &[u8],
755 overlap_start: u16,
756) -> bool {
757 strip_state.as_ref().is_none_or(|state| {
759 let width = ((alphas.len() as u32 - state.alpha_idx) / Tile::HEIGHT as u32) as u16;
760 let strip_end = state.x + width;
761
762 strip_end < overlap_start - 1
763 })
764}
765
766#[cfg(test)]
767mod tests {
768 use crate::clip::{PathDataRef, Region, RowIterator, first_strip_at_or_after, intersect};
769 use crate::geometry::RectU16;
770 use crate::strip::Strip;
771 use crate::strip_generator::StripStorage;
772 use crate::tile::Tile;
773 use fearless_simd::Level;
774 use std::vec;
775
776 #[test]
777 fn intersect_partly_overlapping_strips() {
778 let path_1 = StripBuilder::new().add_strip(0, 0, 32, false).finish();
779
780 let path_2 = StripBuilder::new().add_strip(8, 0, 44, false).finish();
781
782 let expected = StripBuilder::new().add_strip(8, 0, 32, false).finish();
783
784 run_test(expected, path_1, path_2);
785 }
786
787 #[test]
788 fn intersect_multiple_overlapping_strips() {
789 let path_1 = StripBuilder::new()
790 .add_strip(0, 1, 4, false)
791 .add_strip(12, 1, 20, true)
792 .add_strip(28, 1, 32, false)
793 .add_strip(44, 1, 52, true)
794 .finish();
795
796 let path_2 = StripBuilder::new()
797 .add_strip(4, 1, 8, false)
798 .add_strip(16, 1, 20, true)
799 .add_strip(24, 1, 28, false)
800 .add_strip(32, 1, 36, false)
801 .add_strip(44, 1, 48, true)
802 .finish();
803
804 let expected = StripBuilder::new()
805 .add_strip(4, 1, 8, false)
806 .add_strip(12, 1, 20, true)
807 .add_strip(32, 1, 36, false)
808 .add_strip(44, 1, 48, true)
809 .finish();
810
811 run_test(expected, path_1, path_2);
812 }
813
814 #[test]
815 fn multiple_rows() {
816 let path_1 = StripBuilder::new()
817 .add_strip(0, 0, 4, false)
818 .add_strip(16, 0, 20, true)
819 .add_strip(4, 1, 8, false)
820 .add_strip(12, 1, 24, true)
821 .add_strip(4, 2, 8, false)
822 .add_strip(16, 2, 32, true)
823 .finish();
824
825 let path_2 = StripBuilder::new()
826 .add_strip(0, 2, 4, false)
827 .add_strip(16, 2, 24, true)
828 .add_strip(8, 3, 12, false)
829 .add_strip(16, 3, 28, true)
830 .finish();
831
832 let expected = StripBuilder::new()
833 .add_strip(4, 2, 8, false)
834 .add_strip(16, 2, 24, true)
835 .finish();
836
837 run_test(expected, path_1, path_2);
838 }
839
840 #[test]
841 fn alpha_buffer_correct_width() {
842 let path_1 = StripBuilder::new()
843 .add_strip(0, 0, 4, false)
844 .add_strip(0, 1, 12, false)
845 .finish();
846
847 let path_2 = StripBuilder::new()
848 .add_strip(4, 0, 8, false)
849 .add_strip(0, 1, 4, false)
850 .add_strip(12, 1, 16, true)
851 .finish();
852
853 let expected = StripBuilder::new().add_strip(0, 1, 12, false).finish();
854
855 run_test(expected, path_1, path_2);
856 }
857
858 #[test]
859 fn first_strip_at_or_after_returns_first_matching_strip_y() {
860 let path = StripBuilder::new()
861 .add_strip(0, 0, 4, false)
862 .add_strip(0, 2, 4, false)
863 .add_strip(8, 2, 12, false)
864 .add_strip(16, 2, 20, false)
865 .add_strip(0, 4, 4, false)
866 .add_strip(8, 4, 12, false)
867 .add_strip(0, 6, 4, false)
868 .finish();
869
870 assert_eq!(first_strip_at_or_after(&path.strips, 0), 0);
871 assert_eq!(first_strip_at_or_after(&path.strips, 2), 1);
872 assert_eq!(first_strip_at_or_after(&path.strips, 3), 4);
873 assert_eq!(first_strip_at_or_after(&path.strips, 4), 4);
874 assert_eq!(first_strip_at_or_after(&path.strips, 5), 6);
875 assert_eq!(first_strip_at_or_after(&path.strips, 6), 6);
876 assert_eq!(first_strip_at_or_after(&path.strips, 7), path.strips.len());
877 }
878
879 #[test]
880 fn row_iterator_abort_next_line() {
881 let path_1 = StripBuilder::new()
882 .add_strip(0, 0, 4, false)
883 .add_strip(0, 1, 4, false)
884 .finish();
885
886 let path_ref = PathDataRef {
887 strips: &path_1.strips,
888 alphas: &path_1.alphas,
889 bbox: RectU16::new(0, 0, u16::MAX, u16::MAX),
890 };
891
892 let mut idx = 0;
893 let mut iter = RowIterator::new(path_ref, &mut idx, 0);
894
895 assert!(iter.next().is_some());
896 assert!(iter.next().is_none());
897 }
898
899 #[test]
900 fn row_iterator_row_end_fill_gap() {
901 let path = StripBuilder::new()
902 .add_strip(0, 0, Tile::WIDTH, false)
903 .finish_with_fill_gap_row_end(16);
904 let path_ref = path_ref(&path);
905
906 let mut idx = 0;
907 let mut iter = RowIterator::new(path_ref, &mut idx, 0);
908
909 assert_strip_region(iter.next(), 0, Tile::WIDTH);
910 assert_fill_region(iter.next(), Tile::WIDTH, 16 - Tile::WIDTH);
911 assert!(iter.next().is_none());
912 }
913
914 #[test]
915 fn intersect_strip_with_row_end_fill_gap() {
916 let path_1 = StripBuilder::new()
917 .add_strip(0, 0, Tile::WIDTH, false)
918 .finish_with_fill_gap_row_end(16);
919 let path_2 = StripBuilder::new().add_strip(8, 0, 12, false).finish();
920 let expected = StripBuilder::new().add_strip(8, 0, 12, false).finish();
921
922 run_test(expected, path_1, path_2);
923 }
924
925 #[test]
926 fn intersect_two_row_end_fill_gaps() {
927 let path_1 = StripBuilder::new()
928 .add_strip(0, 0, 8, false)
929 .finish_with_fill_gap_row_end(16);
930 let path_2 = StripBuilder::new()
931 .add_strip(4, 0, 12, false)
932 .finish_with_fill_gap_row_end(20);
933 let expected = StripBuilder::new()
934 .add_strip(4, 0, 12, false)
935 .finish_with_fill_gap_row_end(16);
936
937 run_test(expected, path_1, path_2);
938 }
939
940 #[test]
941 fn row_iterator_fill_gap_stops_at_row_boundary() {
942 let path = StripBuilder::new()
943 .add_strip(0, 0, 4, false)
944 .add_row_end(0, 16, true)
945 .add_strip(0, 1, 4, false)
946 .finish();
947
948 let path_ref = path_ref(&path);
949 let mut idx = 0;
950 let mut iter = RowIterator::new(path_ref, &mut idx, 0);
951
952 assert_strip_region(iter.next(), 0, 4);
953 assert_fill_region(iter.next(), 4, 12);
954 assert!(iter.next().is_none());
955
956 let mut iter = RowIterator::new(path_ref, &mut idx, 1);
957
958 assert_strip_region(iter.next(), 0, Tile::WIDTH);
959 assert!(iter.next().is_none());
960 }
961
962 #[test]
963 fn row_iterator_adjacent_unmerged_strips_no_fill() {
964 let path = StripBuilder::new()
965 .add_strip(0, 0, 4, false)
966 .add_strip(4, 0, 8, false)
967 .finish();
968 let path_ref = path_ref(&path);
969
970 let mut idx = 0;
971 let mut iter = RowIterator::new(path_ref, &mut idx, 0);
972
973 assert_strip_region(iter.next(), 0, 4);
974 assert_strip_region(iter.next(), 4, 4);
975 assert!(iter.next().is_none());
976 }
977
978 #[test]
979 fn row_iterator_adjacent_unmerged_strips_with_fill_gap() {
980 let path = StripBuilder::new()
981 .add_strip(0, 0, 4, false)
982 .add_strip(4, 0, 8, true)
983 .finish();
984 let path_ref = path_ref(&path);
985
986 let mut idx = 0;
987 let mut iter = RowIterator::new(path_ref, &mut idx, 0);
988
989 assert_strip_region(iter.next(), 0, 4);
990 assert_strip_region(iter.next(), 4, 4);
991 assert!(iter.next().is_none());
992 }
993
994 #[test]
995 fn intersect_adjacent_unmerged_strips() {
996 let path = StripBuilder::new()
997 .add_strip(0, 0, 4, false)
998 .add_strip(4, 0, 8, true)
999 .finish();
1000 let cover = StripBuilder::new().add_strip(0, 0, 8, false).finish();
1001 let expected = StripBuilder::new().add_strip(0, 0, 8, false).finish();
1002
1003 run_test(expected, path, cover);
1004 }
1005
1006 fn run_test(expected: StripStorage, path_1: StripStorage, path_2: StripStorage) {
1007 let mut write_target = StripStorage::default();
1008
1009 let path_1 = path_ref(&path_1);
1010 let path_2 = path_ref(&path_2);
1011
1012 intersect(Level::new(), path_1, path_2, &mut write_target);
1013
1014 assert_eq!(write_target, expected);
1015 }
1016
1017 fn path_ref(path: &StripStorage) -> PathDataRef<'_> {
1018 PathDataRef {
1019 strips: &path.strips,
1020 alphas: &path.alphas,
1021 bbox: RectU16::new(0, 0, u16::MAX, u16::MAX),
1022 }
1023 }
1024
1025 fn assert_strip_region(region: Option<Region<'_>>, start: u16, width: u16) {
1026 match region {
1027 Some(Region::Strip(strip)) => {
1028 assert_eq!(strip.start, start);
1029 assert_eq!(strip.width, width);
1030 assert_eq!(strip.alphas.len(), (width * Tile::HEIGHT) as usize);
1031 }
1032 other => panic!("expected strip region, got {other:?}"),
1033 }
1034 }
1035
1036 fn assert_fill_region(region: Option<Region<'_>>, start: u16, width: u16) {
1037 match region {
1038 Some(Region::Fill(fill)) => {
1039 assert_eq!(fill.start, start);
1040 assert_eq!(fill.width, width);
1041 }
1042 other => panic!("expected fill region, got {other:?}"),
1043 }
1044 }
1045
1046 struct StripBuilder {
1047 storage: StripStorage,
1048 }
1049
1050 impl StripBuilder {
1051 fn new() -> Self {
1052 Self {
1053 storage: StripStorage::default(),
1054 }
1055 }
1056
1057 fn add_strip(self, x: u16, strip_y: u16, end: u16, fill_gap: bool) -> Self {
1058 let width = end - x;
1059 self.add_strip_with(
1060 x,
1061 strip_y,
1062 end,
1063 fill_gap,
1064 &vec![0; (width * Tile::HEIGHT) as usize],
1065 )
1066 }
1067
1068 fn add_strip_with(
1069 mut self,
1070 x: u16,
1071 strip_y: u16,
1072 end: u16,
1073 fill_gap: bool,
1074 alphas: &[u8],
1075 ) -> Self {
1076 let width = end - x;
1077 assert_eq!(alphas.len(), (width * Tile::HEIGHT) as usize);
1078 let idx = self.storage.alphas.len();
1079 self.storage
1080 .strips
1081 .push(Strip::new(x, strip_y * Tile::HEIGHT, idx as u32, fill_gap));
1082 self.storage.alphas.extend_from_slice(alphas);
1083
1084 self
1085 }
1086
1087 fn finish(mut self) -> StripStorage {
1088 let last_y = self.storage.strips.last().unwrap().y;
1089 let idx = self.storage.alphas.len();
1090
1091 self.storage
1092 .strips
1093 .push(Strip::sentinel(last_y, idx as u32));
1094
1095 self.storage
1096 }
1097
1098 fn add_row_end(mut self, strip_y: u16, x: u16, fill_gap: bool) -> Self {
1099 let idx = self.storage.alphas.len();
1100 self.storage
1101 .strips
1102 .push(Strip::new(x, strip_y * Tile::HEIGHT, idx as u32, fill_gap));
1103
1104 self
1105 }
1106
1107 fn finish_with_fill_gap_row_end(self, x: u16) -> StripStorage {
1108 let strip_y = self.storage.strips.last().unwrap().strip_y();
1109
1110 self.add_row_end(strip_y, x, true).finish()
1111 }
1112 }
1113}