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/layout/LayoutObject.h"
28 #include "core/svg/SVGMPathElement.h"
29 #include "core/svg/SVGParserUtilities.h"
30 #include "core/svg/SVGPathElement.h"
31 #include "core/svg/SVGPathUtilities.h"
32 #include "platform/transforms/AffineTransform.h"
33 #include "wtf/MathExtras.h"
34 #include "wtf/StdLibExtras.h"
38 using namespace SVGNames
;
40 inline SVGAnimateMotionElement::SVGAnimateMotionElement(Document
& document
)
41 : SVGAnimationElement(animateMotionTag
, document
)
42 , m_hasToPointAtEndOfDuration(false)
44 setCalcMode(CalcModePaced
);
47 DEFINE_NODE_FACTORY(SVGAnimateMotionElement
)
49 SVGAnimateMotionElement::~SVGAnimateMotionElement()
53 bool SVGAnimateMotionElement::hasValidAttributeType()
55 SVGElement
* targetElement
= this->targetElement();
59 // We don't have a special attribute name to verify the animation type. Check the element name instead.
60 if (!targetElement
->isSVGGraphicsElement())
62 // Spec: SVG 1.1 section 19.2.15
63 // FIXME: svgTag is missing. Needs to be checked, if transforming <svg> could cause problems.
64 return (isSVGGElement(*targetElement
)
65 || isSVGDefsElement(*targetElement
)
66 || isSVGUseElement(*targetElement
)
67 || isSVGImageElement(*targetElement
)
68 || isSVGSwitchElement(*targetElement
)
69 || isSVGPathElement(*targetElement
)
70 || isSVGRectElement(*targetElement
)
71 || isSVGCircleElement(*targetElement
)
72 || isSVGEllipseElement(*targetElement
)
73 || isSVGLineElement(*targetElement
)
74 || isSVGPolylineElement(*targetElement
)
75 || isSVGPolygonElement(*targetElement
)
76 || isSVGTextElement(*targetElement
)
77 || isSVGClipPathElement(*targetElement
)
78 || isSVGMaskElement(*targetElement
)
79 || isSVGAElement(*targetElement
)
80 || isSVGForeignObjectElement(*targetElement
)
84 bool SVGAnimateMotionElement::hasValidAttributeName()
86 // AnimateMotion does not use attributeName so it is always valid.
90 void SVGAnimateMotionElement::parseAttribute(const QualifiedName
& name
, const AtomicString
& value
)
92 if (name
== SVGNames::pathAttr
) {
94 buildPathFromString(value
, m_path
);
95 updateAnimationPath();
99 SVGAnimationElement::parseAttribute(name
, value
);
102 SVGAnimateMotionElement::RotateMode
SVGAnimateMotionElement::rotateMode() const
104 DEFINE_STATIC_LOCAL(const AtomicString
, autoVal
, ("auto", AtomicString::ConstructFromLiteral
));
105 DEFINE_STATIC_LOCAL(const AtomicString
, autoReverse
, ("auto-reverse", AtomicString::ConstructFromLiteral
));
106 const AtomicString
& rotate
= getAttribute(SVGNames::rotateAttr
);
107 if (rotate
== autoVal
)
109 if (rotate
== autoReverse
)
110 return RotateAutoReverse
;
114 void SVGAnimateMotionElement::updateAnimationPath()
116 m_animationPath
= Path();
117 bool foundMPath
= false;
119 for (SVGMPathElement
* mpath
= Traversal
<SVGMPathElement
>::firstChild(*this); mpath
; mpath
= Traversal
<SVGMPathElement
>::nextSibling(*mpath
)) {
120 if (SVGPathElement
* pathElement
= mpath
->pathElement()) {
121 m_animationPath
= pathElement
->asPath();
127 if (!foundMPath
&& fastHasAttribute(SVGNames::pathAttr
))
128 m_animationPath
= m_path
;
130 updateAnimationMode();
133 template<typename CharType
>
134 static bool parsePointInternal(const String
& string
, FloatPoint
& point
)
136 const CharType
* ptr
= string
.getCharacters
<CharType
>();
137 const CharType
* end
= ptr
+ string
.length();
139 if (!skipOptionalSVGSpaces(ptr
, end
))
143 if (!parseNumber(ptr
, end
, x
))
147 if (!parseNumber(ptr
, end
, y
))
150 point
= FloatPoint(x
, y
);
152 // disallow anything except spaces at the end
153 return !skipOptionalSVGSpaces(ptr
, end
);
156 static bool parsePoint(const String
& string
, FloatPoint
& point
)
158 if (string
.isEmpty())
161 return parsePointInternal
<LChar
>(string
, point
);
162 return parsePointInternal
<UChar
>(string
, point
);
165 void SVGAnimateMotionElement::resetAnimatedType()
167 if (!hasValidAttributeType())
169 SVGElement
* targetElement
= this->targetElement();
172 if (AffineTransform
* transform
= targetElement
->animateMotionTransform())
173 transform
->makeIdentity();
176 void SVGAnimateMotionElement::clearAnimatedType()
178 SVGElement
* targetElement
= this->targetElement();
182 AffineTransform
* transform
= targetElement
->animateMotionTransform();
186 transform
->makeIdentity();
188 if (LayoutObject
* targetLayoutObject
= targetElement
->layoutObject()) {
189 targetLayoutObject
->setNeedsTransformUpdate();
190 markForLayoutAndParentResourceInvalidation(targetLayoutObject
);
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 (LayoutObject
* targetLayoutObject
= targetElement
->layoutObject())
231 targetLayoutObject
->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 (LayoutObject
* layoutObject
= targetElement
->layoutObject())
284 markForLayoutAndParentResourceInvalidation(layoutObject
);
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 for (SVGElement
* shadowTreeElement
: instances
) {
293 ASSERT(shadowTreeElement
);
294 AffineTransform
* transform
= shadowTreeElement
->animateMotionTransform();
297 transform
->setMatrix(t
->a(), t
->b(), t
->c(), t
->d(), t
->e(), t
->f());
298 if (LayoutObject
* layoutObject
= shadowTreeElement
->layoutObject()) {
299 layoutObject
->setNeedsTransformUpdate();
300 markForLayoutAndParentResourceInvalidation(layoutObject
);
305 float SVGAnimateMotionElement::calculateDistance(const String
& fromString
, const String
& toString
)
309 if (!parsePoint(fromString
, from
))
311 if (!parsePoint(toString
, to
))
313 FloatSize diff
= to
- from
;
314 return sqrtf(diff
.width() * diff
.width() + diff
.height() * diff
.height());
317 void SVGAnimateMotionElement::updateAnimationMode()
319 if (!m_animationPath
.isEmpty())
320 setAnimationMode(PathAnimation
);
322 SVGAnimationElement::updateAnimationMode();