• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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