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