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.
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.
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.
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.
24 #include "core/svg/SVGAnimateMotionElement.h"
26 #include "core/SVGNames.h"
27 #include "core/dom/ElementTraversal.h"
28 #include "core/rendering/RenderObject.h"
29 #include "core/rendering/svg/RenderSVGResource.h"
30 #include "core/rendering/svg/SVGPathData.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"
41 using namespace SVGNames;
43 inline SVGAnimateMotionElement::SVGAnimateMotionElement(Document& document)
44 : SVGAnimationElement(animateMotionTag, document)
45 , m_hasToPointAtEndOfDuration(false)
47 setCalcMode(CalcModePaced);
48 ScriptWrappable::init(this);
51 DEFINE_NODE_FACTORY(SVGAnimateMotionElement)
53 SVGAnimateMotionElement::~SVGAnimateMotionElement()
57 bool SVGAnimateMotionElement::hasValidAttributeType()
59 SVGElement* targetElement = this->targetElement();
63 // We don't have a special attribute name to verify the animation type. Check the element name instead.
64 if (!targetElement->isSVGGraphicsElement())
66 // Spec: SVG 1.1 section 19.2.15
67 // FIXME: svgTag is missing. Needs to be checked, if transforming <svg> could cause problems.
68 return (isSVGGElement(*targetElement)
69 || isSVGDefsElement(*targetElement)
70 || isSVGUseElement(*targetElement)
71 || isSVGImageElement(*targetElement)
72 || isSVGSwitchElement(*targetElement)
73 || isSVGPathElement(*targetElement)
74 || isSVGRectElement(*targetElement)
75 || isSVGCircleElement(*targetElement)
76 || isSVGEllipseElement(*targetElement)
77 || isSVGLineElement(*targetElement)
78 || isSVGPolylineElement(*targetElement)
79 || isSVGPolygonElement(*targetElement)
80 || isSVGTextElement(*targetElement)
81 || isSVGClipPathElement(*targetElement)
82 || isSVGMaskElement(*targetElement)
83 || isSVGAElement(*targetElement)
84 || isSVGForeignObjectElement(*targetElement)
88 bool SVGAnimateMotionElement::hasValidAttributeName()
90 // AnimateMotion does not use attributeName so it is always valid.
94 void SVGAnimateMotionElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
96 if (name == SVGNames::pathAttr) {
98 buildPathFromString(value, m_path);
99 updateAnimationPath();
103 SVGAnimationElement::parseAttribute(name, value);
106 SVGAnimateMotionElement::RotateMode SVGAnimateMotionElement::rotateMode() const
108 DEFINE_STATIC_LOCAL(const AtomicString, autoVal, ("auto", AtomicString::ConstructFromLiteral));
109 DEFINE_STATIC_LOCAL(const AtomicString, autoReverse, ("auto-reverse", AtomicString::ConstructFromLiteral));
110 const AtomicString& rotate = getAttribute(SVGNames::rotateAttr);
111 if (rotate == autoVal)
113 if (rotate == autoReverse)
114 return RotateAutoReverse;
118 void SVGAnimateMotionElement::updateAnimationPath()
120 m_animationPath = Path();
121 bool foundMPath = false;
123 for (SVGMPathElement* mpath = Traversal<SVGMPathElement>::firstChild(*this); mpath; mpath = Traversal<SVGMPathElement>::nextSibling(*mpath)) {
124 if (SVGPathElement* pathElement = mpath->pathElement()) {
125 updatePathFromGraphicsElement(pathElement, m_animationPath);
131 if (!foundMPath && fastHasAttribute(SVGNames::pathAttr))
132 m_animationPath = m_path;
134 updateAnimationMode();
137 template<typename CharType>
138 static bool parsePointInternal(const String& string, FloatPoint& point)
140 const CharType* ptr = string.getCharacters<CharType>();
141 const CharType* end = ptr + string.length();
143 if (!skipOptionalSVGSpaces(ptr, end))
147 if (!parseNumber(ptr, end, x))
151 if (!parseNumber(ptr, end, y))
154 point = FloatPoint(x, y);
156 // disallow anything except spaces at the end
157 return !skipOptionalSVGSpaces(ptr, end);
160 static bool parsePoint(const String& string, FloatPoint& point)
162 if (string.isEmpty())
165 return parsePointInternal<LChar>(string, point);
166 return parsePointInternal<UChar>(string, point);
169 void SVGAnimateMotionElement::resetAnimatedType()
171 if (!hasValidAttributeType())
173 SVGElement* targetElement = this->targetElement();
176 if (AffineTransform* transform = targetElement->supplementalTransform())
177 transform->makeIdentity();
180 void SVGAnimateMotionElement::clearAnimatedType(SVGElement* targetElement)
185 AffineTransform* transform = targetElement->supplementalTransform();
189 transform->makeIdentity();
191 if (RenderObject* targetRenderer = targetElement->renderer()) {
192 targetRenderer->setNeedsTransformUpdate();
193 RenderSVGResource::markForLayoutAndParentResourceInvalidation(targetRenderer);
197 bool SVGAnimateMotionElement::calculateToAtEndOfDurationValue(const String& toAtEndOfDurationString)
199 parsePoint(toAtEndOfDurationString, m_toPointAtEndOfDuration);
200 m_hasToPointAtEndOfDuration = true;
204 bool SVGAnimateMotionElement::calculateFromAndToValues(const String& fromString, const String& toString)
206 m_hasToPointAtEndOfDuration = false;
207 parsePoint(fromString, m_fromPoint);
208 parsePoint(toString, m_toPoint);
212 bool SVGAnimateMotionElement::calculateFromAndByValues(const String& fromString, const String& byString)
214 m_hasToPointAtEndOfDuration = false;
215 if (animationMode() == ByAnimation && !isAdditive())
217 parsePoint(fromString, m_fromPoint);
219 parsePoint(byString, byPoint);
220 m_toPoint = FloatPoint(m_fromPoint.x() + byPoint.x(), m_fromPoint.y() + byPoint.y());
224 void SVGAnimateMotionElement::calculateAnimatedValue(float percentage, unsigned repeatCount, SVGSMILElement*)
226 SVGElement* targetElement = this->targetElement();
229 AffineTransform* transform = targetElement->supplementalTransform();
233 if (RenderObject* targetRenderer = targetElement->renderer())
234 targetRenderer->setNeedsTransformUpdate();
237 transform->makeIdentity();
239 if (animationMode() != PathAnimation) {
240 FloatPoint toPointAtEndOfDuration = m_toPoint;
241 if (isAccumulated() && repeatCount && m_hasToPointAtEndOfDuration)
242 toPointAtEndOfDuration = m_toPointAtEndOfDuration;
245 animateAdditiveNumber(percentage, repeatCount, m_fromPoint.x(), m_toPoint.x(), toPointAtEndOfDuration.x(), animatedX);
248 animateAdditiveNumber(percentage, repeatCount, m_fromPoint.y(), m_toPoint.y(), toPointAtEndOfDuration.y(), animatedY);
250 transform->translate(animatedX, animatedY);
254 ASSERT(!m_animationPath.isEmpty());
256 float positionOnPath = m_animationPath.length() * percentage;
259 bool ok = m_animationPath.pointAndNormalAtLength(positionOnPath, position, angle);
263 // Handle accumulate="sum".
264 if (isAccumulated() && repeatCount) {
265 FloatPoint positionAtEndOfDuration = m_animationPath.pointAtLength(m_animationPath.length(), ok);
267 position.move(positionAtEndOfDuration.x() * repeatCount, positionAtEndOfDuration.y() * repeatCount);
270 transform->translate(position.x(), position.y());
271 RotateMode rotateMode = this->rotateMode();
272 if (rotateMode != RotateAuto && rotateMode != RotateAutoReverse)
274 if (rotateMode == RotateAutoReverse)
276 transform->rotate(angle);
279 void SVGAnimateMotionElement::applyResultsToTarget()
281 // We accumulate to the target element transform list so there is not much to do here.
282 SVGElement* targetElement = this->targetElement();
286 if (RenderObject* renderer = targetElement->renderer())
287 RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer);
289 AffineTransform* t = targetElement->supplementalTransform();
293 // ...except in case where we have additional instances in <use> trees.
294 const WillBeHeapHashSet<RawPtrWillBeWeakMember<SVGElement> >& instances = targetElement->instancesForElement();
295 const WillBeHeapHashSet<RawPtrWillBeWeakMember<SVGElement> >::const_iterator end = instances.end();
296 for (WillBeHeapHashSet<RawPtrWillBeWeakMember<SVGElement> >::const_iterator it = instances.begin(); it != end; ++it) {
297 SVGElement* shadowTreeElement = *it;
298 ASSERT(shadowTreeElement);
299 AffineTransform* transform = shadowTreeElement->supplementalTransform();
302 transform->setMatrix(t->a(), t->b(), t->c(), t->d(), t->e(), t->f());
303 if (RenderObject* renderer = shadowTreeElement->renderer()) {
304 renderer->setNeedsTransformUpdate();
305 RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer);
310 float SVGAnimateMotionElement::calculateDistance(const String& fromString, const String& toString)
314 if (!parsePoint(fromString, from))
316 if (!parsePoint(toString, to))
318 FloatSize diff = to - from;
319 return sqrtf(diff.width() * diff.width() + diff.height() * diff.height());
322 void SVGAnimateMotionElement::updateAnimationMode()
324 if (!m_animationPath.isEmpty())
325 setAnimationMode(PathAnimation);
327 SVGAnimationElement::updateAnimationMode();