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.
23 #include "core/svg/SVGAnimateMotionElement.h"
25 #include "core/SVGNames.h"
26 #include "core/dom/ElementTraversal.h"
27 #include "core/rendering/RenderObject.h"
28 #include "core/rendering/svg/SVGPathData.h"
29 #include "core/svg/SVGMPathElement.h"
30 #include "core/svg/SVGParserUtilities.h"
31 #include "core/svg/SVGPathElement.h"
32 #include "core/svg/SVGPathUtilities.h"
33 #include "platform/transforms/AffineTransform.h"
34 #include "wtf/MathExtras.h"
35 #include "wtf/StdLibExtras.h"
39 using namespace SVGNames;
41 inline SVGAnimateMotionElement::SVGAnimateMotionElement(Document& document)
42 : SVGAnimationElement(animateMotionTag, document)
43 , m_hasToPointAtEndOfDuration(false)
45 setCalcMode(CalcModePaced);
48 DEFINE_NODE_FACTORY(SVGAnimateMotionElement)
50 SVGAnimateMotionElement::~SVGAnimateMotionElement()
54 bool SVGAnimateMotionElement::hasValidAttributeType()
56 SVGElement* targetElement = this->targetElement();
60 // We don't have a special attribute name to verify the animation type. Check the element name instead.
61 if (!targetElement->isSVGGraphicsElement())
63 // Spec: SVG 1.1 section 19.2.15
64 // FIXME: svgTag is missing. Needs to be checked, if transforming <svg> could cause problems.
65 return (isSVGGElement(*targetElement)
66 || isSVGDefsElement(*targetElement)
67 || isSVGUseElement(*targetElement)
68 || isSVGImageElement(*targetElement)
69 || isSVGSwitchElement(*targetElement)
70 || isSVGPathElement(*targetElement)
71 || isSVGRectElement(*targetElement)
72 || isSVGCircleElement(*targetElement)
73 || isSVGEllipseElement(*targetElement)
74 || isSVGLineElement(*targetElement)
75 || isSVGPolylineElement(*targetElement)
76 || isSVGPolygonElement(*targetElement)
77 || isSVGTextElement(*targetElement)
78 || isSVGClipPathElement(*targetElement)
79 || isSVGMaskElement(*targetElement)
80 || isSVGAElement(*targetElement)
81 || isSVGForeignObjectElement(*targetElement)
85 bool SVGAnimateMotionElement::hasValidAttributeName()
87 // AnimateMotion does not use attributeName so it is always valid.
91 void SVGAnimateMotionElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
93 if (name == SVGNames::pathAttr) {
95 buildPathFromString(value, m_path);
96 updateAnimationPath();
100 SVGAnimationElement::parseAttribute(name, value);
103 SVGAnimateMotionElement::RotateMode SVGAnimateMotionElement::rotateMode() const
105 DEFINE_STATIC_LOCAL(const AtomicString, autoVal, ("auto", AtomicString::ConstructFromLiteral));
106 DEFINE_STATIC_LOCAL(const AtomicString, autoReverse, ("auto-reverse", AtomicString::ConstructFromLiteral));
107 const AtomicString& rotate = getAttribute(SVGNames::rotateAttr);
108 if (rotate == autoVal)
110 if (rotate == autoReverse)
111 return RotateAutoReverse;
115 void SVGAnimateMotionElement::updateAnimationPath()
117 m_animationPath = Path();
118 bool foundMPath = false;
120 for (SVGMPathElement* mpath = Traversal<SVGMPathElement>::firstChild(*this); mpath; mpath = Traversal<SVGMPathElement>::nextSibling(*mpath)) {
121 if (SVGPathElement* pathElement = mpath->pathElement()) {
122 updatePathFromGraphicsElement(pathElement, m_animationPath);
128 if (!foundMPath && fastHasAttribute(SVGNames::pathAttr))
129 m_animationPath = m_path;
131 updateAnimationMode();
134 template<typename CharType>
135 static bool parsePointInternal(const String& string, FloatPoint& point)
137 const CharType* ptr = string.getCharacters<CharType>();
138 const CharType* end = ptr + string.length();
140 if (!skipOptionalSVGSpaces(ptr, end))
144 if (!parseNumber(ptr, end, x))
148 if (!parseNumber(ptr, end, y))
151 point = FloatPoint(x, y);
153 // disallow anything except spaces at the end
154 return !skipOptionalSVGSpaces(ptr, end);
157 static bool parsePoint(const String& string, FloatPoint& point)
159 if (string.isEmpty())
162 return parsePointInternal<LChar>(string, point);
163 return parsePointInternal<UChar>(string, point);
166 void SVGAnimateMotionElement::resetAnimatedType()
168 if (!hasValidAttributeType())
170 SVGElement* targetElement = this->targetElement();
173 if (AffineTransform* transform = targetElement->animateMotionTransform())
174 transform->makeIdentity();
177 void SVGAnimateMotionElement::clearAnimatedType(SVGElement* targetElement)
182 AffineTransform* transform = targetElement->animateMotionTransform();
186 transform->makeIdentity();
188 if (RenderObject* targetRenderer = targetElement->renderer()) {
189 targetRenderer->setNeedsTransformUpdate();
190 markForLayoutAndParentResourceInvalidation(targetRenderer);
194 bool SVGAnimateMotionElement::calculateToAtEndOfDurationValue(const String& toAtEndOfDurationString)
196 parsePoint(toAtEndOfDurationString, m_toPointAtEndOfDuration);
197 m_hasToPointAtEndOfDuration = true;
201 bool SVGAnimateMotionElement::calculateFromAndToValues(const String& fromString, const String& toString)
203 m_hasToPointAtEndOfDuration = false;
204 parsePoint(fromString, m_fromPoint);
205 parsePoint(toString, m_toPoint);
209 bool SVGAnimateMotionElement::calculateFromAndByValues(const String& fromString, const String& byString)
211 m_hasToPointAtEndOfDuration = false;
212 if (animationMode() == ByAnimation && !isAdditive())
214 parsePoint(fromString, m_fromPoint);
216 parsePoint(byString, byPoint);
217 m_toPoint = FloatPoint(m_fromPoint.x() + byPoint.x(), m_fromPoint.y() + byPoint.y());
221 void SVGAnimateMotionElement::calculateAnimatedValue(float percentage, unsigned repeatCount, SVGSMILElement*)
223 SVGElement* targetElement = this->targetElement();
226 AffineTransform* transform = targetElement->animateMotionTransform();
230 if (RenderObject* targetRenderer = targetElement->renderer())
231 targetRenderer->setNeedsTransformUpdate();
234 transform->makeIdentity();
236 if (animationMode() != PathAnimation) {
237 FloatPoint toPointAtEndOfDuration = m_toPoint;
238 if (isAccumulated() && repeatCount && m_hasToPointAtEndOfDuration)
239 toPointAtEndOfDuration = m_toPointAtEndOfDuration;
242 animateAdditiveNumber(percentage, repeatCount, m_fromPoint.x(), m_toPoint.x(), toPointAtEndOfDuration.x(), animatedX);
245 animateAdditiveNumber(percentage, repeatCount, m_fromPoint.y(), m_toPoint.y(), toPointAtEndOfDuration.y(), animatedY);
247 transform->translate(animatedX, animatedY);
251 ASSERT(!m_animationPath.isEmpty());
253 float positionOnPath = m_animationPath.length() * percentage;
256 bool ok = m_animationPath.pointAndNormalAtLength(positionOnPath, position, angle);
260 // Handle accumulate="sum".
261 if (isAccumulated() && repeatCount) {
262 FloatPoint positionAtEndOfDuration = m_animationPath.pointAtLength(m_animationPath.length(), ok);
264 position.move(positionAtEndOfDuration.x() * repeatCount, positionAtEndOfDuration.y() * repeatCount);
267 transform->translate(position.x(), position.y());
268 RotateMode rotateMode = this->rotateMode();
269 if (rotateMode != RotateAuto && rotateMode != RotateAutoReverse)
271 if (rotateMode == RotateAutoReverse)
273 transform->rotate(angle);
276 void SVGAnimateMotionElement::applyResultsToTarget()
278 // We accumulate to the target element transform list so there is not much to do here.
279 SVGElement* targetElement = this->targetElement();
283 if (RenderObject* renderer = targetElement->renderer())
284 markForLayoutAndParentResourceInvalidation(renderer);
286 AffineTransform* t = targetElement->animateMotionTransform();
290 // ...except in case where we have additional instances in <use> trees.
291 const WillBeHeapHashSet<RawPtrWillBeWeakMember<SVGElement> >& instances = targetElement->instancesForElement();
292 const WillBeHeapHashSet<RawPtrWillBeWeakMember<SVGElement> >::const_iterator end = instances.end();
293 for (WillBeHeapHashSet<RawPtrWillBeWeakMember<SVGElement> >::const_iterator it = instances.begin(); it != end; ++it) {
294 SVGElement* shadowTreeElement = *it;
295 ASSERT(shadowTreeElement);
296 AffineTransform* transform = shadowTreeElement->animateMotionTransform();
299 transform->setMatrix(t->a(), t->b(), t->c(), t->d(), t->e(), t->f());
300 if (RenderObject* renderer = shadowTreeElement->renderer()) {
301 renderer->setNeedsTransformUpdate();
302 markForLayoutAndParentResourceInvalidation(renderer);
307 float SVGAnimateMotionElement::calculateDistance(const String& fromString, const String& toString)
311 if (!parsePoint(fromString, from))
313 if (!parsePoint(toString, to))
315 FloatSize diff = to - from;
316 return sqrtf(diff.width() * diff.width() + diff.height() * diff.height());
319 void SVGAnimateMotionElement::updateAnimationMode()
321 if (!m_animationPath.isEmpty())
322 setAnimationMode(PathAnimation);
324 SVGAnimationElement::updateAnimationMode();