Implement enough of 3d transforms spec to run the CSS FPS demo.

This commit is contained in:
Glenn Watson 2015-06-18 13:06:31 +10:00
parent d86c587925
commit 39ddbbb0e1
19 changed files with 894 additions and 145 deletions

View file

@ -56,10 +56,10 @@ use std::cmp::{max, min};
use std::fmt;
use std::sync::Arc;
use style::computed_values::{border_collapse, box_sizing, display, float, overflow_x, overflow_y};
use style::computed_values::{position, text_align};
use style::computed_values::{transform, transform_style, position, text_align};
use style::properties::ComputedValues;
use style::values::computed::{LengthOrPercentage, LengthOrPercentageOrAuto};
use style::values::computed::{LengthOrPercentageOrNone};
use style::values::computed::{LengthOrNone, LengthOrPercentageOrNone};
use util::geometry::{Au, MAX_AU, MAX_RECT};
use util::logical_geometry::{LogicalPoint, LogicalRect, LogicalSize, WritingMode};
use util::opts;
@ -617,6 +617,33 @@ impl BlockFlow {
}
}
pub fn transform_requires_layer(&self) -> bool {
// Check if the transform matrix is 2D or 3D
if let Some(ref transform_list) = self.fragment.style().get_effects().transform {
for transform in transform_list {
match transform {
&transform::ComputedOperation::Perspective(..) => {
return true;
}
&transform::ComputedOperation::Matrix(m) => {
// See http://dev.w3.org/csswg/css-transforms/#2d-matrix
if m.m31 != 0.0 || m.m32 != 0.0 ||
m.m13 != 0.0 || m.m23 != 0.0 ||
m.m43 != 0.0 || m.m14 != 0.0 ||
m.m24 != 0.0 || m.m34 != 0.0 ||
m.m33 != 1.0 || m.m44 != 1.0 {
return true;
}
}
_ => {}
}
}
}
// Neither perspective nor transform present
false
}
/// Compute the actual inline size and position for this block.
pub fn compute_used_inline_size(&mut self,
layout_context: &LayoutContext,
@ -1676,6 +1703,16 @@ impl Flow for BlockFlow {
self.base.stacking_relative_position_of_display_port = MAX_RECT;
}
// This flow needs a layer if it has a 3d transform, or provides perspective
// to child layers. See http://dev.w3.org/csswg/css-transforms/#3d-rendering-contexts.
let transform_style = self.fragment.style().get_used_transform_style();
let has_3d_transform = self.transform_requires_layer();
let has_perspective = self.fragment.style().get_effects().perspective != LengthOrNone::None;
if has_3d_transform || has_perspective {
self.base.flags.insert(NEEDS_LAYER);
}
if self.base.flags.contains(IS_ABSOLUTELY_POSITIONED) {
let position_start = self.base.position.start.to_physical(self.base.writing_mode,
container_size);
@ -1818,6 +1855,16 @@ impl Flow for BlockFlow {
// Process children.
for kid in self.base.child_iter() {
// If this layer preserves the 3d context of children,
// then children will need a render layer.
// TODO(gw): This isn't always correct. In some cases
// this may create extra layers than needed. I think
// there are also some edge cases where children don't
// get a layer when they should.
if transform_style == transform_style::T::preserve_3d {
flow::mut_base(kid).flags.insert(NEEDS_LAYER);
}
if flow::base(kid).flags.contains(INLINE_POSITION_IS_STATIC) ||
flow::base(kid).flags.contains(BLOCK_POSITION_IS_STATIC) {
let kid_base = flow::mut_base(kid);

View file

@ -21,7 +21,7 @@ use list_item::ListItemFlow;
use model::{self, MaybeAuto, ToGfxMatrix, ToAu};
use table_cell::CollapsedBordersForCell;
use euclid::{Point2D, Rect, Size2D, SideOffsets2D};
use euclid::{Point2D, Point3D, Rect, Size2D, SideOffsets2D};
use euclid::Matrix4;
use gfx_traits::color;
use gfx::display_list::{BLUR_INFLATION_FACTOR, BaseDisplayItem, BorderDisplayItem};
@ -46,11 +46,12 @@ use style::computed_values::filter::Filter;
use style::computed_values::{background_attachment, background_clip, background_origin,
background_repeat, background_size};
use style::computed_values::{border_style, image_rendering, overflow_x, position,
visibility, transform};
visibility, transform, transform_style};
use style::properties::ComputedValues;
use style::properties::style_structs::Border;
use style::values::RGBA;
use style::values::computed::{Image, LinearGradient, LengthOrPercentage, LengthOrPercentageOrAuto};
use style::values::computed::{Image, LinearGradient};
use style::values::computed::{LengthOrNone, LengthOrPercentage, LengthOrPercentageOrAuto};
use style::values::specified::{AngleOrCorner, HorizontalDirection, VerticalDirection};
use url::Url;
use util::cursor::Cursor;
@ -1146,17 +1147,18 @@ impl FragmentDisplayListBuilding for Fragment {
if let Some(ref operations) = self.style().get_effects().transform {
let transform_origin = self.style().get_effects().transform_origin;
let transform_origin =
Point2D::new(model::specified(transform_origin.horizontal,
Point3D::new(model::specified(transform_origin.horizontal,
border_box.size.width).to_f32_px(),
model::specified(transform_origin.vertical,
border_box.size.height).to_f32_px());
border_box.size.height).to_f32_px(),
transform_origin.depth.to_f32_px());
let pre_transform = Matrix4::create_translation(transform_origin.x,
transform_origin.y,
0.0);
transform_origin.z);
let post_transform = Matrix4::create_translation(-transform_origin.x,
-transform_origin.y,
0.0);
-transform_origin.z);
for operation in operations {
let matrix = match operation {
@ -1190,6 +1192,31 @@ impl FragmentDisplayListBuilding for Fragment {
transform = pre_transform.mul(&transform).mul(&post_transform);
}
let perspective = match self.style().get_effects().perspective {
LengthOrNone::Length(d) => {
let perspective_origin = self.style().get_effects().perspective_origin;
let perspective_origin =
Point2D::new(model::specified(perspective_origin.horizontal,
border_box.size.width).to_f32_px(),
model::specified(perspective_origin.vertical,
border_box.size.height).to_f32_px());
let pre_transform = Matrix4::create_translation(perspective_origin.x,
perspective_origin.y,
0.0);
let post_transform = Matrix4::create_translation(-perspective_origin.x,
-perspective_origin.y,
0.0);
let perspective_matrix = Matrix4::create_perspective(d.to_f32_px());
pre_transform.mul(&perspective_matrix).mul(&post_transform)
}
LengthOrNone::None => {
Matrix4::identity()
}
};
// FIXME(pcwalton): Is this vertical-writing-direction-safe?
let margin = self.margin.to_physical(base_flow.writing_mode);
let overflow = base_flow.overflow.translate(&-Point2D::new(margin.left, Au(0)));
@ -1221,16 +1248,19 @@ impl FragmentDisplayListBuilding for Fragment {
.send((layer_id, fragment_info.renderer.clone())).unwrap();
}
let transform_style = self.style().get_used_transform_style();
let layer = layer.map(|l| Arc::new(l));
Arc::new(StackingContext::new(display_list,
&border_box,
&overflow,
self.style().get_box().z_index.number_or_zero(),
&transform,
filters,
self.style().get_effects().mix_blend_mode,
layer))
layer,
transform,
perspective,
transform_style == transform_style::T::flat))
}
#[inline(never)]
@ -1489,11 +1519,28 @@ impl BlockFlowDisplayListBuilding for BlockFlow {
background_border_level);
self.base.display_list_building_result = if self.fragment.establishes_stacking_context() {
DisplayListBuildingResult::StackingContext(
self.fragment.create_stacking_context(&self.base,
display_list,
layout_context,
StackingContextLayer::IfCanvas(self.layer_id(0))))
if self.will_get_layer() {
// If we got here, then we need a new layer.
let scroll_policy = if self.is_fixed() {
ScrollPolicy::FixedPosition
} else {
ScrollPolicy::Scrollable
};
let paint_layer = PaintLayer::new(self.layer_id(0), color::transparent(), scroll_policy);
let layer = StackingContextLayer::Existing(paint_layer);
let stacking_context = self.fragment.create_stacking_context(&self.base,
display_list,
layout_context,
layer);
DisplayListBuildingResult::StackingContext(stacking_context)
} else {
DisplayListBuildingResult::StackingContext(
self.fragment.create_stacking_context(&self.base,
display_list,
layout_context,
StackingContextLayer::IfCanvas(self.layer_id(0))))
}
} else {
match self.fragment.style.get_box().position {
position::T::static_ => {}

View file

@ -40,6 +40,7 @@ use string_cache::Atom;
use style::computed_values::content::ContentItem;
use style::computed_values::{border_collapse, clear, mix_blend_mode, overflow_wrap, position};
use style::computed_values::{text_align, text_decoration, white_space, word_break};
use style::computed_values::transform_style;
use style::node::TNode;
use style::properties::{self, ComputedValues, cascade_anonymous};
use style::values::computed::{LengthOrPercentage, LengthOrPercentageOrAuto};
@ -1990,6 +1991,12 @@ impl Fragment {
if self.style().get_effects().transform.is_some() {
return true
}
match self.style().get_used_transform_style() {
transform_style::T::flat | transform_style::T::preserve_3d => {
return true
}
transform_style::T::auto => {}
}
// Canvas always layerizes, as an special case
// FIXME(pcwalton): Don't unconditionally form stacking contexts for each canvas.

View file

@ -35,7 +35,7 @@ use euclid::scale_factor::ScaleFactor;
use euclid::size::Size2D;
use gfx_traits::color;
use gfx::display_list::{ClippingRegion, DisplayItemMetadata, DisplayList, OpaqueNode};
use gfx::display_list::{StackingContext};
use gfx::display_list::StackingContext;
use gfx::font_cache_task::FontCacheTask;
use gfx::paint_task::Msg as PaintMsg;
use gfx::paint_task::{PaintChan, PaintLayer};
@ -872,10 +872,12 @@ impl LayoutTask {
&origin,
&origin,
0,
&Matrix4::identity(),
filter::T::new(Vec::new()),
mix_blend_mode::T::normal,
Some(paint_layer)));
Some(paint_layer),
Matrix4::identity(),
Matrix4::identity(),
true));
if opts::get().dump_display_list {
println!("#### start printing display list.");