1 /* 2 * Copyright (C) 2004, 2005 Nikolas Zimmermann <zimmermann (at) kde.org> 3 * Copyright (C) 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 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 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 57 PassRefPtr<SVGAnimateTransformElement> SVGAnimateTransformElement::create(const QualifiedName& tagName, Document* document) 58 { 59 return adoptRef(new SVGAnimateTransformElement(tagName, document)); 60 } 61 62 bool SVGAnimateTransformElement::hasValidAttributeType() const 63 { 64 SVGElement* targetElement = this->targetElement(); 65 if (!targetElement) 66 return false; 67 68 return determineAnimatedAttributeType(targetElement) == AnimatedTransformList; 69 } 70 71 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 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 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 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 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 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 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 168 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 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 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