Bug 1394284 - add fallback discrete procedure for transform animation. draft
authorJeremy Chen <jeremychen@mozilla.com>
Wed, 13 Sep 2017 17:39:07 +0800
changeset 668176 4495d60f82cdbae0d08f2bc2f8f914faa91c0a8d
parent 668025 47f7b6c64265bc7bdd22eef7ab71abc97cf3f8bf
child 668177 1f07882c634b4972a4469e2d68c521a8c098354b
push id80952
push userbmo:jeremychen@mozilla.com
push dateThu, 21 Sep 2017 07:49:09 +0000
bugs1394284
milestone57.0a1
Bug 1394284 - add fallback discrete procedure for transform animation. According to the spec, if one of the matrices for transform interpolation is non-invertible, the used animation function must fall-back to a discrete animation. However, in the current implementation, we always use an identity matrix as a fallback for the non-invertible matrix. Decompose2DMatrix and Decompose3DMatrix both return a boolean, but we just never use it. So, in this patch, we use the returned boolean from the matrix decomposition as a condition, and do the fallback discrete procedure for the non-invertible matrices case. MozReview-Commit-ID: E7i1a1MJOXN
layout/style/nsStyleTransformMatrix.cpp
--- a/layout/style/nsStyleTransformMatrix.cpp
+++ b/layout/style/nsStyleTransformMatrix.cpp
@@ -321,16 +321,23 @@ public:
 
     gfxQuaternion result = gfxQuaternion(scale * aTwo.x,
                                          scale * aTwo.y,
                                          scale * aTwo.z,
                                          cos(theta)) * aOne;
     return result.ToMatrix();
   }
 
+  static Matrix4x4 operateForFallback(const Matrix4x4& aMatrix1,
+                                      const Matrix4x4& aMatrix2,
+                                      double aProgress)
+  {
+    return aMatrix1;
+  }
+
   static Matrix4x4 operateByServo(const Matrix4x4& aMatrix1,
                                   const Matrix4x4& aMatrix2,
                                   double aCount)
   {
     Matrix4x4 result;
     Servo_MatrixTransform_Operate(MatrixTransformOperator::Accumulate,
                                   &aMatrix1.components,
                                   &aMatrix2.components,
@@ -364,16 +371,23 @@ public:
 
   static Matrix4x4 operateForRotate(const gfxQuaternion& aOne,
                                     const gfxQuaternion& aTwo,
                                     double aCoeff)
   {
     return aOne.Slerp(aTwo, aCoeff).ToMatrix();
   }
 
+  static Matrix4x4 operateForFallback(const Matrix4x4& aMatrix1,
+                                      const Matrix4x4& aMatrix2,
+                                      double aProgress)
+  {
+    return aProgress < 0.5 ? aMatrix1 : aMatrix2;
+  }
+
   static Matrix4x4 operateByServo(const Matrix4x4& aMatrix1,
                                   const Matrix4x4& aMatrix2,
                                   double aProgress)
   {
     Matrix4x4 result;
     Servo_MatrixTransform_Operate(MatrixTransformOperator::Interpolate,
                                   &aMatrix1.components,
                                   &aMatrix2.components,
@@ -393,36 +407,44 @@ public:
 template <typename Operator>
 static Matrix4x4
 OperateTransformMatrix(const Matrix4x4 &aMatrix1,
                        const Matrix4x4 &aMatrix2,
                        double aProgress)
 {
   // Decompose both matrices
 
-  // TODO: What do we do if one of these returns false (singular matrix)
   Point3D scale1(1, 1, 1), translate1;
   Point4D perspective1(0, 0, 0, 1);
   gfxQuaternion rotate1;
   nsStyleTransformMatrix::ShearArray shear1{0.0f, 0.0f, 0.0f};
 
   Point3D scale2(1, 1, 1), translate2;
   Point4D perspective2(0, 0, 0, 1);
   gfxQuaternion rotate2;
   nsStyleTransformMatrix::ShearArray shear2{0.0f, 0.0f, 0.0f};
 
+  // Check if both matrices are decomposable.
+  bool wasDecomposed;
   Matrix matrix2d1, matrix2d2;
   if (aMatrix1.Is2D(&matrix2d1) && aMatrix2.Is2D(&matrix2d2)) {
-    Decompose2DMatrix(matrix2d1, scale1, shear1, rotate1, translate1);
-    Decompose2DMatrix(matrix2d2, scale2, shear2, rotate2, translate2);
+    wasDecomposed =
+      Decompose2DMatrix(matrix2d1, scale1, shear1, rotate1, translate1) &&
+      Decompose2DMatrix(matrix2d2, scale2, shear2, rotate2, translate2);
   } else {
-    Decompose3DMatrix(aMatrix1, scale1, shear1,
-                      rotate1, translate1, perspective1);
-    Decompose3DMatrix(aMatrix2, scale2, shear2,
-                      rotate2, translate2, perspective2);
+    wasDecomposed =
+      Decompose3DMatrix(aMatrix1, scale1, shear1,
+                        rotate1, translate1, perspective1) &&
+      Decompose3DMatrix(aMatrix2, scale2, shear2,
+                        rotate2, translate2, perspective2);
+  }
+
+  // Fallback to discrete operation if one of the matrices is not decomposable.
+  if (!wasDecomposed) {
+    return Operator::operateForFallback(aMatrix1, aMatrix2, aProgress);
   }
 
   Matrix4x4 result;
 
   // Operate each of the pieces in response to |Operator|.
   Point4D perspective =
     Operator::operateForPerspective(perspective1, perspective2, aProgress);
   result.SetTransposedVector(3, perspective);