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 "core/SVGNames.h" 27#include "core/dom/ElementTraversal.h" 28#include "core/rendering/RenderObject.h" 29#include "core/rendering/svg/RenderSVGResource.h" 30#include "core/rendering/svg/SVGPathData.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 blink { 40 41using namespace SVGNames; 42 43inline SVGAnimateMotionElement::SVGAnimateMotionElement(Document& document) 44 : SVGAnimationElement(animateMotionTag, document) 45 , m_hasToPointAtEndOfDuration(false) 46{ 47 setCalcMode(CalcModePaced); 48} 49 50DEFINE_NODE_FACTORY(SVGAnimateMotionElement) 51 52SVGAnimateMotionElement::~SVGAnimateMotionElement() 53{ 54} 55 56bool SVGAnimateMotionElement::hasValidAttributeType() 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->isSVGGraphicsElement()) 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 return (isSVGGElement(*targetElement) 68 || isSVGDefsElement(*targetElement) 69 || isSVGUseElement(*targetElement) 70 || isSVGImageElement(*targetElement) 71 || isSVGSwitchElement(*targetElement) 72 || isSVGPathElement(*targetElement) 73 || isSVGRectElement(*targetElement) 74 || isSVGCircleElement(*targetElement) 75 || isSVGEllipseElement(*targetElement) 76 || isSVGLineElement(*targetElement) 77 || isSVGPolylineElement(*targetElement) 78 || isSVGPolygonElement(*targetElement) 79 || isSVGTextElement(*targetElement) 80 || isSVGClipPathElement(*targetElement) 81 || isSVGMaskElement(*targetElement) 82 || isSVGAElement(*targetElement) 83 || isSVGForeignObjectElement(*targetElement) 84 ); 85} 86 87bool SVGAnimateMotionElement::hasValidAttributeName() 88{ 89 // AnimateMotion does not use attributeName so it is always valid. 90 return true; 91} 92 93void SVGAnimateMotionElement::parseAttribute(const QualifiedName& name, const AtomicString& value) 94{ 95 if (name == SVGNames::pathAttr) { 96 m_path = Path(); 97 buildPathFromString(value, m_path); 98 updateAnimationPath(); 99 return; 100 } 101 102 SVGAnimationElement::parseAttribute(name, value); 103} 104 105SVGAnimateMotionElement::RotateMode SVGAnimateMotionElement::rotateMode() const 106{ 107 DEFINE_STATIC_LOCAL(const AtomicString, autoVal, ("auto", AtomicString::ConstructFromLiteral)); 108 DEFINE_STATIC_LOCAL(const AtomicString, autoReverse, ("auto-reverse", AtomicString::ConstructFromLiteral)); 109 const AtomicString& rotate = getAttribute(SVGNames::rotateAttr); 110 if (rotate == autoVal) 111 return RotateAuto; 112 if (rotate == autoReverse) 113 return RotateAutoReverse; 114 return RotateAngle; 115} 116 117void SVGAnimateMotionElement::updateAnimationPath() 118{ 119 m_animationPath = Path(); 120 bool foundMPath = false; 121 122 for (SVGMPathElement* mpath = Traversal<SVGMPathElement>::firstChild(*this); mpath; mpath = Traversal<SVGMPathElement>::nextSibling(*mpath)) { 123 if (SVGPathElement* pathElement = mpath->pathElement()) { 124 updatePathFromGraphicsElement(pathElement, m_animationPath); 125 foundMPath = true; 126 break; 127 } 128 } 129 130 if (!foundMPath && fastHasAttribute(SVGNames::pathAttr)) 131 m_animationPath = m_path; 132 133 updateAnimationMode(); 134} 135 136template<typename CharType> 137static bool parsePointInternal(const String& string, FloatPoint& point) 138{ 139 const CharType* ptr = string.getCharacters<CharType>(); 140 const CharType* end = ptr + string.length(); 141 142 if (!skipOptionalSVGSpaces(ptr, end)) 143 return false; 144 145 float x = 0; 146 if (!parseNumber(ptr, end, x)) 147 return false; 148 149 float y = 0; 150 if (!parseNumber(ptr, end, y)) 151 return false; 152 153 point = FloatPoint(x, y); 154 155 // disallow anything except spaces at the end 156 return !skipOptionalSVGSpaces(ptr, end); 157} 158 159static bool parsePoint(const String& string, FloatPoint& point) 160{ 161 if (string.isEmpty()) 162 return false; 163 if (string.is8Bit()) 164 return parsePointInternal<LChar>(string, point); 165 return parsePointInternal<UChar>(string, point); 166} 167 168void SVGAnimateMotionElement::resetAnimatedType() 169{ 170 if (!hasValidAttributeType()) 171 return; 172 SVGElement* targetElement = this->targetElement(); 173 if (!targetElement) 174 return; 175 if (AffineTransform* transform = targetElement->supplementalTransform()) 176 transform->makeIdentity(); 177} 178 179void SVGAnimateMotionElement::clearAnimatedType(SVGElement* targetElement) 180{ 181 if (!targetElement) 182 return; 183 184 AffineTransform* transform = targetElement->supplementalTransform(); 185 if (!transform) 186 return; 187 188 transform->makeIdentity(); 189 190 if (RenderObject* targetRenderer = targetElement->renderer()) { 191 targetRenderer->setNeedsTransformUpdate(); 192 RenderSVGResource::markForLayoutAndParentResourceInvalidation(targetRenderer); 193 } 194} 195 196bool SVGAnimateMotionElement::calculateToAtEndOfDurationValue(const String& toAtEndOfDurationString) 197{ 198 parsePoint(toAtEndOfDurationString, m_toPointAtEndOfDuration); 199 m_hasToPointAtEndOfDuration = true; 200 return true; 201} 202 203bool SVGAnimateMotionElement::calculateFromAndToValues(const String& fromString, const String& toString) 204{ 205 m_hasToPointAtEndOfDuration = false; 206 parsePoint(fromString, m_fromPoint); 207 parsePoint(toString, m_toPoint); 208 return true; 209} 210 211bool SVGAnimateMotionElement::calculateFromAndByValues(const String& fromString, const String& byString) 212{ 213 m_hasToPointAtEndOfDuration = false; 214 if (animationMode() == ByAnimation && !isAdditive()) 215 return false; 216 parsePoint(fromString, m_fromPoint); 217 FloatPoint byPoint; 218 parsePoint(byString, byPoint); 219 m_toPoint = FloatPoint(m_fromPoint.x() + byPoint.x(), m_fromPoint.y() + byPoint.y()); 220 return true; 221} 222 223void SVGAnimateMotionElement::calculateAnimatedValue(float percentage, unsigned repeatCount, SVGSMILElement*) 224{ 225 SVGElement* targetElement = this->targetElement(); 226 if (!targetElement) 227 return; 228 AffineTransform* transform = targetElement->supplementalTransform(); 229 if (!transform) 230 return; 231 232 if (RenderObject* targetRenderer = targetElement->renderer()) 233 targetRenderer->setNeedsTransformUpdate(); 234 235 if (!isAdditive()) 236 transform->makeIdentity(); 237 238 if (animationMode() != PathAnimation) { 239 FloatPoint toPointAtEndOfDuration = m_toPoint; 240 if (isAccumulated() && repeatCount && m_hasToPointAtEndOfDuration) 241 toPointAtEndOfDuration = m_toPointAtEndOfDuration; 242 243 float animatedX = 0; 244 animateAdditiveNumber(percentage, repeatCount, m_fromPoint.x(), m_toPoint.x(), toPointAtEndOfDuration.x(), animatedX); 245 246 float animatedY = 0; 247 animateAdditiveNumber(percentage, repeatCount, m_fromPoint.y(), m_toPoint.y(), toPointAtEndOfDuration.y(), animatedY); 248 249 transform->translate(animatedX, animatedY); 250 return; 251 } 252 253 ASSERT(!m_animationPath.isEmpty()); 254 255 float positionOnPath = m_animationPath.length() * percentage; 256 FloatPoint position; 257 float angle; 258 bool ok = m_animationPath.pointAndNormalAtLength(positionOnPath, position, angle); 259 if (!ok) 260 return; 261 262 // Handle accumulate="sum". 263 if (isAccumulated() && repeatCount) { 264 FloatPoint positionAtEndOfDuration = m_animationPath.pointAtLength(m_animationPath.length(), ok); 265 if (ok) 266 position.move(positionAtEndOfDuration.x() * repeatCount, positionAtEndOfDuration.y() * repeatCount); 267 } 268 269 transform->translate(position.x(), position.y()); 270 RotateMode rotateMode = this->rotateMode(); 271 if (rotateMode != RotateAuto && rotateMode != RotateAutoReverse) 272 return; 273 if (rotateMode == RotateAutoReverse) 274 angle += 180; 275 transform->rotate(angle); 276} 277 278void SVGAnimateMotionElement::applyResultsToTarget() 279{ 280 // We accumulate to the target element transform list so there is not much to do here. 281 SVGElement* targetElement = this->targetElement(); 282 if (!targetElement) 283 return; 284 285 if (RenderObject* renderer = targetElement->renderer()) 286 RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer); 287 288 AffineTransform* t = targetElement->supplementalTransform(); 289 if (!t) 290 return; 291 292 // ...except in case where we have additional instances in <use> trees. 293 const WillBeHeapHashSet<RawPtrWillBeWeakMember<SVGElement> >& instances = targetElement->instancesForElement(); 294 const WillBeHeapHashSet<RawPtrWillBeWeakMember<SVGElement> >::const_iterator end = instances.end(); 295 for (WillBeHeapHashSet<RawPtrWillBeWeakMember<SVGElement> >::const_iterator it = instances.begin(); it != end; ++it) { 296 SVGElement* shadowTreeElement = *it; 297 ASSERT(shadowTreeElement); 298 AffineTransform* transform = shadowTreeElement->supplementalTransform(); 299 if (!transform) 300 continue; 301 transform->setMatrix(t->a(), t->b(), t->c(), t->d(), t->e(), t->f()); 302 if (RenderObject* renderer = shadowTreeElement->renderer()) { 303 renderer->setNeedsTransformUpdate(); 304 RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer); 305 } 306 } 307} 308 309float SVGAnimateMotionElement::calculateDistance(const String& fromString, const String& toString) 310{ 311 FloatPoint from; 312 FloatPoint to; 313 if (!parsePoint(fromString, from)) 314 return -1; 315 if (!parsePoint(toString, to)) 316 return -1; 317 FloatSize diff = to - from; 318 return sqrtf(diff.width() * diff.width() + diff.height() * diff.height()); 319} 320 321void SVGAnimateMotionElement::updateAnimationMode() 322{ 323 if (!m_animationPath.isEmpty()) 324 setAnimationMode(PathAnimation); 325 else 326 SVGAnimationElement::updateAnimationMode(); 327} 328 329} 330