author | Brian Birtles <birtles@gmail.com> |
Wed, 11 Jun 2014 14:19:08 +0900 | |
changeset 188067 | e097537a5589a3265dcc75669362781abc5ea633 |
parent 188066 | 25494b32627f15afb3db4ce5bf4b8f3394db0ac4 |
child 188068 | 7e75b68e61f354d4899aada03f0280c6f969f604 |
push id | 26944 |
push user | emorley@mozilla.com |
push date | Wed, 11 Jun 2014 15:14:00 +0000 |
treeherder | mozilla-central@c7391b84d9c2 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | dholbert |
bugs | 1004365 |
milestone | 33.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
|
--- a/layout/style/AnimationCommon.cpp +++ b/layout/style/AnimationCommon.cpp @@ -395,88 +395,118 @@ ElementAnimation::HasAnimationOfProperty ComputedTiming ElementAnimation::GetComputedTimingAt(TimeDuration aElapsedDuration, const AnimationTiming& aTiming) { // Always return the same object to benefit from return-value optimization. ComputedTiming result; - // Set |currentIterationCount| to the (fractional) number of - // iterations we've completed up to the current position. - double currentIterationCount = aElapsedDuration / aTiming.mIterationDuration; - if (currentIterationCount >= aTiming.mIterationCount) { + TimeDuration activeDuration = ActiveDuration(aTiming); + + // 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. + TimeDuration activeTime; + if (aElapsedDuration >= activeDuration) { result.mPhase = ComputedTiming::AnimationPhase_After; if (!aTiming.FillsForwards()) { // The animation isn't active or filling at this time. result.mTimeFraction = ComputedTiming::kNullTimeFraction; return result; } - currentIterationCount = aTiming.mIterationCount; - } else if (currentIterationCount < 0.0) { + activeTime = activeDuration; + // Note that infinity == floor(infinity) so this will also be true when we + // have finished an infinitely repeating animation of zero duration. + isEndOfFinalIteration = + aTiming.mIterationCount != 0.0 && + aTiming.mIterationCount == floor(aTiming.mIterationCount); + } else if (aElapsedDuration < TimeDuration()) { result.mPhase = ComputedTiming::AnimationPhase_Before; if (!aTiming.FillsBackwards()) { // The animation isn't active or filling at this time. result.mTimeFraction = ComputedTiming::kNullTimeFraction; return result; } - currentIterationCount = 0.0; + // activeTime is zero } else { + MOZ_ASSERT(activeDuration != TimeDuration(), + "How can we be in the middle of a zero-duration interval?"); result.mPhase = ComputedTiming::AnimationPhase_Active; + activeTime = aElapsedDuration; } - // Set |positionInIteration| to the position from 0% to 100% along - // the keyframes. - NS_ABORT_IF_FALSE(currentIterationCount >= 0.0, "must be positive"); - double positionInIteration = fmod(currentIterationCount, 1); + // Get the position within the current iteration. + TimeDuration iterationTime; + if (aTiming.mIterationDuration != TimeDuration()) { + iterationTime = isEndOfFinalIteration + ? aTiming.mIterationDuration + : activeTime % aTiming.mIterationDuration; + } /* else, iterationTime is zero */ - // Set |whichIteration| to the integral index of the current iteration. - // Casting to an integer here gives us floor(currentIterationCount). - // We don't check for overflow here since the range of an unsigned 64-bit - // integer is more than enough (i.e. we could handle an animation that - // iterates every *microsecond* for about 580,000 years). - uint64_t whichIteration = static_cast<uint64_t>(currentIterationCount); + // Determine the 0-based index of the current iteration. + if (isEndOfFinalIteration) { + result.mCurrentIteration = + aTiming.mIterationCount == NS_IEEEPositiveInfinity() + ? UINT64_MAX // FIXME: When we return this via the API we'll need + // to make sure it ends up being infinity. + : static_cast<uint64_t>(aTiming.mIterationCount) - 1; + } else if (activeTime == TimeDuration(0)) { + // If the active time is zero we're either in the first iteration + // (including filling backwards) or we have finished an animation with an + // iteration duration of zero that is filling forwards (but we're not at + // the exact end of an iteration since we deal with that above). + result.mCurrentIteration = + result.mPhase == ComputedTiming::AnimationPhase_After + ? static_cast<uint64_t>(aTiming.mIterationCount) // floor + : 0; + } else { + result.mCurrentIteration = + static_cast<uint64_t>(activeTime / aTiming.mIterationDuration); // floor + } - // Check for the end of the final iteration. - if (whichIteration != 0 && - result.mPhase == ComputedTiming::AnimationPhase_After && - aTiming.mIterationCount == floor(aTiming.mIterationCount)) { - // When the animation's iteration count is an integer (as it - // normally is), we need to end at 100% of its final iteration - // rather than 0% of the next one (unless it's zero). - whichIteration -= 1; - positionInIteration = 1.0; + // Normalize the iteration time into a fraction of the iteration duration. + if (result.mPhase == ComputedTiming::AnimationPhase_Before) { + result.mTimeFraction = 0.0; + } else if (result.mPhase == ComputedTiming::AnimationPhase_After) { + result.mTimeFraction = isEndOfFinalIteration + ? 1.0 + : fmod(aTiming.mIterationCount, 1.0f); + } else { + // We are in the active phase so the iteration duration can't be zero. + MOZ_ASSERT(aTiming.mIterationDuration != TimeDuration(0), + "In the active phase of a zero-duration animation?"); + result.mTimeFraction = + aTiming.mIterationDuration == TimeDuration::Forever() + ? 0.0 + : iterationTime / aTiming.mIterationDuration; } bool thisIterationReverse = false; switch (aTiming.mDirection) { case NS_STYLE_ANIMATION_DIRECTION_NORMAL: thisIterationReverse = false; break; case NS_STYLE_ANIMATION_DIRECTION_REVERSE: thisIterationReverse = true; break; case NS_STYLE_ANIMATION_DIRECTION_ALTERNATE: - // uint64_t has more integer precision than double does, so if - // whichIteration is that large, we've already lost and we're just - // guessing. But the animation is presumably oscillating so fast - // it doesn't matter anyway. - thisIterationReverse = (whichIteration & 1) == 1; + thisIterationReverse = (result.mCurrentIteration & 1) == 1; break; case NS_STYLE_ANIMATION_DIRECTION_ALTERNATE_REVERSE: - // see as previous case - thisIterationReverse = (whichIteration & 1) == 0; + thisIterationReverse = (result.mCurrentIteration & 1) == 0; break; } if (thisIterationReverse) { - positionInIteration = 1.0 - positionInIteration; + result.mTimeFraction = 1.0 - result.mTimeFraction; } - result.mTimeFraction = positionInIteration; - result.mCurrentIteration = whichIteration; return result; } namespace css { bool CommonElementAnimationData::CanAnimatePropertyOnCompositor(const dom::Element *aElement, nsCSSProperty aProperty,
--- a/layout/style/test/test_animations.html +++ b/layout/style/test/test_animations.html @@ -672,17 +672,17 @@ advance_clock(1000); is_approx(px_to_num(cs.paddingBottom), 60 + 100 * gTF.ease_in(0.2), 0.01, "keyframe timing functions test at 3s"); advance_clock(1000); is_approx(px_to_num(cs.paddingBottom), 60 + 100 * gTF.ease_in(0.6), 0.01, "keyframe timing functions test at 4s"); advance_clock(1000); is(cs.paddingBottom, "160px", "keyframe timing functions test at 5s"); -advance_clock(1010); // avoid floating point error +advance_clock(1010); // avoid floating-point error is_approx(px_to_num(cs.paddingBottom), 160 - 40 * step_end(5)(0.4), 0.01, "keyframe timing functions test at 6s"); advance_clock(1000); is_approx(px_to_num(cs.paddingBottom), 160 - 40 * step_end(5)(0.8), 0.01, "keyframe timing functions test at 7s"); advance_clock(990); is_approx(px_to_num(cs.paddingBottom), 120 - 100 * gTF.linear(0.2), 0.01, "keyframe timing functions test at 8s"); @@ -696,23 +696,23 @@ advance_clock(20000); is(cs.paddingBottom, "20px", "keyframe timing functions test at 30s"); advance_clock(1000); is_approx(px_to_num(cs.paddingBottom), 120 - 100 * gTF.linear(0.6), 0.01, "keyframe timing functions test at 31s"); advance_clock(1000); is_approx(px_to_num(cs.paddingBottom), 120 - 100 * gTF.linear(0.2), 0.01, "keyframe timing functions test at 32s"); -advance_clock(1000); +advance_clock(990); // avoid floating-point error is_approx(px_to_num(cs.paddingBottom), 160 - 40 * step_end(5)(0.8), 0.01, "keyframe timing functions test at 33s"); advance_clock(1000); is_approx(px_to_num(cs.paddingBottom), 160 - 40 * step_end(5)(0.4), 0.01, "keyframe timing functions test at 34s"); -advance_clock(1000); +advance_clock(1010); is(cs.paddingBottom, "160px", "keyframe timing functions test at 35s"); advance_clock(1000); is_approx(px_to_num(cs.paddingBottom), 60 + 100 * gTF.ease_in(0.6), 0.01, "keyframe timing functions test at 36s"); advance_clock(1000); is_approx(px_to_num(cs.paddingBottom), 60 + 100 * gTF.ease_in(0.2), 0.01, "keyframe timing functions test at 37s"); @@ -732,20 +732,20 @@ new_div("animation: kf_tf1 ease-in 10s i is(cs.paddingBottom, "20px", "keyframe timing functions test at 0s (test needed for flush)"); advance_clock(11000); is_approx(px_to_num(cs.paddingBottom), 20 + 40 * gTF.ease(0.4), 0.01, "keyframe timing functions test at 11s"); advance_clock(3000); is_approx(px_to_num(cs.paddingBottom), 60 + 100 * gTF.ease_in(0.6), 0.01, "keyframe timing functions test at 14s"); -advance_clock(2000); +advance_clock(2010); // avoid floating-point error is_approx(px_to_num(cs.paddingBottom), 160 - 40 * step_end(5)(0.4), 0.01, "keyframe timing functions test at 16s"); -advance_clock(2000); +advance_clock(1990); is_approx(px_to_num(cs.paddingBottom), 120 - 100 * gTF.linear(0.2), 0.01, "keyframe timing functions test at 18s"); done_div(); /* * css3-animations: 3.2. The 'animation-name' Property * http://dev.w3.org/csswg/css3-animations/#the-animation-name-property- */
--- a/layout/style/test/test_animations_omta.html +++ b/layout/style/test/test_animations_omta.html @@ -737,17 +737,17 @@ addAsyncTest(function *() { "keyframe timing functions test at 3s"); advance_clock(1000); omta_is_approx("transform", { tx: 60 + 100 * gTF.ease_in(0.6) }, RunningOn.Compositor, 0.01, "keyframe timing functions test at 4s"); advance_clock(1000); omta_is("transform", { tx: 160 }, RunningOn.Compositor, "keyframe timing functions test at 5s"); - advance_clock(1010); // avoid floating point error + advance_clock(1010); // avoid floating-point error omta_is_approx("transform", { tx: 160 - 40 * step_end(5)(0.4) }, RunningOn.Compositor, 0.01, "keyframe timing functions test at 6s"); advance_clock(1000); omta_is_approx("transform", { tx: 160 - 40 * step_end(5)(0.8) }, RunningOn.Compositor, 0.01, "keyframe timing functions test at 7s"); advance_clock(990); @@ -767,25 +767,25 @@ addAsyncTest(function *() { advance_clock(1000); omta_is_approx("transform", { tx: 120 - 100 * gTF.linear(0.6) }, RunningOn.Compositor, 0.01, "keyframe timing functions test at 31s"); advance_clock(1000); omta_is_approx("transform", { tx: 120 - 100 * gTF.linear(0.2) }, RunningOn.Compositor, 0.01, "keyframe timing functions test at 32s"); - advance_clock(1000); + advance_clock(990); // avoid floating-point error omta_is_approx("transform", { tx: 160 - 40 * step_end(5)(0.8) }, RunningOn.Compositor, 0.01, "keyframe timing functions test at 33s"); advance_clock(1000); omta_is_approx("transform", { tx: 160 - 40 * step_end(5)(0.4) }, RunningOn.Compositor, 0.01, "keyframe timing functions test at 34s"); - advance_clock(1000); + advance_clock(1010); omta_is("transform", { tx: 160 }, RunningOn.Compositor, "keyframe timing functions test at 35s"); advance_clock(1000); omta_is_approx("transform", { tx: 60 + 100 * gTF.ease_in(0.6) }, RunningOn.Compositor, 0.01, "keyframe timing functions test at 36s"); advance_clock(1000); omta_is_approx("transform", { tx: 60 + 100 * gTF.ease_in(0.2) }, @@ -812,21 +812,21 @@ addAsyncTest(function *() { advance_clock(11000); omta_is_approx("transform", { tx: 20 + 40 * gTF.ease(0.4) }, RunningOn.Compositor, 0.01, "keyframe timing functions test at 11s"); advance_clock(3000); omta_is_approx("transform", { tx: 60 + 100 * gTF.ease_in(0.6) }, RunningOn.Compositor, 0.01, "keyframe timing functions test at 14s"); - advance_clock(2000); + advance_clock(2010); // avoid floating-point error omta_is_approx("transform", { tx: 160 - 40 * step_end(5)(0.4) }, RunningOn.Compositor, 0.01, "keyframe timing functions test at 16s"); - advance_clock(2000); + advance_clock(1990); omta_is_approx("transform", { tx: 120 - 100 * gTF.linear(0.2) }, RunningOn.Compositor, 0.01, "keyframe timing functions test at 18s"); done_div(); }); /* * css3-animations: 3.2. The 'animation-name' Property