aboutsummaryrefslogtreecommitdiffstats
path: root/components
diff options
context:
space:
mode:
authorPyfisch <pyfisch@gmail.com>2017-12-27 22:15:58 +0100
committerPyfisch <pyfisch@gmail.com>2017-12-28 20:14:31 +0100
commit3b3d4a985315e91a339fb8e448f23b16b3be1ddc (patch)
tree9139532af613e01f6a5e316e10bcd8285ae4f287 /components
parentd96fb89c3118ff12142397f25d1546235515ec14 (diff)
downloadservo-3b3d4a985315e91a339fb8e448f23b16b3be1ddc.tar.gz
servo-3b3d4a985315e91a339fb8e448f23b16b3be1ddc.zip
Unify background placement code
Merges the implementations for background-image placement from gradients and images. Add missing parts and fix bugs. Now supported are the CSS properties: * background-attachment (except for local value) * background-clip * background-origin * background-position-x/y * background-repeat * background-size It should be noted that backgrounds are not clipped to rounded border corners.
Diffstat (limited to 'components')
-rw-r--r--components/gfx/display_list/mod.rs2
-rw-r--r--components/layout/display_list_builder.rs560
-rw-r--r--components/layout/fragment.rs71
-rw-r--r--components/layout/webrender_helpers.rs4
4 files changed, 307 insertions, 330 deletions
diff --git a/components/gfx/display_list/mod.rs b/components/gfx/display_list/mod.rs
index 98b78ef5c5d..290b4aed059 100644
--- a/components/gfx/display_list/mod.rs
+++ b/components/gfx/display_list/mod.rs
@@ -729,6 +729,7 @@ pub struct GradientDisplayItem {
///
/// Without tiles, the tile will be the same size as the background.
pub tile: Size2D<Au>,
+ pub tile_spacing: Size2D<Au>,
}
/// Paints a radial gradient.
@@ -763,6 +764,7 @@ pub struct RadialGradientDisplayItem {
///
/// Without tiles, the tile will be the same size as the background.
pub tile: Size2D<Au>,
+ pub tile_spacing: Size2D<Au>,
}
/// A normal border, supporting CSS border styles.
diff --git a/components/layout/display_list_builder.rs b/components/layout/display_list_builder.rs
index 171e56ab9ea..ec706763965 100644
--- a/components/layout/display_list_builder.rs
+++ b/components/layout/display_list_builder.rs
@@ -19,7 +19,7 @@ use flex::FlexFlow;
use flow::{BaseFlow, Flow, FlowFlags};
use flow_ref::FlowRef;
use fnv::FnvHashMap;
-use fragment::{CanvasFragmentSource, CoordinateSystem, Fragment, ImageFragmentInfo, ScannedTextFragmentInfo};
+use fragment::{CanvasFragmentSource, CoordinateSystem, Fragment, ScannedTextFragmentInfo};
use fragment::SpecificFragmentInfo;
use gfx::display_list;
use gfx::display_list::{BLUR_INFLATION_FACTOR, BaseDisplayItem, BorderDetails, BorderDisplayItem};
@@ -48,7 +48,9 @@ use std::{cmp, f32};
use std::default::Default;
use std::mem;
use std::sync::Arc;
-use style::computed_values::{background_attachment, background_clip, background_origin};
+use style::computed_values::background_attachment::single_value::T as BackgroundAttachment;
+use style::computed_values::background_clip::single_value::T as BackgroundClip;
+use style::computed_values::background_origin::single_value::T as BackgroundOrigin;
use style::computed_values::border_style::T as BorderStyle;
use style::computed_values::cursor;
use style::computed_values::image_rendering::T as ImageRendering;
@@ -58,7 +60,6 @@ use style::computed_values::position::T as StylePosition;
use style::computed_values::visibility::T as Visibility;
use style::logical_geometry::{LogicalMargin, LogicalPoint, LogicalRect, LogicalSize, WritingMode};
use style::properties::ComputedValues;
-use style::properties::longhands::background_origin::single_value::computed_value::T as BackgroundOrigin;
use style::properties::longhands::border_image_repeat::computed_value::RepeatKeyword;
use style::properties::style_structs;
use style::servo::restyle_damage::ServoRestyleDamage;
@@ -475,13 +476,18 @@ pub trait FragmentDisplayListBuilding {
display_list_section: DisplayListSection,
absolute_bounds: &Rect<Au>);
- /// Computes the background size for an image with the given background area according to the
- /// rules in CSS-BACKGROUNDS § 3.9.
- fn compute_background_image_size(&self,
- style: &ComputedValues,
- bounds: &Rect<Au>,
- image: &WebRenderImageInfo, index: usize)
- -> Size2D<Au>;
+ /// Determines where to place an element background image or gradient.
+ ///
+ /// Photos have their resolution as intrinsic size while gradients have
+ /// no intrinsic size.
+ fn compute_background_placement(
+ &self,
+ state: &mut DisplayListBuildState,
+ style: &ComputedValues,
+ absolute_bounds: Rect<Au>,
+ intrinsic_size: Option<Size2D<Au>>,
+ index: usize
+ ) -> BackgroundPlacement;
/// Adds the display items necessary to paint a webrender image of this fragment to the
/// appropriate section of the display list.
@@ -489,8 +495,7 @@ pub trait FragmentDisplayListBuilding {
state: &mut DisplayListBuildState,
style: &ComputedValues,
display_list_section: DisplayListSection,
- absolute_bounds: &Rect<Au>,
- clip: &LocalClip,
+ absolute_bounds: Rect<Au>,
webrender_image: WebRenderImageInfo,
index: usize);
@@ -510,7 +515,6 @@ pub trait FragmentDisplayListBuilding {
state: &mut DisplayListBuildState,
display_list_section: DisplayListSection,
absolute_bounds: Rect<Au>,
- clip: &LocalClip,
gradient: &Gradient,
style: &ComputedValues,
index: usize);
@@ -1024,6 +1028,176 @@ fn convert_ellipse_size_keyword(keyword: ShapeExtent,
}
}
+/// Subtract offsets from a bounding box.
+///
+/// As an example if the bounds are the border-box and the border
+/// is provided as offsets the result will be the padding-box.
+fn calculate_inner_bounds(mut bounds: Rect<Au>, offsets: SideOffsets2D<Au>) -> Rect<Au> {
+ bounds.origin.x += offsets.left;
+ bounds.origin.y += offsets.top;
+ bounds.size.width -= offsets.horizontal();
+ bounds.size.height -= offsets.vertical();
+ bounds
+}
+
+
+/// For a given area and an image compute how big the
+/// image should be displayed on the background.
+fn compute_background_image_size(bg_size: BackgroundSize<LengthOrPercentageOrAuto>,
+ bounds_size: Size2D<Au>,
+ intrinsic_size: Option<Size2D<Au>>)
+ -> Size2D<Au> {
+ let own_size = if let Some(size) = intrinsic_size {
+ size
+ } else {
+ return match bg_size {
+ BackgroundSize::Cover | BackgroundSize::Contain => bounds_size,
+ BackgroundSize::Explicit { width, height } => {
+ Size2D::new(
+ MaybeAuto::from_style(width, bounds_size.width)
+ .specified_or_default(bounds_size.width),
+ MaybeAuto::from_style(height, bounds_size.height)
+ .specified_or_default(bounds_size.height))
+ }
+ }
+ };
+ // If `image_aspect_ratio` < `bounds_aspect_ratio`, the image is tall; otherwise, it is
+ // wide.
+ let image_aspect_ratio = own_size.width.to_f32_px() / own_size.height.to_f32_px();
+ let bounds_aspect_ratio = bounds_size.width.to_f32_px() / bounds_size.height.to_f32_px();
+ match (bg_size, image_aspect_ratio < bounds_aspect_ratio) {
+ (BackgroundSize::Contain, false) | (BackgroundSize::Cover, true) => {
+ Size2D::new(bounds_size.width,
+ bounds_size.width.scale_by(image_aspect_ratio.recip()))
+ }
+
+ (BackgroundSize::Contain, true) | (BackgroundSize::Cover, false) => {
+ Size2D::new(bounds_size.height.scale_by(image_aspect_ratio),
+ bounds_size.height)
+ }
+
+ (BackgroundSize::Explicit { width, height: LengthOrPercentageOrAuto::Auto }, _) => {
+ let width = MaybeAuto::from_style(width, bounds_size.width)
+ .specified_or_default(own_size.width);
+ Size2D::new(width, width.scale_by(image_aspect_ratio.recip()))
+ }
+
+ (BackgroundSize::Explicit { width: LengthOrPercentageOrAuto::Auto, height }, _) => {
+ let height = MaybeAuto::from_style(height, bounds_size.height)
+ .specified_or_default(own_size.height);
+ Size2D::new(height.scale_by(image_aspect_ratio), height)
+ }
+
+ (BackgroundSize::Explicit { width, height }, _) => {
+ Size2D::new(MaybeAuto::from_style(width, bounds_size.width)
+ .specified_or_default(own_size.width),
+ MaybeAuto::from_style(height, bounds_size.height)
+ .specified_or_default(own_size.height))
+ }
+ }
+}
+
+fn tile_image_round(position: &mut Au,
+ size: &mut Au,
+ absolute_anchor_origin: Au,
+ image_size: &mut Au) {
+ if *size == Au(0) || *image_size == Au(0) {
+ *position = Au(0);
+ *size =Au(0);
+ return;
+ }
+
+ let number_of_tiles = (size.to_f32_px() / image_size.to_f32_px()).round().max(1.0);
+ *image_size = *size / (number_of_tiles as i32);
+ tile_image(position, size, absolute_anchor_origin, *image_size);
+}
+
+fn tile_image_spaced(position: &mut Au,
+ size: &mut Au,
+ tile_spacing: &mut Au,
+ absolute_anchor_origin: Au,
+ image_size: Au) {
+ if *size == Au(0) || image_size == Au(0) {
+ *position = Au(0);
+ *size = Au(0);
+ *tile_spacing = Au(0);
+ return;
+ }
+
+ // Per the spec, if the space available is not enough for two images, just tile as
+ // normal but only display a single tile.
+ if image_size * 2 >= *size {
+ tile_image(position,
+ size,
+ absolute_anchor_origin,
+ image_size);
+ *tile_spacing = Au(0);
+ *size = image_size;
+ return;
+ }
+
+ // Take the box size, remove room for two tiles on the edges, and then calculate how many
+ // other tiles fit in between them.
+ let size_remaining = *size - (image_size * 2);
+ let num_middle_tiles = (size_remaining.to_f32_px() / image_size.to_f32_px()).floor() as i32;
+
+ // Allocate the remaining space as padding between tiles. background-position is ignored
+ // as per the spec, so the position is just the box origin. We are also ignoring
+ // background-attachment here, which seems unspecced when combined with
+ // background-repeat: space.
+ let space_for_middle_tiles = image_size * num_middle_tiles;
+ *tile_spacing = (size_remaining - space_for_middle_tiles) / (num_middle_tiles + 1);
+}
+
+/// Tile an image
+fn tile_image(position: &mut Au,
+ size: &mut Au,
+ absolute_anchor_origin: Au,
+ image_size: Au) {
+ // Avoid division by zero below!
+ if image_size == Au(0) {
+ return
+ }
+
+ let delta_pixels = absolute_anchor_origin - *position;
+ let image_size_px = image_size.to_f32_px();
+ let tile_count = ((delta_pixels.to_f32_px() + image_size_px - 1.0) / image_size_px).floor();
+ let offset = image_size * (tile_count as i32);
+ let new_position = absolute_anchor_origin - offset;
+ *size = *position - new_position + *size;
+ *position = new_position;
+}
+
+/// For either the x or the y axis ajust various values to account for tiling.
+///
+/// This is done separately for both axes because the repeat keywords may differ.
+fn tile_image_axis(repeat: BackgroundRepeatKeyword,
+ position: &mut Au,
+ size: &mut Au,
+ tile_size: &mut Au,
+ tile_spacing: &mut Au,
+ offset: Au,
+ clip_origin: Au,
+ clip_size: Au)
+{
+ let absolute_anchor_origin = *position + offset;
+ match repeat {
+ BackgroundRepeatKeyword::NoRepeat => {
+ *position += offset;
+ *size = *tile_size;
+ return
+ }
+ BackgroundRepeatKeyword::Repeat => (),
+ BackgroundRepeatKeyword::Space => tile_image_spaced(
+ position, size, tile_spacing, absolute_anchor_origin, *tile_size),
+ BackgroundRepeatKeyword::Round => tile_image_round(
+ position, size, absolute_anchor_origin, tile_size),
+ };
+ *position = clip_origin;
+ *size = clip_size;
+ tile_image(position, size, absolute_anchor_origin, *tile_size);
+}
+
impl FragmentDisplayListBuilding for Fragment {
fn collect_stacking_contexts_for_blocklike_fragment(&mut self,
state: &mut StackingContextCollectionState)
@@ -1073,20 +1247,14 @@ impl FragmentDisplayListBuilding for Fragment {
background.background_image.0.len() - 1);
match *color_clip {
- background_clip::single_value::T::BorderBox => {}
- background_clip::single_value::T::PaddingBox => {
+ BackgroundClip::BorderBox => {}
+ BackgroundClip::PaddingBox => {
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();
+ bounds = calculate_inner_bounds(bounds, border);
}
- background_clip::single_value::T::ContentBox => {
+ BackgroundClip::ContentBox => {
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;
- bounds.size.width = bounds.size.width - border_padding.horizontal();
- bounds.size.height = bounds.size.height - border_padding.vertical();
+ bounds = calculate_inner_bounds(bounds, border_padding);
}
}
@@ -1125,7 +1293,6 @@ impl FragmentDisplayListBuilding for Fragment {
self.build_display_list_for_background_gradient(state,
display_list_section,
*absolute_bounds,
- &clip,
gradient,
style,
i);
@@ -1140,8 +1307,7 @@ impl FragmentDisplayListBuilding for Fragment {
self.build_display_list_for_webrender_image(state,
style,
display_list_section,
- &bounds,
- &clip,
+ *absolute_bounds,
webrender_image,
i);
}
@@ -1168,8 +1334,7 @@ impl FragmentDisplayListBuilding for Fragment {
self.build_display_list_for_webrender_image(state,
style,
display_list_section,
- &bounds,
- &clip,
+ *absolute_bounds,
webrender_image,
i);
}
@@ -1184,172 +1349,100 @@ impl FragmentDisplayListBuilding for Fragment {
}
}
- fn compute_background_image_size(&self,
- style: &ComputedValues,
- bounds: &Rect<Au>,
- image: &WebRenderImageInfo,
- index: usize)
- -> Size2D<Au> {
- // If `image_aspect_ratio` < `bounds_aspect_ratio`, the image is tall; otherwise, it is
- // wide.
- let image_aspect_ratio = (image.width as f64) / (image.height as f64);
- 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));
- let background_size = get_cyclic(&style.get_background().background_size.0, index).clone();
- match (background_size, image_aspect_ratio < bounds_aspect_ratio) {
- (BackgroundSize::Contain, false) | (BackgroundSize::Cover, true) => {
- Size2D::new(bounds.size.width,
- Au::from_f64_px(bounds.size.width.to_f64_px() / image_aspect_ratio))
- }
+ fn compute_background_placement(
+ &self,
+ state: &mut DisplayListBuildState,
+ style: &ComputedValues,
+ absolute_bounds: Rect<Au>,
+ intrinsic_size: Option<Size2D<Au>>,
+ index: usize
+ ) -> BackgroundPlacement {
+ let bg = style.get_background();
+ let bg_attachment = *get_cyclic(&bg.background_attachment.0, index);
+ let bg_clip = *get_cyclic(&bg.background_clip.0, index);
+ let bg_origin = *get_cyclic(&bg.background_origin.0, index);
+ let bg_position_x = get_cyclic(&bg.background_position_x.0, index);
+ let bg_position_y = get_cyclic(&bg.background_position_y.0, index);
+ let bg_repeat = get_cyclic(&bg.background_repeat.0, index);
+ let bg_size = *get_cyclic(&bg.background_size.0, index);
+
+ let css_clip = match bg_clip {
+ BackgroundClip::BorderBox => absolute_bounds,
+ BackgroundClip::PaddingBox => calculate_inner_bounds(
+ absolute_bounds,
+ style.logical_border_width().to_physical(style.writing_mode),
+ ),
+ BackgroundClip::ContentBox => calculate_inner_bounds(
+ absolute_bounds,
+ self.border_padding.to_physical(style.writing_mode),
+ ),
+ };
- (BackgroundSize::Contain, true) | (BackgroundSize::Cover, false) => {
- Size2D::new(Au::from_f64_px(bounds.size.height.to_f64_px() * image_aspect_ratio),
- bounds.size.height)
- }
+ let mut bounds = match bg_attachment {
+ BackgroundAttachment::Scroll => match bg_origin {
+ BackgroundOrigin::BorderBox => absolute_bounds,
+ BackgroundOrigin::PaddingBox => calculate_inner_bounds(
+ absolute_bounds,
+ style.logical_border_width().to_physical(style.writing_mode),
+ ),
+ BackgroundOrigin::ContentBox => calculate_inner_bounds(
+ absolute_bounds,
+ self.border_padding.to_physical(style.writing_mode),
+ ),
+ },
+ BackgroundAttachment::Fixed => Rect::new(
+ Point2D::origin(),
+ // Get current viewport
+ state.layout_context.shared_context().viewport_size(),
+ ),
+ };
- (BackgroundSize::Explicit { width, height: LengthOrPercentageOrAuto::Auto }, _) => {
- let width = MaybeAuto::from_style(width, bounds.size.width)
- .specified_or_default(intrinsic_size.width);
- Size2D::new(width, Au::from_f64_px(width.to_f64_px() / image_aspect_ratio))
- }
+ let mut tile_size = compute_background_image_size(bg_size, bounds.size, intrinsic_size);
- (BackgroundSize::Explicit { width: LengthOrPercentageOrAuto::Auto, height }, _) => {
- let height = MaybeAuto::from_style(height, bounds.size.height)
- .specified_or_default(intrinsic_size.height);
- Size2D::new(Au::from_f64_px(height.to_f64_px() * image_aspect_ratio), height)
- }
-
- (BackgroundSize::Explicit { width, height }, _) => {
- Size2D::new(MaybeAuto::from_style(width, bounds.size.width)
- .specified_or_default(intrinsic_size.width),
- MaybeAuto::from_style(height, bounds.size.height)
- .specified_or_default(intrinsic_size.height))
- }
- }
+ let mut tile_spacing = Size2D::zero();
+ let own_position = bounds.size - intrinsic_size.unwrap_or(Size2D::zero());
+ let pos_x = bg_position_x.to_used_value(own_position.width);
+ let pos_y = bg_position_y.to_used_value(own_position.height);
+ tile_image_axis(
+ bg_repeat.0,
+ &mut bounds.origin.x,
+ &mut bounds.size.width,
+ &mut tile_size.width,
+ &mut tile_spacing.width,
+ pos_x,
+ css_clip.origin.x,
+ css_clip.size.width);
+ tile_image_axis(
+ bg_repeat.1,
+ &mut bounds.origin.y,
+ &mut bounds.size.height,
+ &mut tile_size.height,
+ &mut tile_spacing.height,
+ pos_y,
+ css_clip.origin.y,
+ css_clip.size.height);
+
+ return BackgroundPlacement { bounds, tile_size, tile_spacing, css_clip }
}
fn build_display_list_for_webrender_image(&self,
state: &mut DisplayListBuildState,
style: &ComputedValues,
display_list_section: DisplayListSection,
- absolute_bounds: &Rect<Au>,
- clip: &LocalClip,
+ absolute_bounds: Rect<Au>,
webrender_image: WebRenderImageInfo,
index: usize) {
debug!("(building display list) building background image");
- let background = style.get_background();
-
- // Use `background-size` to get the size.
- let mut bounds = *absolute_bounds;
- let image_size = self.compute_background_image_size(style, &bounds,
- &webrender_image, index);
-
- // Background image should be positioned on the padding box basis.
- let border = style.logical_border_width().to_physical(style.writing_mode);
-
- // Use 'background-origin' to get the origin value.
- let origin = get_cyclic(&background.background_origin.0, index);
- let (mut origin_x, mut origin_y) = match *origin {
- background_origin::single_value::T::PaddingBox => {
- (Au(0), Au(0))
- }
- background_origin::single_value::T::BorderBox => {
- (-border.left, -border.top)
- }
- background_origin::single_value::T::ContentBox => {
- 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 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 horiz_position = *get_cyclic(&background.background_position_x.0, index);
- let vert_position = *get_cyclic(&background.background_position_y.0, index);
- // Use `background-position` to get the offset.
- let horizontal_position = horiz_position.to_used_value(bounds.size.width - image_size.width);
- let vertical_position = vert_position.to_used_value(bounds.size.height - image_size.height);
-
- // The anchor position for this background, based on both the background-attachment
- // and background-position properties.
- let anchor_origin_x = border.left + virtual_origin_x + origin_x + horizontal_position;
- let anchor_origin_y = border.top + virtual_origin_y + origin_y + vertical_position;
-
- let mut tile_spacing = Size2D::zero();
- let mut stretch_size = image_size;
-
- // Adjust origin and size based on background-repeat
- let background_repeat = get_cyclic(&background.background_repeat.0, index);
- match background_repeat.0 {
- BackgroundRepeatKeyword::NoRepeat => {
- bounds.origin.x = anchor_origin_x;
- bounds.size.width = image_size.width;
- }
- BackgroundRepeatKeyword::Repeat => {
- ImageFragmentInfo::tile_image(&mut bounds.origin.x,
- &mut bounds.size.width,
- anchor_origin_x,
- image_size.width);
- }
- BackgroundRepeatKeyword::Space => {
- ImageFragmentInfo::tile_image_spaced(&mut bounds.origin.x,
- &mut bounds.size.width,
- &mut tile_spacing.width,
- anchor_origin_x,
- image_size.width);
- }
- BackgroundRepeatKeyword::Round => {
- ImageFragmentInfo::tile_image_round(&mut bounds.origin.x,
- &mut bounds.size.width,
- anchor_origin_x,
- &mut stretch_size.width);
- }
- };
- match background_repeat.1 {
- BackgroundRepeatKeyword::NoRepeat => {
- bounds.origin.y = anchor_origin_y;
- bounds.size.height = image_size.height;
- }
- BackgroundRepeatKeyword::Repeat => {
- ImageFragmentInfo::tile_image(&mut bounds.origin.y,
- &mut bounds.size.height,
- anchor_origin_y,
- image_size.height);
- }
- BackgroundRepeatKeyword::Space => {
- ImageFragmentInfo::tile_image_spaced(&mut bounds.origin.y,
- &mut bounds.size.height,
- &mut tile_spacing.height,
- anchor_origin_y,
- image_size.height);
-
- }
- BackgroundRepeatKeyword::Round => {
- ImageFragmentInfo::tile_image_round(&mut bounds.origin.y,
- &mut bounds.size.height,
- anchor_origin_y,
- &mut stretch_size.height);
- }
- };
+ let image = Size2D::new(
+ Au::from_px(webrender_image.width as i32),
+ Au::from_px(webrender_image.height as i32));
+ let placement = self.compute_background_placement(
+ state, style, absolute_bounds, Some(image), index);
// Create the image display item.
- let base = state.create_base_display_item(&bounds,
- *clip,
+ let base = state.create_base_display_item(&placement.bounds,
+ LocalClip::Rect(placement.css_clip.to_rectf()),
self.node,
style.get_cursor(Cursor::Default),
display_list_section);
@@ -1359,8 +1452,8 @@ impl FragmentDisplayListBuilding for Fragment {
base: base,
webrender_image: webrender_image,
image_data: None,
- stretch_size: stretch_size,
- tile_spacing: tile_spacing,
+ stretch_size: placement.tile_size,
+ tile_spacing: placement.tile_spacing,
image_rendering: style.get_inheritedbox().image_rendering.clone(),
})));
@@ -1419,80 +1512,15 @@ impl FragmentDisplayListBuilding for Fragment {
state: &mut DisplayListBuildState,
display_list_section: DisplayListSection,
absolute_bounds: Rect<Au>,
- clip: &LocalClip,
gradient: &Gradient,
style: &ComputedValues,
- index: usize) {
- // Calculate where the first "tile" needs to be placed on one axis.
- // * base is the beginning of the visible area
- // * start is the current "tile" position
- // * tile is the length of the "tile"
- // Returns a difference between start and new start.
- // It holds that base - tile < start - diff <= base
- fn get_first_tile(base: Au, start: Au, tile: Au) -> Au {
- if tile == Au(0) {
- return Au(0);
- }
- if start > base {
- ((start - base) / tile + 1) * tile
- } else {
- ((base - start) / tile) * tile
- }
- }
-
- let bg = style.get_background();
- let bg_origin = get_cyclic(&bg.background_origin.0, index).clone();
- let bg_size = get_cyclic(&bg.background_size.0, index).clone();
- let bg_position_x = get_cyclic(&bg.background_position_x.0, index).clone();
- let bg_position_y = get_cyclic(&bg.background_position_y.0, index).clone();
-
- let mut bounds = absolute_bounds;
-
- match bg_origin {
- BackgroundOrigin::BorderBox => {}
- BackgroundOrigin::PaddingBox => {
- let border = style.logical_border_width().to_physical(style.writing_mode);
- bounds.origin.x += border.left;
- bounds.origin.y += border.top;
- bounds.size.width -= border.horizontal();
- bounds.size.height -= border.vertical();
- }
- BackgroundOrigin::ContentBox => {
- let border_padding = self.border_padding.to_physical(style.writing_mode);
- bounds.origin.x += border_padding.left;
- bounds.origin.y += border_padding.top;
- bounds.size.width -= border_padding.horizontal();
- bounds.size.height -= border_padding.vertical();
- }
- }
-
- bounds.origin.x += bg_position_x.to_used_value(bounds.size.width);
- bounds.origin.y += bg_position_y.to_used_value(bounds.size.height);
-
- let tile = match bg_size {
- BackgroundSize::Cover | BackgroundSize::Contain => bounds.size,
- BackgroundSize::Explicit { width, height } => {
- Size2D::new(
- MaybeAuto::from_style(width, bounds.size.width)
- .specified_or_default(bounds.size.width),
- MaybeAuto::from_style(height, bounds.size.height)
- .specified_or_default(bounds.size.height))
- }
- };
+ index: usize)
+ {
+ let placement = self.compute_background_placement(
+ state, style, absolute_bounds, None, index);
- let diff_x = get_first_tile(absolute_bounds.origin.x,
- bounds.origin.x,
- tile.width);
- let diff_y = get_first_tile(absolute_bounds.origin.y,
- bounds.origin.y,
- tile.height);
- let tiled_bounds = Rect::new(
- Point2D::new(bounds.origin.x - diff_x, bounds.origin.y - diff_y),
- Size2D::new(absolute_bounds.size.width + diff_x,
- absolute_bounds.size.height + diff_y));
-
- let base = state.create_base_display_item(&tiled_bounds,
- *clip,
+ let base = state.create_base_display_item(&placement.bounds,
+ LocalClip::Rect(placement.css_clip.to_rectf()),
self.node,
style.get_cursor(Cursor::Default),
display_list_section);
@@ -1500,19 +1528,20 @@ impl FragmentDisplayListBuilding for Fragment {
let display_item = match gradient.kind {
GradientKind::Linear(angle_or_corner) => {
let gradient = convert_linear_gradient(
- tile,
+ placement.tile_size,
&gradient.items[..],
angle_or_corner,
gradient.repeating);
DisplayItem::Gradient(Box::new(GradientDisplayItem {
base: base,
gradient: gradient,
- tile: tile,
+ tile: placement.tile_size,
+ tile_spacing: placement.tile_spacing,
}))
}
GradientKind::Radial(shape, center, _angle) => {
let gradient = convert_radial_gradient(
- tile,
+ placement.tile_size,
&gradient.items[..],
shape,
center,
@@ -1520,7 +1549,8 @@ impl FragmentDisplayListBuilding for Fragment {
DisplayItem::RadialGradient(Box::new(RadialGradientDisplayItem {
base: base,
gradient: gradient,
- tile: tile,
+ tile: placement.tile_size,
+ tile_spacing: placement.tile_spacing,
}))
}
};
@@ -3327,3 +3357,19 @@ pub enum BorderPaintingMode<'a> {
/// Paint no borders.
Hidden,
}
+
+#[derive(Clone, Copy, Debug)]
+pub struct BackgroundPlacement {
+ /// Rendering bounds. The background will start in the uppper-left corner
+ /// and fill the whole area.
+ bounds: Rect<Au>,
+ /// Background tile size. Some backgrounds are repeated. These are the
+ /// dimensions of a single image of the background.
+ tile_size: Size2D<Au>,
+ /// Spacing between tiles. Some backgrounds are not repeated seamless
+ /// but have seams between them like tiles in real life.
+ tile_spacing: Size2D<Au>,
+ /// A clip area. While the background is rendered according to all the
+ /// measures above it is only shown within these bounds.
+ css_clip: Rect<Au>,
+}
diff --git a/components/layout/fragment.rs b/components/layout/fragment.rs
index b2a627347da..77d5f714986 100644
--- a/components/layout/fragment.rs
+++ b/components/layout/fragment.rs
@@ -421,77 +421,6 @@ impl ImageFragmentInfo {
metadata: metadata,
}
}
-
- pub fn tile_image_round(position: &mut Au,
- size: &mut Au,
- absolute_anchor_origin: Au,
- image_size: &mut Au) {
- if *size == Au(0) || *image_size == Au(0) {
- *position = Au(0);
- *size =Au(0);
- return;
- }
-
- let number_of_tiles = (size.to_f32_px() / image_size.to_f32_px()).round().max(1.0);
- *image_size = *size / (number_of_tiles as i32);
- ImageFragmentInfo::tile_image(position, size, absolute_anchor_origin, *image_size);
- }
-
- pub fn tile_image_spaced(position: &mut Au,
- size: &mut Au,
- tile_spacing: &mut Au,
- absolute_anchor_origin: Au,
- image_size: Au) {
- if *size == Au(0) || image_size == Au(0) {
- *position = Au(0);
- *size = Au(0);
- *tile_spacing = Au(0);
- return;
- }
-
- // Per the spec, if the space available is not enough for two images, just tile as
- // normal but only display a single tile.
- if image_size * 2 >= *size {
- ImageFragmentInfo::tile_image(position,
- size,
- absolute_anchor_origin,
- image_size);
- *tile_spacing = Au(0);
- *size = image_size;
- return;
- }
-
- // Take the box size, remove room for two tiles on the edges, and then calculate how many
- // other tiles fit in between them.
- let size_remaining = *size - (image_size * 2);
- let num_middle_tiles = (size_remaining.to_f32_px() / image_size.to_f32_px()).floor() as i32;
-
- // Allocate the remaining space as padding between tiles. background-position is ignored
- // as per the spec, so the position is just the box origin. We are also ignoring
- // background-attachment here, which seems unspecced when combined with
- // background-repeat: space.
- let space_for_middle_tiles = image_size * num_middle_tiles;
- *tile_spacing = (size_remaining - space_for_middle_tiles) / (num_middle_tiles + 1);
- }
-
- /// Tile an image
- pub fn tile_image(position: &mut Au,
- size: &mut Au,
- absolute_anchor_origin: Au,
- image_size: Au) {
- // Avoid division by zero below!
- if image_size == Au(0) {
- return
- }
-
- let delta_pixels = absolute_anchor_origin - *position;
- let image_size_px = image_size.to_f32_px();
- let tile_count = ((delta_pixels.to_f32_px() + image_size_px - 1.0) / image_size_px).floor();
- let offset = image_size * (tile_count as i32);
- let new_position = absolute_anchor_origin - offset;
- *size = *position - new_position + *size;
- *position = new_position;
- }
}
/// A fragment that represents an inline frame (iframe). This stores the frame ID so that the
diff --git a/components/layout/webrender_helpers.rs b/components/layout/webrender_helpers.rs
index 52a19e9c70b..ea224ad2078 100644
--- a/components/layout/webrender_helpers.rs
+++ b/components/layout/webrender_helpers.rs
@@ -442,7 +442,7 @@ impl WebRenderDisplayItemConverter for DisplayItem {
builder.push_gradient(&self.prim_info(),
gradient,
item.tile.to_sizef(),
- webrender_api::LayoutSize::zero());
+ item.tile_spacing.to_sizef());
}
DisplayItem::RadialGradient(ref item) => {
let center = item.gradient.center.to_pointf();
@@ -459,7 +459,7 @@ impl WebRenderDisplayItemConverter for DisplayItem {
builder.push_radial_gradient(&self.prim_info(),
gradient,
item.tile.to_sizef(),
- webrender_api::LayoutSize::zero());
+ item.tile_spacing.to_sizef());
}
DisplayItem::Line(ref item) => {
builder.push_line(&self.prim_info(),