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.
25
#include "SVGAnimateMotionElement.h"
27
#include "Attribute.h"
28
#include "RenderObject.h"
29
#include "RenderSVGResource.h"
30
#include "SVGElementInstance.h"
31
#include "SVGMPathElement.h"
33
#include "SVGParserUtilities.h"
34
#include "SVGPathData.h"
35
#include "SVGPathElement.h"
36
#include "SVGPathUtilities.h"
37
#include "SVGTransformList.h"
38
#include <wtf/MathExtras.h>
39
#include <wtf/StdLibExtras.h>
43
using namespace SVGNames;
45
inline SVGAnimateMotionElement::SVGAnimateMotionElement(const QualifiedName& tagName, Document* document)
46
: SVGAnimationElement(tagName, document)
47
, m_hasToPointAtEndOfDuration(false)
49
setCalcMode(CalcModePaced);
50
ASSERT(hasTagName(animateMotionTag));
53
PassRefPtr<SVGAnimateMotionElement> SVGAnimateMotionElement::create(const QualifiedName& tagName, Document* document)
55
return adoptRef(new SVGAnimateMotionElement(tagName, document));
58
bool SVGAnimateMotionElement::hasValidAttributeType()
60
SVGElement* targetElement = this->targetElement();
64
// We don't have a special attribute name to verify the animation type. Check the element name instead.
65
if (!targetElement->isStyledTransformable() && !targetElement->hasTagName(SVGNames::textTag))
67
// Spec: SVG 1.1 section 19.2.15
68
// FIXME: svgTag is missing. Needs to be checked, if transforming <svg> could cause problems.
69
if (targetElement->hasTagName(gTag)
70
|| targetElement->hasTagName(defsTag)
71
|| targetElement->hasTagName(useTag)
72
|| targetElement->hasTagName(imageTag)
73
|| targetElement->hasTagName(switchTag)
74
|| targetElement->hasTagName(pathTag)
75
|| targetElement->hasTagName(rectTag)
76
|| targetElement->hasTagName(circleTag)
77
|| targetElement->hasTagName(ellipseTag)
78
|| targetElement->hasTagName(lineTag)
79
|| targetElement->hasTagName(polylineTag)
80
|| targetElement->hasTagName(polygonTag)
81
|| targetElement->hasTagName(textTag)
82
|| targetElement->hasTagName(clipPathTag)
83
|| targetElement->hasTagName(maskTag)
84
|| targetElement->hasTagName(aTag)
85
|| targetElement->hasTagName(foreignObjectTag)
91
bool SVGAnimateMotionElement::hasValidAttributeName()
93
// AnimateMotion does not use attributeName so it is always valid.
97
bool SVGAnimateMotionElement::isSupportedAttribute(const QualifiedName& attrName)
99
DEFINE_STATIC_LOCAL(HashSet<QualifiedName>, supportedAttributes, ());
100
if (supportedAttributes.isEmpty())
101
supportedAttributes.add(SVGNames::pathAttr);
102
return supportedAttributes.contains<QualifiedName, SVGAttributeHashTranslator>(attrName);
105
void SVGAnimateMotionElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
107
if (!isSupportedAttribute(name)) {
108
SVGAnimationElement::parseAttribute(name, value);
112
if (name == SVGNames::pathAttr) {
114
buildPathFromString(value, m_path);
115
updateAnimationPath();
119
ASSERT_NOT_REACHED();
122
SVGAnimateMotionElement::RotateMode SVGAnimateMotionElement::rotateMode() const
124
DEFINE_STATIC_LOCAL(const AtomicString, autoVal, ("auto", AtomicString::ConstructFromLiteral));
125
DEFINE_STATIC_LOCAL(const AtomicString, autoReverse, ("auto-reverse", AtomicString::ConstructFromLiteral));
126
const AtomicString& rotate = getAttribute(SVGNames::rotateAttr);
127
if (rotate == autoVal)
129
if (rotate == autoReverse)
130
return RotateAutoReverse;
134
void SVGAnimateMotionElement::updateAnimationPath()
136
m_animationPath = Path();
137
bool foundMPath = false;
139
for (Node* child = firstChild(); child; child = child->nextSibling()) {
140
if (child->hasTagName(SVGNames::mpathTag)) {
141
SVGMPathElement* mPath = static_cast<SVGMPathElement*>(child);
142
SVGPathElement* pathElement = mPath->pathElement();
144
updatePathFromGraphicsElement(pathElement, m_animationPath);
151
if (!foundMPath && fastHasAttribute(SVGNames::pathAttr))
152
m_animationPath = m_path;
154
updateAnimationMode();
157
static bool parsePoint(const String& s, FloatPoint& point)
161
const UChar* cur = s.characters();
162
const UChar* end = cur + s.length();
164
if (!skipOptionalSVGSpaces(cur, end))
168
if (!parseNumber(cur, end, x))
172
if (!parseNumber(cur, end, y))
175
point = FloatPoint(x, y);
177
// disallow anything except spaces at the end
178
return !skipOptionalSVGSpaces(cur, end);
181
void SVGAnimateMotionElement::resetAnimatedType()
183
if (!hasValidAttributeType())
185
SVGElement* targetElement = this->targetElement();
188
if (AffineTransform* transform = targetElement->supplementalTransform())
189
transform->makeIdentity();
192
void SVGAnimateMotionElement::clearAnimatedType(SVGElement* targetElement)
196
if (AffineTransform* transform = targetElement->supplementalTransform())
197
transform->makeIdentity();
200
bool SVGAnimateMotionElement::calculateToAtEndOfDurationValue(const String& toAtEndOfDurationString)
202
parsePoint(toAtEndOfDurationString, m_toPointAtEndOfDuration);
203
m_hasToPointAtEndOfDuration = true;
207
bool SVGAnimateMotionElement::calculateFromAndToValues(const String& fromString, const String& toString)
209
m_hasToPointAtEndOfDuration = false;
210
parsePoint(fromString, m_fromPoint);
211
parsePoint(toString, m_toPoint);
215
bool SVGAnimateMotionElement::calculateFromAndByValues(const String& fromString, const String& byString)
217
m_hasToPointAtEndOfDuration = false;
218
if (animationMode() == ByAnimation && !isAdditive())
220
parsePoint(fromString, m_fromPoint);
222
parsePoint(byString, byPoint);
223
m_toPoint = FloatPoint(m_fromPoint.x() + byPoint.x(), m_fromPoint.y() + byPoint.y());
227
void SVGAnimateMotionElement::buildTransformForProgress(AffineTransform* transform, float percentage)
229
ASSERT(!m_animationPath.isEmpty());
232
float positionOnPath = m_animationPath.length() * percentage;
233
FloatPoint position = m_animationPath.pointAtLength(positionOnPath, ok);
236
transform->translate(position.x(), position.y());
237
RotateMode rotateMode = this->rotateMode();
238
if (rotateMode != RotateAuto && rotateMode != RotateAutoReverse)
240
float angle = m_animationPath.normalAngleAtLength(positionOnPath, ok);
241
if (rotateMode == RotateAutoReverse)
243
transform->rotate(angle);
246
void SVGAnimateMotionElement::calculateAnimatedValue(float percentage, unsigned repeatCount, SVGSMILElement*)
248
SVGElement* targetElement = this->targetElement();
251
AffineTransform* transform = targetElement->supplementalTransform();
255
if (RenderObject* targetRenderer = targetElement->renderer())
256
targetRenderer->setNeedsTransformUpdate();
259
transform->makeIdentity();
261
if (animationMode() != PathAnimation) {
262
FloatPoint toPointAtEndOfDuration = m_toPoint;
263
if (isAccumulated() && repeatCount && m_hasToPointAtEndOfDuration)
264
toPointAtEndOfDuration = m_toPointAtEndOfDuration;
267
animateAdditiveNumber(percentage, repeatCount, m_fromPoint.x(), m_toPoint.x(), toPointAtEndOfDuration.x(), animatedX);
270
animateAdditiveNumber(percentage, repeatCount, m_fromPoint.y(), m_toPoint.y(), toPointAtEndOfDuration.y(), animatedY);
272
transform->translate(animatedX, animatedY);
276
buildTransformForProgress(transform, percentage);
278
// Handle accumulate="sum".
279
if (isAccumulated() && repeatCount) {
280
for (unsigned i = 0; i < repeatCount; ++i)
281
buildTransformForProgress(transform, 1);
285
void SVGAnimateMotionElement::applyResultsToTarget()
287
// We accumulate to the target element transform list so there is not much to do here.
288
SVGElement* targetElement = this->targetElement();
292
if (RenderObject* renderer = targetElement->renderer())
293
RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer);
295
AffineTransform* t = targetElement->supplementalTransform();
299
// ...except in case where we have additional instances in <use> trees.
300
const HashSet<SVGElementInstance*>& instances = targetElement->instancesForElement();
301
const HashSet<SVGElementInstance*>::const_iterator end = instances.end();
302
for (HashSet<SVGElementInstance*>::const_iterator it = instances.begin(); it != end; ++it) {
303
SVGElement* shadowTreeElement = (*it)->shadowTreeElement();
304
ASSERT(shadowTreeElement);
305
AffineTransform* transform = shadowTreeElement->supplementalTransform();
308
transform->setMatrix(t->a(), t->b(), t->c(), t->d(), t->e(), t->f());
309
if (RenderObject* renderer = shadowTreeElement->renderer()) {
310
renderer->setNeedsTransformUpdate();
311
RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer);
316
float SVGAnimateMotionElement::calculateDistance(const String& fromString, const String& toString)
320
if (!parsePoint(fromString, from))
322
if (!parsePoint(toString, to))
324
FloatSize diff = to - from;
325
return sqrtf(diff.width() * diff.width() + diff.height() * diff.height());
328
void SVGAnimateMotionElement::updateAnimationMode()
330
if (!m_animationPath.isEmpty())
331
setAnimationMode(PathAnimation);
333
SVGAnimationElement::updateAnimationMode();
337
#endif // ENABLE(SVG)