diff --git a/components/layout_2020/display_list/background.rs b/components/layout_2020/display_list/background.rs index 99e6c674925..400ba424fe3 100644 --- a/components/layout_2020/display_list/background.rs +++ b/components/layout_2020/display_list/background.rs @@ -30,6 +30,26 @@ fn get_cyclic(values: &[T], layer_index: usize) -> &T { &values[layer_index % values.len()] } +pub(super) fn painting_area<'a>( + fragment_builder: &'a super::BuilderForBoxFragment, + builder: &mut super::DisplayListBuilder, + layer_index: usize, +) -> (&'a units::LayoutRect, wr::CommonItemProperties) { + let fb = fragment_builder; + let b = fb.fragment.style.get_background(); + let (painting_area, clip) = match get_cyclic(&b.background_clip.0, layer_index) { + Clip::ContentBox => (fb.content_rect(), fb.content_edge_clip(builder)), + Clip::PaddingBox => (fb.padding_rect(), fb.padding_edge_clip(builder)), + Clip::BorderBox => (&fb.border_rect, fb.border_edge_clip(builder)), + }; + // The 'backgound-clip' property maps directly to `clip_rect` in `CommonItemProperties`: + let mut common = builder.common_properties(*painting_area); + if let Some(clip_id) = clip { + common.clip_id = clip_id + } + (painting_area, common) +} + pub(super) fn layout_layer( fragment_builder: &mut super::BuilderForBoxFragment, builder: &mut super::DisplayListBuilder, @@ -37,12 +57,8 @@ pub(super) fn layout_layer( intrinsic: IntrinsicSizes, ) -> Option { let b = fragment_builder.fragment.style.get_background(); + let (painting_area, common) = painting_area(fragment_builder, builder, layer_index); - let painting_area = match get_cyclic(&b.background_clip.0, layer_index) { - Clip::ContentBox => fragment_builder.content_rect(), - Clip::PaddingBox => fragment_builder.padding_rect(), - Clip::BorderBox => &fragment_builder.border_rect, - }; let positioning_area = match get_cyclic(&b.background_origin.0, layer_index) { Origin::ContentBox => fragment_builder.content_rect(), Origin::PaddingBox => fragment_builder.padding_rect(), @@ -150,10 +166,6 @@ pub(super) fn layout_layer( Size2D::new(result_x.bounds_size, result_y.bounds_size), ); - // The 'backgound-clip' property maps directly to `clip_rect` in `CommonItemProperties`: - let mut common = builder.common_properties(*painting_area); - fragment_builder.with_border_edge_clip(builder, &mut common); - Some(Layer { common, bounds, diff --git a/components/layout_2020/display_list/mod.rs b/components/layout_2020/display_list/mod.rs index 9d4deaa265b..649dec78d3c 100644 --- a/components/layout_2020/display_list/mod.rs +++ b/components/layout_2020/display_list/mod.rs @@ -141,6 +141,8 @@ struct BuilderForBoxFragment<'a> { content_rect: OnceCell, border_radius: wr::BorderRadius, border_edge_clip_id: OnceCell>, + padding_edge_clip_id: OnceCell>, + content_edge_clip_id: OnceCell>, } impl<'a> BuilderForBoxFragment<'a> { @@ -178,6 +180,8 @@ impl<'a> BuilderForBoxFragment<'a> { padding_rect: OnceCell::new(), content_rect: OnceCell::new(), border_edge_clip_id: OnceCell::new(), + padding_edge_clip_id: OnceCell::new(), + content_edge_clip_id: OnceCell::new(), } } @@ -201,30 +205,41 @@ impl<'a> BuilderForBoxFragment<'a> { }) } - fn with_border_edge_clip( - &mut self, - builder: &mut DisplayListBuilder, - common: &mut wr::CommonItemProperties, - ) { - let initialized = self.border_edge_clip_id.init_once(|| { - if self.border_radius.is_zero() { - None - } else { - Some(builder.wr.define_clip( - &builder.current_space_and_clip, - self.border_rect, - Some(wr::ComplexClipRegion { - rect: self.border_rect, - radii: self.border_radius, - mode: wr::ClipMode::Clip, - }), - None, - )) - } - }); - if let Some(clip_id) = *initialized { - common.clip_id = clip_id - } + fn border_edge_clip(&self, builder: &mut DisplayListBuilder) -> Option { + *self + .border_edge_clip_id + .init_once(|| clip_for_radii(self.border_radius, self.border_rect, builder)) + } + + fn padding_edge_clip(&self, builder: &mut DisplayListBuilder) -> Option { + *self.padding_edge_clip_id.init_once(|| { + clip_for_radii( + inner_radii( + self.border_radius, + self.fragment + .border + .to_physical(self.fragment.style.writing_mode) + .to_webrender(), + ), + self.border_rect, + builder, + ) + }) + } + + fn content_edge_clip(&self, builder: &mut DisplayListBuilder) -> Option { + *self.content_edge_clip_id.init_once(|| { + clip_for_radii( + inner_radii( + self.border_radius, + (&self.fragment.border + &self.fragment.padding) + .to_physical(self.fragment.style.writing_mode) + .to_webrender(), + ), + self.border_rect, + builder, + ) + }) } fn build(&mut self, builder: &mut DisplayListBuilder) { @@ -232,7 +247,9 @@ impl<'a> BuilderForBoxFragment<'a> { if hit_info.is_some() { let mut common = builder.common_properties(self.border_rect); common.hit_info = hit_info; - self.with_border_edge_clip(builder, &mut common); + if let Some(clip_id) = self.border_edge_clip(builder) { + common.clip_id = clip_id + } builder.wr.push_hit_test(&common) } @@ -254,7 +271,9 @@ impl<'a> BuilderForBoxFragment<'a> { let background_color = self.fragment.style.resolve_color(b.background_color); if background_color.alpha > 0 { let mut common = builder.common_properties(self.border_rect); - self.with_border_edge_clip(builder, &mut common); + if let Some(clip_id) = self.border_edge_clip(builder) { + common.clip_id = clip_id + } builder.wr.push_rect(&common, rgba(background_color)) } // Reverse because the property is top layer first, we want to paint bottom layer first. @@ -471,3 +490,40 @@ fn image_rendering(ir: style::computed_values::image_rendering::T) -> wr::ImageR ImageRendering::Pixelated => wr::ImageRendering::Pixelated, } } + +/// Radii for the padding edge or content edge +fn inner_radii(mut radii: wr::BorderRadius, offsets: units::LayoutSideOffsets) -> wr::BorderRadius { + radii.top_left.width -= -offsets.left; + radii.bottom_left.width -= offsets.left; + + radii.top_right.width -= offsets.right; + radii.bottom_right.width -= offsets.right; + + radii.top_left.height -= offsets.top; + radii.top_right.height -= offsets.top; + + radii.bottom_left.height -= offsets.bottom; + radii.bottom_right.height -= offsets.bottom; + radii +} + +fn clip_for_radii( + radii: wr::BorderRadius, + rect: units::LayoutRect, + builder: &mut DisplayListBuilder, +) -> Option { + if radii.is_zero() { + None + } else { + Some(builder.wr.define_clip( + &builder.current_space_and_clip, + rect, + Some(wr::ComplexClipRegion { + rect, + radii, + mode: wr::ClipMode::Clip, + }), + None, + )) + } +} diff --git a/components/layout_2020/geom.rs b/components/layout_2020/geom.rs index 1f9ae597501..60f065f6718 100644 --- a/components/layout_2020/geom.rs +++ b/components/layout_2020/geom.rs @@ -240,6 +240,44 @@ impl flow_relative::Sides { { self.block_start + self.block_end } + + pub fn to_physical(&self, mode: WritingMode) -> PhysicalSides + where + T: Clone, + { + let top; + let right; + let bottom; + let left; + if mode.is_vertical() { + if mode.is_vertical_lr() { + left = self.block_start.clone(); + right = self.block_end.clone(); + } else { + right = self.block_start.clone(); + left = self.block_end.clone(); + } + + if mode.is_inline_tb() { + top = self.inline_start.clone(); + bottom = self.inline_end.clone(); + } else { + bottom = self.inline_start.clone(); + top = self.inline_end.clone(); + } + } else { + top = self.block_start.clone(); + bottom = self.block_end.clone(); + if mode.is_bidi_ltr() { + left = self.inline_start.clone(); + right = self.inline_end.clone(); + } else { + right = self.inline_start.clone(); + left = self.inline_end.clone(); + } + } + PhysicalSides::new(top, right, bottom, left) + } } impl flow_relative::Sides { @@ -342,3 +380,15 @@ impl ToWebRender for PhysicalRect { webrender_api::units::LayoutRect::new(self.origin.to_webrender(), self.size.to_webrender()) } } + +impl ToWebRender for PhysicalSides { + type Type = webrender_api::units::LayoutSideOffsets; + fn to_webrender(&self) -> Self::Type { + webrender_api::units::LayoutSideOffsets::new( + self.top.px(), + self.right.px(), + self.bottom.px(), + self.left.px(), + ) + } +}