534235c558b46ae14cbe2930897055741df5b74a
[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 "FloatConversion.h"
33 #include "QualifiedName.h"
34 #include "RenderObject.h"
35 #include "SVGAnimatorFactory.h"
36 #include "SVGColor.h"
37 #include "SVGNames.h"
38 #include "SVGParserUtilities.h"
39 #include "SVGPathParserFactory.h"
40 #include "SVGPathSegList.h"
41 #include "SVGPointList.h"
42 #include "SVGStyledElement.h"
43
44 using namespace std;
45
46 namespace WebCore {
47
48 SVGAnimateElement::SVGAnimateElement(const QualifiedName& tagName, Document* document)
49     : SVGAnimationElement(tagName, document)
50     , m_animatedAttributeType(AnimatedString)
51     , m_fromNumber(0)
52     , m_toNumber(0)
53     , m_animatedNumber(numeric_limits<float>::infinity())
54     , m_animatedPathPointer(0)
55 {
56     ASSERT(hasTagName(SVGNames::animateTag) || hasTagName(SVGNames::setTag) || hasTagName(SVGNames::animateColorTag));
57 }
58
59 PassRefPtr<SVGAnimateElement> SVGAnimateElement::create(const QualifiedName& tagName, Document* document)
60 {
61     return adoptRef(new SVGAnimateElement(tagName, document));
62 }
63
64 SVGAnimateElement::~SVGAnimateElement()
65 {
66 }
67
68 void SVGAnimateElement::adjustForCurrentColor(SVGElement* targetElement, Color& color)
69 {
70     ASSERT(targetElement);
71     
72     if (RenderObject* targetRenderer = targetElement->renderer())
73         color = targetRenderer->style()->visitedDependentColor(CSSPropertyColor);
74     else
75         color = Color();
76 }
77
78 static inline void getPropertyValue(SVGElement* svgParent, const QualifiedName& attributeName, String& value)
79 {
80     ASSERT(svgParent->isStyled());
81     value = computedStyle(svgParent)->getPropertyValue(cssPropertyID(attributeName.localName()));
82 }
83
84 void SVGAnimateElement::adjustForInheritance(SVGElement* targetElement, const QualifiedName& attributeName, String& value)
85 {
86     // FIXME: At the moment the computed style gets returned as a String and needs to get parsed again.
87     // In the future we might want to work with the value type directly to avoid the String parsing.
88     ASSERT(targetElement);
89
90     Element* parent = targetElement->parentElement();
91     if (!parent || !parent->isSVGElement())
92         return;
93
94     SVGElement* svgParent = static_cast<SVGElement*>(parent);
95     if (svgParent->isStyled())
96         getPropertyValue(svgParent, attributeName, value);
97 }
98
99 bool SVGAnimateElement::hasValidAttributeType() const
100 {
101     SVGElement* targetElement = this->targetElement();
102     if (!targetElement)
103         return false;
104     
105     return determineAnimatedAttributeType(targetElement) != AnimatedUnknown;
106 }
107
108 AnimatedAttributeType SVGAnimateElement::determineAnimatedAttributeType(SVGElement* targetElement) const
109 {
110     ASSERT(targetElement);
111
112     AnimatedAttributeType type = targetElement->animatedPropertyTypeForAttribute(attributeName());
113     if (type == AnimatedUnknown || (hasTagName(SVGNames::animateColorTag) && type != AnimatedColor))
114         return AnimatedUnknown;
115
116     // FIXME: We need type specific animations in the future. Many animations marked as AnimatedString today will
117     // support continuous animations.
118     switch (type) {
119     case AnimatedAngle:
120         return AnimatedAngle;
121     case AnimatedBoolean:
122     case AnimatedEnumeration:
123     case AnimatedNumberList:
124     case AnimatedNumberOptionalNumber:
125     case AnimatedLengthList:
126     case AnimatedPreserveAspectRatio:
127     case AnimatedRect:
128     case AnimatedString:
129         return AnimatedString;
130     case AnimatedLength:
131         return AnimatedLength;
132     case AnimatedInteger:
133     case AnimatedNumber:
134         return AnimatedNumber;
135     case AnimatedPath:
136         return AnimatedPath;
137     case AnimatedPoints:
138         return AnimatedPoints;
139     case AnimatedColor:
140         return AnimatedColor;
141     case AnimatedTransformList:
142     case AnimatedUnknown:
143         // Animations of transform lists are not allowed for <animate> or <set>
144         // http://www.w3.org/TR/SVG/animate.html#AnimationAttributesAndProperties
145         return AnimatedUnknown;
146     }
147
148     ASSERT_NOT_REACHED();
149     return AnimatedUnknown;
150 }
151
152 void SVGAnimateElement::calculateAnimatedValue(float percentage, unsigned repeat, SVGSMILElement* resultElement)
153 {
154     ASSERT(percentage >= 0 && percentage <= 1);
155     ASSERT(resultElement);
156     bool isInFirstHalfOfAnimation = percentage < 0.5f;
157     AnimationMode animationMode = this->animationMode();
158     SVGElement* targetElement = this->targetElement();
159     if (!targetElement)
160         return;
161     
162     if (hasTagName(SVGNames::setTag))
163         percentage = 1;
164     if (!resultElement->hasTagName(SVGNames::animateTag) && !resultElement->hasTagName(SVGNames::animateColorTag) 
165         && !resultElement->hasTagName(SVGNames::setTag))
166         return;
167     SVGAnimateElement* results = static_cast<SVGAnimateElement*>(resultElement);
168     // Can't accumulate over a string property.
169     if (results->m_animatedAttributeType == AnimatedString && m_animatedAttributeType != AnimatedString)
170         return;
171     switch (m_animatedAttributeType) {
172     case AnimatedNumber: {
173         // To animation uses contributions from the lower priority animations as the base value.
174         if (animationMode == ToAnimation)
175             m_fromNumber = results->m_animatedNumber;
176         
177         // Replace 'currentColor' / 'inherit' by their computed property values.
178         if (m_fromPropertyValueType == InheritValue) {
179             String fromNumberString;
180             adjustForInheritance(targetElement, attributeName(), fromNumberString);
181             if (!parseNumberFromString(fromNumberString, m_fromNumber, false))
182                 return;
183         }
184         if (m_toPropertyValueType == InheritValue) {
185             String toNumberString;
186             adjustForInheritance(targetElement, attributeName(), toNumberString);
187             if (!parseNumberFromString(toNumberString, m_toNumber, false))
188                 return;
189         }
190
191         float number;
192         if (calcMode() == CalcModeDiscrete)
193             number = isInFirstHalfOfAnimation ? m_fromNumber : m_toNumber;
194         else
195             number = (m_toNumber - m_fromNumber) * percentage + m_fromNumber;
196
197         // FIXME: This is not correct for values animation.
198         if (isAccumulated() && repeat)
199             number += m_toNumber * repeat;
200         if (isAdditive() && animationMode != ToAnimation)
201             results->m_animatedNumber += number;
202         else 
203             results->m_animatedNumber = number;
204         return;
205     } 
206     case AnimatedColor: {
207         if (animationMode == ToAnimation)
208             m_fromColor = results->m_animatedColor;
209
210         // Replace 'currentColor' / 'inherit' by their computed property values.
211         if (m_fromPropertyValueType == CurrentColorValue)
212             adjustForCurrentColor(targetElement, m_fromColor);
213         else if (m_fromPropertyValueType == InheritValue) {
214             String fromColorString;
215             adjustForInheritance(targetElement, attributeName(), fromColorString);
216             m_fromColor = SVGColor::colorFromRGBColorString(fromColorString);
217         }
218         if (m_toPropertyValueType == CurrentColorValue)
219             adjustForCurrentColor(targetElement, m_toColor);
220         else if (m_toPropertyValueType == InheritValue) {
221             String toColorString;
222             adjustForInheritance(targetElement, attributeName(), toColorString);
223             m_toColor = SVGColor::colorFromRGBColorString(toColorString);
224         }
225
226         Color color;
227         if (calcMode() == CalcModeDiscrete)
228             color = isInFirstHalfOfAnimation ? m_fromColor : m_toColor;
229         else
230             color = ColorDistance(m_fromColor, m_toColor).scaledDistance(percentage).addToColorAndClamp(m_fromColor);
231
232         // FIXME: Accumulate colors.
233         if (isAdditive() && animationMode != ToAnimation)
234             results->m_animatedColor = ColorDistance::addColorsAndClamp(results->m_animatedColor, color);
235         else
236             results->m_animatedColor = color;
237         return;
238     }
239     case AnimatedPath: {
240         if (animationMode == ToAnimation) {
241             ASSERT(results->m_animatedPathPointer);
242             m_fromPath = results->m_animatedPathPointer->copy();
243         }
244         if (!percentage) {
245             ASSERT(m_fromPath);
246             ASSERT(percentage >= 0);
247             results->m_animatedPathPointer = m_fromPath.get();
248         } else if (percentage == 1) {
249             ASSERT(m_toPath);
250             results->m_animatedPathPointer = m_toPath.get();
251         } else {
252             if (m_fromPath && m_toPath) {
253                 SVGPathParserFactory* factory = SVGPathParserFactory::self();
254                 if (!factory->buildAnimatedSVGPathByteStream(m_fromPath.get(), m_toPath.get(), results->m_animatedPath, percentage)) {
255                     results->m_animatedPath.clear();
256                     results->m_animatedPathPointer = 0;
257                 } else
258                     results->m_animatedPathPointer = results->m_animatedPath.get();
259             } else
260                 results->m_animatedPathPointer = 0;
261             // Fall back to discrete animation if the paths are not compatible
262             if (!results->m_animatedPathPointer) {
263                 ASSERT(m_fromPath);
264                 ASSERT(m_toPath);
265                 ASSERT(!results->m_animatedPath);
266                 results->m_animatedPathPointer = ((animationMode == FromToAnimation && percentage > 0.5f) || animationMode == ToAnimation || percentage == 1) 
267                     ? m_toPath.get() : m_fromPath.get();
268             }
269         }
270         return;
271     }
272     case AnimatedPoints: {
273         if (!percentage)
274             results->m_animatedPoints = m_fromPoints;
275         else if (percentage == 1)
276             results->m_animatedPoints = m_toPoints;
277         else {
278             if (!m_fromPoints.isEmpty() && !m_toPoints.isEmpty())
279                 SVGPointList::createAnimated(m_fromPoints, m_toPoints, results->m_animatedPoints, percentage);
280             else
281                 results->m_animatedPoints.clear();
282             // Fall back to discrete animation if the points are not compatible
283             if (results->m_animatedPoints.isEmpty())
284                 results->m_animatedPoints = ((animationMode == FromToAnimation && percentage > 0.5f) || animationMode == ToAnimation || percentage == 1) 
285                     ? m_toPoints : m_fromPoints;
286         }
287         return;
288     }
289     case AnimatedAngle:
290     case AnimatedLength: {
291         ASSERT(m_animator);
292         ASSERT(results->m_animatedType);
293         // Target element might have changed.
294         m_animator->setContextElement(targetElement);
295         m_animator->calculateAnimatedValue(this, percentage, repeat, m_fromType, m_toType, results->m_animatedType, m_fromPropertyValueType == InheritValue, m_toPropertyValueType == InheritValue);
296         return;
297     }
298     default:
299         break;
300     }
301     ASSERT(animationMode == FromToAnimation || animationMode == ToAnimation || animationMode == ValuesAnimation);
302     // Replace 'currentColor' / 'inherit' by their computed property values.
303     if (m_fromPropertyValueType == InheritValue)
304         adjustForInheritance(targetElement, attributeName(), m_fromString);
305     if (m_toPropertyValueType == InheritValue)
306         adjustForInheritance(targetElement, attributeName(), m_toString);
307
308     if ((animationMode == FromToAnimation && percentage > 0.5f) || animationMode == ToAnimation || percentage == 1)
309         results->m_animatedString = m_toString;
310     else
311         results->m_animatedString = m_fromString;
312     // Higher priority replace animation overrides any additive results so far.
313     results->m_animatedAttributeType = AnimatedString;
314 }
315
316 static bool inheritsFromProperty(SVGElement* targetElement, const QualifiedName& attributeName, const String& value)
317 {
318     ASSERT(targetElement);
319     DEFINE_STATIC_LOCAL(const AtomicString, inherit, ("inherit"));
320
321     if (value.isEmpty() || value != inherit || !targetElement->isStyled())
322         return false;
323     return SVGStyledElement::isAnimatableCSSProperty(attributeName);
324 }
325
326 static bool attributeValueIsCurrentColor(const String& value)
327 {
328     DEFINE_STATIC_LOCAL(const AtomicString, currentColor, ("currentColor"));
329     return value == currentColor;
330 }
331
332 bool SVGAnimateElement::calculateFromAndToValues(const String& fromString, const String& toString)
333 {
334     SVGElement* targetElement = this->targetElement();
335     if (!targetElement)
336         return false;
337     m_fromPropertyValueType = inheritsFromProperty(targetElement, attributeName(), fromString) ? InheritValue : RegularPropertyValue;
338     m_toPropertyValueType = inheritsFromProperty(targetElement, attributeName(), toString) ? InheritValue : RegularPropertyValue;
339
340     // FIXME: Needs more solid way determine target attribute type.
341     m_animatedAttributeType = determineAnimatedAttributeType(targetElement);
342     switch (m_animatedAttributeType) {
343     case AnimatedColor: {
344         bool fromIsCurrentColor = attributeValueIsCurrentColor(fromString);
345         bool toIsCurrentColor = attributeValueIsCurrentColor(toString);
346         if (fromIsCurrentColor)
347             m_fromPropertyValueType = CurrentColorValue;
348         else
349             m_fromColor = SVGColor::colorFromRGBColorString(fromString);
350         if (toIsCurrentColor)
351             m_toPropertyValueType = CurrentColorValue;
352         else
353             m_toColor = SVGColor::colorFromRGBColorString(toString);
354         bool fromIsValid = m_fromColor.isValid() || fromIsCurrentColor || m_fromPropertyValueType == InheritValue;
355         bool toIsValid = m_toColor.isValid() || toIsCurrentColor || m_toPropertyValueType == InheritValue;
356         if ((fromIsValid && toIsValid) || (toIsValid && animationMode() == ToAnimation))
357             return true;
358         break;
359     }
360     case AnimatedNumber: {
361         if (parseNumberFromString(toString, m_toNumber, false)) {
362             // For to-animations the from number is calculated later
363             if (animationMode() == ToAnimation || parseNumberFromString(fromString, m_fromNumber, false))
364                 return true;
365         }
366         break;
367     }
368     case AnimatedPath: {
369         SVGPathParserFactory* factory = SVGPathParserFactory::self();
370         if (factory->buildSVGPathByteStreamFromString(toString, m_toPath, UnalteredParsing)) {
371             // For to-animations the from number is calculated later
372             if (animationMode() == ToAnimation || factory->buildSVGPathByteStreamFromString(fromString, m_fromPath, UnalteredParsing))
373                 return true;
374         }
375         m_fromPath.clear();
376         m_toPath.clear();
377         break;
378     }
379     case AnimatedPoints: {
380         m_fromPoints.clear();
381         if (pointsListFromSVGData(m_fromPoints, fromString)) {
382             m_toPoints.clear();
383             if (pointsListFromSVGData(m_toPoints, toString))
384                 return true;
385         }
386         break;
387     }
388     case AnimatedAngle:
389     case AnimatedLength:
390         ensureAnimator()->calculateFromAndToValues(m_fromType, m_toType, fromString, toString);
391         return true;
392     default:
393         break;
394     }
395     m_fromString = fromString;
396     m_toString = toString;
397     m_animatedAttributeType = AnimatedString;
398     return true;
399 }
400
401 bool SVGAnimateElement::calculateFromAndByValues(const String& fromString, const String& byString)
402 {
403     SVGElement* targetElement = this->targetElement();
404     if (!targetElement)
405         return false;
406     m_fromPropertyValueType = inheritsFromProperty(targetElement, attributeName(), fromString) ? InheritValue : RegularPropertyValue;
407     m_toPropertyValueType = inheritsFromProperty(targetElement, attributeName(), byString) ? InheritValue : RegularPropertyValue;
408
409     ASSERT(!hasTagName(SVGNames::setTag));
410     m_animatedAttributeType = determineAnimatedAttributeType(targetElement);
411     switch (m_animatedAttributeType) {
412     case AnimatedColor: {
413         bool fromIsCurrentColor = attributeValueIsCurrentColor(fromString);
414         bool byIsCurrentColor = attributeValueIsCurrentColor(byString);
415         if (fromIsCurrentColor)
416             m_fromPropertyValueType = CurrentColorValue;
417         else
418             m_fromColor = SVGColor::colorFromRGBColorString(fromString);
419         if (byIsCurrentColor)
420             m_toPropertyValueType = CurrentColorValue;
421         else
422             m_toColor = SVGColor::colorFromRGBColorString(byString);
423         
424         if ((!m_fromColor.isValid() && !fromIsCurrentColor)
425             || (!m_toColor.isValid() && !byIsCurrentColor))
426             return false;
427         return true;
428     }
429     case AnimatedAngle:
430     case AnimatedLength:
431         ensureAnimator()->calculateFromAndByValues(m_fromType, m_toType, fromString, byString);
432         return true;
433     default:
434         m_fromNumber = 0;
435         if (!fromString.isEmpty() && !parseNumberFromString(fromString, m_fromNumber, false))
436             return false;
437         if (!parseNumberFromString(byString, m_toNumber, false))
438             return false;
439         m_toNumber += m_fromNumber;
440     }
441     return true;
442 }
443
444 void SVGAnimateElement::resetToBaseValue(const String& baseString)
445 {
446     SVGElement* targetElement = this->targetElement();
447     ASSERT(targetElement);
448     m_animatedString = baseString;
449     AnimatedAttributeType lastType = m_animatedAttributeType;
450     m_animatedAttributeType = determineAnimatedAttributeType(targetElement);
451     switch (m_animatedAttributeType) {
452     case AnimatedColor:
453         m_animatedColor = baseString.isEmpty() ? Color() : SVGColor::colorFromRGBColorString(baseString);
454         if (isContributing(elapsed())) {
455             m_animatedAttributeType = lastType;
456             return;
457         }
458         break;
459     case AnimatedNumber:
460         if (baseString.isEmpty()) {
461             m_animatedNumber = 0;
462             return;
463         }
464         if (parseNumberFromString(baseString, m_animatedNumber, false))
465             return;
466         break;
467     case AnimatedPath: {
468         m_animatedPath.clear();
469         SVGPathParserFactory* factory = SVGPathParserFactory::self();
470         factory->buildSVGPathByteStreamFromString(baseString, m_animatedPath, UnalteredParsing);
471         m_animatedPathPointer = m_animatedPath.get();
472         return;
473     }
474     case AnimatedPoints:
475         m_animatedPoints.clear();
476         return;
477     case AnimatedAngle:
478     case AnimatedLength: {
479         if (!m_animatedType)
480             m_animatedType = ensureAnimator()->constructFromString(baseString);
481         else
482             m_animatedType->setValueAsString(attributeName(), baseString);
483         return;
484     }
485     default:
486         break;
487     }
488     m_animatedAttributeType = AnimatedString;
489 }
490     
491 void SVGAnimateElement::applyResultsToTarget()
492 {
493     String valueToApply;
494     switch (m_animatedAttributeType) {
495     case AnimatedColor:
496         valueToApply = m_animatedColor.serialized();
497         break;
498     case AnimatedNumber:
499         valueToApply = String::number(m_animatedNumber);
500         break;
501     case AnimatedPath: {
502         if (!m_animatedPathPointer || m_animatedPathPointer->isEmpty())
503             valueToApply = m_animatedString;
504         else {
505             // We need to keep going to string and back because we are currently only able to paint
506             // "processed" paths where complex shapes are replaced with simpler ones. Path 
507             // morphing needs to be done with unprocessed paths.
508             // FIXME: This could be optimized if paths were not processed at parse time.
509             SVGPathParserFactory* factory = SVGPathParserFactory::self();
510             factory->buildStringFromByteStream(m_animatedPathPointer, valueToApply, UnalteredParsing);
511         }
512         break;
513     }
514     case AnimatedPoints:
515         valueToApply = m_animatedPoints.isEmpty() ? m_animatedString : m_animatedPoints.valueAsString();
516         break;
517     case AnimatedAngle:
518     case AnimatedLength:
519         valueToApply = m_animatedType->valueAsString();
520         break;
521     default:
522         valueToApply = m_animatedString;
523     }
524     setTargetAttributeAnimatedValue(valueToApply);
525 }
526     
527 float SVGAnimateElement::calculateDistance(const String& fromString, const String& toString)
528 {
529     SVGElement* targetElement = this->targetElement();
530     if (!targetElement)
531         return -1;
532     m_animatedAttributeType = determineAnimatedAttributeType(targetElement);
533     if (m_animatedAttributeType == AnimatedNumber) {
534         float from;
535         float to;
536         if (!parseNumberFromString(fromString, from, false))
537             return -1;
538         if (!parseNumberFromString(toString, to, false))
539             return -1;
540         return fabs(to - from);
541     }
542     if (m_animatedAttributeType == AnimatedColor) {
543         Color from = SVGColor::colorFromRGBColorString(fromString);
544         if (!from.isValid())
545             return -1;
546         Color to = SVGColor::colorFromRGBColorString(toString);
547         if (!to.isValid())
548             return -1;
549         return ColorDistance(from, to).distance();
550     }
551     if (m_animatedAttributeType == AnimatedAngle || m_animatedAttributeType == AnimatedLength)
552         return ensureAnimator()->calculateDistance(this, fromString, toString);
553     return -1;
554 }
555
556 SVGAnimatedTypeAnimator* SVGAnimateElement::ensureAnimator()
557 {
558     if (!m_animator)
559         m_animator = SVGAnimatorFactory::create(targetElement(), m_animatedAttributeType, attributeName());
560     return m_animator.get();
561 }
562
563 }
564 #endif // ENABLE(SVG)