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