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