diff --git a/components/layout/display_list/background.rs b/components/layout/display_list/background.rs index c8a88dd5366..dd9567ee833 100644 --- a/components/layout/display_list/background.rs +++ b/components/layout/display_list/background.rs @@ -2,7 +2,10 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -//! Calculations for CSS images and CSS backgrounds. +//! Calculations for CSS images, backgrounds and borders. +//! +//! * [CSS Images Module Level 3](https://drafts.csswg.org/css-images-3/) +//! * [CSS Backgrounds and Borders Module Level 3](https://drafts.csswg.org/css-backgrounds-3/) #![deny(unsafe_code)] @@ -10,12 +13,16 @@ use app_units::Au; use display_list::ToLayout; -use euclid::{Point2D, Size2D, Vector2D}; -use gfx::display_list; -use model::MaybeAuto; +use euclid::{Point2D, Rect, SideOffsets2D, Size2D, Vector2D}; +use gfx::display_list::{self, BorderDetails, WebRenderImageInfo}; +use model::{self, MaybeAuto}; +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::properties::style_structs::{self, Background}; use style::values::computed::{Angle, GradientItem}; -use style::values::computed::{LengthOrPercentage, LengthOrPercentageOrAuto, Percentage}; -use style::values::computed::Position; +use style::values::computed::{LengthOrPercentage, LengthOrPercentageOrAuto}; +use style::values::computed::{NumberOrPercentage, Percentage, Position}; use style::values::computed::image::{EndingShape, LineDirection}; use style::values::generics::background::BackgroundSize; use style::values::generics::image::{Circle, Ellipse, ShapeExtent}; @@ -23,7 +30,8 @@ use style::values::generics::image::EndingShape as GenericEndingShape; use style::values::generics::image::GradientItem as GenericGradientItem; use style::values::specified::background::BackgroundRepeatKeyword; use style::values::specified::position::{X, Y}; -use webrender_api::{ExtendMode, GradientStop}; +use webrender_api::{BorderRadius, BorderSide, BorderStyle, ColorF, ExtendMode, ImageBorder}; +use webrender_api::{GradientStop, LayoutSize, NinePatchDescriptor, NormalBorder}; /// A helper data structure for gradients. #[derive(Clone, Copy)] @@ -34,9 +42,51 @@ struct StopRun { stop_count: usize, } +/// Placment information for both image and gradient backgrounds. +#[derive(Clone, Copy, Debug)] +pub struct BackgroundPlacement { + /// Rendering bounds. The background will start in the uppper-left corner + /// and fill the whole area. + pub bounds: Rect, + /// Background tile size. Some backgrounds are repeated. These are the + /// dimensions of a single image of the background. + pub tile_size: Size2D, + /// Spacing between tiles. Some backgrounds are not repeated seamless + /// but have seams between them like tiles in real life. + pub tile_spacing: Size2D, + /// A clip area. While the background is rendered according to all the + /// measures above it is only shown within these bounds. + pub css_clip: Rect, + /// Whether or not the background is fixed to the viewport. + pub fixed: bool, +} + +trait ResolvePercentage { + fn resolve(&self, length: u32) -> u32; +} + +impl ResolvePercentage for NumberOrPercentage { + fn resolve(&self, length: u32) -> u32 { + match *self { + NumberOrPercentage::Percentage(p) => (p.0 * length as f32).round() as u32, + NumberOrPercentage::Number(n) => n.round() as u32, + } + } +} + +/// Access element at index modulo the array length. +/// +/// Obviously it does not work with empty arrays. +/// +/// This is used for multiple layered background images. +/// See: https://drafts.csswg.org/css-backgrounds-3/#layering +pub fn get_cyclic(arr: &[T], index: usize) -> &T { + &arr[index % arr.len()] +} + /// For a given area and an image compute how big the /// image should be displayed on the background. -pub fn compute_background_image_size( +fn compute_background_image_size( bg_size: BackgroundSize, bounds_size: Size2D, intrinsic_size: Option>, @@ -99,6 +149,82 @@ pub fn compute_background_image_size( } } +/// Determines where to place an element background image or gradient. +/// +/// Photos have their resolution as intrinsic size while gradients have +/// no intrinsic size. +pub fn compute_background_placement( + bg: &Background, + viewport_size: Size2D, + absolute_bounds: Rect, + intrinsic_size: Option>, + border: SideOffsets2D, + border_padding: SideOffsets2D, + index: usize, +) -> BackgroundPlacement { + 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 => absolute_bounds.inner_rect(border), + BackgroundClip::ContentBox => absolute_bounds.inner_rect(border_padding), + }; + + let mut fixed = false; + let mut bounds = match bg_attachment { + BackgroundAttachment::Scroll => match bg_origin { + BackgroundOrigin::BorderBox => absolute_bounds, + BackgroundOrigin::PaddingBox => absolute_bounds.inner_rect(border), + BackgroundOrigin::ContentBox => absolute_bounds.inner_rect(border_padding), + }, + BackgroundAttachment::Fixed => { + fixed = true; + Rect::new(Point2D::origin(), viewport_size) + }, + }; + + let mut tile_size = compute_background_image_size(bg_size, bounds.size, intrinsic_size); + + let mut tile_spacing = Size2D::zero(); + let own_position = bounds.size - tile_size; + 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, + ); + + BackgroundPlacement { + bounds, + tile_size, + tile_spacing, + css_clip, + fixed, + } +} + fn tile_image_round( position: &mut Au, size: &mut Au, @@ -175,7 +301,7 @@ fn tile_image(position: &mut Au, size: &mut Au, absolute_anchor_origin: Au, imag /// 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. -pub fn tile_image_axis( +fn tile_image_axis( repeat: BackgroundRepeatKeyword, position: &mut Au, size: &mut Au, @@ -510,3 +636,151 @@ fn position_to_offset(position: LengthOrPercentage, total_length: Au) -> f32 { }, } } + +fn scale_border_radii(radii: BorderRadius, factor: f32) -> BorderRadius { + BorderRadius { + top_left: radii.top_left * factor, + top_right: radii.top_right * factor, + bottom_left: radii.bottom_left * factor, + bottom_right: radii.bottom_right * factor, + } +} + +fn handle_overlapping_radii(size: LayoutSize, radii: BorderRadius) -> BorderRadius { + // No two corners' border radii may add up to more than the length of the edge + // between them. To prevent that, all radii are scaled down uniformly. + fn scale_factor(radius_a: f32, radius_b: f32, edge_length: f32) -> f32 { + let required = radius_a + radius_b; + + if required <= edge_length { + 1.0 + } else { + edge_length / required + } + } + + let top_factor = scale_factor(radii.top_left.width, radii.top_right.width, size.width); + let bottom_factor = scale_factor( + radii.bottom_left.width, + radii.bottom_right.width, + size.width, + ); + let left_factor = scale_factor(radii.top_left.height, radii.bottom_left.height, size.height); + let right_factor = scale_factor( + radii.top_right.height, + radii.bottom_right.height, + size.height, + ); + let min_factor = top_factor + .min(bottom_factor) + .min(left_factor) + .min(right_factor); + if min_factor < 1.0 { + scale_border_radii(radii, min_factor) + } else { + radii + } +} + +pub fn build_border_radius( + abs_bounds: &Rect, + border_style: &style_structs::Border, +) -> BorderRadius { + // TODO(cgaebel): Support border radii even in the case of multiple border widths. + // This is an extension of supporting elliptical radii. For now, all percentage + // radii will be relative to the width. + + handle_overlapping_radii( + abs_bounds.size.to_layout(), + BorderRadius { + top_left: model::specified_border_radius( + border_style.border_top_left_radius, + abs_bounds.size, + ).to_layout(), + top_right: model::specified_border_radius( + border_style.border_top_right_radius, + abs_bounds.size, + ).to_layout(), + bottom_right: model::specified_border_radius( + border_style.border_bottom_right_radius, + abs_bounds.size, + ).to_layout(), + bottom_left: model::specified_border_radius( + border_style.border_bottom_left_radius, + abs_bounds.size, + ).to_layout(), + }, + ) +} + +/// Creates a four-sided border with uniform color, width and corner radius. +pub fn simple_normal_border(color: ColorF, style: BorderStyle) -> NormalBorder { + let side = BorderSide { color, style }; + NormalBorder { + left: side, + right: side, + top: side, + bottom: side, + radius: BorderRadius::zero(), + } +} + +/// Calculates radii for the inner side. +/// +/// Radii usually describe the outer side of a border but for the lines to look nice +/// the inner radii need to be smaller depending on the line width. +/// +/// This is used to determine clipping areas. +pub fn calculate_inner_border_radii( + mut radii: BorderRadius, + offsets: SideOffsets2D, +) -> BorderRadius { + fn inner_length(x: f32, offset: Au) -> f32 { + 0.0_f32.max(x - offset.to_f32_px()) + } + radii.top_left.width = inner_length(radii.top_left.width, offsets.left); + radii.bottom_left.width = inner_length(radii.bottom_left.width, offsets.left); + + radii.top_right.width = inner_length(radii.top_right.width, offsets.right); + radii.bottom_right.width = inner_length(radii.bottom_right.width, offsets.right); + + radii.top_left.height = inner_length(radii.top_left.height, offsets.top); + radii.top_right.height = inner_length(radii.top_right.height, offsets.top); + + radii.bottom_left.height = inner_length(radii.bottom_left.height, offsets.bottom); + radii.bottom_right.height = inner_length(radii.bottom_right.height, offsets.bottom); + radii +} + +/// Given an image and a border style constructs a border image. +/// +/// See: https://drafts.csswg.org/css-backgrounds-3/#border-images +pub fn build_image_border_details( + webrender_image: WebRenderImageInfo, + border_style_struct: &style_structs::Border, +) -> Option { + let corners = &border_style_struct.border_image_slice.offsets; + let border_image_repeat = &border_style_struct.border_image_repeat; + if let Some(image_key) = webrender_image.key { + Some(BorderDetails::Image(ImageBorder { + image_key: image_key, + patch: NinePatchDescriptor { + width: webrender_image.width, + height: webrender_image.height, + slice: SideOffsets2D::new( + corners.0.resolve(webrender_image.height), + corners.1.resolve(webrender_image.width), + corners.2.resolve(webrender_image.height), + corners.3.resolve(webrender_image.width), + ), + }, + fill: border_style_struct.border_image_slice.fill, + // TODO(gw): Support border-image-outset + outset: SideOffsets2D::zero(), + repeat_horizontal: border_image_repeat.0.to_layout(), + repeat_vertical: border_image_repeat.1.to_layout(), + })) + } else { + None + } +} diff --git a/components/layout/display_list/builder.rs b/components/layout/display_list/builder.rs index da1867980ee..baa27ad3bf1 100644 --- a/components/layout/display_list/builder.rs +++ b/components/layout/display_list/builder.rs @@ -15,8 +15,10 @@ use block::{BlockFlow, BlockStackingContextType}; use canvas_traits::canvas::{CanvasMsg, FromLayoutMsg}; use context::LayoutContext; use display_list::ToLayout; -use display_list::background::{compute_background_image_size, tile_image_axis}; +use display_list::background::{build_border_radius, build_image_border_details}; +use display_list::background::{calculate_inner_border_radii, compute_background_placement}; use display_list::background::{convert_linear_gradient, convert_radial_gradient}; +use display_list::background::{get_cyclic, simple_normal_border}; use euclid::{rect, Point2D, Rect, SideOffsets2D, Size2D, TypedSize2D, Vector2D}; use flex::FlexFlow; use flow::{BaseFlow, Flow, FlowFlags}; @@ -41,7 +43,7 @@ use gfx_traits::{combine_id_with_fragment_type, FragmentType, StackingContextId} use inline::{InlineFlow, InlineFragmentNodeFlags}; use ipc_channel::ipc; use list_item::ListItemFlow; -use model::{self, MaybeAuto}; +use model::MaybeAuto; use msg::constellation_msg::{BrowsingContextId, PipelineId}; use net_traits::image::base::PixelFormat; use net_traits::image_cache::UsePlaceholder; @@ -52,20 +54,17 @@ use std::default::Default; use std::f32; use std::mem; use std::sync::Arc; -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::overflow_x::T as StyleOverflow; use style::computed_values::pointer_events::T as PointerEvents; use style::computed_values::position::T as StylePosition; use style::computed_values::visibility::T as Visibility; use style::logical_geometry::{LogicalMargin, LogicalPoint, LogicalRect}; -use style::properties::ComputedValues; -use style::properties::style_structs; +use style::properties::{ComputedValues, style_structs}; use style::servo::restyle_damage::ServoRestyleDamage; use style::values::{Either, RGBA}; -use style::values::computed::{Gradient, NumberOrPercentage}; +use style::values::computed::Gradient; use style::values::computed::effects::SimpleShadow; use style::values::computed::pointing::Cursor; use style::values::generics::background::BackgroundSize; @@ -75,24 +74,11 @@ use style_traits::ToCss; use style_traits::cursor::CursorKind; use table_cell::CollapsedBordersForCell; use webrender_api::{self, BorderRadius, BorderSide, BoxShadowClipMode, ClipMode, ColorF}; -use webrender_api::{ComplexClipRegion, ExternalScrollId, FilterOp, GlyphInstance, ImageBorder}; +use webrender_api::{ComplexClipRegion, ExternalScrollId, FilterOp, GlyphInstance}; use webrender_api::{ImageRendering, LayoutRect, LayoutSize, LayoutTransform, LayoutVector2D}; -use webrender_api::{LineStyle, LocalClip, NinePatchDescriptor, NormalBorder, ScrollPolicy}; +use webrender_api::{LineStyle, LocalClip, NormalBorder, ScrollPolicy}; use webrender_api::{ScrollSensitivity, StickyOffsetBounds}; -trait ResolvePercentage { - fn resolve(&self, length: u32) -> u32; -} - -impl ResolvePercentage for NumberOrPercentage { - fn resolve(&self, length: u32) -> u32 { - match *self { - NumberOrPercentage::Percentage(p) => (p.0 * length as f32).round() as u32, - NumberOrPercentage::Number(n) => n.round() as u32, - } - } -} - fn establishes_containing_block_for_absolute( flags: StackingContextCollectionFlags, positioning: StylePosition, @@ -167,10 +153,6 @@ static THREAD_TINT_COLORS: [ColorF; 8] = [ }, ]; -fn get_cyclic(arr: &[T], index: usize) -> &T { - &arr[index % arr.len()] -} - pub struct InlineNodeBorderInfo { is_first_fragment_of_element: bool, is_last_fragment_of_element: bool, @@ -547,19 +529,6 @@ pub trait FragmentDisplayListBuilding { absolute_bounds: &Rect, ); - /// 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, - intrinsic_size: Option>, - index: usize, - ) -> BackgroundPlacement; - /// Adds the display items necessary to paint a webrender image of this fragment to the /// appropriate section of the display list. fn build_display_list_for_webrender_image( @@ -732,82 +701,6 @@ pub trait FragmentDisplayListBuilding { fn fragment_type(&self) -> FragmentType; } -fn scale_border_radii(radii: BorderRadius, factor: f32) -> BorderRadius { - BorderRadius { - top_left: radii.top_left * factor, - top_right: radii.top_right * factor, - bottom_left: radii.bottom_left * factor, - bottom_right: radii.bottom_right * factor, - } -} - -fn handle_overlapping_radii(size: LayoutSize, radii: BorderRadius) -> BorderRadius { - // No two corners' border radii may add up to more than the length of the edge - // between them. To prevent that, all radii are scaled down uniformly. - fn scale_factor(radius_a: f32, radius_b: f32, edge_length: f32) -> f32 { - let required = radius_a + radius_b; - - if required <= edge_length { - 1.0 - } else { - edge_length / required - } - } - - let top_factor = scale_factor(radii.top_left.width, radii.top_right.width, size.width); - let bottom_factor = scale_factor( - radii.bottom_left.width, - radii.bottom_right.width, - size.width, - ); - let left_factor = scale_factor(radii.top_left.height, radii.bottom_left.height, size.height); - let right_factor = scale_factor( - radii.top_right.height, - radii.bottom_right.height, - size.height, - ); - let min_factor = top_factor - .min(bottom_factor) - .min(left_factor) - .min(right_factor); - if min_factor < 1.0 { - scale_border_radii(radii, min_factor) - } else { - radii - } -} - -fn build_border_radius( - abs_bounds: &Rect, - border_style: &style_structs::Border, -) -> BorderRadius { - // TODO(cgaebel): Support border radii even in the case of multiple border widths. - // This is an extension of supporting elliptical radii. For now, all percentage - // radii will be relative to the width. - - handle_overlapping_radii( - abs_bounds.size.to_layout(), - BorderRadius { - top_left: model::specified_border_radius( - border_style.border_top_left_radius, - abs_bounds.size, - ).to_layout(), - top_right: model::specified_border_radius( - border_style.border_top_right_radius, - abs_bounds.size, - ).to_layout(), - bottom_right: model::specified_border_radius( - border_style.border_bottom_right_radius, - abs_bounds.size, - ).to_layout(), - bottom_left: model::specified_border_radius( - border_style.border_bottom_left_radius, - abs_bounds.size, - ).to_layout(), - }, - ) -} - /// Get the border radius for the rectangle inside of a rounded border. This is useful /// for building the clip for the content inside the border. fn build_border_radius_for_inner_rect( @@ -826,97 +719,6 @@ fn build_border_radius_for_inner_rect( calculate_inner_border_radii(radii, border_widths) } -fn simple_normal_border(color: ColorF, style: webrender_api::BorderStyle) -> NormalBorder { - let side = BorderSide { color, style }; - NormalBorder { - left: side, - right: side, - top: side, - bottom: side, - radius: webrender_api::BorderRadius::zero(), - } -} - -fn calculate_inner_border_radii( - mut radii: BorderRadius, - offsets: SideOffsets2D, -) -> BorderRadius { - fn inner_length(x: f32, offset: Au) -> f32 { - 0.0_f32.max(x - offset.to_f32_px()) - } - radii.top_left.width = inner_length(radii.top_left.width, offsets.left); - radii.bottom_left.width = inner_length(radii.bottom_left.width, offsets.left); - - radii.top_right.width = inner_length(radii.top_right.width, offsets.right); - radii.bottom_right.width = inner_length(radii.bottom_right.width, offsets.right); - - radii.top_left.height = inner_length(radii.top_left.height, offsets.top); - radii.top_right.height = inner_length(radii.top_right.height, offsets.top); - - radii.bottom_left.height = inner_length(radii.bottom_left.height, offsets.bottom); - radii.bottom_right.height = inner_length(radii.bottom_right.height, offsets.bottom); - radii -} - -fn build_image_border_details( - webrender_image: WebRenderImageInfo, - border_style_struct: &style_structs::Border, -) -> Option { - let corners = &border_style_struct.border_image_slice.offsets; - let border_image_repeat = &border_style_struct.border_image_repeat; - if let Some(image_key) = webrender_image.key { - Some(BorderDetails::Image(ImageBorder { - image_key: image_key, - patch: NinePatchDescriptor { - width: webrender_image.width, - height: webrender_image.height, - slice: SideOffsets2D::new( - corners.0.resolve(webrender_image.height), - corners.1.resolve(webrender_image.width), - corners.2.resolve(webrender_image.height), - corners.3.resolve(webrender_image.width), - ), - }, - fill: border_style_struct.border_image_slice.fill, - // TODO(gw): Support border-image-outset - outset: SideOffsets2D::zero(), - repeat_horizontal: border_image_repeat.0.to_layout(), - repeat_vertical: border_image_repeat.1.to_layout(), - })) - } else { - None - } -} - -fn convert_text_run_to_glyphs( - text_run: Arc, - range: Range, - mut origin: Point2D, -) -> Vec { - let mut glyphs = vec![]; - - for slice in text_run.natural_word_slices_in_visual_order(&range) { - for glyph in slice.glyphs.iter_glyphs_for_byte_range(&slice.range) { - let glyph_advance = if glyph.char_is_space() { - glyph.advance() + text_run.extra_word_spacing - } else { - glyph.advance() - }; - if !slice.glyphs.is_whitespace() { - let glyph_offset = glyph.offset().unwrap_or(Point2D::zero()); - let point = origin + glyph_offset.to_vector(); - let glyph = GlyphInstance { - index: glyph.id(), - point: point.to_layout(), - }; - glyphs.push(glyph); - } - origin.x += glyph_advance; - } - } - return glyphs; -} - impl FragmentDisplayListBuilding for Fragment { fn collect_stacking_contexts_for_blocklike_fragment( &mut self, @@ -979,15 +781,13 @@ impl FragmentDisplayListBuilding for Fragment { let mut bounds = *absolute_bounds; // 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, - ); + // Background clips are never empty. + let color_clip = &background.background_clip.0.last().unwrap(); // Adjust the clipping region as necessary to account for `border-radius`. let mut border_radii = build_border_radius(absolute_bounds, style.get_border()); - match *color_clip { + match **color_clip { BackgroundClip::BorderBox => {}, BackgroundClip::PaddingBox => { let border = style.logical_border_width().to_physical(style.writing_mode); @@ -1099,83 +899,6 @@ impl FragmentDisplayListBuilding for Fragment { } } - fn compute_background_placement( - &self, - state: &mut DisplayListBuildState, - style: &ComputedValues, - absolute_bounds: Rect, - intrinsic_size: Option>, - 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 => absolute_bounds - .inner_rect(style.logical_border_width().to_physical(style.writing_mode)), - BackgroundClip::ContentBox => { - absolute_bounds.inner_rect(self.border_padding.to_physical(style.writing_mode)) - }, - }; - - let mut bounds = match bg_attachment { - BackgroundAttachment::Scroll => match bg_origin { - BackgroundOrigin::BorderBox => absolute_bounds, - BackgroundOrigin::PaddingBox => absolute_bounds - .inner_rect(style.logical_border_width().to_physical(style.writing_mode)), - BackgroundOrigin::ContentBox => { - absolute_bounds.inner_rect(self.border_padding.to_physical(style.writing_mode)) - }, - }, - BackgroundAttachment::Fixed => Rect::new( - Point2D::origin(), - // Get current viewport - state.layout_context.shared_context().viewport_size(), - ), - }; - - let mut tile_size = compute_background_image_size(bg_size, bounds.size, intrinsic_size); - - let mut tile_spacing = Size2D::zero(); - let own_position = bounds.size - tile_size; - 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, - ); - - BackgroundPlacement { - bounds, - tile_size, - tile_spacing, - css_clip, - } - } - fn build_display_list_for_webrender_image( &self, state: &mut DisplayListBuildState, @@ -1191,8 +914,15 @@ impl FragmentDisplayListBuilding for Fragment { 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); + let placement = compute_background_placement( + style.get_background(), + state.layout_context.shared_context().viewport_size(), + absolute_bounds, + Some(image), + style.logical_border_width().to_physical(style.writing_mode), + self.border_padding.to_physical(style.writing_mode), + index, + ); // Create the image display item. let base = state.create_base_display_item( @@ -1283,8 +1013,15 @@ impl FragmentDisplayListBuilding for Fragment { style: &ComputedValues, index: usize, ) { - let placement = - self.compute_background_placement(state, style, absolute_bounds, None, index); + let placement = compute_background_placement( + style.get_background(), + state.layout_context.shared_context().viewport_size(), + absolute_bounds, + None, + style.logical_border_width().to_physical(style.writing_mode), + self.border_padding.to_physical(style.writing_mode), + index, + ); let base = state.create_base_display_item( &placement.bounds, @@ -3233,20 +2970,33 @@ pub enum BorderPaintingMode<'a> { 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, - /// Background tile size. Some backgrounds are repeated. These are the - /// dimensions of a single image of the background. - tile_size: Size2D, - /// Spacing between tiles. Some backgrounds are not repeated seamless - /// but have seams between them like tiles in real life. - tile_spacing: Size2D, - /// A clip area. While the background is rendered according to all the - /// measures above it is only shown within these bounds. - css_clip: Rect, +fn convert_text_run_to_glyphs( + text_run: Arc, + range: Range, + mut origin: Point2D, +) -> Vec { + let mut glyphs = vec![]; + + for slice in text_run.natural_word_slices_in_visual_order(&range) { + for glyph in slice.glyphs.iter_glyphs_for_byte_range(&slice.range) { + let glyph_advance = if glyph.char_is_space() { + glyph.advance() + text_run.extra_word_spacing + } else { + glyph.advance() + }; + if !slice.glyphs.is_whitespace() { + let glyph_offset = glyph.offset().unwrap_or(Point2D::zero()); + let point = origin + glyph_offset.to_vector(); + let glyph = GlyphInstance { + index: glyph.id(), + point: point.to_layout(), + }; + glyphs.push(glyph); + } + origin.x += glyph_advance; + } + } + return glyphs; } pub struct IndexableTextItem {