• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2     Copyright (C) 2004, 2005 Nikolas Zimmermann <wildfox@kde.org>
3                   2004, 2005, 2006 Rob Buis <buis@kde.org>
4     Copyright (C) 2008 Apple Inc. All rights reserved.
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., 51 Franklin Street, Fifth Floor,
21     Boston, MA 02110-1301, USA.
22 */
23 
24 #include "config.h"
25 #if ENABLE(SVG) && ENABLE(SVG_ANIMATION)
26 #include "SVGAnimateElement.h"
27 
28 #include "ColorDistance.h"
29 #include "FloatConversion.h"
30 #include "SVGColor.h"
31 #include "SVGParserUtilities.h"
32 #include "SVGPathSegList.h"
33 #include <math.h>
34 
35 using namespace std;
36 
37 namespace WebCore {
38 
SVGAnimateElement(const QualifiedName & tagName,Document * doc)39 SVGAnimateElement::SVGAnimateElement(const QualifiedName& tagName, Document* doc)
40     : SVGAnimationElement(tagName, doc)
41     , m_propertyType(StringProperty)
42     , m_fromNumber(0)
43     , m_toNumber(0)
44     , m_animatedNumber(numeric_limits<double>::infinity())
45 {
46 }
47 
~SVGAnimateElement()48 SVGAnimateElement::~SVGAnimateElement()
49 {
50 }
51 
parseNumberValueAndUnit(const String & in,double & value,String & unit)52 static bool parseNumberValueAndUnit(const String& in, double& value, String& unit)
53 {
54     // FIXME: These are from top of my head, figure out all property types that can be animated as numbers.
55     unsigned unitLength = 0;
56     String parse = in.stripWhiteSpace();
57     if (parse.endsWith("%"))
58         unitLength = 1;
59     else if (parse.endsWith("px") || parse.endsWith("pt") || parse.endsWith("em"))
60         unitLength = 2;
61     else if (parse.endsWith("deg") || parse.endsWith("rad"))
62         unitLength = 3;
63     else if (parse.endsWith("grad"))
64         unitLength = 4;
65     String newUnit = parse.right(unitLength);
66     String number = parse.left(parse.length() - unitLength);
67     if (!unit.isEmpty() && newUnit != unit || number.isEmpty())
68         return false;
69     UChar last = number[number.length() - 1];
70     if (last < '0' || last > '9')
71         return false;
72     unit = newUnit;
73     bool ok;
74     value = number.toDouble(&ok);
75     return ok;
76 }
77 
determinePropertyType(const String & attribute) const78 SVGAnimateElement::PropertyType SVGAnimateElement::determinePropertyType(const String& attribute) const
79 {
80     // FIXME: We need a full property table for figuring this out reliably.
81     if (hasTagName(SVGNames::animateColorTag))
82         return ColorProperty;
83     if (attribute == "d")
84         return PathProperty;
85     if (attribute == "color" || attribute == "fill" || attribute == "stroke")
86         return ColorProperty;
87     return NumberProperty;
88 }
89 
calculateAnimatedValue(float percentage,unsigned repeat,SVGSMILElement * resultElement)90 void SVGAnimateElement::calculateAnimatedValue(float percentage, unsigned repeat, SVGSMILElement* resultElement)
91 {
92     ASSERT(percentage >= 0.f && percentage <= 1.f);
93     ASSERT(resultElement);
94     if (hasTagName(SVGNames::setTag))
95         percentage = 1.f;
96     if (!resultElement->hasTagName(SVGNames::animateTag) && !resultElement->hasTagName(SVGNames::animateColorTag)
97         && !resultElement->hasTagName(SVGNames::setTag))
98         return;
99     SVGAnimateElement* results = static_cast<SVGAnimateElement*>(resultElement);
100     // Can't accumulate over a string property.
101     if (results->m_propertyType == StringProperty && m_propertyType != StringProperty)
102         return;
103     if (m_propertyType == NumberProperty) {
104         // To animation uses contributions from the lower priority animations as the base value.
105         if (animationMode() == ToAnimation)
106             m_fromNumber = results->m_animatedNumber;
107 
108         double number = (m_toNumber - m_fromNumber) * percentage + m_fromNumber;
109 
110         // FIXME: This is not correct for values animation.
111         if (isAccumulated() && repeat)
112             number += m_toNumber * repeat;
113         if (isAdditive() && animationMode() != ToAnimation)
114             results->m_animatedNumber += number;
115         else
116             results->m_animatedNumber = number;
117         return;
118     }
119     if (m_propertyType == ColorProperty) {
120         if (animationMode() == ToAnimation)
121             m_fromColor = results->m_animatedColor;
122         Color color = ColorDistance(m_fromColor, m_toColor).scaledDistance(percentage).addToColorAndClamp(m_fromColor);
123         // FIXME: Accumulate colors.
124         if (isAdditive() && animationMode() != ToAnimation)
125             results->m_animatedColor = ColorDistance::addColorsAndClamp(results->m_animatedColor, color);
126         else
127             results->m_animatedColor = color;
128         return;
129     }
130     AnimationMode animationMode = this->animationMode();
131     if (m_propertyType == PathProperty) {
132         if (percentage == 0)
133             results->m_animatedPath = m_fromPath;
134         else if (percentage == 1.f)
135             results->m_animatedPath = m_toPath;
136         else {
137             if (m_fromPath && m_toPath)
138                 results->m_animatedPath = SVGPathSegList::createAnimated(m_fromPath.get(), m_toPath.get(), percentage);
139             else
140                 results->m_animatedPath.clear();
141             // Fall back to discrete animation if the paths are not compatible
142             if (!results->m_animatedPath)
143                 results->m_animatedPath = ((animationMode == FromToAnimation && percentage > 0.5f) || animationMode == ToAnimation || percentage == 1.0f)
144                     ? m_toPath : m_fromPath;
145         }
146         return;
147     }
148     ASSERT(animationMode == FromToAnimation || animationMode == ToAnimation || animationMode == ValuesAnimation);
149     if ((animationMode == FromToAnimation && percentage > 0.5f) || animationMode == ToAnimation || percentage == 1.0f)
150         results->m_animatedString = m_toString;
151     else
152         results->m_animatedString = m_fromString;
153     // Higher priority replace animation overrides any additive results so far.
154     results->m_propertyType = StringProperty;
155 }
156 
calculateFromAndToValues(const String & fromString,const String & toString)157 bool SVGAnimateElement::calculateFromAndToValues(const String& fromString, const String& toString)
158 {
159     // FIXME: Needs more solid way determine target attribute type.
160     m_propertyType = determinePropertyType(attributeName());
161     if (m_propertyType == ColorProperty) {
162         m_fromColor = SVGColor::colorFromRGBColorString(fromString);
163         m_toColor = SVGColor::colorFromRGBColorString(toString);
164         if (m_fromColor.isValid() && m_toColor.isValid())
165             return true;
166     } else if (m_propertyType == NumberProperty) {
167         m_numberUnit = String();
168         if (parseNumberValueAndUnit(toString, m_toNumber, m_numberUnit)) {
169             // For to-animations the from number is calculated later
170             if (animationMode() == ToAnimation || parseNumberValueAndUnit(fromString, m_fromNumber, m_numberUnit))
171                 return true;
172         }
173     } else if (m_propertyType == PathProperty) {
174         m_fromPath = SVGPathSegList::create(SVGNames::dAttr);
175         if (pathSegListFromSVGData(m_fromPath.get(), fromString)) {
176             m_toPath = SVGPathSegList::create(SVGNames::dAttr);
177             if (pathSegListFromSVGData(m_toPath.get(), toString))
178                 return true;
179         }
180         m_fromPath.clear();
181         m_toPath.clear();
182     }
183     m_fromString = fromString;
184     m_toString = toString;
185     m_propertyType = StringProperty;
186     return true;
187 }
188 
calculateFromAndByValues(const String & fromString,const String & byString)189 bool SVGAnimateElement::calculateFromAndByValues(const String& fromString, const String& byString)
190 {
191     ASSERT(!hasTagName(SVGNames::setTag));
192     m_propertyType = determinePropertyType(attributeName());
193     if (m_propertyType == ColorProperty) {
194         m_fromColor = fromString.isEmpty() ? Color() : SVGColor::colorFromRGBColorString(fromString);
195         m_toColor = ColorDistance::addColorsAndClamp(m_fromColor, SVGColor::colorFromRGBColorString(byString));
196         if (!m_fromColor.isValid() || !m_toColor.isValid())
197             return false;
198     } else {
199         m_numberUnit = String();
200         m_fromNumber = 0;
201         if (!fromString.isEmpty() && !parseNumberValueAndUnit(fromString, m_fromNumber, m_numberUnit))
202             return false;
203         if (!parseNumberValueAndUnit(byString, m_toNumber, m_numberUnit))
204             return false;
205         m_toNumber += m_fromNumber;
206     }
207     return true;
208 }
209 
resetToBaseValue(const String & baseString)210 void SVGAnimateElement::resetToBaseValue(const String& baseString)
211 {
212     m_animatedString = baseString;
213     m_propertyType = determinePropertyType(attributeName());
214     if (m_propertyType == ColorProperty) {
215         m_animatedColor = baseString.isEmpty() ? Color() : SVGColor::colorFromRGBColorString(baseString);
216         if (m_animatedColor.isValid())
217             return;
218     } else if (m_propertyType == NumberProperty) {
219         if (baseString.isEmpty()) {
220             m_animatedNumber = 0;
221             m_numberUnit = String();
222             return;
223         }
224         if (parseNumberValueAndUnit(baseString, m_animatedNumber, m_numberUnit))
225             return;
226     } else if (m_propertyType == PathProperty) {
227         m_animatedPath.clear();
228         return;
229     }
230     m_propertyType = StringProperty;
231 }
232 
applyResultsToTarget()233 void SVGAnimateElement::applyResultsToTarget()
234 {
235     String valueToApply;
236     if (m_propertyType == ColorProperty)
237         valueToApply = m_animatedColor.name();
238     else if (m_propertyType == NumberProperty)
239         valueToApply = String::number(m_animatedNumber) + m_numberUnit;
240     else if (m_propertyType == PathProperty) {
241         if (!m_animatedPath || !m_animatedPath->numberOfItems())
242             valueToApply = m_animatedString;
243         else {
244             // We need to keep going to string and back because we are currently only able to paint
245             // "processed" paths where complex shapes are replaced with simpler ones. Path
246             // morphing needs to be done with unprocessed paths.
247             // FIXME: This could be optimized if paths were not processed at parse time.
248             unsigned itemCount = m_animatedPath->numberOfItems();
249             ExceptionCode ec;
250             for (unsigned n = 0; n < itemCount; ++n) {
251                 RefPtr<SVGPathSeg> segment = m_animatedPath->getItem(n, ec);
252                 valueToApply.append(segment->toString() + " ");
253             }
254         }
255     } else
256         valueToApply = m_animatedString;
257 
258     setTargetAttributeAnimatedValue(valueToApply);
259 }
260 
calculateDistance(const String & fromString,const String & toString)261 float SVGAnimateElement::calculateDistance(const String& fromString, const String& toString)
262 {
263     m_propertyType = determinePropertyType(attributeName());
264     if (m_propertyType == NumberProperty) {
265         double from;
266         double to;
267         String unit;
268         if (!parseNumberValueAndUnit(fromString, from, unit))
269             return -1.f;
270         if (!parseNumberValueAndUnit(toString, to, unit))
271             return -1.f;
272         return narrowPrecisionToFloat(fabs(to - from));
273     } else if (m_propertyType == ColorProperty) {
274         Color from = SVGColor::colorFromRGBColorString(fromString);
275         if (!from.isValid())
276             return -1.f;
277         Color to = SVGColor::colorFromRGBColorString(toString);
278         if (!to.isValid())
279             return -1.f;
280         return ColorDistance(from, to).distance();
281     }
282     return -1.f;
283 }
284 
285 }
286 
287 // vim:ts=4:noet
288 #endif // ENABLE(SVG)
289 
290