1 /*
2 * Copyright (C) 2004, 2005 Nikolas Zimmermann <zimmermann@kde.org>
3 * Copyright (C) 2004, 2005, 2006, 2007 Rob Buis <buis@kde.org>
4 * Copyright (C) 2007 Eric Seidel <eric@webkit.org>
5 * Copyright (C) 2008 Apple Inc. 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 "SVGAnimateTransformElement.h"
27
28 #include "AffineTransform.h"
29 #include "Attribute.h"
30 #include "RenderObject.h"
31 #include "RenderSVGResource.h"
32 #include "SVGAngle.h"
33 #include "SVGElementInstance.h"
34 #include "SVGGradientElement.h"
35 #include "SVGNames.h"
36 #include "SVGParserUtilities.h"
37 #include "SVGSVGElement.h"
38 #include "SVGStyledTransformableElement.h"
39 #include "SVGTextElement.h"
40 #include "SVGTransform.h"
41 #include "SVGTransformList.h"
42 #include "SVGUseElement.h"
43 #include <math.h>
44 #include <wtf/MathExtras.h>
45
46 using namespace std;
47
48 namespace WebCore {
49
SVGAnimateTransformElement(const QualifiedName & tagName,Document * document)50 inline SVGAnimateTransformElement::SVGAnimateTransformElement(const QualifiedName& tagName, Document* document)
51 : SVGAnimationElement(tagName, document)
52 , m_type(SVGTransform::SVG_TRANSFORM_UNKNOWN)
53 , m_baseIndexInTransformList(0)
54 {
55 }
56
create(const QualifiedName & tagName,Document * document)57 PassRefPtr<SVGAnimateTransformElement> SVGAnimateTransformElement::create(const QualifiedName& tagName, Document* document)
58 {
59 return adoptRef(new SVGAnimateTransformElement(tagName, document));
60 }
61
hasValidAttributeType() const62 bool SVGAnimateTransformElement::hasValidAttributeType() const
63 {
64 SVGElement* targetElement = this->targetElement();
65 if (!targetElement)
66 return false;
67
68 return determineAnimatedAttributeType(targetElement) == AnimatedTransformList;
69 }
70
determineAnimatedAttributeType(SVGElement * targetElement) const71 AnimatedAttributeType SVGAnimateTransformElement::determineAnimatedAttributeType(SVGElement* targetElement) const
72 {
73 ASSERT(targetElement);
74
75 // Just transform lists can be animated with <animateTransform>
76 // http://www.w3.org/TR/SVG/animate.html#AnimationAttributesAndProperties
77 if (targetElement->animatedPropertyTypeForAttribute(attributeName()) != AnimatedTransformList)
78 return AnimatedUnknown;
79
80 return AnimatedTransformList;
81 }
82
parseMappedAttribute(Attribute * attr)83 void SVGAnimateTransformElement::parseMappedAttribute(Attribute* attr)
84 {
85 if (attr->name() == SVGNames::typeAttr) {
86 if (attr->value() == "translate")
87 m_type = SVGTransform::SVG_TRANSFORM_TRANSLATE;
88 else if (attr->value() == "scale")
89 m_type = SVGTransform::SVG_TRANSFORM_SCALE;
90 else if (attr->value() == "rotate")
91 m_type = SVGTransform::SVG_TRANSFORM_ROTATE;
92 else if (attr->value() == "skewX")
93 m_type = SVGTransform::SVG_TRANSFORM_SKEWX;
94 else if (attr->value() == "skewY")
95 m_type = SVGTransform::SVG_TRANSFORM_SKEWY;
96 } else
97 SVGAnimationElement::parseMappedAttribute(attr);
98 }
99
100
transformListFor(SVGElement * element)101 static SVGTransformList* transformListFor(SVGElement* element)
102 {
103 ASSERT(element);
104 if (element->isStyledTransformable())
105 return &static_cast<SVGStyledTransformableElement*>(element)->transform();
106 if (element->hasTagName(SVGNames::textTag))
107 return &static_cast<SVGTextElement*>(element)->transform();
108 if (element->hasTagName(SVGNames::linearGradientTag) || element->hasTagName(SVGNames::radialGradientTag))
109 return &static_cast<SVGGradientElement*>(element)->gradientTransform();
110 // FIXME: Handle patternTransform, which is obviously missing!
111 return 0;
112 }
113
resetToBaseValue(const String & baseValue)114 void SVGAnimateTransformElement::resetToBaseValue(const String& baseValue)
115 {
116 SVGElement* targetElement = this->targetElement();
117 if (!targetElement || determineAnimatedAttributeType(targetElement) == AnimatedUnknown)
118 return;
119
120 if (targetElement->hasTagName(SVGNames::linearGradientTag) || targetElement->hasTagName(SVGNames::radialGradientTag)) {
121 targetElement->setAttribute(SVGNames::gradientTransformAttr, baseValue.isEmpty() ? "matrix(1 0 0 1 0 0)" : baseValue);
122 return;
123 }
124
125 if (baseValue.isEmpty()) {
126 if (SVGTransformList* list = transformListFor(targetElement))
127 list->clear();
128 } else
129 targetElement->setAttribute(SVGNames::transformAttr, baseValue);
130 }
131
calculateAnimatedValue(float percentage,unsigned repeat,SVGSMILElement *)132 void SVGAnimateTransformElement::calculateAnimatedValue(float percentage, unsigned repeat, SVGSMILElement*)
133 {
134 SVGElement* targetElement = this->targetElement();
135 if (!targetElement || determineAnimatedAttributeType(targetElement) == AnimatedUnknown)
136 return;
137 SVGTransformList* transformList = transformListFor(targetElement);
138 ASSERT(transformList);
139
140 if (!isAdditive())
141 transformList->clear();
142 if (isAccumulated() && repeat) {
143 SVGTransform accumulatedTransform = SVGTransformDistance(m_fromTransform, m_toTransform).scaledDistance(repeat).addToSVGTransform(SVGTransform());
144 transformList->append(accumulatedTransform);
145 }
146 SVGTransform transform = SVGTransformDistance(m_fromTransform, m_toTransform).scaledDistance(percentage).addToSVGTransform(m_fromTransform);
147 transformList->append(transform);
148 }
149
calculateFromAndToValues(const String & fromString,const String & toString)150 bool SVGAnimateTransformElement::calculateFromAndToValues(const String& fromString, const String& toString)
151 {
152 m_fromTransform = parseTransformValue(fromString);
153 if (!m_fromTransform.isValid())
154 return false;
155 m_toTransform = parseTransformValue(toString);
156 return m_toTransform.isValid();
157 }
158
calculateFromAndByValues(const String & fromString,const String & byString)159 bool SVGAnimateTransformElement::calculateFromAndByValues(const String& fromString, const String& byString)
160 {
161 m_fromTransform = parseTransformValue(fromString);
162 if (!m_fromTransform.isValid())
163 return false;
164 m_toTransform = SVGTransformDistance::addSVGTransforms(m_fromTransform, parseTransformValue(byString));
165 return m_toTransform.isValid();
166 }
167
parseTransformValue(const String & value) const168 SVGTransform SVGAnimateTransformElement::parseTransformValue(const String& value) const
169 {
170 if (value.isEmpty())
171 return SVGTransform(m_type);
172 SVGTransform result;
173 // FIXME: This is pretty dumb but parseTransformValue() wants those parenthesis.
174 String parseString("(" + value + ")");
175 const UChar* ptr = parseString.characters();
176 SVGTransformable::parseTransformValue(m_type, ptr, ptr + parseString.length(), result); // ignoring return value
177 return result;
178 }
179
applyResultsToTarget()180 void SVGAnimateTransformElement::applyResultsToTarget()
181 {
182 SVGElement* targetElement = this->targetElement();
183 if (!targetElement || determineAnimatedAttributeType(targetElement) == AnimatedUnknown)
184 return;
185
186 // We accumulate to the target element transform list so there is not much to do here.
187 if (RenderObject* renderer = targetElement->renderer()) {
188 renderer->setNeedsTransformUpdate();
189 RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer);
190 }
191
192 // ...except in case where we have additional instances in <use> trees.
193 SVGTransformList* transformList = transformListFor(targetElement);
194 if (!transformList)
195 return;
196
197 const HashSet<SVGElementInstance*>& instances = targetElement->instancesForElement();
198 const HashSet<SVGElementInstance*>::const_iterator end = instances.end();
199 for (HashSet<SVGElementInstance*>::const_iterator it = instances.begin(); it != end; ++it) {
200 SVGElement* shadowTreeElement = (*it)->shadowTreeElement();
201 ASSERT(shadowTreeElement);
202 if (shadowTreeElement->isStyledTransformable())
203 static_cast<SVGStyledTransformableElement*>(shadowTreeElement)->setTransformBaseValue(*transformList);
204 else if (shadowTreeElement->hasTagName(SVGNames::textTag))
205 static_cast<SVGTextElement*>(shadowTreeElement)->setTransformBaseValue(*transformList);
206 else if (shadowTreeElement->hasTagName(SVGNames::linearGradientTag) || shadowTreeElement->hasTagName(SVGNames::radialGradientTag))
207 static_cast<SVGGradientElement*>(shadowTreeElement)->setGradientTransformBaseValue(*transformList);
208 // FIXME: Handle patternTransform, obviously missing!
209 if (RenderObject* renderer = shadowTreeElement->renderer()) {
210 renderer->setNeedsTransformUpdate();
211 RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer);
212 }
213 }
214 }
215
calculateDistance(const String & fromString,const String & toString)216 float SVGAnimateTransformElement::calculateDistance(const String& fromString, const String& toString)
217 {
218 // FIXME: This is not correct in all cases. The spec demands that each component (translate x and y for example)
219 // is paced separately. To implement this we need to treat each component as individual animation everywhere.
220 SVGTransform from = parseTransformValue(fromString);
221 if (!from.isValid())
222 return -1;
223 SVGTransform to = parseTransformValue(toString);
224 if (!to.isValid() || from.type() != to.type())
225 return -1;
226 if (to.type() == SVGTransform::SVG_TRANSFORM_TRANSLATE) {
227 FloatSize diff = to.translate() - from.translate();
228 return sqrtf(diff.width() * diff.width() + diff.height() * diff.height());
229 }
230 if (to.type() == SVGTransform::SVG_TRANSFORM_ROTATE)
231 return fabsf(to.angle() - from.angle());
232 if (to.type() == SVGTransform::SVG_TRANSFORM_SCALE) {
233 FloatSize diff = to.scale() - from.scale();
234 return sqrtf(diff.width() * diff.width() + diff.height() * diff.height());
235 }
236 return -1;
237 }
238
239 }
240 #endif // ENABLE(SVG)
241