Bug 1286150 - Part 3: Implement shape distance for circle and ellipse. draft
authorBoris Chiou <boris.chiou@gmail.com>
Fri, 14 Oct 2016 16:28:16 +0800
changeset 432446 d5b707d5136b470cd88d581de962bf88e5ab87d3
parent 432445 858632584ef656f9d6be50d3bc6ce07a68a3b9ea
child 432447 2051f0acf3b94428ead256b58aaadfe4d1fa2054
push id34315
push userbmo:boris.chiou@gmail.com
push dateWed, 02 Nov 2016 03:24:03 +0000
bugs1286150
milestone52.0a1
Bug 1286150 - Part 3: Implement shape distance for circle and ellipse. 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)
 {
@@ -1423,19 +1490,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
@@ -2145,26 +2213,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,