Auto merge of #20031 - pyfisch:transform, r=mbrubeck

Reorganise display list builder

Description in the individual commits.

<!-- 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/20031)
<!-- Reviewable:end -->
This commit is contained in:
bors-servo 2018-02-24 10:06:16 -05:00 committed by GitHub
commit 6a2feb3c1b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 466 additions and 495 deletions

View file

@ -14,27 +14,22 @@
//! They are therefore not exactly analogous to constructs like Skia pictures, which consist of //! They are therefore not exactly analogous to constructs like Skia pictures, which consist of
//! low-level drawing primitives. //! low-level drawing primitives.
use euclid::{Transform3D, Vector2D, TypedRect, SideOffsets2D}; use euclid::{Vector2D, TypedRect, SideOffsets2D};
use euclid::num::{One, Zero}; use euclid::num::{One, Zero};
use gfx_traits::{self, StackingContextId}; use gfx_traits::{self, StackingContextId};
use gfx_traits::print_tree::PrintTree; use gfx_traits::print_tree::PrintTree;
use ipc_channel::ipc::IpcSharedMemory;
use msg::constellation_msg::PipelineId; use msg::constellation_msg::PipelineId;
use net_traits::image::base::{Image, PixelFormat}; use net_traits::image::base::{Image, PixelFormat};
use range::Range;
use servo_geometry::MaxRect; use servo_geometry::MaxRect;
use std::cmp::Ordering; use std::cmp::Ordering;
use std::collections::HashMap; use std::collections::HashMap;
use std::f32; use std::f32;
use std::fmt; use std::fmt;
use std::sync::Arc;
use text::TextRun;
use text::glyph::ByteIndex;
use webrender_api::{BorderRadius, BorderWidths, BoxShadowClipMode, ClipMode, ColorF}; use webrender_api::{BorderRadius, BorderWidths, BoxShadowClipMode, ClipMode, ColorF};
use webrender_api::{ComplexClipRegion, ExtendMode, ExternalScrollId, FilterOp, FontInstanceKey}; use webrender_api::{ComplexClipRegion, ExtendMode, ExternalScrollId, FilterOp, FontInstanceKey};
use webrender_api::{GlyphInstance, GradientStop, ImageBorder, ImageKey, ImageRendering}; use webrender_api::{GlyphInstance, GradientStop, ImageBorder, ImageKey, ImageRendering};
use webrender_api::{LayoutPoint, LayoutRect, LayoutSize, LayoutVector2D, LineStyle, LocalClip}; use webrender_api::{LayoutPoint, LayoutRect, LayoutSize, LayoutTransform, LayoutVector2D, LineStyle};
use webrender_api::{MixBlendMode, NormalBorder, ScrollPolicy, ScrollSensitivity}; use webrender_api::{LocalClip, MixBlendMode, NormalBorder, ScrollPolicy, ScrollSensitivity};
use webrender_api::{StickyOffsetBounds, TransformStyle}; use webrender_api::{StickyOffsetBounds, TransformStyle};
pub use style::dom::OpaqueNode; pub use style::dom::OpaqueNode;
@ -104,25 +99,6 @@ impl DisplayList {
} }
} }
// Returns the text index within a node for the point of interest.
pub fn text_index(&self, node: OpaqueNode, point_in_item: LayoutPoint) -> Option<usize> {
for item in &self.list {
match item {
&DisplayItem::Text(ref text) => {
let base = item.base();
if base.metadata.node == node {
let point = point_in_item + item.base().bounds.origin.to_vector();
let offset = point - text.baseline_origin;
return Some(text.text_run.range_index_of_advance(&text.range, offset.x));
}
},
_ => {},
}
}
None
}
pub fn print(&self) { pub fn print(&self) {
let mut print_tree = PrintTree::new("Display List".to_owned()); let mut print_tree = PrintTree::new("Display List".to_owned());
self.print_with_tree(&mut print_tree); self.print_with_tree(&mut print_tree);
@ -209,13 +185,13 @@ pub struct StackingContext {
pub mix_blend_mode: MixBlendMode, pub mix_blend_mode: MixBlendMode,
/// A transform to be applied to this stacking context. /// A transform to be applied to this stacking context.
pub transform: Option<Transform3D<f32>>, pub transform: Option<LayoutTransform>,
/// The transform style of this stacking context. /// The transform style of this stacking context.
pub transform_style: TransformStyle, pub transform_style: TransformStyle,
/// The perspective matrix to be applied to children. /// The perspective matrix to be applied to children.
pub perspective: Option<Transform3D<f32>>, pub perspective: Option<LayoutTransform>,
/// The scroll policy of this layer. /// The scroll policy of this layer.
pub scroll_policy: ScrollPolicy, pub scroll_policy: ScrollPolicy,
@ -234,9 +210,9 @@ impl StackingContext {
z_index: i32, z_index: i32,
filters: Vec<FilterOp>, filters: Vec<FilterOp>,
mix_blend_mode: MixBlendMode, mix_blend_mode: MixBlendMode,
transform: Option<Transform3D<f32>>, transform: Option<LayoutTransform>,
transform_style: TransformStyle, transform_style: TransformStyle,
perspective: Option<Transform3D<f32>>, perspective: Option<LayoutTransform>,
scroll_policy: ScrollPolicy, scroll_policy: ScrollPolicy,
parent_clipping_and_scrolling: ClippingAndScrolling) parent_clipping_and_scrolling: ClippingAndScrolling)
-> StackingContext { -> StackingContext {
@ -651,16 +627,6 @@ pub struct SolidColorDisplayItem {
pub struct TextDisplayItem { pub struct TextDisplayItem {
/// Fields common to all display items. /// Fields common to all display items.
pub base: BaseDisplayItem, pub base: BaseDisplayItem,
/// The text run.
#[ignore_malloc_size_of = "Because it is non-owning"]
pub text_run: Arc<TextRun>,
/// The range of text within the text run.
pub range: Range<ByteIndex>,
/// The position of the start of the baseline of this text.
pub baseline_origin: LayoutPoint,
/// A collection of (non-whitespace) glyphs to be displayed. /// A collection of (non-whitespace) glyphs to be displayed.
pub glyphs: Vec<GlyphInstance>, pub glyphs: Vec<GlyphInstance>,
/// Reference to the font to be used. /// Reference to the font to be used.
@ -683,9 +649,6 @@ pub struct ImageDisplayItem {
pub webrender_image: WebRenderImageInfo, pub webrender_image: WebRenderImageInfo,
#[ignore_malloc_size_of = "Because it is non-owning"]
pub image_data: Option<Arc<IpcSharedMemory>>,
/// The dimensions to which the image display item should be stretched. If this is smaller than /// The dimensions to which the image display item should be stretched. If this is smaller than
/// the bounds of this display item, then the image will be repeated in the appropriate /// the bounds of this display item, then the image will be repeated in the appropriate
/// direction to tile the entire bounds. /// direction to tile the entire bounds.
@ -1028,7 +991,7 @@ pub trait SimpleMatrixDetection {
fn is_identity_or_simple_translation(&self) -> bool; fn is_identity_or_simple_translation(&self) -> bool;
} }
impl SimpleMatrixDetection for Transform3D<f32> { impl SimpleMatrixDetection for LayoutTransform {
#[inline] #[inline]
fn is_identity_or_simple_translation(&self) -> bool { fn is_identity_or_simple_translation(&self) -> bool {
let (_0, _1) = (Zero::zero(), One::one()); let (_0, _1) = (Zero::zero(), One::one());

View file

@ -333,10 +333,10 @@ impl<'a> TextRun {
} }
/// Returns the index in the range of the first glyph advancing over given advance /// Returns the index in the range of the first glyph advancing over given advance
pub fn range_index_of_advance(&self, range: &Range<ByteIndex>, advance: f32) -> usize { pub fn range_index_of_advance(&self, range: &Range<ByteIndex>, advance: Au) -> usize {
// TODO(Issue #199): alter advance direction for RTL // TODO(Issue #199): alter advance direction for RTL
// TODO(Issue #98): using inter-char and inter-word spacing settings when measuring text // TODO(Issue #98): using inter-char and inter-word spacing settings when measuring text
let mut remaining = Au::from_f32_px(advance); let mut remaining = advance;
self.natural_word_slices_in_range(range) self.natural_word_slices_in_range(range)
.map(|slice| { .map(|slice| {
let (slice_index, slice_advance) = let (slice_index, slice_advance) =

View file

@ -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,
@ -322,8 +448,7 @@ fn convert_gradient_stops(gradient_items: &[GradientItem], total_length: Au) ->
} }
// Step 3: Evenly space stops without position. // Step 3: Evenly space stops without position.
// Note: Remove the + 2 if fix_gradient_stops is changed. let mut stops = Vec::with_capacity(stop_items.len());
let mut stops = Vec::with_capacity(stop_items.len() + 2);
let mut stop_run = None; let mut stop_run = None;
for (i, stop) in stop_items.iter().enumerate() { for (i, stop) in stop_items.iter().enumerate() {
let offset = match stop.position { let offset = match stop.position {
@ -427,14 +552,7 @@ pub fn convert_linear_gradient(
// This is the length of the gradient line. // 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 length = Au::from_f32_px((delta.x.to_f32_px() * 2.0).hypot(delta.y.to_f32_px() * 2.0));
let mut stops = convert_gradient_stops(stops, length); let stops = convert_gradient_stops(stops, length);
// Only clamped gradients need to be fixed because in repeating gradients
// there is no "first" or "last" stop because they repeat infinitly in
// both directions, so the rendering is always correct.
if !repeating {
fix_gradient_stops(&mut stops);
}
let center = Point2D::new(size.width / 2, size.height / 2); let center = Point2D::new(size.width / 2, size.height / 2);
@ -473,12 +591,7 @@ pub fn convert_radial_gradient(
}, },
}; };
let mut stops = convert_gradient_stops(stops, radius.width); let stops = convert_gradient_stops(stops, radius.width);
// Repeating gradients have no last stops that can be ignored. So
// fixup is not necessary but may actually break the gradient.
if !repeating {
fix_gradient_stops(&mut stops);
}
display_list::RadialGradient { display_list::RadialGradient {
center: center.to_layout(), center: center.to_layout(),
@ -488,38 +601,6 @@ pub fn convert_radial_gradient(
} }
} }
#[inline]
/// Duplicate the first and last stops if necessary.
///
/// Explanation by pyfisch:
/// If the last stop is at the same position as the previous stop the
/// last color is ignored by webrender. This differs from the spec
/// (I think so). The implementations of Chrome and Firefox seem
/// to have the same problem but work fine if the position of the last
/// stop is smaller than 100%. (Otherwise they ignore the last stop.)
///
/// Similarly the first stop is duplicated if it is not placed
/// at the start of the virtual gradient ray.
fn fix_gradient_stops(stops: &mut Vec<GradientStop>) {
if stops.first().unwrap().offset > 0.0 {
let color = stops.first().unwrap().color;
stops.insert(
0,
GradientStop {
offset: 0.0,
color: color,
},
)
}
if stops.last().unwrap().offset < 1.0 {
let color = stops.last().unwrap().color;
stops.push(GradientStop {
offset: 1.0,
color: color,
})
}
}
/// Returns the the distance to the nearest or farthest corner depending on the comperator. /// 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 fn get_distance_to_corner<F>(size: &Size2D<Au>, center: &Point2D<Au>, cmp: F) -> Au
where where
@ -555,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
}
}

View file

@ -15,9 +15,11 @@ 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 euclid::{Point2D, Rect, SideOffsets2D, Size2D, Transform3D, TypedSize2D, Vector2D, rect}; use display_list::background::{get_cyclic, simple_normal_border};
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};
use flow_ref::FlowRef; use flow_ref::FlowRef;
@ -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,23 +74,10 @@ 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, LayoutVector2D, LineStyle, LocalClip}; use webrender_api::{ImageRendering, LayoutRect, LayoutSize, LayoutTransform, LayoutVector2D};
use webrender_api::{NinePatchDescriptor, NormalBorder, ScrollPolicy, ScrollSensitivity}; use webrender_api::{LineStyle, LocalClip, NormalBorder, ScrollPolicy};
use webrender_api::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,
@ -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,
@ -332,6 +314,9 @@ pub struct DisplayListBuildState<'a> {
/// Vector containing iframe sizes, used to inform the constellation about /// Vector containing iframe sizes, used to inform the constellation about
/// new iframe sizes /// new iframe sizes
pub iframe_sizes: Vec<(BrowsingContextId, TypedSize2D<f32, CSSPixel>)>, pub iframe_sizes: Vec<(BrowsingContextId, TypedSize2D<f32, CSSPixel>)>,
/// Stores text runs to answer text queries used to place a cursor inside text.
pub indexable_text: IndexableText,
} }
impl<'a> DisplayListBuildState<'a> { impl<'a> DisplayListBuildState<'a> {
@ -350,6 +335,7 @@ impl<'a> DisplayListBuildState<'a> {
current_stacking_context_id: StackingContextId::root(), current_stacking_context_id: StackingContextId::root(),
current_clipping_and_scrolling: root_clip_indices, current_clipping_and_scrolling: root_clip_indices,
iframe_sizes: Vec::new(), iframe_sizes: Vec::new(),
indexable_text: IndexableText::default(),
} }
} }
@ -543,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(
@ -728,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(
@ -822,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,
@ -975,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);
@ -1095,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,
@ -1187,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(
@ -1203,7 +937,6 @@ impl FragmentDisplayListBuilding for Fragment {
state.add_display_item(DisplayItem::Image(Box::new(ImageDisplayItem { state.add_display_item(DisplayItem::Image(Box::new(ImageDisplayItem {
base: base, base: base,
webrender_image: webrender_image, webrender_image: webrender_image,
image_data: None,
stretch_size: placement.tile_size.to_layout(), stretch_size: placement.tile_size.to_layout(),
tile_spacing: placement.tile_spacing.to_layout(), tile_spacing: placement.tile_spacing.to_layout(),
image_rendering: style.get_inheritedbox().image_rendering.to_layout(), image_rendering: style.get_inheritedbox().image_rendering.to_layout(),
@ -1280,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,
@ -2000,7 +1740,6 @@ impl FragmentDisplayListBuilding for Fragment {
state.add_display_item(DisplayItem::Image(Box::new(ImageDisplayItem { state.add_display_item(DisplayItem::Image(Box::new(ImageDisplayItem {
base: base, base: base,
webrender_image: WebRenderImageInfo::from_image(image), webrender_image: WebRenderImageInfo::from_image(image),
image_data: Some(Arc::new(image.bytes.clone())),
stretch_size: stacking_relative_content_box.size.to_layout(), stretch_size: stacking_relative_content_box.size.to_layout(),
tile_spacing: LayoutSize::zero(), tile_spacing: LayoutSize::zero(),
image_rendering: self.style.get_inheritedbox().image_rendering.to_layout(), image_rendering: self.style.get_inheritedbox().image_rendering.to_layout(),
@ -2041,7 +1780,6 @@ impl FragmentDisplayListBuilding for Fragment {
format: format, format: format,
key: Some(image_key), key: Some(image_key),
}, },
image_data: None,
stretch_size: stacking_relative_content_box.size.to_layout(), stretch_size: stacking_relative_content_box.size.to_layout(),
tile_spacing: LayoutSize::zero(), tile_spacing: LayoutSize::zero(),
image_rendering: ImageRendering::Auto, image_rendering: ImageRendering::Auto,
@ -2178,7 +1916,7 @@ impl FragmentDisplayListBuilding for Fragment {
// Create display items for text decorations. // Create display items for text decorations.
let text_decorations = self.style().get_inheritedtext().text_decorations_in_effect; let text_decorations = self.style().get_inheritedtext().text_decorations_in_effect;
let stacking_relative_content_box = LogicalRect::from_physical( let logical_stacking_relative_content_box = LogicalRect::from_physical(
self.style.writing_mode, self.style.writing_mode,
*stacking_relative_content_box, *stacking_relative_content_box,
container_size, container_size,
@ -2186,9 +1924,10 @@ impl FragmentDisplayListBuilding for Fragment {
// Underline // Underline
if text_decorations.underline { if text_decorations.underline {
let mut stacking_relative_box = stacking_relative_content_box; let mut stacking_relative_box = logical_stacking_relative_content_box;
stacking_relative_box.start.b = stacking_relative_box.start.b = logical_stacking_relative_content_box.start.b +
stacking_relative_content_box.start.b + metrics.ascent - metrics.underline_offset; metrics.ascent -
metrics.underline_offset;
stacking_relative_box.size.block = metrics.underline_size; stacking_relative_box.size.block = metrics.underline_size;
self.build_display_list_for_text_decoration( self.build_display_list_for_text_decoration(
state, state,
@ -2200,7 +1939,7 @@ impl FragmentDisplayListBuilding for Fragment {
// Overline // Overline
if text_decorations.overline { if text_decorations.overline {
let mut stacking_relative_box = stacking_relative_content_box; let mut stacking_relative_box = logical_stacking_relative_content_box;
stacking_relative_box.size.block = metrics.underline_size; stacking_relative_box.size.block = metrics.underline_size;
self.build_display_list_for_text_decoration( self.build_display_list_for_text_decoration(
state, state,
@ -2217,11 +1956,16 @@ impl FragmentDisplayListBuilding for Fragment {
baseline_origin, baseline_origin,
); );
if !glyphs.is_empty() { if !glyphs.is_empty() {
state.add_display_item(DisplayItem::Text(Box::new(TextDisplayItem { let indexable_text = IndexableTextItem {
base: base.clone(), origin: stacking_relative_content_box.origin,
text_run: text_fragment.run.clone(), text_run: text_fragment.run.clone(),
range: text_fragment.range, range: text_fragment.range,
baseline_origin: baseline_origin.to_layout(), baseline_origin,
};
state.indexable_text.insert(self.node, indexable_text);
state.add_display_item(DisplayItem::Text(Box::new(TextDisplayItem {
base: base.clone(),
glyphs: glyphs, glyphs: glyphs,
font_key: text_fragment.run.font_key, font_key: text_fragment.run.font_key,
text_color: text_color.to_layout(), text_color: text_color.to_layout(),
@ -2233,7 +1977,7 @@ impl FragmentDisplayListBuilding for Fragment {
// Line-Through // Line-Through
if text_decorations.line_through { if text_decorations.line_through {
let mut stacking_relative_box = stacking_relative_content_box; let mut stacking_relative_box = logical_stacking_relative_content_box;
stacking_relative_box.start.b = stacking_relative_box.start.b =
stacking_relative_box.start.b + metrics.ascent - metrics.strikeout_offset; stacking_relative_box.start.b + metrics.ascent - metrics.strikeout_offset;
stacking_relative_box.size.block = metrics.strikeout_size; stacking_relative_box.size.block = metrics.strikeout_size;
@ -2464,7 +2208,7 @@ impl BlockFlowDisplayListBuilding for BlockFlow {
let perspective = self.fragment let perspective = self.fragment
.perspective_matrix(&border_box) .perspective_matrix(&border_box)
.unwrap_or_else(Transform3D::identity); .unwrap_or(LayoutTransform::identity());
let transform = transform.pre_mul(&perspective).inverse(); let transform = transform.pre_mul(&perspective).inverse();
let origin = &border_box.origin; let origin = &border_box.origin;
@ -2766,10 +2510,8 @@ impl BlockFlowDisplayListBuilding for BlockFlow {
let content_size = self.base.overflow.scroll.origin + self.base.overflow.scroll.size; let content_size = self.base.overflow.scroll.origin + self.base.overflow.scroll.size;
let content_size = Size2D::new(content_size.x, content_size.y); let content_size = Size2D::new(content_size.x, content_size.y);
let external_id = ExternalScrollId( let external_id =
self.fragment.unique_id(), ExternalScrollId(self.fragment.unique_id(), state.pipeline_id.to_webrender());
state.pipeline_id.to_webrender()
);
let new_clip_scroll_index = state.add_clip_scroll_node(ClipScrollNode { let new_clip_scroll_index = state.add_clip_scroll_node(ClipScrollNode {
parent_index: self.clipping_and_scrolling().scrolling, parent_index: self.clipping_and_scrolling().scrolling,
clip: clip, clip: clip,
@ -3228,18 +2970,67 @@ 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 {
/// The placement of the text item on the plane.
pub origin: Point2D<Au>,
/// The text run.
pub text_run: Arc<TextRun>,
/// The range of text within the text run.
pub range: Range<ByteIndex>,
/// The position of the start of the baseline of this text.
pub baseline_origin: Point2D<Au>,
}
#[derive(Default)]
pub struct IndexableText {
inner: FnvHashMap<OpaqueNode, Vec<IndexableTextItem>>,
}
impl IndexableText {
fn insert(&mut self, node: OpaqueNode, item: IndexableTextItem) {
let entries = self.inner.entry(node).or_insert(Vec::new());
entries.push(item);
}
pub fn get(&self, node: OpaqueNode) -> Option<&[IndexableTextItem]> {
self.inner.get(&node).map(|x| x.as_slice())
}
// Returns the text index within a node for the point of interest.
pub fn text_index(&self, node: OpaqueNode, point_in_item: Point2D<Au>) -> Option<usize> {
let item = self.inner.get(&node)?;
// TODO(#20020): access all elements
let point = point_in_item + item[0].origin.to_vector();
let offset = point - item[0].baseline_origin;
Some(item[0].text_run.range_index_of_advance(&item[0].range, offset.x))
}
} }

View file

@ -6,6 +6,7 @@ pub use self::builder::BlockFlowDisplayListBuilding;
pub use self::builder::BorderPaintingMode; pub use self::builder::BorderPaintingMode;
pub use self::builder::DisplayListBuildState; pub use self::builder::DisplayListBuildState;
pub use self::builder::FlexFlowDisplayListBuilding; pub use self::builder::FlexFlowDisplayListBuilding;
pub use self::builder::IndexableText;
pub use self::builder::InlineFlowDisplayListBuilding; pub use self::builder::InlineFlowDisplayListBuilding;
pub use self::builder::ListItemFlowDisplayListBuilding; pub use self::builder::ListItemFlowDisplayListBuilding;
pub use self::builder::StackingContextCollectionFlags; pub use self::builder::StackingContextCollectionFlags;

View file

@ -11,8 +11,7 @@ use gfx::display_list::{BorderDetails, ClipScrollNode};
use gfx::display_list::{ClipScrollNodeIndex, ClipScrollNodeType, DisplayItem}; use gfx::display_list::{ClipScrollNodeIndex, ClipScrollNodeType, DisplayItem};
use gfx::display_list::{DisplayList, StackingContextType}; use gfx::display_list::{DisplayList, StackingContextType};
use msg::constellation_msg::PipelineId; use msg::constellation_msg::PipelineId;
use webrender_api::{self, ClipAndScrollInfo, ClipId}; use webrender_api::{self, ClipAndScrollInfo, ClipId, DisplayListBuilder};
use webrender_api::{DisplayListBuilder, LayoutTransform};
pub trait WebRenderDisplayListConverter { pub trait WebRenderDisplayListConverter {
fn convert_to_webrender(&self, pipeline_id: PipelineId) -> DisplayListBuilder; fn convert_to_webrender(&self, pipeline_id: PipelineId) -> DisplayListBuilder;
@ -224,19 +223,12 @@ impl WebRenderDisplayItemConverter for DisplayItem {
let stacking_context = &item.stacking_context; let stacking_context = &item.stacking_context;
debug_assert_eq!(stacking_context.context_type, StackingContextType::Real); debug_assert_eq!(stacking_context.context_type, StackingContextType::Real);
let transform = stacking_context
.transform
.map(|transform| LayoutTransform::from_untyped(&transform).into());
let perspective = stacking_context
.perspective
.map(|perspective| LayoutTransform::from_untyped(&perspective));
builder.push_stacking_context( builder.push_stacking_context(
&webrender_api::LayoutPrimitiveInfo::new(stacking_context.bounds), &webrender_api::LayoutPrimitiveInfo::new(stacking_context.bounds),
stacking_context.scroll_policy, stacking_context.scroll_policy,
transform, stacking_context.transform.map(Into::into),
stacking_context.transform_style, stacking_context.transform_style,
perspective, stacking_context.perspective,
stacking_context.mix_blend_mode, stacking_context.mix_blend_mode,
stacking_context.filters.clone(), stacking_context.filters.clone(),
); );

View file

@ -29,7 +29,7 @@ use app_units::Au;
use block::{BlockFlow, FormattingContextType}; use block::{BlockFlow, FormattingContextType};
use context::LayoutContext; use context::LayoutContext;
use display_list::{DisplayListBuildState, StackingContextCollectionState}; use display_list::{DisplayListBuildState, StackingContextCollectionState};
use euclid::{Transform3D, Point2D, Vector2D, Rect, Size2D}; use euclid::{Point2D, Vector2D, Rect, Size2D};
use flex::FlexFlow; use flex::FlexFlow;
use floats::{Floats, SpeculatedFloatPlacement}; use floats::{Floats, SpeculatedFloatPlacement};
use flow_list::{FlowList, FlowListIterator, MutFlowListIterator}; use flow_list::{FlowList, FlowListIterator, MutFlowListIterator};
@ -67,6 +67,7 @@ use table_colgroup::TableColGroupFlow;
use table_row::TableRowFlow; use table_row::TableRowFlow;
use table_rowgroup::TableRowGroupFlow; use table_rowgroup::TableRowGroupFlow;
use table_wrapper::TableWrapperFlow; use table_wrapper::TableWrapperFlow;
use webrender_api::LayoutTransform;
/// This marker trait indicates that a type is a struct with `#[repr(C)]` whose first field /// This marker trait indicates that a type is a struct with `#[repr(C)]` whose first field
/// is of type `BaseFlow` or some type that also implements this trait. /// is of type `BaseFlow` or some type that also implements this trait.
@ -335,8 +336,8 @@ pub trait Flow: HasBaseFlow + fmt::Debug + Sync + Send + 'static {
let transform_2d = self.as_block() let transform_2d = self.as_block()
.fragment .fragment
.transform_matrix(&position) .transform_matrix(&position)
.unwrap_or(Transform3D::identity()) .unwrap_or(LayoutTransform::identity())
.to_2d(); .to_2d().to_untyped();
let transformed_overflow = Overflow { let transformed_overflow = Overflow {
paint: f32_rect_to_au_rect(transform_2d.transform_rect( paint: f32_rect_to_au_rect(transform_2d.transform_rect(
&au_rect_to_f32_rect(overflow.paint))), &au_rect_to_f32_rect(overflow.paint))),

View file

@ -10,7 +10,8 @@ use ServoArc;
use app_units::Au; use app_units::Au;
use canvas_traits::canvas::CanvasMsg; use canvas_traits::canvas::CanvasMsg;
use context::{LayoutContext, with_thread_local_font_context}; use context::{LayoutContext, with_thread_local_font_context};
use euclid::{Transform3D, Point2D, Vector2D, Rect, Size2D}; use display_list::ToLayout;
use euclid::{Point2D, Vector2D, Rect, Size2D};
use floats::ClearType; use floats::ClearType;
use flow::{GetBaseFlow, ImmutableFlowUtils}; use flow::{GetBaseFlow, ImmutableFlowUtils};
use flow_ref::FlowRef; use flow_ref::FlowRef;
@ -65,7 +66,7 @@ use style::values::generics::box_::VerticalAlign;
use style::values::generics::transform; use style::values::generics::transform;
use text; use text;
use text::TextRunScanner; use text::TextRunScanner;
use webrender_api; use webrender_api::{self, LayoutTransform};
use wrapper::ThreadSafeLayoutNodeHelpers; use wrapper::ThreadSafeLayoutNodeHelpers;
// From gfxFontConstants.h in Firefox. // From gfxFontConstants.h in Firefox.
@ -2867,9 +2868,10 @@ impl Fragment {
} }
/// Returns the 4D matrix representing this fragment's transform. /// Returns the 4D matrix representing this fragment's transform.
pub fn transform_matrix(&self, stacking_relative_border_box: &Rect<Au>) -> Option<Transform3D<f32>> { pub fn transform_matrix(&self, stacking_relative_border_box: &Rect<Au>) -> Option<LayoutTransform> {
let list = &self.style.get_box().transform; let list = &self.style.get_box().transform;
let transform = list.to_transform_3d_matrix(Some(stacking_relative_border_box)).ok()?.0; let transform = LayoutTransform::from_untyped(
&list.to_transform_3d_matrix(Some(stacking_relative_border_box)).ok()?.0);
let transform_origin = &self.style.get_box().transform_origin; let transform_origin = &self.style.get_box().transform_origin;
let transform_origin_x = let transform_origin_x =
@ -2882,38 +2884,42 @@ impl Fragment {
.to_f32_px(); .to_f32_px();
let transform_origin_z = transform_origin.depth.px(); let transform_origin_z = transform_origin.depth.px();
let pre_transform = Transform3D::create_translation(transform_origin_x, let pre_transform = LayoutTransform::create_translation(
transform_origin_y, transform_origin_x,
transform_origin_z); transform_origin_y,
let post_transform = Transform3D::create_translation(-transform_origin_x, transform_origin_z);
-transform_origin_y, let post_transform = LayoutTransform::create_translation(
-transform_origin_z); -transform_origin_x,
-transform_origin_y,
-transform_origin_z);
Some(pre_transform.pre_mul(&transform).pre_mul(&post_transform)) Some(pre_transform.pre_mul(&transform).pre_mul(&post_transform))
} }
/// Returns the 4D matrix representing this fragment's perspective. /// Returns the 4D matrix representing this fragment's perspective.
pub fn perspective_matrix(&self, stacking_relative_border_box: &Rect<Au>) -> Option<Transform3D<f32>> { pub fn perspective_matrix(&self, stacking_relative_border_box: &Rect<Au>) -> Option<LayoutTransform> {
match self.style().get_box().perspective { match self.style().get_box().perspective {
Either::First(length) => { Either::First(length) => {
let perspective_origin = self.style().get_box().perspective_origin; let perspective_origin = self.style().get_box().perspective_origin;
let perspective_origin = let perspective_origin =
Point2D::new( Point2D::new(
perspective_origin.horizontal perspective_origin.horizontal
.to_used_value(stacking_relative_border_box.size.width) .to_used_value(stacking_relative_border_box.size.width),
.to_f32_px(),
perspective_origin.vertical perspective_origin.vertical
.to_used_value(stacking_relative_border_box.size.height) .to_used_value(stacking_relative_border_box.size.height)
.to_f32_px()); ).to_layout();
let pre_transform = Transform3D::create_translation(perspective_origin.x, let pre_transform = LayoutTransform::create_translation(
perspective_origin.y, perspective_origin.x,
0.0); perspective_origin.y,
let post_transform = Transform3D::create_translation(-perspective_origin.x, 0.0);
-perspective_origin.y, let post_transform = LayoutTransform::create_translation(
0.0); -perspective_origin.x,
-perspective_origin.y,
0.0);
let perspective_matrix = transform::create_perspective_matrix(length.px()); let perspective_matrix = LayoutTransform::from_untyped(
&transform::create_perspective_matrix(length.px()));
Some(pre_transform.pre_mul(&perspective_matrix).pre_mul(&post_transform)) Some(pre_transform.pre_mul(&perspective_matrix).pre_mul(&post_transform))
} }

View file

@ -7,10 +7,11 @@
use app_units::Au; use app_units::Au;
use construct::ConstructionResult; use construct::ConstructionResult;
use context::LayoutContext; use context::LayoutContext;
use display_list::IndexableText;
use euclid::{Point2D, Vector2D, Rect, Size2D}; use euclid::{Point2D, Vector2D, Rect, Size2D};
use flow::{Flow, GetBaseFlow}; use flow::{Flow, GetBaseFlow};
use fragment::{Fragment, FragmentBorderBoxIterator, SpecificFragmentInfo}; use fragment::{Fragment, FragmentBorderBoxIterator, SpecificFragmentInfo};
use gfx::display_list::{DisplayItem, DisplayList, OpaqueNode, ScrollOffsetMap}; use gfx::display_list::{DisplayList, OpaqueNode, ScrollOffsetMap};
use inline::InlineFragmentNodeFlags; use inline::InlineFragmentNodeFlags;
use ipc_channel::ipc::IpcSender; use ipc_channel::ipc::IpcSender;
use msg::constellation_msg::PipelineId; use msg::constellation_msg::PipelineId;
@ -26,7 +27,6 @@ use script_traits::LayoutMsg as ConstellationMsg;
use script_traits::UntrustedNodeAddress; use script_traits::UntrustedNodeAddress;
use sequential; use sequential;
use std::cmp::{min, max}; use std::cmp::{min, max};
use std::collections::HashMap;
use std::ops::Deref; use std::ops::Deref;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use style::computed_values::display::T as Display; use style::computed_values::display::T as Display;
@ -51,6 +51,8 @@ pub struct LayoutThreadData {
/// The root stacking context. /// The root stacking context.
pub display_list: Option<Arc<DisplayList>>, pub display_list: Option<Arc<DisplayList>>,
pub indexable_text: IndexableText,
/// A queued response for the union of the content boxes of a node. /// A queued response for the union of the content boxes of a node.
pub content_box_response: Option<Rect<Au>>, pub content_box_response: Option<Rect<Au>>,
@ -885,16 +887,11 @@ enum InnerTextItem {
// https://html.spec.whatwg.org/multipage/#the-innertext-idl-attribute // https://html.spec.whatwg.org/multipage/#the-innertext-idl-attribute
pub fn process_element_inner_text_query<N: LayoutNode>(node: N, pub fn process_element_inner_text_query<N: LayoutNode>(node: N,
display_list: &Option<Arc<DisplayList>>) -> String { indexable_text: &IndexableText) -> String {
if !display_list.is_some() {
warn!("We should have a display list at this point. Cannot get inner text");
return String::new();
}
// Step 1. // Step 1.
let mut results = Vec::new(); let mut results = Vec::new();
// Step 2. // Step 2.
inner_text_collection_steps(node, display_list.as_ref().unwrap(), &mut results); inner_text_collection_steps(node, indexable_text, &mut results);
let mut max_req_line_break_count = 0; let mut max_req_line_break_count = 0;
let mut inner_text = Vec::new(); let mut inner_text = Vec::new();
for item in results { for item in results {
@ -933,18 +930,8 @@ pub fn process_element_inner_text_query<N: LayoutNode>(node: N,
// https://html.spec.whatwg.org/multipage/#inner-text-collection-steps // https://html.spec.whatwg.org/multipage/#inner-text-collection-steps
#[allow(unsafe_code)] #[allow(unsafe_code)]
fn inner_text_collection_steps<N: LayoutNode>(node: N, fn inner_text_collection_steps<N: LayoutNode>(node: N,
display_list: &Arc<DisplayList>, indexable_text: &IndexableText,
results: &mut Vec<InnerTextItem>) { results: &mut Vec<InnerTextItem>) {
// Extracts the text nodes from the display list to avoid traversing it
// for each child node.
let mut text = HashMap::new();
for item in &display_list.as_ref().list {
if let &DisplayItem::Text(ref text_content) = item {
let entries = text.entry(&item.base().metadata.node).or_insert(Vec::new());
entries.push(&text_content.text_run.text);
}
}
let mut items = Vec::new(); let mut items = Vec::new();
for child in node.traverse_preorder() { for child in node.traverse_preorder() {
let node = match child.type_id() { let node = match child.type_id() {
@ -983,9 +970,9 @@ fn inner_text_collection_steps<N: LayoutNode>(node: N,
match child.type_id() { match child.type_id() {
LayoutNodeType::Text => { LayoutNodeType::Text => {
// Step 4. // Step 4.
if let Some(text_content) = text.get(&child.opaque()) { if let Some(text_content) = indexable_text.get(child.opaque()) {
for content in text_content { for content in text_content {
items.push(InnerTextItem::Text(content.to_string())); items.push(InnerTextItem::Text(content.text_run.text.to_string()));
} }
} }
}, },

View file

@ -17,6 +17,7 @@ use servo_config::opts;
use style::servo::restyle_damage::ServoRestyleDamage; use style::servo::restyle_damage::ServoRestyleDamage;
use traversal::{AssignBSizes, AssignISizes, BubbleISizes, BuildDisplayList}; use traversal::{AssignBSizes, AssignISizes, BubbleISizes, BuildDisplayList};
use traversal::{InorderFlowTraversal, PostorderFlowTraversal, PreorderFlowTraversal}; use traversal::{InorderFlowTraversal, PostorderFlowTraversal, PreorderFlowTraversal};
use webrender_api::LayoutPoint;
pub fn resolve_generated_content(root: &mut Flow, layout_context: &LayoutContext) { pub fn resolve_generated_content(root: &mut Flow, layout_context: &LayoutContext) {
ResolveGeneratedContent::new(&layout_context).traverse(root, 0); ResolveGeneratedContent::new(&layout_context).traverse(root, 0);
@ -98,7 +99,7 @@ pub fn iterate_through_flow_tree_fragment_border_boxes(root: &mut Flow, iterator
if let Some(matrix) = kid.as_block() if let Some(matrix) = kid.as_block()
.fragment .fragment
.transform_matrix(&relative_position) { .transform_matrix(&relative_position) {
let transform_matrix = matrix.transform_point2d(&Point2D::zero()); let transform_matrix = matrix.transform_point2d(&LayoutPoint::zero());
stacking_context_position = stacking_context_position + stacking_context_position = stacking_context_position +
Vector2D::new(Au::from_f32_px(transform_matrix.x), Vector2D::new(Au::from_f32_px(transform_matrix.x),
Au::from_f32_px(transform_matrix.y)) Au::from_f32_px(transform_matrix.y))

View file

@ -68,7 +68,7 @@ use layout::context::LayoutContext;
use layout::context::RegisteredPainter; use layout::context::RegisteredPainter;
use layout::context::RegisteredPainters; use layout::context::RegisteredPainters;
use layout::context::malloc_size_of_persistent_local_context; use layout::context::malloc_size_of_persistent_local_context;
use layout::display_list::ToLayout; use layout::display_list::{IndexableText, ToLayout};
use layout::display_list::WebRenderDisplayListConverter; use layout::display_list::WebRenderDisplayListConverter;
use layout::flow::{Flow, GetBaseFlow, ImmutableFlowUtils, MutableOwnedFlowUtils}; use layout::flow::{Flow, GetBaseFlow, ImmutableFlowUtils, MutableOwnedFlowUtils};
use layout::flow_ref::FlowRef; use layout::flow_ref::FlowRef;
@ -515,6 +515,7 @@ impl LayoutThread {
LayoutThreadData { LayoutThreadData {
constellation_chan: constellation_chan, constellation_chan: constellation_chan,
display_list: None, display_list: None,
indexable_text: IndexableText::default(),
content_box_response: None, content_box_response: None,
content_boxes_response: Vec::new(), content_boxes_response: Vec::new(),
client_rect_response: Rect::zero(), client_rect_response: Rect::zero(),
@ -1002,6 +1003,9 @@ impl LayoutThread {
} }
} }
rw_data.indexable_text = std::mem::replace(
&mut build_state.indexable_text,
IndexableText::default());
rw_data.display_list = Some(Arc::new(build_state.to_display_list())); rw_data.display_list = Some(Arc::new(build_state.to_display_list()));
} }
} }
@ -1366,10 +1370,7 @@ impl LayoutThread {
Au::from_f32_px(point_in_node.y) Au::from_f32_px(point_in_node.y)
); );
rw_data.text_index_response = TextIndexResponse( rw_data.text_index_response = TextIndexResponse(
rw_data.display_list rw_data.indexable_text.text_index(opaque_node, point_in_node)
.as_ref()
.expect("Tried to hit test with no display list")
.text_index(opaque_node, point_in_node.to_layout())
); );
}, },
ReflowGoal::NodeGeometryQuery(node) => { ReflowGoal::NodeGeometryQuery(node) => {
@ -1426,7 +1427,7 @@ impl LayoutThread {
ReflowGoal::ElementInnerTextQuery(node) => { ReflowGoal::ElementInnerTextQuery(node) => {
let node = unsafe { ServoLayoutNode::new(&node) }; let node = unsafe { ServoLayoutNode::new(&node) };
rw_data.element_inner_text_response = rw_data.element_inner_text_response =
process_element_inner_text_query(node, &rw_data.display_list); process_element_inner_text_query(node, &rw_data.indexable_text);
}, },
ReflowGoal::Full | ReflowGoal::TickAnimations => {} ReflowGoal::Full | ReflowGoal::TickAnimations => {}
} }

View file

@ -126,7 +126,6 @@ fn test_first_contentful_paint_setter() {
format: PixelFormat::RGB8, format: PixelFormat::RGB8,
key: None, key: None,
}, },
image_data: None,
stretch_size: LayoutSize::zero(), stretch_size: LayoutSize::zero(),
tile_spacing: LayoutSize::zero(), tile_spacing: LayoutSize::zero(),
image_rendering: ImageRendering::Auto, image_rendering: ImageRendering::Auto,