float SVGSMILElement::calculateAnimationPercentAndRepeat(SMILTime elapsed, unsigned& repeat) const { SMILTime simpleDuration = this->simpleDuration(); repeat = 0; if (simpleDuration.isIndefinite()) { repeat = 0; return 0.f; } if (!simpleDuration) { repeat = 0; return 1.f; } ASSERT(m_intervalBegin.isFinite()); ASSERT(simpleDuration.isFinite()); SMILTime activeTime = elapsed - m_intervalBegin; SMILTime repeatingDuration = this->repeatingDuration(); if (elapsed >= m_intervalEnd || activeTime > repeatingDuration) { repeat = static_cast<unsigned>(repeatingDuration.value() / simpleDuration.value()); if (fmod(repeatingDuration.value(), !simpleDuration.value())) repeat--; SMILTime simpleEndTime = fmod(m_intervalEnd.value() - m_intervalBegin.value(), simpleDuration.value()); if (simpleEndTime.value()) return narrowPrecisionToFloat(simpleEndTime.value() / simpleDuration.value()); return 1.f; } repeat = static_cast<unsigned>(activeTime.value() / simpleDuration.value()); SMILTime simpleTime = fmod(activeTime.value(), simpleDuration.value()); return narrowPrecisionToFloat(simpleTime.value() / simpleDuration.value()); }
void SMILTimeContainer::begin() { RELEASE_ASSERT(!m_beginTime); if (!handleAnimationPolicy(RestartOnceTimerIfNotPaused)) return; double now = currentTime(); // If 'm_presetStartTime' is set, the timeline was modified via setElapsed() before the document began. // In this case pass on 'seekToTime=true' to updateAnimations(). m_beginTime = now - m_presetStartTime; #if !ENABLE(OILPAN) DiscardScope discardScope(m_ownerSVGElement); #endif SMILTime earliestFireTime = updateAnimations(SMILTime(m_presetStartTime), m_presetStartTime ? true : false); m_presetStartTime = 0; if (m_pauseTime) { m_pauseTime = now; // If updateAnimations() caused new syncbase instance to be generated, // we don't want to cancel those. Excepting that, no frame should've // been scheduled at this point. ASSERT(m_frameSchedulingState == Idle || m_frameSchedulingState == SynchronizeAnimations); } else if (!hasPendingSynchronization()) { ASSERT(isTimelineRunning()); // If the timeline is running, and there's pending animation updates, // always perform the first update after the timeline was started using // the wake-up mechanism. if (earliestFireTime.isFinite()) { SMILTime delay = earliestFireTime - elapsed(); scheduleWakeUp(std::max(initialFrameDelay, delay.value()), SynchronizeAnimations); } } }
void SVGSMILElement::beginListChanged(SMILTime eventTime) { if (m_isWaitingForFirstInterval) resolveFirstInterval(); else { SMILTime newBegin = findInstanceTime(Begin, eventTime, true); if (newBegin.isFinite() && (m_intervalEnd <= eventTime || newBegin < m_intervalBegin)) { // Begin time changed, re-resolve the interval. SMILTime oldBegin = m_intervalBegin; m_intervalEnd = eventTime; resolveInterval(false, m_intervalBegin, m_intervalEnd); ASSERT(!m_intervalBegin.isUnresolved()); if (m_intervalBegin != oldBegin) { if (m_activeState == Active && m_intervalBegin > eventTime) { m_activeState = determineActiveState(eventTime); if (m_activeState != Active) endedActiveInterval(); } notifyDependentsIntervalChanged(ExistingInterval); } } } m_nextProgressTime = elapsed(); reschedule(); }
float SVGAnimationElement::getStartTime(ExceptionState& exceptionState) const { SMILTime startTime = intervalBegin(); if (!startTime.isFinite()) { exceptionState.throwDOMException(InvalidStateError, "No current interval."); return 0; } return narrowPrecisionToFloat(startTime.value()); }
float SVGAnimationElement::getSimpleDuration(ExceptionState& exceptionState) const { SMILTime duration = simpleDuration(); if (!duration.isFinite()) { exceptionState.throwDOMException(NotSupportedError, "No simple duration defined."); return 0; } return narrowPrecisionToFloat(duration.value()); }
void SMILTimeContainer::schedule(SVGSMILElement* animation) { ASSERT(animation->timeContainer() == this); SMILTime nextFireTime = animation->nextProgressTime(); if (!nextFireTime.isFinite()) return; m_scheduledAnimations.add(animation); startTimer(0); }
float SVGAnimationElement::calculatePercentForSpline(float percent, unsigned splineIndex) const { ASSERT(calcMode() == CalcModeSpline); ASSERT(splineIndex < m_keySplines.size()); UnitBezier bezier = m_keySplines[splineIndex]; SMILTime duration = simpleDuration(); if (!duration.isFinite()) duration = 100.0; return narrowPrecisionToFloat(bezier.solve(percent, solveEpsilon(duration.value()))); }
void SMILTimeContainer::startTimer(SMILTime fireTime, SMILTime minimumDelay) { if (!m_beginTime || isPaused()) return; if (!fireTime.isFinite()) return; SMILTime delay = max(fireTime - elapsed(), minimumDelay); m_timer.startOneShot(delay.value()); }
void SMILTimeContainer::scheduleAnimationFrame(SMILTime fireTime) { if (!isTimelineRunning()) return; if (!fireTime.isFinite()) return; SMILTime delay = max(fireTime - elapsed(), SMILTime(animationFrameDelay)); m_timer.startOneShot(delay.value()); }
void SMILTimeContainer::scheduleAnimationFrame(SMILTime fireTime) { ASSERT(isTimelineRunning() && fireTime.isFinite()); ASSERT(!m_wakeupTimer.isActive()); SMILTime delay = fireTime - elapsed(); if (delay.value() < AnimationTimeline::s_minimumDelay) { serviceOnNextFrame(); } else { scheduleWakeUp(delay.value() - AnimationTimeline::s_minimumDelay, FutureAnimationFrame); } }
void SMILTimeContainer::updateAnimationsAndScheduleFrameIfNeeded(SMILTime elapsed, bool seekToTime) { #if !ENABLE(OILPAN) DiscardScope discardScope(m_ownerSVGElement); #endif SMILTime earliestFireTime = updateAnimations(elapsed, seekToTime); // If updateAnimations() ended up triggering a synchronization (most likely // via syncbases), then give that priority. if (hasPendingSynchronization()) return; if (!isTimelineRunning()) return; if (!earliestFireTime.isFinite()) return; scheduleAnimationFrame(earliestFireTime); }
void SMILTimeContainer::updateAnimationsAndScheduleFrameIfNeeded(SMILTime elapsed, bool seekToTime) { if (!document().isActive()) return; SMILTime earliestFireTime = updateAnimations(elapsed, seekToTime); // If updateAnimations() ended up triggering a synchronization (most likely // via syncbases), then give that priority. if (hasPendingSynchronization()) return; if (!isTimelineRunning()) return; if (!earliestFireTime.isFinite()) return; scheduleAnimationFrame(earliestFireTime); }
void SMILTimeContainer::schedule(SVGSMILElement* animation, SVGElement* target, const QualifiedName& attributeName) { ASSERT(animation->timeContainer() == this); ASSERT(target); ASSERT(animation->hasValidAttributeName()); #ifndef NDEBUG ASSERT(!m_preventScheduledAnimationsChanges); #endif ElementAttributePair key(target, attributeName); std::unique_ptr<AnimationsVector>& scheduled = m_scheduledAnimations.add(key, nullptr).iterator->value; if (!scheduled) scheduled = std::make_unique<AnimationsVector>(); ASSERT(!scheduled->contains(animation)); scheduled->append(animation); SMILTime nextFireTime = animation->nextProgressTime(); if (nextFireTime.isFinite()) notifyIntervalsChanged(); }
void SMILTimeContainer::schedule(SVGSMILElement* animation, SVGElement* target, const QualifiedName& attributeName) { ASSERT(animation->timeContainer() == this); ASSERT(target); ASSERT(animation->hasValidAttributeName()); #if ENABLE(ASSERT) ASSERT(!m_preventScheduledAnimationsChanges); #endif ElementAttributePair key(target, attributeName); OwnPtrWillBeMember<AnimationsLinkedHashSet>& scheduled = m_scheduledAnimations.add(key, nullptr).storedValue->value; if (!scheduled) scheduled = adoptPtrWillBeNoop(new AnimationsLinkedHashSet); ASSERT(!scheduled->contains(animation)); scheduled->add(animation); SMILTime nextFireTime = animation->nextProgressTime(); if (nextFireTime.isFinite()) notifyIntervalsChanged(); }
void SVGSMILElement::createInstanceTimesFromSyncbase(SVGSMILElement* syncbase, NewOrExistingInterval) { // FIXME: To be really correct, this should handle updating exising interval by changing // the associated times instead of creating new ones. for (unsigned n = 0; n < m_conditions.size(); ++n) { Condition& condition = m_conditions[n]; if (condition.m_type == Condition::Syncbase && condition.m_syncbase == syncbase) { ASSERT(condition.m_name == "begin" || condition.m_name == "end"); // No nested time containers in SVG, no need for crazy time space conversions. Phew! SMILTime time = 0; if (condition.m_name == "begin") time = syncbase->m_intervalBegin + condition.m_offset; else time = syncbase->m_intervalEnd + condition.m_offset; ASSERT(time.isFinite()); if (condition.m_beginOrEnd == Begin) addBeginTime(elapsed(), time); else addEndTime(elapsed(), time); } } }
SMILTime SVGSMILElement::resolveActiveEnd(SMILTime resolvedBegin, SMILTime resolvedEnd) const { // Computing the active duration // http://www.w3.org/TR/SMIL2/smil-timing.html#Timing-ComputingActiveDur SMILTime preliminaryActiveDuration; if (!resolvedEnd.isUnresolved() && dur().isUnresolved() && repeatDur().isUnresolved() && repeatCount().isUnresolved()) preliminaryActiveDuration = resolvedEnd - resolvedBegin; else if (!resolvedEnd.isFinite()) preliminaryActiveDuration = repeatingDuration(); else preliminaryActiveDuration = min(repeatingDuration(), resolvedEnd - resolvedBegin); SMILTime minValue = this->minValue(); SMILTime maxValue = this->maxValue(); if (minValue > maxValue) { // Ignore both. // http://www.w3.org/TR/2001/REC-smil-animation-20010904/#MinMax minValue = 0; maxValue = SMILTime::indefinite(); } return resolvedBegin + min(maxValue, max(minValue, preliminaryActiveDuration)); }
void SMILTimeContainer::schedule(SVGSMILElement* animation, SVGElement* target, const QualifiedName& attributeName) { ASSERT(animation->timeContainer() == this); ASSERT(target); ASSERT(animation->hasValidAttributeName()); #ifndef NDEBUG ASSERT(!m_preventScheduledAnimationsChanges); #endif ElementAttributePair key(target, attributeName); AnimationsVector* scheduled = m_scheduledAnimations.get(key); if (!scheduled) { scheduled = new AnimationsVector(); m_scheduledAnimations.set(key, scheduled); } ASSERT(!scheduled->contains(animation)); scheduled->append(animation); SMILTime nextFireTime = animation->nextProgressTime(); if (nextFireTime.isFinite()) notifyIntervalsChanged(); }
void SMILTimeContainer::updateAnimations(SMILTime elapsed, bool seekToTime) { SMILTime earliestFireTime = SMILTime::unresolved(); #ifndef NDEBUG // This boolean will catch any attempts to schedule/unschedule scheduledAnimations during this critical section. // Similarly, any elements removed will unschedule themselves, so this will catch modification of animationsToApply. m_preventScheduledAnimationsChanges = true; #endif AnimationsVector animationsToApply; GroupedAnimationsMap::iterator end = m_scheduledAnimations.end(); for (GroupedAnimationsMap::iterator it = m_scheduledAnimations.begin(); it != end; ++it) { AnimationsVector* scheduled = it->second; // Sort according to priority. Elements with later begin time have higher priority. // In case of a tie, document order decides. // FIXME: This should also consider timing relationships between the elements. Dependents // have higher priority. sortByPriority(*scheduled, elapsed); SVGSMILElement* resultElement = 0; unsigned size = scheduled->size(); for (unsigned n = 0; n < size; n++) { SVGSMILElement* animation = scheduled->at(n); ASSERT(animation->timeContainer() == this); ASSERT(animation->targetElement()); ASSERT(animation->hasValidAttributeName()); // Results are accumulated to the first animation that animates and contributes to a particular element/attribute pair. if (!resultElement) { if (!animation->hasValidAttributeType()) continue; resultElement = animation; } // This will calculate the contribution from the animation and add it to the resultsElement. if (!animation->progress(elapsed, resultElement, seekToTime) && resultElement == animation) resultElement = 0; SMILTime nextFireTime = animation->nextProgressTime(); if (nextFireTime.isFinite()) earliestFireTime = min(nextFireTime, earliestFireTime); } if (resultElement) animationsToApply.append(resultElement); } unsigned animationsToApplySize = animationsToApply.size(); if (!animationsToApplySize) { #ifndef NDEBUG m_preventScheduledAnimationsChanges = false; #endif startTimer(earliestFireTime, animationFrameDelay); return; } // Apply results to target elements. for (unsigned i = 0; i < animationsToApplySize; ++i) animationsToApply[i]->applyResultsToTarget(); #ifndef NDEBUG m_preventScheduledAnimationsChanges = false; #endif startTimer(earliestFireTime, animationFrameDelay); Document::updateStyleForAllDocuments(); }
SMILTime SMILTimeContainer::updateAnimations(SMILTime elapsed, bool seekToTime) { SMILTime earliestFireTime = SMILTime::unresolved(); #if ENABLE(ASSERT) // This boolean will catch any attempts to schedule/unschedule scheduledAnimations during this critical section. // Similarly, any elements removed will unschedule themselves, so this will catch modification of animationsToApply. m_preventScheduledAnimationsChanges = true; #endif if (m_documentOrderIndexesDirty) updateDocumentOrderIndexes(); WillBeHeapHashSet<ElementAttributePair> invalidKeys; using AnimationsVector = WillBeHeapVector<RefPtrWillBeMember<SVGSMILElement>>; AnimationsVector animationsToApply; for (const auto& entry : m_scheduledAnimations) { if (!entry.key.first || entry.value->isEmpty()) { invalidKeys.add(entry.key); continue; } AnimationsLinkedHashSet* scheduled = entry.value.get(); // Sort according to priority. Elements with later begin time have higher priority. // In case of a tie, document order decides. // FIXME: This should also consider timing relationships between the elements. Dependents // have higher priority. AnimationsVector scheduledAnimations; copyToVector(*scheduled, scheduledAnimations); std::sort(scheduledAnimations.begin(), scheduledAnimations.end(), PriorityCompare(elapsed)); SVGSMILElement* resultElement = nullptr; for (const auto& itAnimation : scheduledAnimations) { SVGSMILElement* animation = itAnimation.get(); ASSERT(animation->timeContainer() == this); ASSERT(animation->targetElement()); ASSERT(animation->hasValidAttributeName()); // Results are accumulated to the first animation that animates and contributes to a particular element/attribute pair. // FIXME: we should ensure that resultElement is of an appropriate type. if (!resultElement) { if (!animation->hasValidAttributeType()) continue; resultElement = animation; } // This will calculate the contribution from the animation and add it to the resultsElement. if (!animation->progress(elapsed, resultElement, seekToTime) && resultElement == animation) resultElement = nullptr; SMILTime nextFireTime = animation->nextProgressTime(); if (nextFireTime.isFinite()) earliestFireTime = std::min(nextFireTime, earliestFireTime); } if (resultElement) animationsToApply.append(resultElement); } m_scheduledAnimations.removeAll(invalidKeys); std::sort(animationsToApply.begin(), animationsToApply.end(), PriorityCompare(elapsed)); unsigned animationsToApplySize = animationsToApply.size(); if (!animationsToApplySize) { #if ENABLE(ASSERT) m_preventScheduledAnimationsChanges = false; #endif return earliestFireTime; } // Apply results to target elements. for (unsigned i = 0; i < animationsToApplySize; ++i) animationsToApply[i]->applyResultsToTarget(); #if ENABLE(ASSERT) m_preventScheduledAnimationsChanges = false; #endif for (unsigned i = 0; i < animationsToApplySize; ++i) { if (animationsToApply[i]->inDocument() && animationsToApply[i]->isSVGDiscardElement()) { RefPtrWillBeRawPtr<SVGSMILElement> animDiscard = animationsToApply[i]; RefPtrWillBeRawPtr<SVGElement> targetElement = animDiscard->targetElement(); if (targetElement && targetElement->inDocument()) { targetElement->remove(IGNORE_EXCEPTION); ASSERT(!targetElement->inDocument()); } if (animDiscard->inDocument()) { animDiscard->remove(IGNORE_EXCEPTION); ASSERT(!animDiscard->inDocument()); } } } return earliestFireTime; }
void SMILTimeContainer::updateAnimations(SMILTime elapsed, bool seekToTime) { SMILTime earliestFireTime = SMILTime::unresolved(); #ifndef NDEBUG // This boolean will catch any attempts to schedule/unschedule scheduledAnimations during this critical section. // Similarly, any elements removed will unschedule themselves, so this will catch modification of animationsToApply. m_preventScheduledAnimationsChanges = true; #endif if (m_documentOrderIndexesDirty) updateDocumentOrderIndexes(); Vector<RefPtr<SVGSMILElement> > animationsToApply; GroupedAnimationsMap::iterator end = m_scheduledAnimations.end(); for (GroupedAnimationsMap::iterator it = m_scheduledAnimations.begin(); it != end; ++it) { AnimationsVector* scheduled = it->value.get(); // Sort according to priority. Elements with later begin time have higher priority. // In case of a tie, document order decides. // FIXME: This should also consider timing relationships between the elements. Dependents // have higher priority. std::sort(scheduled->begin(), scheduled->end(), PriorityCompare(elapsed)); SVGSMILElement* resultElement = 0; unsigned size = scheduled->size(); for (unsigned n = 0; n < size; n++) { SVGSMILElement* animation = scheduled->at(n); ASSERT(animation->timeContainer() == this); ASSERT(animation->targetElement()); ASSERT(animation->hasValidAttributeName()); // Results are accumulated to the first animation that animates and contributes to a particular element/attribute pair. // FIXME: we should ensure that resultElement is of an appropriate type. if (!resultElement) { if (!animation->hasValidAttributeType()) continue; resultElement = animation; } // This will calculate the contribution from the animation and add it to the resultsElement. if (!animation->progress(elapsed, resultElement, seekToTime) && resultElement == animation) resultElement = 0; SMILTime nextFireTime = animation->nextProgressTime(); if (nextFireTime.isFinite()) earliestFireTime = min(nextFireTime, earliestFireTime); } if (resultElement) animationsToApply.append(resultElement); } std::sort(animationsToApply.begin(), animationsToApply.end(), PriorityCompare(elapsed)); unsigned animationsToApplySize = animationsToApply.size(); if (!animationsToApplySize) { #ifndef NDEBUG m_preventScheduledAnimationsChanges = false; #endif scheduleAnimationFrame(earliestFireTime); return; } // Apply results to target elements. for (unsigned i = 0; i < animationsToApplySize; ++i) animationsToApply[i]->applyResultsToTarget(); #ifndef NDEBUG m_preventScheduledAnimationsChanges = false; #endif scheduleAnimationFrame(earliestFireTime); for (unsigned i = 0; i < animationsToApplySize; ++i) { if (animationsToApply[i]->inDocument() && animationsToApply[i]->isSVGDiscardElement()) { RefPtr<SVGSMILElement> animDiscard = animationsToApply[i]; RefPtr<SVGElement> targetElement = animDiscard->targetElement(); if (targetElement && targetElement->inDocument()) { targetElement->remove(IGNORE_EXCEPTION); ASSERT(!targetElement->inDocument()); } if (animDiscard->inDocument()) { animDiscard->remove(IGNORE_EXCEPTION); ASSERT(!animDiscard->inDocument()); } } } }
void SMILTimeContainer::updateAnimations(SMILTime elapsed, bool seekToTime) { SMILTime earliersFireTime = SMILTime::unresolved(); Vector<SVGSMILElement*> toAnimate; copyToVector(m_scheduledAnimations, toAnimate); // Sort according to priority. Elements with later begin time have higher priority. // In case of a tie, document order decides. // FIXME: This should also consider timing relationships between the elements. Dependents // have higher priority. sortByPriority(toAnimate, elapsed); // Calculate animation contributions. typedef pair<SVGElement*, QualifiedName> ElementAttributePair; typedef HashMap<ElementAttributePair, RefPtr<SVGSMILElement> > ResultElementMap; ResultElementMap resultsElements; HashSet<SVGSMILElement*> contributingElements; for (unsigned n = 0; n < toAnimate.size(); ++n) { SVGSMILElement* animation = toAnimate[n]; ASSERT(animation->timeContainer() == this); SVGElement* targetElement = animation->targetElement(); if (!targetElement) continue; QualifiedName attributeName = animation->attributeName(); if (attributeName == anyQName()) { if (animation->hasTagName(SVGNames::animateMotionTag)) attributeName = SVGNames::animateMotionTag; else continue; } // Results are accumulated to the first animation that animates and contributes to a particular element/attribute pair. ElementAttributePair key(targetElement, attributeName); SVGSMILElement* resultElement = resultsElements.get(key).get(); bool accumulatedResultElement = false; if (!resultElement) { if (!animation->hasValidAttributeType()) continue; resultElement = animation; resultsElements.add(key, resultElement); accumulatedResultElement = true; } // This will calculate the contribution from the animation and add it to the resultsElement. if (animation->progress(elapsed, resultElement, seekToTime)) contributingElements.add(resultElement); else if (accumulatedResultElement) resultsElements.remove(key); SMILTime nextFireTime = animation->nextProgressTime(); if (nextFireTime.isFinite()) earliersFireTime = min(nextFireTime, earliersFireTime); } Vector<SVGSMILElement*> animationsToApply; ResultElementMap::iterator end = resultsElements.end(); for (ResultElementMap::iterator it = resultsElements.begin(); it != end; ++it) { SVGSMILElement* animation = it->second.get(); if (contributingElements.contains(animation)) animationsToApply.append(animation); } unsigned animationsToApplySize = animationsToApply.size(); if (!animationsToApplySize) { startTimer(earliersFireTime, animationFrameDelay); return; } // Sort <animateTranform> to be the last one to be applied. <animate> may change transform attribute as // well (directly or indirectly by modifying <use> x/y) and this way transforms combine properly. sortByApplyOrder(animationsToApply); // Apply results to target elements. for (unsigned i = 0; i < animationsToApplySize; ++i) animationsToApply[i]->applyResultsToTarget(); startTimer(earliersFireTime, animationFrameDelay); Document::updateStyleForAllDocuments(); }
void SMILTimeContainer::updateAnimations(SMILTime elapsed, double nextManualSampleTime, const String& nextSamplingTarget) { SMILTime earliersFireTime = SMILTime::unresolved(); Vector<SVGSMILElement*> toAnimate; copyToVector(m_scheduledAnimations, toAnimate); if (nextManualSampleTime) { SMILTime samplingDiff; for (unsigned n = 0; n < toAnimate.size(); ++n) { SVGSMILElement* animation = toAnimate[n]; ASSERT(animation->timeContainer() == this); SVGElement* targetElement = animation->targetElement(); // FIXME: This should probably be using getIdAttribute instead of idForStyleResolution. if (!targetElement || !targetElement->hasID() || targetElement->idForStyleResolution() != nextSamplingTarget) continue; samplingDiff = animation->intervalBegin(); break; } elapsed = SMILTime(nextManualSampleTime) + samplingDiff; } // Sort according to priority. Elements with later begin time have higher priority. // In case of a tie, document order decides. // FIXME: This should also consider timing relationships between the elements. Dependents // have higher priority. sortByPriority(toAnimate, elapsed); // Calculate animation contributions. typedef HashMap<ElementAttributePair, RefPtr<SVGSMILElement> > ResultElementMap; ResultElementMap resultsElements; for (unsigned n = 0; n < toAnimate.size(); ++n) { SVGSMILElement* animation = toAnimate[n]; ASSERT(animation->timeContainer() == this); SVGElement* targetElement = animation->targetElement(); if (!targetElement) continue; QualifiedName attributeName = animation->attributeName(); if (attributeName == anyQName()) { if (animation->hasTagName(SVGNames::animateMotionTag)) attributeName = SVGNames::animateMotionTag; else continue; } // Results are accumulated to the first animation that animates a particular element/attribute pair. ElementAttributePair key(targetElement, attributeName); SVGSMILElement* resultElement = resultsElements.get(key).get(); if (!resultElement) { if (!animation->hasValidAttributeType()) continue; resultElement = animation; resultElement->resetToBaseValue(baseValueFor(key)); resultsElements.add(key, resultElement); } // This will calculate the contribution from the animation and add it to the resultsElement. animation->progress(elapsed, resultElement); SMILTime nextFireTime = animation->nextProgressTime(); if (nextFireTime.isFinite()) earliersFireTime = min(nextFireTime, earliersFireTime); } Vector<SVGSMILElement*> animationsToApply; ResultElementMap::iterator end = resultsElements.end(); for (ResultElementMap::iterator it = resultsElements.begin(); it != end; ++it) animationsToApply.append(it->second.get()); // Sort <animateTranform> to be the last one to be applied. <animate> may change transform attribute as // well (directly or indirectly by modifying <use> x/y) and this way transforms combine properly. sortByApplyOrder(animationsToApply); // Apply results to target elements. for (unsigned n = 0; n < animationsToApply.size(); ++n) animationsToApply[n]->applyResultsToTarget(); startTimer(earliersFireTime, animationFrameDelay); Document::updateStyleForAllDocuments(); }
SMILTime SVGSMILElement::calculateNextProgressTime(SMILTime elapsed) const { if (m_activeState == Active) { // If duration is indefinite the value does not actually change over time. Same is true for <set>. SMILTime simpleDuration = this->simpleDuration(); if (simpleDuration.isIndefinite() || hasTagName(SVGNames::setTag)) { SMILTime repeatingDurationEnd = m_intervalBegin + repeatingDuration(); // We are supposed to do freeze semantics when repeating ends, even if the element is still active. // Take care that we get a timer callback at that point. if (elapsed < repeatingDurationEnd && repeatingDurationEnd < m_intervalEnd && repeatingDurationEnd.isFinite()) return repeatingDurationEnd; return m_intervalEnd; } return elapsed + 0.025; } return m_intervalBegin >= elapsed ? m_intervalBegin : SMILTime::unresolved(); }