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