1 /* 2 * Copyright (C) 2007 Eric Seidel <eric (at) webkit.org> 3 * Copyright (C) 2007 Rob Buis <buis (at) kde.org> 4 * Copyright (C) 2008 Apple Inc. All rights reserved. 5 * 6 * This library is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU Library General Public 8 * License as published by the Free Software Foundation; either 9 * version 2 of the License, or (at your option) any later version. 10 * 11 * This library is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 * Library General Public License for more details. 15 * 16 * You should have received a copy of the GNU Library General Public License 17 * along with this library; see the file COPYING.LIB. If not, write to 18 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 19 * Boston, MA 02110-1301, USA. 20 */ 21 22 #include "config.h" 23 24 #if ENABLE(SVG) && ENABLE(SVG_ANIMATION) 25 #include "SVGAnimateMotionElement.h" 26 27 #include "Attribute.h" 28 #include "RenderObject.h" 29 #include "RenderSVGResource.h" 30 #include "SVGElementInstance.h" 31 #include "SVGMPathElement.h" 32 #include "SVGNames.h" 33 #include "SVGParserUtilities.h" 34 #include "SVGPathElement.h" 35 #include "SVGPathParserFactory.h" 36 #include "SVGTransformList.h" 37 #include <wtf/MathExtras.h> 38 #include <wtf/StdLibExtras.h> 39 40 namespace WebCore { 41 42 using namespace SVGNames; 43 44 inline SVGAnimateMotionElement::SVGAnimateMotionElement(const QualifiedName& tagName, Document* document) 45 : SVGAnimationElement(tagName, document) 46 , m_baseIndexInTransformList(0) 47 , m_angle(0) 48 { 49 } 50 51 PassRefPtr<SVGAnimateMotionElement> SVGAnimateMotionElement::create(const QualifiedName& tagName, Document* document) 52 { 53 return adoptRef(new SVGAnimateMotionElement(tagName, document)); 54 } 55 56 bool SVGAnimateMotionElement::hasValidAttributeType() const 57 { 58 SVGElement* targetElement = this->targetElement(); 59 if (!targetElement) 60 return false; 61 62 // We don't have a special attribute name to verify the animation type. Check the element name instead. 63 if (!targetElement->isStyledTransformable() && !targetElement->hasTagName(SVGNames::textTag)) 64 return false; 65 // Spec: SVG 1.1 section 19.2.15 66 // FIXME: svgTag is missing. Needs to be checked, if transforming <svg> could cause problems. 67 if (targetElement->hasTagName(gTag) 68 || targetElement->hasTagName(defsTag) 69 || targetElement->hasTagName(useTag) 70 || targetElement->hasTagName(imageTag) 71 || targetElement->hasTagName(switchTag) 72 || targetElement->hasTagName(pathTag) 73 || targetElement->hasTagName(rectTag) 74 || targetElement->hasTagName(circleTag) 75 || targetElement->hasTagName(ellipseTag) 76 || targetElement->hasTagName(lineTag) 77 || targetElement->hasTagName(polylineTag) 78 || targetElement->hasTagName(polygonTag) 79 || targetElement->hasTagName(textTag) 80 || targetElement->hasTagName(clipPathTag) 81 || targetElement->hasTagName(maskTag) 82 || targetElement->hasTagName(aTag) 83 #if ENABLE(SVG_FOREIGN_OBJECT) 84 || targetElement->hasTagName(foreignObjectTag) 85 #endif 86 ) 87 return true; 88 return false; 89 } 90 91 void SVGAnimateMotionElement::parseMappedAttribute(Attribute* attr) 92 { 93 if (attr->name() == SVGNames::pathAttr) { 94 m_path = Path(); 95 SVGPathParserFactory* factory = SVGPathParserFactory::self(); 96 factory->buildPathFromString(attr->value(), m_path); 97 } else 98 SVGAnimationElement::parseMappedAttribute(attr); 99 } 100 101 SVGAnimateMotionElement::RotateMode SVGAnimateMotionElement::rotateMode() const 102 { 103 DEFINE_STATIC_LOCAL(const AtomicString, autoVal, ("auto")); 104 DEFINE_STATIC_LOCAL(const AtomicString, autoReverse, ("auto-reverse")); 105 String rotate = getAttribute(SVGNames::rotateAttr); 106 if (rotate == autoVal) 107 return RotateAuto; 108 if (rotate == autoReverse) 109 return RotateAutoReverse; 110 return RotateAngle; 111 } 112 113 Path SVGAnimateMotionElement::animationPath() const 114 { 115 for (Node* child = firstChild(); child; child = child->nextSibling()) { 116 if (child->hasTagName(SVGNames::mpathTag)) { 117 SVGMPathElement* mPath = static_cast<SVGMPathElement*>(child); 118 SVGPathElement* pathElement = mPath->pathElement(); 119 Path path; 120 if (pathElement) 121 pathElement->toPathData(path); 122 return path; 123 } 124 } 125 if (hasAttribute(SVGNames::pathAttr)) 126 return m_path; 127 return Path(); 128 } 129 130 static bool parsePoint(const String& s, FloatPoint& point) 131 { 132 if (s.isEmpty()) 133 return false; 134 const UChar* cur = s.characters(); 135 const UChar* end = cur + s.length(); 136 137 if (!skipOptionalSpaces(cur, end)) 138 return false; 139 140 float x = 0; 141 if (!parseNumber(cur, end, x)) 142 return false; 143 144 float y = 0; 145 if (!parseNumber(cur, end, y)) 146 return false; 147 148 point = FloatPoint(x, y); 149 150 // disallow anything except spaces at the end 151 return !skipOptionalSpaces(cur, end); 152 } 153 154 void SVGAnimateMotionElement::resetToBaseValue(const String&) 155 { 156 if (!hasValidAttributeType()) 157 return; 158 AffineTransform* transform = targetElement()->supplementalTransform(); 159 if (!transform) 160 return; 161 transform->makeIdentity(); 162 } 163 164 bool SVGAnimateMotionElement::calculateFromAndToValues(const String& fromString, const String& toString) 165 { 166 parsePoint(fromString, m_fromPoint); 167 parsePoint(toString, m_toPoint); 168 return true; 169 } 170 171 bool SVGAnimateMotionElement::calculateFromAndByValues(const String& fromString, const String& byString) 172 { 173 parsePoint(fromString, m_fromPoint); 174 FloatPoint byPoint; 175 parsePoint(byString, byPoint); 176 m_toPoint = FloatPoint(m_fromPoint.x() + byPoint.x(), m_fromPoint.y() + byPoint.y()); 177 return true; 178 } 179 180 void SVGAnimateMotionElement::calculateAnimatedValue(float percentage, unsigned, SVGSMILElement*) 181 { 182 SVGElement* targetElement = this->targetElement(); 183 if (!targetElement) 184 return; 185 AffineTransform* transform = targetElement->supplementalTransform(); 186 if (!transform) 187 return; 188 189 if (RenderObject* targetRenderer = targetElement->renderer()) 190 targetRenderer->setNeedsTransformUpdate(); 191 192 if (!isAdditive()) 193 transform->makeIdentity(); 194 195 // FIXME: Implement accumulate. 196 197 if (animationMode() == PathAnimation) { 198 ASSERT(!animationPath().isEmpty()); 199 Path path = animationPath(); 200 float positionOnPath = path.length() * percentage; 201 bool ok; 202 FloatPoint position = path.pointAtLength(positionOnPath, ok); 203 if (ok) { 204 transform->translate(position.x(), position.y()); 205 RotateMode rotateMode = this->rotateMode(); 206 if (rotateMode == RotateAuto || rotateMode == RotateAutoReverse) { 207 float angle = path.normalAngleAtLength(positionOnPath, ok); 208 if (rotateMode == RotateAutoReverse) 209 angle += 180; 210 transform->rotate(angle); 211 } 212 } 213 return; 214 } 215 FloatSize diff = m_toPoint - m_fromPoint; 216 transform->translate(diff.width() * percentage + m_fromPoint.x(), diff.height() * percentage + m_fromPoint.y()); 217 } 218 219 void SVGAnimateMotionElement::applyResultsToTarget() 220 { 221 // We accumulate to the target element transform list so there is not much to do here. 222 SVGElement* targetElement = this->targetElement(); 223 if (!targetElement) 224 return; 225 226 if (RenderObject* renderer = targetElement->renderer()) 227 RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer); 228 229 AffineTransform* t = targetElement->supplementalTransform(); 230 if (!t) 231 return; 232 233 // ...except in case where we have additional instances in <use> trees. 234 const HashSet<SVGElementInstance*>& instances = targetElement->instancesForElement(); 235 const HashSet<SVGElementInstance*>::const_iterator end = instances.end(); 236 for (HashSet<SVGElementInstance*>::const_iterator it = instances.begin(); it != end; ++it) { 237 SVGElement* shadowTreeElement = (*it)->shadowTreeElement(); 238 ASSERT(shadowTreeElement); 239 AffineTransform* transform = shadowTreeElement->supplementalTransform(); 240 if (!transform) 241 continue; 242 transform->setMatrix(t->a(), t->b(), t->c(), t->d(), t->e(), t->f()); 243 if (RenderObject* renderer = shadowTreeElement->renderer()) { 244 renderer->setNeedsTransformUpdate(); 245 RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer); 246 } 247 } 248 } 249 250 float SVGAnimateMotionElement::calculateDistance(const String& fromString, const String& toString) 251 { 252 FloatPoint from; 253 FloatPoint to; 254 if (!parsePoint(fromString, from)) 255 return -1; 256 if (!parsePoint(toString, to)) 257 return -1; 258 FloatSize diff = to - from; 259 return sqrtf(diff.width() * diff.width() + diff.height() * diff.height()); 260 } 261 262 } 263 #endif // ENABLE(SVG) 264