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
//! low-level drawing primitives.
use euclid::{Transform3D, Vector2D, TypedRect, SideOffsets2D};
use euclid::{Vector2D, TypedRect, SideOffsets2D};
use euclid::num::{One, Zero};
use gfx_traits::{self, StackingContextId};
use gfx_traits::print_tree::PrintTree;
use ipc_channel::ipc::IpcSharedMemory;
use msg::constellation_msg::PipelineId;
use net_traits::image::base::{Image, PixelFormat};
use range::Range;
use servo_geometry::MaxRect;
use std::cmp::Ordering;
use std::collections::HashMap;
use std::f32;
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::{ComplexClipRegion, ExtendMode, ExternalScrollId, FilterOp, FontInstanceKey};
use webrender_api::{GlyphInstance, GradientStop, ImageBorder, ImageKey, ImageRendering};
use webrender_api::{LayoutPoint, LayoutRect, LayoutSize, LayoutVector2D, LineStyle, LocalClip};
use webrender_api::{MixBlendMode, NormalBorder, ScrollPolicy, ScrollSensitivity};
use webrender_api::{LayoutPoint, LayoutRect, LayoutSize, LayoutTransform, LayoutVector2D, LineStyle};
use webrender_api::{LocalClip, MixBlendMode, NormalBorder, ScrollPolicy, ScrollSensitivity};
use webrender_api::{StickyOffsetBounds, TransformStyle};
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) {
let mut print_tree = PrintTree::new("Display List".to_owned());
self.print_with_tree(&mut print_tree);
@ -209,13 +185,13 @@ pub struct StackingContext {
pub mix_blend_mode: MixBlendMode,
/// 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.
pub transform_style: TransformStyle,
/// The perspective matrix to be applied to children.
pub perspective: Option<Transform3D<f32>>,
pub perspective: Option<LayoutTransform>,
/// The scroll policy of this layer.
pub scroll_policy: ScrollPolicy,
@ -234,9 +210,9 @@ impl StackingContext {
z_index: i32,
filters: Vec<FilterOp>,
mix_blend_mode: MixBlendMode,
transform: Option<Transform3D<f32>>,
transform: Option<LayoutTransform>,
transform_style: TransformStyle,
perspective: Option<Transform3D<f32>>,
perspective: Option<LayoutTransform>,
scroll_policy: ScrollPolicy,
parent_clipping_and_scrolling: ClippingAndScrolling)
-> StackingContext {
@ -651,16 +627,6 @@ pub struct SolidColorDisplayItem {
pub struct TextDisplayItem {
/// Fields common to all display items.
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.
pub glyphs: Vec<GlyphInstance>,
/// Reference to the font to be used.
@ -683,9 +649,6 @@ pub struct ImageDisplayItem {
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 bounds of this display item, then the image will be repeated in the appropriate
/// direction to tile the entire bounds.
@ -1028,7 +991,7 @@ pub trait SimpleMatrixDetection {
fn is_identity_or_simple_translation(&self) -> bool;
}
impl SimpleMatrixDetection for Transform3D<f32> {
impl SimpleMatrixDetection for LayoutTransform {
#[inline]
fn is_identity_or_simple_translation(&self) -> bool {
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
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 #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)
.map(|slice| {
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
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
//! Calculations for CSS images and CSS backgrounds.
//! Calculations for CSS images, backgrounds and borders.
//!
//! * [CSS Images Module Level 3](https://drafts.csswg.org/css-images-3/)
//! * [CSS Backgrounds and Borders Module Level 3](https://drafts.csswg.org/css-backgrounds-3/)
#![deny(unsafe_code)]
@ -10,12 +13,16 @@
use app_units::Au;
use display_list::ToLayout;
use euclid::{Point2D, Size2D, Vector2D};
use gfx::display_list;
use model::MaybeAuto;
use euclid::{Point2D, Rect, SideOffsets2D, Size2D, Vector2D};
use gfx::display_list::{self, BorderDetails, WebRenderImageInfo};
use model::{self, MaybeAuto};
use style::computed_values::background_attachment::single_value::T as BackgroundAttachment;
use style::computed_values::background_clip::single_value::T as BackgroundClip;
use style::computed_values::background_origin::single_value::T as BackgroundOrigin;
use style::properties::style_structs::{self, Background};
use style::values::computed::{Angle, GradientItem};
use style::values::computed::{LengthOrPercentage, LengthOrPercentageOrAuto, Percentage};
use style::values::computed::Position;
use style::values::computed::{LengthOrPercentage, LengthOrPercentageOrAuto};
use style::values::computed::{NumberOrPercentage, Percentage, Position};
use style::values::computed::image::{EndingShape, LineDirection};
use style::values::generics::background::BackgroundSize;
use style::values::generics::image::{Circle, Ellipse, ShapeExtent};
@ -23,7 +30,8 @@ use style::values::generics::image::EndingShape as GenericEndingShape;
use style::values::generics::image::GradientItem as GenericGradientItem;
use style::values::specified::background::BackgroundRepeatKeyword;
use style::values::specified::position::{X, Y};
use webrender_api::{ExtendMode, GradientStop};
use webrender_api::{BorderRadius, BorderSide, BorderStyle, ColorF, ExtendMode, ImageBorder};
use webrender_api::{GradientStop, LayoutSize, NinePatchDescriptor, NormalBorder};
/// A helper data structure for gradients.
#[derive(Clone, Copy)]
@ -34,9 +42,51 @@ struct StopRun {
stop_count: usize,
}
/// Placment information for both image and gradient backgrounds.
#[derive(Clone, Copy, Debug)]
pub struct BackgroundPlacement {
/// Rendering bounds. The background will start in the uppper-left corner
/// and fill the whole area.
pub bounds: Rect<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
/// image should be displayed on the background.
pub fn compute_background_image_size(
fn compute_background_image_size(
bg_size: BackgroundSize<LengthOrPercentageOrAuto>,
bounds_size: 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(
position: &mut Au,
size: &mut Au,
@ -175,7 +301,7 @@ fn tile_image(position: &mut Au, size: &mut Au, absolute_anchor_origin: Au, imag
/// For either the x or the y axis ajust various values to account for tiling.
///
/// This is done separately for both axes because the repeat keywords may differ.
pub fn tile_image_axis(
fn tile_image_axis(
repeat: BackgroundRepeatKeyword,
position: &mut Au,
size: &mut Au,
@ -322,8 +448,7 @@ fn convert_gradient_stops(gradient_items: &[GradientItem], total_length: Au) ->
}
// 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() + 2);
let mut stops = Vec::with_capacity(stop_items.len());
let mut stop_run = None;
for (i, stop) in stop_items.iter().enumerate() {
let offset = match stop.position {
@ -427,14 +552,7 @@ pub fn convert_linear_gradient(
// This is the length of the gradient line.
let length = Au::from_f32_px((delta.x.to_f32_px() * 2.0).hypot(delta.y.to_f32_px() * 2.0));
let mut 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 stops = convert_gradient_stops(stops, length);
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);
// 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);
}
let stops = convert_gradient_stops(stops, radius.width);
display_list::RadialGradient {
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.
fn get_distance_to_corner<F>(size: &Size2D<Au>, center: &Point2D<Au>, cmp: F) -> Au
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 context::LayoutContext;
use display_list::ToLayout;
use display_list::background::{compute_background_image_size, tile_image_axis};
use display_list::background::{build_border_radius, build_image_border_details};
use display_list::background::{calculate_inner_border_radii, compute_background_placement};
use display_list::background::{convert_linear_gradient, convert_radial_gradient};
use 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 flow::{BaseFlow, Flow, FlowFlags};
use flow_ref::FlowRef;
@ -41,7 +43,7 @@ use gfx_traits::{combine_id_with_fragment_type, FragmentType, StackingContextId}
use inline::{InlineFlow, InlineFragmentNodeFlags};
use ipc_channel::ipc;
use list_item::ListItemFlow;
use model::{self, MaybeAuto};
use model::MaybeAuto;
use msg::constellation_msg::{BrowsingContextId, PipelineId};
use net_traits::image::base::PixelFormat;
use net_traits::image_cache::UsePlaceholder;
@ -52,20 +54,17 @@ use std::default::Default;
use std::f32;
use std::mem;
use std::sync::Arc;
use style::computed_values::background_attachment::single_value::T as BackgroundAttachment;
use style::computed_values::background_clip::single_value::T as BackgroundClip;
use style::computed_values::background_origin::single_value::T as BackgroundOrigin;
use style::computed_values::border_style::T as BorderStyle;
use style::computed_values::overflow_x::T as StyleOverflow;
use style::computed_values::pointer_events::T as PointerEvents;
use style::computed_values::position::T as StylePosition;
use style::computed_values::visibility::T as Visibility;
use style::logical_geometry::{LogicalMargin, LogicalPoint, LogicalRect};
use style::properties::ComputedValues;
use style::properties::style_structs;
use style::properties::{ComputedValues, style_structs};
use style::servo::restyle_damage::ServoRestyleDamage;
use style::values::{Either, RGBA};
use style::values::computed::{Gradient, NumberOrPercentage};
use style::values::computed::Gradient;
use style::values::computed::effects::SimpleShadow;
use style::values::computed::pointing::Cursor;
use style::values::generics::background::BackgroundSize;
@ -75,23 +74,10 @@ use style_traits::ToCss;
use style_traits::cursor::CursorKind;
use table_cell::CollapsedBordersForCell;
use webrender_api::{self, BorderRadius, BorderSide, BoxShadowClipMode, ClipMode, ColorF};
use webrender_api::{ComplexClipRegion, ExternalScrollId, FilterOp, GlyphInstance, ImageBorder};
use webrender_api::{ImageRendering, LayoutRect, LayoutSize, LayoutVector2D, LineStyle, LocalClip};
use webrender_api::{NinePatchDescriptor, NormalBorder, ScrollPolicy, ScrollSensitivity};
use webrender_api::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,
}
}
}
use webrender_api::{ComplexClipRegion, ExternalScrollId, FilterOp, GlyphInstance};
use webrender_api::{ImageRendering, LayoutRect, LayoutSize, LayoutTransform, LayoutVector2D};
use webrender_api::{LineStyle, LocalClip, NormalBorder, ScrollPolicy};
use webrender_api::{ScrollSensitivity, StickyOffsetBounds};
fn establishes_containing_block_for_absolute(
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 {
is_first_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
/// new iframe sizes
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> {
@ -350,6 +335,7 @@ impl<'a> DisplayListBuildState<'a> {
current_stacking_context_id: StackingContextId::root(),
current_clipping_and_scrolling: root_clip_indices,
iframe_sizes: Vec::new(),
indexable_text: IndexableText::default(),
}
}
@ -543,19 +529,6 @@ pub trait FragmentDisplayListBuilding {
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
/// appropriate section of the display list.
fn build_display_list_for_webrender_image(
@ -728,82 +701,6 @@ pub trait FragmentDisplayListBuilding {
fn fragment_type(&self) -> FragmentType;
}
fn scale_border_radii(radii: BorderRadius, factor: f32) -> BorderRadius {
BorderRadius {
top_left: radii.top_left * factor,
top_right: radii.top_right * factor,
bottom_left: radii.bottom_left * factor,
bottom_right: radii.bottom_right * factor,
}
}
fn handle_overlapping_radii(size: LayoutSize, radii: BorderRadius) -> BorderRadius {
// No two corners' border radii may add up to more than the length of the edge
// between them. To prevent that, all radii are scaled down uniformly.
fn scale_factor(radius_a: f32, radius_b: f32, edge_length: f32) -> f32 {
let required = radius_a + radius_b;
if required <= edge_length {
1.0
} else {
edge_length / required
}
}
let top_factor = scale_factor(radii.top_left.width, radii.top_right.width, size.width);
let bottom_factor = scale_factor(
radii.bottom_left.width,
radii.bottom_right.width,
size.width,
);
let left_factor = scale_factor(radii.top_left.height, radii.bottom_left.height, size.height);
let right_factor = scale_factor(
radii.top_right.height,
radii.bottom_right.height,
size.height,
);
let min_factor = top_factor
.min(bottom_factor)
.min(left_factor)
.min(right_factor);
if min_factor < 1.0 {
scale_border_radii(radii, min_factor)
} else {
radii
}
}
fn build_border_radius(
abs_bounds: &Rect<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
/// for building the clip for the content inside the border.
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)
}
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 {
fn collect_stacking_contexts_for_blocklike_fragment(
&mut self,
@ -975,15 +781,13 @@ impl FragmentDisplayListBuilding for Fragment {
let mut bounds = *absolute_bounds;
// This is the clip for the color (which is the last element in the bg array)
let color_clip = get_cyclic(
&background.background_clip.0,
background.background_image.0.len() - 1,
);
// Background clips are never empty.
let color_clip = &background.background_clip.0.last().unwrap();
// Adjust the clipping region as necessary to account for `border-radius`.
let mut border_radii = build_border_radius(absolute_bounds, style.get_border());
match *color_clip {
match **color_clip {
BackgroundClip::BorderBox => {},
BackgroundClip::PaddingBox => {
let border = style.logical_border_width().to_physical(style.writing_mode);
@ -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(
&self,
state: &mut DisplayListBuildState,
@ -1187,8 +914,15 @@ impl FragmentDisplayListBuilding for Fragment {
Au::from_px(webrender_image.width as i32),
Au::from_px(webrender_image.height as i32),
);
let placement =
self.compute_background_placement(state, style, absolute_bounds, Some(image), index);
let placement = compute_background_placement(
style.get_background(),
state.layout_context.shared_context().viewport_size(),
absolute_bounds,
Some(image),
style.logical_border_width().to_physical(style.writing_mode),
self.border_padding.to_physical(style.writing_mode),
index,
);
// Create the image display item.
let base = state.create_base_display_item(
@ -1203,7 +937,6 @@ impl FragmentDisplayListBuilding for Fragment {
state.add_display_item(DisplayItem::Image(Box::new(ImageDisplayItem {
base: base,
webrender_image: webrender_image,
image_data: None,
stretch_size: placement.tile_size.to_layout(),
tile_spacing: placement.tile_spacing.to_layout(),
image_rendering: style.get_inheritedbox().image_rendering.to_layout(),
@ -1280,8 +1013,15 @@ impl FragmentDisplayListBuilding for Fragment {
style: &ComputedValues,
index: usize,
) {
let placement =
self.compute_background_placement(state, style, absolute_bounds, None, index);
let placement = compute_background_placement(
style.get_background(),
state.layout_context.shared_context().viewport_size(),
absolute_bounds,
None,
style.logical_border_width().to_physical(style.writing_mode),
self.border_padding.to_physical(style.writing_mode),
index,
);
let base = state.create_base_display_item(
&placement.bounds,
@ -2000,7 +1740,6 @@ impl FragmentDisplayListBuilding for Fragment {
state.add_display_item(DisplayItem::Image(Box::new(ImageDisplayItem {
base: base,
webrender_image: WebRenderImageInfo::from_image(image),
image_data: Some(Arc::new(image.bytes.clone())),
stretch_size: stacking_relative_content_box.size.to_layout(),
tile_spacing: LayoutSize::zero(),
image_rendering: self.style.get_inheritedbox().image_rendering.to_layout(),
@ -2041,7 +1780,6 @@ impl FragmentDisplayListBuilding for Fragment {
format: format,
key: Some(image_key),
},
image_data: None,
stretch_size: stacking_relative_content_box.size.to_layout(),
tile_spacing: LayoutSize::zero(),
image_rendering: ImageRendering::Auto,
@ -2178,7 +1916,7 @@ impl FragmentDisplayListBuilding for Fragment {
// Create display items for text decorations.
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,
*stacking_relative_content_box,
container_size,
@ -2186,9 +1924,10 @@ impl FragmentDisplayListBuilding for Fragment {
// Underline
if text_decorations.underline {
let mut stacking_relative_box = stacking_relative_content_box;
stacking_relative_box.start.b =
stacking_relative_content_box.start.b + metrics.ascent - metrics.underline_offset;
let mut stacking_relative_box = logical_stacking_relative_content_box;
stacking_relative_box.start.b = logical_stacking_relative_content_box.start.b +
metrics.ascent -
metrics.underline_offset;
stacking_relative_box.size.block = metrics.underline_size;
self.build_display_list_for_text_decoration(
state,
@ -2200,7 +1939,7 @@ impl FragmentDisplayListBuilding for Fragment {
// 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;
self.build_display_list_for_text_decoration(
state,
@ -2217,11 +1956,16 @@ impl FragmentDisplayListBuilding for Fragment {
baseline_origin,
);
if !glyphs.is_empty() {
state.add_display_item(DisplayItem::Text(Box::new(TextDisplayItem {
base: base.clone(),
let indexable_text = IndexableTextItem {
origin: stacking_relative_content_box.origin,
text_run: text_fragment.run.clone(),
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,
font_key: text_fragment.run.font_key,
text_color: text_color.to_layout(),
@ -2233,7 +1977,7 @@ impl FragmentDisplayListBuilding for Fragment {
// 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 + metrics.ascent - metrics.strikeout_offset;
stacking_relative_box.size.block = metrics.strikeout_size;
@ -2464,7 +2208,7 @@ impl BlockFlowDisplayListBuilding for BlockFlow {
let perspective = self.fragment
.perspective_matrix(&border_box)
.unwrap_or_else(Transform3D::identity);
.unwrap_or(LayoutTransform::identity());
let transform = transform.pre_mul(&perspective).inverse();
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 = Size2D::new(content_size.x, content_size.y);
let external_id = ExternalScrollId(
self.fragment.unique_id(),
state.pipeline_id.to_webrender()
);
let external_id =
ExternalScrollId(self.fragment.unique_id(), state.pipeline_id.to_webrender());
let new_clip_scroll_index = state.add_clip_scroll_node(ClipScrollNode {
parent_index: self.clipping_and_scrolling().scrolling,
clip: clip,
@ -3228,18 +2970,67 @@ pub enum BorderPaintingMode<'a> {
Hidden,
}
#[derive(Clone, Copy, Debug)]
pub struct BackgroundPlacement {
/// Rendering bounds. The background will start in the uppper-left corner
/// and fill the whole area.
bounds: Rect<Au>,
/// Background tile size. Some backgrounds are repeated. These are the
/// dimensions of a single image of the background.
tile_size: Size2D<Au>,
/// Spacing between tiles. Some backgrounds are not repeated seamless
/// but have seams between them like tiles in real life.
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.
css_clip: Rect<Au>,
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;
}
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::DisplayListBuildState;
pub use self::builder::FlexFlowDisplayListBuilding;
pub use self::builder::IndexableText;
pub use self::builder::InlineFlowDisplayListBuilding;
pub use self::builder::ListItemFlowDisplayListBuilding;
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::{DisplayList, StackingContextType};
use msg::constellation_msg::PipelineId;
use webrender_api::{self, ClipAndScrollInfo, ClipId};
use webrender_api::{DisplayListBuilder, LayoutTransform};
use webrender_api::{self, ClipAndScrollInfo, ClipId, DisplayListBuilder};
pub trait WebRenderDisplayListConverter {
fn convert_to_webrender(&self, pipeline_id: PipelineId) -> DisplayListBuilder;
@ -224,19 +223,12 @@ impl WebRenderDisplayItemConverter for DisplayItem {
let stacking_context = &item.stacking_context;
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(
&webrender_api::LayoutPrimitiveInfo::new(stacking_context.bounds),
stacking_context.scroll_policy,
transform,
stacking_context.transform.map(Into::into),
stacking_context.transform_style,
perspective,
stacking_context.perspective,
stacking_context.mix_blend_mode,
stacking_context.filters.clone(),
);

View file

@ -29,7 +29,7 @@ use app_units::Au;
use block::{BlockFlow, FormattingContextType};
use context::LayoutContext;
use display_list::{DisplayListBuildState, StackingContextCollectionState};
use euclid::{Transform3D, Point2D, Vector2D, Rect, Size2D};
use euclid::{Point2D, Vector2D, Rect, Size2D};
use flex::FlexFlow;
use floats::{Floats, SpeculatedFloatPlacement};
use flow_list::{FlowList, FlowListIterator, MutFlowListIterator};
@ -67,6 +67,7 @@ use table_colgroup::TableColGroupFlow;
use table_row::TableRowFlow;
use table_rowgroup::TableRowGroupFlow;
use table_wrapper::TableWrapperFlow;
use webrender_api::LayoutTransform;
/// 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.
@ -335,8 +336,8 @@ pub trait Flow: HasBaseFlow + fmt::Debug + Sync + Send + 'static {
let transform_2d = self.as_block()
.fragment
.transform_matrix(&position)
.unwrap_or(Transform3D::identity())
.to_2d();
.unwrap_or(LayoutTransform::identity())
.to_2d().to_untyped();
let transformed_overflow = Overflow {
paint: f32_rect_to_au_rect(transform_2d.transform_rect(
&au_rect_to_f32_rect(overflow.paint))),

View file

@ -10,7 +10,8 @@ use ServoArc;
use app_units::Au;
use canvas_traits::canvas::CanvasMsg;
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 flow::{GetBaseFlow, ImmutableFlowUtils};
use flow_ref::FlowRef;
@ -65,7 +66,7 @@ use style::values::generics::box_::VerticalAlign;
use style::values::generics::transform;
use text;
use text::TextRunScanner;
use webrender_api;
use webrender_api::{self, LayoutTransform};
use wrapper::ThreadSafeLayoutNodeHelpers;
// From gfxFontConstants.h in Firefox.
@ -2867,9 +2868,10 @@ impl Fragment {
}
/// 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 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_x =
@ -2882,10 +2884,12 @@ impl Fragment {
.to_f32_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_x,
transform_origin_y,
transform_origin_z);
let post_transform = Transform3D::create_translation(-transform_origin_x,
let post_transform = LayoutTransform::create_translation(
-transform_origin_x,
-transform_origin_y,
-transform_origin_z);
@ -2893,27 +2897,29 @@ impl Fragment {
}
/// 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 {
Either::First(length) => {
let perspective_origin = self.style().get_box().perspective_origin;
let perspective_origin =
Point2D::new(
perspective_origin.horizontal
.to_used_value(stacking_relative_border_box.size.width)
.to_f32_px(),
.to_used_value(stacking_relative_border_box.size.width),
perspective_origin.vertical
.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.x,
perspective_origin.y,
0.0);
let post_transform = Transform3D::create_translation(-perspective_origin.x,
let post_transform = LayoutTransform::create_translation(
-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))
}

View file

@ -7,10 +7,11 @@
use app_units::Au;
use construct::ConstructionResult;
use context::LayoutContext;
use display_list::IndexableText;
use euclid::{Point2D, Vector2D, Rect, Size2D};
use flow::{Flow, GetBaseFlow};
use fragment::{Fragment, FragmentBorderBoxIterator, SpecificFragmentInfo};
use gfx::display_list::{DisplayItem, DisplayList, OpaqueNode, ScrollOffsetMap};
use gfx::display_list::{DisplayList, OpaqueNode, ScrollOffsetMap};
use inline::InlineFragmentNodeFlags;
use ipc_channel::ipc::IpcSender;
use msg::constellation_msg::PipelineId;
@ -26,7 +27,6 @@ use script_traits::LayoutMsg as ConstellationMsg;
use script_traits::UntrustedNodeAddress;
use sequential;
use std::cmp::{min, max};
use std::collections::HashMap;
use std::ops::Deref;
use std::sync::{Arc, Mutex};
use style::computed_values::display::T as Display;
@ -51,6 +51,8 @@ pub struct LayoutThreadData {
/// The root stacking context.
pub display_list: Option<Arc<DisplayList>>,
pub indexable_text: IndexableText,
/// A queued response for the union of the content boxes of a node.
pub content_box_response: Option<Rect<Au>>,
@ -885,16 +887,11 @@ enum InnerTextItem {
// https://html.spec.whatwg.org/multipage/#the-innertext-idl-attribute
pub fn process_element_inner_text_query<N: LayoutNode>(node: N,
display_list: &Option<Arc<DisplayList>>) -> String {
if !display_list.is_some() {
warn!("We should have a display list at this point. Cannot get inner text");
return String::new();
}
indexable_text: &IndexableText) -> String {
// Step 1.
let mut results = Vec::new();
// 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 inner_text = Vec::new();
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
#[allow(unsafe_code)]
fn inner_text_collection_steps<N: LayoutNode>(node: N,
display_list: &Arc<DisplayList>,
indexable_text: &IndexableText,
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();
for child in node.traverse_preorder() {
let node = match child.type_id() {
@ -983,9 +970,9 @@ fn inner_text_collection_steps<N: LayoutNode>(node: N,
match child.type_id() {
LayoutNodeType::Text => {
// 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 {
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 traversal::{AssignBSizes, AssignISizes, BubbleISizes, BuildDisplayList};
use traversal::{InorderFlowTraversal, PostorderFlowTraversal, PreorderFlowTraversal};
use webrender_api::LayoutPoint;
pub fn resolve_generated_content(root: &mut Flow, layout_context: &LayoutContext) {
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()
.fragment
.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 +
Vector2D::new(Au::from_f32_px(transform_matrix.x),
Au::from_f32_px(transform_matrix.y))

View file

@ -68,7 +68,7 @@ use layout::context::LayoutContext;
use layout::context::RegisteredPainter;
use layout::context::RegisteredPainters;
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::flow::{Flow, GetBaseFlow, ImmutableFlowUtils, MutableOwnedFlowUtils};
use layout::flow_ref::FlowRef;
@ -515,6 +515,7 @@ impl LayoutThread {
LayoutThreadData {
constellation_chan: constellation_chan,
display_list: None,
indexable_text: IndexableText::default(),
content_box_response: None,
content_boxes_response: Vec::new(),
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()));
}
}
@ -1366,10 +1370,7 @@ impl LayoutThread {
Au::from_f32_px(point_in_node.y)
);
rw_data.text_index_response = TextIndexResponse(
rw_data.display_list
.as_ref()
.expect("Tried to hit test with no display list")
.text_index(opaque_node, point_in_node.to_layout())
rw_data.indexable_text.text_index(opaque_node, point_in_node)
);
},
ReflowGoal::NodeGeometryQuery(node) => {
@ -1426,7 +1427,7 @@ impl LayoutThread {
ReflowGoal::ElementInnerTextQuery(node) => {
let node = unsafe { ServoLayoutNode::new(&node) };
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 => {}
}

View file

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