webrender/
segment.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5//!  Primitive segmentation
6//!
7//! # Overview
8//!
9//! Segmenting is the process of breaking rectangular primitives into smaller rectangular
10//! primitives in order to extract parts that could benefit from a fast paths.
11//!
12//! Typically this is used to allow fully opaque segments to be rendered in the opaque
13//! pass. For example when an opaque rectangle has a non-axis-aligned transform applied,
14//! we usually have to apply some anti-aliasing around the edges which requires alpha
15//! blending. By segmenting the edges out of the center of the primitive, we can keep a
16//! large amount of pixels in the opaque pass.
17//! Segmenting also lets us avoids rasterizing parts of clip masks that we know to have
18//! no effect or to be fully masking. For example by segmenting the corners of a rounded
19//! rectangle clip, we can optimize both rendering the mask and the primitive by only
20//! rasterize the corners in the mask and not applying any clipping to the segments of
21//! the primitive that don't overlap the borders.
22//!
23//! It is a flexible system in the sense that different sources of segmentation (for
24//! example two rounded rectangle clips) can affect the segmentation, and the possibility
25//! to segment some effects such as specific clip kinds does not necessarily mean the
26//! primitive will actually be segmented.
27//!
28//! ## Segments and clipping
29//!
30//! Segments of a primitive can be either not clipped, fully clipped, or partially clipped.
31//! In the first two case we don't need a clip mask. For each partially masked segments, a
32//! mask is rasterized using a render task. All of the interesting steps happen during frame
33//! building.
34//!
35//! - The first step is to determine the segmentation and write the associated GPU data.
36//!   See `PrimitiveInstance::build_segments_if_needed` and `write_brush_segment_description`
37//!   in `prim_store/mod.rs` which uses the segment builder of this module.
38//! - The second step is to generate the mask render tasks.
39//!   See `BrushSegment::update_clip_task` and `RenderTask::new_mask`. For each segment that
40//!   needs a mask, the contribution of all clips that affect the segment is added to the
41//!   mask's render task.
42//! - Segments are assigned to batches (See `batch.rs`). Segments of a given primitive can
43//!   be assigned to different batches.
44//!
45//! See also the [`clip` module documentation][clip.rs] for details about how clipping
46//! information is represented.
47//!
48//!
49//! [clip.rs]: ../clip/index.html
50//!
51
52use api::{BorderRadius, ClipMode};
53use api::units::*;
54use std::{cmp, usize};
55use crate::util::extract_inner_rect_safe;
56use smallvec::SmallVec;
57
58// We don't want to generate too many segments in edge cases, as it will result in a lot of
59// clip mask overhead, and possibly exceeding the maximum row size of the GPU cache.
60const MAX_SEGMENTS: usize = 64;
61
62// Note: This can use up to 4 bits due to how it will be packed in
63// the instance data.
64
65/// Each bit of the edge AA mask is:
66/// 0, when the edge of the primitive needs to be considered for AA
67/// 1, when the edge of the segment needs to be considered for AA
68///
69/// *Note*: the bit values have to match the shader logic in
70/// `write_transform_vertex()` function.
71#[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        ///
79        const LEFT = 0x1;
80        ///
81        const TOP = 0x2;
82        ///
83        const RIGHT = 0x4;
84        ///
85        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// The segment builder outputs a list of these segments.
109#[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// The segment builder creates a list of x/y axis events
119// that are used to build a segment list. Right now, we
120// don't bother providing a list of *which* clip regions
121// are active for a given segment. Instead, if there is
122// any clip mask present in a segment, we will just end
123// up drawing each of the masks to that segment clip.
124// This is a fairly rare case, but we can detect this
125// in the future and only apply clip masks that are
126// relevant to each segment region.
127// TODO(gw): Provide clip region info with each segment.
128#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd)]
129enum EventKind {
130    // Beginning of a clip (rounded) rect.
131    BeginClip,
132    // End of a clip (rounded) rect.
133    EndClip,
134    // Begin the next region in the primitive.
135    BeginRegion,
136}
137
138// Events must be ordered such that when the coordinates
139// of two events are the same, the end events are processed
140// before the begin events. This ensures that we're able
141// to detect which regions are active for a given segment.
142impl 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// A x/y event where we will create a vertex in the
171// segment builder.
172#[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// An item that provides some kind of clip region (either
232// a clip in/out rect, or a mask region).
233#[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
263// The main public interface to the segment module.
264pub 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    // Create a new segment builder, supplying the primitive
276    // local rect and associated local clip rect.
277    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        // This must be set after the push_clip_rect calls above, since we
302        // want to skip segment building if those are the only clips.
303        self.has_interesting_clips = false;
304
305        #[cfg(debug_assertions)]
306        {
307            self.initialized = true;
308        }
309    }
310
311    // Push a region defined by an inner and outer rect where there
312    // is a mask required. This ensures that segments which intersect
313    // with these areas will get a clip mask task allocated. This
314    // is currently used to mark where a box-shadow region can affect
315    // the pixels of a clip-mask. It might be useful for other types
316    // such as dashed and dotted borders in the future.
317    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    // Push some kind of clipping region into the segment builder.
396    // If radius is None, it's a simple rect.
397    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        // Keep track of a minimal bounding rect for the set of
406        // segments that will be generated.
407        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                // For a rounded rect, try to create a nine-patch where there
417                // is a clip item for each corner, inner and edge region.
418                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                        // If we get here, we could not extract an inner rectangle
487                        // for this clip region. This can occur in cases such as
488                        // a rounded rect where the top-left and bottom-left radii
489                        // result in overlapping rects. In that case, just create
490                        // a single clip region for the entire rounded rect.
491                        self.items.push(Item::new(
492                            rect,
493                            mode,
494                            true,
495                        ))
496                    }
497                }
498            }
499            None => {
500                // For a simple rect, just create one clipping item.
501                self.items.push(Item::new(
502                    rect,
503                    mode,
504                    false,
505                ))
506            }
507        }
508    }
509
510    // Consume this segment builder and produce a list of segments.
511    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            // There were no additional clips added, so don't bother building segments.
527            // Just emit a single segment for the bounding rect of the primitive.
528            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        // First, filter out any items that don't intersect
539        // with the visible bounding rect.
540        self.items.retain(|item| item.rect.intersects(&bounding_rect));
541
542        // Create events for each item
543        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        // Add the region events, if provided.
557        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        // Get the minimal bounding rect in app units. We will
566        // work in fixed point in order to avoid float precision
567        // error while handling events.
568        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        // Sort the events in ascending order.
579        x_events.sort();
580        y_events.sort();
581
582        // Generate segments from the event lists, by sweeping the y-axis
583        // and then the x-axis for each event. This can generate a significant
584        // number of segments, but most importantly, it ensures that there are
585        // no t-junctions in the generated segments. It's probably possible
586        // to come up with more efficient segmentation algorithms, at least
587        // for simple / common cases.
588
589        // Each coordinate is clamped to the bounds of the minimal
590        // bounding rect. This ensures that we don't generate segments
591        // outside that bounding rect, but does allow correctly handling
592        // clips where the clip region starts outside the minimal
593        // rect but still intersects with it.
594
595        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 we created more than 64 segments, just bail out and draw it as a single primitive
647        // with a single mask, to avoid overhead of excessive amounts of segments. This can only
648        // happen in pathological cases, for example a cascade of a dozen or more overlapping
649        // and intersecting rounded clips.
650        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        // Run user supplied closure for each valid segment.
662        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    // TODO(gw): Don't scan the whole list of items for
706    //           each segment rect. Store active list
707    //           in a hash set or similar if this ever
708    //           shows up in a profile.
709    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                // corners
980                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                // inner
986                seg(30.0, 30.0, 50.0, 50.0, false, None),
987
988                // edges
989                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                // top row
1032                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                // left
1039                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                // right
1044                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                // bottom row
1049                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                // inner corners
1056                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                // corners
1094                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                // inner
1099                seg(10.0, 10.0, 90.0, 90.0, false, None),
1100
1101                // edges
1102                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}