tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

segment.rs (46363B)


      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 
     52 use api::{BorderRadius, ClipMode};
     53 use api::units::*;
     54 use std::{cmp, usize};
     55 use crate::util::extract_inner_rect_safe;
     56 use 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.
     60 const 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)]
     74 pub struct EdgeAaSegmentMask(u8);
     75 
     76 bitflags! {
     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 
     89 impl 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 
     99 bitflags! {
    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)]
    110 pub 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)]
    129 enum 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.
    142 impl 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)]
    173 struct Event {
    174    value: Au,
    175    item_index: ItemIndex,
    176    kind: EventKind,
    177 }
    178 
    179 impl 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 
    187 impl 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)]
    234 struct Item {
    235    rect: LayoutRect,
    236    mode: Option<ClipMode>,
    237    flags: ItemFlags,
    238 }
    239 
    240 impl 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)]
    261 struct ItemIndex(usize);
    262 
    263 // The main public interface to the segment module.
    264 pub 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 
    274 impl 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 
    689 fn clamp(low: Au, value: Au, high: Au) -> Au {
    690    value.max(low).min(high)
    691 }
    692 
    693 fn 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)]
    742 mod 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 }