Bug 1488414 - Use decomposition to interpolate matched perspective transform operations. r=birtles a=pascalc
Looks like this produces sensible results for interpolation with 0, though I'm
not really convinced about the results from, let's say, 1px to 2000px in the
attached test-case, I would've expected a linear interpolation from that to go
through normal length interpolation.
css-transforms-1 says:
> Two transform functions with the same name and the same number of arguments
> are interpolated numerically without a former conversion. The calculated
> value will be of the same transform function type with the same number of
> arguments.
>
> Special rules apply to <matrix()>.
Which is what we do... I was going to file a spec issue but turns out that it's
already addressed in css-transforms-2:
https://drafts.csswg.org/css-transforms-2/#interpolation-of-transform-functions
Which says:
> The transform functions <matrix()>, matrix3d() and perspective() get
> converted into 4x4 matrices first and interpolated as defined in section
> Interpolation of Matrices afterwards.
Differential Revision:
https://phabricator.services.mozilla.com/D4942
--- a/servo/components/style/properties/helpers/animated_properties.mako.rs
+++ b/servo/components/style/properties/helpers/animated_properties.mako.rs
@@ -1291,19 +1291,37 @@ impl Animate for ComputedTransformOperat
Ok(TransformOperation::Rotate(
fa.animate(&ta, procedure)?
))
},
(
&TransformOperation::Perspective(ref fd),
&TransformOperation::Perspective(ref td),
) => {
- Ok(TransformOperation::Perspective(
- fd.animate(td, procedure)?
- ))
+ use values::computed::CSSPixelLength;
+ use values::generics::transform::create_perspective_matrix;
+
+ // From https://drafts.csswg.org/css-transforms-2/#interpolation-of-transform-functions:
+ //
+ // The transform functions matrix(), matrix3d() and
+ // perspective() get converted into 4x4 matrices first and
+ // interpolated as defined in section Interpolation of
+ // Matrices afterwards.
+ //
+ let from = create_perspective_matrix(fd.px());
+ let to = create_perspective_matrix(td.px());
+
+ let interpolated =
+ Matrix3D::from(from).animate(&Matrix3D::from(to), procedure)?;
+
+ let decomposed = decompose_3d_matrix(interpolated)?;
+ let perspective_z = decomposed.perspective.2;
+ let used_value =
+ if perspective_z == 0. { 0. } else { -1. / perspective_z };
+ Ok(TransformOperation::Perspective(CSSPixelLength::new(used_value)))
},
_ if self.is_translate() && other.is_translate() => {
self.to_translate_3d().animate(&other.to_translate_3d(), procedure)
}
_ if self.is_scale() && other.is_scale() => {
self.to_scale_3d().animate(&other.to_scale_3d(), procedure)
}
_ if self.is_rotate() && other.is_rotate() => {
--- a/servo/components/style/values/computed/transform.rs
+++ b/servo/components/style/values/computed/transform.rs
@@ -37,16 +37,17 @@ impl TransformOrigin {
LengthOrPercentage::Percentage(Percentage(0.5)),
Length::new(0.),
)
}
}
/// computed value of matrix3d()
pub type Matrix3D = generic::Matrix3D<Number>;
+
/// computed value of matrix()
pub type Matrix = generic::Matrix<Number>;
// we rustfmt_skip here because we want the matrices to look like
// matrices instead of being split across lines
#[cfg_attr(rustfmt, rustfmt_skip)]
impl Matrix3D {
#[inline]