Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  segment.rs   Sprache: unbekannt

 
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

//!  Primitive segmentation
//!
//! # Overview
//!
//! Segmenting is the process of breaking rectangular primitives into smaller rectangular
//! primitives in order to extract parts that could benefit from a fast paths.
//!
//! Typically this is used to allow fully opaque segments to be rendered in the opaque
//! pass. For example when an opaque rectangle has a non-axis-aligned transform applied,
//! we usually have to apply some anti-aliasing around the edges which requires alpha
//! blending. By segmenting the edges out of the center of the primitive, we can keep a
//! large amount of pixels in the opaque pass.
//! Segmenting also lets us avoids rasterizing parts of clip masks that we know to have
//! no effect or to be fully masking. For example by segmenting the corners of a rounded
//! rectangle clip, we can optimize both rendering the mask and the primitive by only
//! rasterize the corners in the mask and not applying any clipping to the segments of
//! the primitive that don't overlap the borders.
//!
//! It is a flexible system in the sense that different sources of segmentation (for
//! example two rounded rectangle clips) can affect the segmentation, and the possibility
//! to segment some effects such as specific clip kinds does not necessarily mean the
//! primitive will actually be segmented.
//!
//! ## Segments and clipping
//!
//! Segments of a primitive can be either not clipped, fully clipped, or partially clipped.
//! In the first two case we don't need a clip mask. For each partially masked segments, a
//! mask is rasterized using a render task. All of the interesting steps happen during frame
//! building.
//!
//! - The first step is to determine the segmentation and write the associated GPU data.
//!   See `PrimitiveInstance::build_segments_if_needed` and `write_brush_segment_description`
//!   in `prim_store/mod.rs` which uses the segment builder of this module.
//! - The second step is to generate the mask render tasks.
//!   See `BrushSegment::update_clip_task` and `RenderTask::new_mask`. For each segment that
//!   needs a mask, the contribution of all clips that affect the segment is added to the
//!   mask's render task.
//! - Segments are assigned to batches (See `batch.rs`). Segments of a given primitive can
//!   be assigned to different batches.
//!
//! See also the [`clip` module documentation][clip.rs] for details about how clipping
//! information is represented.
//!
//!
//! [clip.rs]: ../clip/index.html
//!

use api::{BorderRadius, ClipMode};
use api::units::*;
use std::{cmp, usize};
use crate::util::extract_inner_rect_safe;
use smallvec::SmallVec;

// We don't want to generate too many segments in edge cases, as it will result in a lot of
// clip mask overhead, and possibly exceeding the maximum row size of the GPU cache.
const MAX_SEGMENTS: usize = 64;

// Note: This can use up to 4 bits due to how it will be packed in
// the instance data.

/// Each bit of the edge AA mask is:
/// 0, when the edge of the primitive needs to be considered for AA
/// 1, when the edge of the segment needs to be considered for AA
///
/// *Note*: the bit values have to match the shader logic in
/// `write_transform_vertex()` function.
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
#[derive(Copy, PartialEq, Eq, Clone, PartialOrd, Ord, Hash, MallocSizeOf)]
pub struct EdgeAaSegmentMask(u8);

bitflags! {
    impl EdgeAaSegmentMask: u8 {
        ///
        const LEFT = 0x1;
        ///
        const TOP = 0x2;
        ///
        const RIGHT = 0x4;
        ///
        const BOTTOM = 0x8;
    }
}

impl core::fmt::Debug for EdgeAaSegmentMask {
    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
        if self.is_empty() {
            write!(f, "{:#x}", Self::empty().bits())
        } else {
            bitflags::parser::to_writer(self, f)
        }
    }
}

bitflags! {
    #[derive(Debug, Copy, PartialEq, Eq, Clone, PartialOrd, Ord, Hash)]
    pub struct ItemFlags: u8 {
        const X_ACTIVE = 0x1;
        const Y_ACTIVE = 0x2;
        const HAS_MASK = 0x4;
    }
}

// The segment builder outputs a list of these segments.
#[derive(Debug, PartialEq)]
pub struct Segment {
    pub rect: LayoutRect,
    pub has_mask: bool,
    pub edge_flags: EdgeAaSegmentMask,
    pub region_x: usize,
    pub region_y: usize,
}

// The segment builder creates a list of x/y axis events
// that are used to build a segment list. Right now, we
// don't bother providing a list of *which* clip regions
// are active for a given segment. Instead, if there is
// any clip mask present in a segment, we will just end
// up drawing each of the masks to that segment clip.
// This is a fairly rare case, but we can detect this
// in the future and only apply clip masks that are
// relevant to each segment region.
// TODO(gw): Provide clip region info with each segment.
#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd)]
enum EventKind {
    // Beginning of a clip (rounded) rect.
    BeginClip,
    // End of a clip (rounded) rect.
    EndClip,
    // Begin the next region in the primitive.
    BeginRegion,
}

// Events must be ordered such that when the coordinates
// of two events are the same, the end events are processed
// before the begin events. This ensures that we're able
// to detect which regions are active for a given segment.
impl Ord for EventKind {
    fn cmp(&self, other: &EventKind) -> cmp::Ordering {
        match (*self, *other) {
            (EventKind::BeginRegion, EventKind::BeginRegion) => {
                panic!("bug: regions must be non-overlapping")
            }
            (EventKind::EndClip, EventKind::BeginRegion) |
            (EventKind::BeginRegion, EventKind::BeginClip) => {
                cmp::Ordering::Less
            }
            (EventKind::BeginClip, EventKind::BeginRegion) |
            (EventKind::BeginRegion, EventKind::EndClip) => {
                cmp::Ordering::Greater
            }
            (EventKind::BeginClip, EventKind::BeginClip) |
            (EventKind::EndClip, EventKind::EndClip) => {
                cmp::Ordering::Equal
            }
            (EventKind::BeginClip, EventKind::EndClip) => {
                cmp::Ordering::Greater
            }
            (EventKind::EndClip, EventKind::BeginClip) => {
                cmp::Ordering::Less
            }
        }
    }
}

// A x/y event where we will create a vertex in the
// segment builder.
#[derive(Debug, Eq, PartialEq, PartialOrd)]
struct Event {
    value: Au,
    item_index: ItemIndex,
    kind: EventKind,
}

impl Ord for Event {
    fn cmp(&self, other: &Event) -> cmp::Ordering {
        self.value
            .cmp(&other.value)
            .then(self.kind.cmp(&other.kind))
    }
}

impl Event {
    fn begin(value: f32, index: usize) -> Event {
        Event {
            value: Au::from_f32_px(value),
            item_index: ItemIndex(index),
            kind: EventKind::BeginClip,
        }
    }

    fn end(value: f32, index: usize) -> Event {
        Event {
            value: Au::from_f32_px(value),
            item_index: ItemIndex(index),
            kind: EventKind::EndClip,
        }
    }

    fn region(value: f32) -> Event {
        Event {
            value: Au::from_f32_px(value),
            kind: EventKind::BeginRegion,
            item_index: ItemIndex(usize::MAX),
        }
    }

    fn update(
        &self,
        flag: ItemFlags,
        items: &mut [Item],
        region: &mut usize,
    ) {
        let is_active = match self.kind {
            EventKind::BeginClip => true,
            EventKind::EndClip => false,
            EventKind::BeginRegion => {
                *region += 1;
                return;
            }
        };

        items[self.item_index.0].flags.set(flag, is_active);
    }
}

// An item that provides some kind of clip region (either
// a clip in/out rect, or a mask region).
#[derive(Debug)]
struct Item {
    rect: LayoutRect,
    mode: Option<ClipMode>,
    flags: ItemFlags,
}

impl Item {
    fn new(
        rect: LayoutRect,
        mode: Option<ClipMode>,
        has_mask: bool,
    ) -> Item {
        let flags = if has_mask {
            ItemFlags::HAS_MASK
        } else {
            ItemFlags::empty()
        };

        Item {
            rect,
            mode,
            flags,
        }
    }
}

#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd)]
struct ItemIndex(usize);

// The main public interface to the segment module.
pub struct SegmentBuilder {
    items: Vec<Item>,
    inner_rect: Option<LayoutRect>,
    bounding_rect: Option<LayoutRect>,
    has_interesting_clips: bool,

    #[cfg(debug_assertions)]
    initialized: bool,
}

impl SegmentBuilder {
    // Create a new segment builder, supplying the primitive
    // local rect and associated local clip rect.
    pub fn new() -> SegmentBuilder {
        SegmentBuilder {
            items: Vec::with_capacity(4),
            bounding_rect: None,
            inner_rect: None,
            has_interesting_clips: false,
            #[cfg(debug_assertions)]
            initialized: false,
        }
    }

    pub fn initialize(
        &mut self,
        local_rect: LayoutRect,
        inner_rect: Option<LayoutRect>,
        local_clip_rect: LayoutRect,
    ) {
        self.items.clear();
        self.inner_rect = inner_rect;
        self.bounding_rect = Some(local_rect);

        self.push_clip_rect(local_rect, None, ClipMode::Clip);
        self.push_clip_rect(local_clip_rect, None, ClipMode::Clip);

        // This must be set after the push_clip_rect calls above, since we
        // want to skip segment building if those are the only clips.
        self.has_interesting_clips = false;

        #[cfg(debug_assertions)]
        {
            self.initialized = true;
        }
    }

    // Push a region defined by an inner and outer rect where there
    // is a mask required. This ensures that segments which intersect
    // with these areas will get a clip mask task allocated. This
    // is currently used to mark where a box-shadow region can affect
    // the pixels of a clip-mask. It might be useful for other types
    // such as dashed and dotted borders in the future.
    pub fn push_mask_region(
        &mut self,
        outer_rect: LayoutRect,
        inner_rect: LayoutRect,
        inner_clip_mode: Option<ClipMode>,
    ) {
        self.has_interesting_clips = true;

        if inner_rect.is_empty() {
            self.items.push(Item::new(
                outer_rect,
                None,
                true
            ));
            return;
        }

        debug_assert!(outer_rect.contains_box(&inner_rect));

        let p0 = outer_rect.min;
        let p1 = inner_rect.min;
        let p2 = inner_rect.max;
        let p3 = outer_rect.max;

        let segments = &[
            LayoutRect {
                min: LayoutPoint::new(p0.x, p0.y),
                max: LayoutPoint::new(p1.x, p1.y),
            },
            LayoutRect {
                min: LayoutPoint::new(p2.x, p0.y),
                max: LayoutPoint::new(p3.x, p1.y),
            },
            LayoutRect {
                min: LayoutPoint::new(p2.x, p2.y),
                max: LayoutPoint::new(p3.x, p3.y),
            },
            LayoutRect {
                min: LayoutPoint::new(p0.x, p2.y),
                max: LayoutPoint::new(p1.x, p3.y),
            },
            LayoutRect {
                min: LayoutPoint::new(p1.x, p0.y),
                max: LayoutPoint::new(p2.x, p1.y),
            },
            LayoutRect {
                min: LayoutPoint::new(p2.x, p1.y),
                max: LayoutPoint::new(p3.x, p2.y),
            },
            LayoutRect {
                min: LayoutPoint::new(p1.x, p2.y),
                max: LayoutPoint::new(p2.x, p3.y),
            },
            LayoutRect {
                min: LayoutPoint::new(p0.x, p1.y),
                max: LayoutPoint::new(p1.x, p2.y),
            },
        ];

        self.items.reserve(segments.len() + 1);

        for segment in segments {
            self.items.push(Item::new(
                *segment,
                None,
                true
            ));
        }

        if inner_clip_mode.is_some() {
            self.items.push(Item::new(
                inner_rect,
                inner_clip_mode,
                false,
            ));
        }
    }

    // Push some kind of clipping region into the segment builder.
    // If radius is None, it's a simple rect.
    pub fn push_clip_rect(
        &mut self,
        rect: LayoutRect,
        radius: Option<BorderRadius>,
        mode: ClipMode,
    ) {
        self.has_interesting_clips = true;

        // Keep track of a minimal bounding rect for the set of
        // segments that will be generated.
        if mode == ClipMode::Clip {
            self.bounding_rect = self.bounding_rect.and_then(|bounding_rect| {
                bounding_rect.intersection(&rect)
            });
        }
        let mode = Some(mode);

        match radius {
            Some(radius) => {
                // For a rounded rect, try to create a nine-patch where there
                // is a clip item for each corner, inner and edge region.
                match extract_inner_rect_safe(&rect, &radius) {
                    Some(inner) => {
                        let p0 = rect.min;
                        let p1 = inner.min;
                        let p2 = inner.max;
                        let p3 = rect.max;

                        self.items.reserve(9);

                        let corner_segments = &[
                            LayoutRect {
                                min: LayoutPoint::new(p0.x, p0.y),
                                max: LayoutPoint::new(p1.x, p1.y),
                            },
                            LayoutRect {
                                min: LayoutPoint::new(p2.x, p0.y),
                                max: LayoutPoint::new(p3.x, p1.y),
                            },
                            LayoutRect {
                                min: LayoutPoint::new(p2.x, p2.y),
                                max: LayoutPoint::new(p3.x, p3.y),
                            },
                            LayoutRect {
                                min: LayoutPoint::new(p0.x, p2.y),
                                max: LayoutPoint::new(p1.x, p3.y),
                            },
                        ];

                        for segment in corner_segments {
                            self.items.push(Item::new(
                                *segment,
                                mode,
                                true
                            ));
                        }

                        let other_segments = &[
                            LayoutRect {
                                min: LayoutPoint::new(p1.x, p0.y),
                                max: LayoutPoint::new(p2.x, p1.y),
                            },
                            LayoutRect {
                                min: LayoutPoint::new(p2.x, p1.y),
                                max: LayoutPoint::new(p3.x, p2.y),
                            },
                            LayoutRect {
                                min: LayoutPoint::new(p1.x, p2.y),
                                max: LayoutPoint::new(p2.x, p3.y),
                            },
                            LayoutRect {
                                min: LayoutPoint::new(p0.x, p1.y),
                                max: LayoutPoint::new(p1.x, p2.y),
                            },
                            LayoutRect {
                                min: LayoutPoint::new(p1.x, p1.y),
                                max: LayoutPoint::new(p2.x, p2.y),
                            },
                        ];

                        for segment in other_segments {
                            self.items.push(Item::new(
                                *segment,
                                mode,
                                false,
                            ));
                        }
                    }
                    None => {
                        // If we get here, we could not extract an inner rectangle
                        // for this clip region. This can occur in cases such as
                        // a rounded rect where the top-left and bottom-left radii
                        // result in overlapping rects. In that case, just create
                        // a single clip region for the entire rounded rect.
                        self.items.push(Item::new(
                            rect,
                            mode,
                            true,
                        ))
                    }
                }
            }
            None => {
                // For a simple rect, just create one clipping item.
                self.items.push(Item::new(
                    rect,
                    mode,
                    false,
                ))
            }
        }
    }

    // Consume this segment builder and produce a list of segments.
    pub fn build<F>(&mut self, mut f: F) where F: FnMut(&Segment) {
        #[cfg(debug_assertions)]
        debug_assert!(self.initialized);

        #[cfg(debug_assertions)]
        {
            self.initialized = false;
        }

        let bounding_rect = match self.bounding_rect {
            Some(bounding_rect) => bounding_rect,
            None => return,
        };

        if !self.has_interesting_clips {
            // There were no additional clips added, so don't bother building segments.
            // Just emit a single segment for the bounding rect of the primitive.
            f(&Segment {
                edge_flags: EdgeAaSegmentMask::all(),
                region_x: 0,
                region_y: 0,
                has_mask: false,
                rect: bounding_rect,
            });
            return
        }

        // First, filter out any items that don't intersect
        // with the visible bounding rect.
        self.items.retain(|item| item.rect.intersects(&bounding_rect));

        // Create events for each item
        let mut x_events : SmallVec<[Event; 4]> = SmallVec::new();
        let mut y_events : SmallVec<[Event; 4]> = SmallVec::new();

        for (item_index, item) in self.items.iter().enumerate() {
            let p0 = item.rect.min;
            let p1 = item.rect.max;

            x_events.push(Event::begin(p0.x, item_index));
            x_events.push(Event::end(p1.x, item_index));
            y_events.push(Event::begin(p0.y, item_index));
            y_events.push(Event::end(p1.y, item_index));
        }

        // Add the region events, if provided.
        if let Some(inner_rect) = self.inner_rect {
            x_events.push(Event::region(inner_rect.min.x));
            x_events.push(Event::region(inner_rect.max.x));

            y_events.push(Event::region(inner_rect.min.y));
            y_events.push(Event::region(inner_rect.max.y));
        }

        // Get the minimal bounding rect in app units. We will
        // work in fixed point in order to avoid float precision
        // error while handling events.
        let p0 = LayoutPointAu::new(
            Au::from_f32_px(bounding_rect.min.x),
            Au::from_f32_px(bounding_rect.min.y),
        );

        let p1 = LayoutPointAu::new(
            Au::from_f32_px(bounding_rect.max.x),
            Au::from_f32_px(bounding_rect.max.y),
        );

        // Sort the events in ascending order.
        x_events.sort();
        y_events.sort();

        // Generate segments from the event lists, by sweeping the y-axis
        // and then the x-axis for each event. This can generate a significant
        // number of segments, but most importantly, it ensures that there are
        // no t-junctions in the generated segments. It's probably possible
        // to come up with more efficient segmentation algorithms, at least
        // for simple / common cases.

        // Each coordinate is clamped to the bounds of the minimal
        // bounding rect. This ensures that we don't generate segments
        // outside that bounding rect, but does allow correctly handling
        // clips where the clip region starts outside the minimal
        // rect but still intersects with it.

        let mut prev_y = clamp(p0.y, y_events[0].value, p1.y);
        let mut region_y = 0;
        let mut segments : SmallVec<[_; 16]> = SmallVec::new();
        let mut x_count = 0;
        let mut y_count = 0;

        for ey in &y_events {
            let cur_y = clamp(p0.y, ey.value, p1.y);

            if cur_y != prev_y {
                let mut prev_x = clamp(p0.x, x_events[0].value, p1.x);
                let mut region_x = 0;

                for ex in &x_events {
                    let cur_x = clamp(p0.x, ex.value, p1.x);

                    if cur_x != prev_x {
                        segments.push(emit_segment_if_needed(
                            prev_x,
                            prev_y,
                            cur_x,
                            cur_y,
                            region_x,
                            region_y,
                            &self.items,
                        ));

                        prev_x = cur_x;
                        if y_count == 0 {
                            x_count += 1;
                        }
                    }

                    ex.update(
                        ItemFlags::X_ACTIVE,
                        &mut self.items,
                        &mut region_x,
                    );
                }

                prev_y = cur_y;
                y_count += 1;
            }

            ey.update(
                ItemFlags::Y_ACTIVE,
                &mut self.items,
                &mut region_y,
            );
        }

        // If we created more than 64 segments, just bail out and draw it as a single primitive
        // with a single mask, to avoid overhead of excessive amounts of segments. This can only
        // happen in pathological cases, for example a cascade of a dozen or more overlapping
        // and intersecting rounded clips.
        if segments.len() > MAX_SEGMENTS {
            f(&Segment {
                edge_flags: EdgeAaSegmentMask::all(),
                region_x: 0,
                region_y: 0,
                has_mask: true,
                rect: bounding_rect,
            });
            return
        }

        // Run user supplied closure for each valid segment.
        debug_assert_eq!(segments.len(), x_count * y_count);
        for y in 0 .. y_count {
            for x in 0 .. x_count {
                let mut edge_flags = EdgeAaSegmentMask::empty();

                if x == 0 || segments[y * x_count + x - 1].is_none() {
                    edge_flags |= EdgeAaSegmentMask::LEFT;
                }
                if x == x_count-1 || segments[y * x_count + x + 1].is_none() {
                    edge_flags |= EdgeAaSegmentMask::RIGHT;
                }
                if y == 0 || segments[(y-1) * x_count + x].is_none() {
                    edge_flags |= EdgeAaSegmentMask::TOP;
                }
                if y == y_count-1 || segments[(y+1) * x_count + x].is_none() {
                    edge_flags |= EdgeAaSegmentMask::BOTTOM;
                }

                if let Some(ref mut segment) = segments[y * x_count + x] {
                    segment.edge_flags = edge_flags;
                    f(segment);
                }
            }
        }
    }
}

fn clamp(low: Au, value: Au, high: Au) -> Au {
    value.max(low).min(high)
}

fn emit_segment_if_needed(
    x0: Au,
    y0: Au,
    x1: Au,
    y1: Au,
    region_x: usize,
    region_y: usize,
    items: &[Item],
) -> Option<Segment> {
    debug_assert!(x1 > x0);
    debug_assert!(y1 > y0);

    // TODO(gw): Don't scan the whole list of items for
    //           each segment rect. Store active list
    //           in a hash set or similar if this ever
    //           shows up in a profile.
    let mut has_clip_mask = false;

    for item in items {
        if item.flags.contains(ItemFlags::X_ACTIVE | ItemFlags::Y_ACTIVE) {
            has_clip_mask |= item.flags.contains(ItemFlags::HAS_MASK);

            if item.mode == Some(ClipMode::ClipOut) && !item.flags.contains(ItemFlags::HAS_MASK) {
                return None;
            }
        }
    }

    let segment_rect = LayoutRect {
        min: LayoutPoint::new(
            x0.to_f32_px(),
            y0.to_f32_px(),
        ),
        max: LayoutPoint::new(
            x1.to_f32_px(),
            y1.to_f32_px(),
        ),
    };

    Some(Segment {
        rect: segment_rect,
        has_mask: has_clip_mask,
        edge_flags: EdgeAaSegmentMask::empty(),
        region_x,
        region_y,
    })
}

#[cfg(test)]
mod test {
    use api::{BorderRadius, ClipMode};
    use api::units::{LayoutPoint, LayoutRect};
    use super::{Segment, SegmentBuilder, EdgeAaSegmentMask};
    use std::cmp;

    fn rect(x0: f32, y0: f32, x1: f32, y1: f32) -> LayoutRect {
        LayoutRect {
            min: LayoutPoint::new(x0, y0),
            max: LayoutPoint::new(x1, y1),
        }
    }

    fn seg(
        x0: f32,
        y0: f32,
        x1: f32,
        y1: f32,
        has_mask: bool,
        edge_flags: Option<EdgeAaSegmentMask>,
    ) -> Segment {
        seg_region(x0, y0, x1, y1, 0, 0, has_mask, edge_flags)
    }

    fn seg_region(
        x0: f32,
        y0: f32,
        x1: f32,
        y1: f32,
        region_x: usize,
        region_y: usize,
        has_mask: bool,
        edge_flags: Option<EdgeAaSegmentMask>,
    ) -> Segment {
        Segment {
            rect: LayoutRect {
                min: LayoutPoint::new(x0, y0),
                max: LayoutPoint::new(x1, y1),
            },
            has_mask,
            edge_flags: edge_flags.unwrap_or(EdgeAaSegmentMask::empty()),
            region_x,
            region_y,
        }
    }

    fn segment_sorter(s0: &Segment, s1: &Segment) -> cmp::Ordering {
        let r0 = &s0.rect;
        let r1 = &s1.rect;

        (
            (r0.min.x, r0.min.y, r0.max.x, r0.max.y)
        ).partial_cmp(&
            (r1.min.x, r1.min.y, r1.max.x, r1.max.y)
        ).unwrap()
    }

    fn seg_test(
        local_rect: LayoutRect,
        inner_rect: Option<LayoutRect>,
        local_clip_rect: LayoutRect,
        clips: &[(LayoutRect, Option<BorderRadius>, ClipMode)],
        expected_segments: &mut [Segment]
    ) {
        let mut sb = SegmentBuilder::new();
        sb.initialize(
            local_rect,
            inner_rect,
            local_clip_rect,
        );
        sb.push_clip_rect(local_rect, None, ClipMode::Clip);
        sb.push_clip_rect(local_clip_rect, None, ClipMode::Clip);
        let mut segments = Vec::new();
        for &(rect, radius, mode) in clips {
            sb.push_clip_rect(rect, radius, mode);
        }
        sb.build(|segment| {
            segments.push(Segment {
                ..*segment
            });
        });
        segments.sort_by(segment_sorter);
        expected_segments.sort_by(segment_sorter);
        assert_eq!(
            segments.len(),
            expected_segments.len(),
            "segments\n{:?}\nexpected\n{:?}\n",
            segments,
            expected_segments
        );
        for (segment, expected) in segments.iter().zip(expected_segments.iter()) {
            assert_eq!(segment, expected);
        }
    }

    #[test]
    fn segment_empty() {
        seg_test(
            rect(0.0, 0.0, 0.0, 0.0),
            None,
            rect(0.0, 0.0, 0.0, 0.0),
            &[],
            &mut [],
        );
    }

    #[test]
    fn segment_single() {
        seg_test(
            rect(10.0, 20.0, 30.0, 40.0),
            None,
            rect(10.0, 20.0, 30.0, 40.0),
            &[],
            &mut [
                seg(10.0, 20.0, 30.0, 40.0, false,
                    Some(EdgeAaSegmentMask::LEFT |
                         EdgeAaSegmentMask::TOP |
                         EdgeAaSegmentMask::RIGHT |
                         EdgeAaSegmentMask::BOTTOM
                    )
                ),
            ],
        );
    }

    #[test]
    fn segment_single_clip() {
        seg_test(
            rect(10.0, 20.0, 30.0, 40.0),
            None,
            rect(10.0, 20.0, 25.0, 35.0),
            &[],
            &mut [
                seg(10.0, 20.0, 25.0, 35.0, false,
                    Some(EdgeAaSegmentMask::LEFT |
                         EdgeAaSegmentMask::TOP |
                         EdgeAaSegmentMask::RIGHT |
                         EdgeAaSegmentMask::BOTTOM
                    )
                ),
            ],
        );
    }

    #[test]
    fn segment_inner_clip() {
        seg_test(
            rect(10.0, 20.0, 30.0, 40.0),
            None,
            rect(15.0, 25.0, 25.0, 35.0),
            &[],
            &mut [
                seg(15.0, 25.0, 25.0, 35.0, false,
                    Some(EdgeAaSegmentMask::LEFT |
                         EdgeAaSegmentMask::TOP |
                         EdgeAaSegmentMask::RIGHT |
                         EdgeAaSegmentMask::BOTTOM
                    )
                ),
            ],
        );
    }

    #[test]
    fn segment_outer_clip() {
        seg_test(
            rect(15.0, 25.0, 25.0, 35.0),
            None,
            rect(10.0, 20.0, 30.0, 40.0),
            &[],
            &mut [
                seg(15.0, 25.0, 25.0, 35.0, false,
                    Some(EdgeAaSegmentMask::LEFT |
                         EdgeAaSegmentMask::TOP |
                         EdgeAaSegmentMask::RIGHT |
                         EdgeAaSegmentMask::BOTTOM
                    )
                ),
            ],
        );
    }

    #[test]
    fn segment_clip_int() {
        seg_test(
            rect(10.0, 20.0, 30.0, 40.0),
            None,
            rect(20.0, 10.0, 40.0, 30.0),
            &[],
            &mut [
                seg(20.0, 20.0, 30.0, 30.0, false,
                    Some(EdgeAaSegmentMask::LEFT |
                         EdgeAaSegmentMask::TOP |
                         EdgeAaSegmentMask::RIGHT |
                         EdgeAaSegmentMask::BOTTOM
                    )
                ),
            ],
        );
    }

    #[test]
    fn segment_clip_disjoint() {
        seg_test(
            rect(10.0, 20.0, 30.0, 40.0),
            None,
            rect(30.0, 20.0, 50.0, 40.0),
            &[],
            &mut [],
        );
    }

    #[test]
    fn segment_clips() {
        seg_test(
            rect(0.0, 0.0, 100.0, 100.0),
            None,
            rect(-1000.0, -1000.0, 1000.0, 1000.0),
            &[
                (rect(20.0, 20.0, 40.0, 40.0), None, ClipMode::Clip),
                (rect(40.0, 20.0, 60.0, 40.0), None, ClipMode::Clip),
            ],
            &mut [
            ],
        );
    }

    #[test]
    fn segment_rounded_clip() {
        seg_test(
            rect(0.0, 0.0, 100.0, 100.0),
            None,
            rect(-1000.0, -1000.0, 1000.0, 1000.0),
            &[
                (rect(20.0, 20.0, 60.0, 60.0), Some(BorderRadius::uniform(10.0)), ClipMode::Clip),
            ],
            &mut [
                // corners
                seg(20.0, 20.0, 30.0, 30.0, true, Some(EdgeAaSegmentMask::LEFT | EdgeAaSegmentMask::TOP)),
                seg(20.0, 50.0, 30.0, 60.0, true, Some(EdgeAaSegmentMask::LEFT | EdgeAaSegmentMask::BOTTOM)),
                seg(50.0, 20.0, 60.0, 30.0, true, Some(EdgeAaSegmentMask::RIGHT | EdgeAaSegmentMask::TOP)),
                seg(50.0, 50.0, 60.0, 60.0, true, Some(EdgeAaSegmentMask::RIGHT | EdgeAaSegmentMask::BOTTOM)),

                // inner
                seg(30.0, 30.0, 50.0, 50.0, false, None),

                // edges
                seg(30.0, 20.0, 50.0, 30.0, false, Some(EdgeAaSegmentMask::TOP)),
                seg(30.0, 50.0, 50.0, 60.0, false, Some(EdgeAaSegmentMask::BOTTOM)),
                seg(20.0, 30.0, 30.0, 50.0, false, Some(EdgeAaSegmentMask::LEFT)),
                seg(50.0, 30.0, 60.0, 50.0, false, Some(EdgeAaSegmentMask::RIGHT)),
            ],
        );
    }

    #[test]
    fn segment_clip_out() {
        seg_test(
            rect(0.0, 0.0, 100.0, 100.0),
            None,
            rect(-1000.0, -1000.0, 2000.0, 2000.0),
            &[
                (rect(20.0, 20.0, 60.0, 60.0), None, ClipMode::ClipOut),
            ],
            &mut [
                seg(0.0, 0.0, 20.0, 20.0, false, Some(EdgeAaSegmentMask::TOP | EdgeAaSegmentMask::LEFT)),
                seg(20.0, 0.0, 60.0, 20.0, false, Some(EdgeAaSegmentMask::TOP | EdgeAaSegmentMask::BOTTOM)),
                seg(60.0, 0.0, 100.0, 20.0, false, Some(EdgeAaSegmentMask::TOP | EdgeAaSegmentMask::RIGHT)),

                seg(0.0, 20.0, 20.0, 60.0, false, Some(EdgeAaSegmentMask::LEFT | EdgeAaSegmentMask::RIGHT)),
                seg(60.0, 20.0, 100.0, 60.0, false, Some(EdgeAaSegmentMask::RIGHT | EdgeAaSegmentMask::LEFT)),

                seg(0.0, 60.0, 20.0, 100.0, false, Some(EdgeAaSegmentMask::LEFT | EdgeAaSegmentMask::BOTTOM)),
                seg(20.0, 60.0, 60.0, 100.0, false, Some(EdgeAaSegmentMask::BOTTOM | EdgeAaSegmentMask::TOP)),
                seg(60.0, 60.0, 100.0, 100.0, false, Some(EdgeAaSegmentMask::BOTTOM | EdgeAaSegmentMask::RIGHT)),
            ],
        );
    }

    #[test]
    fn segment_rounded_clip_out() {
        seg_test(
            rect(0.0, 0.0, 100.0, 100.0),
            None,
            rect(-1000.0, -1000.0, 2000.0, 2000.0),
            &[
                (rect(20.0, 20.0, 60.0, 60.0), Some(BorderRadius::uniform(10.0)), ClipMode::ClipOut),
            ],
            &mut [
                // top row
                seg(0.0, 0.0, 20.0, 20.0, false, Some(EdgeAaSegmentMask::TOP | EdgeAaSegmentMask::LEFT)),
                seg(20.0, 0.0, 30.0, 20.0, false, Some(EdgeAaSegmentMask::TOP)),
                seg(30.0, 0.0, 50.0, 20.0, false, Some(EdgeAaSegmentMask::TOP | EdgeAaSegmentMask::BOTTOM)),
                seg(50.0, 0.0, 60.0, 20.0, false, Some(EdgeAaSegmentMask::TOP)),
                seg(60.0, 0.0, 100.0, 20.0, false, Some(EdgeAaSegmentMask::TOP | EdgeAaSegmentMask::RIGHT)),

                // left
                seg(0.0, 20.0, 20.0, 30.0, false, Some(EdgeAaSegmentMask::LEFT)),
                seg(0.0, 30.0, 20.0, 50.0, false, Some(EdgeAaSegmentMask::LEFT | EdgeAaSegmentMask::RIGHT)),
                seg(0.0, 50.0, 20.0, 60.0, false, Some(EdgeAaSegmentMask::LEFT)),

                // right
                seg(60.0, 20.0, 100.0, 30.0, false, Some(EdgeAaSegmentMask::RIGHT)),
                seg(60.0, 30.0, 100.0, 50.0, false, Some(EdgeAaSegmentMask::RIGHT | EdgeAaSegmentMask::LEFT)),
                seg(60.0, 50.0, 100.0, 60.0, false, Some(EdgeAaSegmentMask::RIGHT)),

                // bottom row
                seg(0.0, 60.0, 20.0, 100.0, false, Some(EdgeAaSegmentMask::LEFT | EdgeAaSegmentMask::BOTTOM)),
                seg(20.0, 60.0, 30.0, 100.0, false, Some(EdgeAaSegmentMask::BOTTOM)),
                seg(30.0, 60.0, 50.0, 100.0, false, Some(EdgeAaSegmentMask::BOTTOM | EdgeAaSegmentMask::TOP)),
                seg(50.0, 60.0, 60.0, 100.0, false, Some(EdgeAaSegmentMask::BOTTOM)),
                seg(60.0, 60.0, 100.0, 100.0, false, Some(EdgeAaSegmentMask::RIGHT | EdgeAaSegmentMask::BOTTOM)),

                // inner corners
                seg(20.0, 20.0, 30.0, 30.0, true, Some(EdgeAaSegmentMask::RIGHT | EdgeAaSegmentMask::BOTTOM)),
                seg(20.0, 50.0, 30.0, 60.0, true, Some(EdgeAaSegmentMask::TOP | EdgeAaSegmentMask::RIGHT)),
                seg(50.0, 20.0, 60.0, 30.0, true, Some(EdgeAaSegmentMask::LEFT | EdgeAaSegmentMask::BOTTOM)),
                seg(50.0, 50.0, 60.0, 60.0, true, Some(EdgeAaSegmentMask::LEFT | EdgeAaSegmentMask::TOP)),
            ],
        );
    }

    #[test]
    fn segment_clip_in_clip_out() {
        seg_test(
            rect(0.0, 0.0, 100.0, 100.0),
            None,
            rect(-1000.0, -1000.0, 2000.0, 2000.0),
            &[
                (rect(20.0, 20.0, 60.0, 60.0), None, ClipMode::Clip),
                (rect(50.0, 50.0, 80.0, 80.0), None, ClipMode::ClipOut),
            ],
            &mut [
                seg(20.0, 20.0, 50.0, 50.0, false, Some(EdgeAaSegmentMask::LEFT | EdgeAaSegmentMask::TOP)),
                seg(50.0, 20.0, 60.0, 50.0, false, Some(EdgeAaSegmentMask::TOP | EdgeAaSegmentMask::RIGHT | EdgeAaSegmentMask::BOTTOM)),
                seg(20.0, 50.0, 50.0, 60.0, false, Some(EdgeAaSegmentMask::LEFT | EdgeAaSegmentMask::BOTTOM | EdgeAaSegmentMask::RIGHT)),
            ],
        );
    }

    #[test]
    fn segment_rounded_clip_overlap() {
        seg_test(
            rect(0.0, 0.0, 100.0, 100.0),
            None,
            rect(0.0, 0.0, 100.0, 100.0),
            &[
                (rect(0.0, 0.0, 10.0, 10.0), None, ClipMode::ClipOut),
                (rect(0.0, 0.0, 100.0, 100.0), Some(BorderRadius::uniform(10.0)), ClipMode::Clip),
            ],
            &mut [
                // corners
                seg(0.0, 90.0, 10.0, 100.0, true, Some(EdgeAaSegmentMask::LEFT | EdgeAaSegmentMask::BOTTOM)),
                seg(90.0, 0.0, 100.0, 10.0, true, Some(EdgeAaSegmentMask::RIGHT | EdgeAaSegmentMask::TOP)),
                seg(90.0, 90.0, 100.0, 100.0, true, Some(EdgeAaSegmentMask::RIGHT | EdgeAaSegmentMask::BOTTOM)),

                // inner
                seg(10.0, 10.0, 90.0, 90.0, false, None),

                // edges
                seg(10.0, 0.0, 90.0, 10.0, false, Some(EdgeAaSegmentMask::TOP | EdgeAaSegmentMask::LEFT)),
                seg(10.0, 90.0, 90.0, 100.0, false, Some(EdgeAaSegmentMask::BOTTOM)),
                seg(0.0, 10.0, 10.0, 90.0, false, Some(EdgeAaSegmentMask::LEFT | EdgeAaSegmentMask::TOP)),
                seg(90.0, 10.0, 100.0, 90.0, false, Some(EdgeAaSegmentMask::RIGHT)),
            ],
        );
    }

    #[test]
    fn segment_rounded_clip_overlap_reverse() {
        seg_test(
            rect(0.0, 0.0, 100.0, 100.0),
            None,
            rect(0.0, 0.0, 100.0, 100.0),
            &[
                (rect(10.0, 10.0, 90.0, 90.0), None, ClipMode::Clip),
                (rect(0.0, 0.0, 100.0, 100.0), Some(BorderRadius::uniform(10.0)), ClipMode::Clip),
            ],
            &mut [
                seg(10.0, 10.0, 90.0, 90.0, false,
                    Some(EdgeAaSegmentMask::LEFT |
                         EdgeAaSegmentMask::TOP |
                         EdgeAaSegmentMask::RIGHT |
                         EdgeAaSegmentMask::BOTTOM
                    )
                ),
            ],
        );
    }

    #[test]
    fn segment_clip_in_clip_out_overlap() {
        seg_test(
            rect(0.0, 0.0, 100.0, 100.0),
            None,
            rect(0.0, 0.0, 100.0, 100.0),
            &[
                (rect(10.0, 10.0, 90.0, 90.0), None, ClipMode::Clip),
                (rect(10.0, 10.0, 90.0, 90.0), None, ClipMode::ClipOut),
            ],
            &mut [
            ],
        );
    }

    #[test]
    fn segment_event_order() {
        seg_test(
            rect(0.0, 0.0, 100.0, 100.0),
            None,
            rect(0.0, 0.0, 100.0, 100.0),
            &[
                (rect(0.0, 0.0, 100.0, 90.0), None, ClipMode::ClipOut),
            ],
            &mut [
                seg(0.0, 90.0, 100.0, 100.0, false, Some(
                    EdgeAaSegmentMask::LEFT |
                    EdgeAaSegmentMask::RIGHT |
                    EdgeAaSegmentMask::BOTTOM |
                    EdgeAaSegmentMask::TOP
                )),
            ],
        );
    }

    #[test]
    fn segment_region_simple() {
        seg_test(
            rect(0.0, 0.0, 100.0, 100.0),
            Some(rect(20.0, 40.0, 60.0, 80.0)),
            rect(0.0, 0.0, 100.0, 100.0),
            &[
            ],
            &mut [
                seg_region(
                    0.0, 0.0,
                    20.0, 40.0,
                    0, 0,
                    false,
                    Some(EdgeAaSegmentMask::LEFT | EdgeAaSegmentMask::TOP)
                ),

                seg_region(
                    20.0, 0.0,
                    60.0, 40.0,
                    1, 0,
                    false,
                    Some(EdgeAaSegmentMask::TOP)
                ),

                seg_region(
                    60.0, 0.0,
                    100.0, 40.0,
                    2, 0,
                    false,
                    Some(EdgeAaSegmentMask::TOP | EdgeAaSegmentMask::RIGHT)
                ),

                seg_region(
                    0.0, 40.0,
                    20.0, 80.0,
                    0, 1,
                    false,
                    Some(EdgeAaSegmentMask::LEFT)
                ),

                seg_region(
                    20.0, 40.0,
                    60.0, 80.0,
                    1, 1,
                    false,
                    None,
                ),

                seg_region(
                    60.0, 40.0,
                    100.0, 80.0,
                    2, 1,
                    false,
                    Some(EdgeAaSegmentMask::RIGHT)
                ),

                seg_region(
                    0.0, 80.0,
                    20.0, 100.0,
                    0, 2,
                    false,
                    Some(EdgeAaSegmentMask::LEFT | EdgeAaSegmentMask::BOTTOM)
                ),

                seg_region(
                    20.0, 80.0,
                    60.0, 100.0,
                    1, 2,
                    false,
                    Some(EdgeAaSegmentMask::BOTTOM),
                ),

                seg_region(
                    60.0, 80.0,
                    100.0, 100.0,
                    2, 2,
                    false,
                    Some(EdgeAaSegmentMask::RIGHT | EdgeAaSegmentMask::BOTTOM)
                ),

            ],
        );
    }

    #[test]
    fn segment_region_clip() {
        seg_test(
            rect(0.0, 0.0, 100.0, 100.0),
            Some(rect(20.0, 40.0, 60.0, 80.0)),
            rect(0.0, 0.0, 100.0, 100.0),
            &[
                (rect(0.0, 0.0, 100.0, 90.0), None, ClipMode::ClipOut),
            ],
            &mut [
                seg_region(
                    0.0, 90.0,
                    20.0, 100.0,
                    0, 2,
                    false,
                    Some(EdgeAaSegmentMask::LEFT | EdgeAaSegmentMask::BOTTOM | EdgeAaSegmentMask::TOP)
                ),

                seg_region(
                    20.0, 90.0,
                    60.0, 100.0,
                    1, 2,
                    false,
                    Some(EdgeAaSegmentMask::BOTTOM | EdgeAaSegmentMask::TOP),
                ),

                seg_region(
                    60.0, 90.0,
                    100.0, 100.0,
                    2, 2,
                    false,
                    Some(EdgeAaSegmentMask::RIGHT | EdgeAaSegmentMask::BOTTOM | EdgeAaSegmentMask::TOP)
                ),

            ],
        );
    }

    #[test]
    fn segment_region_clip2() {
        seg_test(
            rect(0.0, 0.0, 100.0, 100.0),
            Some(rect(20.0, 20.0, 80.0, 80.0)),
            rect(0.0, 0.0, 100.0, 100.0),
            &[
                (rect(20.0, 20.0, 100.0, 100.0), None, ClipMode::ClipOut),
            ],
            &mut [
                seg_region(
                    0.0, 0.0,
                    20.0, 20.0,
                    0, 0,
                    false,
                    Some(EdgeAaSegmentMask::LEFT | EdgeAaSegmentMask::TOP)
                ),

                seg_region(
                    20.0, 0.0,
                    80.0, 20.0,
                    1, 0,
                    false,
                    Some(EdgeAaSegmentMask::TOP | EdgeAaSegmentMask::BOTTOM),
                ),

                seg_region(
                    80.0, 0.0,
                    100.0, 20.0,
                    2, 0,
                    false,
                    Some(EdgeAaSegmentMask::RIGHT | EdgeAaSegmentMask::TOP | EdgeAaSegmentMask::BOTTOM)
                ),

                seg_region(
                    0.0, 20.0,
                    20.0, 80.0,
                    0, 1,
                    false,
                    Some(EdgeAaSegmentMask::LEFT | EdgeAaSegmentMask::RIGHT)
                ),

                seg_region(
                    0.0, 80.0,
                    20.0, 100.0,
                    0, 2,
                    false,
                    Some(EdgeAaSegmentMask::LEFT | EdgeAaSegmentMask::BOTTOM | EdgeAaSegmentMask::RIGHT)
                ),
            ],
        );
    }

    #[test]
    fn segment_region_clip3() {
        seg_test(
            rect(0.0, 0.0, 100.0, 100.0),
            Some(rect(20.0, 20.0, 80.0, 80.0)),
            rect(0.0, 0.0, 100.0, 100.0),
            &[
                (rect(10.0, 10.0, 30.0, 30.0), None, ClipMode::Clip),
            ],
            &mut [
                seg_region(
                    10.0, 10.0,
                    20.0, 20.0,
                    0, 0,
                    false,
                    Some(EdgeAaSegmentMask::TOP | EdgeAaSegmentMask::LEFT),
                ),

                seg_region(
                    20.0, 10.0,
                    30.0, 20.0,
                    1, 0,
                    false,
                    Some(EdgeAaSegmentMask::TOP | EdgeAaSegmentMask::RIGHT),
                ),

                seg_region(
                    10.0, 20.0,
                    20.0, 30.0,
                    0, 1,
                    false,
                    Some(EdgeAaSegmentMask::BOTTOM | EdgeAaSegmentMask::LEFT),
                ),

                seg_region(
                    20.0, 20.0,
                    30.0, 30.0,
                    1, 1,
                    false,
                    Some(EdgeAaSegmentMask::BOTTOM | EdgeAaSegmentMask::RIGHT),
                ),
            ],
        );
    }
}

[ Dauer der Verarbeitung: 0.42 Sekunden  (vorverarbeitet)  ]

                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge