1 /*
2 * Copyright (C) 2007 Eric Seidel <eric@webkit.org>
3 * Copyright (C) 2007 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
24 #include "core/svg/SVGAnimateMotionElement.h"
25
26 #include "SVGNames.h"
27 #include "core/rendering/RenderObject.h"
28 #include "core/rendering/svg/RenderSVGResource.h"
29 #include "core/rendering/svg/SVGPathData.h"
30 #include "core/svg/SVGElementInstance.h"
31 #include "core/svg/SVGMPathElement.h"
32 #include "core/svg/SVGParserUtilities.h"
33 #include "core/svg/SVGPathElement.h"
34 #include "core/svg/SVGPathUtilities.h"
35 #include "platform/transforms/AffineTransform.h"
36 #include "wtf/MathExtras.h"
37 #include "wtf/StdLibExtras.h"
38
39 namespace WebCore {
40
41 using namespace SVGNames;
42
SVGAnimateMotionElement(Document & document)43 inline SVGAnimateMotionElement::SVGAnimateMotionElement(Document& document)
44 : SVGAnimationElement(animateMotionTag, document)
45 , m_hasToPointAtEndOfDuration(false)
46 {
47 setCalcMode(CalcModePaced);
48 ScriptWrappable::init(this);
49 }
50
~SVGAnimateMotionElement()51 SVGAnimateMotionElement::~SVGAnimateMotionElement()
52 {
53 if (targetElement())
54 clearAnimatedType(targetElement());
55 }
56
create(Document & document)57 PassRefPtr<SVGAnimateMotionElement> SVGAnimateMotionElement::create(Document& document)
58 {
59 return adoptRef(new SVGAnimateMotionElement(document));
60 }
61
hasValidAttributeType()62 bool SVGAnimateMotionElement::hasValidAttributeType()
63 {
64 SVGElement* targetElement = this->targetElement();
65 if (!targetElement)
66 return false;
67
68 // We don't have a special attribute name to verify the animation type. Check the element name instead.
69 if (!targetElement->isSVGGraphicsElement())
70 return false;
71 // Spec: SVG 1.1 section 19.2.15
72 // FIXME: svgTag is missing. Needs to be checked, if transforming <svg> could cause problems.
73 if (targetElement->hasTagName(gTag)
74 || targetElement->hasTagName(defsTag)
75 || targetElement->hasTagName(useTag)
76 || targetElement->hasTagName(SVGNames::imageTag)
77 || targetElement->hasTagName(switchTag)
78 || targetElement->hasTagName(pathTag)
79 || targetElement->hasTagName(rectTag)
80 || targetElement->hasTagName(circleTag)
81 || targetElement->hasTagName(ellipseTag)
82 || targetElement->hasTagName(lineTag)
83 || targetElement->hasTagName(polylineTag)
84 || targetElement->hasTagName(polygonTag)
85 || targetElement->hasTagName(textTag)
86 || targetElement->hasTagName(clipPathTag)
87 || targetElement->hasTagName(maskTag)
88 || targetElement->hasTagName(SVGNames::aTag)
89 || targetElement->hasTagName(foreignObjectTag)
90 )
91 return true;
92 return false;
93 }
94
hasValidAttributeName()95 bool SVGAnimateMotionElement::hasValidAttributeName()
96 {
97 // AnimateMotion does not use attributeName so it is always valid.
98 return true;
99 }
100
isSupportedAttribute(const QualifiedName & attrName)101 bool SVGAnimateMotionElement::isSupportedAttribute(const QualifiedName& attrName)
102 {
103 DEFINE_STATIC_LOCAL(HashSet<QualifiedName>, supportedAttributes, ());
104 if (supportedAttributes.isEmpty())
105 supportedAttributes.add(SVGNames::pathAttr);
106 return supportedAttributes.contains<SVGAttributeHashTranslator>(attrName);
107 }
108
parseAttribute(const QualifiedName & name,const AtomicString & value)109 void SVGAnimateMotionElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
110 {
111 if (!isSupportedAttribute(name)) {
112 SVGAnimationElement::parseAttribute(name, value);
113 return;
114 }
115
116 if (name == SVGNames::pathAttr) {
117 m_path = Path();
118 buildPathFromString(value, m_path);
119 updateAnimationPath();
120 return;
121 }
122
123 ASSERT_NOT_REACHED();
124 }
125
rotateMode() const126 SVGAnimateMotionElement::RotateMode SVGAnimateMotionElement::rotateMode() const
127 {
128 DEFINE_STATIC_LOCAL(const AtomicString, autoVal, ("auto", AtomicString::ConstructFromLiteral));
129 DEFINE_STATIC_LOCAL(const AtomicString, autoReverse, ("auto-reverse", AtomicString::ConstructFromLiteral));
130 const AtomicString& rotate = getAttribute(SVGNames::rotateAttr);
131 if (rotate == autoVal)
132 return RotateAuto;
133 if (rotate == autoReverse)
134 return RotateAutoReverse;
135 return RotateAngle;
136 }
137
updateAnimationPath()138 void SVGAnimateMotionElement::updateAnimationPath()
139 {
140 m_animationPath = Path();
141 bool foundMPath = false;
142
143 for (Node* child = firstChild(); child; child = child->nextSibling()) {
144 if (child->hasTagName(SVGNames::mpathTag)) {
145 SVGMPathElement* mPath = toSVGMPathElement(child);
146 SVGPathElement* pathElement = mPath->pathElement();
147 if (pathElement) {
148 updatePathFromGraphicsElement(pathElement, m_animationPath);
149 foundMPath = true;
150 break;
151 }
152 }
153 }
154
155 if (!foundMPath && fastHasAttribute(SVGNames::pathAttr))
156 m_animationPath = m_path;
157
158 updateAnimationMode();
159 }
160
161 template<typename CharType>
parsePointInternal(const String & string,FloatPoint & point)162 static bool parsePointInternal(const String& string, FloatPoint& point)
163 {
164 const CharType* ptr = string.getCharacters<CharType>();
165 const CharType* end = ptr + string.length();
166
167 if (!skipOptionalSVGSpaces(ptr, end))
168 return false;
169
170 float x = 0;
171 if (!parseNumber(ptr, end, x))
172 return false;
173
174 float y = 0;
175 if (!parseNumber(ptr, end, y))
176 return false;
177
178 point = FloatPoint(x, y);
179
180 // disallow anything except spaces at the end
181 return !skipOptionalSVGSpaces(ptr, end);
182 }
183
parsePoint(const String & string,FloatPoint & point)184 static bool parsePoint(const String& string, FloatPoint& point)
185 {
186 if (string.isEmpty())
187 return false;
188 if (string.is8Bit())
189 return parsePointInternal<LChar>(string, point);
190 return parsePointInternal<UChar>(string, point);
191 }
192
resetAnimatedType()193 void SVGAnimateMotionElement::resetAnimatedType()
194 {
195 if (!hasValidAttributeType())
196 return;
197 SVGElement* targetElement = this->targetElement();
198 if (!targetElement)
199 return;
200 if (AffineTransform* transform = targetElement->supplementalTransform())
201 transform->makeIdentity();
202 }
203
clearAnimatedType(SVGElement * targetElement)204 void SVGAnimateMotionElement::clearAnimatedType(SVGElement* targetElement)
205 {
206 if (!targetElement)
207 return;
208
209 AffineTransform* transform = targetElement->supplementalTransform();
210 if (!transform)
211 return;
212
213 transform->makeIdentity();
214
215 if (RenderObject* targetRenderer = targetElement->renderer()) {
216 targetRenderer->setNeedsTransformUpdate();
217 RenderSVGResource::markForLayoutAndParentResourceInvalidation(targetRenderer);
218 }
219 }
220
calculateToAtEndOfDurationValue(const String & toAtEndOfDurationString)221 bool SVGAnimateMotionElement::calculateToAtEndOfDurationValue(const String& toAtEndOfDurationString)
222 {
223 parsePoint(toAtEndOfDurationString, m_toPointAtEndOfDuration);
224 m_hasToPointAtEndOfDuration = true;
225 return true;
226 }
227
calculateFromAndToValues(const String & fromString,const String & toString)228 bool SVGAnimateMotionElement::calculateFromAndToValues(const String& fromString, const String& toString)
229 {
230 m_hasToPointAtEndOfDuration = false;
231 parsePoint(fromString, m_fromPoint);
232 parsePoint(toString, m_toPoint);
233 return true;
234 }
235
calculateFromAndByValues(const String & fromString,const String & byString)236 bool SVGAnimateMotionElement::calculateFromAndByValues(const String& fromString, const String& byString)
237 {
238 m_hasToPointAtEndOfDuration = false;
239 if (animationMode() == ByAnimation && !isAdditive())
240 return false;
241 parsePoint(fromString, m_fromPoint);
242 FloatPoint byPoint;
243 parsePoint(byString, byPoint);
244 m_toPoint = FloatPoint(m_fromPoint.x() + byPoint.x(), m_fromPoint.y() + byPoint.y());
245 return true;
246 }
247
calculateAnimatedValue(float percentage,unsigned repeatCount,SVGSMILElement *)248 void SVGAnimateMotionElement::calculateAnimatedValue(float percentage, unsigned repeatCount, SVGSMILElement*)
249 {
250 SVGElement* targetElement = this->targetElement();
251 if (!targetElement)
252 return;
253 AffineTransform* transform = targetElement->supplementalTransform();
254 if (!transform)
255 return;
256
257 if (RenderObject* targetRenderer = targetElement->renderer())
258 targetRenderer->setNeedsTransformUpdate();
259
260 if (!isAdditive())
261 transform->makeIdentity();
262
263 if (animationMode() != PathAnimation) {
264 FloatPoint toPointAtEndOfDuration = m_toPoint;
265 if (isAccumulated() && repeatCount && m_hasToPointAtEndOfDuration)
266 toPointAtEndOfDuration = m_toPointAtEndOfDuration;
267
268 float animatedX = 0;
269 animateAdditiveNumber(percentage, repeatCount, m_fromPoint.x(), m_toPoint.x(), toPointAtEndOfDuration.x(), animatedX);
270
271 float animatedY = 0;
272 animateAdditiveNumber(percentage, repeatCount, m_fromPoint.y(), m_toPoint.y(), toPointAtEndOfDuration.y(), animatedY);
273
274 transform->translate(animatedX, animatedY);
275 return;
276 }
277
278 ASSERT(!m_animationPath.isEmpty());
279
280 bool ok = false;
281 float positionOnPath = m_animationPath.length() * percentage;
282 FloatPoint position;
283 float angle;
284 ok = m_animationPath.pointAndNormalAtLength(positionOnPath, position, angle);
285 if (!ok)
286 return;
287
288 // Handle accumulate="sum".
289 if (isAccumulated() && repeatCount) {
290 FloatPoint positionAtEndOfDuration = m_animationPath.pointAtLength(m_animationPath.length(), ok);
291 if (ok)
292 position.move(positionAtEndOfDuration.x() * repeatCount, positionAtEndOfDuration.y() * repeatCount);
293 }
294
295 transform->translate(position.x(), position.y());
296 RotateMode rotateMode = this->rotateMode();
297 if (rotateMode != RotateAuto && rotateMode != RotateAutoReverse)
298 return;
299 if (rotateMode == RotateAutoReverse)
300 angle += 180;
301 transform->rotate(angle);
302 }
303
applyResultsToTarget()304 void SVGAnimateMotionElement::applyResultsToTarget()
305 {
306 // We accumulate to the target element transform list so there is not much to do here.
307 SVGElement* targetElement = this->targetElement();
308 if (!targetElement)
309 return;
310
311 if (RenderObject* renderer = targetElement->renderer())
312 RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer);
313
314 AffineTransform* t = targetElement->supplementalTransform();
315 if (!t)
316 return;
317
318 // ...except in case where we have additional instances in <use> trees.
319 const HashSet<SVGElementInstance*>& instances = targetElement->instancesForElement();
320 const HashSet<SVGElementInstance*>::const_iterator end = instances.end();
321 for (HashSet<SVGElementInstance*>::const_iterator it = instances.begin(); it != end; ++it) {
322 SVGElement* shadowTreeElement = (*it)->shadowTreeElement();
323 ASSERT(shadowTreeElement);
324 AffineTransform* transform = shadowTreeElement->supplementalTransform();
325 if (!transform)
326 continue;
327 transform->setMatrix(t->a(), t->b(), t->c(), t->d(), t->e(), t->f());
328 if (RenderObject* renderer = shadowTreeElement->renderer()) {
329 renderer->setNeedsTransformUpdate();
330 RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer);
331 }
332 }
333 }
334
calculateDistance(const String & fromString,const String & toString)335 float SVGAnimateMotionElement::calculateDistance(const String& fromString, const String& toString)
336 {
337 FloatPoint from;
338 FloatPoint to;
339 if (!parsePoint(fromString, from))
340 return -1;
341 if (!parsePoint(toString, to))
342 return -1;
343 FloatSize diff = to - from;
344 return sqrtf(diff.width() * diff.width() + diff.height() * diff.height());
345 }
346
updateAnimationMode()347 void SVGAnimateMotionElement::updateAnimationMode()
348 {
349 if (!m_animationPath.isEmpty())
350 setAnimationMode(PathAnimation);
351 else
352 SVGAnimationElement::updateAnimationMode();
353 }
354
355 }
356