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