Bug 1304886 - Part 8: Add Accumulate operator. r?boris, derf draft
authorHiroyuki Ikezoe <hiikezoe@mozilla-japan.org>
Wed, 16 Nov 2016 20:32:33 +0900
changeset 439613 b639a35b96358bd00676f2d0111b9c155b8d2441
parent 439612 8a363012210a7e18f86415104f57fc839b16a95f
child 439614 c36e1b115b6bafaf7801bdc9f26efff2f25fbb1b
push id36059
push userhiikezoe@mozilla-japan.org
push dateWed, 16 Nov 2016 11:32:59 +0000
reviewersboris, derf
bugs1304886
milestone53.0a1
Bug 1304886 - Part 8: Add Accumulate operator. r?boris, derf MozReview-Commit-ID: Bzcn3IRSx6L
layout/base/nsDisplayList.cpp
layout/style/StyleAnimationValue.cpp
layout/style/nsCSSKeywordList.h
layout/style/nsStyleTransformMatrix.cpp
layout/style/nsStyleTransformMatrix.h
--- a/layout/base/nsDisplayList.cpp
+++ b/layout/base/nsDisplayList.cpp
@@ -338,16 +338,29 @@ static void AddTransformFunctions(nsCSSV
                                                          aContext,
                                                          aPresContext,
                                                          conditions,
                                                          aRefBox,
                                                          &dummy);
         aFunctions.AppendElement(TransformMatrix(matrix));
         break;
       }
+      case eCSSKeyword_accumulatematrix:
+      {
+        bool dummy;
+        Matrix4x4 matrix;
+        nsStyleTransformMatrix::ProcessAccumulateMatrix(matrix, array,
+                                                        aContext,
+                                                        aPresContext,
+                                                        conditions,
+                                                        aRefBox,
+                                                        &dummy);
+        aFunctions.AppendElement(TransformMatrix(matrix));
+        break;
+      }
       case eCSSKeyword_perspective:
       {
         aFunctions.AppendElement(Perspective(array->Item(1).GetFloatValue()));
         break;
       }
       default:
         NS_ERROR("Function not handled yet!");
     }
--- a/layout/style/StyleAnimationValue.cpp
+++ b/layout/style/StyleAnimationValue.cpp
@@ -127,16 +127,23 @@ ToPrimitive(nsCSSKeyword aKeyword)
     default:
       return aKeyword;
   }
 }
 
 static bool
 TransformFunctionsMatch(nsCSSKeyword func1, nsCSSKeyword func2)
 {
+  // Handle eCSSKeyword_accumulatematrix as different function to be calculated
+  // (decomposed and recomposed) them later.
+  if (func1 == eCSSKeyword_accumulatematrix ||
+      func2 == eCSSKeyword_accumulatematrix) {
+    return false;
+  }
+
   return ToPrimitive(func1) == ToPrimitive(func2);
 }
 
 static bool
 TransformFunctionListsMatch(const nsCSSValueList *list1,
                             const nsCSSValueList *list2)
 {
   const nsCSSValueList *item1 = list1, *item2 = list2;
@@ -168,16 +175,17 @@ AppendFunction(nsCSSKeyword aTransformFu
       break;
     case eCSSKeyword_matrix:
       nargs = 6;
       break;
     case eCSSKeyword_rotate3d:
       nargs = 4;
       break;
     case eCSSKeyword_interpolatematrix:
+    case eCSSKeyword_accumulatematrix:
     case eCSSKeyword_translate3d:
     case eCSSKeyword_scale3d:
       nargs = 3;
       break;
     case eCSSKeyword_translate:
     case eCSSKeyword_skew:
     case eCSSKeyword_scale:
       nargs = 2;
@@ -822,17 +830,18 @@ ComputeShapeDistance(nsCSSPropertyID aPr
     default:
       MOZ_ASSERT_UNREACHABLE("Unknown shape type");
   }
   return sqrt(squareDistance);
 }
 
 static nsCSSValueList*
 AddTransformLists(double aCoeff1, const nsCSSValueList* aList1,
-                  double aCoeff2, const nsCSSValueList* aList2);
+                  double aCoeff2, const nsCSSValueList* aList2,
+                  nsCSSKeyword aOperatorType = eCSSKeyword_interpolatematrix);
 
 static double
 ComputeTransform2DMatrixDistance(const Matrix& aMatrix1,
                                  const Matrix& aMatrix2)
 {
   Point3D scale1(1, 1, 1);
   Point3D translate1;
   gfxQuaternion rotate1;
@@ -1076,16 +1085,17 @@ ComputeTransformDistance(nsCSSValue::Arr
       MOZ_ASSERT(a2->Count() == 17, "unexpected count");
 
       distance = ComputeTransform3DMatrixDistance(
         nsStyleTransformMatrix::CSSValueArrayTo3DMatrix(a1),
         nsStyleTransformMatrix::CSSValueArrayTo3DMatrix(a2));
       break;
     }
     case eCSSKeyword_interpolatematrix:
+    case eCSSKeyword_accumulatematrix:
     default:
       MOZ_ASSERT_UNREACHABLE("Unsupported transform function");
       break;
   }
   return distance;
 }
 
 static double
@@ -1833,24 +1843,25 @@ StyleAnimationValue::AppendTransformFunc
   *aListTail = item;
   aListTail = &item->mNext;
 
   return arr.forget();
 }
 
 static nsCSSValueList*
 AddDifferentTransformLists(double aCoeff1, const nsCSSValueList* aList1,
-                           double aCoeff2, const nsCSSValueList* aList2)
+                           double aCoeff2, const nsCSSValueList* aList2,
+                           nsCSSKeyword aOperatorType)
 {
   nsAutoPtr<nsCSSValueList> result;
   nsCSSValueList **resultTail = getter_Transfers(result);
 
   RefPtr<nsCSSValue::Array> arr;
   arr =
-    StyleAnimationValue::AppendTransformFunction(eCSSKeyword_interpolatematrix,
+    StyleAnimationValue::AppendTransformFunction(aOperatorType,
                                                  resultTail);
 
   // FIXME: We should change the other transform code to also only
   // take a single progress value, as having values that don't
   // sum to 1 doesn't make sense for these.
   if (aList1 == aList2) {
     arr->Item(1).Reset();
   } else {
@@ -2262,17 +2273,18 @@ AddShapeFunction(nsCSSPropertyID aProper
   result->Item(1).SetIntValue(aArray1->Item(1).GetIntValue(),
                               eCSSUnit_Enumerated);
 
   return result.forget();
 }
 
 static nsCSSValueList*
 AddTransformLists(double aCoeff1, const nsCSSValueList* aList1,
-                  double aCoeff2, const nsCSSValueList* aList2)
+                  double aCoeff2, const nsCSSValueList* aList2,
+                  nsCSSKeyword aOperatorType)
 {
   nsAutoPtr<nsCSSValueList> result;
   nsCSSValueList **resultTail = getter_Transfers(result);
 
   do {
     RefPtr<nsCSSValue::Array> a1 = ToPrimitive(aList1->mValue.GetArrayValue()),
                                 a2 = ToPrimitive(aList2->mValue.GetArrayValue());
     MOZ_ASSERT(
@@ -2414,31 +2426,36 @@ AddTransformLists(double aCoeff1, const 
 
         // Construct temporary lists with only this item in them.
         nsCSSValueList tempList1, tempList2;
         tempList1.mValue = aList1->mValue;
         tempList2.mValue = aList2->mValue;
 
         if (aList1 == aList2) {
           *resultTail =
-            AddDifferentTransformLists(aCoeff1, &tempList1, aCoeff2, &tempList1);
+            AddDifferentTransformLists(aCoeff1, &tempList1,
+                                       aCoeff2, &tempList1,
+                                       aOperatorType);
         } else {
           *resultTail =
-            AddDifferentTransformLists(aCoeff1, &tempList1, aCoeff2, &tempList2);
+            AddDifferentTransformLists(aCoeff1, &tempList1,
+                                       aCoeff2, &tempList2,
+                                       aOperatorType);
         }
 
         // Now advance resultTail to point to the new tail slot.
         while (*resultTail) {
           resultTail = &(*resultTail)->mNext;
         }
 
         break;
       }
       default:
-        MOZ_ASSERT(false, "unknown transform function");
+        MOZ_ASSERT_UNREACHABLE(
+          "unknown transform function or accumulatematrix");
     }
 
     aList1 = aList1->mNext;
     aList2 = aList2->mNext;
   } while (aList1);
   MOZ_ASSERT(!aList2, "list length mismatch");
   MOZ_ASSERT(!*resultTail,
              "resultTail isn't pointing to the tail");
@@ -2937,17 +2954,19 @@ StyleAnimationValue::AddWeighted(nsCSSPr
           result = AddTransformLists(0, list2, aCoeff2, list2);
         }
       } else {
         if (list2->mValue.GetUnit() == eCSSUnit_None) {
           result = AddTransformLists(0, list1, aCoeff1, list1);
         } else if (TransformFunctionListsMatch(list1, list2)) {
           result = AddTransformLists(aCoeff1, list1, aCoeff2, list2);
         } else {
-          result = AddDifferentTransformLists(aCoeff1, list1, aCoeff2, list2);
+          result = AddDifferentTransformLists(aCoeff1, list1,
+                                              aCoeff2, list2,
+                                              eCSSKeyword_interpolatematrix);
         }
       }
 
       aResultValue.SetTransformValue(new nsCSSValueSharedList(result.forget()));
       return true;
     }
     case eUnit_BackgroundPositionCoord: {
       const nsCSSValueList *position1 = aValue1.GetCSSValueListValue();
--- a/layout/style/nsCSSKeywordList.h
+++ b/layout/style/nsCSSKeywordList.h
@@ -330,16 +330,17 @@ CSS_KEY(inline-end, inline_end)
 CSS_KEY(inline-flex, inline_flex)
 CSS_KEY(inline-grid, inline_grid)
 CSS_KEY(inline-start, inline_start)
 CSS_KEY(inline-table, inline_table)
 CSS_KEY(inset, inset)
 CSS_KEY(inside, inside)
 // CSS_KEY(inter-character, inter_character) // TODO see bug 1055672
 CSS_KEY(interpolatematrix, interpolatematrix)
+CSS_KEY(accumulatematrix, accumulatematrix)
 CSS_KEY(intersect, intersect)
 CSS_KEY(isolate, isolate)
 CSS_KEY(isolate-override, isolate_override)
 CSS_KEY(invert, invert)
 CSS_KEY(italic, italic)
 CSS_KEY(japanese-formal, japanese_formal)
 CSS_KEY(japanese-informal, japanese_informal)
 CSS_KEY(jis78, jis78)
--- a/layout/style/nsStyleTransformMatrix.cpp
+++ b/layout/style/nsStyleTransformMatrix.cpp
@@ -259,26 +259,85 @@ ProcessMatrix3D(Matrix4x4& aMatrix,
                                   &aRefBox, &TransformReferenceBox::Height);
   temp._43 = ProcessTranslatePart(aData->Item(15),
                                   aContext, aPresContext, aConditions,
                                   nullptr);
 
   aMatrix = temp * aMatrix;
 }
 
+// For accumulation for transform functions, |aOne| corresponds to |aB| and
+// |aTwo| corresponds to |aA| for StyleAnimationValue::Accumulate().
+class Accumulate {
+public:
+  template<typename T>
+  static T operate(const T& aOne, const T& aTwo, double aCoeff)
+  {
+    return aOne + aTwo * aCoeff;
+  }
+
+  static Point4D operateForPerspective(const Point4D& aOne,
+                                       const Point4D& aTwo,
+                                       double aCoeff)
+  {
+    return (aOne - Point4D(0, 0, 0, 1)) +
+           (aTwo - Point4D(0, 0, 0, 1)) * aCoeff +
+           Point4D(0, 0, 0, 1);
+  }
+  static Point3D operateForScale(const Point3D& aOne,
+                                 const Point3D& aTwo,
+                                 double aCoeff)
+  {
+    // For scale, the identify element is 1, see AddTransformScale in
+    // StyleAnimationValue.cpp.
+    return (aOne - Point3D(1, 1, 1)) +
+           (aTwo - Point3D(1, 1, 1)) * aCoeff +
+           Point3D(1, 1, 1);
+  }
+
+  static Matrix4x4 operateForRotate(const gfxQuaternion& aOne,
+                                    const gfxQuaternion& aTwo,
+                                    double aCoeff)
+  {
+    if (aCoeff == 0.0) {
+      return aOne.ToMatrix();
+    }
+
+    double theta = acos(mozilla::clamped(aTwo.w, -1.0, 1.0));
+    double scale = (theta != 0.0) ? 1.0 / sin(theta) : 0.0;
+    theta *= aCoeff;
+    scale *= sin(theta);
+
+    gfxQuaternion result = gfxQuaternion(scale * aTwo.x,
+                                         scale * aTwo.y,
+                                         scale * aTwo.z,
+                                         cos(theta)) * aOne;
+    return result.ToMatrix();
+  }
+};
+
 class Interpolate {
 public:
   template<typename T>
   static T operate(const T& aOne, const T& aTwo, double aCoeff)
   {
     MOZ_ASSERT(aCoeff >= 0.0 && aCoeff <= 1.0,
                "Coefficient should be in the range [0.0, 1.0]");
     return aOne + (aTwo - aOne) * aCoeff;
   }
 
+  static Point4D operateForPerspective(const Point4D& aOne,
+                                       const Point4D& aTwo,
+                                       double aCoeff)
+  {
+    MOZ_ASSERT(aCoeff >= 0.0 && aCoeff <= 1.0,
+               "Coefficient should be in the range [0.0, 1.0]");
+    return aOne + (aTwo - aOne) * aCoeff;
+  }
+
   static Point3D operateForScale(const Point3D& aOne,
                                  const Point3D& aTwo,
                                  double aCoeff)
   {
     MOZ_ASSERT(aCoeff >= 0.0 && aCoeff <= 1.0,
                "Coefficient should be in the range [0.0, 1.0]");
     return aOne + (aTwo - aOne) * aCoeff;
   }
@@ -325,33 +384,33 @@ OperateTransformMatrix(const Matrix4x4 &
     Decompose2DMatrix(matrix2d2, scale2, shear2, rotate2, translate2);
   } else {
     Decompose3DMatrix(aMatrix1, scale1, shear1,
                       rotate1, translate1, perspective1);
     Decompose3DMatrix(aMatrix2, scale2, shear2,
                       rotate2, translate2, perspective2);
   }
 
-  // Interpolate each of the pieces
   Matrix4x4 result;
 
+  // Operate each of the pieces in response to |Operator|.
   Point4D perspective =
-    Operator::operate(perspective1, perspective2, aProgress);
+    Operator::operateForPerspective(perspective1, perspective2, aProgress);
   result.SetTransposedVector(3, perspective);
 
   Point3D translate =
     Operator::operate(translate1, translate2, aProgress);
   result.PreTranslate(translate.x, translate.y, translate.z);
 
   Matrix4x4 rotate = Operator::operateForRotate(rotate1, rotate2, aProgress);
   if (!rotate.IsIdentity()) {
     result = rotate * result;
   }
 
-  // TODO: Would it be better to interpolate these as angles?
+  // TODO: Would it be better to operate these as angles?
   //       How do we convert back to angles?
   float yzshear =
     Operator::operate(shear1[ShearType::YZSHEAR],
                       shear2[ShearType::YZSHEAR],
                       aProgress);
   if (yzshear != 0.0) {
     result.SkewYZ(yzshear);
   }
@@ -424,16 +483,30 @@ ProcessInterpolateMatrix(Matrix4x4& aMat
                          TransformReferenceBox& aRefBox,
                          bool* aContains3dTransform)
 {
   ProcessMatrixOperator<Interpolate>(aMatrix, aData, aContext, aPresContext,
                                      aConditions, aRefBox,
                                      aContains3dTransform);
 }
 
+void
+ProcessAccumulateMatrix(Matrix4x4& aMatrix,
+                        const nsCSSValue::Array* aData,
+                        nsStyleContext* aContext,
+                        nsPresContext* aPresContext,
+                        RuleNodeCacheConditions& aConditions,
+                        TransformReferenceBox& aRefBox,
+                        bool* aContains3dTransform)
+{
+  ProcessMatrixOperator<Accumulate>(aMatrix, aData, aContext, aPresContext,
+                                    aConditions, aRefBox,
+                                    aContains3dTransform);
+}
+
 /* Helper function to process a translatex function. */
 static void
 ProcessTranslateX(Matrix4x4& aMatrix,
                   const nsCSSValue::Array* aData,
                   nsStyleContext* aContext,
                   nsPresContext* aPresContext,
                   RuleNodeCacheConditions& aConditions,
                   TransformReferenceBox& aRefBox)
@@ -791,16 +864,21 @@ MatrixForTransformFunction(Matrix4x4& aM
     ProcessMatrix3D(aMatrix, aData, aContext, aPresContext,
                     aConditions, aRefBox);
     break;
   case eCSSKeyword_interpolatematrix:
     ProcessMatrixOperator<Interpolate>(aMatrix, aData, aContext, aPresContext,
                                        aConditions, aRefBox,
                                        aContains3dTransform);
     break;
+  case eCSSKeyword_accumulatematrix:
+    ProcessMatrixOperator<Accumulate>(aMatrix, aData, aContext, aPresContext,
+                                      aConditions, aRefBox,
+                                      aContains3dTransform);
+    break;
   case eCSSKeyword_perspective:
     *aContains3dTransform = true;
     ProcessPerspective(aMatrix, aData, aContext, aPresContext,
                        aConditions);
     break;
   default:
     NS_NOTREACHED("Unknown transform function!");
   }
--- a/layout/style/nsStyleTransformMatrix.h
+++ b/layout/style/nsStyleTransformMatrix.h
@@ -146,16 +146,25 @@ namespace nsStyleTransformMatrix {
   ProcessInterpolateMatrix(mozilla::gfx::Matrix4x4& aMatrix,
                            const nsCSSValue::Array* aData,
                            nsStyleContext* aContext,
                            nsPresContext* aPresContext,
                            mozilla::RuleNodeCacheConditions& aConditions,
                            TransformReferenceBox& aBounds,
                            bool* aContains3dTransform);
 
+  void
+  ProcessAccumulateMatrix(mozilla::gfx::Matrix4x4& aMatrix,
+                          const nsCSSValue::Array* aData,
+                          nsStyleContext* aContext,
+                          nsPresContext* aPresContext,
+                          mozilla::RuleNodeCacheConditions& aConditions,
+                          TransformReferenceBox& aBounds,
+                          bool* aContains3dTransform);
+
   /**
    * Given an nsCSSValueList containing -moz-transform functions,
    * returns a matrix containing the value of those functions.
    *
    * @param aData The nsCSSValueList containing the transform functions
    * @param aContext The style context, used for unit conversion.
    * @param aPresContext The presentation context, used for unit conversion.
    * @param aConditions Set to uncachable (by calling SetUncacheable()) if the