KHTML
SMILTimeContainer.cpp
Go to the documentation of this file.
00001 /* 00002 * Copyright (C) 2008 Apple Inc. All Rights Reserved. 00003 * 00004 * Redistribution and use in source and binary forms, with or without 00005 * modification, are permitted provided that the following conditions 00006 * are met: 00007 * 1. Redistributions of source code must retain the above copyright 00008 * notice, this list of conditions and the following disclaimer. 00009 * 2. Redistributions in binary form must reproduce the above copyright 00010 * notice, this list of conditions and the following disclaimer in the 00011 * documentation and/or other materials provided with the distribution. 00012 * 00013 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY 00014 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 00015 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 00016 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR 00017 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 00018 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 00019 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 00020 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 00021 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 00022 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 00023 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 00024 */ 00025 00026 #include "config.h" 00027 #include "SMILTimeContainer.h" 00028 00029 #include "CSSComputedStyleDeclaration.h" 00030 #include "CSSParser.h" 00031 #include "Document.h" 00032 #include "SVGAnimationElement.h" 00033 #include "SVGSMILElement.h" 00034 #include "SVGSVGElement.h" 00035 #include "SystemTime.h" 00036 00037 using namespace std; 00038 00039 namespace WebCore { 00040 00041 static const double animationFrameDelay = 0.025; 00042 00043 SMILTimeContainer::SMILTimeContainer(SVGSVGElement* owner) 00044 : m_beginTime(0) 00045 , m_pauseTime(0) 00046 , m_accumulatedPauseTime(0) 00047 , m_documentOrderIndexesDirty(false) 00048 , m_timer(this, &SMILTimeContainer::timerFired) 00049 , m_ownerSVGElement(owner) 00050 { 00051 } 00052 00053 #if !ENABLE(SVG_ANIMATION) 00054 void SMILTimeContainer::begin() {} 00055 void SMILTimeContainer::pause() {} 00056 void SMILTimeContainer::resume() {} 00057 SMILTime SMILTimeContainer::elapsed() const { return 0; } 00058 bool SMILTimeContainer::isPaused() const { return false; } 00059 void SMILTimeContainer::timerFired(Timer<SMILTimeContainer>*) {} 00060 #else 00061 00062 void SMILTimeContainer::schedule(SVGSMILElement* animation) 00063 { 00064 ASSERT(animation->timeContainer() == this); 00065 SMILTime nextFireTime = animation->nextProgressTime(); 00066 if (!nextFireTime.isFinite()) 00067 return; 00068 m_scheduledAnimations.add(animation); 00069 startTimer(0); 00070 } 00071 00072 void SMILTimeContainer::unschedule(SVGSMILElement* animation) 00073 { 00074 ASSERT(animation->timeContainer() == this); 00075 00076 m_scheduledAnimations.remove(animation); 00077 } 00078 00079 SMILTime SMILTimeContainer::elapsed() const 00080 { 00081 if (!m_beginTime) 00082 return 0; 00083 return currentTime() - m_beginTime - m_accumulatedPauseTime; 00084 } 00085 00086 bool SMILTimeContainer::isActive() const 00087 { 00088 return m_beginTime && !isPaused(); 00089 } 00090 00091 bool SMILTimeContainer::isPaused() const 00092 { 00093 return m_pauseTime; 00094 } 00095 00096 void SMILTimeContainer::begin() 00097 { 00098 ASSERT(!m_beginTime); 00099 m_beginTime = currentTime(); 00100 updateAnimations(0); 00101 } 00102 00103 void SMILTimeContainer::pause() 00104 { 00105 if (!m_beginTime) 00106 return; 00107 ASSERT(!isPaused()); 00108 m_pauseTime = currentTime(); 00109 m_timer.stop(); 00110 } 00111 00112 void SMILTimeContainer::resume() 00113 { 00114 if (!m_beginTime) 00115 return; 00116 ASSERT(isPaused()); 00117 m_accumulatedPauseTime += currentTime() - m_pauseTime; 00118 m_pauseTime = 0; 00119 startTimer(0); 00120 } 00121 00122 void SMILTimeContainer::startTimer(SMILTime fireTime, SMILTime minimumDelay) 00123 { 00124 if (!m_beginTime || isPaused()) 00125 return; 00126 00127 if (!fireTime.isFinite()) 00128 return; 00129 00130 SMILTime delay = max(fireTime - elapsed(), minimumDelay); 00131 m_timer.startOneShot(delay.value()); 00132 } 00133 00134 void SMILTimeContainer::timerFired(Timer<SMILTimeContainer>*) 00135 { 00136 ASSERT(m_beginTime); 00137 ASSERT(!m_pauseTime); 00138 SMILTime elapsed = this->elapsed(); 00139 updateAnimations(elapsed); 00140 } 00141 00142 void SMILTimeContainer::updateDocumentOrderIndexes() 00143 { 00144 unsigned timingElementCount = 0; 00145 for (Node* node = m_ownerSVGElement; node; node = node->traverseNextNode(m_ownerSVGElement)) { 00146 if (SVGSMILElement::isSMILElement(node)) 00147 static_cast<SVGSMILElement*>(node)->setDocumentOrderIndex(timingElementCount++); 00148 } 00149 m_documentOrderIndexesDirty = false; 00150 } 00151 00152 struct PriorityCompare { 00153 PriorityCompare(SMILTime elapsed) : m_elapsed(elapsed) {} 00154 bool operator()(SVGSMILElement* a, SVGSMILElement* b) 00155 { 00156 // FIXME: This should also consider possible timing relations between the elements. 00157 SMILTime aBegin = a->intervalBegin(); 00158 SMILTime bBegin = b->intervalBegin(); 00159 // Frozen elements need to be prioritized based on their previous interval. 00160 aBegin = a->isFrozen() && m_elapsed < aBegin ? a->previousIntervalBegin() : aBegin; 00161 bBegin = b->isFrozen() && m_elapsed < bBegin ? b->previousIntervalBegin() : bBegin; 00162 if (aBegin == bBegin) 00163 return a->documentOrderIndex() < b->documentOrderIndex(); 00164 return aBegin < bBegin; 00165 } 00166 SMILTime m_elapsed; 00167 }; 00168 00169 void SMILTimeContainer::sortByPriority(Vector<SVGSMILElement*>& smilElements, SMILTime elapsed) 00170 { 00171 if (m_documentOrderIndexesDirty) 00172 updateDocumentOrderIndexes(); 00173 std::sort(smilElements.begin(), smilElements.end(), PriorityCompare(elapsed)); 00174 } 00175 00176 static bool applyOrderSortFunction(SVGSMILElement* a, SVGSMILElement* b) 00177 { 00178 if (!a->hasTagName(SVGNames::animateTransformTag) && b->hasTagName(SVGNames::animateTransformTag)) 00179 return true; 00180 return false; 00181 } 00182 00183 static void sortByApplyOrder(Vector<SVGSMILElement*>& smilElements) 00184 { 00185 std::sort(smilElements.begin(), smilElements.end(), applyOrderSortFunction); 00186 } 00187 00188 String SMILTimeContainer::baseValueFor(ElementAttributePair key) 00189 { 00190 // FIXME: We wouldn't need to do this if we were keeping base values around properly in DOM. 00191 // Currently animation overwrites them so we need to save them somewhere. 00192 BaseValueMap::iterator it = m_savedBaseValues.find(key); 00193 if (it != m_savedBaseValues.end()) 00194 return it->second; 00195 00196 SVGElement* target = key.first; 00197 String attributeName = key.second; 00198 ASSERT(target); 00199 ASSERT(!attributeName.isEmpty()); 00200 String baseValue; 00201 if (SVGAnimationElement::attributeIsCSS(attributeName)) { 00202 CSSComputedStyleDeclaration computedStyle(target); 00203 baseValue = computedStyle.getPropertyValue(cssPropertyID(attributeName)); 00204 } else 00205 baseValue = target->getAttribute(attributeName); 00206 m_savedBaseValues.add(key, baseValue); 00207 return baseValue; 00208 } 00209 00210 void SMILTimeContainer::updateAnimations(SMILTime elapsed) 00211 { 00212 SMILTime earliersFireTime = SMILTime::unresolved(); 00213 00214 Vector<SVGSMILElement*> toAnimate; 00215 copyToVector(m_scheduledAnimations, toAnimate); 00216 00217 // Sort according to priority. Elements with later begin time have higher priority. 00218 // In case of a tie, document order decides. 00219 // FIXME: This should also consider timing relationships between the elements. Dependents 00220 // have higher priority. 00221 sortByPriority(toAnimate, elapsed); 00222 00223 // Calculate animation contributions. 00224 typedef HashMap<ElementAttributePair, SVGSMILElement*> ResultElementMap; 00225 ResultElementMap resultsElements; 00226 for (unsigned n = 0; n < toAnimate.size(); ++n) { 00227 SVGSMILElement* animation = toAnimate[n]; 00228 ASSERT(animation->timeContainer() == this); 00229 00230 SVGElement* targetElement = animation->targetElement(); 00231 if (!targetElement) 00232 continue; 00233 String attributeName = animation->attributeName(); 00234 if (attributeName.isEmpty()) { 00235 if (animation->hasTagName(SVGNames::animateMotionTag)) 00236 attributeName = SVGNames::animateMotionTag.localName(); 00237 else 00238 continue; 00239 } 00240 00241 // Results are accumulated to the first animation that animates a particular element/attribute pair. 00242 ElementAttributePair key(targetElement, attributeName); 00243 SVGSMILElement* resultElement = resultsElements.get(key); 00244 if (!resultElement) { 00245 resultElement = animation; 00246 resultElement->resetToBaseValue(baseValueFor(key)); 00247 resultsElements.add(key, resultElement); 00248 } 00249 00250 // This will calculate the contribution from the animation and add it to the resultsElement. 00251 animation->progress(elapsed, resultElement); 00252 00253 SMILTime nextFireTime = animation->nextProgressTime(); 00254 if (nextFireTime.isFinite()) 00255 earliersFireTime = min(nextFireTime, earliersFireTime); 00256 else if (!animation->isContributing(elapsed)) { 00257 m_scheduledAnimations.remove(animation); 00258 if (m_scheduledAnimations.isEmpty()) 00259 m_savedBaseValues.clear(); 00260 } 00261 } 00262 00263 Vector<SVGSMILElement*> animationsToApply; 00264 ResultElementMap::iterator end = resultsElements.end(); 00265 for (ResultElementMap::iterator it = resultsElements.begin(); it != end; ++it) 00266 animationsToApply.append(it->second); 00267 00268 // Sort <animateTranform> to be the last one to be applied. <animate> may change transform attribute as 00269 // well (directly or indirectly by modifying <use> x/y) and this way transforms combine properly. 00270 sortByApplyOrder(animationsToApply); 00271 00272 // Apply results to target elements. 00273 for (unsigned n = 0; n < animationsToApply.size(); ++n) 00274 animationsToApply[n]->applyResultsToTarget(); 00275 00276 startTimer(earliersFireTime, animationFrameDelay); 00277 00278 Document::updateDocumentsRendering(); 00279 } 00280 00281 #endif 00282 } 00283
KDE 4.6 API Reference