Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Background follow ups #25594

Merged
merged 10 commits into from Jan 25, 2020

Fix combinations of `border-radius` and `background-clip`

  • Loading branch information
SimonSapin committed Jan 24, 2020
commit ea4882a1c5cedcb41ba10f182eaf36d53dd9ddea
@@ -30,19 +30,35 @@ fn get_cyclic<T>(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,
layer_index: usize,
intrinsic: IntrinsicSizes,
) -> Option<Layer> {
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,
@@ -141,6 +141,8 @@ struct BuilderForBoxFragment<'a> {
content_rect: OnceCell<units::LayoutRect>,
border_radius: wr::BorderRadius,
border_edge_clip_id: OnceCell<Option<wr::ClipId>>,
padding_edge_clip_id: OnceCell<Option<wr::ClipId>>,
content_edge_clip_id: OnceCell<Option<wr::ClipId>>,
}

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,38 +205,51 @@ 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<wr::ClipId> {
*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<wr::ClipId> {
*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<wr::ClipId> {
*self.content_edge_clip_id.init_once(|| {

This comment has been minimized.

Copy link
@mrobinson

mrobinson Jan 24, 2020

Member

Nice use of init_once here and above!

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) {
let hit_info = hit_info(&self.fragment.style, self.fragment.tag, Cursor::Default);
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) {

This comment has been minimized.

Copy link
@mrobinson

mrobinson Jan 24, 2020

Member

At some point it would be good to keep a stack of clips and spatial node ids, but for the moment I don't think it's necessary.

This comment has been minimized.

Copy link
@SimonSapin

SimonSapin Jan 25, 2020

Author Member

Yes, having that stack on the call stack is the idea of:

// FIXME: use this for the `overflow` property or anything else that clips an entire subtree.
#[allow(unused)]
fn clipping_and_scrolling_scope<R>(&mut self, f: impl FnOnce(&mut Self) -> R) -> R {
let previous = self.current_space_and_clip;
let result = f(self);
self.current_space_and_clip = previous;
result
}

… which I copied from Layout 2013 code, but ended up not using for backgrounds because each background layer has a potentially-different background-clip value that only applies to one display item, so pushing/popping to the stack isn’t as useful.

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<wr::ClipId> {
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,
))
}
}
@@ -240,6 +240,44 @@ impl<T> flow_relative::Sides<T> {
{
self.block_start + self.block_end
}

pub fn to_physical(&self, mode: WritingMode) -> PhysicalSides<T>
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<LengthPercentage> {
@@ -342,3 +380,15 @@ impl ToWebRender for PhysicalRect<Length> {
webrender_api::units::LayoutRect::new(self.origin.to_webrender(), self.size.to_webrender())
}
}

impl ToWebRender for PhysicalSides<Length> {
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(),
)
}
}
ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.