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.
6 This file is part of the KDE project
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.
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.
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.
25 #if ENABLE(SVG) && ENABLE(SVG_ANIMATION)
26 #include "SVGAnimateElement.h"
28 #include "ColorDistance.h"
29 #include "FloatConversion.h"
31 #include "SVGParserUtilities.h"
38 SVGAnimateElement::SVGAnimateElement(const QualifiedName
& tagName
, Document
* doc
)
39 : SVGAnimationElement(tagName
, doc
)
40 , m_propertyType(StringProperty
)
43 , m_animatedNumber(numeric_limits
<double>::infinity())
47 SVGAnimateElement::~SVGAnimateElement()
51 static bool parseNumberValueAndUnit(const String
& in
, double& value
, String
& unit
)
53 // FIXME: These are from top of my head, figure out all property types that can be animated as numbers.
54 unsigned unitLength
= 0;
55 String parse
= in
.stripWhiteSpace();
56 if (parse
.endsWith("%"))
58 else if (parse
.endsWith("px") || parse
.endsWith("pt") || parse
.endsWith("em"))
60 else if (parse
.endsWith("deg") || parse
.endsWith("rad"))
62 else if (parse
.endsWith("grad"))
64 String newUnit
= parse
.right(unitLength
);
65 String number
= parse
.left(parse
.length() - unitLength
);
66 if (!unit
.isEmpty() && newUnit
!= unit
|| number
.isEmpty())
68 UChar last
= number
[number
.length() - 1];
69 if (last
< '0' || last
> '9')
73 value
= number
.toDouble(&ok
);
77 SVGAnimateElement::PropertyType
SVGAnimateElement::determinePropertyType(const String
& attribute
) const
79 // FIXME: We need a full property table for figuring this out reliably.
80 if (hasTagName(SVGNames::animateColorTag
))
82 if (attribute
== "color" || attribute
== "fill" || attribute
== "stroke")
84 return NumberProperty
;
87 void SVGAnimateElement::calculateAnimatedValue(float percentage
, unsigned repeat
, SVGSMILElement
* resultElement
)
89 ASSERT(percentage
>= 0.f
&& percentage
<= 1.f
);
90 ASSERT(resultElement
);
91 if (hasTagName(SVGNames::setTag
))
93 if (!resultElement
->hasTagName(SVGNames::animateTag
) && !resultElement
->hasTagName(SVGNames::animateColorTag
)
94 && !resultElement
->hasTagName(SVGNames::setTag
))
96 SVGAnimateElement
* results
= static_cast<SVGAnimateElement
*>(resultElement
);
97 // Can't accumulate over a string property.
98 if (results
->m_propertyType
== StringProperty
&& m_propertyType
!= StringProperty
)
100 if (m_propertyType
== NumberProperty
) {
101 // To animation uses contributions from the lower priority animations as the base value.
102 if (animationMode() == ToAnimation
)
103 m_fromNumber
= results
->m_animatedNumber
;
105 double number
= (m_toNumber
- m_fromNumber
) * percentage
+ m_fromNumber
;
107 // FIXME: This is not correct for values animation.
108 if (isAccumulated() && repeat
)
109 number
+= m_toNumber
* repeat
;
110 if (isAdditive() && animationMode() != ToAnimation
)
111 results
->m_animatedNumber
+= number
;
113 results
->m_animatedNumber
= number
;
116 if (m_propertyType
== ColorProperty
) {
117 if (animationMode() == ToAnimation
)
118 m_fromColor
= results
->m_animatedColor
;
119 Color color
= ColorDistance(m_fromColor
, m_toColor
).scaledDistance(percentage
).addToColorAndClamp(m_fromColor
);
120 // FIXME: Accumulate colors.
121 if (isAdditive() && animationMode() != ToAnimation
)
122 results
->m_animatedColor
= ColorDistance::addColorsAndClamp(results
->m_animatedColor
, color
);
124 results
->m_animatedColor
= color
;
127 AnimationMode animationMode
= this->animationMode();
128 ASSERT(animationMode
== FromToAnimation
|| animationMode
== ToAnimation
|| animationMode
== ValuesAnimation
);
129 if ((animationMode
== FromToAnimation
&& percentage
> 0.5f
) || animationMode
== ToAnimation
|| percentage
== 1.0f
)
130 results
->m_animatedString
= m_toString
;
132 results
->m_animatedString
= m_fromString
;
133 // Higher priority replace animation overrides any additive results so far.
134 results
->m_propertyType
= StringProperty
;
137 bool SVGAnimateElement::calculateFromAndToValues(const String
& fromString
, const String
& toString
)
139 // FIXME: Needs more solid way determine target attribute type.
140 m_propertyType
= determinePropertyType(attributeName());
141 if (m_propertyType
== ColorProperty
) {
142 m_fromColor
= SVGColor::colorFromRGBColorString(fromString
);
143 m_toColor
= SVGColor::colorFromRGBColorString(toString
);
144 if (m_fromColor
.isValid() && m_toColor
.isValid())
146 } else if (m_propertyType
== NumberProperty
) {
147 m_numberUnit
= String();
148 if (parseNumberValueAndUnit(toString
, m_toNumber
, m_numberUnit
)) {
149 // For to-animations the from number is calculated later
150 if (animationMode() == ToAnimation
|| parseNumberValueAndUnit(fromString
, m_fromNumber
, m_numberUnit
))
154 m_fromString
= fromString
;
155 m_toString
= toString
;
156 m_propertyType
= StringProperty
;
160 bool SVGAnimateElement::calculateFromAndByValues(const String
& fromString
, const String
& byString
)
162 ASSERT(!hasTagName(SVGNames::setTag
));
163 m_propertyType
= determinePropertyType(attributeName());
164 if (m_propertyType
== ColorProperty
) {
165 m_fromColor
= fromString
.isEmpty() ? Color() : SVGColor::colorFromRGBColorString(fromString
);
166 m_toColor
= ColorDistance::addColorsAndClamp(m_fromColor
, SVGColor::colorFromRGBColorString(byString
));
167 if (!m_fromColor
.isValid() || !m_toColor
.isValid())
170 m_numberUnit
= String();
172 if (!fromString
.isEmpty() && !parseNumberValueAndUnit(fromString
, m_fromNumber
, m_numberUnit
))
174 if (!parseNumberValueAndUnit(byString
, m_toNumber
, m_numberUnit
))
176 m_toNumber
+= m_fromNumber
;
181 void SVGAnimateElement::resetToBaseValue(const String
& baseString
)
183 m_animatedString
= baseString
;
184 m_propertyType
= determinePropertyType(attributeName());
185 if (m_propertyType
== ColorProperty
) {
186 m_animatedColor
= baseString
.isEmpty() ? Color() : SVGColor::colorFromRGBColorString(baseString
);
187 if (m_animatedColor
.isValid())
189 } else if (m_propertyType
== NumberProperty
) {
190 if (baseString
.isEmpty()) {
191 m_animatedNumber
= 0;
192 m_numberUnit
= String();
195 if (parseNumberValueAndUnit(baseString
, m_animatedNumber
, m_numberUnit
))
198 m_propertyType
= StringProperty
;
201 void SVGAnimateElement::applyResultsToTarget()
204 if (m_propertyType
== ColorProperty
)
205 valueToApply
= m_animatedColor
.name();
206 else if (m_propertyType
== NumberProperty
)
207 valueToApply
= String::number(m_animatedNumber
) + m_numberUnit
;
209 valueToApply
= m_animatedString
;
211 setTargetAttributeAnimatedValue(valueToApply
);
214 float SVGAnimateElement::calculateDistance(const String
& fromString
, const String
& toString
)
216 m_propertyType
= determinePropertyType(attributeName());
217 if (m_propertyType
== NumberProperty
) {
221 if (!parseNumberValueAndUnit(fromString
, from
, unit
))
223 if (!parseNumberValueAndUnit(toString
, to
, unit
))
225 return narrowPrecisionToFloat(fabs(to
- from
));
226 } else if (m_propertyType
== ColorProperty
) {
227 Color from
= SVGColor::colorFromRGBColorString(fromString
);
230 Color to
= SVGColor::colorFromRGBColorString(toString
);
233 return ColorDistance(from
, to
).distance();
241 #endif // ENABLE(SVG)