KHtml

SMILTimeContainer.cpp
1 /*
2  * Copyright (C) 2008 Apple Inc. All Rights Reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  * notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  * notice, this list of conditions and the following disclaimer in the
11  * documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25 
26 #include "SMILTimeContainer.h"
27 
28 #include "CSSComputedStyleDeclaration.h"
29 #include "CSSParser.h"
30 #include "Document.h"
31 #include "SVGAnimationElement.h"
32 #include "SVGSMILElement.h"
33 #include "SVGSVGElement.h"
34 #include "SystemTime.h"
35 
36 using namespace std;
37 
38 namespace WebCore
39 {
40 
41 static const double animationFrameDelay = 0.025;
42 
43 SMILTimeContainer::SMILTimeContainer(SVGSVGElement *owner)
44  : m_beginTime(0)
45  , m_pauseTime(0)
46  , m_accumulatedPauseTime(0)
47  , m_documentOrderIndexesDirty(false)
48  , m_timer(this, &SMILTimeContainer::timerFired)
49  , m_ownerSVGElement(owner)
50 {
51 }
52 
53 #if !ENABLE(SVG_ANIMATION)
54 void SMILTimeContainer::begin() {}
55 void SMILTimeContainer::pause() {}
56 void SMILTimeContainer::resume() {}
57 SMILTime SMILTimeContainer::elapsed() const
58 {
59  return 0;
60 }
61 bool SMILTimeContainer::isPaused() const
62 {
63  return false;
64 }
65 void SMILTimeContainer::timerFired(Timer<SMILTimeContainer> *) {}
66 #else
67 
68 void SMILTimeContainer::schedule(SVGSMILElement *animation)
69 {
70  ASSERT(animation->timeContainer() == this);
71  SMILTime nextFireTime = animation->nextProgressTime();
72  if (!nextFireTime.isFinite()) {
73  return;
74  }
75  m_scheduledAnimations.add(animation);
76  startTimer(0);
77 }
78 
79 void SMILTimeContainer::unschedule(SVGSMILElement *animation)
80 {
81  ASSERT(animation->timeContainer() == this);
82 
83  m_scheduledAnimations.remove(animation);
84 }
85 
86 SMILTime SMILTimeContainer::elapsed() const
87 {
88  if (!m_beginTime) {
89  return 0;
90  }
91  return currentTime() - m_beginTime - m_accumulatedPauseTime;
92 }
93 
94 bool SMILTimeContainer::isActive() const
95 {
96  return m_beginTime && !isPaused();
97 }
98 
99 bool SMILTimeContainer::isPaused() const
100 {
101  return m_pauseTime;
102 }
103 
104 void SMILTimeContainer::begin()
105 {
106  ASSERT(!m_beginTime);
107  m_beginTime = currentTime();
108  updateAnimations(0);
109 }
110 
111 void SMILTimeContainer::pause()
112 {
113  if (!m_beginTime) {
114  return;
115  }
116  ASSERT(!isPaused());
117  m_pauseTime = currentTime();
118  m_timer.stop();
119 }
120 
121 void SMILTimeContainer::resume()
122 {
123  if (!m_beginTime) {
124  return;
125  }
126  ASSERT(isPaused());
127  m_accumulatedPauseTime += currentTime() - m_pauseTime;
128  m_pauseTime = 0;
129  startTimer(0);
130 }
131 
132 void SMILTimeContainer::startTimer(SMILTime fireTime, SMILTime minimumDelay)
133 {
134  if (!m_beginTime || isPaused()) {
135  return;
136  }
137 
138  if (!fireTime.isFinite()) {
139  return;
140  }
141 
142  SMILTime delay = max(fireTime - elapsed(), minimumDelay);
143  m_timer.startOneShot(delay.value());
144 }
145 
146 void SMILTimeContainer::timerFired(Timer<SMILTimeContainer> *)
147 {
148  ASSERT(m_beginTime);
149  ASSERT(!m_pauseTime);
150  SMILTime elapsed = this->elapsed();
151  updateAnimations(elapsed);
152 }
153 
154 void SMILTimeContainer::updateDocumentOrderIndexes()
155 {
156  unsigned timingElementCount = 0;
157  for (Node *node = m_ownerSVGElement; node; node = node->traverseNextNode(m_ownerSVGElement)) {
158  if (SVGSMILElement::isSMILElement(node)) {
159  static_cast<SVGSMILElement *>(node)->setDocumentOrderIndex(timingElementCount++);
160  }
161  }
162  m_documentOrderIndexesDirty = false;
163 }
164 
165 struct PriorityCompare {
166  PriorityCompare(SMILTime elapsed) : m_elapsed(elapsed) {}
167  bool operator()(SVGSMILElement *a, SVGSMILElement *b)
168  {
169  // FIXME: This should also consider possible timing relations between the elements.
170  SMILTime aBegin = a->intervalBegin();
171  SMILTime bBegin = b->intervalBegin();
172  // Frozen elements need to be prioritized based on their previous interval.
173  aBegin = a->isFrozen() && m_elapsed < aBegin ? a->previousIntervalBegin() : aBegin;
174  bBegin = b->isFrozen() && m_elapsed < bBegin ? b->previousIntervalBegin() : bBegin;
175  if (aBegin == bBegin) {
176  return a->documentOrderIndex() < b->documentOrderIndex();
177  }
178  return aBegin < bBegin;
179  }
180  SMILTime m_elapsed;
181 };
182 
183 void SMILTimeContainer::sortByPriority(Vector<SVGSMILElement *> &smilElements, SMILTime elapsed)
184 {
185  if (m_documentOrderIndexesDirty) {
186  updateDocumentOrderIndexes();
187  }
188  std::sort(smilElements.begin(), smilElements.end(), PriorityCompare(elapsed));
189 }
190 
191 static bool applyOrderSortFunction(SVGSMILElement *a, SVGSMILElement *b)
192 {
193  if (!a->hasTagName(SVGNames::animateTransformTag) && b->hasTagName(SVGNames::animateTransformTag)) {
194  return true;
195  }
196  return false;
197 }
198 
199 static void sortByApplyOrder(Vector<SVGSMILElement *> &smilElements)
200 {
201  std::sort(smilElements.begin(), smilElements.end(), applyOrderSortFunction);
202 }
203 
204 String SMILTimeContainer::baseValueFor(ElementAttributePair key)
205 {
206  // FIXME: We wouldn't need to do this if we were keeping base values around properly in DOM.
207  // Currently animation overwrites them so we need to save them somewhere.
208  BaseValueMap::iterator it = m_savedBaseValues.find(key);
209  if (it != m_savedBaseValues.end()) {
210  return it->second;
211  }
212 
213  SVGElement *target = key.first;
214  String attributeName = key.second;
215  ASSERT(target);
216  ASSERT(!attributeName.isEmpty());
217  String baseValue;
218  if (SVGAnimationElement::attributeIsCSS(attributeName)) {
219  CSSComputedStyleDeclaration computedStyle(target);
220  baseValue = computedStyle.getPropertyValue(cssPropertyID(attributeName));
221  } else {
222  baseValue = target->getAttribute(attributeName);
223  }
224  m_savedBaseValues.add(key, baseValue);
225  return baseValue;
226 }
227 
228 void SMILTimeContainer::updateAnimations(SMILTime elapsed)
229 {
230  SMILTime earliersFireTime = SMILTime::unresolved();
231 
232  Vector<SVGSMILElement *> toAnimate;
233  copyToVector(m_scheduledAnimations, toAnimate);
234 
235  // Sort according to priority. Elements with later begin time have higher priority.
236  // In case of a tie, document order decides.
237  // FIXME: This should also consider timing relationships between the elements. Dependents
238  // have higher priority.
239  sortByPriority(toAnimate, elapsed);
240 
241  // Calculate animation contributions.
242  typedef HashMap<ElementAttributePair, SVGSMILElement *> ResultElementMap;
243  ResultElementMap resultsElements;
244  for (unsigned n = 0; n < toAnimate.size(); ++n) {
245  SVGSMILElement *animation = toAnimate[n];
246  ASSERT(animation->timeContainer() == this);
247 
248  SVGElement *targetElement = animation->targetElement();
249  if (!targetElement) {
250  continue;
251  }
252  String attributeName = animation->attributeName();
253  if (attributeName.isEmpty()) {
254  if (animation->hasTagName(SVGNames::animateMotionTag)) {
255  attributeName = SVGNames::animateMotionTag.localName();
256  } else {
257  continue;
258  }
259  }
260 
261  // Results are accumulated to the first animation that animates a particular element/attribute pair.
262  ElementAttributePair key(targetElement, attributeName);
263  SVGSMILElement *resultElement = resultsElements.get(key);
264  if (!resultElement) {
265  resultElement = animation;
266  resultElement->resetToBaseValue(baseValueFor(key));
267  resultsElements.add(key, resultElement);
268  }
269 
270  // This will calculate the contribution from the animation and add it to the resultsElement.
271  animation->progress(elapsed, resultElement);
272 
273  SMILTime nextFireTime = animation->nextProgressTime();
274  if (nextFireTime.isFinite()) {
275  earliersFireTime = min(nextFireTime, earliersFireTime);
276  } else if (!animation->isContributing(elapsed)) {
277  m_scheduledAnimations.remove(animation);
278  if (m_scheduledAnimations.isEmpty()) {
279  m_savedBaseValues.clear();
280  }
281  }
282  }
283 
284  Vector<SVGSMILElement *> animationsToApply;
285  ResultElementMap::iterator end = resultsElements.end();
286  for (ResultElementMap::iterator it = resultsElements.begin(); it != end; ++it) {
287  animationsToApply.append(it->second);
288  }
289 
290  // Sort <animateTranform> to be the last one to be applied. <animate> may change transform attribute as
291  // well (directly or indirectly by modifying <use> x/y) and this way transforms combine properly.
292  sortByApplyOrder(animationsToApply);
293 
294  // Apply results to target elements.
295  for (unsigned n = 0; n < animationsToApply.size(); ++n) {
296  animationsToApply[n]->applyResultsToTarget();
297  }
298 
299  startTimer(earliersFireTime, animationFrameDelay);
300 
301  Document::updateDocumentsRendering();
302 }
303 
304 #endif
305 }
306 
const QList< QKeySequence > & end()
This file is part of the KDE documentation.
Documentation copyright © 1996-2021 The KDE developers.
Generated on Sat Oct 16 2021 22:48:01 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.