/opensource.apple.com/source/WebCore/WebCore-1298.39/svg/animation/SMILTimeContainer.cpp
C++ | 347 lines | 274 code | 51 blank | 22 comment | 13 complexity | d6c82e7bfe98a8029ed60458d29324c1 MD5 | raw file
Possible License(s): MPL-2.0-no-copyleft-exception, GPL-2.0, BSD-3-Clause, GPL-3.0, MPL-2.0, LGPL-2.0, LGPL-2.1, CC-BY-SA-3.0, IPL-1.0, ISC, AGPL-1.0, AGPL-3.0, JSON, Apache-2.0, 0BSD
- <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
- <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
- <head>
- <title>SMILTimeContainer.cpp</title>
- <style type="text/css">
- .enscript-comment { font-style: italic; color: rgb(178,34,34); }
- .enscript-function-name { font-weight: bold; color: rgb(0,0,255); }
- .enscript-variable-name { font-weight: bold; color: rgb(184,134,11); }
- .enscript-keyword { font-weight: bold; color: rgb(160,32,240); }
- .enscript-reference { font-weight: bold; color: rgb(95,158,160); }
- .enscript-string { font-weight: bold; color: rgb(188,143,143); }
- .enscript-builtin { font-weight: bold; color: rgb(218,112,214); }
- .enscript-type { font-weight: bold; color: rgb(34,139,34); }
- .enscript-highlight { text-decoration: underline; color: 0; }
- </style>
- </head>
- <body id="top">
- <h1 style="margin:8px;" id="f1">SMILTimeContainer.cpp <span style="font-weight: normal; font-size: 0.5em;">[<a href="?txt">plain text</a>]</span></h1>
- <hr/>
- <div></div>
- <pre>
- <span class="enscript-comment">/*
- * Copyright (C) 2008 Apple Inc. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
- * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */</span>
- #<span class="enscript-reference">include</span> <span class="enscript-string">"config.h"</span>
- #<span class="enscript-reference">include</span> <span class="enscript-string">"SMILTimeContainer.h"</span>
- #<span class="enscript-reference">if</span> <span class="enscript-variable-name">ENABLE</span>(<span class="enscript-variable-name">SVG</span>)
- #<span class="enscript-reference">include</span> <span class="enscript-string">"CSSComputedStyleDeclaration.h"</span>
- #<span class="enscript-reference">include</span> <span class="enscript-string">"CSSParser.h"</span>
- #<span class="enscript-reference">include</span> <span class="enscript-string">"Document.h"</span>
- #<span class="enscript-reference">include</span> <span class="enscript-string">"SVGAnimationElement.h"</span>
- #<span class="enscript-reference">include</span> <span class="enscript-string">"SVGNames.h"</span>
- #<span class="enscript-reference">include</span> <span class="enscript-string">"SVGSMILElement.h"</span>
- #<span class="enscript-reference">include</span> <span class="enscript-string">"SVGSVGElement.h"</span>
- #<span class="enscript-reference">include</span> <span class="enscript-string"><wtf/CurrentTime.h></span>
- using namespace std;
- namespace WebCore {
-
- <span class="enscript-type">static</span> <span class="enscript-type">const</span> <span class="enscript-type">double</span> animationFrameDelay = 0.025;
- <span class="enscript-function-name">SMILTimeContainer::SMILTimeContainer</span>(SVGSVGElement* owner)
- : m_beginTime(0)
- , m_pauseTime(0)
- , m_accumulatedPauseTime(0)
- , m_nextManualSampleTime(0)
- , m_documentOrderIndexesDirty(false)
- , m_timer(<span class="enscript-keyword">this</span>, &SMILTimeContainer::timerFired)
- , m_ownerSVGElement(owner)
- {
- }
-
- #<span class="enscript-reference">if</span> !<span class="enscript-variable-name">ENABLE</span>(<span class="enscript-variable-name">SVG_ANIMATION</span>)
- <span class="enscript-type">void</span> <span class="enscript-function-name">SMILTimeContainer::begin</span>() {}
- <span class="enscript-type">void</span> <span class="enscript-function-name">SMILTimeContainer::pause</span>() {}
- <span class="enscript-type">void</span> <span class="enscript-function-name">SMILTimeContainer::resume</span>() {}
- SMILTime <span class="enscript-function-name">SMILTimeContainer::elapsed</span>() <span class="enscript-type">const</span> { <span class="enscript-keyword">return</span> 0; }
- <span class="enscript-type">bool</span> <span class="enscript-function-name">SMILTimeContainer::isPaused</span>() <span class="enscript-type">const</span> { <span class="enscript-keyword">return</span> false; }
- <span class="enscript-type">void</span> <span class="enscript-function-name">SMILTimeContainer::timerFired</span>(Timer<SMILTimeContainer>*) {}
- #<span class="enscript-reference">else</span>
-
- <span class="enscript-type">void</span> <span class="enscript-function-name">SMILTimeContainer::schedule</span>(SVGSMILElement* animation)
- {
- ASSERT(animation->timeContainer() == <span class="enscript-keyword">this</span>);
- SMILTime nextFireTime = animation->nextProgressTime();
- <span class="enscript-keyword">if</span> (!nextFireTime.isFinite())
- <span class="enscript-keyword">return</span>;
- m_scheduledAnimations.add(animation);
- startTimer(0);
- }
-
- <span class="enscript-type">void</span> <span class="enscript-function-name">SMILTimeContainer::unschedule</span>(SVGSMILElement* animation)
- {
- ASSERT(animation->timeContainer() == <span class="enscript-keyword">this</span>);
- m_scheduledAnimations.remove(animation);
- }
- SMILTime <span class="enscript-function-name">SMILTimeContainer::elapsed</span>() <span class="enscript-type">const</span>
- {
- <span class="enscript-keyword">if</span> (!m_beginTime)
- <span class="enscript-keyword">return</span> 0;
- <span class="enscript-keyword">return</span> currentTime() - m_beginTime - m_accumulatedPauseTime;
- }
-
- <span class="enscript-type">bool</span> <span class="enscript-function-name">SMILTimeContainer::isActive</span>() <span class="enscript-type">const</span>
- {
- <span class="enscript-keyword">return</span> m_beginTime && !isPaused();
- }
-
- <span class="enscript-type">bool</span> <span class="enscript-function-name">SMILTimeContainer::isPaused</span>() <span class="enscript-type">const</span>
- {
- <span class="enscript-keyword">return</span> m_pauseTime;
- }
- <span class="enscript-type">void</span> <span class="enscript-function-name">SMILTimeContainer::begin</span>()
- {
- ASSERT(!m_beginTime);
- m_beginTime = currentTime();
- updateAnimations(0);
- }
- <span class="enscript-type">void</span> <span class="enscript-function-name">SMILTimeContainer::pause</span>()
- {
- <span class="enscript-keyword">if</span> (!m_beginTime)
- <span class="enscript-keyword">return</span>;
- ASSERT(!isPaused());
- m_pauseTime = currentTime();
- m_timer.stop();
- }
- <span class="enscript-type">void</span> <span class="enscript-function-name">SMILTimeContainer::resume</span>()
- {
- <span class="enscript-keyword">if</span> (!m_beginTime)
- <span class="enscript-keyword">return</span>;
- ASSERT(isPaused());
- m_accumulatedPauseTime += currentTime() - m_pauseTime;
- m_pauseTime = 0;
- startTimer(0);
- }
- <span class="enscript-type">void</span> <span class="enscript-function-name">SMILTimeContainer::startTimer</span>(SMILTime fireTime, SMILTime minimumDelay)
- {
- <span class="enscript-keyword">if</span> (!m_beginTime || isPaused())
- <span class="enscript-keyword">return</span>;
-
- <span class="enscript-keyword">if</span> (!fireTime.isFinite())
- <span class="enscript-keyword">return</span>;
-
- SMILTime delay = max(fireTime - elapsed(), minimumDelay);
- m_timer.startOneShot(delay.value());
- }
-
- <span class="enscript-type">void</span> <span class="enscript-function-name">SMILTimeContainer::timerFired</span>(Timer<SMILTimeContainer>*)
- {
- ASSERT(m_beginTime);
- ASSERT(!m_pauseTime);
- SMILTime elapsed = <span class="enscript-keyword">this</span>->elapsed();
- updateAnimations(elapsed);
- }
-
- <span class="enscript-type">void</span> <span class="enscript-function-name">SMILTimeContainer::updateDocumentOrderIndexes</span>()
- {
- <span class="enscript-type">unsigned</span> timingElementCount = 0;
- <span class="enscript-keyword">for</span> (Node* node = m_ownerSVGElement; node; node = node->traverseNextNode(m_ownerSVGElement)) {
- <span class="enscript-keyword">if</span> (SVGSMILElement::isSMILElement(node))
- static_cast<SVGSMILElement*>(node)->setDocumentOrderIndex(timingElementCount++);
- }
- m_documentOrderIndexesDirty = false;
- }
- <span class="enscript-type">struct</span> PriorityCompare {
- PriorityCompare(SMILTime elapsed) : m_elapsed(elapsed) {}
- <span class="enscript-type">bool</span> <span class="enscript-keyword">operator</span>()(SVGSMILElement* a, SVGSMILElement* b)
- {
- <span class="enscript-comment">// FIXME: This should also consider possible timing relations between the elements.
- </span> SMILTime aBegin = a->intervalBegin();
- SMILTime bBegin = b->intervalBegin();
- <span class="enscript-comment">// Frozen elements need to be prioritized based on their previous interval.
- </span> aBegin = a->isFrozen() && m_elapsed < aBegin ? a->previousIntervalBegin() : aBegin;
- bBegin = b->isFrozen() && m_elapsed < bBegin ? b->previousIntervalBegin() : bBegin;
- <span class="enscript-keyword">if</span> (aBegin == bBegin)
- <span class="enscript-keyword">return</span> a->documentOrderIndex() < b->documentOrderIndex();
- <span class="enscript-keyword">return</span> aBegin < bBegin;
- }
- SMILTime m_elapsed;
- };
- <span class="enscript-type">void</span> <span class="enscript-function-name">SMILTimeContainer::sortByPriority</span>(Vector<SVGSMILElement*>& smilElements, SMILTime elapsed)
- {
- <span class="enscript-keyword">if</span> (m_documentOrderIndexesDirty)
- updateDocumentOrderIndexes();
- <span class="enscript-reference">std</span>::sort(smilElements.begin(), smilElements.end(), PriorityCompare(elapsed));
- }
-
- <span class="enscript-type">static</span> <span class="enscript-type">bool</span> <span class="enscript-function-name">applyOrderSortFunction</span>(SVGSMILElement* a, SVGSMILElement* b)
- {
- <span class="enscript-keyword">if</span> (!a->hasTagName(SVGNames::animateTransformTag) && b->hasTagName(SVGNames::animateTransformTag))
- <span class="enscript-keyword">return</span> true;
- <span class="enscript-keyword">return</span> false;
- }
-
- <span class="enscript-type">static</span> <span class="enscript-type">void</span> <span class="enscript-function-name">sortByApplyOrder</span>(Vector<SVGSMILElement*>& smilElements)
- {
- <span class="enscript-reference">std</span>::sort(smilElements.begin(), smilElements.end(), applyOrderSortFunction);
- }
- String <span class="enscript-function-name">SMILTimeContainer::baseValueFor</span>(ElementAttributePair key)
- {
- <span class="enscript-comment">// FIXME: We wouldn't need to do this if we were keeping base values around properly in DOM.
- </span> <span class="enscript-comment">// Currently animation overwrites them so we need to save them somewhere.
- </span> <span class="enscript-reference">BaseValueMap</span>::iterator it = m_savedBaseValues.find(key);
- <span class="enscript-keyword">if</span> (it != m_savedBaseValues.end())
- <span class="enscript-keyword">return</span> it->second;
-
- SVGElement* targetElement = key.first;
- QualifiedName attributeName = key.second;
- ASSERT(targetElement);
- ASSERT(attributeName != anyQName());
- String baseValue;
- <span class="enscript-keyword">if</span> (SVGAnimationElement::isTargetAttributeCSSProperty(targetElement, attributeName))
- baseValue = computedStyle(targetElement)->getPropertyValue(cssPropertyID(attributeName.localName()));
- <span class="enscript-keyword">else</span>
- baseValue = targetElement->getAttribute(attributeName);
- m_savedBaseValues.add(key, baseValue);
- <span class="enscript-keyword">return</span> baseValue;
- }
- <span class="enscript-type">void</span> <span class="enscript-function-name">SMILTimeContainer::sampleAnimationAtTime</span>(<span class="enscript-type">const</span> String& elementId, <span class="enscript-type">double</span> newTime)
- {
- ASSERT(m_beginTime);
- ASSERT(!isPaused());
- <span class="enscript-comment">// Fast-forward to the time DRT wants to sample
- </span> m_timer.stop();
- m_nextSamplingTarget = elementId;
- m_nextManualSampleTime = newTime;
- updateAnimations(elapsed());
- }
- <span class="enscript-type">void</span> <span class="enscript-function-name">SMILTimeContainer::updateAnimations</span>(SMILTime elapsed)
- {
- SMILTime earliersFireTime = SMILTime::unresolved();
- Vector<SVGSMILElement*> toAnimate;
- copyToVector(m_scheduledAnimations, toAnimate);
- <span class="enscript-keyword">if</span> (m_nextManualSampleTime) {
- SMILTime samplingDiff;
- <span class="enscript-keyword">for</span> (<span class="enscript-type">unsigned</span> n = 0; n < toAnimate.size(); ++n) {
- SVGSMILElement* animation = toAnimate[n];
- ASSERT(animation->timeContainer() == <span class="enscript-keyword">this</span>);
- SVGElement* targetElement = animation->targetElement();
- <span class="enscript-comment">// FIXME: This should probably be using getIdAttribute instead of idForStyleResolution.
- </span> <span class="enscript-keyword">if</span> (!targetElement || !targetElement->hasID() || targetElement->idForStyleResolution() != m_nextSamplingTarget)
- <span class="enscript-keyword">continue</span>;
- samplingDiff = animation->intervalBegin();
- <span class="enscript-keyword">break</span>;
- }
- elapsed = SMILTime(m_nextManualSampleTime) + samplingDiff;
- m_nextManualSampleTime = 0;
- }
- <span class="enscript-comment">// Sort according to priority. Elements with later begin time have higher priority.
- </span> <span class="enscript-comment">// In case of a tie, document order decides.
- </span> <span class="enscript-comment">// FIXME: This should also consider timing relationships between the elements. Dependents
- </span> <span class="enscript-comment">// have higher priority.
- </span> sortByPriority(toAnimate, elapsed);
-
- <span class="enscript-comment">// Calculate animation contributions.
- </span> <span class="enscript-type">typedef</span> HashMap<ElementAttributePair, RefPtr<SVGSMILElement> > ResultElementMap;
- ResultElementMap resultsElements;
- <span class="enscript-keyword">for</span> (<span class="enscript-type">unsigned</span> n = 0; n < toAnimate.size(); ++n) {
- SVGSMILElement* animation = toAnimate[n];
- ASSERT(animation->timeContainer() == <span class="enscript-keyword">this</span>);
- SVGElement* targetElement = animation->targetElement();
- <span class="enscript-keyword">if</span> (!targetElement)
- <span class="enscript-keyword">continue</span>;
-
- QualifiedName attributeName = animation->attributeName();
- <span class="enscript-keyword">if</span> (attributeName == anyQName()) {
- <span class="enscript-keyword">if</span> (animation->hasTagName(SVGNames::animateMotionTag))
- attributeName = SVGNames::animateMotionTag;
- <span class="enscript-keyword">else</span>
- <span class="enscript-keyword">continue</span>;
- }
-
- <span class="enscript-comment">// Results are accumulated to the first animation that animates a particular element/attribute pair.
- </span> ElementAttributePair key(targetElement, attributeName);
- SVGSMILElement* resultElement = resultsElements.get(key).get();
- <span class="enscript-keyword">if</span> (!resultElement) {
- <span class="enscript-keyword">if</span> (!animation->hasValidAttributeType())
- <span class="enscript-keyword">continue</span>;
- resultElement = animation;
- resultElement->resetToBaseValue(baseValueFor(key));
- resultsElements.add(key, resultElement);
- }
- <span class="enscript-comment">// This will calculate the contribution from the animation and add it to the resultsElement.
- </span> animation->progress(elapsed, resultElement);
- SMILTime nextFireTime = animation->nextProgressTime();
- <span class="enscript-keyword">if</span> (nextFireTime.isFinite())
- earliersFireTime = min(nextFireTime, earliersFireTime);
- <span class="enscript-keyword">else</span> <span class="enscript-keyword">if</span> (!animation->isContributing(elapsed)) {
- m_scheduledAnimations.remove(animation);
- <span class="enscript-keyword">if</span> (m_scheduledAnimations.isEmpty())
- m_savedBaseValues.clear();
- }
- }
-
- Vector<SVGSMILElement*> animationsToApply;
- <span class="enscript-reference">ResultElementMap</span>::iterator end = resultsElements.end();
- <span class="enscript-keyword">for</span> (ResultElementMap::iterator it = resultsElements.begin(); it != end; ++it)
- animationsToApply.append(it->second.get());
- <span class="enscript-comment">// Sort <animateTranform> to be the last one to be applied. <animate> may change transform attribute as
- </span> <span class="enscript-comment">// well (directly or indirectly by modifying <use> x/y) and this way transforms combine properly.
- </span> sortByApplyOrder(animationsToApply);
-
- <span class="enscript-comment">// Apply results to target elements.
- </span> <span class="enscript-keyword">for</span> (<span class="enscript-type">unsigned</span> n = 0; n < animationsToApply.size(); ++n)
- animationsToApply[n]->applyResultsToTarget();
- startTimer(earliersFireTime, animationFrameDelay);
-
- <span class="enscript-reference">Document</span>::updateStyleForAllDocuments();
- }
- #<span class="enscript-reference">endif</span>
- }
- #<span class="enscript-reference">endif</span> // <span class="enscript-variable-name">ENABLE</span>(<span class="enscript-variable-name">SVG</span>)
- </pre>
- <hr />
- </body></html>