Implement to_transform_3d_matrix for computing distance of mismatched transform lists.

We could use this method to convert a TransformList into a Matrix, and
use this matrix for computing distance for Stylo and rendering the transform
for Servo.

This is an equivalent of nsStyleTransformMatrix::ReadTransforms in Gecko.
This commit is contained in:
Boris Chiou 2017-08-22 10:25:35 +08:00
parent 772a8464eb
commit 5c2d8507be
5 changed files with 215 additions and 146 deletions

View file

@ -10,7 +10,7 @@ use ServoArc;
use app_units::Au;
use canvas_traits::canvas::CanvasMsg;
use context::{LayoutContext, with_thread_local_font_context};
use euclid::{Transform3D, Point2D, Vector2D, Radians, Rect, Size2D};
use euclid::{Transform3D, Point2D, Vector2D, Rect, Size2D};
use floats::ClearType;
use flow::{self, ImmutableFlowUtils};
use flow_ref::FlowRef;
@ -25,7 +25,7 @@ use ipc_channel::ipc::IpcSender;
#[cfg(debug_assertions)]
use layout_debug;
use model::{self, IntrinsicISizes, IntrinsicISizesContribution, MaybeAuto, SizeConstraint};
use model::{style_length, ToGfxMatrix};
use model::style_length;
use msg::constellation_msg::{BrowsingContextId, PipelineId};
use net_traits::image::base::{Image, ImageMetadata};
use net_traits::image_cache::{ImageOrMetadataAvailable, UsePlaceholder};
@ -41,11 +41,12 @@ use std::cmp::{Ordering, max, min};
use std::collections::LinkedList;
use std::sync::{Arc, Mutex};
use style::computed_values::{border_collapse, box_sizing, clear, color, display, mix_blend_mode};
use style::computed_values::{overflow_wrap, overflow_x, position, text_decoration_line, transform};
use style::computed_values::{overflow_wrap, overflow_x, position, text_decoration_line};
use style::computed_values::{transform_style, vertical_align, white_space, word_break};
use style::computed_values::content::ContentItem;
use style::logical_geometry::{Direction, LogicalMargin, LogicalRect, LogicalSize, WritingMode};
use style::properties::ComputedValues;
use style::properties::longhands::transform::computed_value::T as TransformList;
use style::selector_parser::RestyleDamage;
use style::servo::restyle_damage::RECONSTRUCT_FLOW;
use style::str::char_is_whitespace;
@ -2867,12 +2868,12 @@ 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>> {
let operations = match self.style.get_box().transform.0 {
let list = &self.style.get_box().transform;
let transform = match list.to_transform_3d_matrix(Some(stacking_relative_border_box)) {
Some(transform) => transform,
None => return None,
Some(ref operations) => operations,
};
let mut transform = Transform3D::identity();
let transform_origin = &self.style.get_box().transform_origin;
let transform_origin_x =
transform_origin.horizontal
@ -2891,55 +2892,6 @@ impl Fragment {
-transform_origin_y,
-transform_origin_z);
for operation in operations {
let matrix = match *operation {
transform::ComputedOperation::Rotate(ax, ay, az, theta) => {
// https://www.w3.org/TR/css-transforms-1/#funcdef-rotate3d
// A direction vector that cannot be normalized, such as [0, 0, 0], will cause
// the rotation to not be applied, so we use identity matrix in this case.
let len = (ax * ax + ay * ay + az * az).sqrt();
if len > 0. {
let theta = 2.0f32 * f32::consts::PI - theta.radians();
Transform3D::create_rotation(ax / len, ay / len, az / len,
Radians::new(theta))
} else {
Transform3D::identity()
}
}
transform::ComputedOperation::Perspective(d) => {
create_perspective_matrix(d)
}
transform::ComputedOperation::Scale(sx, sy, sz) => {
Transform3D::create_scale(sx, sy, sz)
}
transform::ComputedOperation::Translate(tx, ty, tz) => {
let tx = tx.to_used_value(stacking_relative_border_box.size.width).to_f32_px();
let ty = ty.to_used_value(stacking_relative_border_box.size.height).to_f32_px();
let tz = tz.to_f32_px();
Transform3D::create_translation(tx, ty, tz)
}
transform::ComputedOperation::Matrix(m) => {
m.to_gfx_matrix()
}
transform::ComputedOperation::MatrixWithPercents(_) => {
// `-moz-transform` is not implemented in Servo yet.
unreachable!()
}
transform::ComputedOperation::Skew(theta_x, theta_y) => {
Transform3D::create_skew(Radians::new(theta_x.radians()),
Radians::new(theta_y.radians()))
}
transform::ComputedOperation::InterpolateMatrix { .. } |
transform::ComputedOperation::AccumulateMatrix { .. } => {
// TODO: Convert InterpolateMatrix/AccmulateMatrix into a valid Transform3D by
// the reference box.
Transform3D::identity()
}
};
transform = transform.pre_mul(&matrix);
}
Some(pre_transform.pre_mul(&transform).pre_mul(&post_transform))
}
@ -2964,7 +2916,7 @@ impl Fragment {
-perspective_origin.y,
0.0);
let perspective_matrix = create_perspective_matrix(length);
let perspective_matrix = TransformList::create_perspective_matrix(length);
Some(pre_transform.pre_mul(&perspective_matrix).pre_mul(&post_transform))
}
@ -3208,20 +3160,3 @@ impl Serialize for DebugId {
serializer.serialize_u16(self.0)
}
}
// TODO(gw): The transforms spec says that perspective length must
// be positive. However, there is some confusion between the spec
// and browser implementations as to handling the case of 0 for the
// perspective value. Until the spec bug is resolved, at least ensure
// that a provided perspective value of <= 0.0 doesn't cause panics
// and behaves as it does in other browsers.
// See https://lists.w3.org/Archives/Public/www-style/2016Jan/0020.html for more details.
#[inline]
fn create_perspective_matrix(d: Au) -> Transform3D<f32> {
let d = d.to_f32_px();
if d <= 0.0 {
Transform3D::identity()
} else {
Transform3D::create_perspective(d)
}
}

View file

@ -7,11 +7,10 @@
#![deny(unsafe_code)]
use app_units::Au;
use euclid::{Transform3D, SideOffsets2D, Size2D};
use euclid::{SideOffsets2D, Size2D};
use fragment::Fragment;
use std::cmp::{max, min};
use std::fmt;
use style::computed_values::transform::ComputedMatrix;
use style::logical_geometry::{LogicalMargin, WritingMode};
use style::properties::ComputedValues;
use style::values::computed::{BorderCornerRadius, LengthOrPercentageOrAuto};
@ -508,20 +507,6 @@ pub fn specified_margin_from_style(style: &ComputedValues,
MaybeAuto::from_style(margin_style.margin_left, Au(0)).specified_or_zero()))
}
pub trait ToGfxMatrix {
fn to_gfx_matrix(&self) -> Transform3D<f32>;
}
impl ToGfxMatrix for ComputedMatrix {
fn to_gfx_matrix(&self) -> Transform3D<f32> {
Transform3D::row_major(
self.m11 as f32, self.m12 as f32, self.m13 as f32, self.m14 as f32,
self.m21 as f32, self.m22 as f32, self.m23 as f32, self.m24 as f32,
self.m31 as f32, self.m32 as f32, self.m33 as f32, self.m34 as f32,
self.m41 as f32, self.m42 as f32, self.m43 as f32, self.m44 as f32)
}
}
/// A min-size and max-size constraint. The constructor has a optional `border`
/// parameter, and when it is present the constraint will be subtracted. This is
/// used to adjust the constraint for `box-sizing: border-box`, and when you do so