a38a9d414e5fc556a012cc9586cd6156c6a350ec
[WebKit-https.git] / WebCore / ksvg2 / misc / SVGTimer.cpp
1 /*
2     Copyright (C) 2004, 2005 Nikolas Zimmermann <wildfox@kde.org>
3                   2004, 2005 Rob Buis <buis@kde.org>
4     Copyright (C) 2006 Apple Computer, Inc.
5
6     This file is part of the KDE project
7
8     This library is free software; you can redistribute it and/or
9     modify it under the terms of the GNU Library General Public
10     License as published by the Free Software Foundation; either
11     version 2 of the License, or (at your option) any later version.
12
13     This library is distributed in the hope that it will be useful,
14     but WITHOUT ANY WARRANTY; without even the implied warranty of
15     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16     Library General Public License for more details.
17
18     You should have received a copy of the GNU Library General Public License
19     along with this library; see the file COPYING.LIB.  If not, write to
20     the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21     Boston, MA 02111-1307, USA.
22 */
23
24 #include "config.h"
25 #ifdef SVG_SUPPORT
26 #include "SVGTimer.h"
27
28 #include <wtf/HashMap.h>
29 #include "SVGAnimateTransformElement.h"
30 #include "SVGTransformList.h"
31 #include "SVGAnimateColorElement.h"
32 #include "SVGStyledTransformableElement.h"
33
34 namespace WebCore {
35
36 SVGTimer::SVGTimer(TimeScheduler* scheduler, double interval, bool singleShot)
37     : Timer<TimeScheduler>(scheduler, &TimeScheduler::timerFired)
38     , m_scheduler(scheduler)
39     , m_interval(interval)
40     , m_singleShot(singleShot)
41 {
42 }
43
44 void SVGTimer::start()
45 {
46     if (m_singleShot)
47         startOneShot(m_interval);
48     else
49         startRepeating(m_interval);
50 }
51
52 SVGTimer::TargetAnimationMap SVGTimer::animationsByElement(double elapsedSeconds)
53 {
54     // Build a list of all animations which apply to each element
55     // FIXME: This list should be sorted by animation priority
56     ExceptionCode ec = 0;
57     TargetAnimationMap targetMap;
58     SVGNotifySet::const_iterator end = m_notifySet.end();
59     for (SVGNotifySet::const_iterator it = m_notifySet.begin(); it != end; ++it) {
60         SVGAnimationElement* animation = *it;
61         
62         // If we're dealing with a disabled element with fill="freeze",
63         // we have to take it into account for further calculations.
64         if (!m_enabledNotifySet.contains(animation)) {
65             if (!animation->isFrozen())
66                 continue;
67             if (elapsedSeconds <= (animation->getStartTime() + animation->getSimpleDuration(ec)))
68                 continue;
69         }
70         
71         SVGElement* target = const_cast<SVGElement *>(animation->targetElement());
72         TargetAnimationMap::iterator i = targetMap.find(target);
73         if (i != targetMap.end())
74             i->second.append(animation);
75         else {
76             Vector<SVGAnimationElement*> list;
77             list.append(animation);
78             targetMap.set(target, list);
79         }
80     }
81     return targetMap;
82 }
83
84 // FIXME: This funtion will eventually become part of the AnimationCompositor
85 void SVGTimer::applyAnimations(double elapsedSeconds, const SVGTimer::TargetAnimationMap& targetMap)
86 {
87     ExceptionCode ec = 0;
88     
89     TargetAnimationMap::const_iterator targetIterator = targetMap.begin();
90     TargetAnimationMap::const_iterator tend = targetMap.end();
91     for (; targetIterator != tend; ++targetIterator) {
92         HashMap<String, Color> attributeToColorMap; // special <animateColor> case
93         RefPtr<SVGTransformList> targetTransforms; // special <animateTransform> case
94         
95         // FIXME: This is still not 100% correct.  Correct would be:
96         // 1. Walk backwards through the priority list until a replace (!isAdditive()) is found
97         // 2. Set the initial value (or last replace) as the new animVal
98         // 3. Call each enabled animation in turn, to have it apply its changes
99         // 4. After building a new animVal, set it on the element.
100         
101         unsigned count = targetIterator->second.size();
102         for (unsigned i = 0; i < count; ++i) {
103             SVGAnimationElement* animation = targetIterator->second[i];
104             
105             if (!animation->updateForElapsedSeconds(elapsedSeconds))
106                 continue;
107             
108             if (animation->hasTagName(SVGNames::animateTransformTag)) {
109                 SVGAnimateTransformElement* animTransform = static_cast<SVGAnimateTransformElement*>(animation);
110                 if (!targetTransforms) {
111                     targetTransforms = new SVGTransformList();
112                     if (animation->isAdditive()) { // Avoid mallocing a transform which is about to be replaced
113                         RefPtr<SVGTransform> initialTransform = new SVGTransform();
114                         initialTransform->setMatrix(animTransform->initialMatrix());
115                         targetTransforms->appendItem(initialTransform.get(), ec);
116                     }
117                 }
118                 animTransform->applyAnimationToValue(targetTransforms.get());
119             } else if (animation->hasTagName(SVGNames::animateColorTag)) {
120                 SVGAnimateColorElement* animColor = static_cast<SVGAnimateColorElement*>(animation);
121                 String name = animColor->attributeName();
122                 Color currentColor = attributeToColorMap.contains(name) ? attributeToColorMap.get(name) : animColor->initialColor();
123                 animColor->applyAnimationToValue(currentColor);
124                 attributeToColorMap.set(name, currentColor);
125             }
126         }
127         
128         // Apply any transform changes (animateTransform)
129         if (targetTransforms) {
130             SVGElement* key = targetIterator->first;
131             if (key && key->isStyledTransformable()) {
132                 SVGStyledTransformableElement* transform = static_cast<SVGStyledTransformableElement*>(key);
133                 transform->setTransform(targetTransforms.get());
134                 transform->updateLocalTransform(transform->transform());
135             }
136         }
137         
138         // Apply any color changes (animateColor)
139         HashMap<String, Color>::iterator colorIteratorEnd = attributeToColorMap.end();
140         for (HashMap<String, Color>::iterator colorIterator = attributeToColorMap.begin(); colorIterator != colorIteratorEnd; ++colorIterator) {
141             if (colorIterator->second.isValid())
142                 SVGAnimationElement::setTargetAttribute(targetIterator->first, colorIterator->first, colorIterator->second.name());
143         }
144     }
145     
146     // Make a second pass through the map to avoid multiple setChanged calls on the same element.
147     for (targetIterator = targetMap.begin(); targetIterator != tend; ++targetIterator) {
148         SVGElement* key = targetIterator->first;
149         if (key && key->isStyled())
150             static_cast<SVGStyledElement*>(key)->setChanged(true);
151     }
152 }
153
154 void SVGTimer::notifyAll()
155 {
156     if (m_enabledNotifySet.isEmpty())
157         return;
158
159     // First build a list of animation elements per target element
160     double elapsedSeconds = m_scheduler->elapsed() * 1000.0; // Take time now.
161     TargetAnimationMap targetMap = animationsByElement(elapsedSeconds);
162     
163     // Then composite those animations down to final values and apply
164     applyAnimations(elapsedSeconds, targetMap);
165 }
166
167 void SVGTimer::addNotify(SVGAnimationElement* element, bool enabled)
168 {
169     m_notifySet.add(element);
170     if (enabled)
171         m_enabledNotifySet.add(element);
172     else
173         m_enabledNotifySet.remove(element);
174 }
175
176 void SVGTimer::removeNotify(SVGAnimationElement *element)
177 {
178     // FIXME: Why do we keep a pointer to the element forever (marked disabled)?
179     // That can't be right!
180
181     m_enabledNotifySet.remove(element);
182     if (m_enabledNotifySet.isEmpty())
183         stop();
184 }
185
186 } // namespace
187
188 // vim:ts=4:noet
189 #endif // SVG_SUPPORT