diff --git a/components/layout/display_list/builder.rs b/components/layout/display_list/builder.rs index 3efb254cd32f..9d5f1e100abb 100644 --- a/components/layout/display_list/builder.rs +++ b/components/layout/display_list/builder.rs @@ -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}; @@ -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) @@ -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; @@ -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); diff --git a/components/layout/display_list/items.rs b/components/layout/display_list/items.rs index 58ec79cb0fa2..27b4ec1fa884 100644 --- a/components/layout/display_list/items.rs +++ b/components/layout/display_list/items.rs @@ -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, @@ -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. @@ -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. @@ -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, } impl ClippingRegion { @@ -478,7 +497,6 @@ impl ClippingRegion { pub fn empty() -> ClippingRegion { ClippingRegion { main: LayoutRect::zero(), - complex: Vec::new(), } } @@ -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 } } } @@ -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. diff --git a/components/layout/display_list/webrender_helpers.rs b/components/layout/display_list/webrender_helpers.rs index de6ffc0e4fe5..dd187cf37049 100644 --- a/components/layout/display_list/webrender_helpers.rs +++ b/components/layout/display_list/webrender_helpers.rs @@ -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; @@ -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); @@ -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(), ); diff --git a/components/layout_2020/display_list/mod.rs b/components/layout_2020/display_list/mod.rs index 3e0019ea42e2..43529043a5ad 100644 --- a/components/layout_2020/display_list/mod.rs +++ b/components/layout_2020/display_list/mod.rs @@ -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() { @@ -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, - }), + }, )) } } diff --git a/components/layout_2020/display_list/stacking_context.rs b/components/layout_2020/display_list/stacking_context.rs index c31e35aa637e..1b0644e9d91a 100644 --- a/components/layout_2020/display_list/stacking_context.rs +++ b/components/layout_2020/display_list/stacking_context.rs @@ -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(), );