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