aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorbors-servo <lbergstrom+bors@mozilla.com>2018-10-23 05:29:06 -0400
committerGitHub <noreply@github.com>2018-10-23 05:29:06 -0400
commit5422ca4a9b681cb07150ab9a1f01afa7c9f1b049 (patch)
tree173ae8db0829ac258fcf835fad3038ea984ba6a0
parent2304f02123038a2e729bc90a2e2db9a795f30158 (diff)
parentd9b1950d74e6a3c17de0579d47a8cd7987a29d9e (diff)
downloadservo-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 -->
-rw-r--r--components/layout/block.rs2
-rw-r--r--components/layout/construct.rs2
-rw-r--r--components/layout/display_list/background.rs534
-rw-r--r--components/layout/display_list/border.rs202
-rw-r--r--components/layout/display_list/builder.rs74
-rw-r--r--components/layout/display_list/gradient.rs327
-rw-r--r--components/layout/display_list/mod.rs2
-rw-r--r--components/layout/display_list/webrender_helpers.rs100
-rw-r--r--components/layout/flex.rs2
-rw-r--r--components/layout/fragment.rs2
-rw-r--r--components/layout/inline.rs2
-rw-r--r--components/layout/list_item.rs2
-rw-r--r--components/layout/model.rs25
-rw-r--r--components/layout/multicol.rs2
-rw-r--r--components/layout/table.rs2
-rw-r--r--components/layout/table_caption.rs2
-rw-r--r--components/layout/table_cell.rs2
-rw-r--r--components/layout/table_colgroup.rs2
-rw-r--r--components/layout/table_row.rs2
-rw-r--r--components/layout/table_rowgroup.rs2
-rw-r--r--components/layout/table_wrapper.rs2
-rw-r--r--components/layout/text.rs2
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, &center)
- },
- 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, &center)
- },
- };
-
- 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, &center)
+ },
+ 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, &center)
+ },
+ };
+
+ 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};