static bool SampleAnimations(Layer* aLayer, TimeStamp aPoint) { AnimationArray& animations = aLayer->GetAnimations(); InfallibleTArray<AnimData>& animationData = aLayer->GetAnimationData(); bool activeAnimations = false; for (uint32_t i = animations.Length(); i-- !=0; ) { Animation& animation = animations[i]; AnimData& animData = animationData[i]; double numIterations = animation.numIterations() != -1 ? animation.numIterations() : NS_IEEEPositiveInfinity(); double positionInIteration = ElementAnimations::GetPositionInIteration(aPoint - animation.startTime(), animation.duration(), numIterations, animation.direction()); NS_ABORT_IF_FALSE(0.0 <= positionInIteration && positionInIteration <= 1.0, "position should be in [0-1]"); int segmentIndex = 0; AnimationSegment* segment = animation.segments().Elements(); while (segment->endPortion() < positionInIteration) { ++segment; ++segmentIndex; } double positionInSegment = (positionInIteration - segment->startPortion()) / (segment->endPortion() - segment->startPortion()); double portion = animData.mFunctions[segmentIndex]->GetValue(positionInSegment); activeAnimations = true; // interpolate the property Animatable interpolatedValue; SampleValue(portion, animation, animData.mStartValues[segmentIndex], animData.mEndValues[segmentIndex], &interpolatedValue); LayerComposite* layerComposite = aLayer->AsLayerComposite(); switch (animation.property()) { case eCSSProperty_opacity: { layerComposite->SetShadowOpacity(interpolatedValue.get_float()); break; } case eCSSProperty_transform: { gfx3DMatrix matrix = interpolatedValue.get_ArrayOfTransformFunction()[0].get_TransformMatrix().value(); if (ContainerLayer* c = aLayer->AsContainerLayer()) { matrix.ScalePost(c->GetInheritedXScale(), c->GetInheritedYScale(), 1); } layerComposite->SetShadowTransform(matrix); layerComposite->SetShadowTransformSetByAnimation(true); break; } default: NS_WARNING("Unhandled animated property"); } } for (Layer* child = aLayer->GetFirstChild(); child; child = child->GetNextSibling()) { activeAnimations |= SampleAnimations(child, aPoint); } return activeAnimations; }
ComputedTiming Animation::GetComputedTimingAt(const Nullable<TimeDuration>& aLocalTime, const AnimationTiming& aTiming) { const TimeDuration zeroDuration; // Currently we expect negative durations to be picked up during CSS // parsing but when we start receiving timing parameters from other sources // we will need to clamp negative durations here. // For now, if we're hitting this it probably means we're overflowing // integer arithmetic in mozilla::TimeStamp. MOZ_ASSERT(aTiming.mIterationDuration >= zeroDuration, "Expecting iteration duration >= 0"); // Always return the same object to benefit from return-value optimization. ComputedTiming result; result.mActiveDuration = ActiveDuration(aTiming); // 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. StickyTimeDuration activeTime; if (localTime >= aTiming.mDelay + result.mActiveDuration) { result.mPhase = ComputedTiming::AnimationPhase_After; if (!aTiming.FillsForwards()) { // The animation isn't active or filling at this time. result.mTimeFraction = ComputedTiming::kNullTimeFraction; return result; } activeTime = result.mActiveDuration; // 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 (localTime < aTiming.mDelay) { result.mPhase = ComputedTiming::AnimationPhase_Before; if (!aTiming.FillsBackwards()) { // The animation isn't active or filling at this time. result.mTimeFraction = ComputedTiming::kNullTimeFraction; 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; } // Get the position within the current iteration. StickyTimeDuration iterationTime; if (aTiming.mIterationDuration != zeroDuration) { iterationTime = isEndOfFinalIteration ? StickyTimeDuration(aTiming.mIterationDuration) : activeTime % aTiming.mIterationDuration; } /* else, iterationTime is zero */ // 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 == zeroDuration) { // 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 } // 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 != zeroDuration, "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: thisIterationReverse = (result.mCurrentIteration & 1) == 1; break; case NS_STYLE_ANIMATION_DIRECTION_ALTERNATE_REVERSE: thisIterationReverse = (result.mCurrentIteration & 1) == 0; break; } if (thisIterationReverse) { result.mTimeFraction = 1.0 - result.mTimeFraction; } return result; }