• 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 "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