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,23 +398,26 @@ 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 {
None => {}
Some(computed::Image::LinearGradient(ref gradient)) => {
self.build_display_list_for_background_linear_gradient(state,
display_list_section,
&bounds,
&clip,
gradient,
style);
}
Some(computed::Image::Url(ref image_url, ref _extra_data)) => {
self.build_display_list_for_background_image(state,
style,
display_list_section,
&bounds,
&clip,
image_url);
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,
display_list_section,
&bounds,
&clip,
gradient,
style);
}
Some(computed::Image::Url(ref image_url, ref _extra_data)) => {
self.build_display_list_for_background_image(state,
style,
display_list_section,
&bounds,
&clip,
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,51 +514,54 @@ 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 => {
(Au(0), Au(0))
}
background_origin::T::border_box => {
(-border.left, -border.top)
}
background_origin::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)
}
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::single_value::T::border_box => {
(-border.left, -border.top)
}
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 => {
(absolute_bounds.origin.x, absolute_bounds.origin.y)
}
background_attachment::T::fixed => {
// If the background-attachment value for this image is fixed, then
// 'background-origin' has no effect.
origin_x = Au(0);
origin_y = Au(0);
(Au(0), Au(0))
}
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::single_value::T::fixed => {
// If the background-attachment value for this image is fixed, then
// 'background-origin' has no effect.
origin_x = Au(0);
origin_y = Au(0);
(Au(0), Au(0))
}
};
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,
bounds.size.width - image_size.width);
let vertical_position = model::specified(background.background_position.vertical,
bounds.size.height - image_size.height);
let horizontal_position = model::specified(position.horizontal,
bounds.size.width - image_size.width);
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,31 +577,32 @@ 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,
image_size.width.to_nearest_px() as u32);
&mut bounds.size.width,
abs_x,
image_size.width.to_nearest_px() as u32);
ImageFragmentInfo::tile_image(&mut bounds.origin.y,
&mut bounds.size.height,
abs_y,
image_size.height.to_nearest_px() as u32);
&mut bounds.size.height,
abs_y,
image_size.height.to_nearest_px() as u32);
}
};
// Create the image display item.
let base = state.create_base_display_item(&bounds,
&clip,
self.node,
style.get_cursor(Cursor::Default),
display_list_section);
&clip,
self.node,
style.get_cursor(Cursor::Default),
display_list_section);
state.add_display_item(DisplayItem::ImageClass(box ImageDisplayItem {
base: base,
webrender_image: webrender_image,
image_data: image_data.map(Arc::new),
stretch_size: Size2D::new(image_size.width, image_size.height),
image_rendering: style.get_inheritedbox().image_rendering.clone(),
base: base,
webrender_image: webrender_image,
image_data: image_data.map(Arc::new),
stretch_size: Size2D::new(image_size.width, image_size.height),
image_rendering: style.get_inheritedbox().image_rendering.clone(),
}));
}
}