• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2004, 2005 Nikolas Zimmermann <zimmermann@kde.org>
3  * Copyright (C) 2004, 2005, 2006, 2007 Rob Buis <buis@kde.org>
4  * Copyright (C) 2007 Eric Seidel <eric@webkit.org>
5  * Copyright (C) 2008 Apple Inc. All rights reserved.
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Library General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library General Public License
18  * along with this library; see the file COPYING.LIB.  If not, write to
19  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20  * Boston, MA 02110-1301, USA.
21  */
22 
23 #include "config.h"
24 
25 #if ENABLE(SVG) && ENABLE(SVG_ANIMATION)
26 #include "SVGAnimateTransformElement.h"
27 
28 #include "AffineTransform.h"
29 #include "Attribute.h"
30 #include "RenderObject.h"
31 #include "RenderSVGResource.h"
32 #include "SVGAngle.h"
33 #include "SVGElementInstance.h"
34 #include "SVGGradientElement.h"
35 #include "SVGNames.h"
36 #include "SVGParserUtilities.h"
37 #include "SVGSVGElement.h"
38 #include "SVGStyledTransformableElement.h"
39 #include "SVGTextElement.h"
40 #include "SVGTransform.h"
41 #include "SVGTransformList.h"
42 #include "SVGUseElement.h"
43 #include <math.h>
44 #include <wtf/MathExtras.h>
45 
46 using namespace std;
47 
48 namespace WebCore {
49 
SVGAnimateTransformElement(const QualifiedName & tagName,Document * document)50 inline SVGAnimateTransformElement::SVGAnimateTransformElement(const QualifiedName& tagName, Document* document)
51     : SVGAnimationElement(tagName, document)
52     , m_type(SVGTransform::SVG_TRANSFORM_UNKNOWN)
53     , m_baseIndexInTransformList(0)
54 {
55 }
56 
create(const QualifiedName & tagName,Document * document)57 PassRefPtr<SVGAnimateTransformElement> SVGAnimateTransformElement::create(const QualifiedName& tagName, Document* document)
58 {
59     return adoptRef(new SVGAnimateTransformElement(tagName, document));
60 }
61 
hasValidAttributeType() const62 bool SVGAnimateTransformElement::hasValidAttributeType() const
63 {
64     SVGElement* targetElement = this->targetElement();
65     if (!targetElement)
66         return false;
67 
68     return determineAnimatedAttributeType(targetElement) == AnimatedTransformList;
69 }
70 
determineAnimatedAttributeType(SVGElement * targetElement) const71 AnimatedAttributeType SVGAnimateTransformElement::determineAnimatedAttributeType(SVGElement* targetElement) const
72 {
73     ASSERT(targetElement);
74 
75     // Just transform lists can be animated with <animateTransform>
76     // http://www.w3.org/TR/SVG/animate.html#AnimationAttributesAndProperties
77     if (targetElement->animatedPropertyTypeForAttribute(attributeName()) != AnimatedTransformList)
78         return AnimatedUnknown;
79 
80     return AnimatedTransformList;
81 }
82 
parseMappedAttribute(Attribute * attr)83 void SVGAnimateTransformElement::parseMappedAttribute(Attribute* attr)
84 {
85     if (attr->name() == SVGNames::typeAttr) {
86         if (attr->value() == "translate")
87             m_type = SVGTransform::SVG_TRANSFORM_TRANSLATE;
88         else if (attr->value() == "scale")
89             m_type = SVGTransform::SVG_TRANSFORM_SCALE;
90         else if (attr->value() == "rotate")
91             m_type = SVGTransform::SVG_TRANSFORM_ROTATE;
92         else if (attr->value() == "skewX")
93             m_type = SVGTransform::SVG_TRANSFORM_SKEWX;
94         else if (attr->value() == "skewY")
95             m_type = SVGTransform::SVG_TRANSFORM_SKEWY;
96     } else
97         SVGAnimationElement::parseMappedAttribute(attr);
98 }
99 
100 
transformListFor(SVGElement * element)101 static SVGTransformList* transformListFor(SVGElement* element)
102 {
103     ASSERT(element);
104     if (element->isStyledTransformable())
105         return &static_cast<SVGStyledTransformableElement*>(element)->transform();
106     if (element->hasTagName(SVGNames::textTag))
107         return &static_cast<SVGTextElement*>(element)->transform();
108     if (element->hasTagName(SVGNames::linearGradientTag) || element->hasTagName(SVGNames::radialGradientTag))
109         return &static_cast<SVGGradientElement*>(element)->gradientTransform();
110     // FIXME: Handle patternTransform, which is obviously missing!
111     return 0;
112 }
113 
resetToBaseValue(const String & baseValue)114 void SVGAnimateTransformElement::resetToBaseValue(const String& baseValue)
115 {
116     SVGElement* targetElement = this->targetElement();
117     if (!targetElement || determineAnimatedAttributeType(targetElement) == AnimatedUnknown)
118         return;
119 
120     if (targetElement->hasTagName(SVGNames::linearGradientTag) || targetElement->hasTagName(SVGNames::radialGradientTag)) {
121         targetElement->setAttribute(SVGNames::gradientTransformAttr, baseValue.isEmpty() ? "matrix(1 0 0 1 0 0)" : baseValue);
122         return;
123     }
124 
125     if (baseValue.isEmpty()) {
126         if (SVGTransformList* list = transformListFor(targetElement))
127             list->clear();
128     } else
129         targetElement->setAttribute(SVGNames::transformAttr, baseValue);
130 }
131 
calculateAnimatedValue(float percentage,unsigned repeat,SVGSMILElement *)132 void SVGAnimateTransformElement::calculateAnimatedValue(float percentage, unsigned repeat, SVGSMILElement*)
133 {
134     SVGElement* targetElement = this->targetElement();
135     if (!targetElement || determineAnimatedAttributeType(targetElement) == AnimatedUnknown)
136         return;
137     SVGTransformList* transformList = transformListFor(targetElement);
138     ASSERT(transformList);
139 
140     if (!isAdditive())
141         transformList->clear();
142     if (isAccumulated() && repeat) {
143         SVGTransform accumulatedTransform = SVGTransformDistance(m_fromTransform, m_toTransform).scaledDistance(repeat).addToSVGTransform(SVGTransform());
144         transformList->append(accumulatedTransform);
145     }
146     SVGTransform transform = SVGTransformDistance(m_fromTransform, m_toTransform).scaledDistance(percentage).addToSVGTransform(m_fromTransform);
147     transformList->append(transform);
148 }
149 
calculateFromAndToValues(const String & fromString,const String & toString)150 bool SVGAnimateTransformElement::calculateFromAndToValues(const String& fromString, const String& toString)
151 {
152     m_fromTransform = parseTransformValue(fromString);
153     if (!m_fromTransform.isValid())
154         return false;
155     m_toTransform = parseTransformValue(toString);
156     return m_toTransform.isValid();
157 }
158 
calculateFromAndByValues(const String & fromString,const String & byString)159 bool SVGAnimateTransformElement::calculateFromAndByValues(const String& fromString, const String& byString)
160 {
161     m_fromTransform = parseTransformValue(fromString);
162     if (!m_fromTransform.isValid())
163         return false;
164     m_toTransform = SVGTransformDistance::addSVGTransforms(m_fromTransform, parseTransformValue(byString));
165     return m_toTransform.isValid();
166 }
167 
parseTransformValue(const String & value) const168 SVGTransform SVGAnimateTransformElement::parseTransformValue(const String& value) const
169 {
170     if (value.isEmpty())
171         return SVGTransform(m_type);
172     SVGTransform result;
173     // FIXME: This is pretty dumb but parseTransformValue() wants those parenthesis.
174     String parseString("(" + value + ")");
175     const UChar* ptr = parseString.characters();
176     SVGTransformable::parseTransformValue(m_type, ptr, ptr + parseString.length(), result); // ignoring return value
177     return result;
178 }
179 
applyResultsToTarget()180 void SVGAnimateTransformElement::applyResultsToTarget()
181 {
182     SVGElement* targetElement = this->targetElement();
183     if (!targetElement || determineAnimatedAttributeType(targetElement) == AnimatedUnknown)
184         return;
185 
186     // We accumulate to the target element transform list so there is not much to do here.
187     if (RenderObject* renderer = targetElement->renderer()) {
188         renderer->setNeedsTransformUpdate();
189         RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer);
190     }
191 
192     // ...except in case where we have additional instances in <use> trees.
193     SVGTransformList* transformList = transformListFor(targetElement);
194     if (!transformList)
195         return;
196 
197     const HashSet<SVGElementInstance*>& instances = targetElement->instancesForElement();
198     const HashSet<SVGElementInstance*>::const_iterator end = instances.end();
199     for (HashSet<SVGElementInstance*>::const_iterator it = instances.begin(); it != end; ++it) {
200         SVGElement* shadowTreeElement = (*it)->shadowTreeElement();
201         ASSERT(shadowTreeElement);
202         if (shadowTreeElement->isStyledTransformable())
203             static_cast<SVGStyledTransformableElement*>(shadowTreeElement)->setTransformBaseValue(*transformList);
204         else if (shadowTreeElement->hasTagName(SVGNames::textTag))
205             static_cast<SVGTextElement*>(shadowTreeElement)->setTransformBaseValue(*transformList);
206         else if (shadowTreeElement->hasTagName(SVGNames::linearGradientTag) || shadowTreeElement->hasTagName(SVGNames::radialGradientTag))
207             static_cast<SVGGradientElement*>(shadowTreeElement)->setGradientTransformBaseValue(*transformList);
208         // FIXME: Handle patternTransform, obviously missing!
209         if (RenderObject* renderer = shadowTreeElement->renderer()) {
210             renderer->setNeedsTransformUpdate();
211             RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer);
212         }
213     }
214 }
215 
calculateDistance(const String & fromString,const String & toString)216 float SVGAnimateTransformElement::calculateDistance(const String& fromString, const String& toString)
217 {
218     // FIXME: This is not correct in all cases. The spec demands that each component (translate x and y for example)
219     // is paced separately. To implement this we need to treat each component as individual animation everywhere.
220     SVGTransform from = parseTransformValue(fromString);
221     if (!from.isValid())
222         return -1;
223     SVGTransform to = parseTransformValue(toString);
224     if (!to.isValid() || from.type() != to.type())
225         return -1;
226     if (to.type() == SVGTransform::SVG_TRANSFORM_TRANSLATE) {
227         FloatSize diff = to.translate() - from.translate();
228         return sqrtf(diff.width() * diff.width() + diff.height() * diff.height());
229     }
230     if (to.type() == SVGTransform::SVG_TRANSFORM_ROTATE)
231         return fabsf(to.angle() - from.angle());
232     if (to.type() == SVGTransform::SVG_TRANSFORM_SCALE) {
233         FloatSize diff = to.scale() - from.scale();
234         return sqrtf(diff.width() * diff.width() + diff.height() * diff.height());
235     }
236     return -1;
237 }
238 
239 }
240 #endif // ENABLE(SVG)
241