Skip to content

Commit

Permalink
Use new webrender rounded clip API.
Browse files Browse the repository at this point in the history
  • Loading branch information
jdm committed May 12, 2020
1 parent eb2f7f7 commit af05da5
Show file tree
Hide file tree
Showing 5 changed files with 61 additions and 106 deletions.
29 changes: 16 additions & 13 deletions components/layout/display_list/builder.rs
Expand Up @@ -14,7 +14,9 @@ use crate::display_list::background::{self, get_cyclic};
use crate::display_list::border;
use crate::display_list::gradient;
use crate::display_list::items::{self, BaseDisplayItem, ClipScrollNode};
use crate::display_list::items::{ClipScrollNodeIndex, ClipScrollNodeType, ClippingAndScrolling};
use crate::display_list::items::{
ClipScrollNodeIndex, ClipScrollNodeType, ClipType, ClippingAndScrolling,
};
use crate::display_list::items::{ClippingRegion, DisplayItem, DisplayItemMetadata, DisplayList};
use crate::display_list::items::{CommonDisplayItem, DisplayListSection};
use crate::display_list::items::{IframeDisplayItem, OpaqueNode};
Expand Down Expand Up @@ -424,15 +426,8 @@ impl<'a> DisplayListBuildState<'a> {
}

fn add_late_clip_node(&mut self, rect: LayoutRect, radii: BorderRadius) -> ClipScrollNodeIndex {
let mut clip = ClippingRegion::from_rect(rect);
clip.intersect_with_rounded_rect(rect, radii);

let node = ClipScrollNode {
parent_index: self.current_clipping_and_scrolling.scrolling,
clip,
content_rect: LayoutRect::zero(), // content_rect isn't important for clips.
node_type: ClipScrollNodeType::Clip,
};
let node =
ClipScrollNode::rounded(rect, radii, self.current_clipping_and_scrolling.scrolling);

// We want the scroll root to be defined before any possible item that could use it,
// so we make sure that it is added to the beginning of the parent "real" (non-pseudo)
Expand Down Expand Up @@ -2643,10 +2638,18 @@ impl BlockFlow {
.to_physical(self.fragment.style.writing_mode);
let clip_rect = border_box.inner_rect(border_widths);

let mut clip = ClippingRegion::from_rect(clip_rect.to_layout());
let clip = ClippingRegion::from_rect(clip_rect.to_layout());
let radii = build_border_radius_for_inner_rect(border_box, &self.fragment.style);
if !radii.is_zero() {
clip.intersect_with_rounded_rect(clip_rect.to_layout(), radii)
let node = ClipScrollNode::rounded(
clip_rect.to_layout(),
radii,
state.current_clipping_and_scrolling.scrolling,
);
let clip_id = state.add_clip_scroll_node(node);
let new_clipping_and_scrolling = ClippingAndScrolling::simple(clip_id);
self.base.clipping_and_scrolling = Some(new_clipping_and_scrolling);
state.current_clipping_and_scrolling = new_clipping_and_scrolling;
}

let content_size = self.base.overflow.scroll.origin + self.base.overflow.scroll.size;
Expand Down Expand Up @@ -2713,7 +2716,7 @@ impl BlockFlow {
parent_index: self.clipping_and_scrolling().scrolling,
clip: ClippingRegion::from_rect(clip_rect.to_layout()),
content_rect: LayoutRect::zero(), // content_rect isn't important for clips.
node_type: ClipScrollNodeType::Clip,
node_type: ClipScrollNodeType::Clip(ClipType::Rect),
});

let new_indices = ClippingAndScrolling::new(new_index, new_index);
Expand Down
104 changes: 28 additions & 76 deletions components/layout/display_list/items.rs
Expand Up @@ -24,7 +24,7 @@ use std::f32;
use std::fmt;
use style::computed_values::_servo_top_layer::T as InTopLayer;
use webrender_api as wr;
use webrender_api::units::{LayoutPixel, LayoutPoint, LayoutRect, LayoutSize, LayoutTransform};
use webrender_api::units::{LayoutPixel, LayoutRect, LayoutTransform};
use webrender_api::{
BorderRadius, ClipId, ClipMode, CommonItemProperties, ComplexClipRegion, ExternalScrollId,
FilterOp, GlyphInstance, GradientStop, ImageKey, MixBlendMode, PrimitiveFlags,
Expand Down Expand Up @@ -334,12 +334,18 @@ pub struct StickyFrameData {
pub horizontal_offset_bounds: StickyOffsetBounds,
}

#[derive(Clone, Copy, Debug, PartialEq, Serialize)]
pub enum ClipType {
Rounded(ComplexClipRegion),
Rect,
}

#[derive(Clone, Debug, PartialEq, Serialize)]
pub enum ClipScrollNodeType {
Placeholder,
ScrollFrame(ScrollSensitivity, ExternalScrollId),
StickyFrame(StickyFrameData),
Clip,
Clip(ClipType),
}

/// Defines a clip scroll node.
Expand Down Expand Up @@ -371,6 +377,24 @@ impl ClipScrollNode {
pub fn is_placeholder(&self) -> bool {
self.node_type == ClipScrollNodeType::Placeholder
}

pub fn rounded(
clip_rect: LayoutRect,
radii: BorderRadius,
parent_index: ClipScrollNodeIndex,
) -> ClipScrollNode {
let complex_region = ComplexClipRegion {
rect: clip_rect,
radii,
mode: ClipMode::Clip,
};
ClipScrollNode {
parent_index,
clip: ClippingRegion::from_rect(clip_rect),
content_rect: LayoutRect::zero(), // content_rect isn't important for clips.
node_type: ClipScrollNodeType::Clip(ClipType::Rounded(complex_region)),
}
}
}

/// One drawing command in the list.
Expand Down Expand Up @@ -465,11 +489,6 @@ pub fn empty_common_item_properties() -> CommonItemProperties {
pub struct ClippingRegion {
/// The main rectangular region. This does not include any corners.
pub main: LayoutRect,
/// Any complex regions.
///
/// TODO(pcwalton): Atomically reference count these? Not sure if it's worth the trouble.
/// Measure and follow up.
pub complex: Vec<ComplexClipRegion>,
}

impl ClippingRegion {
Expand All @@ -478,7 +497,6 @@ impl ClippingRegion {
pub fn empty() -> ClippingRegion {
ClippingRegion {
main: LayoutRect::zero(),
complex: Vec::new(),
}
}

Expand All @@ -487,45 +505,13 @@ impl ClippingRegion {
pub fn max() -> ClippingRegion {
ClippingRegion {
main: LayoutRect::max_rect(),
complex: Vec::new(),
}
}

/// Returns a clipping region that represents the given rectangle.
#[inline]
pub fn from_rect(rect: LayoutRect) -> ClippingRegion {
ClippingRegion {
main: rect,
complex: Vec::new(),
}
}

/// Intersects this clipping region with the given rounded rectangle.
#[inline]
pub fn intersect_with_rounded_rect(&mut self, rect: LayoutRect, radii: BorderRadius) {
let new_complex_region = ComplexClipRegion {
rect,
radii,
mode: ClipMode::Clip,
};

// FIXME(pcwalton): This is O(n²) worst case for disjoint clipping regions. Is that OK?
// They're slow anyway…
//
// Possibly relevant if we want to do better:
//
// http://www.inrg.csie.ntu.edu.tw/algorithm2014/presentation/D&C%20Lee-84.pdf
for existing_complex_region in &mut self.complex {
if completely_encloses(&existing_complex_region, &new_complex_region) {
*existing_complex_region = new_complex_region;
return;
}
if completely_encloses(&new_complex_region, &existing_complex_region) {
return;
}
}

self.complex.push(new_complex_region);
ClippingRegion { main: rect }
}
}

Expand All @@ -535,46 +521,12 @@ impl fmt::Debug for ClippingRegion {
write!(f, "ClippingRegion::Max")
} else if *self == ClippingRegion::empty() {
write!(f, "ClippingRegion::Empty")
} else if self.main == LayoutRect::max_rect() {
write!(f, "ClippingRegion(Complex={:?})", self.complex)
} else {
write!(
f,
"ClippingRegion(Rect={:?}, Complex={:?})",
self.main, self.complex
)
write!(f, "ClippingRegion(Rect={:?})", self.main,)
}
}
}

// TODO(pcwalton): This could be more aggressive by considering points that touch the inside of
// the border radius ellipse.
fn completely_encloses(this: &ComplexClipRegion, other: &ComplexClipRegion) -> bool {
let left = this.radii.top_left.width.max(this.radii.bottom_left.width);
let top = this.radii.top_left.height.max(this.radii.top_right.height);
let right = this
.radii
.top_right
.width
.max(this.radii.bottom_right.width);
let bottom = this
.radii
.bottom_left
.height
.max(this.radii.bottom_right.height);
let interior = LayoutRect::new(
LayoutPoint::new(this.rect.origin.x + left, this.rect.origin.y + top),
LayoutSize::new(
this.rect.size.width - left - right,
this.rect.size.height - top - bottom,
),
);
interior.origin.x <= other.rect.origin.x &&
interior.origin.y <= other.rect.origin.y &&
interior.max_x() >= other.rect.max_x() &&
interior.max_y() >= other.rect.max_y()
}

/// Metadata attached to each display item. This is useful for performing auxiliary threads with
/// the display list involving hit testing: finding the originating DOM node and determining the
/// cursor to use when the element is hovered over.
Expand Down
23 changes: 13 additions & 10 deletions components/layout/display_list/webrender_helpers.rs
Expand Up @@ -7,7 +7,7 @@
// This might be achieved by sharing types between WR and Servo display lists, or
// completely converting layout to directly generate WebRender display lists, for example.

use crate::display_list::items::{BaseDisplayItem, ClipScrollNode, ClipScrollNodeType};
use crate::display_list::items::{BaseDisplayItem, ClipScrollNode, ClipScrollNodeType, ClipType};
use crate::display_list::items::{DisplayItem, DisplayList, StackingContextType};
use msg::constellation_msg::PipelineId;
use webrender_api::units::LayoutPoint;
Expand Down Expand Up @@ -269,15 +269,19 @@ impl DisplayItem {
.expect("Tried to use WebRender parent ClipId before it was defined.");

match node.node_type {
ClipScrollNodeType::Clip => {
let id = builder.define_clip(
&SpaceAndClipInfo {
clip_id: parent_clip_id,
spatial_id: parent_spatial_id,
ClipScrollNodeType::Clip(clip_type) => {
let space_and_clip_info = SpaceAndClipInfo {
clip_id: parent_clip_id,
spatial_id: parent_spatial_id,
};
let id = match clip_type {
ClipType::Rect => {
builder.define_clip_rect(&space_and_clip_info, item_rect)
},
item_rect,
node.clip.complex.clone(),
);
ClipType::Rounded(complex) => {
builder.define_clip_rounded_rect(&space_and_clip_info, complex)
},
};

state.spatial_ids[item.node_index.to_index()] = Some(parent_spatial_id);
state.clip_ids[item.node_index.to_index()] = Some(id);
Expand All @@ -291,7 +295,6 @@ impl DisplayItem {
Some(external_id),
node.content_rect,
node.clip.main,
node.clip.complex.clone(),
scroll_sensitivity,
webrender_api::units::LayoutVector2D::zero(),
);
Expand Down
9 changes: 4 additions & 5 deletions components/layout_2020/display_list/mod.rs
Expand Up @@ -344,7 +344,7 @@ impl<'a> BuilderForBoxFragment<'a> {
let (bounds, common) = background::painting_area(self, builder, layer_index);
builder
.wr
.push_rect(&common, bounds, rgba(background_color))
.push_rect(&common, *bounds, rgba(background_color))
}
// Reverse because the property is top layer first, we want to paint bottom layer first.
for (index, image) in b.background_image.0.iter().enumerate().rev() {
Expand Down Expand Up @@ -589,14 +589,13 @@ fn clip_for_radii(
if radii.is_zero() {
None
} else {
Some(builder.wr.define_clip(
Some(builder.wr.define_clip_rounded_rect(
&builder.current_space_and_clip,
rect,
Some(wr::ComplexClipRegion {
wr::ComplexClipRegion {
rect,
radii,
mode: wr::ClipMode::Clip,
}),
},
))
}
}
2 changes: 0 additions & 2 deletions components/layout_2020/display_list/stacking_context.rs
Expand Up @@ -605,8 +605,6 @@ impl BoxFragment {
self.scrollable_overflow(&containing_block_info.rect)
.to_webrender(),
padding_rect,
vec![], // complex_clips
None, // image_mask
sensitivity,
LayoutVector2D::zero(),
);
Expand Down

0 comments on commit af05da5

Please sign in to comment.