diff options
author | Pyfisch <pyfisch@gmail.com> | 2017-12-27 22:15:58 +0100 |
---|---|---|
committer | Pyfisch <pyfisch@gmail.com> | 2017-12-28 20:14:31 +0100 |
commit | 3b3d4a985315e91a339fb8e448f23b16b3be1ddc (patch) | |
tree | 9139532af613e01f6a5e316e10bcd8285ae4f287 /components | |
parent | d96fb89c3118ff12142397f25d1546235515ec14 (diff) | |
download | servo-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.rs | 2 | ||||
-rw-r--r-- | components/layout/display_list_builder.rs | 560 | ||||
-rw-r--r-- | components/layout/fragment.rs | 71 | ||||
-rw-r--r-- | components/layout/webrender_helpers.rs | 4 |
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(), |