diff options
author | bors-servo <lbergstrom+bors@mozilla.com> | 2018-10-23 05:29:06 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-10-23 05:29:06 -0400 |
commit | 5422ca4a9b681cb07150ab9a1f01afa7c9f1b049 (patch) | |
tree | 173ae8db0829ac258fcf835fad3038ea984ba6a0 | |
parent | 2304f02123038a2e729bc90a2e2db9a795f30158 (diff) | |
parent | d9b1950d74e6a3c17de0579d47a8cd7987a29d9e (diff) | |
download | servo-5422ca4a9b681cb07150ab9a1f01afa7c9f1b049.tar.gz servo-5422ca4a9b681cb07150ab9a1f01afa7c9f1b049.zip |
Auto merge of #21972 - pyfisch:split-background-rs, r=jdm
Split background.rs file in background, border and gradient
I happened to read a style guide and found that many people use short function names but import only the parent module so the function is called gradient::linear instead of create_linear_gradient. (I usually prefix my function names with a verb but this does not appear to be the preferred style)
In the layout crate files quite often contain the line `#![deny(unsafe_code)]` but writing it once in `lib.rs` should suffice.
Finally the Webrender DisplayListBuilder is now bypassed for some items and they are directly pushed to the display list. (See #19676)
<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/21972)
<!-- Reviewable:end -->
22 files changed, 593 insertions, 701 deletions
diff --git a/components/layout/block.rs b/components/layout/block.rs index ae1bc242e32..21ddba5d96e 100644 --- a/components/layout/block.rs +++ b/components/layout/block.rs @@ -25,8 +25,6 @@ //! //! http://dev.w3.org/csswg/css-sizing/ -#![deny(unsafe_code)] - use app_units::{Au, MAX_AU}; use context::LayoutContext; use display_list::{BlockFlowDisplayListBuilding, BorderPaintingMode}; diff --git a/components/layout/construct.rs b/components/layout/construct.rs index ba86416f935..48727bb3d1a 100644 --- a/components/layout/construct.rs +++ b/components/layout/construct.rs @@ -11,8 +11,6 @@ //! maybe it's an absolute or fixed position thing that hasn't found its containing block yet. //! Construction items bubble up the tree from children to parents until they find their homes. -#![deny(unsafe_code)] - use ServoArc; use block::BlockFlow; use context::{LayoutContext, with_thread_local_font_context}; diff --git a/components/layout/display_list/background.rs b/components/layout/display_list/background.rs index 7da2bff68d6..305b5e5be31 100644 --- a/components/layout/display_list/background.rs +++ b/components/layout/display_list/background.rs @@ -2,52 +2,21 @@ * 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, 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)] - -// FIXME(rust-lang/rust#26264): Remove GenericEndingShape, GenericGradientItem, -// GenericBackgroundSize and GenericBorderImageSideWidth. +// FIXME(rust-lang/rust#26264): Remove GenericBackgroundSize. use app_units::Au; -use display_list::ToLayout; -use euclid::{Point2D, Rect, SideOffsets2D, Size2D, Vector2D}; -use model::{self, MaybeAuto}; +use display_list::border; +use euclid::{Point2D, Rect, SideOffsets2D, Size2D}; +use model::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::computed_values::border_image_outset::T as BorderImageOutset; -use style::properties::ComputedValues; -use style::properties::style_structs::{self, Background}; -use style::values::Either; -use style::values::computed::{Angle, GradientItem, BackgroundSize}; -use style::values::computed::{BorderImageWidth, BorderImageSideWidth}; -use style::values::computed::{LengthOrNumber, LengthOrPercentage, LengthOrPercentageOrAuto}; -use style::values::computed::{NumberOrPercentage, Percentage, Position}; -use style::values::computed::image::{EndingShape, LineDirection}; +use style::properties::style_structs::Background; +use style::values::computed::{BackgroundSize, LengthOrPercentageOrAuto}; use style::values::generics::NonNegative; use style::values::generics::background::BackgroundSize as GenericBackgroundSize; -use style::values::generics::border::{BorderImageSideWidth as GenericBorderImageSideWidth}; -use style::values::generics::image::{Circle, Ellipse, ShapeExtent}; -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::{BorderRadius, BorderSide, BorderStyle, ColorF}; -use webrender_api::{ExtendMode, Gradient, GradientStop, LayoutSize, LayoutSideOffsets}; -use webrender_api::{NormalBorder, RadialGradient}; - -/// A helper data structure for gradients. -#[derive(Clone, Copy)] -struct StopRun { - start_offset: f32, - end_offset: f32, - start_index: usize, - stop_count: usize, -} +use webrender_api::BorderRadius; /// Placment information for both image and gradient backgrounds. #[derive(Clone, Copy, Debug)] @@ -70,19 +39,6 @@ pub struct BackgroundPlacement { pub fixed: bool, } -pub 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. @@ -162,7 +118,8 @@ fn compute_background_image_size( } } -pub fn compute_background_clip( +/// Compute a rounded clip rect for the background. +pub fn clip( bg_clip: BackgroundClip, absolute_bounds: Rect<Au>, border: SideOffsets2D<Au>, @@ -173,11 +130,11 @@ pub fn compute_background_clip( BackgroundClip::BorderBox => (absolute_bounds, border_radii), BackgroundClip::PaddingBox => ( absolute_bounds.inner_rect(border), - calculate_inner_border_radii(border_radii, border), + border::inner_radii(border_radii, border), ), BackgroundClip::ContentBox => ( absolute_bounds.inner_rect(border_padding), - calculate_inner_border_radii(border_radii, border_padding), + border::inner_radii(border_radii, border_padding), ), } } @@ -186,7 +143,7 @@ pub fn compute_background_clip( /// /// Photos have their resolution as intrinsic size while gradients have /// no intrinsic size. -pub fn compute_background_placement( +pub fn placement( bg: &Background, viewport_size: Size2D<Au>, absolute_bounds: Rect<Au>, @@ -204,7 +161,7 @@ pub fn compute_background_placement( let bg_repeat = get_cyclic(&bg.background_repeat.0, index); let bg_size = *get_cyclic(&bg.background_size.0, index); - let (clip_rect, clip_radii) = compute_background_clip( + let (clip_rect, clip_radii) = clip( bg_clip, absolute_bounds, border, @@ -380,468 +337,3 @@ fn tile_image_axis( }, } } - -/// Determines the radius of a circle if it was not explictly provided. -/// <https://drafts.csswg.org/css-images-3/#typedef-size> -fn convert_circle_size_keyword( - keyword: ShapeExtent, - size: &Size2D<Au>, - center: &Point2D<Au>, -) -> Size2D<Au> { - let radius = match keyword { - ShapeExtent::ClosestSide | ShapeExtent::Contain => { - let dist = get_distance_to_sides(size, center, ::std::cmp::min); - ::std::cmp::min(dist.width, dist.height) - }, - ShapeExtent::FarthestSide => { - let dist = get_distance_to_sides(size, center, ::std::cmp::max); - ::std::cmp::max(dist.width, dist.height) - }, - ShapeExtent::ClosestCorner => get_distance_to_corner(size, center, ::std::cmp::min), - ShapeExtent::FarthestCorner | ShapeExtent::Cover => { - get_distance_to_corner(size, center, ::std::cmp::max) - }, - }; - Size2D::new(radius, radius) -} - -/// Returns the radius for an ellipse with the same ratio as if it was matched to the sides. -fn get_ellipse_radius<F>(size: &Size2D<Au>, center: &Point2D<Au>, cmp: F) -> Size2D<Au> -where - F: Fn(Au, Au) -> Au, -{ - let dist = get_distance_to_sides(size, center, cmp); - Size2D::new( - dist.width.scale_by(::std::f32::consts::FRAC_1_SQRT_2 * 2.0), - dist.height - .scale_by(::std::f32::consts::FRAC_1_SQRT_2 * 2.0), - ) -} - -/// Determines the radius of an ellipse if it was not explictly provided. -/// <https://drafts.csswg.org/css-images-3/#typedef-size> -fn convert_ellipse_size_keyword( - keyword: ShapeExtent, - size: &Size2D<Au>, - center: &Point2D<Au>, -) -> Size2D<Au> { - match keyword { - ShapeExtent::ClosestSide | ShapeExtent::Contain => { - get_distance_to_sides(size, center, ::std::cmp::min) - }, - ShapeExtent::FarthestSide => get_distance_to_sides(size, center, ::std::cmp::max), - ShapeExtent::ClosestCorner => get_ellipse_radius(size, center, ::std::cmp::min), - ShapeExtent::FarthestCorner | ShapeExtent::Cover => { - get_ellipse_radius(size, center, ::std::cmp::max) - }, - } -} - -fn convert_gradient_stops( - style: &ComputedValues, - gradient_items: &[GradientItem], - total_length: Au, -) -> Vec<GradientStop> { - // Determine the position of each stop per CSS-IMAGES § 3.4. - - // Only keep the color stops, discard the color interpolation hints. - let mut stop_items = gradient_items - .iter() - .filter_map(|item| match *item { - GenericGradientItem::ColorStop(ref stop) => Some(*stop), - _ => None, - }).collect::<Vec<_>>(); - - assert!(stop_items.len() >= 2); - - // Run the algorithm from - // https://drafts.csswg.org/css-images-3/#color-stop-syntax - - // Step 1: - // If the first color stop does not have a position, set its position to 0%. - { - let first = stop_items.first_mut().unwrap(); - if first.position.is_none() { - first.position = Some(LengthOrPercentage::Percentage(Percentage(0.0))); - } - } - // If the last color stop does not have a position, set its position to 100%. - { - let last = stop_items.last_mut().unwrap(); - if last.position.is_none() { - last.position = Some(LengthOrPercentage::Percentage(Percentage(1.0))); - } - } - - // Step 2: Move any stops placed before earlier stops to the - // same position as the preceding stop. - let mut last_stop_position = stop_items.first().unwrap().position.unwrap(); - for stop in stop_items.iter_mut().skip(1) { - if let Some(pos) = stop.position { - if position_to_offset(last_stop_position, total_length) > - position_to_offset(pos, total_length) - { - stop.position = Some(last_stop_position); - } - last_stop_position = stop.position.unwrap(); - } - } - - // Step 3: Evenly space stops without position. - let mut stops = Vec::with_capacity(stop_items.len()); - let mut stop_run = None; - for (i, stop) in stop_items.iter().enumerate() { - let offset = match stop.position { - None => { - if stop_run.is_none() { - // Initialize a new stop run. - // `unwrap()` here should never fail because this is the beginning of - // a stop run, which is always bounded by a length or percentage. - let start_offset = - position_to_offset(stop_items[i - 1].position.unwrap(), total_length); - // `unwrap()` here should never fail because this is the end of - // a stop run, which is always bounded by a length or percentage. - let (end_index, end_stop) = stop_items[(i + 1)..] - .iter() - .enumerate() - .find(|&(_, ref stop)| stop.position.is_some()) - .unwrap(); - let end_offset = position_to_offset(end_stop.position.unwrap(), total_length); - stop_run = Some(StopRun { - start_offset, - end_offset, - start_index: i - 1, - stop_count: end_index, - }) - } - - let stop_run = stop_run.unwrap(); - let stop_run_length = stop_run.end_offset - stop_run.start_offset; - stop_run.start_offset + - stop_run_length * (i - stop_run.start_index) as f32 / - ((2 + stop_run.stop_count) as f32) - }, - Some(position) => { - stop_run = None; - position_to_offset(position, total_length) - }, - }; - assert!(offset.is_finite()); - stops.push(GradientStop { - offset: offset, - color: style.resolve_color(stop.color).to_layout(), - }) - } - stops -} - -fn as_gradient_extend_mode(repeating: bool) -> ExtendMode { - if repeating { - ExtendMode::Repeat - } else { - ExtendMode::Clamp - } -} - -pub fn convert_linear_gradient( - style: &ComputedValues, - size: Size2D<Au>, - stops: &[GradientItem], - direction: LineDirection, - repeating: bool, -) -> (Gradient, Vec<GradientStop>) { - let angle = match direction { - LineDirection::Angle(angle) => angle.radians(), - LineDirection::Horizontal(x) => match x { - X::Left => Angle::from_degrees(270.).radians(), - X::Right => Angle::from_degrees(90.).radians(), - }, - LineDirection::Vertical(y) => match y { - Y::Top => Angle::from_degrees(0.).radians(), - Y::Bottom => Angle::from_degrees(180.).radians(), - }, - LineDirection::Corner(horizontal, vertical) => { - // This the angle for one of the diagonals of the box. Our angle - // will either be this one, this one + PI, or one of the other - // two perpendicular angles. - let atan = (size.height.to_f32_px() / size.width.to_f32_px()).atan(); - match (horizontal, vertical) { - (X::Right, Y::Bottom) => ::std::f32::consts::PI - atan, - (X::Left, Y::Bottom) => ::std::f32::consts::PI + atan, - (X::Right, Y::Top) => atan, - (X::Left, Y::Top) => -atan, - } - }, - }; - - // Get correct gradient line length, based on: - // https://drafts.csswg.org/css-images-3/#linear-gradients - let dir = Point2D::new(angle.sin(), -angle.cos()); - - let line_length = - (dir.x * size.width.to_f32_px()).abs() + (dir.y * size.height.to_f32_px()).abs(); - - let inv_dir_length = 1.0 / (dir.x * dir.x + dir.y * dir.y).sqrt(); - - // This is the vector between the center and the ending point; i.e. half - // of the distance between the starting point and the ending point. - let delta = Vector2D::new( - Au::from_f32_px(dir.x * inv_dir_length * line_length / 2.0), - Au::from_f32_px(dir.y * inv_dir_length * line_length / 2.0), - ); - - // This is the length of the gradient line. - let length = Au::from_f32_px((delta.x.to_f32_px() * 2.0).hypot(delta.y.to_f32_px() * 2.0)); - - let stops = convert_gradient_stops(style, stops, length); - - let center = Point2D::new(size.width / 2, size.height / 2); - - ( - Gradient { - start_point: (center - delta).to_layout(), - end_point: (center + delta).to_layout(), - extend_mode: as_gradient_extend_mode(repeating), - }, - stops, - ) -} - -pub fn convert_radial_gradient( - style: &ComputedValues, - size: Size2D<Au>, - stops: &[GradientItem], - shape: EndingShape, - center: Position, - repeating: bool, -) -> (RadialGradient, Vec<GradientStop>) { - let center = Point2D::new( - center.horizontal.to_used_value(size.width), - center.vertical.to_used_value(size.height), - ); - let radius = match shape { - GenericEndingShape::Circle(Circle::Radius(length)) => { - let length = Au::from(length); - Size2D::new(length, length) - }, - GenericEndingShape::Circle(Circle::Extent(extent)) => { - convert_circle_size_keyword(extent, &size, ¢er) - }, - GenericEndingShape::Ellipse(Ellipse::Radii(x, y)) => { - Size2D::new(x.to_used_value(size.width), y.to_used_value(size.height)) - }, - GenericEndingShape::Ellipse(Ellipse::Extent(extent)) => { - convert_ellipse_size_keyword(extent, &size, ¢er) - }, - }; - - let stops = convert_gradient_stops(style, stops, radius.width); - - ( - RadialGradient { - center: center.to_layout(), - radius: radius.to_layout(), - extend_mode: as_gradient_extend_mode(repeating), - // FIXME(pyfisch): These values are calculated by WR. - start_offset: 0.0, - end_offset: 0.0, - }, - stops, - ) -} - -/// Returns the the distance to the nearest or farthest corner depending on the comperator. -fn get_distance_to_corner<F>(size: &Size2D<Au>, center: &Point2D<Au>, cmp: F) -> Au -where - F: Fn(Au, Au) -> Au, -{ - let dist = get_distance_to_sides(size, center, cmp); - Au::from_f32_px(dist.width.to_f32_px().hypot(dist.height.to_f32_px())) -} - -/// Returns the distance to the nearest or farthest sides depending on the comparator. -/// -/// The first return value is horizontal distance the second vertical distance. -fn get_distance_to_sides<F>(size: &Size2D<Au>, center: &Point2D<Au>, cmp: F) -> Size2D<Au> -where - F: Fn(Au, Au) -> Au, -{ - let top_side = center.y; - let right_side = size.width - center.x; - let bottom_side = size.height - center.y; - let left_side = center.x; - Size2D::new(cmp(left_side, right_side), cmp(top_side, bottom_side)) -} - -fn position_to_offset(position: LengthOrPercentage, total_length: Au) -> f32 { - if total_length == Au(0) { - return 0.0; - } - match position { - LengthOrPercentage::Length(l) => l.to_i32_au() as f32 / total_length.0 as f32, - LengthOrPercentage::Percentage(percentage) => percentage.0 as f32, - LengthOrPercentage::Calc(calc) => { - calc.to_used_value(Some(total_length)).unwrap().0 as f32 / total_length.0 as 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<Au>, - 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(), - do_aa: true, - } -} - -/// 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<Au>, -) -> 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 calculate_border_image_outset_side(outset: LengthOrNumber, border_width: Au) -> Au { - match outset { - Either::First(length) => length.into(), - Either::Second(factor) => border_width.scale_by(factor), - } -} - -pub fn calculate_border_image_outset( - outset: BorderImageOutset, - border: SideOffsets2D<Au>, -) -> SideOffsets2D<Au> { - SideOffsets2D::new( - calculate_border_image_outset_side(outset.0, border.top), - calculate_border_image_outset_side(outset.1, border.right), - calculate_border_image_outset_side(outset.2, border.bottom), - calculate_border_image_outset_side(outset.3, border.left), - ) -} - -fn calculate_border_image_width_side( - border_image_width: BorderImageSideWidth, - border_width: f32, - total_length: Au, -) -> f32 { - match border_image_width { - GenericBorderImageSideWidth::Length(v) => v.to_used_value(total_length).to_f32_px(), - GenericBorderImageSideWidth::Number(x) => border_width * x, - GenericBorderImageSideWidth::Auto => border_width, - } -} - -pub fn calculate_border_image_width( - width: &BorderImageWidth, - border: LayoutSideOffsets, - border_area: Size2D<Au>, -) -> LayoutSideOffsets { - LayoutSideOffsets::new( - calculate_border_image_width_side(width.0, border.top, border_area.height), - calculate_border_image_width_side(width.1, border.right, border_area.width), - calculate_border_image_width_side(width.2, border.bottom, border_area.height), - calculate_border_image_width_side(width.3, border.left, border_area.width), - ) -} diff --git a/components/layout/display_list/border.rs b/components/layout/display_list/border.rs new file mode 100644 index 00000000000..8b34356ca9b --- /dev/null +++ b/components/layout/display_list/border.rs @@ -0,0 +1,202 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * 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/. */ + +// FIXME(rust-lang/rust#26264): Remove GenericBorderImageSideWidth. + +use app_units::Au; +use display_list::ToLayout; +use euclid::{Rect, SideOffsets2D, Size2D}; +use style::computed_values::border_image_outset::T as BorderImageOutset; +use style::properties::style_structs::Border; +use style::values::Either; +use style::values::computed::{BorderCornerRadius, BorderImageWidth}; +use style::values::computed::{BorderImageSideWidth, LengthOrNumber}; +use style::values::computed::NumberOrPercentage; +use style::values::generics::border::{BorderImageSideWidth as GenericBorderImageSideWidth}; +use style::values::generics::rect::Rect as StyleRect; +use webrender_api::{BorderRadius, BorderSide, BorderStyle, ColorF}; +use webrender_api::{LayoutSize, LayoutSideOffsets, NormalBorder}; + +/// Computes a border radius size against the containing size. +/// +/// Note that percentages in `border-radius` are resolved against the relevant +/// box dimension instead of only against the width per [1]: +/// +/// > Percentages: Refer to corresponding dimension of the border box. +/// +/// [1]: https://drafts.csswg.org/css-backgrounds-3/#border-radius +fn corner_radius(radius: BorderCornerRadius, containing_size: Size2D<Au>) -> Size2D<Au> { + let w = radius.0.width().to_used_value(containing_size.width); + let h = radius.0.height().to_used_value(containing_size.height); + Size2D::new(w, h) +} + +fn scaled_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 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 { + scaled_radii(radii, min_factor) + } else { + radii + } +} + +/// Determine the four corner radii of a border. +/// +/// Radii may either be absolute or relative to the absolute bounds. +/// Each corner radius has a width and a height which may differ. +/// Lastly overlapping radii are shrank so they don't collide anymore. +pub fn radii(abs_bounds: Rect<Au>, border_style: &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. + + overlapping_radii( + abs_bounds.size.to_layout(), + BorderRadius { + top_left: corner_radius(border_style.border_top_left_radius, abs_bounds.size) + .to_layout(), + top_right: corner_radius(border_style.border_top_right_radius, abs_bounds.size) + .to_layout(), + bottom_right: corner_radius(border_style.border_bottom_right_radius, abs_bounds.size) + .to_layout(), + bottom_left: corner_radius(border_style.border_bottom_left_radius, abs_bounds.size) + .to_layout(), + }, + ) +} + +/// 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 inner_radii(mut radii: BorderRadius, offsets: SideOffsets2D<Au>) -> 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 +} + +/// Creates a four-sided border with square corners and uniform color and width. +pub fn simple(color: ColorF, style: BorderStyle) -> NormalBorder { + let side = BorderSide { color, style }; + NormalBorder { + left: side, + right: side, + top: side, + bottom: side, + radius: BorderRadius::zero(), + do_aa: true, + } +} + +fn side_image_outset(outset: LengthOrNumber, border_width: Au) -> Au { + match outset { + Either::First(length) => length.into(), + Either::Second(factor) => border_width.scale_by(factor), + } +} + +/// Compute the additional border-image area. +pub fn image_outset(outset: BorderImageOutset, border: SideOffsets2D<Au>) -> SideOffsets2D<Au> { + SideOffsets2D::new( + side_image_outset(outset.0, border.top), + side_image_outset(outset.1, border.right), + side_image_outset(outset.2, border.bottom), + side_image_outset(outset.3, border.left), + ) +} + +fn side_image_width( + border_image_width: BorderImageSideWidth, + border_width: f32, + total_length: Au, +) -> f32 { + match border_image_width { + GenericBorderImageSideWidth::Length(v) => v.to_used_value(total_length).to_f32_px(), + GenericBorderImageSideWidth::Number(x) => border_width * x, + GenericBorderImageSideWidth::Auto => border_width, + } +} + +pub fn image_width( + width: &BorderImageWidth, + border: LayoutSideOffsets, + border_area: Size2D<Au>, +) -> LayoutSideOffsets { + LayoutSideOffsets::new( + side_image_width(width.0, border.top, border_area.height), + side_image_width(width.1, border.right, border_area.width), + side_image_width(width.2, border.bottom, border_area.height), + side_image_width(width.3, border.left, border_area.width), + ) +} + +fn resolve_percentage(value: NumberOrPercentage, length: u32) -> u32 { + match value { + NumberOrPercentage::Percentage(p) => (p.0 * length as f32).round() as u32, + NumberOrPercentage::Number(n) => n.round() as u32, + } +} + +pub fn image_slice( + border_image_slice: &StyleRect<NumberOrPercentage>, + width: u32, + height: u32, +) -> SideOffsets2D<u32> { + SideOffsets2D::new( + resolve_percentage(border_image_slice.0, height), + resolve_percentage(border_image_slice.1, width), + resolve_percentage(border_image_slice.2, height), + resolve_percentage(border_image_slice.3, width), + ) +} diff --git a/components/layout/display_list/builder.rs b/components/layout/display_list/builder.rs index d8b78a4310b..8d2354dbb19 100644 --- a/components/layout/display_list/builder.rs +++ b/components/layout/display_list/builder.rs @@ -8,18 +8,14 @@ //! list building, as the actual painting does not happen here—only deciding *what* we're going to //! paint. -#![deny(unsafe_code)] - use app_units::{Au, AU_PER_PX}; use block::BlockFlow; use canvas_traits::canvas::{CanvasMsg, FromLayoutMsg}; use context::LayoutContext; use display_list::ToLayout; -use display_list::background::{build_border_radius, calculate_inner_border_radii}; -use display_list::background::{calculate_border_image_outset, calculate_border_image_width}; -use display_list::background::{compute_background_clip, compute_background_placement}; -use display_list::background::{convert_linear_gradient, convert_radial_gradient, get_cyclic}; -use display_list::background::{simple_normal_border, ResolvePercentage}; +use display_list::background::{self, get_cyclic}; +use display_list::border; +use display_list::gradient; use display_list::items::{BaseDisplayItem, BLUR_INFLATION_FACTOR, ClipScrollNode}; use display_list::items::{ClipScrollNodeIndex, ClipScrollNodeType, ClippingAndScrolling}; use display_list::items::{ClippingRegion, DisplayItem, DisplayItemMetadata, DisplayList}; @@ -810,7 +806,7 @@ fn build_border_radius_for_inner_rect( outer_rect: Rect<Au>, style: &ComputedValues, ) -> BorderRadius { - let radii = build_border_radius(outer_rect, style.get_border()); + let radii = border::radii(outer_rect, style.get_border()); if radii.is_zero() { return radii; } @@ -819,7 +815,7 @@ fn build_border_radius_for_inner_rect( // border width), we need to adjust to border radius so that we are smaller // rectangle with the same border curve. let border_widths = style.logical_border_width().to_physical(style.writing_mode); - calculate_inner_border_radii(radii, border_widths) + border::inner_radii(radii, border_widths) } impl FragmentDisplayListBuilding for Fragment { @@ -921,12 +917,12 @@ impl FragmentDisplayListBuilding for Fragment { // > with the bottom-most background image layer. let last_background_image_index = background.background_image.0.len() - 1; let color_clip = *get_cyclic(&background.background_clip.0, last_background_image_index); - let (bounds, border_radii) = compute_background_clip( + let (bounds, border_radii) = background::clip( color_clip, absolute_bounds, style.logical_border_width().to_physical(style.writing_mode), self.border_padding.to_physical(self.style.writing_mode), - build_border_radius(absolute_bounds, style.get_border()), + border::radii(absolute_bounds, style.get_border()), ); state.clipping_and_scrolling_scope(|state| { @@ -1045,14 +1041,14 @@ impl FragmentDisplayListBuilding for Fragment { Au::from_px(webrender_image.width as i32), Au::from_px(webrender_image.height as i32), ); - let placement = compute_background_placement( + let placement = 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(self.style.writing_mode), - build_border_radius(absolute_bounds, style.get_border()), + border::radii(absolute_bounds, style.get_border()), index, ); @@ -1156,14 +1152,14 @@ impl FragmentDisplayListBuilding for Fragment { style: &ComputedValues, index: usize, ) { - let placement = compute_background_placement( + let placement = 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(self.style.writing_mode), - build_border_radius(absolute_bounds, style.get_border()), + border::radii(absolute_bounds, style.get_border()), index, ); @@ -1184,7 +1180,7 @@ impl FragmentDisplayListBuilding for Fragment { let display_item = match gradient.kind { GradientKind::Linear(angle_or_corner) => { - let (gradient, stops) = convert_linear_gradient( + let (gradient, stops) = gradient::linear( style, placement.tile_size, &gradient.items[..], @@ -1199,7 +1195,7 @@ impl FragmentDisplayListBuilding for Fragment { DisplayItem::Gradient(CommonDisplayItem::with_data(base, item, stops)) }, GradientKind::Radial(shape, center, _angle) => { - let (gradient, stops) = convert_radial_gradient( + let (gradient, stops) = gradient::radial( style, placement.tile_size, &gradient.items[..], @@ -1245,7 +1241,7 @@ impl FragmentDisplayListBuilding for Fragment { style.get_cursor(CursorKind::Default), display_list_section, ); - let border_radius = build_border_radius(absolute_bounds, style.get_border()); + let border_radius = border::radii(absolute_bounds, style.get_border()); state.add_display_item(DisplayItem::BoxShadow(CommonDisplayItem::new( base, webrender_api::BoxShadowDisplayItem { @@ -1328,7 +1324,7 @@ impl FragmentDisplayListBuilding for Fragment { display_list_section, ); - let border_radius = build_border_radius(bounds, border_style_struct); + let border_radius = border::radii(bounds, border_style_struct); let border_widths = border.to_physical(style.writing_mode); if let Either::Second(ref image) = border_style_struct.border_image_source { @@ -1340,7 +1336,8 @@ impl FragmentDisplayListBuilding for Fragment { bounds, image, border_widths, - ).is_some() + ) + .is_some() { return; } @@ -1390,9 +1387,9 @@ impl FragmentDisplayListBuilding for Fragment { ) -> Option<()> { let border_style_struct = style.get_border(); let border_image_outset = - calculate_border_image_outset(border_style_struct.border_image_outset, border_width); + border::image_outset(border_style_struct.border_image_outset, border_width); let border_image_area = bounds.outer_rect(border_image_outset).size; - let border_image_width = calculate_border_image_width( + let border_image_width = border::image_width( &border_style_struct.border_image_width, border_width.to_layout(), border_image_area, @@ -1429,7 +1426,7 @@ impl FragmentDisplayListBuilding for Fragment { }, Image::Gradient(ref gradient) => match gradient.kind { GradientKind::Linear(angle_or_corner) => { - let (wr_gradient, linear_stops) = convert_linear_gradient( + let (wr_gradient, linear_stops) = gradient::linear( style, border_image_area, &gradient.items[..], @@ -1440,7 +1437,7 @@ impl FragmentDisplayListBuilding for Fragment { NinePatchBorderSource::Gradient(wr_gradient) }, GradientKind::Radial(shape, center, _angle) => { - let (wr_gradient, radial_stops) = convert_radial_gradient( + let (wr_gradient, radial_stops) = gradient::radial( style, border_image_area, &gradient.items[..], @@ -1459,12 +1456,7 @@ impl FragmentDisplayListBuilding for Fragment { source, width, height, - slice: SideOffsets2D::new( - border_image_slice.0.resolve(height), - border_image_slice.1.resolve(width), - border_image_slice.2.resolve(height), - border_image_slice.3.resolve(width), - ), + slice: border::image_slice(border_image_slice, width, height), fill: border_image_fill, repeat_horizontal: border_image_repeat.0.to_layout(), repeat_vertical: border_image_repeat.1.to_layout(), @@ -1526,10 +1518,7 @@ impl FragmentDisplayListBuilding for Fragment { base, webrender_api::BorderDisplayItem { widths: SideOffsets2D::new_all_same(width).to_layout(), - details: BorderDetails::Normal(simple_normal_border( - color, - outline_style.to_layout(), - )), + details: BorderDetails::Normal(border::simple(color, outline_style.to_layout())), }, Vec::new(), ))); @@ -1559,7 +1548,7 @@ impl FragmentDisplayListBuilding for Fragment { base, webrender_api::BorderDisplayItem { widths: SideOffsets2D::new_all_same(Au::from_px(1)).to_layout(), - details: BorderDetails::Normal(simple_normal_border( + details: BorderDetails::Normal(border::simple( ColorF::rgb(0, 0, 200), webrender_api::BorderStyle::Solid, )), @@ -1609,7 +1598,7 @@ impl FragmentDisplayListBuilding for Fragment { base, webrender_api::BorderDisplayItem { widths: SideOffsets2D::new_all_same(Au::from_px(1)).to_layout(), - details: BorderDetails::Normal(simple_normal_border( + details: BorderDetails::Normal(border::simple( ColorF::rgb(0, 0, 200), webrender_api::BorderStyle::Solid, )), @@ -2003,8 +1992,7 @@ impl FragmentDisplayListBuilding for Fragment { } }, SpecificFragmentInfo::Media(ref fragment_info) => { - if let Some((ref image_key, _, _)) = fragment_info.current_frame - { + if let Some((ref image_key, _, _)) = fragment_info.current_frame { let base = create_base_display_item(state); state.add_image_item( base, @@ -2018,7 +2006,7 @@ impl FragmentDisplayListBuilding for Fragment { }, ); } - } + }, SpecificFragmentInfo::Canvas(ref canvas_fragment_info) => { let image_key = match canvas_fragment_info.source { CanvasFragmentSource::WebGL(image_key) => image_key, @@ -2030,7 +2018,8 @@ impl FragmentDisplayListBuilding for Fragment { .send(CanvasMsg::FromLayout( FromLayoutMsg::SendData(sender), canvas_fragment_info.canvas_id.clone(), - )).unwrap(); + )) + .unwrap(); receiver.recv().unwrap().image_key }, None => return, @@ -2147,7 +2136,8 @@ impl FragmentDisplayListBuilding for Fragment { self.style.writing_mode, Au(0), metrics.ascent, - ).to_physical(self.style.writing_mode, container_size) + ) + .to_physical(self.style.writing_mode, container_size) .to_vector(); // Base item for all text/shadows @@ -3212,7 +3202,7 @@ impl BaseFlowDisplayListBuilding for BaseFlow { base, webrender_api::BorderDisplayItem { widths: SideOffsets2D::new_all_same(Au::from_px(2)).to_layout(), - details: BorderDetails::Normal(simple_normal_border( + details: BorderDetails::Normal(border::simple( color, webrender_api::BorderStyle::Solid, )), diff --git a/components/layout/display_list/gradient.rs b/components/layout/display_list/gradient.rs new file mode 100644 index 00000000000..bb8617968d2 --- /dev/null +++ b/components/layout/display_list/gradient.rs @@ -0,0 +1,327 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * 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/. */ + +// FIXME(rust-lang/rust#26264): Remove GenericEndingShape and GenericGradientItem. + +use app_units::Au; +use display_list::ToLayout; +use euclid::{Point2D, Size2D, Vector2D}; +use style::properties::ComputedValues; +use style::values::computed::{Angle, GradientItem, LengthOrPercentage, Percentage, Position}; +use style::values::computed::image::{EndingShape, LineDirection}; +use style::values::generics::image::{Circle, Ellipse, ShapeExtent}; +use style::values::generics::image::EndingShape as GenericEndingShape; +use style::values::generics::image::GradientItem as GenericGradientItem; +use style::values::specified::position::{X, Y}; +use webrender_api::{ExtendMode, Gradient, GradientBuilder, GradientStop, RadialGradient}; + +/// A helper data structure for gradients. +#[derive(Clone, Copy)] +struct StopRun { + start_offset: f32, + end_offset: f32, + start_index: usize, + stop_count: usize, +} + +/// Determines the radius of a circle if it was not explictly provided. +/// <https://drafts.csswg.org/css-images-3/#typedef-size> +fn circle_size_keyword( + keyword: ShapeExtent, + size: &Size2D<Au>, + center: &Point2D<Au>, +) -> Size2D<Au> { + let radius = match keyword { + ShapeExtent::ClosestSide | ShapeExtent::Contain => { + let dist = distance_to_sides(size, center, ::std::cmp::min); + ::std::cmp::min(dist.width, dist.height) + }, + ShapeExtent::FarthestSide => { + let dist = distance_to_sides(size, center, ::std::cmp::max); + ::std::cmp::max(dist.width, dist.height) + }, + ShapeExtent::ClosestCorner => distance_to_corner(size, center, ::std::cmp::min), + ShapeExtent::FarthestCorner | ShapeExtent::Cover => { + distance_to_corner(size, center, ::std::cmp::max) + }, + }; + Size2D::new(radius, radius) +} + +/// Returns the radius for an ellipse with the same ratio as if it was matched to the sides. +fn ellipse_radius<F>(size: &Size2D<Au>, center: &Point2D<Au>, cmp: F) -> Size2D<Au> +where + F: Fn(Au, Au) -> Au, +{ + let dist = distance_to_sides(size, center, cmp); + Size2D::new( + dist.width.scale_by(::std::f32::consts::FRAC_1_SQRT_2 * 2.0), + dist.height + .scale_by(::std::f32::consts::FRAC_1_SQRT_2 * 2.0), + ) +} + +/// Determines the radius of an ellipse if it was not explictly provided. +/// <https://drafts.csswg.org/css-images-3/#typedef-size> +fn ellipse_size_keyword( + keyword: ShapeExtent, + size: &Size2D<Au>, + center: &Point2D<Au>, +) -> Size2D<Au> { + match keyword { + ShapeExtent::ClosestSide | ShapeExtent::Contain => { + distance_to_sides(size, center, ::std::cmp::min) + }, + ShapeExtent::FarthestSide => distance_to_sides(size, center, ::std::cmp::max), + ShapeExtent::ClosestCorner => ellipse_radius(size, center, ::std::cmp::min), + ShapeExtent::FarthestCorner | ShapeExtent::Cover => { + ellipse_radius(size, center, ::std::cmp::max) + }, + } +} + +fn convert_gradient_stops( + style: &ComputedValues, + gradient_items: &[GradientItem], + total_length: Au, +) -> GradientBuilder { + // Determine the position of each stop per CSS-IMAGES § 3.4. + + // Only keep the color stops, discard the color interpolation hints. + let mut stop_items = gradient_items + .iter() + .filter_map(|item| match *item { + GenericGradientItem::ColorStop(ref stop) => Some(*stop), + _ => None, + }) + .collect::<Vec<_>>(); + + assert!(stop_items.len() >= 2); + + // Run the algorithm from + // https://drafts.csswg.org/css-images-3/#color-stop-syntax + + // Step 1: + // If the first color stop does not have a position, set its position to 0%. + { + let first = stop_items.first_mut().unwrap(); + if first.position.is_none() { + first.position = Some(LengthOrPercentage::Percentage(Percentage(0.0))); + } + } + // If the last color stop does not have a position, set its position to 100%. + { + let last = stop_items.last_mut().unwrap(); + if last.position.is_none() { + last.position = Some(LengthOrPercentage::Percentage(Percentage(1.0))); + } + } + + // Step 2: Move any stops placed before earlier stops to the + // same position as the preceding stop. + let mut last_stop_position = stop_items.first().unwrap().position.unwrap(); + for stop in stop_items.iter_mut().skip(1) { + if let Some(pos) = stop.position { + if position_to_offset(last_stop_position, total_length) > + position_to_offset(pos, total_length) + { + stop.position = Some(last_stop_position); + } + last_stop_position = stop.position.unwrap(); + } + } + + // Step 3: Evenly space stops without position. + let mut stops = GradientBuilder::new(); + let mut stop_run = None; + for (i, stop) in stop_items.iter().enumerate() { + let offset = match stop.position { + None => { + if stop_run.is_none() { + // Initialize a new stop run. + // `unwrap()` here should never fail because this is the beginning of + // a stop run, which is always bounded by a length or percentage. + let start_offset = + position_to_offset(stop_items[i - 1].position.unwrap(), total_length); + // `unwrap()` here should never fail because this is the end of + // a stop run, which is always bounded by a length or percentage. + let (end_index, end_stop) = stop_items[(i + 1)..] + .iter() + .enumerate() + .find(|&(_, ref stop)| stop.position.is_some()) + .unwrap(); + let end_offset = position_to_offset(end_stop.position.unwrap(), total_length); + stop_run = Some(StopRun { + start_offset, + end_offset, + start_index: i - 1, + stop_count: end_index, + }) + } + + let stop_run = stop_run.unwrap(); + let stop_run_length = stop_run.end_offset - stop_run.start_offset; + stop_run.start_offset + + stop_run_length * (i - stop_run.start_index) as f32 / + ((2 + stop_run.stop_count) as f32) + }, + Some(position) => { + stop_run = None; + position_to_offset(position, total_length) + }, + }; + assert!(offset.is_finite()); + stops.push(GradientStop { + offset: offset, + color: style.resolve_color(stop.color).to_layout(), + }) + } + stops +} + +fn extend_mode(repeating: bool) -> ExtendMode { + if repeating { + ExtendMode::Repeat + } else { + ExtendMode::Clamp + } +} +/// Returns the the distance to the nearest or farthest corner depending on the comperator. +fn distance_to_corner<F>(size: &Size2D<Au>, center: &Point2D<Au>, cmp: F) -> Au +where + F: Fn(Au, Au) -> Au, +{ + let dist = distance_to_sides(size, center, cmp); + Au::from_f32_px(dist.width.to_f32_px().hypot(dist.height.to_f32_px())) +} + +/// Returns the distance to the nearest or farthest sides depending on the comparator. +/// +/// The first return value is horizontal distance the second vertical distance. +fn distance_to_sides<F>(size: &Size2D<Au>, center: &Point2D<Au>, cmp: F) -> Size2D<Au> +where + F: Fn(Au, Au) -> Au, +{ + let top_side = center.y; + let right_side = size.width - center.x; + let bottom_side = size.height - center.y; + let left_side = center.x; + Size2D::new(cmp(left_side, right_side), cmp(top_side, bottom_side)) +} + +fn position_to_offset(position: LengthOrPercentage, total_length: Au) -> f32 { + if total_length == Au(0) { + return 0.0; + } + match position { + LengthOrPercentage::Length(l) => l.to_i32_au() as f32 / total_length.0 as f32, + LengthOrPercentage::Percentage(percentage) => percentage.0 as f32, + LengthOrPercentage::Calc(calc) => { + calc.to_used_value(Some(total_length)).unwrap().0 as f32 / total_length.0 as f32 + }, + } +} + +pub fn linear( + style: &ComputedValues, + size: Size2D<Au>, + stops: &[GradientItem], + direction: LineDirection, + repeating: bool, +) -> (Gradient, Vec<GradientStop>) { + let angle = match direction { + LineDirection::Angle(angle) => angle.radians(), + LineDirection::Horizontal(x) => match x { + X::Left => Angle::from_degrees(270.).radians(), + X::Right => Angle::from_degrees(90.).radians(), + }, + LineDirection::Vertical(y) => match y { + Y::Top => Angle::from_degrees(0.).radians(), + Y::Bottom => Angle::from_degrees(180.).radians(), + }, + LineDirection::Corner(horizontal, vertical) => { + // This the angle for one of the diagonals of the box. Our angle + // will either be this one, this one + PI, or one of the other + // two perpendicular angles. + let atan = (size.height.to_f32_px() / size.width.to_f32_px()).atan(); + match (horizontal, vertical) { + (X::Right, Y::Bottom) => ::std::f32::consts::PI - atan, + (X::Left, Y::Bottom) => ::std::f32::consts::PI + atan, + (X::Right, Y::Top) => atan, + (X::Left, Y::Top) => -atan, + } + }, + }; + + // Get correct gradient line length, based on: + // https://drafts.csswg.org/css-images-3/#linear-gradients + let dir = Point2D::new(angle.sin(), -angle.cos()); + + let line_length = + (dir.x * size.width.to_f32_px()).abs() + (dir.y * size.height.to_f32_px()).abs(); + + let inv_dir_length = 1.0 / (dir.x * dir.x + dir.y * dir.y).sqrt(); + + // This is the vector between the center and the ending point; i.e. half + // of the distance between the starting point and the ending point. + let delta = Vector2D::new( + Au::from_f32_px(dir.x * inv_dir_length * line_length / 2.0), + Au::from_f32_px(dir.y * inv_dir_length * line_length / 2.0), + ); + + // This is the length of the gradient line. + let length = Au::from_f32_px((delta.x.to_f32_px() * 2.0).hypot(delta.y.to_f32_px() * 2.0)); + + let mut builder = convert_gradient_stops(style, stops, length); + + let center = Point2D::new(size.width / 2, size.height / 2); + + ( + builder.gradient( + (center - delta).to_layout(), + (center + delta).to_layout(), + extend_mode(repeating), + ), + builder.stops().to_vec(), + ) +} + +pub fn radial( + style: &ComputedValues, + size: Size2D<Au>, + stops: &[GradientItem], + shape: EndingShape, + center: Position, + repeating: bool, +) -> (RadialGradient, Vec<GradientStop>) { + let center = Point2D::new( + center.horizontal.to_used_value(size.width), + center.vertical.to_used_value(size.height), + ); + let radius = match shape { + GenericEndingShape::Circle(Circle::Radius(length)) => { + let length = Au::from(length); + Size2D::new(length, length) + }, + GenericEndingShape::Circle(Circle::Extent(extent)) => { + circle_size_keyword(extent, &size, ¢er) + }, + GenericEndingShape::Ellipse(Ellipse::Radii(x, y)) => { + Size2D::new(x.to_used_value(size.width), y.to_used_value(size.height)) + }, + GenericEndingShape::Ellipse(Ellipse::Extent(extent)) => { + ellipse_size_keyword(extent, &size, ¢er) + }, + }; + + let mut builder = convert_gradient_stops(style, stops, radius.width); + ( + builder.radial_gradient( + center.to_layout(), + radius.to_layout(), + extend_mode(repeating), + ), + builder.stops().to_vec(), + ) +} diff --git a/components/layout/display_list/mod.rs b/components/layout/display_list/mod.rs index 602a36d400f..4ee6f1fde8e 100644 --- a/components/layout/display_list/mod.rs +++ b/components/layout/display_list/mod.rs @@ -15,7 +15,9 @@ pub use self::conversions::ToLayout; pub use self::webrender_helpers::WebRenderDisplayListConverter; mod background; +mod border; mod builder; mod conversions; +mod gradient; pub mod items; mod webrender_helpers; diff --git a/components/layout/display_list/webrender_helpers.rs b/components/layout/display_list/webrender_helpers.rs index cb386daa748..4c5fa9df8c2 100644 --- a/components/layout/display_list/webrender_helpers.rs +++ b/components/layout/display_list/webrender_helpers.rs @@ -11,7 +11,7 @@ use display_list::items::{ClipScrollNode, ClipScrollNodeIndex, ClipScrollNodeTyp use display_list::items::{DisplayItem, DisplayList, StackingContextType}; use msg::constellation_msg::PipelineId; use webrender_api::{self, ClipAndScrollInfo, ClipId, DisplayListBuilder, RasterSpace}; -use webrender_api::LayoutPoint; +use webrender_api::{LayoutPoint, SpecificDisplayItem}; pub trait WebRenderDisplayListConverter { fn convert_to_webrender(&self, pipeline_id: PipelineId) -> DisplayListBuilder; @@ -104,93 +104,30 @@ impl WebRenderDisplayItemConverter for DisplayItem { match *self { DisplayItem::Rectangle(ref item) => { - builder.push_rect(&self.prim_info(), item.item.color); + builder.push_item(SpecificDisplayItem::Rectangle(item.item), &self.prim_info()); }, DisplayItem::Text(ref item) => { - builder.push_text( - &self.prim_info(), - &item.data, - item.item.font_key, - item.item.color, - item.item.glyph_options, - ); + builder.push_item(SpecificDisplayItem::Text(item.item), &self.prim_info()); + builder.push_iter(item.data.iter()); }, DisplayItem::Image(ref item) => { - builder.push_image( - &self.prim_info(), - item.item.stretch_size, - item.item.tile_spacing, - item.item.image_rendering, - item.item.alpha_type, - item.item.image_key, - item.item.color, - ); + builder.push_item(SpecificDisplayItem::Image(item.item), &self.prim_info()); }, DisplayItem::Border(ref item) => { - if item.data.is_empty() { - builder.push_border(&self.prim_info(), item.item.widths, item.item.details); - } else { - let mut details = item.item.details.clone(); - match &mut details { - webrender_api::BorderDetails::NinePatch( - webrender_api::NinePatchBorder { - source: - webrender_api::NinePatchBorderSource::Gradient(ref mut gradient), - .. - }, - ) => { - *gradient = builder.create_gradient( - gradient.start_point, - gradient.end_point, - item.data.clone(), - gradient.extend_mode, - ); - }, - webrender_api::BorderDetails::NinePatch( - webrender_api::NinePatchBorder { - source: - webrender_api::NinePatchBorderSource::RadialGradient(gradient), - .. - }, - ) => { - *gradient = builder.create_radial_gradient( - gradient.center, - gradient.radius, - item.data.clone(), - gradient.extend_mode, - ) - }, - _ => unreachable!(), - } - builder.push_border(&self.prim_info(), item.item.widths, details); + if !item.data.is_empty() { + builder.push_stops(item.data.as_ref()); } + builder.push_item(SpecificDisplayItem::Border(item.item), &self.prim_info()); }, DisplayItem::Gradient(ref item) => { - let gradient = builder.create_gradient( - item.item.gradient.start_point, - item.item.gradient.end_point, - item.data.clone(), - item.item.gradient.extend_mode, - ); - builder.push_gradient( - &self.prim_info(), - gradient, - item.item.tile_size, - item.item.tile_spacing, - ); + builder.push_stops(item.data.as_ref()); + builder.push_item(SpecificDisplayItem::Gradient(item.item), &self.prim_info()); }, DisplayItem::RadialGradient(ref item) => { - let gradient = builder.create_radial_gradient( - item.item.gradient.center, - item.item.gradient.radius, - item.data.clone(), - item.item.gradient.extend_mode, - ); - builder.push_radial_gradient( + builder.push_stops(item.data.as_ref()); + builder.push_item( + SpecificDisplayItem::RadialGradient(item.item), &self.prim_info(), - gradient, - item.item.tile_size, - item.item.tile_spacing, ); }, DisplayItem::Line(ref item) => { @@ -204,16 +141,7 @@ impl WebRenderDisplayItemConverter for DisplayItem { ); }, DisplayItem::BoxShadow(ref item) => { - builder.push_box_shadow( - &self.prim_info(), - item.item.box_bounds, - item.item.offset, - item.item.color, - item.item.blur_radius, - item.item.spread_radius, - item.item.border_radius, - item.item.clip_mode, - ); + builder.push_item(SpecificDisplayItem::BoxShadow(item.item), &self.prim_info()); }, DisplayItem::PushTextShadow(ref item) => { builder.push_shadow(&self.prim_info(), item.shadow); diff --git a/components/layout/flex.rs b/components/layout/flex.rs index e1baa390382..1f40278e76c 100644 --- a/components/layout/flex.rs +++ b/components/layout/flex.rs @@ -4,8 +4,6 @@ //! Layout for elements with a CSS `display` property of `flex`. -#![deny(unsafe_code)] - use app_units::{Au, MAX_AU}; use block::{AbsoluteAssignBSizesTraversal, BlockFlow, MarginsMayCollapseFlag}; use context::LayoutContext; diff --git a/components/layout/fragment.rs b/components/layout/fragment.rs index 486a344989b..3a57ce71e7d 100644 --- a/components/layout/fragment.rs +++ b/components/layout/fragment.rs @@ -4,8 +4,6 @@ //! The `Fragment` type, which represents the leaves of the layout tree. -#![deny(unsafe_code)] - use ServoArc; use app_units::Au; use canvas_traits::canvas::{CanvasMsg, CanvasId}; diff --git a/components/layout/inline.rs b/components/layout/inline.rs index 7a66ad84bac..51d6013a2e9 100644 --- a/components/layout/inline.rs +++ b/components/layout/inline.rs @@ -2,8 +2,6 @@ * 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/. */ -#![deny(unsafe_code)] - use ServoArc; use app_units::{Au, MIN_AU}; use block::AbsoluteAssignBSizesTraversal; diff --git a/components/layout/list_item.rs b/components/layout/list_item.rs index 72e87578635..2b05a5f7dc4 100644 --- a/components/layout/list_item.rs +++ b/components/layout/list_item.rs @@ -5,8 +5,6 @@ //! Layout for elements with a CSS `display` property of `list-item`. These elements consist of a //! block and an extra inline fragment for the marker. -#![deny(unsafe_code)] - use app_units::Au; use block::BlockFlow; use context::{LayoutContext, with_thread_local_font_context}; diff --git a/components/layout/model.rs b/components/layout/model.rs index 50959398f27..b936fb2d856 100644 --- a/components/layout/model.rs +++ b/components/layout/model.rs @@ -4,17 +4,15 @@ //! Borders, padding, and margins. -#![deny(unsafe_code)] - use app_units::Au; -use euclid::{SideOffsets2D, Size2D}; +use euclid::SideOffsets2D; use fragment::Fragment; use std::cmp::{max, min}; use std::fmt; use style::logical_geometry::{LogicalMargin, WritingMode}; use style::properties::ComputedValues; -use style::values::computed::{BorderCornerRadius, LengthOrPercentageOrAuto}; -use style::values::computed::{LengthOrPercentage, LengthOrPercentageOrNone}; +use style::values::computed::{LengthOrPercentageOrAuto, LengthOrPercentage}; +use style::values::computed::LengthOrPercentageOrNone; /// A collapsible margin. See CSS 2.1 § 8.3.1. #[derive(Clone, Copy, Debug)] @@ -515,23 +513,6 @@ pub fn style_length( } } -/// Computes a border radius size against the containing size. -/// -/// Note that percentages in `border-radius` are resolved against the relevant -/// box dimension instead of only against the width per [1]: -/// -/// > Percentages: Refer to corresponding dimension of the border box. -/// -/// [1]: https://drafts.csswg.org/css-backgrounds-3/#border-radius -pub fn specified_border_radius( - radius: BorderCornerRadius, - containing_size: Size2D<Au>, -) -> Size2D<Au> { - let w = radius.0.width().to_used_value(containing_size.width); - let h = radius.0.height().to_used_value(containing_size.height); - Size2D::new(w, h) -} - #[inline] pub fn padding_from_style( style: &ComputedValues, diff --git a/components/layout/multicol.rs b/components/layout/multicol.rs index 082fa1a1367..e689120424f 100644 --- a/components/layout/multicol.rs +++ b/components/layout/multicol.rs @@ -4,8 +4,6 @@ //! CSS Multi-column layout http://dev.w3.org/csswg/css-multicol/ -#![deny(unsafe_code)] - use ServoArc; use app_units::Au; use block::BlockFlow; diff --git a/components/layout/table.rs b/components/layout/table.rs index cb80850e91e..b6cf3981cd6 100644 --- a/components/layout/table.rs +++ b/components/layout/table.rs @@ -4,8 +4,6 @@ //! CSS table formatting contexts. -#![deny(unsafe_code)] - use app_units::Au; use block::{BlockFlow, CandidateBSizeIterator, ISizeAndMarginsComputer}; use block::{ISizeConstraintInput, ISizeConstraintSolution}; diff --git a/components/layout/table_caption.rs b/components/layout/table_caption.rs index 51a6078ec31..df93326d5ce 100644 --- a/components/layout/table_caption.rs +++ b/components/layout/table_caption.rs @@ -4,8 +4,6 @@ //! CSS table formatting contexts. -#![deny(unsafe_code)] - use app_units::Au; use block::BlockFlow; use context::LayoutContext; diff --git a/components/layout/table_cell.rs b/components/layout/table_cell.rs index 1af32166a5f..5ce5cf7a3f9 100644 --- a/components/layout/table_cell.rs +++ b/components/layout/table_cell.rs @@ -4,8 +4,6 @@ //! CSS table formatting contexts. -#![deny(unsafe_code)] - use app_units::Au; use block::{BlockFlow, ISizeAndMarginsComputer, MarginsMayCollapseFlag}; use context::LayoutContext; diff --git a/components/layout/table_colgroup.rs b/components/layout/table_colgroup.rs index 7a3b54a2ed0..6698dc4228c 100644 --- a/components/layout/table_colgroup.rs +++ b/components/layout/table_colgroup.rs @@ -4,8 +4,6 @@ //! CSS table formatting contexts. -#![deny(unsafe_code)] - use app_units::Au; use context::LayoutContext; use display_list::{DisplayListBuildState, StackingContextCollectionState}; diff --git a/components/layout/table_row.rs b/components/layout/table_row.rs index 9d5c3a15618..236aeb42110 100644 --- a/components/layout/table_row.rs +++ b/components/layout/table_row.rs @@ -4,8 +4,6 @@ //! CSS table formatting contexts. -#![deny(unsafe_code)] - use app_units::Au; use block::{BlockFlow, ISizeAndMarginsComputer}; use context::LayoutContext; diff --git a/components/layout/table_rowgroup.rs b/components/layout/table_rowgroup.rs index 3d77aca9382..752ad573a12 100644 --- a/components/layout/table_rowgroup.rs +++ b/components/layout/table_rowgroup.rs @@ -4,8 +4,6 @@ //! CSS table formatting contexts. -#![deny(unsafe_code)] - use app_units::Au; use block::{BlockFlow, ISizeAndMarginsComputer}; use context::LayoutContext; diff --git a/components/layout/table_wrapper.rs b/components/layout/table_wrapper.rs index 65684f8e165..b7f84908ec2 100644 --- a/components/layout/table_wrapper.rs +++ b/components/layout/table_wrapper.rs @@ -11,8 +11,6 @@ //! //! Hereafter this document is referred to as INTRINSIC. -#![deny(unsafe_code)] - use app_units::Au; use block::{AbsoluteNonReplaced, BlockFlow, FloatNonReplaced, ISizeAndMarginsComputer, ISizeConstraintInput}; use block::{ISizeConstraintSolution, MarginsMayCollapseFlag}; diff --git a/components/layout/text.rs b/components/layout/text.rs index c8321e9a03b..ab8de4d7ab2 100644 --- a/components/layout/text.rs +++ b/components/layout/text.rs @@ -4,8 +4,6 @@ //! Text layout. -#![deny(unsafe_code)] - use app_units::Au; use context::LayoutFontContext; use fragment::{Fragment, ScannedTextFlags}; |