fix tricky regression noticed by Vyacheslav Tokarev on Google Reader.
[kdelibs.git] / khtml / svg / SVGAnimateTransformElement.cpp
bloba83e66f26591f06ca94a2708889c23245dbd82c5
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.
7 This file is part of the WebKit project
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.
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.
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.
25 #include "config.h"
26 #if ENABLE(SVG) && ENABLE(SVG_ANIMATION)
27 #include "SVGAnimateTransformElement.h"
29 #include "AffineTransform.h"
30 #include "RenderObject.h"
31 #include "SVGAngle.h"
32 #include "SVGElementInstance.h"
33 #include "SVGParserUtilities.h"
34 #include "SVGSVGElement.h"
35 #include "SVGStyledTransformableElement.h"
36 #include "SVGTextElement.h"
37 #include "SVGTransform.h"
38 #include "SVGTransformList.h"
39 #include "SVGUseElement.h"
41 #include <math.h>
42 #include <wtf/MathExtras.h>
44 using namespace std;
46 namespace WebCore {
48 SVGAnimateTransformElement::SVGAnimateTransformElement(const QualifiedName& tagName, Document* doc)
49 : SVGAnimationElement(tagName, doc)
50 , m_type(SVGTransform::SVG_TRANSFORM_UNKNOWN)
51 , m_baseIndexInTransformList(0)
55 SVGAnimateTransformElement::~SVGAnimateTransformElement()
59 bool SVGAnimateTransformElement::hasValidTarget() const
61 SVGElement* targetElement = this->targetElement();
62 return SVGAnimationElement::hasValidTarget() && (targetElement->isStyledTransformable() || targetElement->hasTagName(SVGNames::textTag));
65 void SVGAnimateTransformElement::parseMappedAttribute(MappedAttribute* attr)
67 if (attr->name() == SVGNames::typeAttr) {
68 if (attr->value() == "translate")
69 m_type = SVGTransform::SVG_TRANSFORM_TRANSLATE;
70 else if (attr->value() == "scale")
71 m_type = SVGTransform::SVG_TRANSFORM_SCALE;
72 else if (attr->value() == "rotate")
73 m_type = SVGTransform::SVG_TRANSFORM_ROTATE;
74 else if (attr->value() == "skewX")
75 m_type = SVGTransform::SVG_TRANSFORM_SKEWX;
76 else if (attr->value() == "skewY")
77 m_type = SVGTransform::SVG_TRANSFORM_SKEWY;
78 } else
79 SVGAnimationElement::parseMappedAttribute(attr);
83 static PassRefPtr<SVGTransformList> transformListFor(SVGElement* element)
85 ASSERT(element);
86 if (element->isStyledTransformable())
87 return static_cast<SVGStyledTransformableElement*>(element)->transform();
88 if (element->hasTagName(SVGNames::textTag))
89 return static_cast<SVGTextElement*>(element)->transform();
90 return 0;
93 void SVGAnimateTransformElement::resetToBaseValue(const String& baseValue)
95 if (!hasValidTarget())
96 return;
97 if (baseValue.isEmpty()) {
98 ExceptionCode ec;
99 RefPtr<SVGTransformList> list = transformListFor(targetElement());
100 list->clear(ec);
101 } else
102 targetElement()->setAttribute(SVGNames::transformAttr, baseValue);
105 void SVGAnimateTransformElement::calculateAnimatedValue(float percentage, unsigned repeat, SVGSMILElement* resultElement)
107 if (!hasValidTarget())
108 return;
109 SVGElement* targetElement = resultElement->targetElement();
110 RefPtr<SVGTransformList> transformList = transformListFor(targetElement);
111 ASSERT(transformList);
113 ExceptionCode ec;
114 if (!isAdditive())
115 transformList->clear(ec);
116 if (isAccumulated() && repeat) {
117 SVGTransform accumulatedTransform = SVGTransformDistance(m_fromTransform, m_toTransform).scaledDistance(repeat).addToSVGTransform(SVGTransform());
118 transformList->appendItem(accumulatedTransform, ec);
120 SVGTransform transform = SVGTransformDistance(m_fromTransform, m_toTransform).scaledDistance(percentage).addToSVGTransform(m_fromTransform);
121 transformList->appendItem(transform, ec);
124 bool SVGAnimateTransformElement::calculateFromAndToValues(const String& fromString, const String& toString)
126 m_fromTransform = parseTransformValue(fromString);
127 if (!m_fromTransform.isValid())
128 return false;
129 m_toTransform = parseTransformValue(toString);
130 return m_toTransform.isValid();
133 bool SVGAnimateTransformElement::calculateFromAndByValues(const String& fromString, const String& byString)
136 m_fromTransform = parseTransformValue(fromString);
137 if (!m_fromTransform.isValid())
138 return false;
139 m_toTransform = SVGTransformDistance::addSVGTransforms(m_fromTransform, parseTransformValue(byString));
140 return m_toTransform.isValid();
143 SVGTransform SVGAnimateTransformElement::parseTransformValue(const String& value) const
145 if (value.isEmpty())
146 return SVGTransform(m_type);
147 SVGTransform result;
148 // FIXME: This is pretty dumb but parseTransformValue() wants those parenthesis.
149 String parseString("(" + value + ")");
150 const UChar* ptr = parseString.characters();
151 SVGTransformable::parseTransformValue(m_type, ptr, ptr + parseString.length(), result); // ignoring return value
152 return result;
155 void SVGAnimateTransformElement::applyResultsToTarget()
157 if (!hasValidTarget())
158 return;
159 // We accumulate to the target element transform list so there is not much to do here.
160 SVGElement* targetElement = this->targetElement();
161 if (targetElement->renderer())
162 targetElement->renderer()->setNeedsLayout(true);
164 // ...except in case where we have additional instances in <use> trees.
165 HashSet<SVGElementInstance*>* instances = document()->accessSVGExtensions()->instancesForElement(targetElement);
166 if (!instances)
167 return;
168 RefPtr<SVGTransformList> transformList = transformListFor(targetElement);
169 HashSet<SVGElementInstance*>::iterator end = instances->end();
170 for (HashSet<SVGElementInstance*>::iterator it = instances->begin(); it != end; ++it) {
171 SVGElement* shadowTreeElement = (*it)->shadowTreeElement();
172 ASSERT(shadowTreeElement);
173 if (shadowTreeElement->isStyledTransformable())
174 static_cast<SVGStyledTransformableElement*>(shadowTreeElement)->setTransform(transformList.get());
175 else if (shadowTreeElement->hasTagName(SVGNames::textTag))
176 static_cast<SVGTextElement*>(shadowTreeElement)->setTransform(transformList.get());
177 if (shadowTreeElement->renderer())
178 shadowTreeElement->renderer()->setNeedsLayout(true);
182 float SVGAnimateTransformElement::calculateDistance(const String& fromString, const String& toString)
184 // FIXME: This is not correct in all cases. The spec demands that each component (translate x and y for example)
185 // is paced separately. To implement this we need to treat each component as individual animation everywhere.
186 SVGTransform from = parseTransformValue(fromString);
187 if (!from.isValid())
188 return -1.f;
189 SVGTransform to = parseTransformValue(toString);
190 if (!to.isValid() || from.type() != to.type())
191 return -1.f;
192 if (to.type() == SVGTransform::SVG_TRANSFORM_TRANSLATE) {
193 FloatSize diff = to.translate() - from.translate();
194 return sqrtf(diff.width() * diff.width() + diff.height() * diff.height());
196 if (to.type() == SVGTransform::SVG_TRANSFORM_ROTATE)
197 return fabsf(to.angle() - from.angle());
198 if (to.type() == SVGTransform::SVG_TRANSFORM_SCALE) {
199 FloatSize diff = to.scale() - from.scale();
200 return sqrtf(diff.width() * diff.width() + diff.height() * diff.height());
202 return -1.f;
207 // vim:ts=4:noet
208 #endif // ENABLE(SVG)