use api::{BorderRadius, ClipMode};
use api::units::*;
use std::{cmp, usize};
use crate::util::{extract_inner_rect_safe};
use smallvec::SmallVec;
bitflags! {
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
#[derive(MallocSizeOf)]
pub struct EdgeAaSegmentMask: u8 {
const LEFT = 0x1;
const TOP = 0x2;
const RIGHT = 0x4;
const BOTTOM = 0x8;
}
}
bitflags! {
pub struct ItemFlags: u8 {
const X_ACTIVE = 0x1;
const Y_ACTIVE = 0x2;
const HAS_MASK = 0x4;
}
}
#[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,
}
#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd)]
enum EventKind {
BeginClip,
EndClip,
BeginRegion,
}
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
}
}
}
}
#[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);
}
}
#[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);
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 {
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);
self.has_interesting_clips = false;
#[cfg(debug_assertions)]
{
self.initialized = true;
}
}
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,
));
}
}
pub fn push_clip_rect(
&mut self,
rect: LayoutRect,
radius: Option<BorderRadius>,
mode: ClipMode,
) {
self.has_interesting_clips = true;
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) => {
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 => {
self.items.push(Item::new(
rect,
mode,
true,
))
}
}
}
None => {
self.items.push(Item::new(
rect,
mode,
false,
))
}
}
}
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 {
f(&Segment {
edge_flags: EdgeAaSegmentMask::all(),
region_x: 0,
region_y: 0,
has_mask: false,
rect: bounding_rect,
});
return
}
self.items.retain(|item| item.rect.intersects(&bounding_rect));
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));
}
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));
}
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),
);
x_events.sort();
y_events.sort();
let mut prev_y = clamp(p0.y, y_events[0].value, p1.y);
let mut region_y = 0;
let mut segments : SmallVec<[_; 4]> = 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,
);
}
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);
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 [
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)),
seg(30.0, 30.0, 50.0, 50.0, false, None),
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 [
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)),
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)),
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)),
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)),
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 [
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)),
seg(10.0, 10.0, 90.0, 90.0, false, None),
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),
),
],
);
}
}