mirror of
https://github.com/servo/servo.git
synced 2025-08-04 05:00:08 +01:00
Auto merge of #19388 - BorisChiou:stylo/dommatrix/parser, r=emilio,heycam
stylo: Implement Servo_ParseTransformIntoMatrix This is an inter-dependent patch of Bug 1408310. DOMMatrix needs to convert a specified transform list into a matrix, so we rewrite to_transform_3d_matrix by generics for both specified and computed transform lists. --- - [X] `./mach build -d` does not report any errors - [X] `./mach test-tidy` does not report any errors - [X] These changes fix [Bug 1408310](https://bugzilla.mozilla.org/show_bug.cgi?id=1408310). - [X] These changes do not require tests because we can count on the wpt tests for DOMMatrix on Gecko side. <!-- 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/19388) <!-- Reviewable:end -->
This commit is contained in:
commit
823da9e34a
16 changed files with 450 additions and 238 deletions
|
@ -46,13 +46,13 @@ use style::computed_values::{transform_style, white_space, word_break};
|
||||||
use style::computed_values::content::ContentItem;
|
use style::computed_values::content::ContentItem;
|
||||||
use style::logical_geometry::{Direction, LogicalMargin, LogicalRect, LogicalSize, WritingMode};
|
use style::logical_geometry::{Direction, LogicalMargin, LogicalRect, LogicalSize, WritingMode};
|
||||||
use style::properties::ComputedValues;
|
use style::properties::ComputedValues;
|
||||||
use style::properties::longhands::transform::computed_value::T as TransformList;
|
|
||||||
use style::selector_parser::RestyleDamage;
|
use style::selector_parser::RestyleDamage;
|
||||||
use style::servo::restyle_damage::ServoRestyleDamage;
|
use style::servo::restyle_damage::ServoRestyleDamage;
|
||||||
use style::str::char_is_whitespace;
|
use style::str::char_is_whitespace;
|
||||||
use style::values::{self, Either, Auto};
|
use style::values::{self, Either, Auto};
|
||||||
use style::values::computed::{Length, LengthOrPercentage, LengthOrPercentageOrAuto};
|
use style::values::computed::{Length, LengthOrPercentage, LengthOrPercentageOrAuto};
|
||||||
use style::values::generics::box_::VerticalAlign;
|
use style::values::generics::box_::VerticalAlign;
|
||||||
|
use style::values::generics::transform;
|
||||||
use text;
|
use text;
|
||||||
use text::TextRunScanner;
|
use text::TextRunScanner;
|
||||||
use webrender_api;
|
use webrender_api;
|
||||||
|
@ -2895,7 +2895,7 @@ 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<Transform3D<f32>> {
|
||||||
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))?;
|
let transform = 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 =
|
||||||
|
@ -2939,7 +2939,7 @@ impl Fragment {
|
||||||
-perspective_origin.y,
|
-perspective_origin.y,
|
||||||
0.0);
|
0.0);
|
||||||
|
|
||||||
let perspective_matrix = TransformList::create_perspective_matrix(length.px());
|
let perspective_matrix = 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))
|
||||||
}
|
}
|
||||||
|
|
|
@ -1577,6 +1577,8 @@ extern "C" {
|
||||||
pub fn Servo_ComputeColor ( set : RawServoStyleSetBorrowedOrNull , current_color : nscolor , value : * const nsAString , result_color : * mut nscolor , ) -> bool ;
|
pub fn Servo_ComputeColor ( set : RawServoStyleSetBorrowedOrNull , current_color : nscolor , value : * const nsAString , result_color : * mut nscolor , ) -> bool ;
|
||||||
} extern "C" {
|
} extern "C" {
|
||||||
pub fn Servo_ParseIntersectionObserverRootMargin ( value : * const nsAString , result : * mut nsCSSRect , ) -> bool ;
|
pub fn Servo_ParseIntersectionObserverRootMargin ( value : * const nsAString , result : * mut nsCSSRect , ) -> bool ;
|
||||||
|
} extern "C" {
|
||||||
|
pub fn Servo_ParseTransformIntoMatrix ( value : * const nsAString , contains_3d_transform : * mut bool , result : * mut RawGeckoGfxMatrix4x4 , ) -> bool ;
|
||||||
} extern "C" {
|
} extern "C" {
|
||||||
pub fn Gecko_CreateCSSErrorReporter ( sheet : * mut ServoStyleSheet , loader : * mut Loader , uri : * mut nsIURI , ) -> * mut ErrorReporter ;
|
pub fn Gecko_CreateCSSErrorReporter ( sheet : * mut ServoStyleSheet , loader : * mut Loader , uri : * mut nsIURI , ) -> * mut ErrorReporter ;
|
||||||
} extern "C" {
|
} extern "C" {
|
||||||
|
|
|
@ -45,7 +45,7 @@ use values::computed::ToComputedValue;
|
||||||
use values::computed::transform::{DirectionVector, Matrix, Matrix3D};
|
use values::computed::transform::{DirectionVector, Matrix, Matrix3D};
|
||||||
use values::computed::transform::TransformOperation as ComputedTransformOperation;
|
use values::computed::transform::TransformOperation as ComputedTransformOperation;
|
||||||
use values::computed::transform::Transform as ComputedTransform;
|
use values::computed::transform::Transform as ComputedTransform;
|
||||||
use values::generics::transform::{Transform, TransformOperation};
|
use values::generics::transform::{self, Transform, TransformOperation};
|
||||||
use values::distance::{ComputeSquaredDistance, SquaredDistance};
|
use values::distance::{ComputeSquaredDistance, SquaredDistance};
|
||||||
#[cfg(feature = "gecko")] use values::generics::FontSettings as GenericFontSettings;
|
#[cfg(feature = "gecko")] use values::generics::FontSettings as GenericFontSettings;
|
||||||
#[cfg(feature = "gecko")] use values::generics::FontSettingTag as GenericFontSettingTag;
|
#[cfg(feature = "gecko")] use values::generics::FontSettingTag as GenericFontSettingTag;
|
||||||
|
@ -1147,10 +1147,8 @@ impl Animate for ComputedTransformOperation {
|
||||||
&TransformOperation::Rotate3D(fx, fy, fz, fa),
|
&TransformOperation::Rotate3D(fx, fy, fz, fa),
|
||||||
&TransformOperation::Rotate3D(tx, ty, tz, ta),
|
&TransformOperation::Rotate3D(tx, ty, tz, ta),
|
||||||
) => {
|
) => {
|
||||||
let (fx, fy, fz, fa) =
|
let (fx, fy, fz, fa) = transform::get_normalized_vector_and_angle(fx, fy, fz, fa);
|
||||||
ComputedTransform::get_normalized_vector_and_angle(fx, fy, fz, fa);
|
let (tx, ty, tz, ta) = transform::get_normalized_vector_and_angle(tx, ty, tz, ta);
|
||||||
let (tx, ty, tz, ta) =
|
|
||||||
ComputedTransform::get_normalized_vector_and_angle(tx, ty, tz, ta);
|
|
||||||
if (fx, fy, fz) == (tx, ty, tz) {
|
if (fx, fy, fz) == (tx, ty, tz) {
|
||||||
let ia = fa.animate(&ta, procedure)?;
|
let ia = fa.animate(&ta, procedure)?;
|
||||||
Ok(TransformOperation::Rotate3D(fx, fy, fz, ia))
|
Ok(TransformOperation::Rotate3D(fx, fy, fz, ia))
|
||||||
|
@ -2416,9 +2414,9 @@ impl ComputeSquaredDistance for ComputedTransformOperation {
|
||||||
&TransformOperation::Rotate3D(tx, ty, tz, ta),
|
&TransformOperation::Rotate3D(tx, ty, tz, ta),
|
||||||
) => {
|
) => {
|
||||||
let (fx, fy, fz, angle1) =
|
let (fx, fy, fz, angle1) =
|
||||||
ComputedTransform::get_normalized_vector_and_angle(fx, fy, fz, fa);
|
transform::get_normalized_vector_and_angle(fx, fy, fz, fa);
|
||||||
let (tx, ty, tz, angle2) =
|
let (tx, ty, tz, angle2) =
|
||||||
ComputedTransform::get_normalized_vector_and_angle(tx, ty, tz, ta);
|
transform::get_normalized_vector_and_angle(tx, ty, tz, ta);
|
||||||
if (fx, fy, fz) == (tx, ty, tz) {
|
if (fx, fy, fz) == (tx, ty, tz) {
|
||||||
angle1.compute_squared_distance(&angle2)
|
angle1.compute_squared_distance(&angle2)
|
||||||
} else {
|
} else {
|
||||||
|
@ -2509,8 +2507,8 @@ impl ComputeSquaredDistance for ComputedTransform {
|
||||||
// Roll back to matrix interpolation if there is any Err(()) in the transform lists, such
|
// Roll back to matrix interpolation if there is any Err(()) in the transform lists, such
|
||||||
// as mismatched transform functions.
|
// as mismatched transform functions.
|
||||||
if let Err(_) = squared_dist {
|
if let Err(_) = squared_dist {
|
||||||
let matrix1: Matrix3D = self.to_transform_3d_matrix(None).ok_or(())?.into();
|
let matrix1: Matrix3D = self.to_transform_3d_matrix(None)?.0.into();
|
||||||
let matrix2: Matrix3D = other.to_transform_3d_matrix(None).ok_or(())?.into();
|
let matrix2: Matrix3D = other.to_transform_3d_matrix(None)?.0.into();
|
||||||
return matrix1.compute_squared_distance(&matrix2);
|
return matrix1.compute_squared_distance(&matrix2);
|
||||||
}
|
}
|
||||||
squared_dist
|
squared_dist
|
||||||
|
|
|
@ -4,9 +4,10 @@
|
||||||
|
|
||||||
//! Computed angles.
|
//! Computed angles.
|
||||||
|
|
||||||
use euclid::Radians;
|
use num_traits::Zero;
|
||||||
use std::{f32, f64};
|
use std::{f32, f64};
|
||||||
use std::f64::consts::PI;
|
use std::f64::consts::PI;
|
||||||
|
use std::ops::Add;
|
||||||
use values::CSSFloat;
|
use values::CSSFloat;
|
||||||
use values::animated::{Animate, Procedure};
|
use values::animated::{Animate, Procedure};
|
||||||
use values::distance::{ComputeSquaredDistance, SquaredDistance};
|
use values::distance::{ComputeSquaredDistance, SquaredDistance};
|
||||||
|
@ -64,11 +65,6 @@ impl Angle {
|
||||||
radians.min(f64::MAX).max(f64::MIN)
|
radians.min(f64::MAX).max(f64::MIN)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns an angle that represents a rotation of zero radians.
|
|
||||||
pub fn zero() -> Self {
|
|
||||||
Self::from_radians(0.0)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <https://drafts.csswg.org/css-transitions/#animtype-number>
|
/// <https://drafts.csswg.org/css-transitions/#animtype-number>
|
||||||
#[inline]
|
#[inline]
|
||||||
fn animate_fallback(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
|
fn animate_fallback(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
|
||||||
|
@ -76,6 +72,45 @@ impl Angle {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl AsRef<Angle> for Angle {
|
||||||
|
#[inline]
|
||||||
|
fn as_ref(&self) -> &Self {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Add for Angle {
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn add(self, rhs: Self) -> Self {
|
||||||
|
match (self, rhs) {
|
||||||
|
(Angle::Deg(x), Angle::Deg(y)) => Angle::Deg(x + y),
|
||||||
|
(Angle::Grad(x), Angle::Grad(y)) => Angle::Grad(x + y),
|
||||||
|
(Angle::Turn(x), Angle::Turn(y)) => Angle::Turn(x + y),
|
||||||
|
(Angle::Rad(x), Angle::Rad(y)) => Angle::Rad(x + y),
|
||||||
|
_ => Angle::from_radians(self.radians() + rhs.radians()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Zero for Angle {
|
||||||
|
#[inline]
|
||||||
|
fn zero() -> Self {
|
||||||
|
Angle::from_radians(0.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn is_zero(&self) -> bool {
|
||||||
|
match *self {
|
||||||
|
Angle::Deg(val) |
|
||||||
|
Angle::Grad(val) |
|
||||||
|
Angle::Turn(val) |
|
||||||
|
Angle::Rad(val) => val == 0.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl ComputeSquaredDistance for Angle {
|
impl ComputeSquaredDistance for Angle {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
|
fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
|
||||||
|
@ -84,10 +119,3 @@ impl ComputeSquaredDistance for Angle {
|
||||||
self.radians64().compute_squared_distance(&other.radians64())
|
self.radians64().compute_squared_distance(&other.radians64())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Angle> for Radians<CSSFloat> {
|
|
||||||
#[inline]
|
|
||||||
fn from(a: Angle) -> Self {
|
|
||||||
Radians::new(a.radians())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -266,6 +266,24 @@ impl specified::CalcLengthOrPercentage {
|
||||||
pub fn to_computed_value_zoomed(&self, context: &Context, base_size: FontBaseSize) -> CalcLengthOrPercentage {
|
pub fn to_computed_value_zoomed(&self, context: &Context, base_size: FontBaseSize) -> CalcLengthOrPercentage {
|
||||||
self.to_computed_value_with_zoom(context, |abs| context.maybe_zoom_text(abs.into()).0, base_size)
|
self.to_computed_value_with_zoom(context, |abs| context.maybe_zoom_text(abs.into()).0, base_size)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Compute the value into pixel length as CSSFloat without context,
|
||||||
|
/// so it returns Err(()) if there is any non-absolute unit.
|
||||||
|
pub fn to_computed_pixel_length_without_context(&self) -> Result<CSSFloat, ()> {
|
||||||
|
if self.vw.is_some() || self.vh.is_some() || self.vmin.is_some() || self.vmax.is_some() ||
|
||||||
|
self.em.is_some() || self.ex.is_some() || self.ch.is_some() || self.rem.is_some() ||
|
||||||
|
self.percentage.is_some() {
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
|
||||||
|
match self.absolute {
|
||||||
|
Some(abs) => Ok(abs.to_px()),
|
||||||
|
None => {
|
||||||
|
debug_assert!(false, "Someone forgot to handle an unit here: {:?}", self);
|
||||||
|
Err(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToComputedValue for specified::CalcLengthOrPercentage {
|
impl ToComputedValue for specified::CalcLengthOrPercentage {
|
||||||
|
|
|
@ -4,14 +4,13 @@
|
||||||
|
|
||||||
//! Computed types for CSS values that are related to transformations.
|
//! Computed types for CSS values that are related to transformations.
|
||||||
|
|
||||||
use app_units::Au;
|
use euclid::{Transform3D, Vector3D};
|
||||||
use euclid::{Rect, Transform3D, Vector3D};
|
use num_traits::Zero;
|
||||||
use std::f32;
|
|
||||||
use super::{CSSFloat, Either};
|
use super::{CSSFloat, Either};
|
||||||
use values::animated::ToAnimatedZero;
|
use values::animated::ToAnimatedZero;
|
||||||
use values::computed::{Angle, Integer, Length, LengthOrPercentage, Number, Percentage};
|
use values::computed::{Angle, Integer, Length, LengthOrPercentage, Number, Percentage};
|
||||||
use values::computed::{LengthOrNumber, LengthOrPercentageOrNumber};
|
use values::computed::{LengthOrNumber, LengthOrPercentageOrNumber};
|
||||||
use values::generics::transform::{Matrix as GenericMatrix, Matrix3D as GenericMatrix3D};
|
use values::generics::transform::{self, Matrix as GenericMatrix, Matrix3D as GenericMatrix3D};
|
||||||
use values::generics::transform::{Transform as GenericTransform, TransformOperation as GenericTransformOperation};
|
use values::generics::transform::{Transform as GenericTransform, TransformOperation as GenericTransformOperation};
|
||||||
use values::generics::transform::TimingFunction as GenericTimingFunction;
|
use values::generics::transform::TimingFunction as GenericTimingFunction;
|
||||||
use values::generics::transform::TransformOrigin as GenericTransformOrigin;
|
use values::generics::transform::TransformOrigin as GenericTransformOrigin;
|
||||||
|
@ -146,18 +145,6 @@ impl PrefixedMatrix {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(rustfmt, rustfmt_skip)]
|
|
||||||
impl From<Matrix3D> for Transform3D<CSSFloat> {
|
|
||||||
#[inline]
|
|
||||||
fn from(m: Matrix3D) -> Self {
|
|
||||||
Transform3D::row_major(
|
|
||||||
m.m11, m.m12, m.m13, m.m14,
|
|
||||||
m.m21, m.m22, m.m23, m.m24,
|
|
||||||
m.m31, m.m32, m.m33, m.m34,
|
|
||||||
m.m41, m.m42, m.m43, m.m44)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg_attr(rustfmt, rustfmt_skip)]
|
#[cfg_attr(rustfmt, rustfmt_skip)]
|
||||||
impl From<Transform3D<CSSFloat>> for Matrix3D {
|
impl From<Transform3D<CSSFloat>> for Matrix3D {
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -171,18 +158,6 @@ impl From<Transform3D<CSSFloat>> for Matrix3D {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(rustfmt, rustfmt_skip)]
|
|
||||||
impl From<Matrix> for Transform3D<CSSFloat> {
|
|
||||||
#[inline]
|
|
||||||
fn from(m: Matrix) -> Self {
|
|
||||||
Transform3D::row_major(
|
|
||||||
m.a, m.b, 0.0, 0.0,
|
|
||||||
m.c, m.d, 0.0, 0.0,
|
|
||||||
0.0, 0.0, 1.0, 0.0,
|
|
||||||
m.e, m.f, 0.0, 1.0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TransformOperation {
|
impl TransformOperation {
|
||||||
/// Convert to a Translate3D.
|
/// Convert to a Translate3D.
|
||||||
///
|
///
|
||||||
|
@ -280,7 +255,7 @@ impl ToAnimatedZero for TransformOperation {
|
||||||
GenericTransformOperation::ScaleY(..) => Ok(GenericTransformOperation::ScaleY(1.0)),
|
GenericTransformOperation::ScaleY(..) => Ok(GenericTransformOperation::ScaleY(1.0)),
|
||||||
GenericTransformOperation::ScaleZ(..) => Ok(GenericTransformOperation::ScaleZ(1.0)),
|
GenericTransformOperation::ScaleZ(..) => Ok(GenericTransformOperation::ScaleZ(1.0)),
|
||||||
GenericTransformOperation::Rotate3D(x, y, z, a) => {
|
GenericTransformOperation::Rotate3D(x, y, z, a) => {
|
||||||
let (x, y, z, _) = Transform::get_normalized_vector_and_angle(x, y, z, a);
|
let (x, y, z, _) = transform::get_normalized_vector_and_angle(x, y, z, a);
|
||||||
Ok(GenericTransformOperation::Rotate3D(x, y, z, Angle::zero()))
|
Ok(GenericTransformOperation::Rotate3D(x, y, z, Angle::zero()))
|
||||||
},
|
},
|
||||||
GenericTransformOperation::RotateX(_) => Ok(GenericTransformOperation::RotateX(Angle::zero())),
|
GenericTransformOperation::RotateX(_) => Ok(GenericTransformOperation::RotateX(Angle::zero())),
|
||||||
|
@ -318,174 +293,3 @@ impl ToAnimatedZero for Transform {
|
||||||
.collect::<Result<Vec<_>, _>>()?))
|
.collect::<Result<Vec<_>, _>>()?))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Transform {
|
|
||||||
/// Return the equivalent 3d matrix of this transform list.
|
|
||||||
/// If |reference_box| is None, we will drop the percent part from translate because
|
|
||||||
/// we can resolve it without the layout info.
|
|
||||||
pub fn to_transform_3d_matrix(&self, reference_box: Option<&Rect<Au>>) -> Option<Transform3D<CSSFloat>> {
|
|
||||||
let mut transform = Transform3D::identity();
|
|
||||||
let list = &self.0;
|
|
||||||
if list.len() == 0 {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
let extract_pixel_length = |lop: &LengthOrPercentage| match *lop {
|
|
||||||
LengthOrPercentage::Length(px) => px.px(),
|
|
||||||
LengthOrPercentage::Percentage(_) => 0.,
|
|
||||||
LengthOrPercentage::Calc(calc) => calc.length().px(),
|
|
||||||
};
|
|
||||||
|
|
||||||
for operation in list {
|
|
||||||
let matrix = match *operation {
|
|
||||||
GenericTransformOperation::Rotate3D(ax, ay, az, theta) => {
|
|
||||||
let theta = Angle::from_radians(2.0f32 * f32::consts::PI - theta.radians());
|
|
||||||
let (ax, ay, az, theta) = Self::get_normalized_vector_and_angle(ax, ay, az, theta);
|
|
||||||
Transform3D::create_rotation(ax, ay, az, theta.into())
|
|
||||||
},
|
|
||||||
GenericTransformOperation::RotateX(theta) => {
|
|
||||||
let theta = Angle::from_radians(2.0f32 * f32::consts::PI - theta.radians());
|
|
||||||
Transform3D::create_rotation(1., 0., 0., theta.into())
|
|
||||||
},
|
|
||||||
GenericTransformOperation::RotateY(theta) => {
|
|
||||||
let theta = Angle::from_radians(2.0f32 * f32::consts::PI - theta.radians());
|
|
||||||
Transform3D::create_rotation(0., 1., 0., theta.into())
|
|
||||||
},
|
|
||||||
GenericTransformOperation::RotateZ(theta) |
|
|
||||||
GenericTransformOperation::Rotate(theta) => {
|
|
||||||
let theta = Angle::from_radians(2.0f32 * f32::consts::PI - theta.radians());
|
|
||||||
Transform3D::create_rotation(0., 0., 1., theta.into())
|
|
||||||
},
|
|
||||||
GenericTransformOperation::Perspective(d) => Self::create_perspective_matrix(d.px()),
|
|
||||||
GenericTransformOperation::Scale3D(sx, sy, sz) => Transform3D::create_scale(sx, sy, sz),
|
|
||||||
GenericTransformOperation::Scale(sx, sy) => Transform3D::create_scale(sx, sy.unwrap_or(sx), 1.),
|
|
||||||
GenericTransformOperation::ScaleX(s) => Transform3D::create_scale(s, 1., 1.),
|
|
||||||
GenericTransformOperation::ScaleY(s) => Transform3D::create_scale(1., s, 1.),
|
|
||||||
GenericTransformOperation::ScaleZ(s) => Transform3D::create_scale(1., 1., s),
|
|
||||||
GenericTransformOperation::Translate3D(tx, ty, tz) => {
|
|
||||||
let (tx, ty) = match reference_box {
|
|
||||||
Some(relative_border_box) => {
|
|
||||||
(
|
|
||||||
tx.to_pixel_length(relative_border_box.size.width).px(),
|
|
||||||
ty.to_pixel_length(relative_border_box.size.height).px(),
|
|
||||||
)
|
|
||||||
},
|
|
||||||
None => {
|
|
||||||
// If we don't have reference box, we cannot resolve the used value,
|
|
||||||
// so only retrieve the length part. This will be used for computing
|
|
||||||
// distance without any layout info.
|
|
||||||
(extract_pixel_length(&tx), extract_pixel_length(&ty))
|
|
||||||
},
|
|
||||||
};
|
|
||||||
let tz = tz.px();
|
|
||||||
Transform3D::create_translation(tx, ty, tz)
|
|
||||||
},
|
|
||||||
GenericTransformOperation::Translate(tx, Some(ty)) => {
|
|
||||||
let (tx, ty) = match reference_box {
|
|
||||||
Some(relative_border_box) => {
|
|
||||||
(
|
|
||||||
tx.to_pixel_length(relative_border_box.size.width).px(),
|
|
||||||
ty.to_pixel_length(relative_border_box.size.height).px(),
|
|
||||||
)
|
|
||||||
},
|
|
||||||
None => {
|
|
||||||
// If we don't have reference box, we cannot resolve the used value,
|
|
||||||
// so only retrieve the length part. This will be used for computing
|
|
||||||
// distance without any layout info.
|
|
||||||
(extract_pixel_length(&tx), extract_pixel_length(&ty))
|
|
||||||
},
|
|
||||||
};
|
|
||||||
Transform3D::create_translation(tx, ty, 0.)
|
|
||||||
},
|
|
||||||
GenericTransformOperation::TranslateX(t) |
|
|
||||||
GenericTransformOperation::Translate(t, None) => {
|
|
||||||
let t = match reference_box {
|
|
||||||
Some(relative_border_box) => t.to_pixel_length(relative_border_box.size.width).px(),
|
|
||||||
None => {
|
|
||||||
// If we don't have reference box, we cannot resolve the used value,
|
|
||||||
// so only retrieve the length part. This will be used for computing
|
|
||||||
// distance without any layout info.
|
|
||||||
extract_pixel_length(&t)
|
|
||||||
},
|
|
||||||
};
|
|
||||||
Transform3D::create_translation(t, 0., 0.)
|
|
||||||
},
|
|
||||||
GenericTransformOperation::TranslateY(t) => {
|
|
||||||
let t = match reference_box {
|
|
||||||
Some(relative_border_box) => t.to_pixel_length(relative_border_box.size.height).px(),
|
|
||||||
None => {
|
|
||||||
// If we don't have reference box, we cannot resolve the used value,
|
|
||||||
// so only retrieve the length part. This will be used for computing
|
|
||||||
// distance without any layout info.
|
|
||||||
extract_pixel_length(&t)
|
|
||||||
},
|
|
||||||
};
|
|
||||||
Transform3D::create_translation(0., t, 0.)
|
|
||||||
},
|
|
||||||
GenericTransformOperation::TranslateZ(z) => Transform3D::create_translation(0., 0., z.px()),
|
|
||||||
GenericTransformOperation::Skew(theta_x, theta_y) => {
|
|
||||||
Transform3D::create_skew(theta_x.into(), theta_y.unwrap_or(Angle::zero()).into())
|
|
||||||
},
|
|
||||||
GenericTransformOperation::SkewX(theta) => Transform3D::create_skew(theta.into(), Angle::zero().into()),
|
|
||||||
GenericTransformOperation::SkewY(theta) => Transform3D::create_skew(Angle::zero().into(), theta.into()),
|
|
||||||
GenericTransformOperation::Matrix3D(m) => m.into(),
|
|
||||||
GenericTransformOperation::Matrix(m) => m.into(),
|
|
||||||
GenericTransformOperation::PrefixedMatrix3D(_) |
|
|
||||||
GenericTransformOperation::PrefixedMatrix(_) => {
|
|
||||||
// `-moz-transform` is not implemented in Servo yet.
|
|
||||||
unreachable!()
|
|
||||||
},
|
|
||||||
GenericTransformOperation::InterpolateMatrix {
|
|
||||||
..
|
|
||||||
} |
|
|
||||||
GenericTransformOperation::AccumulateMatrix {
|
|
||||||
..
|
|
||||||
} => {
|
|
||||||
// TODO: Convert InterpolateMatrix/AccmulateMatrix into a valid Transform3D by
|
|
||||||
// the reference box and do interpolation on these two Transform3D matrices.
|
|
||||||
// Both Gecko and Servo don't support this for computing distance, and Servo
|
|
||||||
// doesn't support animations on InterpolateMatrix/AccumulateMatrix, so
|
|
||||||
// return None.
|
|
||||||
return None;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
transform = transform.pre_mul(&matrix);
|
|
||||||
}
|
|
||||||
|
|
||||||
Some(transform)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Return the transform matrix from a perspective length.
|
|
||||||
#[inline]
|
|
||||||
pub fn create_perspective_matrix(d: CSSFloat) -> Transform3D<f32> {
|
|
||||||
// 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.
|
|
||||||
if d <= 0.0 {
|
|
||||||
Transform3D::identity()
|
|
||||||
} else {
|
|
||||||
Transform3D::create_perspective(d)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Return the normalized direction vector and its angle for Rotate3D.
|
|
||||||
pub fn get_normalized_vector_and_angle(x: f32, y: f32, z: f32, angle: Angle) -> (f32, f32, f32, Angle) {
|
|
||||||
use euclid::approxeq::ApproxEq;
|
|
||||||
use euclid::num::Zero;
|
|
||||||
let vector = DirectionVector::new(x, y, z);
|
|
||||||
if vector.square_length().approx_eq(&f32::zero()) {
|
|
||||||
// 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 (i.e. rotate3d(0, 0, 1, 0)).
|
|
||||||
(0., 0., 1., Angle::zero())
|
|
||||||
} else {
|
|
||||||
let vector = vector.normalize();
|
|
||||||
(vector.x, vector.y, vector.z, angle)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -4,9 +4,16 @@
|
||||||
|
|
||||||
//! Generic types for CSS values that are related to transformations.
|
//! Generic types for CSS values that are related to transformations.
|
||||||
|
|
||||||
|
use app_units::Au;
|
||||||
|
use euclid::{Radians, Rect, Transform3D};
|
||||||
|
use num_traits::Zero;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use style_traits::ToCss;
|
use style_traits::ToCss;
|
||||||
use values::{computed, CSSFloat};
|
use values::{computed, CSSFloat};
|
||||||
|
use values::computed::length::Length as ComputedLength;
|
||||||
|
use values::computed::length::LengthOrPercentage as ComputedLengthOrPercentage;
|
||||||
|
use values::specified::length::Length as SpecifiedLength;
|
||||||
|
use values::specified::length::LengthOrPercentage as SpecifiedLengthOrPercentage;
|
||||||
|
|
||||||
/// A generic 2D transformation matrix.
|
/// A generic 2D transformation matrix.
|
||||||
#[allow(missing_docs)]
|
#[allow(missing_docs)]
|
||||||
|
@ -31,6 +38,32 @@ pub struct Matrix3D<T, U = T, V = T> {
|
||||||
pub m41: U, pub m42: U, pub m43: V, pub m44: T,
|
pub m41: U, pub m42: U, pub m43: V, pub m44: T,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(rustfmt, rustfmt_skip)]
|
||||||
|
impl<T: Into<f64>> From<Matrix<T>> for Transform3D<f64> {
|
||||||
|
#[inline]
|
||||||
|
fn from(m: Matrix<T>) -> Self {
|
||||||
|
Transform3D::row_major(
|
||||||
|
m.a.into(), m.b.into(), 0.0, 0.0,
|
||||||
|
m.c.into(), m.d.into(), 0.0, 0.0,
|
||||||
|
0.0, 0.0, 1.0, 0.0,
|
||||||
|
m.e.into(), m.f.into(), 0.0, 1.0,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(rustfmt, rustfmt_skip)]
|
||||||
|
impl<T: Into<f64>> From<Matrix3D<T>> for Transform3D<f64> {
|
||||||
|
#[inline]
|
||||||
|
fn from(m: Matrix3D<T>) -> Self {
|
||||||
|
Transform3D::row_major(
|
||||||
|
m.m11.into(), m.m12.into(), m.m13.into(), m.m14.into(),
|
||||||
|
m.m21.into(), m.m22.into(), m.m23.into(), m.m24.into(),
|
||||||
|
m.m31.into(), m.m32.into(), m.m33.into(), m.m34.into(),
|
||||||
|
m.m41.into(), m.m42.into(), m.m43.into(), m.m44.into(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// A generic transform origin.
|
/// A generic transform origin.
|
||||||
#[derive(Animate, Clone, ComputeSquaredDistance, Copy, Debug)]
|
#[derive(Animate, Clone, ComputeSquaredDistance, Copy, Debug)]
|
||||||
#[derive(MallocSizeOf, PartialEq, ToAnimatedZero, ToComputedValue, ToCss)]
|
#[derive(MallocSizeOf, PartialEq, ToAnimatedZero, ToComputedValue, ToCss)]
|
||||||
|
@ -158,8 +191,7 @@ impl TimingKeyword {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, MallocSizeOf, PartialEq)]
|
#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToComputedValue)]
|
||||||
#[derive(ToComputedValue)]
|
|
||||||
/// A single operation in the list of a `transform` value
|
/// A single operation in the list of a `transform` value
|
||||||
pub enum TransformOperation<Angle, Number, Length, Integer, LengthOrNumber, LengthOrPercentage, LoPoNumber> {
|
pub enum TransformOperation<Angle, Number, Length, Integer, LengthOrNumber, LengthOrPercentage, LoPoNumber> {
|
||||||
/// Represents a 2D 2x3 matrix.
|
/// Represents a 2D 2x3 matrix.
|
||||||
|
@ -319,6 +351,194 @@ impl<Angle, Number, Length, Integer, LengthOrNumber, LengthOrPercentage, LoPoNum
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Convert a length type into the absolute lengths.
|
||||||
|
pub trait ToAbsoluteLength {
|
||||||
|
/// Returns the absolute length as pixel value.
|
||||||
|
fn to_pixel_length(&self, containing_len: Option<Au>) -> Result<CSSFloat, ()>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToAbsoluteLength for SpecifiedLength {
|
||||||
|
// This returns Err(()) if there is any relative length or percentage. We use this when
|
||||||
|
// parsing a transform list of DOMMatrix because we want to return a DOM Exception
|
||||||
|
// if there is relative length.
|
||||||
|
#[inline]
|
||||||
|
fn to_pixel_length(&self, _containing_len: Option<Au>) -> Result<CSSFloat, ()> {
|
||||||
|
match *self {
|
||||||
|
SpecifiedLength::NoCalc(len) => len.to_computed_pixel_length_without_context(),
|
||||||
|
SpecifiedLength::Calc(ref calc) => calc.to_computed_pixel_length_without_context(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToAbsoluteLength for SpecifiedLengthOrPercentage {
|
||||||
|
// This returns Err(()) if there is any relative length or percentage. We use this when
|
||||||
|
// parsing a transform list of DOMMatrix because we want to return a DOM Exception
|
||||||
|
// if there is relative length.
|
||||||
|
#[inline]
|
||||||
|
fn to_pixel_length(&self, _containing_len: Option<Au>) -> Result<CSSFloat, ()> {
|
||||||
|
use self::SpecifiedLengthOrPercentage::*;
|
||||||
|
match *self {
|
||||||
|
Length(len) => len.to_computed_pixel_length_without_context(),
|
||||||
|
Calc(ref calc) => calc.to_computed_pixel_length_without_context(),
|
||||||
|
_ => Err(()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToAbsoluteLength for ComputedLength {
|
||||||
|
#[inline]
|
||||||
|
fn to_pixel_length(&self, _containing_len: Option<Au>) -> Result<CSSFloat, ()> {
|
||||||
|
Ok(self.px())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToAbsoluteLength for ComputedLengthOrPercentage {
|
||||||
|
#[inline]
|
||||||
|
fn to_pixel_length(&self, containing_len: Option<Au>) -> Result<CSSFloat, ()> {
|
||||||
|
let extract_pixel_length = |lop: &ComputedLengthOrPercentage| match *lop {
|
||||||
|
ComputedLengthOrPercentage::Length(px) => px.px(),
|
||||||
|
ComputedLengthOrPercentage::Percentage(_) => 0.,
|
||||||
|
ComputedLengthOrPercentage::Calc(calc) => calc.length().px(),
|
||||||
|
};
|
||||||
|
|
||||||
|
match containing_len {
|
||||||
|
Some(relative_len) => Ok(self.to_pixel_length(relative_len).px()),
|
||||||
|
// If we don't have reference box, we cannot resolve the used value,
|
||||||
|
// so only retrieve the length part. This will be used for computing
|
||||||
|
// distance without any layout info.
|
||||||
|
None => Ok(extract_pixel_length(self))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Support the conversion to a 3d matrix.
|
||||||
|
pub trait ToMatrix {
|
||||||
|
/// Check if it is a 3d transform function.
|
||||||
|
fn is_3d(&self) -> bool;
|
||||||
|
|
||||||
|
/// Return the equivalent 3d matrix.
|
||||||
|
fn to_3d_matrix(&self, reference_box: Option<&Rect<Au>>) -> Result<Transform3D<f64>, ()>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Angle, Number, Length, Integer, LoN, LoP, LoPoNumber> ToMatrix
|
||||||
|
for TransformOperation<Angle, Number, Length, Integer, LoN, LoP, LoPoNumber>
|
||||||
|
where
|
||||||
|
Angle: Copy + AsRef<computed::angle::Angle>,
|
||||||
|
Number: Copy + Into<f32> + Into<f64>,
|
||||||
|
Length: ToAbsoluteLength,
|
||||||
|
LoP: ToAbsoluteLength,
|
||||||
|
{
|
||||||
|
#[inline]
|
||||||
|
fn is_3d(&self) -> bool {
|
||||||
|
use self::TransformOperation::*;
|
||||||
|
match *self {
|
||||||
|
Translate3D(..) | TranslateZ(..) |
|
||||||
|
Rotate3D(..) | RotateX(..) | RotateY(..) | RotateZ(..) |
|
||||||
|
Scale3D(..) | ScaleZ(..) |
|
||||||
|
Perspective(..) | Matrix3D(..) | PrefixedMatrix3D(..) => true,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// If |reference_box| is None, we will drop the percent part from translate because
|
||||||
|
/// we cannot resolve it without the layout info, for computed TransformOperation.
|
||||||
|
/// However, for specified TransformOperation, we will return Err(()) if there is any relative
|
||||||
|
/// lengths because the only caller, DOMMatrix, doesn't accept relative lengths.
|
||||||
|
#[inline]
|
||||||
|
fn to_3d_matrix(&self, reference_box: Option<&Rect<Au>>) -> Result<Transform3D<f64>, ()> {
|
||||||
|
use self::TransformOperation::*;
|
||||||
|
use std::f64;
|
||||||
|
|
||||||
|
const TWO_PI: f64 = 2.0f64 * f64::consts::PI;
|
||||||
|
let reference_width = reference_box.map(|v| v.size.width);
|
||||||
|
let reference_height = reference_box.map(|v| v.size.height);
|
||||||
|
let matrix = match *self {
|
||||||
|
Rotate3D(ax, ay, az, theta) => {
|
||||||
|
let theta = TWO_PI - theta.as_ref().radians64();
|
||||||
|
let (ax, ay, az, theta) =
|
||||||
|
get_normalized_vector_and_angle(ax.into(), ay.into(), az.into(), theta);
|
||||||
|
Transform3D::create_rotation(ax as f64, ay as f64, az as f64, Radians::new(theta))
|
||||||
|
},
|
||||||
|
RotateX(theta) => {
|
||||||
|
let theta = Radians::new(TWO_PI - theta.as_ref().radians64());
|
||||||
|
Transform3D::create_rotation(1., 0., 0., theta)
|
||||||
|
},
|
||||||
|
RotateY(theta) => {
|
||||||
|
let theta = Radians::new(TWO_PI - theta.as_ref().radians64());
|
||||||
|
Transform3D::create_rotation(0., 1., 0., theta)
|
||||||
|
},
|
||||||
|
RotateZ(theta) | Rotate(theta) => {
|
||||||
|
let theta = Radians::new(TWO_PI - theta.as_ref().radians64());
|
||||||
|
Transform3D::create_rotation(0., 0., 1., theta)
|
||||||
|
},
|
||||||
|
Perspective(ref d) => {
|
||||||
|
let m = create_perspective_matrix(d.to_pixel_length(None)?);
|
||||||
|
m.cast().expect("Casting from f32 to f64 should be successful")
|
||||||
|
},
|
||||||
|
Scale3D(sx, sy, sz) => Transform3D::create_scale(sx.into(), sy.into(), sz.into()),
|
||||||
|
Scale(sx, sy) => Transform3D::create_scale(sx.into(), sy.unwrap_or(sx).into(), 1.),
|
||||||
|
ScaleX(s) => Transform3D::create_scale(s.into(), 1., 1.),
|
||||||
|
ScaleY(s) => Transform3D::create_scale(1., s.into(), 1.),
|
||||||
|
ScaleZ(s) => Transform3D::create_scale(1., 1., s.into()),
|
||||||
|
Translate3D(ref tx, ref ty, ref tz) => {
|
||||||
|
let tx = tx.to_pixel_length(reference_width)? as f64;
|
||||||
|
let ty = ty.to_pixel_length(reference_height)? as f64;
|
||||||
|
Transform3D::create_translation(tx, ty, tz.to_pixel_length(None)? as f64)
|
||||||
|
},
|
||||||
|
Translate(ref tx, Some(ref ty)) => {
|
||||||
|
let tx = tx.to_pixel_length(reference_width)? as f64;
|
||||||
|
let ty = ty.to_pixel_length(reference_height)? as f64;
|
||||||
|
Transform3D::create_translation(tx, ty, 0.)
|
||||||
|
},
|
||||||
|
TranslateX(ref t) | Translate(ref t, None) => {
|
||||||
|
let t = t.to_pixel_length(reference_width)? as f64;
|
||||||
|
Transform3D::create_translation(t, 0., 0.)
|
||||||
|
},
|
||||||
|
TranslateY(ref t) => {
|
||||||
|
let t = t.to_pixel_length(reference_height)? as f64;
|
||||||
|
Transform3D::create_translation(0., t, 0.)
|
||||||
|
},
|
||||||
|
TranslateZ(ref z) => {
|
||||||
|
Transform3D::create_translation(0., 0., z.to_pixel_length(None)? as f64)
|
||||||
|
},
|
||||||
|
Skew(theta_x, theta_y) => {
|
||||||
|
Transform3D::create_skew(
|
||||||
|
Radians::new(theta_x.as_ref().radians64()),
|
||||||
|
Radians::new(theta_y.map_or(0., |a| a.as_ref().radians64())),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
SkewX(theta) => {
|
||||||
|
Transform3D::create_skew(
|
||||||
|
Radians::new(theta.as_ref().radians64()),
|
||||||
|
Radians::new(0.),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
SkewY(theta) => {
|
||||||
|
Transform3D::create_skew(
|
||||||
|
Radians::new(0.),
|
||||||
|
Radians::new(theta.as_ref().radians64()),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
Matrix3D(m) => m.into(),
|
||||||
|
Matrix(m) => m.into(),
|
||||||
|
PrefixedMatrix3D(_) | PrefixedMatrix(_) => {
|
||||||
|
unreachable!("-moz-transform` is not implemented in Servo yet, and DOMMatrix \
|
||||||
|
doesn't support this")
|
||||||
|
},
|
||||||
|
InterpolateMatrix { .. } | AccumulateMatrix { .. } => {
|
||||||
|
// TODO: Convert InterpolateMatrix/AccumulateMatrix into a valid Transform3D by
|
||||||
|
// the reference box and do interpolation on these two Transform3D matrices.
|
||||||
|
// Both Gecko and Servo don't support this for computing distance, and Servo
|
||||||
|
// doesn't support animations on InterpolateMatrix/AccumulateMatrix, so
|
||||||
|
// return an identity matrix.
|
||||||
|
// Note: DOMMatrix doesn't go into this arm.
|
||||||
|
Transform3D::identity()
|
||||||
|
},
|
||||||
|
};
|
||||||
|
Ok(matrix)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg_attr(rustfmt, rustfmt_skip)]
|
#[cfg_attr(rustfmt, rustfmt_skip)]
|
||||||
impl<Angle: ToCss + Copy, Number: ToCss + Copy, Length: ToCss,
|
impl<Angle: ToCss + Copy, Number: ToCss + Copy, Length: ToCss,
|
||||||
Integer: ToCss + Copy, LengthOrNumber: ToCss, LengthOrPercentage: ToCss, LoPoNumber: ToCss>
|
Integer: ToCss + Copy, LengthOrNumber: ToCss, LengthOrPercentage: ToCss, LoPoNumber: ToCss>
|
||||||
|
@ -457,3 +677,79 @@ impl<T> Transform<T> {
|
||||||
Transform(vec![])
|
Transform(vec![])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T: ToMatrix> Transform<T> {
|
||||||
|
/// Return the equivalent 3d matrix of this transform list.
|
||||||
|
/// We return a pair: the first one is the transform matrix, and the second one
|
||||||
|
/// indicates if there is any 3d transform function in this transform list.
|
||||||
|
#[cfg_attr(rustfmt, rustfmt_skip)]
|
||||||
|
pub fn to_transform_3d_matrix(
|
||||||
|
&self,
|
||||||
|
reference_box: Option<&Rect<Au>>
|
||||||
|
) -> Result<(Transform3D<CSSFloat>, bool), ()> {
|
||||||
|
let cast_3d_transform = |m: Transform3D<f64>| -> Transform3D<CSSFloat> {
|
||||||
|
use std::{f32, f64};
|
||||||
|
let cast = |v: f64| { v.min(f32::MAX as f64).max(f32::MIN as f64) as f32 };
|
||||||
|
Transform3D::row_major(
|
||||||
|
cast(m.m11), cast(m.m12), cast(m.m13), cast(m.m14),
|
||||||
|
cast(m.m21), cast(m.m22), cast(m.m23), cast(m.m24),
|
||||||
|
cast(m.m31), cast(m.m32), cast(m.m33), cast(m.m34),
|
||||||
|
cast(m.m41), cast(m.m42), cast(m.m43), cast(m.m44),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
// We intentionally use Transform3D<f64> during computation to avoid error propagation
|
||||||
|
// because using f32 to compute triangle functions (e.g. in create_rotation()) is not
|
||||||
|
// accurate enough. In Gecko, we also use "double" to compute the triangle functions.
|
||||||
|
// Therefore, let's use Transform3D<f64> during matrix computation and cast it into f32
|
||||||
|
// in the end.
|
||||||
|
let mut transform = Transform3D::<f64>::identity();
|
||||||
|
let mut contain_3d = false;
|
||||||
|
|
||||||
|
for operation in &self.0 {
|
||||||
|
let matrix = operation.to_3d_matrix(reference_box)?;
|
||||||
|
contain_3d |= operation.is_3d();
|
||||||
|
transform = transform.pre_mul(&matrix);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok((cast_3d_transform(transform), contain_3d))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the transform matrix from a perspective length.
|
||||||
|
#[inline]
|
||||||
|
pub fn create_perspective_matrix(d: CSSFloat) -> Transform3D<CSSFloat> {
|
||||||
|
// 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.
|
||||||
|
if d <= 0.0 {
|
||||||
|
Transform3D::identity()
|
||||||
|
} else {
|
||||||
|
Transform3D::create_perspective(d)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the normalized direction vector and its angle for Rotate3D.
|
||||||
|
pub fn get_normalized_vector_and_angle<T: Zero>(
|
||||||
|
x: CSSFloat,
|
||||||
|
y: CSSFloat,
|
||||||
|
z: CSSFloat,
|
||||||
|
angle: T,
|
||||||
|
) -> (CSSFloat, CSSFloat, CSSFloat, T) {
|
||||||
|
use euclid::approxeq::ApproxEq;
|
||||||
|
use values::computed::transform::DirectionVector;
|
||||||
|
let vector = DirectionVector::new(x, y, z);
|
||||||
|
if vector.square_length().approx_eq(&f32::zero()) {
|
||||||
|
// 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 (i.e. rotate3d(0, 0, 1, 0)).
|
||||||
|
(0., 0., 1., T::zero())
|
||||||
|
} else {
|
||||||
|
let vector = vector.normalize();
|
||||||
|
(vector.x, vector.y, vector.z, angle)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -95,6 +95,13 @@ impl Angle {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl AsRef<ComputedAngle> for Angle {
|
||||||
|
#[inline]
|
||||||
|
fn as_ref(&self) -> &ComputedAngle {
|
||||||
|
&self.value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Parse for Angle {
|
impl Parse for Angle {
|
||||||
/// Parses an angle according to CSS-VALUES § 6.1.
|
/// Parses an angle according to CSS-VALUES § 6.1.
|
||||||
fn parse<'i, 't>(
|
fn parse<'i, 't>(
|
||||||
|
|
|
@ -63,6 +63,10 @@ pub enum CalcUnit {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A struct to hold a simplified `<length>` or `<percentage>` expression.
|
/// A struct to hold a simplified `<length>` or `<percentage>` expression.
|
||||||
|
///
|
||||||
|
/// In some cases, e.g. DOMMatrix, we support calc(), but reject all the relative lengths, and
|
||||||
|
/// to_computed_pixel_length_without_context() handles this case. Therefore, if you want to add a
|
||||||
|
/// new field, please make sure this function work properly.
|
||||||
#[derive(Clone, Copy, Debug, Default, MallocSizeOf, PartialEq)]
|
#[derive(Clone, Copy, Debug, Default, MallocSizeOf, PartialEq)]
|
||||||
#[allow(missing_docs)]
|
#[allow(missing_docs)]
|
||||||
pub struct CalcLengthOrPercentage {
|
pub struct CalcLengthOrPercentage {
|
||||||
|
|
|
@ -463,6 +463,15 @@ impl NoCalcLength {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get a px value without context.
|
||||||
|
#[inline]
|
||||||
|
pub fn to_computed_pixel_length_without_context(&self) -> Result<CSSFloat, ()> {
|
||||||
|
match *self {
|
||||||
|
NoCalcLength::Absolute(len) => Ok(len.to_px()),
|
||||||
|
_ => Err(()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Get an absolute length from a px value.
|
/// Get an absolute length from a px value.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn from_px(px_value: CSSFloat) -> NoCalcLength {
|
pub fn from_px(px_value: CSSFloat) -> NoCalcLength {
|
||||||
|
|
|
@ -270,6 +270,20 @@ impl ToCss for Number {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<Number> for f32 {
|
||||||
|
#[inline]
|
||||||
|
fn from(n: Number) -> Self {
|
||||||
|
n.get()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Number> for f64 {
|
||||||
|
#[inline]
|
||||||
|
fn from(n: Number) -> Self {
|
||||||
|
n.get() as f64
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// A Number which is >= 0.0.
|
/// A Number which is >= 0.0.
|
||||||
pub type NonNegativeNumber = NonNegative<Number>;
|
pub type NonNegativeNumber = NonNegative<Number>;
|
||||||
|
|
||||||
|
|
|
@ -4650,6 +4650,42 @@ pub extern "C" fn Servo_ParseIntersectionObserverRootMargin(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn Servo_ParseTransformIntoMatrix(
|
||||||
|
value: *const nsAString,
|
||||||
|
contain_3d: *mut bool,
|
||||||
|
result: *mut RawGeckoGfxMatrix4x4
|
||||||
|
) -> bool {
|
||||||
|
use style::properties::longhands::transform;
|
||||||
|
|
||||||
|
let string = unsafe { (*value).to_string() };
|
||||||
|
let mut input = ParserInput::new(&string);
|
||||||
|
let mut parser = Parser::new(&mut input);
|
||||||
|
let context = ParserContext::new(
|
||||||
|
Origin::Author,
|
||||||
|
unsafe { dummy_url_data() },
|
||||||
|
Some(CssRuleType::Style),
|
||||||
|
ParsingMode::DEFAULT,
|
||||||
|
QuirksMode::NoQuirks
|
||||||
|
);
|
||||||
|
|
||||||
|
let transform = match parser.parse_entirely(|t| transform::parse(&context, t)) {
|
||||||
|
Ok(t) => t,
|
||||||
|
Err(..) => return false,
|
||||||
|
};
|
||||||
|
|
||||||
|
let (m, is_3d) = match transform.to_transform_3d_matrix(None) {
|
||||||
|
Ok(result) => result,
|
||||||
|
Err(..) => return false,
|
||||||
|
};
|
||||||
|
|
||||||
|
let result = unsafe { result.as_mut() }.expect("not a valid matrix");
|
||||||
|
let contain_3d = unsafe { contain_3d.as_mut() }.expect("not a valid bool");
|
||||||
|
*result = m.to_row_major_array();
|
||||||
|
*contain_3d = is_3d;
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub unsafe extern "C" fn Servo_SourceSizeList_Parse(
|
pub unsafe extern "C" fn Servo_SourceSizeList_Parse(
|
||||||
value: *const nsACString,
|
value: *const nsACString,
|
||||||
|
|
|
@ -65189,11 +65189,11 @@
|
||||||
"support"
|
"support"
|
||||||
],
|
],
|
||||||
"css/transform_skew_a.html": [
|
"css/transform_skew_a.html": [
|
||||||
"8ae3384ece6fc2ebd537736e63723e12b974fff3",
|
"521496ff3fb82a34498d313c1d600a6ca271b1ed",
|
||||||
"reftest"
|
"reftest"
|
||||||
],
|
],
|
||||||
"css/transform_skew_ref.html": [
|
"css/transform_skew_ref.html": [
|
||||||
"6f5154aef6acf1428ac391f0d2dbb2e369ca930b",
|
"a941cd3a8c968494f292ddadd28de5b541ad71b2",
|
||||||
"support"
|
"support"
|
||||||
],
|
],
|
||||||
"css/transform_stacking_context_a.html": [
|
"css/transform_stacking_context_a.html": [
|
||||||
|
|
|
@ -1,4 +0,0 @@
|
||||||
[transform_skew_a.html]
|
|
||||||
type: reftest
|
|
||||||
expected:
|
|
||||||
if os == "linux": FAIL
|
|
|
@ -18,7 +18,7 @@ div>div {
|
||||||
}
|
}
|
||||||
|
|
||||||
.transformed1 {
|
.transformed1 {
|
||||||
transform: skewX(0.3rad);
|
transform: skewX(0.25rad);
|
||||||
}
|
}
|
||||||
|
|
||||||
.transformed2 {
|
.transformed2 {
|
||||||
|
@ -26,7 +26,7 @@ div>div {
|
||||||
}
|
}
|
||||||
|
|
||||||
.transformed3 {
|
.transformed3 {
|
||||||
transform: skew(0.3rad, 0.5rad);
|
transform: skew(0.25rad, 0.5rad);
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
|
|
|
@ -17,7 +17,7 @@ div>div {
|
||||||
}
|
}
|
||||||
|
|
||||||
.transformed1_ref {
|
.transformed1_ref {
|
||||||
transform: matrix(1, 0, 0.30933624961, 1, 0, 0);
|
transform: matrix(1, 0, 0.25534192122, 1, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
.transformed2_ref {
|
.transformed2_ref {
|
||||||
|
@ -25,7 +25,7 @@ div>div {
|
||||||
}
|
}
|
||||||
|
|
||||||
.transformed3_ref {
|
.transformed3_ref {
|
||||||
transform: matrix(1, 0.54630248984, 0.30933624961, 1, 0, 0);
|
transform: matrix(1, 0.54630248984, 0.25534192122, 1, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue