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