• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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