1 /*
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.
5 *
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.
10 *
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.
15 *
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.
20 */
21
22 #include "config.h"
23
24 #if ENABLE(SVG) && ENABLE(SVG_ANIMATION)
25 #include "SVGAnimateMotionElement.h"
26
27 #include "Attribute.h"
28 #include "RenderObject.h"
29 #include "RenderSVGResource.h"
30 #include "SVGElementInstance.h"
31 #include "SVGMPathElement.h"
32 #include "SVGNames.h"
33 #include "SVGParserUtilities.h"
34 #include "SVGPathElement.h"
35 #include "SVGPathParserFactory.h"
36 #include "SVGTransformList.h"
37 #include <wtf/MathExtras.h>
38 #include <wtf/StdLibExtras.h>
39
40 namespace WebCore {
41
42 using namespace SVGNames;
43
SVGAnimateMotionElement(const QualifiedName & tagName,Document * document)44 inline SVGAnimateMotionElement::SVGAnimateMotionElement(const QualifiedName& tagName, Document* document)
45 : SVGAnimationElement(tagName, document)
46 , m_baseIndexInTransformList(0)
47 , m_angle(0)
48 {
49 }
50
create(const QualifiedName & tagName,Document * document)51 PassRefPtr<SVGAnimateMotionElement> SVGAnimateMotionElement::create(const QualifiedName& tagName, Document* document)
52 {
53 return adoptRef(new SVGAnimateMotionElement(tagName, document));
54 }
55
hasValidAttributeType() const56 bool SVGAnimateMotionElement::hasValidAttributeType() const
57 {
58 SVGElement* targetElement = this->targetElement();
59 if (!targetElement)
60 return false;
61
62 // We don't have a special attribute name to verify the animation type. Check the element name instead.
63 if (!targetElement->isStyledTransformable() && !targetElement->hasTagName(SVGNames::textTag))
64 return false;
65 // Spec: SVG 1.1 section 19.2.15
66 // FIXME: svgTag is missing. Needs to be checked, if transforming <svg> could cause problems.
67 if (targetElement->hasTagName(gTag)
68 || targetElement->hasTagName(defsTag)
69 || targetElement->hasTagName(useTag)
70 || targetElement->hasTagName(imageTag)
71 || targetElement->hasTagName(switchTag)
72 || targetElement->hasTagName(pathTag)
73 || targetElement->hasTagName(rectTag)
74 || targetElement->hasTagName(circleTag)
75 || targetElement->hasTagName(ellipseTag)
76 || targetElement->hasTagName(lineTag)
77 || targetElement->hasTagName(polylineTag)
78 || targetElement->hasTagName(polygonTag)
79 || targetElement->hasTagName(textTag)
80 || targetElement->hasTagName(clipPathTag)
81 || targetElement->hasTagName(maskTag)
82 || targetElement->hasTagName(aTag)
83 #if ENABLE(SVG_FOREIGN_OBJECT)
84 || targetElement->hasTagName(foreignObjectTag)
85 #endif
86 )
87 return true;
88 return false;
89 }
90
parseMappedAttribute(Attribute * attr)91 void SVGAnimateMotionElement::parseMappedAttribute(Attribute* attr)
92 {
93 if (attr->name() == SVGNames::pathAttr) {
94 m_path = Path();
95 SVGPathParserFactory* factory = SVGPathParserFactory::self();
96 factory->buildPathFromString(attr->value(), m_path);
97 } else
98 SVGAnimationElement::parseMappedAttribute(attr);
99 }
100
rotateMode() const101 SVGAnimateMotionElement::RotateMode SVGAnimateMotionElement::rotateMode() const
102 {
103 DEFINE_STATIC_LOCAL(const AtomicString, autoVal, ("auto"));
104 DEFINE_STATIC_LOCAL(const AtomicString, autoReverse, ("auto-reverse"));
105 String rotate = getAttribute(SVGNames::rotateAttr);
106 if (rotate == autoVal)
107 return RotateAuto;
108 if (rotate == autoReverse)
109 return RotateAutoReverse;
110 return RotateAngle;
111 }
112
animationPath() const113 Path SVGAnimateMotionElement::animationPath() const
114 {
115 for (Node* child = firstChild(); child; child = child->nextSibling()) {
116 if (child->hasTagName(SVGNames::mpathTag)) {
117 SVGMPathElement* mPath = static_cast<SVGMPathElement*>(child);
118 SVGPathElement* pathElement = mPath->pathElement();
119 Path path;
120 if (pathElement)
121 pathElement->toPathData(path);
122 return path;
123 }
124 }
125 if (hasAttribute(SVGNames::pathAttr))
126 return m_path;
127 return Path();
128 }
129
parsePoint(const String & s,FloatPoint & point)130 static bool parsePoint(const String& s, FloatPoint& point)
131 {
132 if (s.isEmpty())
133 return false;
134 const UChar* cur = s.characters();
135 const UChar* end = cur + s.length();
136
137 if (!skipOptionalSpaces(cur, end))
138 return false;
139
140 float x = 0;
141 if (!parseNumber(cur, end, x))
142 return false;
143
144 float y = 0;
145 if (!parseNumber(cur, end, y))
146 return false;
147
148 point = FloatPoint(x, y);
149
150 // disallow anything except spaces at the end
151 return !skipOptionalSpaces(cur, end);
152 }
153
resetToBaseValue(const String &)154 void SVGAnimateMotionElement::resetToBaseValue(const String&)
155 {
156 if (!hasValidAttributeType())
157 return;
158 AffineTransform* transform = targetElement()->supplementalTransform();
159 if (!transform)
160 return;
161 transform->makeIdentity();
162 }
163
calculateFromAndToValues(const String & fromString,const String & toString)164 bool SVGAnimateMotionElement::calculateFromAndToValues(const String& fromString, const String& toString)
165 {
166 parsePoint(fromString, m_fromPoint);
167 parsePoint(toString, m_toPoint);
168 return true;
169 }
170
calculateFromAndByValues(const String & fromString,const String & byString)171 bool SVGAnimateMotionElement::calculateFromAndByValues(const String& fromString, const String& byString)
172 {
173 parsePoint(fromString, m_fromPoint);
174 FloatPoint byPoint;
175 parsePoint(byString, byPoint);
176 m_toPoint = FloatPoint(m_fromPoint.x() + byPoint.x(), m_fromPoint.y() + byPoint.y());
177 return true;
178 }
179
calculateAnimatedValue(float percentage,unsigned,SVGSMILElement *)180 void SVGAnimateMotionElement::calculateAnimatedValue(float percentage, unsigned, SVGSMILElement*)
181 {
182 SVGElement* targetElement = this->targetElement();
183 if (!targetElement)
184 return;
185 AffineTransform* transform = targetElement->supplementalTransform();
186 if (!transform)
187 return;
188
189 if (RenderObject* targetRenderer = targetElement->renderer())
190 targetRenderer->setNeedsTransformUpdate();
191
192 if (!isAdditive())
193 transform->makeIdentity();
194
195 // FIXME: Implement accumulate.
196
197 if (animationMode() == PathAnimation) {
198 ASSERT(!animationPath().isEmpty());
199 Path path = animationPath();
200 float positionOnPath = path.length() * percentage;
201 bool ok;
202 FloatPoint position = path.pointAtLength(positionOnPath, ok);
203 if (ok) {
204 transform->translate(position.x(), position.y());
205 RotateMode rotateMode = this->rotateMode();
206 if (rotateMode == RotateAuto || rotateMode == RotateAutoReverse) {
207 float angle = path.normalAngleAtLength(positionOnPath, ok);
208 if (rotateMode == RotateAutoReverse)
209 angle += 180;
210 transform->rotate(angle);
211 }
212 }
213 return;
214 }
215 FloatSize diff = m_toPoint - m_fromPoint;
216 transform->translate(diff.width() * percentage + m_fromPoint.x(), diff.height() * percentage + m_fromPoint.y());
217 }
218
applyResultsToTarget()219 void SVGAnimateMotionElement::applyResultsToTarget()
220 {
221 // We accumulate to the target element transform list so there is not much to do here.
222 SVGElement* targetElement = this->targetElement();
223 if (!targetElement)
224 return;
225
226 if (RenderObject* renderer = targetElement->renderer())
227 RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer);
228
229 AffineTransform* t = targetElement->supplementalTransform();
230 if (!t)
231 return;
232
233 // ...except in case where we have additional instances in <use> trees.
234 const HashSet<SVGElementInstance*>& instances = targetElement->instancesForElement();
235 const HashSet<SVGElementInstance*>::const_iterator end = instances.end();
236 for (HashSet<SVGElementInstance*>::const_iterator it = instances.begin(); it != end; ++it) {
237 SVGElement* shadowTreeElement = (*it)->shadowTreeElement();
238 ASSERT(shadowTreeElement);
239 AffineTransform* transform = shadowTreeElement->supplementalTransform();
240 if (!transform)
241 continue;
242 transform->setMatrix(t->a(), t->b(), t->c(), t->d(), t->e(), t->f());
243 if (RenderObject* renderer = shadowTreeElement->renderer()) {
244 renderer->setNeedsTransformUpdate();
245 RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer);
246 }
247 }
248 }
249
calculateDistance(const String & fromString,const String & toString)250 float SVGAnimateMotionElement::calculateDistance(const String& fromString, const String& toString)
251 {
252 FloatPoint from;
253 FloatPoint to;
254 if (!parsePoint(fromString, from))
255 return -1;
256 if (!parsePoint(toString, to))
257 return -1;
258 FloatSize diff = to - from;
259 return sqrtf(diff.width() * diff.width() + diff.height() * diff.height());
260 }
261
262 }
263 #endif // ENABLE(SVG)
264