1use api::{BorderRadius, ClipMode};
53use api::units::*;
54use std::{cmp, usize};
55use crate::util::extract_inner_rect_safe;
56use smallvec::SmallVec;
57
58const MAX_SEGMENTS: usize = 64;
61
62#[cfg_attr(feature = "capture", derive(Serialize))]
68#[cfg_attr(feature = "replay", derive(Deserialize))]
69#[derive(Copy, PartialEq, Eq, Clone, PartialOrd, Ord, Hash, MallocSizeOf)]
70pub struct EdgeMask(u8);
71
72bitflags! {
73 impl EdgeMask: u8 {
74 const LEFT = 0x1;
76 const TOP = 0x2;
78 const RIGHT = 0x4;
80 const BOTTOM = 0x8;
82 }
83}
84
85impl core::fmt::Debug for EdgeMask {
86 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
87 if self.is_empty() {
88 write!(f, "{:#x}", Self::empty().bits())
89 } else {
90 bitflags::parser::to_writer(self, f)
91 }
92 }
93}
94
95impl EdgeMask {
96 pub fn select<T: Copy, U>(&self, a: euclid::Box2D<T, U>, b: euclid::Box2D<T, U>) -> euclid::Box2D<T, U> {
100 let mut rect = b;
101 if self.contains(Self::LEFT) {
102 rect.min.x = a.min.x;
103 }
104 if self.contains(Self::TOP) {
105 rect.min.y = a.min.y;
106 }
107 if self.contains(Self::RIGHT) {
108 rect.max.x = a.max.x;
109 }
110 if self.contains(Self::BOTTOM) {
111 rect.max.y = a.max.y;
112 }
113
114 rect
115 }
116}
117
118bitflags! {
119 #[derive(Debug, Copy, PartialEq, Eq, Clone, PartialOrd, Ord, Hash)]
120 pub struct ItemFlags: u8 {
121 const X_ACTIVE = 0x1;
122 const Y_ACTIVE = 0x2;
123 const HAS_MASK = 0x4;
124 }
125}
126
127#[derive(Debug, PartialEq)]
129pub struct Segment {
130 pub rect: LayoutRect,
131 pub has_mask: bool,
132 pub edge_flags: EdgeMask,
133 pub region_x: usize,
134 pub region_y: usize,
135}
136
137#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd)]
148enum EventKind {
149 BeginClip,
151 EndClip,
153 BeginRegion,
155}
156
157impl Ord for EventKind {
162 fn cmp(&self, other: &EventKind) -> cmp::Ordering {
163 match (*self, *other) {
164 (EventKind::BeginRegion, EventKind::BeginRegion) => {
165 panic!("bug: regions must be non-overlapping")
166 }
167 (EventKind::EndClip, EventKind::BeginRegion) |
168 (EventKind::BeginRegion, EventKind::BeginClip) => {
169 cmp::Ordering::Less
170 }
171 (EventKind::BeginClip, EventKind::BeginRegion) |
172 (EventKind::BeginRegion, EventKind::EndClip) => {
173 cmp::Ordering::Greater
174 }
175 (EventKind::BeginClip, EventKind::BeginClip) |
176 (EventKind::EndClip, EventKind::EndClip) => {
177 cmp::Ordering::Equal
178 }
179 (EventKind::BeginClip, EventKind::EndClip) => {
180 cmp::Ordering::Greater
181 }
182 (EventKind::EndClip, EventKind::BeginClip) => {
183 cmp::Ordering::Less
184 }
185 }
186 }
187}
188
189#[derive(Debug, Eq, PartialEq, PartialOrd)]
192struct Event {
193 value: Au,
194 item_index: ItemIndex,
195 kind: EventKind,
196}
197
198impl Ord for Event {
199 fn cmp(&self, other: &Event) -> cmp::Ordering {
200 self.value
201 .cmp(&other.value)
202 .then(self.kind.cmp(&other.kind))
203 }
204}
205
206impl Event {
207 fn begin(value: f32, index: usize) -> Event {
208 Event {
209 value: Au::from_f32_px(value),
210 item_index: ItemIndex(index),
211 kind: EventKind::BeginClip,
212 }
213 }
214
215 fn end(value: f32, index: usize) -> Event {
216 Event {
217 value: Au::from_f32_px(value),
218 item_index: ItemIndex(index),
219 kind: EventKind::EndClip,
220 }
221 }
222
223 fn region(value: f32) -> Event {
224 Event {
225 value: Au::from_f32_px(value),
226 kind: EventKind::BeginRegion,
227 item_index: ItemIndex(usize::MAX),
228 }
229 }
230
231 fn update(
232 &self,
233 flag: ItemFlags,
234 items: &mut [Item],
235 region: &mut usize,
236 ) {
237 let is_active = match self.kind {
238 EventKind::BeginClip => true,
239 EventKind::EndClip => false,
240 EventKind::BeginRegion => {
241 *region += 1;
242 return;
243 }
244 };
245
246 items[self.item_index.0].flags.set(flag, is_active);
247 }
248}
249
250#[derive(Debug)]
253struct Item {
254 rect: LayoutRect,
255 mode: Option<ClipMode>,
256 flags: ItemFlags,
257}
258
259impl Item {
260 fn new(
261 rect: LayoutRect,
262 mode: Option<ClipMode>,
263 has_mask: bool,
264 ) -> Item {
265 let flags = if has_mask {
266 ItemFlags::HAS_MASK
267 } else {
268 ItemFlags::empty()
269 };
270
271 Item {
272 rect,
273 mode,
274 flags,
275 }
276 }
277}
278
279#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd)]
280struct ItemIndex(usize);
281
282pub struct SegmentBuilder {
284 items: Vec<Item>,
285 inner_rect: Option<LayoutRect>,
286 bounding_rect: Option<LayoutRect>,
287 has_interesting_clips: bool,
288
289 #[cfg(debug_assertions)]
290 initialized: bool,
291}
292
293impl SegmentBuilder {
294 pub fn new() -> SegmentBuilder {
297 SegmentBuilder {
298 items: Vec::with_capacity(4),
299 bounding_rect: None,
300 inner_rect: None,
301 has_interesting_clips: false,
302 #[cfg(debug_assertions)]
303 initialized: false,
304 }
305 }
306
307 pub fn initialize(
308 &mut self,
309 local_rect: LayoutRect,
310 inner_rect: Option<LayoutRect>,
311 local_clip_rect: LayoutRect,
312 ) {
313 self.items.clear();
314 self.inner_rect = inner_rect;
315 self.bounding_rect = Some(local_rect);
316
317 self.push_clip_rect(local_rect, None, ClipMode::Clip);
318 self.push_clip_rect(local_clip_rect, None, ClipMode::Clip);
319
320 self.has_interesting_clips = false;
323
324 #[cfg(debug_assertions)]
325 {
326 self.initialized = true;
327 }
328 }
329
330 pub fn push_mask_region(
337 &mut self,
338 outer_rect: LayoutRect,
339 inner_rect: LayoutRect,
340 inner_clip_mode: Option<ClipMode>,
341 ) {
342 self.has_interesting_clips = true;
343
344 if inner_rect.is_empty() {
345 self.items.push(Item::new(
346 outer_rect,
347 None,
348 true
349 ));
350 return;
351 }
352
353 debug_assert!(outer_rect.contains_box(&inner_rect));
354
355 let p0 = outer_rect.min;
356 let p1 = inner_rect.min;
357 let p2 = inner_rect.max;
358 let p3 = outer_rect.max;
359
360 let segments = &[
361 LayoutRect {
362 min: LayoutPoint::new(p0.x, p0.y),
363 max: LayoutPoint::new(p1.x, p1.y),
364 },
365 LayoutRect {
366 min: LayoutPoint::new(p2.x, p0.y),
367 max: LayoutPoint::new(p3.x, p1.y),
368 },
369 LayoutRect {
370 min: LayoutPoint::new(p2.x, p2.y),
371 max: LayoutPoint::new(p3.x, p3.y),
372 },
373 LayoutRect {
374 min: LayoutPoint::new(p0.x, p2.y),
375 max: LayoutPoint::new(p1.x, p3.y),
376 },
377 LayoutRect {
378 min: LayoutPoint::new(p1.x, p0.y),
379 max: LayoutPoint::new(p2.x, p1.y),
380 },
381 LayoutRect {
382 min: LayoutPoint::new(p2.x, p1.y),
383 max: LayoutPoint::new(p3.x, p2.y),
384 },
385 LayoutRect {
386 min: LayoutPoint::new(p1.x, p2.y),
387 max: LayoutPoint::new(p2.x, p3.y),
388 },
389 LayoutRect {
390 min: LayoutPoint::new(p0.x, p1.y),
391 max: LayoutPoint::new(p1.x, p2.y),
392 },
393 ];
394
395 self.items.reserve(segments.len() + 1);
396
397 for segment in segments {
398 self.items.push(Item::new(
399 *segment,
400 None,
401 true
402 ));
403 }
404
405 if inner_clip_mode.is_some() {
406 self.items.push(Item::new(
407 inner_rect,
408 inner_clip_mode,
409 false,
410 ));
411 }
412 }
413
414 pub fn push_clip_rect(
417 &mut self,
418 rect: LayoutRect,
419 radius: Option<BorderRadius>,
420 mode: ClipMode,
421 ) {
422 self.has_interesting_clips = true;
423
424 if mode == ClipMode::Clip {
427 self.bounding_rect = self.bounding_rect.and_then(|bounding_rect| {
428 bounding_rect.intersection(&rect)
429 });
430 }
431 let mode = Some(mode);
432
433 match radius {
434 Some(radius) => {
435 match extract_inner_rect_safe(&rect, &radius) {
438 Some(inner) => {
439 let p0 = rect.min;
440 let p1 = inner.min;
441 let p2 = inner.max;
442 let p3 = rect.max;
443
444 self.items.reserve(9);
445
446 let corner_segments = &[
447 LayoutRect {
448 min: LayoutPoint::new(p0.x, p0.y),
449 max: LayoutPoint::new(p1.x, p1.y),
450 },
451 LayoutRect {
452 min: LayoutPoint::new(p2.x, p0.y),
453 max: LayoutPoint::new(p3.x, p1.y),
454 },
455 LayoutRect {
456 min: LayoutPoint::new(p2.x, p2.y),
457 max: LayoutPoint::new(p3.x, p3.y),
458 },
459 LayoutRect {
460 min: LayoutPoint::new(p0.x, p2.y),
461 max: LayoutPoint::new(p1.x, p3.y),
462 },
463 ];
464
465 for segment in corner_segments {
466 self.items.push(Item::new(
467 *segment,
468 mode,
469 true
470 ));
471 }
472
473 let other_segments = &[
474 LayoutRect {
475 min: LayoutPoint::new(p1.x, p0.y),
476 max: LayoutPoint::new(p2.x, p1.y),
477 },
478 LayoutRect {
479 min: LayoutPoint::new(p2.x, p1.y),
480 max: LayoutPoint::new(p3.x, p2.y),
481 },
482 LayoutRect {
483 min: LayoutPoint::new(p1.x, p2.y),
484 max: LayoutPoint::new(p2.x, p3.y),
485 },
486 LayoutRect {
487 min: LayoutPoint::new(p0.x, p1.y),
488 max: LayoutPoint::new(p1.x, p2.y),
489 },
490 LayoutRect {
491 min: LayoutPoint::new(p1.x, p1.y),
492 max: LayoutPoint::new(p2.x, p2.y),
493 },
494 ];
495
496 for segment in other_segments {
497 self.items.push(Item::new(
498 *segment,
499 mode,
500 false,
501 ));
502 }
503 }
504 None => {
505 self.items.push(Item::new(
511 rect,
512 mode,
513 true,
514 ))
515 }
516 }
517 }
518 None => {
519 self.items.push(Item::new(
521 rect,
522 mode,
523 false,
524 ))
525 }
526 }
527 }
528
529 pub fn build<F>(&mut self, mut f: F) where F: FnMut(&Segment) {
531 #[cfg(debug_assertions)]
532 debug_assert!(self.initialized);
533
534 #[cfg(debug_assertions)]
535 {
536 self.initialized = false;
537 }
538
539 let bounding_rect = match self.bounding_rect {
540 Some(bounding_rect) => bounding_rect,
541 None => return,
542 };
543
544 if !self.has_interesting_clips {
545 f(&Segment {
548 edge_flags: EdgeMask::all(),
549 region_x: 0,
550 region_y: 0,
551 has_mask: false,
552 rect: bounding_rect,
553 });
554 return
555 }
556
557 self.items.retain(|item| item.rect.intersects(&bounding_rect));
560
561 let mut x_events : SmallVec<[Event; 4]> = SmallVec::new();
563 let mut y_events : SmallVec<[Event; 4]> = SmallVec::new();
564
565 for (item_index, item) in self.items.iter().enumerate() {
566 let p0 = item.rect.min;
567 let p1 = item.rect.max;
568
569 x_events.push(Event::begin(p0.x, item_index));
570 x_events.push(Event::end(p1.x, item_index));
571 y_events.push(Event::begin(p0.y, item_index));
572 y_events.push(Event::end(p1.y, item_index));
573 }
574
575 if let Some(inner_rect) = self.inner_rect {
577 x_events.push(Event::region(inner_rect.min.x));
578 x_events.push(Event::region(inner_rect.max.x));
579
580 y_events.push(Event::region(inner_rect.min.y));
581 y_events.push(Event::region(inner_rect.max.y));
582 }
583
584 let p0 = LayoutPointAu::new(
588 Au::from_f32_px(bounding_rect.min.x),
589 Au::from_f32_px(bounding_rect.min.y),
590 );
591
592 let p1 = LayoutPointAu::new(
593 Au::from_f32_px(bounding_rect.max.x),
594 Au::from_f32_px(bounding_rect.max.y),
595 );
596
597 x_events.sort();
599 y_events.sort();
600
601 let mut prev_y = clamp(p0.y, y_events[0].value, p1.y);
615 let mut region_y = 0;
616 let mut segments : SmallVec<[_; 16]> = SmallVec::new();
617 let mut x_count = 0;
618 let mut y_count = 0;
619
620 for ey in &y_events {
621 let cur_y = clamp(p0.y, ey.value, p1.y);
622
623 if cur_y != prev_y {
624 let mut prev_x = clamp(p0.x, x_events[0].value, p1.x);
625 let mut region_x = 0;
626
627 for ex in &x_events {
628 let cur_x = clamp(p0.x, ex.value, p1.x);
629
630 if cur_x != prev_x {
631 segments.push(emit_segment_if_needed(
632 prev_x,
633 prev_y,
634 cur_x,
635 cur_y,
636 region_x,
637 region_y,
638 &self.items,
639 ));
640
641 prev_x = cur_x;
642 if y_count == 0 {
643 x_count += 1;
644 }
645 }
646
647 ex.update(
648 ItemFlags::X_ACTIVE,
649 &mut self.items,
650 &mut region_x,
651 );
652 }
653
654 prev_y = cur_y;
655 y_count += 1;
656 }
657
658 ey.update(
659 ItemFlags::Y_ACTIVE,
660 &mut self.items,
661 &mut region_y,
662 );
663 }
664
665 if segments.len() > MAX_SEGMENTS {
670 f(&Segment {
671 edge_flags: EdgeMask::all(),
672 region_x: 0,
673 region_y: 0,
674 has_mask: true,
675 rect: bounding_rect,
676 });
677 return
678 }
679
680 debug_assert_eq!(segments.len(), x_count * y_count);
682 for y in 0 .. y_count {
683 for x in 0 .. x_count {
684 let mut edge_flags = EdgeMask::empty();
685
686 if x == 0 || segments[y * x_count + x - 1].is_none() {
687 edge_flags |= EdgeMask::LEFT;
688 }
689 if x == x_count-1 || segments[y * x_count + x + 1].is_none() {
690 edge_flags |= EdgeMask::RIGHT;
691 }
692 if y == 0 || segments[(y-1) * x_count + x].is_none() {
693 edge_flags |= EdgeMask::TOP;
694 }
695 if y == y_count-1 || segments[(y+1) * x_count + x].is_none() {
696 edge_flags |= EdgeMask::BOTTOM;
697 }
698
699 if let Some(ref mut segment) = segments[y * x_count + x] {
700 segment.edge_flags = edge_flags;
701 f(segment);
702 }
703 }
704 }
705 }
706}
707
708fn clamp(low: Au, value: Au, high: Au) -> Au {
709 value.max(low).min(high)
710}
711
712fn emit_segment_if_needed(
713 x0: Au,
714 y0: Au,
715 x1: Au,
716 y1: Au,
717 region_x: usize,
718 region_y: usize,
719 items: &[Item],
720) -> Option<Segment> {
721 debug_assert!(x1 > x0);
722 debug_assert!(y1 > y0);
723
724 let mut has_clip_mask = false;
729
730 for item in items {
731 if item.flags.contains(ItemFlags::X_ACTIVE | ItemFlags::Y_ACTIVE) {
732 has_clip_mask |= item.flags.contains(ItemFlags::HAS_MASK);
733
734 if item.mode == Some(ClipMode::ClipOut) && !item.flags.contains(ItemFlags::HAS_MASK) {
735 return None;
736 }
737 }
738 }
739
740 let segment_rect = LayoutRect {
741 min: LayoutPoint::new(
742 x0.to_f32_px(),
743 y0.to_f32_px(),
744 ),
745 max: LayoutPoint::new(
746 x1.to_f32_px(),
747 y1.to_f32_px(),
748 ),
749 };
750
751 Some(Segment {
752 rect: segment_rect,
753 has_mask: has_clip_mask,
754 edge_flags: EdgeMask::empty(),
755 region_x,
756 region_y,
757 })
758}
759
760#[cfg(test)]
761mod test {
762 use api::{BorderRadius, ClipMode};
763 use api::units::{LayoutPoint, LayoutRect};
764 use super::{Segment, SegmentBuilder, EdgeMask};
765 use std::cmp;
766
767 fn rect(x0: f32, y0: f32, x1: f32, y1: f32) -> LayoutRect {
768 LayoutRect {
769 min: LayoutPoint::new(x0, y0),
770 max: LayoutPoint::new(x1, y1),
771 }
772 }
773
774 fn seg(
775 x0: f32,
776 y0: f32,
777 x1: f32,
778 y1: f32,
779 has_mask: bool,
780 edge_flags: Option<EdgeMask>,
781 ) -> Segment {
782 seg_region(x0, y0, x1, y1, 0, 0, has_mask, edge_flags)
783 }
784
785 fn seg_region(
786 x0: f32,
787 y0: f32,
788 x1: f32,
789 y1: f32,
790 region_x: usize,
791 region_y: usize,
792 has_mask: bool,
793 edge_flags: Option<EdgeMask>,
794 ) -> Segment {
795 Segment {
796 rect: LayoutRect {
797 min: LayoutPoint::new(x0, y0),
798 max: LayoutPoint::new(x1, y1),
799 },
800 has_mask,
801 edge_flags: edge_flags.unwrap_or(EdgeMask::empty()),
802 region_x,
803 region_y,
804 }
805 }
806
807 fn segment_sorter(s0: &Segment, s1: &Segment) -> cmp::Ordering {
808 let r0 = &s0.rect;
809 let r1 = &s1.rect;
810
811 (
812 (r0.min.x, r0.min.y, r0.max.x, r0.max.y)
813 ).partial_cmp(&
814 (r1.min.x, r1.min.y, r1.max.x, r1.max.y)
815 ).unwrap()
816 }
817
818 fn seg_test(
819 local_rect: LayoutRect,
820 inner_rect: Option<LayoutRect>,
821 local_clip_rect: LayoutRect,
822 clips: &[(LayoutRect, Option<BorderRadius>, ClipMode)],
823 expected_segments: &mut [Segment]
824 ) {
825 let mut sb = SegmentBuilder::new();
826 sb.initialize(
827 local_rect,
828 inner_rect,
829 local_clip_rect,
830 );
831 sb.push_clip_rect(local_rect, None, ClipMode::Clip);
832 sb.push_clip_rect(local_clip_rect, None, ClipMode::Clip);
833 let mut segments = Vec::new();
834 for &(rect, radius, mode) in clips {
835 sb.push_clip_rect(rect, radius, mode);
836 }
837 sb.build(|segment| {
838 segments.push(Segment {
839 ..*segment
840 });
841 });
842 segments.sort_by(segment_sorter);
843 expected_segments.sort_by(segment_sorter);
844 assert_eq!(
845 segments.len(),
846 expected_segments.len(),
847 "segments\n{:?}\nexpected\n{:?}\n",
848 segments,
849 expected_segments
850 );
851 for (segment, expected) in segments.iter().zip(expected_segments.iter()) {
852 assert_eq!(segment, expected);
853 }
854 }
855
856 #[test]
857 fn segment_empty() {
858 seg_test(
859 rect(0.0, 0.0, 0.0, 0.0),
860 None,
861 rect(0.0, 0.0, 0.0, 0.0),
862 &[],
863 &mut [],
864 );
865 }
866
867 #[test]
868 fn segment_single() {
869 seg_test(
870 rect(10.0, 20.0, 30.0, 40.0),
871 None,
872 rect(10.0, 20.0, 30.0, 40.0),
873 &[],
874 &mut [
875 seg(10.0, 20.0, 30.0, 40.0, false,
876 Some(EdgeMask::LEFT |
877 EdgeMask::TOP |
878 EdgeMask::RIGHT |
879 EdgeMask::BOTTOM
880 )
881 ),
882 ],
883 );
884 }
885
886 #[test]
887 fn segment_single_clip() {
888 seg_test(
889 rect(10.0, 20.0, 30.0, 40.0),
890 None,
891 rect(10.0, 20.0, 25.0, 35.0),
892 &[],
893 &mut [
894 seg(10.0, 20.0, 25.0, 35.0, false,
895 Some(EdgeMask::LEFT |
896 EdgeMask::TOP |
897 EdgeMask::RIGHT |
898 EdgeMask::BOTTOM
899 )
900 ),
901 ],
902 );
903 }
904
905 #[test]
906 fn segment_inner_clip() {
907 seg_test(
908 rect(10.0, 20.0, 30.0, 40.0),
909 None,
910 rect(15.0, 25.0, 25.0, 35.0),
911 &[],
912 &mut [
913 seg(15.0, 25.0, 25.0, 35.0, false,
914 Some(EdgeMask::LEFT |
915 EdgeMask::TOP |
916 EdgeMask::RIGHT |
917 EdgeMask::BOTTOM
918 )
919 ),
920 ],
921 );
922 }
923
924 #[test]
925 fn segment_outer_clip() {
926 seg_test(
927 rect(15.0, 25.0, 25.0, 35.0),
928 None,
929 rect(10.0, 20.0, 30.0, 40.0),
930 &[],
931 &mut [
932 seg(15.0, 25.0, 25.0, 35.0, false,
933 Some(EdgeMask::LEFT |
934 EdgeMask::TOP |
935 EdgeMask::RIGHT |
936 EdgeMask::BOTTOM
937 )
938 ),
939 ],
940 );
941 }
942
943 #[test]
944 fn segment_clip_int() {
945 seg_test(
946 rect(10.0, 20.0, 30.0, 40.0),
947 None,
948 rect(20.0, 10.0, 40.0, 30.0),
949 &[],
950 &mut [
951 seg(20.0, 20.0, 30.0, 30.0, false,
952 Some(EdgeMask::LEFT |
953 EdgeMask::TOP |
954 EdgeMask::RIGHT |
955 EdgeMask::BOTTOM
956 )
957 ),
958 ],
959 );
960 }
961
962 #[test]
963 fn segment_clip_disjoint() {
964 seg_test(
965 rect(10.0, 20.0, 30.0, 40.0),
966 None,
967 rect(30.0, 20.0, 50.0, 40.0),
968 &[],
969 &mut [],
970 );
971 }
972
973 #[test]
974 fn segment_clips() {
975 seg_test(
976 rect(0.0, 0.0, 100.0, 100.0),
977 None,
978 rect(-1000.0, -1000.0, 1000.0, 1000.0),
979 &[
980 (rect(20.0, 20.0, 40.0, 40.0), None, ClipMode::Clip),
981 (rect(40.0, 20.0, 60.0, 40.0), None, ClipMode::Clip),
982 ],
983 &mut [
984 ],
985 );
986 }
987
988 #[test]
989 fn segment_rounded_clip() {
990 seg_test(
991 rect(0.0, 0.0, 100.0, 100.0),
992 None,
993 rect(-1000.0, -1000.0, 1000.0, 1000.0),
994 &[
995 (rect(20.0, 20.0, 60.0, 60.0), Some(BorderRadius::uniform(10.0)), ClipMode::Clip),
996 ],
997 &mut [
998 seg(20.0, 20.0, 30.0, 30.0, true, Some(EdgeMask::LEFT | EdgeMask::TOP)),
1000 seg(20.0, 50.0, 30.0, 60.0, true, Some(EdgeMask::LEFT | EdgeMask::BOTTOM)),
1001 seg(50.0, 20.0, 60.0, 30.0, true, Some(EdgeMask::RIGHT | EdgeMask::TOP)),
1002 seg(50.0, 50.0, 60.0, 60.0, true, Some(EdgeMask::RIGHT | EdgeMask::BOTTOM)),
1003
1004 seg(30.0, 30.0, 50.0, 50.0, false, None),
1006
1007 seg(30.0, 20.0, 50.0, 30.0, false, Some(EdgeMask::TOP)),
1009 seg(30.0, 50.0, 50.0, 60.0, false, Some(EdgeMask::BOTTOM)),
1010 seg(20.0, 30.0, 30.0, 50.0, false, Some(EdgeMask::LEFT)),
1011 seg(50.0, 30.0, 60.0, 50.0, false, Some(EdgeMask::RIGHT)),
1012 ],
1013 );
1014 }
1015
1016 #[test]
1017 fn segment_clip_out() {
1018 seg_test(
1019 rect(0.0, 0.0, 100.0, 100.0),
1020 None,
1021 rect(-1000.0, -1000.0, 2000.0, 2000.0),
1022 &[
1023 (rect(20.0, 20.0, 60.0, 60.0), None, ClipMode::ClipOut),
1024 ],
1025 &mut [
1026 seg(0.0, 0.0, 20.0, 20.0, false, Some(EdgeMask::TOP | EdgeMask::LEFT)),
1027 seg(20.0, 0.0, 60.0, 20.0, false, Some(EdgeMask::TOP | EdgeMask::BOTTOM)),
1028 seg(60.0, 0.0, 100.0, 20.0, false, Some(EdgeMask::TOP | EdgeMask::RIGHT)),
1029
1030 seg(0.0, 20.0, 20.0, 60.0, false, Some(EdgeMask::LEFT | EdgeMask::RIGHT)),
1031 seg(60.0, 20.0, 100.0, 60.0, false, Some(EdgeMask::RIGHT | EdgeMask::LEFT)),
1032
1033 seg(0.0, 60.0, 20.0, 100.0, false, Some(EdgeMask::LEFT | EdgeMask::BOTTOM)),
1034 seg(20.0, 60.0, 60.0, 100.0, false, Some(EdgeMask::BOTTOM | EdgeMask::TOP)),
1035 seg(60.0, 60.0, 100.0, 100.0, false, Some(EdgeMask::BOTTOM | EdgeMask::RIGHT)),
1036 ],
1037 );
1038 }
1039
1040 #[test]
1041 fn segment_rounded_clip_out() {
1042 seg_test(
1043 rect(0.0, 0.0, 100.0, 100.0),
1044 None,
1045 rect(-1000.0, -1000.0, 2000.0, 2000.0),
1046 &[
1047 (rect(20.0, 20.0, 60.0, 60.0), Some(BorderRadius::uniform(10.0)), ClipMode::ClipOut),
1048 ],
1049 &mut [
1050 seg(0.0, 0.0, 20.0, 20.0, false, Some(EdgeMask::TOP | EdgeMask::LEFT)),
1052 seg(20.0, 0.0, 30.0, 20.0, false, Some(EdgeMask::TOP)),
1053 seg(30.0, 0.0, 50.0, 20.0, false, Some(EdgeMask::TOP | EdgeMask::BOTTOM)),
1054 seg(50.0, 0.0, 60.0, 20.0, false, Some(EdgeMask::TOP)),
1055 seg(60.0, 0.0, 100.0, 20.0, false, Some(EdgeMask::TOP | EdgeMask::RIGHT)),
1056
1057 seg(0.0, 20.0, 20.0, 30.0, false, Some(EdgeMask::LEFT)),
1059 seg(0.0, 30.0, 20.0, 50.0, false, Some(EdgeMask::LEFT | EdgeMask::RIGHT)),
1060 seg(0.0, 50.0, 20.0, 60.0, false, Some(EdgeMask::LEFT)),
1061
1062 seg(60.0, 20.0, 100.0, 30.0, false, Some(EdgeMask::RIGHT)),
1064 seg(60.0, 30.0, 100.0, 50.0, false, Some(EdgeMask::RIGHT | EdgeMask::LEFT)),
1065 seg(60.0, 50.0, 100.0, 60.0, false, Some(EdgeMask::RIGHT)),
1066
1067 seg(0.0, 60.0, 20.0, 100.0, false, Some(EdgeMask::LEFT | EdgeMask::BOTTOM)),
1069 seg(20.0, 60.0, 30.0, 100.0, false, Some(EdgeMask::BOTTOM)),
1070 seg(30.0, 60.0, 50.0, 100.0, false, Some(EdgeMask::BOTTOM | EdgeMask::TOP)),
1071 seg(50.0, 60.0, 60.0, 100.0, false, Some(EdgeMask::BOTTOM)),
1072 seg(60.0, 60.0, 100.0, 100.0, false, Some(EdgeMask::RIGHT | EdgeMask::BOTTOM)),
1073
1074 seg(20.0, 20.0, 30.0, 30.0, true, Some(EdgeMask::RIGHT | EdgeMask::BOTTOM)),
1076 seg(20.0, 50.0, 30.0, 60.0, true, Some(EdgeMask::TOP | EdgeMask::RIGHT)),
1077 seg(50.0, 20.0, 60.0, 30.0, true, Some(EdgeMask::LEFT | EdgeMask::BOTTOM)),
1078 seg(50.0, 50.0, 60.0, 60.0, true, Some(EdgeMask::LEFT | EdgeMask::TOP)),
1079 ],
1080 );
1081 }
1082
1083 #[test]
1084 fn segment_clip_in_clip_out() {
1085 seg_test(
1086 rect(0.0, 0.0, 100.0, 100.0),
1087 None,
1088 rect(-1000.0, -1000.0, 2000.0, 2000.0),
1089 &[
1090 (rect(20.0, 20.0, 60.0, 60.0), None, ClipMode::Clip),
1091 (rect(50.0, 50.0, 80.0, 80.0), None, ClipMode::ClipOut),
1092 ],
1093 &mut [
1094 seg(20.0, 20.0, 50.0, 50.0, false, Some(EdgeMask::LEFT | EdgeMask::TOP)),
1095 seg(50.0, 20.0, 60.0, 50.0, false, Some(EdgeMask::TOP | EdgeMask::RIGHT | EdgeMask::BOTTOM)),
1096 seg(20.0, 50.0, 50.0, 60.0, false, Some(EdgeMask::LEFT | EdgeMask::BOTTOM | EdgeMask::RIGHT)),
1097 ],
1098 );
1099 }
1100
1101 #[test]
1102 fn segment_rounded_clip_overlap() {
1103 seg_test(
1104 rect(0.0, 0.0, 100.0, 100.0),
1105 None,
1106 rect(0.0, 0.0, 100.0, 100.0),
1107 &[
1108 (rect(0.0, 0.0, 10.0, 10.0), None, ClipMode::ClipOut),
1109 (rect(0.0, 0.0, 100.0, 100.0), Some(BorderRadius::uniform(10.0)), ClipMode::Clip),
1110 ],
1111 &mut [
1112 seg(0.0, 90.0, 10.0, 100.0, true, Some(EdgeMask::LEFT | EdgeMask::BOTTOM)),
1114 seg(90.0, 0.0, 100.0, 10.0, true, Some(EdgeMask::RIGHT | EdgeMask::TOP)),
1115 seg(90.0, 90.0, 100.0, 100.0, true, Some(EdgeMask::RIGHT | EdgeMask::BOTTOM)),
1116
1117 seg(10.0, 10.0, 90.0, 90.0, false, None),
1119
1120 seg(10.0, 0.0, 90.0, 10.0, false, Some(EdgeMask::TOP | EdgeMask::LEFT)),
1122 seg(10.0, 90.0, 90.0, 100.0, false, Some(EdgeMask::BOTTOM)),
1123 seg(0.0, 10.0, 10.0, 90.0, false, Some(EdgeMask::LEFT | EdgeMask::TOP)),
1124 seg(90.0, 10.0, 100.0, 90.0, false, Some(EdgeMask::RIGHT)),
1125 ],
1126 );
1127 }
1128
1129 #[test]
1130 fn segment_rounded_clip_overlap_reverse() {
1131 seg_test(
1132 rect(0.0, 0.0, 100.0, 100.0),
1133 None,
1134 rect(0.0, 0.0, 100.0, 100.0),
1135 &[
1136 (rect(10.0, 10.0, 90.0, 90.0), None, ClipMode::Clip),
1137 (rect(0.0, 0.0, 100.0, 100.0), Some(BorderRadius::uniform(10.0)), ClipMode::Clip),
1138 ],
1139 &mut [
1140 seg(10.0, 10.0, 90.0, 90.0, false,
1141 Some(EdgeMask::LEFT |
1142 EdgeMask::TOP |
1143 EdgeMask::RIGHT |
1144 EdgeMask::BOTTOM
1145 )
1146 ),
1147 ],
1148 );
1149 }
1150
1151 #[test]
1152 fn segment_clip_in_clip_out_overlap() {
1153 seg_test(
1154 rect(0.0, 0.0, 100.0, 100.0),
1155 None,
1156 rect(0.0, 0.0, 100.0, 100.0),
1157 &[
1158 (rect(10.0, 10.0, 90.0, 90.0), None, ClipMode::Clip),
1159 (rect(10.0, 10.0, 90.0, 90.0), None, ClipMode::ClipOut),
1160 ],
1161 &mut [
1162 ],
1163 );
1164 }
1165
1166 #[test]
1167 fn segment_event_order() {
1168 seg_test(
1169 rect(0.0, 0.0, 100.0, 100.0),
1170 None,
1171 rect(0.0, 0.0, 100.0, 100.0),
1172 &[
1173 (rect(0.0, 0.0, 100.0, 90.0), None, ClipMode::ClipOut),
1174 ],
1175 &mut [
1176 seg(0.0, 90.0, 100.0, 100.0, false, Some(
1177 EdgeMask::LEFT |
1178 EdgeMask::RIGHT |
1179 EdgeMask::BOTTOM |
1180 EdgeMask::TOP
1181 )),
1182 ],
1183 );
1184 }
1185
1186 #[test]
1187 fn segment_region_simple() {
1188 seg_test(
1189 rect(0.0, 0.0, 100.0, 100.0),
1190 Some(rect(20.0, 40.0, 60.0, 80.0)),
1191 rect(0.0, 0.0, 100.0, 100.0),
1192 &[
1193 ],
1194 &mut [
1195 seg_region(
1196 0.0, 0.0,
1197 20.0, 40.0,
1198 0, 0,
1199 false,
1200 Some(EdgeMask::LEFT | EdgeMask::TOP)
1201 ),
1202
1203 seg_region(
1204 20.0, 0.0,
1205 60.0, 40.0,
1206 1, 0,
1207 false,
1208 Some(EdgeMask::TOP)
1209 ),
1210
1211 seg_region(
1212 60.0, 0.0,
1213 100.0, 40.0,
1214 2, 0,
1215 false,
1216 Some(EdgeMask::TOP | EdgeMask::RIGHT)
1217 ),
1218
1219 seg_region(
1220 0.0, 40.0,
1221 20.0, 80.0,
1222 0, 1,
1223 false,
1224 Some(EdgeMask::LEFT)
1225 ),
1226
1227 seg_region(
1228 20.0, 40.0,
1229 60.0, 80.0,
1230 1, 1,
1231 false,
1232 None,
1233 ),
1234
1235 seg_region(
1236 60.0, 40.0,
1237 100.0, 80.0,
1238 2, 1,
1239 false,
1240 Some(EdgeMask::RIGHT)
1241 ),
1242
1243 seg_region(
1244 0.0, 80.0,
1245 20.0, 100.0,
1246 0, 2,
1247 false,
1248 Some(EdgeMask::LEFT | EdgeMask::BOTTOM)
1249 ),
1250
1251 seg_region(
1252 20.0, 80.0,
1253 60.0, 100.0,
1254 1, 2,
1255 false,
1256 Some(EdgeMask::BOTTOM),
1257 ),
1258
1259 seg_region(
1260 60.0, 80.0,
1261 100.0, 100.0,
1262 2, 2,
1263 false,
1264 Some(EdgeMask::RIGHT | EdgeMask::BOTTOM)
1265 ),
1266
1267 ],
1268 );
1269 }
1270
1271 #[test]
1272 fn segment_region_clip() {
1273 seg_test(
1274 rect(0.0, 0.0, 100.0, 100.0),
1275 Some(rect(20.0, 40.0, 60.0, 80.0)),
1276 rect(0.0, 0.0, 100.0, 100.0),
1277 &[
1278 (rect(0.0, 0.0, 100.0, 90.0), None, ClipMode::ClipOut),
1279 ],
1280 &mut [
1281 seg_region(
1282 0.0, 90.0,
1283 20.0, 100.0,
1284 0, 2,
1285 false,
1286 Some(EdgeMask::LEFT | EdgeMask::BOTTOM | EdgeMask::TOP)
1287 ),
1288
1289 seg_region(
1290 20.0, 90.0,
1291 60.0, 100.0,
1292 1, 2,
1293 false,
1294 Some(EdgeMask::BOTTOM | EdgeMask::TOP),
1295 ),
1296
1297 seg_region(
1298 60.0, 90.0,
1299 100.0, 100.0,
1300 2, 2,
1301 false,
1302 Some(EdgeMask::RIGHT | EdgeMask::BOTTOM | EdgeMask::TOP)
1303 ),
1304
1305 ],
1306 );
1307 }
1308
1309 #[test]
1310 fn segment_region_clip2() {
1311 seg_test(
1312 rect(0.0, 0.0, 100.0, 100.0),
1313 Some(rect(20.0, 20.0, 80.0, 80.0)),
1314 rect(0.0, 0.0, 100.0, 100.0),
1315 &[
1316 (rect(20.0, 20.0, 100.0, 100.0), None, ClipMode::ClipOut),
1317 ],
1318 &mut [
1319 seg_region(
1320 0.0, 0.0,
1321 20.0, 20.0,
1322 0, 0,
1323 false,
1324 Some(EdgeMask::LEFT | EdgeMask::TOP)
1325 ),
1326
1327 seg_region(
1328 20.0, 0.0,
1329 80.0, 20.0,
1330 1, 0,
1331 false,
1332 Some(EdgeMask::TOP | EdgeMask::BOTTOM),
1333 ),
1334
1335 seg_region(
1336 80.0, 0.0,
1337 100.0, 20.0,
1338 2, 0,
1339 false,
1340 Some(EdgeMask::RIGHT | EdgeMask::TOP | EdgeMask::BOTTOM)
1341 ),
1342
1343 seg_region(
1344 0.0, 20.0,
1345 20.0, 80.0,
1346 0, 1,
1347 false,
1348 Some(EdgeMask::LEFT | EdgeMask::RIGHT)
1349 ),
1350
1351 seg_region(
1352 0.0, 80.0,
1353 20.0, 100.0,
1354 0, 2,
1355 false,
1356 Some(EdgeMask::LEFT | EdgeMask::BOTTOM | EdgeMask::RIGHT)
1357 ),
1358 ],
1359 );
1360 }
1361
1362 #[test]
1363 fn segment_region_clip3() {
1364 seg_test(
1365 rect(0.0, 0.0, 100.0, 100.0),
1366 Some(rect(20.0, 20.0, 80.0, 80.0)),
1367 rect(0.0, 0.0, 100.0, 100.0),
1368 &[
1369 (rect(10.0, 10.0, 30.0, 30.0), None, ClipMode::Clip),
1370 ],
1371 &mut [
1372 seg_region(
1373 10.0, 10.0,
1374 20.0, 20.0,
1375 0, 0,
1376 false,
1377 Some(EdgeMask::TOP | EdgeMask::LEFT),
1378 ),
1379
1380 seg_region(
1381 20.0, 10.0,
1382 30.0, 20.0,
1383 1, 0,
1384 false,
1385 Some(EdgeMask::TOP | EdgeMask::RIGHT),
1386 ),
1387
1388 seg_region(
1389 10.0, 20.0,
1390 20.0, 30.0,
1391 0, 1,
1392 false,
1393 Some(EdgeMask::BOTTOM | EdgeMask::LEFT),
1394 ),
1395
1396 seg_region(
1397 20.0, 20.0,
1398 30.0, 30.0,
1399 1, 1,
1400 false,
1401 Some(EdgeMask::BOTTOM | EdgeMask::RIGHT),
1402 ),
1403 ],
1404 );
1405 }
1406}