Bug 1266257 - Revise timing model calculations to use fraction-based approach; r=hiro
authorBrian Birtles <birtles@gmail.com>
Thu, 21 Apr 2016 14:51:36 +0900
changeset 332120 b8984af0dc14e43b85e2c44e5057692d612b276b
parent 332119 bb544df7950167f606b5852967b900d382cc55c6
child 332121 b38998a7ae5bfe7cbee1a439284df6c1342b4a09
push id6048
push userkmoir@mozilla.com
push dateMon, 06 Jun 2016 19:02:08 +0000
treeherdermozilla-beta@46d72a56c57d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewershiro
bugs1266257
milestone48.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1266257 - Revise timing model calculations to use fraction-based approach; r=hiro As per updates to Web Animations spec: https://github.com/w3c/web-animations/compare/10fc97366041%5E...b9a721e6536ae27c37df1e3948b013c6a52d5f4c MozReview-Commit-ID: 5NHnF56aETJ
dom/animation/KeyframeEffect.cpp
--- a/dom/animation/KeyframeEffect.cpp
+++ b/dom/animation/KeyframeEffect.cpp
@@ -213,46 +213,48 @@ KeyframeEffectReadOnly::GetLocalTime() c
   Nullable<TimeDuration> result;
   if (mAnimation) {
     result = mAnimation->GetCurrentTime();
   }
   return result;
 }
 
 void
-KeyframeEffectReadOnly::GetComputedTimingAsDict(ComputedTimingProperties& aRetVal) const
+KeyframeEffectReadOnly::GetComputedTimingAsDict(
+    ComputedTimingProperties& aRetVal) const
 {
   const Nullable<TimeDuration> currentTime = GetLocalTime();
   GetComputedTimingDictionary(GetComputedTimingAt(currentTime,
                                                   SpecifiedTiming()),
                               currentTime,
                               SpecifiedTiming(),
                               aRetVal);
 }
 
 ComputedTiming
 KeyframeEffectReadOnly::GetComputedTimingAt(
-                          const Nullable<TimeDuration>& aLocalTime,
-                          const TimingParams& aTiming)
+    const Nullable<TimeDuration>& aLocalTime,
+    const TimingParams& aTiming)
 {
   const StickyTimeDuration zeroDuration;
 
   // Always return the same object to benefit from return-value optimization.
   ComputedTiming result;
 
   if (aTiming.mDuration) {
     MOZ_ASSERT(aTiming.mDuration.ref() >= zeroDuration,
                "Iteration duration should be positive");
     result.mDuration = aTiming.mDuration.ref();
   }
 
   MOZ_ASSERT(aTiming.mIterations >= 0.0 && !IsNaN(aTiming.mIterations),
              "mIterations should be nonnegative & finite, as ensured by "
              "ValidateIterations or CSSParser");
   result.mIterations = aTiming.mIterations;
+
   MOZ_ASSERT(aTiming.mIterationStart >= 0.0,
              "mIterationStart should be nonnegative, as ensured by "
              "ValidateIterationStart");
   result.mIterationStart = aTiming.mIterationStart;
 
   result.mActiveDuration = aTiming.ActiveDuration();
   result.mEndTime = aTiming.EndTime();
   result.mFill = aTiming.mFill == dom::FillMode::Auto ?
@@ -261,118 +263,87 @@ KeyframeEffectReadOnly::GetComputedTimin
 
   // The default constructor for ComputedTiming sets all other members to
   // values consistent with an animation that has not been sampled.
   if (aLocalTime.IsNull()) {
     return result;
   }
   const TimeDuration& localTime = aLocalTime.Value();
 
-  // When we finish exactly at the end of an iteration we need to report
-  // the end of the final iteration and not the start of the next iteration
-  // so we set up a flag for that case.
-  bool isEndOfFinalIteration = false;
-
-  // Get the normalized time within the active interval.
+  // Calculate the time within the active interval.
+  // https://w3c.github.io/web-animations/#active-time
   StickyTimeDuration activeTime;
   if (localTime >=
         std::min(StickyTimeDuration(aTiming.mDelay + result.mActiveDuration),
                  result.mEndTime)) {
     result.mPhase = ComputedTiming::AnimationPhase::After;
     if (!result.FillsForwards()) {
       // The animation isn't active or filling at this time.
-      result.mProgress.SetNull();
       return result;
     }
     activeTime = result.mActiveDuration;
-    double finiteProgress =
-      (IsInfinite(result.mIterations) ? 0.0 : result.mIterations)
-      + result.mIterationStart;
-    isEndOfFinalIteration = result.mIterations != 0.0 &&
-                            fmod(finiteProgress, 1.0) == 0;
   } else if (localTime <
                std::min(StickyTimeDuration(aTiming.mDelay), result.mEndTime)) {
     result.mPhase = ComputedTiming::AnimationPhase::Before;
     if (!result.FillsBackwards()) {
       // The animation isn't active or filling at this time.
-      result.mProgress.SetNull();
       return result;
     }
     // activeTime is zero
   } else {
     MOZ_ASSERT(result.mActiveDuration != zeroDuration,
                "How can we be in the middle of a zero-duration interval?");
     result.mPhase = ComputedTiming::AnimationPhase::Active;
     activeTime = localTime - aTiming.mDelay;
   }
 
-  // Calculate the scaled active time
-  // (We handle the case where the iterationStart is zero separately in case
-  // the duration is infinity, since 0 * Infinity is undefined.)
-  StickyTimeDuration startOffset =
-    result.mIterationStart == 0.0
-    ? StickyTimeDuration(0)
-    : result.mDuration.MultDouble(result.mIterationStart);
-  StickyTimeDuration scaledActiveTime = activeTime + startOffset;
+  // Convert active time to a multiple of iterations.
+  // https://w3c.github.io/web-animations/#overall-progress
+  double overallProgress;
+  if (result.mDuration == zeroDuration) {
+    overallProgress = result.mPhase == ComputedTiming::AnimationPhase::Before
+                      ? 0.0
+                      : result.mIterations;
+  } else {
+    overallProgress = activeTime / result.mDuration;
+  }
 
-  // Get the position within the current iteration.
-  StickyTimeDuration iterationTime;
-  if (result.mDuration != zeroDuration &&
-      scaledActiveTime != StickyTimeDuration::Forever()) {
-    iterationTime = isEndOfFinalIteration
-                    ? result.mDuration
-      : scaledActiveTime % result.mDuration;
-  } /* else, either the duration is zero and iterationTime is zero,
-       or the scaledActiveTime is infinity in which case the iterationTime
-       should become infinity but we will not use the iterationTime in that
-       case so we just leave it as zero */
+  // Factor in iteration start offset.
+  if (IsFinite(overallProgress)) {
+    overallProgress += result.mIterationStart;
+  }
 
   // Determine the 0-based index of the current iteration.
-  if (result.mPhase == ComputedTiming::AnimationPhase::Before ||
-      result.mIterations == 0) {
-    result.mCurrentIteration = static_cast<uint64_t>(result.mIterationStart);
-  } else if (result.mPhase == ComputedTiming::AnimationPhase::After) {
-    result.mCurrentIteration =
-      IsInfinite(result.mIterations)
-      ? UINT64_MAX // In GetComputedTimingDictionary(), we will convert this
-                   // into Infinity.
-      : static_cast<uint64_t>(ceil(result.mIterations +
-                              result.mIterationStart)) - 1;
-  } else if (result.mDuration == StickyTimeDuration::Forever()) {
-    result.mCurrentIteration = static_cast<uint64_t>(result.mIterationStart);
-  } else {
-    result.mCurrentIteration =
-      static_cast<uint64_t>(scaledActiveTime / result.mDuration); // floor
+  // https://w3c.github.io/web-animations/#current-iteration
+  result.mCurrentIteration =
+    IsInfinite(result.mIterations) &&
+      result.mPhase == ComputedTiming::AnimationPhase::After
+    ? UINT64_MAX // In GetComputedTimingDictionary(),
+                 // we will convert this into Infinity
+    : static_cast<uint64_t>(overallProgress);
+
+  // Convert the overall progress to a fraction of a single iteration--the
+  // simply iteration progress.
+  // https://w3c.github.io/web-animations/#simple-iteration-progress
+  double progress = IsFinite(overallProgress)
+                    ? fmod(overallProgress, 1.0)
+                    : fmod(result.mIterationStart, 1.0);
+
+  // When we finish exactly at the end of an iteration we need to report
+  // the end of the final iteration and not the start of the next iteration.
+  if (result.mPhase == ComputedTiming::AnimationPhase::After &&
+      progress == 0.0 &&
+      result.mIterations != 0.0) {
+    progress = 1.0;
+    if (result.mCurrentIteration != UINT64_MAX) {
+      result.mCurrentIteration--;
+    }
   }
 
-  // Normalize the iteration time into a fraction of the iteration duration.
-  if (result.mPhase == ComputedTiming::AnimationPhase::Before ||
-      result.mIterations == 0) {
-    double progress = fmod(result.mIterationStart, 1.0);
-    result.mProgress.SetValue(progress);
-  } else if (result.mPhase == ComputedTiming::AnimationPhase::After) {
-    double progress;
-    if (isEndOfFinalIteration) {
-      progress = 1.0;
-    } else if (IsInfinite(result.mIterations)) {
-      progress = fmod(result.mIterationStart, 1.0);
-    } else {
-      progress = fmod(result.mIterations + result.mIterationStart, 1.0);
-    }
-    result.mProgress.SetValue(progress);
-  } else {
-    // We are in the active phase so the iteration duration can't be zero.
-    MOZ_ASSERT(result.mDuration != zeroDuration,
-               "In the active phase of a zero-duration animation?");
-    double progress = result.mDuration == StickyTimeDuration::Forever()
-                      ? fmod(result.mIterationStart, 1.0)
-                      : iterationTime / result.mDuration;
-    result.mProgress.SetValue(progress);
-  }
-
+  // Factor in the direction.
   bool thisIterationReverse = false;
   switch (aTiming.mDirection) {
     case PlaybackDirection::Normal:
       thisIterationReverse = false;
       break;
     case PlaybackDirection::Reverse:
       thisIterationReverse = true;
       break;
@@ -381,32 +352,35 @@ KeyframeEffectReadOnly::GetComputedTimin
       break;
     case PlaybackDirection::Alternate_reverse:
       thisIterationReverse = (result.mCurrentIteration & 1) == 0;
       break;
     default:
       MOZ_ASSERT(true, "Unknown PlaybackDirection type");
   }
   if (thisIterationReverse) {
-    result.mProgress.SetValue(1.0 - result.mProgress.Value());
+    progress = 1.0 - progress;
   }
 
+  // Calculate the 'before flag' which we use when applying step timing
+  // functions.
   if ((result.mPhase == ComputedTiming::AnimationPhase::After &&
        thisIterationReverse) ||
       (result.mPhase == ComputedTiming::AnimationPhase::Before &&
        !thisIterationReverse)) {
     result.mBeforeFlag = ComputedTimingFunction::BeforeFlag::Set;
   }
 
+  // Apply the easing.
   if (aTiming.mFunction) {
-    result.mProgress.SetValue(
-      aTiming.mFunction->GetValue(result.mProgress.Value(),
-                                  result.mBeforeFlag));
+    progress = aTiming.mFunction->GetValue(progress, result.mBeforeFlag);
   }
 
+  MOZ_ASSERT(IsFinite(progress), "Progress value should be finite");
+  result.mProgress.SetValue(progress);
   return result;
 }
 
 // https://w3c.github.io/web-animations/#in-play
 bool
 KeyframeEffectReadOnly::IsInPlay() const
 {
   if (!mAnimation || mAnimation->PlayState() == AnimationPlayState::Finished) {