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

Correctly paint the CSS canvas’ background #26414

Merged
merged 8 commits into from May 15, 2020
@@ -6,6 +6,7 @@ use crate::replaced::IntrinsicSizes;
use euclid::{Size2D, Vector2D};
use style::computed_values::background_clip::single_value::T as Clip;
use style::computed_values::background_origin::single_value::T as Origin;
use style::properties::ComputedValues;
use style::values::computed::background::BackgroundSize as Size;
use style::values::computed::{Length, LengthPercentage};
use style::values::specified::background::BackgroundRepeat as RepeatXY;
@@ -20,6 +21,7 @@ pub(super) struct BackgroundLayer {
pub repeat: bool,
}

#[derive(Debug)]
struct Layout1DResult {
repeat: bool,
bounds_origin: f32,
@@ -31,17 +33,34 @@ fn get_cyclic<T>(values: &[T], layer_index: usize) -> &T {
&values[layer_index % values.len()]
}

pub(super) enum Source<'a> {
Fragment,
Canvas {
style: &'a ComputedValues,

// Theoretically the painting area is the infinite 2D plane,
// but WebRender doesn’t really do infinite so this is the part of it that can be visible.
painting_area: units::LayoutRect,
},
}

pub(super) fn painting_area<'a>(
fragment_builder: &'a super::BuilderForBoxFragment,
source: &'a Source,
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)),
let (painting_area, clip) = match source {
Source::Canvas { painting_area, .. } => (painting_area, None),
Source::Fragment => {
let fb = fragment_builder;
let b = fb.fragment.style.get_background();
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);
@@ -53,12 +72,17 @@ pub(super) fn painting_area<'a>(

pub(super) fn layout_layer(
fragment_builder: &mut super::BuilderForBoxFragment,
source: &Source,
builder: &mut super::DisplayListBuilder,
layer_index: usize,
intrinsic: IntrinsicSizes,
) -> Option<BackgroundLayer> {
let b = fragment_builder.fragment.style.get_background();
let (painting_area, common) = painting_area(fragment_builder, builder, layer_index);
let style = match *source {
Source::Canvas { style, .. } => style,
Source::Fragment => &fragment_builder.fragment.style,
};
let b = style.get_background();
let (painting_area, common) = painting_area(fragment_builder, source, builder, layer_index);

let positioning_area = match get_cyclic(&b.background_origin.0, layer_index) {
Origin::ContentBox => fragment_builder.content_rect(),
@@ -227,7 +251,8 @@ fn layout_1d(
let tile_stride = *tile_size + tile_spacing;
let offset = position - painting_area_origin;
let bounds_origin = position - tile_stride * (offset / tile_stride).ceil();
let bounds_size = painting_area_size - bounds_origin - painting_area_origin;
let bounds_end = painting_area_origin + painting_area_size;
let bounds_size = bounds_end - bounds_origin;
Layout1DResult {
repeat: true,
bounds_origin,
@@ -41,6 +41,7 @@ pub struct DisplayListBuilder<'a> {
/// The current SpatialId and ClipId information for this `DisplayListBuilder`.
current_space_and_clip: wr::SpaceAndClipInfo,

element_for_canvas_background: OpaqueNode,
pub context: &'a LayoutContext<'a>,
pub wr: wr::DisplayListBuilder,

@@ -55,13 +56,14 @@ impl<'a> DisplayListBuilder<'a> {
pub fn new(
pipeline_id: wr::PipelineId,
context: &'a LayoutContext,
viewport_size: wr::units::LayoutSize,
fragment_tree: &crate::FragmentTree,
) -> Self {
Self {
current_space_and_clip: wr::SpaceAndClipInfo::root_scroll(pipeline_id),
element_for_canvas_background: fragment_tree.canvas_background.from_element,
is_contentful: false,
context,
wr: wr::DisplayListBuilder::new(pipeline_id, viewport_size),
wr: wr::DisplayListBuilder::new(pipeline_id, fragment_tree.scrollable_overflow()),
}
}

@@ -333,19 +335,40 @@ impl<'a> BuilderForBoxFragment<'a> {
}

fn build_background(&mut self, builder: &mut DisplayListBuilder) {
use style::values::computed::image::Image;
let b = self.fragment.style.get_background();
let background_color = self.fragment.style.resolve_color(b.background_color);
if self.fragment.tag == builder.element_for_canvas_background {
// This background is already painted for the canvas, don’t paint it again here.
return;
}

let source = background::Source::Fragment;
let style = &self.fragment.style;
let b = style.get_background();
let background_color = style.resolve_color(b.background_color);
if background_color.alpha > 0 {
// https://drafts.csswg.org/css-backgrounds/#background-color
// “The background color is clipped according to the background-clip
// value associated with the bottom-most background image layer.”
let layer_index = b.background_image.0.len() - 1;
let (bounds, common) = background::painting_area(self, builder, layer_index);
let (bounds, common) = background::painting_area(self, &source, builder, layer_index);
builder
.wr
.push_rect(&common, *bounds, rgba(background_color))
}

self.build_background_image(builder, source);
}

fn build_background_image(
&mut self,
builder: &mut DisplayListBuilder,
source: background::Source<'a>,
) {
use style::values::computed::image::Image;
let style = match source {
background::Source::Canvas { style, .. } => style,
background::Source::Fragment => &self.fragment.style,
};
let b = style.get_background();
// 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() {
match image {
@@ -356,9 +379,10 @@ impl<'a> BuilderForBoxFragment<'a> {
height: None,
ratio: None,
};
if let Some(layer) = &background::layout_layer(self, builder, index, intrinsic)
if let Some(layer) =
&background::layout_layer(self, &source, builder, index, intrinsic)
{
gradient::build(&self.fragment.style, &gradient, layer, builder)
gradient::build(&style, &gradient, layer, builder)
}
},
Image::Url(ref image_url) => {
@@ -393,9 +417,10 @@ impl<'a> BuilderForBoxFragment<'a> {
ratio: Some(width as f32 / height as f32),
};

if let Some(layer) = background::layout_layer(self, builder, index, intrinsic) {
let image_rendering =
image_rendering(self.fragment.style.clone_image_rendering());
if let Some(layer) =
background::layout_layer(self, &source, builder, index, intrinsic)
{
let image_rendering = image_rendering(style.clone_image_rendering());
if layer.repeat {
builder.wr.push_repeating_image(
&layer.common,
ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.