Eine aufbereitete Darstellung der Quelle

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

Benutzer

Quelle  segment.rs   Sprache: Rust

 

/* 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.

/// *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 EdgeMask(u8);

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

impl core::fmt::Debug for EdgeMask {
    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)
        }
    }
}

impl EdgeMask {
    /// Returns a rectangle with the egdes of `a` or `b`, selected indicidually.
    ///
    /// For each side bit, if it is set then use the edge from `a`, else use the edge from `b`.
    pub fn select<T: Copy, U>(&self, a: euclid::Box2D<T, U>, b: euclid::Box2D<T, U>) -> euclid::Box2D<T, U> {
        let mut rect = b;
        if self.contains(Self::LEFT) {
            rect.min.x = a.min.x;
        }
        if self.contains(Self::TOP) {
            rect.min.y = a.min.y;
        }
        if self.contains(Self::RIGHT) {
            rect.max.x = a.max.x;
        }
        if self.contains(Self::BOTTOM) {
            rect.max.y = a.max.y;
        }

        rect
    }
}

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: EdgeMask,
    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: EdgeMask::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: EdgeMask::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 = EdgeMask::empty();

                if x == 0 || segments[y * x_count + x - 1].is_none() {
                    edge_flags |= EdgeMask::LEFT;
                }
                if x == x_count-1 || segments[y * x_count + x + 1].is_none() {
                    edge_flags |= EdgeMask::RIGHT;
                }
                if y == 0 || segments[(y-1) * x_count + x].is_none() {
                    edge_flags |= EdgeMask::TOP;
                }
                if y == y_count-1 || segments[(y+1) * x_count + x].is_none() {
                    edge_flags |= EdgeMask::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: EdgeMask::empty(),
        region_x,
        region_y,
    })
}

#[cfg(test)]
mod test {
    use api::{BorderRadius, ClipMode};
    use api::units::{LayoutPoint, LayoutRect};
    use super::{Segment, SegmentBuilder, EdgeMask};
    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<EdgeMask>,
    ) -> 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<EdgeMask>,
    ) -> Segment {
        Segment {
            rect: LayoutRect {
                min: LayoutPoint::new(x0, y0),
                max: LayoutPoint::new(x1, y1),
            },
            has_mask,
            edge_flags: edge_flags.unwrap_or(EdgeMask::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(EdgeMask::LEFT |
                         EdgeMask::TOP |
                         EdgeMask::RIGHT |
                         EdgeMask::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(EdgeMask::LEFT |
                         EdgeMask::TOP |
                         EdgeMask::RIGHT |
                         EdgeMask::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(EdgeMask::LEFT |
                         EdgeMask::TOP |
                         EdgeMask::RIGHT |
                         EdgeMask::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(EdgeMask::LEFT |
                         EdgeMask::TOP |
                         EdgeMask::RIGHT |
                         EdgeMask::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(EdgeMask::LEFT |
                         EdgeMask::TOP |
                         EdgeMask::RIGHT |
                         EdgeMask::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(EdgeMask::LEFT | EdgeMask::TOP)),
                seg(20.0, 50.0, 30.0, 60.0, true, Some(EdgeMask::LEFT | EdgeMask::BOTTOM)),
                seg(50.0, 20.0, 60.0, 30.0, true, Some(EdgeMask::RIGHT | EdgeMask::TOP)),
                seg(50.0, 50.0, 60.0, 60.0, true, Some(EdgeMask::RIGHT | EdgeMask::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(EdgeMask::TOP)),
                seg(30.0, 50.0, 50.0, 60.0, false, Some(EdgeMask::BOTTOM)),
                seg(20.0, 30.0, 30.0, 50.0, false, Some(EdgeMask::LEFT)),
                seg(50.0, 30.0, 60.0, 50.0, false, Some(EdgeMask::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(EdgeMask::TOP | EdgeMask::LEFT)),
                seg(20.0, 0.0, 60.0, 20.0, false, Some(EdgeMask::TOP | EdgeMask::BOTTOM)),
                seg(60.0, 0.0, 100.0, 20.0, false, Some(EdgeMask::TOP | EdgeMask::RIGHT)),

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

                seg(0.0, 60.0, 20.0, 100.0, false, Some(EdgeMask::LEFT | EdgeMask::BOTTOM)),
                seg(20.0, 60.0, 60.0, 100.0, false, Some(EdgeMask::BOTTOM | EdgeMask::TOP)),
                seg(60.0, 60.0, 100.0, 100.0, false, Some(EdgeMask::BOTTOM | EdgeMask::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(EdgeMask::TOP | EdgeMask::LEFT)),
                seg(20.0, 0.0, 30.0, 20.0, false, Some(EdgeMask::TOP)),
                seg(30.0, 0.0, 50.0, 20.0, false, Some(EdgeMask::TOP | EdgeMask::BOTTOM)),
                seg(50.0, 0.0, 60.0, 20.0, false, Some(EdgeMask::TOP)),
                seg(60.0, 0.0, 100.0, 20.0, false, Some(EdgeMask::TOP | EdgeMask::RIGHT)),

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

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

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

                // inner corners
                seg(20.0, 20.0, 30.0, 30.0, true, Some(EdgeMask::RIGHT | EdgeMask::BOTTOM)),
                seg(20.0, 50.0, 30.0, 60.0, true, Some(EdgeMask::TOP | EdgeMask::RIGHT)),
                seg(50.0, 20.0, 60.0, 30.0, true, Some(EdgeMask::LEFT | EdgeMask::BOTTOM)),
                seg(50.0, 50.0, 60.0, 60.0, true, Some(EdgeMask::LEFT | EdgeMask::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(EdgeMask::LEFT | EdgeMask::TOP)),
                seg(50.0, 20.0, 60.0, 50.0, false, Some(EdgeMask::TOP | EdgeMask::RIGHT | EdgeMask::BOTTOM)),
                seg(20.0, 50.0, 50.0, 60.0, false, Some(EdgeMask::LEFT | EdgeMask::BOTTOM | EdgeMask::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(EdgeMask::LEFT | EdgeMask::BOTTOM)),
                seg(90.0, 0.0, 100.0, 10.0, true, Some(EdgeMask::RIGHT | EdgeMask::TOP)),
                seg(90.0, 90.0, 100.0, 100.0, true, Some(EdgeMask::RIGHT | EdgeMask::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(EdgeMask::TOP | EdgeMask::LEFT)),
                seg(10.0, 90.0, 90.0, 100.0, false, Some(EdgeMask::BOTTOM)),
                seg(0.0, 10.0, 10.0, 90.0, false, Some(EdgeMask::LEFT | EdgeMask::TOP)),
                seg(90.0, 10.0, 100.0, 90.0, false, Some(EdgeMask::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(EdgeMask::LEFT |
                         EdgeMask::TOP |
                         EdgeMask::RIGHT |
                         EdgeMask::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(
                    EdgeMask::LEFT |
                    EdgeMask::RIGHT |
                    EdgeMask::BOTTOM |
                    EdgeMask::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(EdgeMask::LEFT | EdgeMask::TOP)
                ),

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

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

                seg_region(
                    0.0, 40.0,
                    20.0, 80.0,
                    0, 1,
                    false,
                    Some(EdgeMask::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(EdgeMask::RIGHT)
                ),

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

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

                seg_region(
                    60.0, 80.0,
                    100.0, 100.0,
                    2, 2,
                    false,
                    Some(EdgeMask::RIGHT | EdgeMask::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(EdgeMask::LEFT | EdgeMask::BOTTOM | EdgeMask::TOP)
                ),

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

                seg_region(
                    60.0, 90.0,
                    100.0, 100.0,
                    2, 2,
                    false,
                    Some(EdgeMask::RIGHT | EdgeMask::BOTTOM | EdgeMask::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(EdgeMask::LEFT | EdgeMask::TOP)
                ),

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

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

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

                seg_region(
                    0.0, 80.0,
                    20.0, 100.0,
                    0, 2,
                    false,
                    Some(EdgeMask::LEFT | EdgeMask::BOTTOM | EdgeMask::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(EdgeMask::TOP | EdgeMask::LEFT),
                ),

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

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

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

Messung V0.5 in Prozent
C=90 H=100 G=95

¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.28Angebot  (Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können 2026-09-27) ¤

*Eine klare Vorstellung vom Zielzustand






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Open Source Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Statistik
#Sources=1126864
#Domains=1897691