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