Bug 1244590 - Part 4: Rewrite ApplyDistributeSpacing. draft
authorBoris Chiou <boris.chiou@gmail.com>
Tue, 10 May 2016 17:40:53 +0800
changeset 371283 8222b59a52d2d554b5dc61be63cf8b4df1ccd900
parent 371282 33fc5677b9b824d7689f56e058cecd5f32827774
child 371284 e031d7d4b9d9f5b6a9daebe90def5b090e80f852
push id19290
push userbmo:boris.chiou@gmail.com
push dateThu, 26 May 2016 09:18:45 +0000
bugs1244590
milestone49.0a1
Bug 1244590 - Part 4: Rewrite ApplyDistributeSpacing. Rename it to ApplySpacing, and let it handle both distribute and paced mode. MozReview-Commit-ID: 2kGA6LMHUsz
dom/animation/KeyframeEffect.cpp
dom/animation/KeyframeUtils.cpp
dom/animation/KeyframeUtils.h
--- a/dom/animation/KeyframeEffect.cpp
+++ b/dom/animation/KeyframeEffect.cpp
@@ -464,17 +464,21 @@ void
 KeyframeEffectReadOnly::SetKeyframes(nsTArray<Keyframe>&& aKeyframes,
                                   nsStyleContext* aStyleContext)
 {
   if (KeyframesEqualIgnoringComputedOffsets(aKeyframes, mKeyframes)) {
     return;
   }
 
   mKeyframes = Move(aKeyframes);
-  KeyframeUtils::ApplyDistributeSpacing(mKeyframes);
+  // Apply distribute spacing irrespective of the spacing mode. We will apply
+  // the specified spacing mode when we generate computed animation property
+  // values from the keyframes since both operations require a style context
+  // and need to be performed whenever the style context changes.
+  KeyframeUtils::ApplySpacing(mKeyframes, SpacingMode::distribute);
 
   if (mAnimation && mAnimation->IsRelevant()) {
     nsNodeUtils::AnimationChanged(mAnimation);
   }
 
   if (aStyleContext) {
     UpdateProperties(aStyleContext);
   }
--- a/dom/animation/KeyframeUtils.cpp
+++ b/dom/animation/KeyframeUtils.cpp
@@ -378,16 +378,18 @@ GetKeyframeListFromPropertyIndexedKeyfra
                                            JS::Handle<JS::Value> aValue,
                                            nsTArray<Keyframe>& aResult,
                                            ErrorResult& aRv);
 
 static bool
 RequiresAdditiveAnimation(const nsTArray<Keyframe>& aKeyframes,
                           nsIDocument* aDocument);
 
+static void
+DistributeRange(const Range<Keyframe>& aKeyframes);
 
 // ------------------------------------------------------------------
 //
 // Public API
 //
 // ------------------------------------------------------------------
 
 /* static */ nsTArray<Keyframe>
@@ -444,48 +446,55 @@ KeyframeUtils::GetKeyframesFromObject(JS
     aRv.Throw(NS_ERROR_DOM_ANIM_MISSING_PROPS_ERR);
     keyframes.Clear();
   }
 
   return keyframes;
 }
 
 /* static */ void
-KeyframeUtils::ApplyDistributeSpacing(nsTArray<Keyframe>& aKeyframes)
+KeyframeUtils::ApplySpacing(nsTArray<Keyframe>& aKeyframes,
+                            SpacingMode aSpacingMode)
 {
   if (aKeyframes.IsEmpty()) {
     return;
   }
 
-  // If the first or last keyframes have an unspecified offset,
-  // fill them in with 0% and 100%.  If there is only a single keyframe,
-  // then it gets 100%.
-  Keyframe& lastElement = aKeyframes.LastElement();
-  lastElement.mComputedOffset = lastElement.mOffset.valueOr(1.0);
+  // If the first keyframes have an unspecified offset, fill it in with 0%.
+  // If there is only a single keyframe, then it gets 100%.
   if (aKeyframes.Length() > 1) {
     Keyframe& firstElement = aKeyframes[0];
     firstElement.mComputedOffset = firstElement.mOffset.valueOr(0.0);
+    // We will fill in the last keyframe's offset below
+  } else {
+    Keyframe& lastElement = aKeyframes.LastElement();
+    lastElement.mComputedOffset = lastElement.mOffset.valueOr(1.0);
   }
 
   // Fill in remaining missing offsets.
-  size_t i = 0;
-  while (i < aKeyframes.Length() - 1) {
-    double start = aKeyframes[i].mComputedOffset;
-    size_t j = i + 1;
-    while (aKeyframes[j].mOffset.isNothing() && j < aKeyframes.Length() - 1) {
-      ++j;
+  const Keyframe* const last = aKeyframes.cend() - 1;
+  RangedPtr<Keyframe> keyframeA(aKeyframes.begin(), aKeyframes.Length());
+  while (keyframeA != last) {
+    // Find frame A and frame B *between* which we will apply spacing.
+    RangedPtr<Keyframe> keyframeB = keyframeA + 1;
+    while ((*keyframeB).mOffset.isNothing() && keyframeB != last) {
+      ++keyframeB;
     }
-    double end = aKeyframes[j].mOffset.valueOr(1.0);
-    size_t n = j - i;
-    for (size_t k = 1; k < n; ++k) {
-      double offset = start + double(k) / n * (end - start);
-      aKeyframes[i + k].mComputedOffset = offset;
+    (*keyframeB).mComputedOffset = (*keyframeB).mOffset.valueOr(1.0);
+
+    // Fill computed offsets in (frame A, frame B).
+    if (aSpacingMode == SpacingMode::distribute) {
+      DistributeRange(Range<Keyframe>(keyframeA.get(),
+                                      keyframeB - keyframeA + 1));
+    } else {
+      // TODO
+      MOZ_ASSERT(false, "not implement yet");
     }
-    i = j;
-    aKeyframes[j].mComputedOffset = end;
+
+    keyframeA = keyframeB;
   }
 }
 
 /* static */ nsTArray<AnimationProperty>
 KeyframeUtils::GetAnimationPropertiesFromKeyframes(
     nsStyleContext* aStyleContext,
     dom::Element* aElement,
     CSSPseudoElementType aPseudoType,
@@ -1130,17 +1139,17 @@ RequiresAdditiveAnimation(const nsTArray
     } else if (aOffset == 1.0) {
       propertiesWithToValue.AddProperty(aProperty);
     }
   };
 
   for (size_t i = 0, len = aKeyframes.Length(); i < len; i++) {
     const Keyframe& frame = aKeyframes[i];
 
-    // We won't have called ApplyDistributeSpacing when this is called so
+    // We won't have called ApplySpacing when this is called so
     // we can't use frame.mComputedOffset. Instead we do a rough version
     // of that algorithm that substitutes null offsets with 0.0 for the first
     // frame, 1.0 for the last frame, and 0.5 for everything else.
     double computedOffset = i == len - 1
                             ? 1.0
                             : i == 0 ? 0.0 : 0.5;
     double offsetToUse = frame.mOffset
                          ? frame.mOffset.value()
@@ -1167,9 +1176,28 @@ RequiresAdditiveAnimation(const nsTArray
       }
     }
   }
 
   return !propertiesWithFromValue.Equals(properties) ||
          !propertiesWithToValue.Equals(properties);
 }
 
+/**
+ * Apply evenly distributing computed offsets in (A, B). We should pass the
+ * range keyframes in [A, B] and use A, B to calculate computed offsets in
+ * (A, B).
+ *
+ * @param aKeyframes The sequence of keyframes between whose endpoints we should
+ *   apply distribute spacing.
+ */
+static void
+DistributeRange(const Range<Keyframe>& aKeyframes)
+{
+  const size_t n = aKeyframes.length() - 1;
+  const double startOffset = aKeyframes[0].mComputedOffset;
+  const double diffOffset = aKeyframes[n].mComputedOffset - startOffset;
+  for (size_t i = 1; i < n; ++i) {
+    aKeyframes[i].mComputedOffset = startOffset + double(i) / n * diffOffset;
+  }
+}
+
 } // namespace mozilla
--- a/dom/animation/KeyframeUtils.h
+++ b/dom/animation/KeyframeUtils.h
@@ -48,23 +48,25 @@ public:
    */
   static nsTArray<Keyframe>
   GetKeyframesFromObject(JSContext* aCx,
                          JS::Handle<JSObject*> aFrames,
                          ErrorResult& aRv);
 
   /**
    * Fills in the mComputedOffset member of each keyframe in the given array
-   * using the "distribute" spacing algorithm.
+   * using the specified spacing mode.
    *
-   * http://w3c.github.io/web-animations/#distribute-keyframe-spacing-mode
+   * https://w3c.github.io/web-animations/#spacing-keyframes
    *
-   * @param keyframes The set of keyframes to adjust.
+   * @param aKeyframes The set of keyframes to adjust.
+   * @param aSpacingMode The spacing mode to apply.
    */
-  static void ApplyDistributeSpacing(nsTArray<Keyframe>& aKeyframes);
+  static void ApplySpacing(nsTArray<Keyframe>& aKeyframes,
+                           SpacingMode aSpacingMode);
 
   /**
    * Converts an array of Keyframe objects into an array of AnimationProperty
    * objects. This involves expanding shorthand properties into longhand
    * properties, creating an array of computed values for each longhand
    * property and determining the offset and timing function to use for each
    * value.
    *