Bug 1286150 - Part 3: Implement shape distance for circle and ellipse. r=hiro
authorBoris Chiou <boris.chiou@gmail.com>
Fri, 14 Oct 2016 16:28:16 +0800
changeset 347143 acbcd6418399a7c2793778bf54896a6027c22a4c
parent 347142 0a19bbc3c7152f62cdb643f1437ebf01e24b586a
child 347144 3f70a3328993e4f31952b0c306b75de5a5951ab3
push id10298
push userraliiev@mozilla.com
push dateMon, 14 Nov 2016 12:33:03 +0000
treeherdermozilla-aurora@7e29173b1641 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewershiro
bugs1286150
milestone52.0a1
Bug 1286150 - Part 3: Implement shape distance for circle and ellipse. r=hiro MozReview-Commit-ID: EX4C4IM0nVx
layout/style/StyleAnimationValue.cpp
--- a/layout/style/StyleAnimationValue.cpp
+++ b/layout/style/StyleAnimationValue.cpp
@@ -728,16 +728,83 @@ StyleAnimationValue::ComputeColorDistanc
 
   double diffA = startA - endA;
   double diffR = startR - endR;
   double diffG = startG - endG;
   double diffB = startB - endB;
   return sqrt(diffA * diffA + diffR * diffR + diffG * diffG + diffB * diffB);
 }
 
+enum class Restrictions {
+  Enable,
+  Disable
+};
+
+static already_AddRefed<nsCSSValue::Array>
+AddShapeFunction(nsCSSPropertyID aProperty,
+                 double aCoeff1, const nsCSSValue::Array* aArray1,
+                 double aCoeff2, const nsCSSValue::Array* aArray2,
+                 Restrictions aRestriction = Restrictions::Enable);
+
+static double
+ComputeShapeDistance(nsCSSPropertyID aProperty,
+                     const nsCSSValue::Array* aArray1,
+                     const nsCSSValue::Array* aArray2)
+{
+  // Use AddShapeFunction to get the difference between two shape functions.
+  RefPtr<nsCSSValue::Array> diffShape =
+    AddShapeFunction(aProperty, 1.0, aArray2, -1.0, aArray1,
+                     Restrictions::Disable);
+  if (!diffShape) {
+    return 0.0;
+  }
+
+  // A helper function to convert a calc() diff value into a double distance.
+  auto pixelCalcDistance = [](const PixelCalcValue& aValue) {
+    MOZ_ASSERT(aValue.mHasPercent || aValue.mPercent == 0.0f,
+             "can't have a nonzero percentage part without having percentages");
+    return aValue.mLength * aValue.mLength + aValue.mPercent * aValue.mPercent;
+  };
+
+  double squareDistance = 0.0;
+  const nsCSSValue::Array* func = diffShape->Item(0).GetArrayValue();
+  nsCSSKeyword shapeFuncName = func->Item(0).GetKeywordValue();
+  switch (shapeFuncName) {
+    case eCSSKeyword_ellipse:
+    case eCSSKeyword_circle: {
+      // Skip the first element which is the function keyword.
+      // Also, skip the last element which is an array for <position>
+      const size_t len = func->Count();
+      for (size_t i = 1; i < len - 1; ++i) {
+        squareDistance += pixelCalcDistance(ExtractCalcValue(func->Item(i)));
+      }
+      // Only iterate over elements 1 and 3. The <position> is 'uncomputed' to
+      // only those elements.  See also the comment in SetPositionValue.
+      for (size_t i = 1; i < 4; i += 2) {
+        const nsCSSValue& value = func->Item(len - 1).GetArrayValue()->Item(i);
+        squareDistance += pixelCalcDistance(ExtractCalcValue(value));
+      }
+      break;
+    }
+    case eCSSKeyword_polygon:
+      // TODO: will be fixed in the later patch.
+      MOZ_ASSERT(false);
+      break;
+
+    case eCSSKeyword_inset:
+      // TODO: will be fixed in the later patch.
+      MOZ_ASSERT(false);
+      break;
+
+    default:
+      MOZ_ASSERT_UNREACHABLE("Unknown shape type");
+  }
+  return sqrt(squareDistance);
+}
+
 static nsCSSValueList*
 AddTransformLists(double aCoeff1, const nsCSSValueList* aList1,
                   double aCoeff2, const nsCSSValueList* aList2);
 
 static double
 ComputeTransform2DMatrixDistance(const Matrix& aMatrix1,
                                  const Matrix& aMatrix2)
 {
@@ -1411,19 +1478,20 @@ StyleAnimationValue::ComputeDistance(nsC
         shadow1 = shadow1->mNext;
         shadow2 = shadow2->mNext;
         MOZ_ASSERT(!shadow1 == !shadow2, "lists should be same length");
       }
       aDistance = sqrt(squareDistance);
       return true;
     }
     case eUnit_Shape:
-      // Bug 1286150: The CSS Shapes spec doesn't define paced animations for
-      // shape functions, but we still need to implement one.
-      return false;
+      aDistance = ComputeShapeDistance(aProperty,
+                                       aStartValue.GetCSSValueArrayValue(),
+                                       aEndValue.GetCSSValueArrayValue());
+      return true;
 
     case eUnit_Filter:
       // Bug 1286151: Support paced animations for filter function
       // interpolation.
       return false;
 
     case eUnit_Transform: {
       // FIXME: We don't have an official spec to define the distance of
@@ -2127,26 +2195,21 @@ AddCSSValuePairList(nsCSSPropertyID aPro
 
   if (aList1 || aList2) {
     return nullptr; // We can't interpolate lists of different lengths
   }
 
   return result;
 }
 
-enum class Restrictions {
-  Enable,
-  Disable
-};
-
 static already_AddRefed<nsCSSValue::Array>
 AddShapeFunction(nsCSSPropertyID aProperty,
                  double aCoeff1, const nsCSSValue::Array* aArray1,
                  double aCoeff2, const nsCSSValue::Array* aArray2,
-                 Restrictions aRestriction = Restrictions::Enable)
+                 Restrictions aRestriction)
 {
   MOZ_ASSERT(aArray1 && aArray1->Count() == 2, "expected shape function");
   MOZ_ASSERT(aArray2 && aArray2->Count() == 2, "expected shape function");
   MOZ_ASSERT(aArray1->Item(0).GetUnit() == eCSSUnit_Function,
              "expected function");
   MOZ_ASSERT(aArray2->Item(0).GetUnit() == eCSSUnit_Function,
              "expected function");
   MOZ_ASSERT(aArray1->Item(1).GetUnit() == eCSSUnit_Enumerated,