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 #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 39 namespace WebCore { 40 41 using namespace SVGNames; 42 43 inline SVGAnimateMotionElement::SVGAnimateMotionElement(Document& document) 44 : SVGAnimationElement(animateMotionTag, document) 45 , m_hasToPointAtEndOfDuration(false) 46 { 47 setCalcMode(CalcModePaced); 48 ScriptWrappable::init(this); 49 } 50 51 SVGAnimateMotionElement::~SVGAnimateMotionElement() 52 { 53 if (targetElement()) 54 clearAnimatedType(targetElement()); 55 } 56 57 PassRefPtr<SVGAnimateMotionElement> SVGAnimateMotionElement::create(Document& document) 58 { 59 return adoptRef(new SVGAnimateMotionElement(document)); 60 } 61 62 bool 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 95 bool SVGAnimateMotionElement::hasValidAttributeName() 96 { 97 // AnimateMotion does not use attributeName so it is always valid. 98 return true; 99 } 100 101 bool 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 109 void 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 126 SVGAnimateMotionElement::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 138 void 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 161 template<typename CharType> 162 static 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 184 static 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 193 void 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 204 void 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 221 bool SVGAnimateMotionElement::calculateToAtEndOfDurationValue(const String& toAtEndOfDurationString) 222 { 223 parsePoint(toAtEndOfDurationString, m_toPointAtEndOfDuration); 224 m_hasToPointAtEndOfDuration = true; 225 return true; 226 } 227 228 bool 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 236 bool 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 248 void 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 304 void 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 335 float 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 347 void SVGAnimateMotionElement::updateAnimationMode() 348 { 349 if (!m_animationPath.isEmpty()) 350 setAnimationMode(PathAnimation); 351 else 352 SVGAnimationElement::updateAnimationMode(); 353 } 354 355 } 356