1/* 2 * Copyright (C) 2007 Eric Seidel <eric@webkit.org> 3 * Copyright (C) 2007 Rob Buis <buis@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#include "core/svg/SVGAnimateMotionElement.h" 25 26#include "SVGNames.h" 27#include "core/rendering/RenderObject.h" 28#include "core/rendering/svg/RenderSVGResource.h" 29#include "core/rendering/svg/SVGPathData.h" 30#include "core/svg/SVGElementInstance.h" 31#include "core/svg/SVGMPathElement.h" 32#include "core/svg/SVGParserUtilities.h" 33#include "core/svg/SVGPathElement.h" 34#include "core/svg/SVGPathUtilities.h" 35#include "platform/transforms/AffineTransform.h" 36#include "wtf/MathExtras.h" 37#include "wtf/StdLibExtras.h" 38 39namespace WebCore { 40 41using namespace SVGNames; 42 43inline SVGAnimateMotionElement::SVGAnimateMotionElement(Document& document) 44 : SVGAnimationElement(animateMotionTag, document) 45 , m_hasToPointAtEndOfDuration(false) 46{ 47 setCalcMode(CalcModePaced); 48 ScriptWrappable::init(this); 49} 50 51SVGAnimateMotionElement::~SVGAnimateMotionElement() 52{ 53 if (targetElement()) 54 clearAnimatedType(targetElement()); 55} 56 57PassRefPtr<SVGAnimateMotionElement> SVGAnimateMotionElement::create(Document& document) 58{ 59 return adoptRef(new SVGAnimateMotionElement(document)); 60} 61 62bool SVGAnimateMotionElement::hasValidAttributeType() 63{ 64 SVGElement* targetElement = this->targetElement(); 65 if (!targetElement) 66 return false; 67 68 // We don't have a special attribute name to verify the animation type. Check the element name instead. 69 if (!targetElement->isSVGGraphicsElement()) 70 return false; 71 // Spec: SVG 1.1 section 19.2.15 72 // FIXME: svgTag is missing. Needs to be checked, if transforming <svg> could cause problems. 73 if (targetElement->hasTagName(gTag) 74 || targetElement->hasTagName(defsTag) 75 || targetElement->hasTagName(useTag) 76 || targetElement->hasTagName(SVGNames::imageTag) 77 || targetElement->hasTagName(switchTag) 78 || targetElement->hasTagName(pathTag) 79 || targetElement->hasTagName(rectTag) 80 || targetElement->hasTagName(circleTag) 81 || targetElement->hasTagName(ellipseTag) 82 || targetElement->hasTagName(lineTag) 83 || targetElement->hasTagName(polylineTag) 84 || targetElement->hasTagName(polygonTag) 85 || targetElement->hasTagName(textTag) 86 || targetElement->hasTagName(clipPathTag) 87 || targetElement->hasTagName(maskTag) 88 || targetElement->hasTagName(SVGNames::aTag) 89 || targetElement->hasTagName(foreignObjectTag) 90 ) 91 return true; 92 return false; 93} 94 95bool SVGAnimateMotionElement::hasValidAttributeName() 96{ 97 // AnimateMotion does not use attributeName so it is always valid. 98 return true; 99} 100 101bool SVGAnimateMotionElement::isSupportedAttribute(const QualifiedName& attrName) 102{ 103 DEFINE_STATIC_LOCAL(HashSet<QualifiedName>, supportedAttributes, ()); 104 if (supportedAttributes.isEmpty()) 105 supportedAttributes.add(SVGNames::pathAttr); 106 return supportedAttributes.contains<SVGAttributeHashTranslator>(attrName); 107} 108 109void SVGAnimateMotionElement::parseAttribute(const QualifiedName& name, const AtomicString& value) 110{ 111 if (!isSupportedAttribute(name)) { 112 SVGAnimationElement::parseAttribute(name, value); 113 return; 114 } 115 116 if (name == SVGNames::pathAttr) { 117 m_path = Path(); 118 buildPathFromString(value, m_path); 119 updateAnimationPath(); 120 return; 121 } 122 123 ASSERT_NOT_REACHED(); 124} 125 126SVGAnimateMotionElement::RotateMode SVGAnimateMotionElement::rotateMode() const 127{ 128 DEFINE_STATIC_LOCAL(const AtomicString, autoVal, ("auto", AtomicString::ConstructFromLiteral)); 129 DEFINE_STATIC_LOCAL(const AtomicString, autoReverse, ("auto-reverse", AtomicString::ConstructFromLiteral)); 130 const AtomicString& rotate = getAttribute(SVGNames::rotateAttr); 131 if (rotate == autoVal) 132 return RotateAuto; 133 if (rotate == autoReverse) 134 return RotateAutoReverse; 135 return RotateAngle; 136} 137 138void SVGAnimateMotionElement::updateAnimationPath() 139{ 140 m_animationPath = Path(); 141 bool foundMPath = false; 142 143 for (Node* child = firstChild(); child; child = child->nextSibling()) { 144 if (child->hasTagName(SVGNames::mpathTag)) { 145 SVGMPathElement* mPath = toSVGMPathElement(child); 146 SVGPathElement* pathElement = mPath->pathElement(); 147 if (pathElement) { 148 updatePathFromGraphicsElement(pathElement, m_animationPath); 149 foundMPath = true; 150 break; 151 } 152 } 153 } 154 155 if (!foundMPath && fastHasAttribute(SVGNames::pathAttr)) 156 m_animationPath = m_path; 157 158 updateAnimationMode(); 159} 160 161template<typename CharType> 162static bool parsePointInternal(const String& string, FloatPoint& point) 163{ 164 const CharType* ptr = string.getCharacters<CharType>(); 165 const CharType* end = ptr + string.length(); 166 167 if (!skipOptionalSVGSpaces(ptr, end)) 168 return false; 169 170 float x = 0; 171 if (!parseNumber(ptr, end, x)) 172 return false; 173 174 float y = 0; 175 if (!parseNumber(ptr, end, y)) 176 return false; 177 178 point = FloatPoint(x, y); 179 180 // disallow anything except spaces at the end 181 return !skipOptionalSVGSpaces(ptr, end); 182} 183 184static bool parsePoint(const String& string, FloatPoint& point) 185{ 186 if (string.isEmpty()) 187 return false; 188 if (string.is8Bit()) 189 return parsePointInternal<LChar>(string, point); 190 return parsePointInternal<UChar>(string, point); 191} 192 193void SVGAnimateMotionElement::resetAnimatedType() 194{ 195 if (!hasValidAttributeType()) 196 return; 197 SVGElement* targetElement = this->targetElement(); 198 if (!targetElement) 199 return; 200 if (AffineTransform* transform = targetElement->supplementalTransform()) 201 transform->makeIdentity(); 202} 203 204void SVGAnimateMotionElement::clearAnimatedType(SVGElement* targetElement) 205{ 206 if (!targetElement) 207 return; 208 209 AffineTransform* transform = targetElement->supplementalTransform(); 210 if (!transform) 211 return; 212 213 transform->makeIdentity(); 214 215 if (RenderObject* targetRenderer = targetElement->renderer()) { 216 targetRenderer->setNeedsTransformUpdate(); 217 RenderSVGResource::markForLayoutAndParentResourceInvalidation(targetRenderer); 218 } 219} 220 221bool SVGAnimateMotionElement::calculateToAtEndOfDurationValue(const String& toAtEndOfDurationString) 222{ 223 parsePoint(toAtEndOfDurationString, m_toPointAtEndOfDuration); 224 m_hasToPointAtEndOfDuration = true; 225 return true; 226} 227 228bool SVGAnimateMotionElement::calculateFromAndToValues(const String& fromString, const String& toString) 229{ 230 m_hasToPointAtEndOfDuration = false; 231 parsePoint(fromString, m_fromPoint); 232 parsePoint(toString, m_toPoint); 233 return true; 234} 235 236bool SVGAnimateMotionElement::calculateFromAndByValues(const String& fromString, const String& byString) 237{ 238 m_hasToPointAtEndOfDuration = false; 239 if (animationMode() == ByAnimation && !isAdditive()) 240 return false; 241 parsePoint(fromString, m_fromPoint); 242 FloatPoint byPoint; 243 parsePoint(byString, byPoint); 244 m_toPoint = FloatPoint(m_fromPoint.x() + byPoint.x(), m_fromPoint.y() + byPoint.y()); 245 return true; 246} 247 248void SVGAnimateMotionElement::calculateAnimatedValue(float percentage, unsigned repeatCount, SVGSMILElement*) 249{ 250 SVGElement* targetElement = this->targetElement(); 251 if (!targetElement) 252 return; 253 AffineTransform* transform = targetElement->supplementalTransform(); 254 if (!transform) 255 return; 256 257 if (RenderObject* targetRenderer = targetElement->renderer()) 258 targetRenderer->setNeedsTransformUpdate(); 259 260 if (!isAdditive()) 261 transform->makeIdentity(); 262 263 if (animationMode() != PathAnimation) { 264 FloatPoint toPointAtEndOfDuration = m_toPoint; 265 if (isAccumulated() && repeatCount && m_hasToPointAtEndOfDuration) 266 toPointAtEndOfDuration = m_toPointAtEndOfDuration; 267 268 float animatedX = 0; 269 animateAdditiveNumber(percentage, repeatCount, m_fromPoint.x(), m_toPoint.x(), toPointAtEndOfDuration.x(), animatedX); 270 271 float animatedY = 0; 272 animateAdditiveNumber(percentage, repeatCount, m_fromPoint.y(), m_toPoint.y(), toPointAtEndOfDuration.y(), animatedY); 273 274 transform->translate(animatedX, animatedY); 275 return; 276 } 277 278 ASSERT(!m_animationPath.isEmpty()); 279 280 bool ok = false; 281 float positionOnPath = m_animationPath.length() * percentage; 282 FloatPoint position; 283 float angle; 284 ok = m_animationPath.pointAndNormalAtLength(positionOnPath, position, angle); 285 if (!ok) 286 return; 287 288 // Handle accumulate="sum". 289 if (isAccumulated() && repeatCount) { 290 FloatPoint positionAtEndOfDuration = m_animationPath.pointAtLength(m_animationPath.length(), ok); 291 if (ok) 292 position.move(positionAtEndOfDuration.x() * repeatCount, positionAtEndOfDuration.y() * repeatCount); 293 } 294 295 transform->translate(position.x(), position.y()); 296 RotateMode rotateMode = this->rotateMode(); 297 if (rotateMode != RotateAuto && rotateMode != RotateAutoReverse) 298 return; 299 if (rotateMode == RotateAutoReverse) 300 angle += 180; 301 transform->rotate(angle); 302} 303 304void SVGAnimateMotionElement::applyResultsToTarget() 305{ 306 // We accumulate to the target element transform list so there is not much to do here. 307 SVGElement* targetElement = this->targetElement(); 308 if (!targetElement) 309 return; 310 311 if (RenderObject* renderer = targetElement->renderer()) 312 RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer); 313 314 AffineTransform* t = targetElement->supplementalTransform(); 315 if (!t) 316 return; 317 318 // ...except in case where we have additional instances in <use> trees. 319 const HashSet<SVGElementInstance*>& instances = targetElement->instancesForElement(); 320 const HashSet<SVGElementInstance*>::const_iterator end = instances.end(); 321 for (HashSet<SVGElementInstance*>::const_iterator it = instances.begin(); it != end; ++it) { 322 SVGElement* shadowTreeElement = (*it)->shadowTreeElement(); 323 ASSERT(shadowTreeElement); 324 AffineTransform* transform = shadowTreeElement->supplementalTransform(); 325 if (!transform) 326 continue; 327 transform->setMatrix(t->a(), t->b(), t->c(), t->d(), t->e(), t->f()); 328 if (RenderObject* renderer = shadowTreeElement->renderer()) { 329 renderer->setNeedsTransformUpdate(); 330 RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer); 331 } 332 } 333} 334 335float SVGAnimateMotionElement::calculateDistance(const String& fromString, const String& toString) 336{ 337 FloatPoint from; 338 FloatPoint to; 339 if (!parsePoint(fromString, from)) 340 return -1; 341 if (!parsePoint(toString, to)) 342 return -1; 343 FloatSize diff = to - from; 344 return sqrtf(diff.width() * diff.width() + diff.height() * diff.height()); 345} 346 347void SVGAnimateMotionElement::updateAnimationMode() 348{ 349 if (!m_animationPath.isEmpty()) 350 setAnimationMode(PathAnimation); 351 else 352 SVGAnimationElement::updateAnimationMode(); 353} 354 355} 356