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"
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"
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 SVGAnimateMotionElement::~SVGAnimateMotionElement()
55 clearAnimatedType(targetElement());
59 PassRefPtr<SVGAnimateMotionElement> SVGAnimateMotionElement::create(Document& document)
61 return adoptRef(new SVGAnimateMotionElement(document));
64 bool SVGAnimateMotionElement::hasValidAttributeType()
66 SVGElement* targetElement = this->targetElement();
70 // We don't have a special attribute name to verify the animation type. Check the element name instead.
71 if (!targetElement->isSVGGraphicsElement())
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)
95 bool SVGAnimateMotionElement::hasValidAttributeName()
97 // AnimateMotion does not use attributeName so it is always valid.
101 bool SVGAnimateMotionElement::isSupportedAttribute(const QualifiedName& attrName)
103 DEFINE_STATIC_LOCAL(HashSet<QualifiedName>, supportedAttributes, ());
104 if (supportedAttributes.isEmpty())
105 supportedAttributes.add(SVGNames::pathAttr);
106 return supportedAttributes.contains<SVGAttributeHashTranslator>(attrName);
109 void SVGAnimateMotionElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
111 if (!isSupportedAttribute(name)) {
112 SVGAnimationElement::parseAttribute(name, value);
116 if (name == SVGNames::pathAttr) {
118 buildPathFromString(value, m_path);
119 updateAnimationPath();
123 ASSERT_NOT_REACHED();
126 SVGAnimateMotionElement::RotateMode SVGAnimateMotionElement::rotateMode() const
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)
133 if (rotate == autoReverse)
134 return RotateAutoReverse;
138 void SVGAnimateMotionElement::updateAnimationPath()
140 m_animationPath = Path();
141 bool foundMPath = false;
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);
151 if (!foundMPath && fastHasAttribute(SVGNames::pathAttr))
152 m_animationPath = m_path;
154 updateAnimationMode();
157 template<typename CharType>
158 static bool parsePointInternal(const String& string, FloatPoint& point)
160 const CharType* ptr = string.getCharacters<CharType>();
161 const CharType* end = ptr + string.length();
163 if (!skipOptionalSVGSpaces(ptr, end))
167 if (!parseNumber(ptr, end, x))
171 if (!parseNumber(ptr, end, y))
174 point = FloatPoint(x, y);
176 // disallow anything except spaces at the end
177 return !skipOptionalSVGSpaces(ptr, end);
180 static bool parsePoint(const String& string, FloatPoint& point)
182 if (string.isEmpty())
185 return parsePointInternal<LChar>(string, point);
186 return parsePointInternal<UChar>(string, point);
189 void SVGAnimateMotionElement::resetAnimatedType()
191 if (!hasValidAttributeType())
193 SVGElement* targetElement = this->targetElement();
196 if (AffineTransform* transform = targetElement->supplementalTransform())
197 transform->makeIdentity();
200 void SVGAnimateMotionElement::clearAnimatedType(SVGElement* targetElement)
205 AffineTransform* transform = targetElement->supplementalTransform();
209 transform->makeIdentity();
211 if (RenderObject* targetRenderer = targetElement->renderer()) {
212 targetRenderer->setNeedsTransformUpdate();
213 RenderSVGResource::markForLayoutAndParentResourceInvalidation(targetRenderer);
217 bool SVGAnimateMotionElement::calculateToAtEndOfDurationValue(const String& toAtEndOfDurationString)
219 parsePoint(toAtEndOfDurationString, m_toPointAtEndOfDuration);
220 m_hasToPointAtEndOfDuration = true;
224 bool SVGAnimateMotionElement::calculateFromAndToValues(const String& fromString, const String& toString)
226 m_hasToPointAtEndOfDuration = false;
227 parsePoint(fromString, m_fromPoint);
228 parsePoint(toString, m_toPoint);
232 bool SVGAnimateMotionElement::calculateFromAndByValues(const String& fromString, const String& byString)
234 m_hasToPointAtEndOfDuration = false;
235 if (animationMode() == ByAnimation && !isAdditive())
237 parsePoint(fromString, m_fromPoint);
239 parsePoint(byString, byPoint);
240 m_toPoint = FloatPoint(m_fromPoint.x() + byPoint.x(), m_fromPoint.y() + byPoint.y());
244 void SVGAnimateMotionElement::calculateAnimatedValue(float percentage, unsigned repeatCount, SVGSMILElement*)
246 SVGElement* targetElement = this->targetElement();
249 AffineTransform* transform = targetElement->supplementalTransform();
253 if (RenderObject* targetRenderer = targetElement->renderer())
254 targetRenderer->setNeedsTransformUpdate();
257 transform->makeIdentity();
259 if (animationMode() != PathAnimation) {
260 FloatPoint toPointAtEndOfDuration = m_toPoint;
261 if (isAccumulated() && repeatCount && m_hasToPointAtEndOfDuration)
262 toPointAtEndOfDuration = m_toPointAtEndOfDuration;
265 animateAdditiveNumber(percentage, repeatCount, m_fromPoint.x(), m_toPoint.x(), toPointAtEndOfDuration.x(), animatedX);
268 animateAdditiveNumber(percentage, repeatCount, m_fromPoint.y(), m_toPoint.y(), toPointAtEndOfDuration.y(), animatedY);
270 transform->translate(animatedX, animatedY);
274 ASSERT(!m_animationPath.isEmpty());
276 float positionOnPath = m_animationPath.length() * percentage;
279 bool ok = m_animationPath.pointAndNormalAtLength(positionOnPath, position, angle);
283 // Handle accumulate="sum".
284 if (isAccumulated() && repeatCount) {
285 FloatPoint positionAtEndOfDuration = m_animationPath.pointAtLength(m_animationPath.length(), ok);
287 position.move(positionAtEndOfDuration.x() * repeatCount, positionAtEndOfDuration.y() * repeatCount);
290 transform->translate(position.x(), position.y());
291 RotateMode rotateMode = this->rotateMode();
292 if (rotateMode != RotateAuto && rotateMode != RotateAutoReverse)
294 if (rotateMode == RotateAutoReverse)
296 transform->rotate(angle);
299 void SVGAnimateMotionElement::applyResultsToTarget()
301 // We accumulate to the target element transform list so there is not much to do here.
302 SVGElement* targetElement = this->targetElement();
306 if (RenderObject* renderer = targetElement->renderer())
307 RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer);
309 AffineTransform* t = targetElement->supplementalTransform();
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();
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);
330 float SVGAnimateMotionElement::calculateDistance(const String& fromString, const String& toString)
334 if (!parsePoint(fromString, from))
336 if (!parsePoint(toString, to))
338 FloatSize diff = to - from;
339 return sqrtf(diff.width() * diff.width() + diff.height() * diff.height());
342 void SVGAnimateMotionElement::updateAnimationMode()
344 if (!m_animationPath.isEmpty())
345 setAnimationMode(PathAnimation);
347 SVGAnimationElement::updateAnimationMode();