mirror of
https://github.com/servo/servo.git
synced 2025-08-03 04:30:10 +01:00
Implement enough of 3d transforms spec to run the CSS FPS demo.
This commit is contained in:
parent
d86c587925
commit
39ddbbb0e1
19 changed files with 894 additions and 145 deletions
|
@ -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);
|
||||
|
|
|
@ -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_ => {}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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.");
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue