2011-06-19 Dirk Schulze <krit@webkit.org>
[WebKit-https.git] / Source / WebCore / svg / SVGAnimateElement.cpp
1 /*
2  * Copyright (C) 2004, 2005 Nikolas Zimmermann <zimmermann@kde.org>
3  * Copyright (C) 2004, 2005, 2006 Rob Buis <buis@kde.org>
4  * Copyright (C) 2008 Apple Inc. All rights reserved.
5  * Copyright (C) Research In Motion Limited 2011. All rights reserved.
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Library General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library General Public License
18  * along with this library; see the file COPYING.LIB.  If not, write to
19  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20  * Boston, MA 02110-1301, USA.
21  */
22
23 #include "config.h"
24
25 #if ENABLE(SVG) && ENABLE(SVG_ANIMATION)
26 #include "SVGAnimateElement.h"
27
28 #include "CSSComputedStyleDeclaration.h"
29 #include "CSSParser.h"
30 #include "CSSPropertyNames.h"
31 #include "ColorDistance.h"
32 #include "QualifiedName.h"
33 #include "RenderObject.h"
34 #include "SVGAnimatorFactory.h"
35 #include "SVGColor.h"
36 #include "SVGNames.h"
37 #include "SVGParserUtilities.h"
38 #include "SVGPathParserFactory.h"
39 #include "SVGPathSegList.h"
40 #include "SVGPointList.h"
41 #include "SVGStyledElement.h"
42
43 using namespace std;
44
45 namespace WebCore {
46
47 SVGAnimateElement::SVGAnimateElement(const QualifiedName& tagName, Document* document)
48     : SVGAnimationElement(tagName, document)
49     , m_animatedAttributeType(AnimatedString)
50     , m_animatedPathPointer(0)
51 {
52     ASSERT(hasTagName(SVGNames::animateTag) || hasTagName(SVGNames::setTag) || hasTagName(SVGNames::animateColorTag));
53 }
54
55 PassRefPtr<SVGAnimateElement> SVGAnimateElement::create(const QualifiedName& tagName, Document* document)
56 {
57     return adoptRef(new SVGAnimateElement(tagName, document));
58 }
59
60 SVGAnimateElement::~SVGAnimateElement()
61 {
62 }
63
64 void SVGAnimateElement::adjustForCurrentColor(SVGElement* targetElement, Color& color)
65 {
66     ASSERT(targetElement);
67     
68     if (RenderObject* targetRenderer = targetElement->renderer())
69         color = targetRenderer->style()->visitedDependentColor(CSSPropertyColor);
70     else
71         color = Color();
72 }
73
74 static inline void getPropertyValue(SVGElement* svgParent, const QualifiedName& attributeName, String& value)
75 {
76     ASSERT(svgParent->isStyled());
77     value = computedStyle(svgParent)->getPropertyValue(cssPropertyID(attributeName.localName()));
78 }
79
80 void SVGAnimateElement::adjustForInheritance(SVGElement* targetElement, const QualifiedName& attributeName, String& value)
81 {
82     // FIXME: At the moment the computed style gets returned as a String and needs to get parsed again.
83     // In the future we might want to work with the value type directly to avoid the String parsing.
84     ASSERT(targetElement);
85
86     Element* parent = targetElement->parentElement();
87     if (!parent || !parent->isSVGElement())
88         return;
89
90     SVGElement* svgParent = static_cast<SVGElement*>(parent);
91     if (svgParent->isStyled())
92         getPropertyValue(svgParent, attributeName, value);
93 }
94
95 bool SVGAnimateElement::hasValidAttributeType() const
96 {
97     SVGElement* targetElement = this->targetElement();
98     if (!targetElement)
99         return false;
100     
101     return determineAnimatedAttributeType(targetElement) != AnimatedUnknown;
102 }
103
104 AnimatedAttributeType SVGAnimateElement::determineAnimatedAttributeType(SVGElement* targetElement) const
105 {
106     ASSERT(targetElement);
107
108     AnimatedAttributeType type = targetElement->animatedPropertyTypeForAttribute(attributeName());
109     if (type == AnimatedUnknown || (hasTagName(SVGNames::animateColorTag) && type != AnimatedColor))
110         return AnimatedUnknown;
111
112     // FIXME: We need type specific animations in the future. Many animations marked as AnimatedString today will
113     // support continuous animations.
114     switch (type) {
115     case AnimatedAngle:
116         return AnimatedAngle;
117     case AnimatedBoolean:
118     case AnimatedEnumeration:
119     case AnimatedNumberList:
120     case AnimatedNumberOptionalNumber:
121     case AnimatedLengthList:
122     case AnimatedPreserveAspectRatio:
123     case AnimatedRect:
124     case AnimatedString:
125         return AnimatedString;
126     case AnimatedLength:
127         return AnimatedLength;
128     case AnimatedInteger:
129     case AnimatedNumber:
130         return AnimatedNumber;
131     case AnimatedPath:
132         return AnimatedPath;
133     case AnimatedPoints:
134         return AnimatedPoints;
135     case AnimatedColor:
136         return AnimatedColor;
137     case AnimatedTransformList:
138     case AnimatedUnknown:
139         // Animations of transform lists are not allowed for <animate> or <set>
140         // http://www.w3.org/TR/SVG/animate.html#AnimationAttributesAndProperties
141         return AnimatedUnknown;
142     }
143
144     ASSERT_NOT_REACHED();
145     return AnimatedUnknown;
146 }
147
148 void SVGAnimateElement::calculateAnimatedValue(float percentage, unsigned repeat, SVGSMILElement* resultElement)
149 {
150     ASSERT(percentage >= 0 && percentage <= 1);
151     ASSERT(resultElement);
152     bool isInFirstHalfOfAnimation = percentage < 0.5f;
153     AnimationMode animationMode = this->animationMode();
154     SVGElement* targetElement = this->targetElement();
155     if (!targetElement)
156         return;
157     
158     if (hasTagName(SVGNames::setTag))
159         percentage = 1;
160     if (!resultElement->hasTagName(SVGNames::animateTag) && !resultElement->hasTagName(SVGNames::animateColorTag) 
161         && !resultElement->hasTagName(SVGNames::setTag))
162         return;
163     SVGAnimateElement* results = static_cast<SVGAnimateElement*>(resultElement);
164     // Can't accumulate over a string property.
165     if (results->m_animatedAttributeType == AnimatedString && m_animatedAttributeType != AnimatedString)
166         return;
167     switch (m_animatedAttributeType) {
168     case AnimatedColor: {
169         if (animationMode == ToAnimation)
170             m_fromColor = results->m_animatedColor;
171
172         // Replace 'currentColor' / 'inherit' by their computed property values.
173         if (m_fromPropertyValueType == CurrentColorValue)
174             adjustForCurrentColor(targetElement, m_fromColor);
175         else if (m_fromPropertyValueType == InheritValue) {
176             String fromColorString;
177             adjustForInheritance(targetElement, attributeName(), fromColorString);
178             m_fromColor = SVGColor::colorFromRGBColorString(fromColorString);
179         }
180         if (m_toPropertyValueType == CurrentColorValue)
181             adjustForCurrentColor(targetElement, m_toColor);
182         else if (m_toPropertyValueType == InheritValue) {
183             String toColorString;
184             adjustForInheritance(targetElement, attributeName(), toColorString);
185             m_toColor = SVGColor::colorFromRGBColorString(toColorString);
186         }
187
188         Color color;
189         if (calcMode() == CalcModeDiscrete)
190             color = isInFirstHalfOfAnimation ? m_fromColor : m_toColor;
191         else
192             color = ColorDistance(m_fromColor, m_toColor).scaledDistance(percentage).addToColorAndClamp(m_fromColor);
193
194         // FIXME: Accumulate colors.
195         if (isAdditive() && animationMode != ToAnimation)
196             results->m_animatedColor = ColorDistance::addColorsAndClamp(results->m_animatedColor, color);
197         else
198             results->m_animatedColor = color;
199         return;
200     }
201     case AnimatedPath: {
202         if (animationMode == ToAnimation) {
203             ASSERT(results->m_animatedPathPointer);
204             m_fromPath = results->m_animatedPathPointer->copy();
205         }
206         if (!percentage) {
207             ASSERT(m_fromPath);
208             ASSERT(percentage >= 0);
209             results->m_animatedPathPointer = m_fromPath.get();
210         } else if (percentage == 1) {
211             ASSERT(m_toPath);
212             results->m_animatedPathPointer = m_toPath.get();
213         } else {
214             if (m_fromPath && m_toPath) {
215                 SVGPathParserFactory* factory = SVGPathParserFactory::self();
216                 if (!factory->buildAnimatedSVGPathByteStream(m_fromPath.get(), m_toPath.get(), results->m_animatedPath, percentage)) {
217                     results->m_animatedPath.clear();
218                     results->m_animatedPathPointer = 0;
219                 } else
220                     results->m_animatedPathPointer = results->m_animatedPath.get();
221             } else
222                 results->m_animatedPathPointer = 0;
223             // Fall back to discrete animation if the paths are not compatible
224             if (!results->m_animatedPathPointer) {
225                 ASSERT(m_fromPath);
226                 ASSERT(m_toPath);
227                 ASSERT(!results->m_animatedPath);
228                 results->m_animatedPathPointer = ((animationMode == FromToAnimation && percentage > 0.5f) || animationMode == ToAnimation || percentage == 1) 
229                     ? m_toPath.get() : m_fromPath.get();
230             }
231         }
232         return;
233     }
234     case AnimatedPoints: {
235         if (!percentage)
236             results->m_animatedPoints = m_fromPoints;
237         else if (percentage == 1)
238             results->m_animatedPoints = m_toPoints;
239         else {
240             if (!m_fromPoints.isEmpty() && !m_toPoints.isEmpty())
241                 SVGPointList::createAnimated(m_fromPoints, m_toPoints, results->m_animatedPoints, percentage);
242             else
243                 results->m_animatedPoints.clear();
244             // Fall back to discrete animation if the points are not compatible
245             if (results->m_animatedPoints.isEmpty())
246                 results->m_animatedPoints = ((animationMode == FromToAnimation && percentage > 0.5f) || animationMode == ToAnimation || percentage == 1) 
247                     ? m_toPoints : m_fromPoints;
248         }
249         return;
250     }
251     case AnimatedAngle:
252     case AnimatedLength:
253     case AnimatedNumber: {
254         ASSERT(m_animator);
255         ASSERT(results->m_animatedType);
256         // Target element might have changed.
257         m_animator->setContextElement(targetElement);
258         m_animator->calculateAnimatedValue(this, percentage, repeat, m_fromType, m_toType, results->m_animatedType, m_fromPropertyValueType == InheritValue, m_toPropertyValueType == InheritValue);
259         return;
260     }
261     default:
262         break;
263     }
264     ASSERT(animationMode == FromToAnimation || animationMode == ToAnimation || animationMode == ValuesAnimation);
265     // Replace 'currentColor' / 'inherit' by their computed property values.
266     if (m_fromPropertyValueType == InheritValue)
267         adjustForInheritance(targetElement, attributeName(), m_fromString);
268     if (m_toPropertyValueType == InheritValue)
269         adjustForInheritance(targetElement, attributeName(), m_toString);
270
271     if ((animationMode == FromToAnimation && percentage > 0.5f) || animationMode == ToAnimation || percentage == 1)
272         results->m_animatedString = m_toString;
273     else
274         results->m_animatedString = m_fromString;
275     // Higher priority replace animation overrides any additive results so far.
276     results->m_animatedAttributeType = AnimatedString;
277 }
278
279 static bool inheritsFromProperty(SVGElement* targetElement, const QualifiedName& attributeName, const String& value)
280 {
281     ASSERT(targetElement);
282     DEFINE_STATIC_LOCAL(const AtomicString, inherit, ("inherit"));
283
284     if (value.isEmpty() || value != inherit || !targetElement->isStyled())
285         return false;
286     return SVGStyledElement::isAnimatableCSSProperty(attributeName);
287 }
288
289 static bool attributeValueIsCurrentColor(const String& value)
290 {
291     DEFINE_STATIC_LOCAL(const AtomicString, currentColor, ("currentColor"));
292     return value == currentColor;
293 }
294
295 bool SVGAnimateElement::calculateFromAndToValues(const String& fromString, const String& toString)
296 {
297     SVGElement* targetElement = this->targetElement();
298     if (!targetElement)
299         return false;
300     m_fromPropertyValueType = inheritsFromProperty(targetElement, attributeName(), fromString) ? InheritValue : RegularPropertyValue;
301     m_toPropertyValueType = inheritsFromProperty(targetElement, attributeName(), toString) ? InheritValue : RegularPropertyValue;
302
303     // FIXME: Needs more solid way determine target attribute type.
304     m_animatedAttributeType = determineAnimatedAttributeType(targetElement);
305     switch (m_animatedAttributeType) {
306     case AnimatedColor: {
307         bool fromIsCurrentColor = attributeValueIsCurrentColor(fromString);
308         bool toIsCurrentColor = attributeValueIsCurrentColor(toString);
309         if (fromIsCurrentColor)
310             m_fromPropertyValueType = CurrentColorValue;
311         else
312             m_fromColor = SVGColor::colorFromRGBColorString(fromString);
313         if (toIsCurrentColor)
314             m_toPropertyValueType = CurrentColorValue;
315         else
316             m_toColor = SVGColor::colorFromRGBColorString(toString);
317         bool fromIsValid = m_fromColor.isValid() || fromIsCurrentColor || m_fromPropertyValueType == InheritValue;
318         bool toIsValid = m_toColor.isValid() || toIsCurrentColor || m_toPropertyValueType == InheritValue;
319         if ((fromIsValid && toIsValid) || (toIsValid && animationMode() == ToAnimation))
320             return true;
321         break;
322     }
323     case AnimatedPath: {
324         SVGPathParserFactory* factory = SVGPathParserFactory::self();
325         if (factory->buildSVGPathByteStreamFromString(toString, m_toPath, UnalteredParsing)) {
326             // For to-animations the from number is calculated later
327             if (animationMode() == ToAnimation || factory->buildSVGPathByteStreamFromString(fromString, m_fromPath, UnalteredParsing))
328                 return true;
329         }
330         m_fromPath.clear();
331         m_toPath.clear();
332         break;
333     }
334     case AnimatedPoints: {
335         m_fromPoints.clear();
336         if (pointsListFromSVGData(m_fromPoints, fromString)) {
337             m_toPoints.clear();
338             if (pointsListFromSVGData(m_toPoints, toString))
339                 return true;
340         }
341         break;
342     }
343     case AnimatedAngle:
344     case AnimatedLength:
345     case AnimatedNumber:
346         ensureAnimator()->calculateFromAndToValues(m_fromType, m_toType, fromString, toString);
347         return true;
348     default:
349         break;
350     }
351     m_fromString = fromString;
352     m_toString = toString;
353     m_animatedAttributeType = AnimatedString;
354     return true;
355 }
356
357 bool SVGAnimateElement::calculateFromAndByValues(const String& fromString, const String& byString)
358 {
359     SVGElement* targetElement = this->targetElement();
360     if (!targetElement)
361         return false;
362     m_fromPropertyValueType = inheritsFromProperty(targetElement, attributeName(), fromString) ? InheritValue : RegularPropertyValue;
363     m_toPropertyValueType = inheritsFromProperty(targetElement, attributeName(), byString) ? InheritValue : RegularPropertyValue;
364
365     ASSERT(!hasTagName(SVGNames::setTag));
366     m_animatedAttributeType = determineAnimatedAttributeType(targetElement);
367     switch (m_animatedAttributeType) {
368     case AnimatedColor: {
369         bool fromIsCurrentColor = attributeValueIsCurrentColor(fromString);
370         bool byIsCurrentColor = attributeValueIsCurrentColor(byString);
371         if (fromIsCurrentColor)
372             m_fromPropertyValueType = CurrentColorValue;
373         else
374             m_fromColor = SVGColor::colorFromRGBColorString(fromString);
375         if (byIsCurrentColor)
376             m_toPropertyValueType = CurrentColorValue;
377         else
378             m_toColor = SVGColor::colorFromRGBColorString(byString);
379         
380         if ((!m_fromColor.isValid() && !fromIsCurrentColor)
381             || (!m_toColor.isValid() && !byIsCurrentColor))
382             return false;
383         return true;
384     }
385     case AnimatedAngle:
386     case AnimatedLength:
387     case AnimatedNumber:
388         ensureAnimator()->calculateFromAndByValues(m_fromType, m_toType, fromString, byString);
389         return true;
390     default:
391         break;
392     }
393     return true;
394 }
395
396 void SVGAnimateElement::resetToBaseValue(const String& baseString)
397 {
398     SVGElement* targetElement = this->targetElement();
399     ASSERT(targetElement);
400     m_animatedString = baseString;
401     AnimatedAttributeType lastType = m_animatedAttributeType;
402     m_animatedAttributeType = determineAnimatedAttributeType(targetElement);
403     switch (m_animatedAttributeType) {
404     case AnimatedColor:
405         m_animatedColor = baseString.isEmpty() ? Color() : SVGColor::colorFromRGBColorString(baseString);
406         if (isContributing(elapsed())) {
407             m_animatedAttributeType = lastType;
408             return;
409         }
410         break;
411     case AnimatedPath: {
412         m_animatedPath.clear();
413         SVGPathParserFactory* factory = SVGPathParserFactory::self();
414         factory->buildSVGPathByteStreamFromString(baseString, m_animatedPath, UnalteredParsing);
415         m_animatedPathPointer = m_animatedPath.get();
416         return;
417     }
418     case AnimatedPoints:
419         m_animatedPoints.clear();
420         return;
421     case AnimatedAngle:
422     case AnimatedLength:
423     case AnimatedNumber: {
424         if (!m_animatedType)
425             m_animatedType = ensureAnimator()->constructFromString(baseString);
426         else
427             m_animatedType->setValueAsString(attributeName(), baseString);
428         return;
429     }
430     default:
431         break;
432     }
433     m_animatedAttributeType = AnimatedString;
434 }
435     
436 void SVGAnimateElement::applyResultsToTarget()
437 {
438     String valueToApply;
439     switch (m_animatedAttributeType) {
440     case AnimatedColor:
441         valueToApply = m_animatedColor.serialized();
442         break;
443     case AnimatedPath: {
444         if (!m_animatedPathPointer || m_animatedPathPointer->isEmpty())
445             valueToApply = m_animatedString;
446         else {
447             // We need to keep going to string and back because we are currently only able to paint
448             // "processed" paths where complex shapes are replaced with simpler ones. Path 
449             // morphing needs to be done with unprocessed paths.
450             // FIXME: This could be optimized if paths were not processed at parse time.
451             SVGPathParserFactory* factory = SVGPathParserFactory::self();
452             factory->buildStringFromByteStream(m_animatedPathPointer, valueToApply, UnalteredParsing);
453         }
454         break;
455     }
456     case AnimatedPoints:
457         valueToApply = m_animatedPoints.isEmpty() ? m_animatedString : m_animatedPoints.valueAsString();
458         break;
459     case AnimatedAngle:
460     case AnimatedLength:
461     case AnimatedNumber:
462         valueToApply = m_animatedType->valueAsString();
463         break;
464     default:
465         valueToApply = m_animatedString;
466     }
467     setTargetAttributeAnimatedValue(valueToApply);
468 }
469     
470 float SVGAnimateElement::calculateDistance(const String& fromString, const String& toString)
471 {
472     // FIXME: A return value of float is not enough to support paced animations on lists.
473     SVGElement* targetElement = this->targetElement();
474     if (!targetElement)
475         return -1;
476     m_animatedAttributeType = determineAnimatedAttributeType(targetElement);
477     switch (m_animatedAttributeType) {
478     case AnimatedColor: {
479         Color from = SVGColor::colorFromRGBColorString(fromString);
480         if (!from.isValid())
481             return -1;
482         Color to = SVGColor::colorFromRGBColorString(toString);
483         if (!to.isValid())
484             return -1;
485         return ColorDistance(from, to).distance();
486     }
487     case AnimatedAngle:
488     case AnimatedLength:
489     case AnimatedNumber:
490         return ensureAnimator()->calculateDistance(this, fromString, toString);
491     default:
492         break;
493     }
494     return -1;
495 }
496
497 SVGAnimatedTypeAnimator* SVGAnimateElement::ensureAnimator()
498 {
499     if (!m_animator)
500         m_animator = SVGAnimatorFactory::create(targetElement(), m_animatedAttributeType, attributeName());
501     return m_animator.get();
502 }
503
504 }
505 #endif // ENABLE(SVG)