Support multiple backgrounds in servo layout

This commit is contained in:
Manish Goregaokar 2016-08-20 00:20:16 +05:30
parent 66cdf9ae4f
commit 69ada0d7a3
5 changed files with 125 additions and 95 deletions

View file

@ -60,6 +60,10 @@ use table_cell::CollapsedBordersForCell;
use url::Url;
use util::opts;
fn get_cyclic<T>(arr: &[T], index: usize) -> &T {
&arr[index % arr.len()]
}
pub struct DisplayListBuildState<'a> {
pub layout_context: &'a LayoutContext<'a>,
pub items: Vec<DisplayItem>,
@ -146,7 +150,7 @@ pub trait FragmentDisplayListBuilding {
fn compute_background_image_size(&self,
style: &ServoComputedValues,
bounds: &Rect<Au>,
image: &WebRenderImageInfo)
image: &WebRenderImageInfo, index: usize)
-> Size2D<Au>;
/// Adds the display items necessary to paint the background image of this fragment to the
@ -157,7 +161,8 @@ pub trait FragmentDisplayListBuilding {
display_list_section: DisplayListSection,
absolute_bounds: &Rect<Au>,
clip: &ClippingRegion,
image_url: &Url);
image_url: &Url,
background_index: usize);
/// Adds the display items necessary to paint the background linear gradient of this fragment
/// to the appropriate section of the display list.
@ -344,27 +349,32 @@ impl FragmentDisplayListBuilding for Fragment {
if !border_radii.is_square() {
clip.intersect_with_rounded_rect(absolute_bounds, &border_radii)
}
let background = style.get_background();
// FIXME: This causes a lot of background colors to be displayed when they are clearly not
// needed. We could use display list optimization to clean this up, but it still seems
// inefficient. What we really want is something like "nearest ancestor element that
// doesn't have a fragment".
let background_color = style.resolve_color(style.get_background().background_color);
let background_color = style.resolve_color(background.background_color);
// 'background-clip' determines the area within which the background is painted.
// http://dev.w3.org/csswg/css-backgrounds-3/#the-background-clip
let mut bounds = *absolute_bounds;
match style.get_background().background_clip {
background_clip::T::border_box => {}
background_clip::T::padding_box => {
// This is the clip for the color (which is the last element in the bg array)
let color_clip = get_cyclic(&background.background_clip.0,
background.background_image.0.len() - 1);
match *color_clip {
background_clip::single_value::T::border_box => {}
background_clip::single_value::T::padding_box => {
let border = style.logical_border_width().to_physical(style.writing_mode);
bounds.origin.x = bounds.origin.x + border.left;
bounds.origin.y = bounds.origin.y + border.top;
bounds.size.width = bounds.size.width - border.horizontal();
bounds.size.height = bounds.size.height - border.vertical();
}
background_clip::T::content_box => {
background_clip::single_value::T::content_box => {
let border_padding = self.border_padding.to_physical(style.writing_mode);
bounds.origin.x = bounds.origin.x + border_padding.left;
bounds.origin.y = bounds.origin.y + border_padding.top;
@ -388,7 +398,8 @@ impl FragmentDisplayListBuilding for Fragment {
// Implements background image, per spec:
// http://www.w3.org/TR/CSS21/colors.html#background
let background = style.get_background();
match background.background_image.0 {
for (i, background_image) in background.background_image.0.iter().enumerate().rev() {
match background_image.0 {
None => {}
Some(computed::Image::LinearGradient(ref gradient)) => {
self.build_display_list_for_background_linear_gradient(state,
@ -404,7 +415,9 @@ impl FragmentDisplayListBuilding for Fragment {
display_list_section,
&bounds,
&clip,
image_url);
image_url,
i);
}
}
}
}
@ -412,7 +425,8 @@ impl FragmentDisplayListBuilding for Fragment {
fn compute_background_image_size(&self,
style: &ServoComputedValues,
bounds: &Rect<Au>,
image: &WebRenderImageInfo)
image: &WebRenderImageInfo,
index: usize)
-> Size2D<Au> {
// If `image_aspect_ratio` < `bounds_aspect_ratio`, the image is tall; otherwise, it is
// wide.
@ -420,19 +434,22 @@ impl FragmentDisplayListBuilding for Fragment {
let bounds_aspect_ratio = bounds.size.width.to_f64_px() / bounds.size.height.to_f64_px();
let intrinsic_size = Size2D::new(Au::from_px(image.width as i32),
Au::from_px(image.height as i32));
match (style.get_background().background_size.clone(),
image_aspect_ratio < bounds_aspect_ratio) {
(background_size::T::Contain, false) | (background_size::T::Cover, true) => {
let background_size = get_cyclic(&style.get_background().background_size.0, index).clone();
match (background_size, image_aspect_ratio < bounds_aspect_ratio) {
(background_size::single_value::T::Contain, false) |
(background_size::single_value::T::Cover, true) => {
Size2D::new(bounds.size.width,
Au::from_f64_px(bounds.size.width.to_f64_px() / image_aspect_ratio))
}
(background_size::T::Contain, true) | (background_size::T::Cover, false) => {
(background_size::single_value::T::Contain, true) |
(background_size::single_value::T::Cover, false) => {
Size2D::new(Au::from_f64_px(bounds.size.height.to_f64_px() * image_aspect_ratio),
bounds.size.height)
}
(background_size::T::Explicit(background_size::ExplicitSize {
(background_size::single_value::T::Explicit(background_size::single_value
::ExplicitSize {
width,
height: LengthOrPercentageOrAuto::Auto,
}), _) => {
@ -441,7 +458,8 @@ impl FragmentDisplayListBuilding for Fragment {
Size2D::new(width, Au::from_f64_px(width.to_f64_px() / image_aspect_ratio))
}
(background_size::T::Explicit(background_size::ExplicitSize {
(background_size::single_value::T::Explicit(background_size::single_value
::ExplicitSize {
width: LengthOrPercentageOrAuto::Auto,
height
}), _) => {
@ -450,7 +468,8 @@ impl FragmentDisplayListBuilding for Fragment {
Size2D::new(Au::from_f64_px(height.to_f64_px() * image_aspect_ratio), height)
}
(background_size::T::Explicit(background_size::ExplicitSize {
(background_size::single_value::T::Explicit(background_size::single_value
::ExplicitSize {
width,
height
}), _) => {
@ -468,19 +487,22 @@ impl FragmentDisplayListBuilding for Fragment {
display_list_section: DisplayListSection,
absolute_bounds: &Rect<Au>,
clip: &ClippingRegion,
image_url: &Url) {
image_url: &Url,
index: usize) {
let background = style.get_background();
let fetch_image_data_as_well = !opts::get().use_webrender;
let webrender_image =
state.layout_context.get_webrender_image_for_url(image_url,
UsePlaceholder::No,
fetch_image_data_as_well);
if let Some((webrender_image, image_data)) = webrender_image {
debug!("(building display list) building background image");
// Use `background-size` to get the size.
let mut bounds = *absolute_bounds;
let image_size = self.compute_background_image_size(style, &bounds, &webrender_image);
let image_size = self.compute_background_image_size(style, &bounds,
&webrender_image, index);
// Clip.
//
@ -492,25 +514,27 @@ impl FragmentDisplayListBuilding for Fragment {
let border = style.logical_border_width().to_physical(style.writing_mode);
// Use 'background-origin' to get the origin value.
let (mut origin_x, mut origin_y) = match background.background_origin {
background_origin::T::padding_box => {
let origin = get_cyclic(&background.background_origin.0, index);
let (mut origin_x, mut origin_y) = match *origin {
background_origin::single_value::T::padding_box => {
(Au(0), Au(0))
}
background_origin::T::border_box => {
background_origin::single_value::T::border_box => {
(-border.left, -border.top)
}
background_origin::T::content_box => {
background_origin::single_value::T::content_box => {
let border_padding = self.border_padding.to_physical(self.style.writing_mode);
(border_padding.left - border.left, border_padding.top - border.top)
}
};
// Use `background-attachment` to get the initial virtual origin
let (virtual_origin_x, virtual_origin_y) = match background.background_attachment {
background_attachment::T::scroll => {
let attachment = get_cyclic(&background.background_attachment.0, index);
let (virtual_origin_x, virtual_origin_y) = match *attachment {
background_attachment::single_value::T::scroll => {
(absolute_bounds.origin.x, absolute_bounds.origin.y)
}
background_attachment::T::fixed => {
background_attachment::single_value::T::fixed => {
// If the background-attachment value for this image is fixed, then
// 'background-origin' has no effect.
origin_x = Au(0);
@ -519,24 +543,25 @@ impl FragmentDisplayListBuilding for Fragment {
}
};
let position = *get_cyclic(&background.background_position.0, index);
// Use `background-position` to get the offset.
let horizontal_position = model::specified(background.background_position.horizontal,
let horizontal_position = model::specified(position.horizontal,
bounds.size.width - image_size.width);
let vertical_position = model::specified(background.background_position.vertical,
let vertical_position = model::specified(position.vertical,
bounds.size.height - image_size.height);
let abs_x = border.left + virtual_origin_x + horizontal_position + origin_x;
let abs_y = border.top + virtual_origin_y + vertical_position + origin_y;
// Adjust origin and size based on background-repeat
match background.background_repeat {
background_repeat::T::no_repeat => {
match *get_cyclic(&background.background_repeat.0, index) {
background_repeat::single_value::T::no_repeat => {
bounds.origin.x = abs_x;
bounds.origin.y = abs_y;
bounds.size.width = image_size.width;
bounds.size.height = image_size.height;
}
background_repeat::T::repeat_x => {
background_repeat::single_value::T::repeat_x => {
bounds.origin.y = abs_y;
bounds.size.height = image_size.height;
ImageFragmentInfo::tile_image(&mut bounds.origin.x,
@ -544,7 +569,7 @@ impl FragmentDisplayListBuilding for Fragment {
abs_x,
image_size.width.to_nearest_px() as u32);
}
background_repeat::T::repeat_y => {
background_repeat::single_value::T::repeat_y => {
bounds.origin.x = abs_x;
bounds.size.width = image_size.width;
ImageFragmentInfo::tile_image(&mut bounds.origin.y,
@ -552,7 +577,7 @@ impl FragmentDisplayListBuilding for Fragment {
abs_y,
image_size.height.to_nearest_px() as u32);
}
background_repeat::T::repeat => {
background_repeat::single_value::T::repeat => {
ImageFragmentInfo::tile_image(&mut bounds.origin.x,
&mut bounds.size.width,
abs_x,
@ -577,6 +602,7 @@ impl FragmentDisplayListBuilding for Fragment {
stretch_size: Size2D::new(image_size.width, image_size.height),
image_rendering: style.get_inheritedbox().image_rendering.clone(),
}));
}
}

View file

@ -362,8 +362,11 @@ impl LayoutElementHelpers for LayoutJS<Element> {
if let Some(url) = background {
hints.push(from_declaration(
PropertyDeclaration::BackgroundImage(DeclaredValue::Value(
background_image::SpecifiedValue(Some(
specified::Image::Url(url, specified::UrlExtraData { })))))));
background_image::SpecifiedValue(vec![
background_image::single_value::SpecifiedValue(Some(
specified::Image::Url(url, specified::UrlExtraData { })
))
])))));
}
let color = if let Some(this) = self.downcast::<HTMLFontElement>() {

View file

@ -69,10 +69,10 @@
${caller.body()}
}
pub mod computed_value {
use super::single_value;
pub use super::single_value::computed_value as single_value;
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub struct T(pub Vec<single_value::computed_value::T>);
pub struct T(pub Vec<single_value::T>);
}
impl ToCss for computed_value::T {

View file

@ -10,7 +10,7 @@ ${helpers.predefined_type("background-color", "CSSColor",
"::cssparser::Color::RGBA(::cssparser::RGBA { red: 0., green: 0., blue: 0., alpha: 0. }) /* transparent */",
animatable=True)}
<%helpers:vector_longhand gecko_only="True" name="background-image" animatable="False">
<%helpers:vector_longhand name="background-image" animatable="False">
use cssparser::ToCss;
use std::fmt;
use values::specified::Image;
@ -79,7 +79,7 @@ ${helpers.predefined_type("background-color", "CSSColor",
}
</%helpers:vector_longhand>
<%helpers:vector_longhand name="background-position" gecko_only="True" animatable="True">
<%helpers:vector_longhand name="background-position" animatable="True">
use cssparser::ToCss;
use std::fmt;
use values::LocalToCss;
@ -121,25 +121,21 @@ ${helpers.predefined_type("background-color", "CSSColor",
${helpers.single_keyword("background-repeat",
"repeat repeat-x repeat-y no-repeat",
vector=True,
gecko_only=True,
animatable=False)}
${helpers.single_keyword("background-attachment",
"scroll fixed" + (" local" if product == "gecko" else ""),
vector=True,
gecko_only=True,
animatable=False)}
${helpers.single_keyword("background-clip",
"border-box padding-box content-box",
vector=True,
gecko_only=True,
animatable=False)}
${helpers.single_keyword("background-origin",
"padding-box border-box content-box",
vector=True,
gecko_only=True,
animatable=False)}
<%helpers:vector_longhand name="background-size" animatable="True">

View file

@ -23,15 +23,15 @@
% endfor
loop {
if background_color.is_none() {
if let Ok(value) = input.try(|input| background_color::parse(context, input)) {
if background_color.is_none() {
background_color = Some(value);
continue
}
} else {
// color can only be the last element
return Err(())
}
}
if position.is_none() {
if let Ok(value) = input.try(|input| background_position::single_value::parse(context, input)) {
position = Some(value);
@ -118,6 +118,11 @@
.unwrap_or(0));
% endfor
// There should be at least one declared value
if len == 0 {
return Err(())
}
let iter = repeat(None).take(len - 1).chain(once(Some(self.background_color)))
% for name in "image position repeat size attachment origin clip".split():
.zip(extract_value(self.background_${name}).into_iter()