1 /* 2 Copyright (C) 2004, 2005 Nikolas Zimmermann <wildfox (at) kde.org> 3 2004, 2005, 2006, 2007 Rob Buis <buis (at) kde.org> 4 Copyright (C) 2007 Eric Seidel <eric (at) 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 "AffineTransform.h" 31 #include "MappedAttribute.h" 32 #include "RenderObject.h" 33 #include "SVGAngle.h" 34 #include "SVGElementInstance.h" 35 #include "SVGParserUtilities.h" 36 #include "SVGSVGElement.h" 37 #include "SVGStyledTransformableElement.h" 38 #include "SVGTextElement.h" 39 #include "SVGTransform.h" 40 #include "SVGTransformList.h" 41 #include "SVGUseElement.h" 42 #include <math.h> 43 #include <wtf/MathExtras.h> 44 45 using namespace std; 46 47 namespace WebCore { 48 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 56 SVGAnimateTransformElement::~SVGAnimateTransformElement() 57 { 58 } 59 60 bool SVGAnimateTransformElement::hasValidTarget() const 61 { 62 SVGElement* targetElement = this->targetElement(); 63 return SVGAnimationElement::hasValidTarget() && (targetElement->isStyledTransformable() || targetElement->hasTagName(SVGNames::textTag)); 64 } 65 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 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 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 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 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 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 144 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 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 const HashSet<SVGElementInstance*>& instances = targetElement->instancesForElement(); 167 RefPtr<SVGTransformList> transformList = transformListFor(targetElement); 168 const HashSet<SVGElementInstance*>::const_iterator end = instances.end(); 169 for (HashSet<SVGElementInstance*>::const_iterator it = instances.begin(); it != end; ++it) { 170 SVGElement* shadowTreeElement = (*it)->shadowTreeElement(); 171 ASSERT(shadowTreeElement); 172 if (shadowTreeElement->isStyledTransformable()) 173 static_cast<SVGStyledTransformableElement*>(shadowTreeElement)->setTransformBaseValue(transformList.get()); 174 else if (shadowTreeElement->hasTagName(SVGNames::textTag)) 175 static_cast<SVGTextElement*>(shadowTreeElement)->setTransformBaseValue(transformList.get()); 176 if (shadowTreeElement->renderer()) 177 shadowTreeElement->renderer()->setNeedsLayout(true); 178 } 179 } 180 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