1 /*
2 Copyright (C) 2004, 2005 Nikolas Zimmermann <wildfox@kde.org>
3 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 file is part of the WebKit project
8
9 This library is free software; you can redistribute it and/or
10 modify it under the terms of the GNU Library General Public
11 License as published by the Free Software Foundation; either
12 version 2 of the License, or (at your option) any later version.
13
14 This library is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 Library General Public License for more details.
18
19 You should have received a copy of the GNU Library General Public License
20 along with this library; see the file COPYING.LIB. If not, write to
21 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
22 Boston, MA 02110-1301, USA.
23 */
24
25 #include "config.h"
26
27 #if ENABLE(SVG) && ENABLE(SVG_ANIMATION)
28 #include "SVGAnimateTransformElement.h"
29
30 #include "MappedAttribute.h"
31 #include "RenderObject.h"
32 #include "SVGAngle.h"
33 #include "SVGElementInstance.h"
34 #include "SVGParserUtilities.h"
35 #include "SVGSVGElement.h"
36 #include "SVGStyledTransformableElement.h"
37 #include "SVGTextElement.h"
38 #include "SVGTransform.h"
39 #include "SVGTransformList.h"
40 #include "SVGUseElement.h"
41 #include "TransformationMatrix.h"
42 #include <math.h>
43 #include <wtf/MathExtras.h>
44
45 using namespace std;
46
47 namespace WebCore {
48
SVGAnimateTransformElement(const QualifiedName & tagName,Document * doc)49 SVGAnimateTransformElement::SVGAnimateTransformElement(const QualifiedName& tagName, Document* doc)
50 : SVGAnimationElement(tagName, doc)
51 , m_type(SVGTransform::SVG_TRANSFORM_UNKNOWN)
52 , m_baseIndexInTransformList(0)
53 {
54 }
55
~SVGAnimateTransformElement()56 SVGAnimateTransformElement::~SVGAnimateTransformElement()
57 {
58 }
59
hasValidTarget() const60 bool SVGAnimateTransformElement::hasValidTarget() const
61 {
62 SVGElement* targetElement = this->targetElement();
63 return SVGAnimationElement::hasValidTarget() && (targetElement->isStyledTransformable() || targetElement->hasTagName(SVGNames::textTag));
64 }
65
parseMappedAttribute(MappedAttribute * attr)66 void SVGAnimateTransformElement::parseMappedAttribute(MappedAttribute* attr)
67 {
68 if (attr->name() == SVGNames::typeAttr) {
69 if (attr->value() == "translate")
70 m_type = SVGTransform::SVG_TRANSFORM_TRANSLATE;
71 else if (attr->value() == "scale")
72 m_type = SVGTransform::SVG_TRANSFORM_SCALE;
73 else if (attr->value() == "rotate")
74 m_type = SVGTransform::SVG_TRANSFORM_ROTATE;
75 else if (attr->value() == "skewX")
76 m_type = SVGTransform::SVG_TRANSFORM_SKEWX;
77 else if (attr->value() == "skewY")
78 m_type = SVGTransform::SVG_TRANSFORM_SKEWY;
79 } else
80 SVGAnimationElement::parseMappedAttribute(attr);
81 }
82
83
transformListFor(SVGElement * element)84 static PassRefPtr<SVGTransformList> transformListFor(SVGElement* element)
85 {
86 ASSERT(element);
87 if (element->isStyledTransformable())
88 return static_cast<SVGStyledTransformableElement*>(element)->transform();
89 if (element->hasTagName(SVGNames::textTag))
90 return static_cast<SVGTextElement*>(element)->transform();
91 return 0;
92 }
93
resetToBaseValue(const String & baseValue)94 void SVGAnimateTransformElement::resetToBaseValue(const String& baseValue)
95 {
96 if (!hasValidTarget())
97 return;
98 if (baseValue.isEmpty()) {
99 ExceptionCode ec;
100 RefPtr<SVGTransformList> list = transformListFor(targetElement());
101 list->clear(ec);
102 } else
103 targetElement()->setAttribute(SVGNames::transformAttr, baseValue);
104 }
105
calculateAnimatedValue(float percentage,unsigned repeat,SVGSMILElement * resultElement)106 void SVGAnimateTransformElement::calculateAnimatedValue(float percentage, unsigned repeat, SVGSMILElement* resultElement)
107 {
108 if (!hasValidTarget())
109 return;
110 SVGElement* targetElement = resultElement->targetElement();
111 RefPtr<SVGTransformList> transformList = transformListFor(targetElement);
112 ASSERT(transformList);
113
114 ExceptionCode ec;
115 if (!isAdditive())
116 transformList->clear(ec);
117 if (isAccumulated() && repeat) {
118 SVGTransform accumulatedTransform = SVGTransformDistance(m_fromTransform, m_toTransform).scaledDistance(repeat).addToSVGTransform(SVGTransform());
119 transformList->appendItem(accumulatedTransform, ec);
120 }
121 SVGTransform transform = SVGTransformDistance(m_fromTransform, m_toTransform).scaledDistance(percentage).addToSVGTransform(m_fromTransform);
122 transformList->appendItem(transform, ec);
123 }
124
calculateFromAndToValues(const String & fromString,const String & toString)125 bool SVGAnimateTransformElement::calculateFromAndToValues(const String& fromString, const String& toString)
126 {
127 m_fromTransform = parseTransformValue(fromString);
128 if (!m_fromTransform.isValid())
129 return false;
130 m_toTransform = parseTransformValue(toString);
131 return m_toTransform.isValid();
132 }
133
calculateFromAndByValues(const String & fromString,const String & byString)134 bool SVGAnimateTransformElement::calculateFromAndByValues(const String& fromString, const String& byString)
135 {
136
137 m_fromTransform = parseTransformValue(fromString);
138 if (!m_fromTransform.isValid())
139 return false;
140 m_toTransform = SVGTransformDistance::addSVGTransforms(m_fromTransform, parseTransformValue(byString));
141 return m_toTransform.isValid();
142 }
143
parseTransformValue(const String & value) const144 SVGTransform SVGAnimateTransformElement::parseTransformValue(const String& value) const
145 {
146 if (value.isEmpty())
147 return SVGTransform(m_type);
148 SVGTransform result;
149 // FIXME: This is pretty dumb but parseTransformValue() wants those parenthesis.
150 String parseString("(" + value + ")");
151 const UChar* ptr = parseString.characters();
152 SVGTransformable::parseTransformValue(m_type, ptr, ptr + parseString.length(), result); // ignoring return value
153 return result;
154 }
155
applyResultsToTarget()156 void SVGAnimateTransformElement::applyResultsToTarget()
157 {
158 if (!hasValidTarget())
159 return;
160 // We accumulate to the target element transform list so there is not much to do here.
161 SVGElement* targetElement = this->targetElement();
162 if (targetElement->renderer())
163 targetElement->renderer()->setNeedsLayout(true);
164
165 // ...except in case where we have additional instances in <use> trees.
166 HashSet<SVGElementInstance*> instances = targetElement->instancesForElement();
167 RefPtr<SVGTransformList> transformList = transformListFor(targetElement);
168 HashSet<SVGElementInstance*>::iterator end = instances.end();
169 for (HashSet<SVGElementInstance*>::iterator it = instances.begin(); it != end; ++it) {
170 SVGElement* shadowTreeElement = (*it)->shadowTreeElement();
171 ASSERT(shadowTreeElement);
172 if (shadowTreeElement->isStyledTransformable())
173 static_cast<SVGStyledTransformableElement*>(shadowTreeElement)->setTransform(transformList.get());
174 else if (shadowTreeElement->hasTagName(SVGNames::textTag))
175 static_cast<SVGTextElement*>(shadowTreeElement)->setTransform(transformList.get());
176 if (shadowTreeElement->renderer())
177 shadowTreeElement->renderer()->setNeedsLayout(true);
178 }
179 }
180
calculateDistance(const String & fromString,const String & toString)181 float SVGAnimateTransformElement::calculateDistance(const String& fromString, const String& toString)
182 {
183 // FIXME: This is not correct in all cases. The spec demands that each component (translate x and y for example)
184 // is paced separately. To implement this we need to treat each component as individual animation everywhere.
185 SVGTransform from = parseTransformValue(fromString);
186 if (!from.isValid())
187 return -1.f;
188 SVGTransform to = parseTransformValue(toString);
189 if (!to.isValid() || from.type() != to.type())
190 return -1.f;
191 if (to.type() == SVGTransform::SVG_TRANSFORM_TRANSLATE) {
192 FloatSize diff = to.translate() - from.translate();
193 return sqrtf(diff.width() * diff.width() + diff.height() * diff.height());
194 }
195 if (to.type() == SVGTransform::SVG_TRANSFORM_ROTATE)
196 return fabsf(to.angle() - from.angle());
197 if (to.type() == SVGTransform::SVG_TRANSFORM_SCALE) {
198 FloatSize diff = to.scale() - from.scale();
199 return sqrtf(diff.width() * diff.width() + diff.height() * diff.height());
200 }
201 return -1.f;
202 }
203
204 }
205
206 // vim:ts=4:noet
207 #endif // ENABLE(SVG)
208
209