Skip to main content

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