File indexing completed on 2024-04-28 15:24:27

0001 /*
0002     Copyright (C) 2007 Eric Seidel <eric@webkit.org>
0003               (C) 2007 Rob Buis <buis@kde.org>
0004     Copyright (C) 2008 Apple Inc. All Rights Reserved.
0005 
0006     This file is part of the WebKit project
0007 
0008     This library is free software; you can redistribute it and/or
0009     modify it under the terms of the GNU Library General Public
0010     License as published by the Free Software Foundation; either
0011     version 2 of the License, or (at your option) any later version.
0012 
0013     This library is distributed in the hope that it will be useful,
0014     but WITHOUT ANY WARRANTY; without even the implied warranty of
0015     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0016     Library General Public License for more details.
0017 
0018     You should have received a copy of the GNU Library General Public License
0019     along with this library; see the file COPYING.LIB.  If not, write to
0020     the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0021     Boston, MA 02110-1301, USA.
0022 */
0023 
0024 #if ENABLE(SVG) && ENABLE(SVG_ANIMATION)
0025 #include "SVGAnimateMotionElement.h"
0026 
0027 #include "RenderObject.h"
0028 #include "SVGElementInstance.h"
0029 #include "SVGMPathElement.h"
0030 #include "SVGParserUtilities.h"
0031 #include "SVGPathElement.h"
0032 #include "SVGTransformList.h"
0033 #include <math.h>
0034 
0035 namespace WebCore
0036 {
0037 
0038 using namespace SVGNames;
0039 
0040 SVGAnimateMotionElement::SVGAnimateMotionElement(const QualifiedName &tagName, Document *doc)
0041     : SVGAnimationElement(tagName, doc)
0042     , m_baseIndexInTransformList(0)
0043     , m_angle(0)
0044 {
0045 }
0046 
0047 SVGAnimateMotionElement::~SVGAnimateMotionElement()
0048 {
0049 }
0050 
0051 bool SVGAnimateMotionElement::hasValidTarget() const
0052 {
0053     if (!SVGAnimationElement::hasValidTarget()) {
0054         return false;
0055     }
0056     SVGElement *targetElement = this->targetElement();
0057     if (!targetElement->isStyledTransformable() && !targetElement->hasTagName(SVGNames::textTag)) {
0058         return false;
0059     }
0060     // Spec: SVG 1.1 section 19.2.15
0061     if (targetElement->hasTagName(gTag)
0062             || targetElement->hasTagName(defsTag)
0063             || targetElement->hasTagName(useTag)
0064             || targetElement->hasTagName(imageTag)
0065             || targetElement->hasTagName(switchTag)
0066             || targetElement->hasTagName(pathTag)
0067             || targetElement->hasTagName(rectTag)
0068             || targetElement->hasTagName(circleTag)
0069             || targetElement->hasTagName(ellipseTag)
0070             || targetElement->hasTagName(lineTag)
0071             || targetElement->hasTagName(polylineTag)
0072             || targetElement->hasTagName(polygonTag)
0073             || targetElement->hasTagName(textTag)
0074             || targetElement->hasTagName(clipPathTag)
0075             || targetElement->hasTagName(maskTag)
0076             || targetElement->hasTagName(aTag)
0077 #if ENABLE(SVG_FOREIGN_OBJECT)
0078             || targetElement->hasTagName(foreignObjectTag)
0079 #endif
0080        ) {
0081         return true;
0082     }
0083     return false;
0084 }
0085 
0086 void SVGAnimateMotionElement::parseMappedAttribute(MappedAttribute *attr)
0087 {
0088     if (attr->name() == SVGNames::pathAttr) {
0089         m_path = Path();
0090         pathFromSVGData(m_path, attr->value());
0091     } else {
0092         SVGAnimationElement::parseMappedAttribute(attr);
0093     }
0094 }
0095 
0096 SVGAnimateMotionElement::RotateMode SVGAnimateMotionElement::rotateMode() const
0097 {
0098     static const AtomicString autoVal("auto");
0099     static const AtomicString autoReverse("auto-reverse");
0100     String rotate = getAttribute(SVGNames::rotateAttr);
0101     if (rotate == autoVal) {
0102         return RotateAuto;
0103     }
0104     if (rotate == autoReverse) {
0105         return RotateAutoReverse;
0106     }
0107     return RotateAngle;
0108 }
0109 
0110 Path SVGAnimateMotionElement::animationPath() const
0111 {
0112     for (Node *child = firstChild(); child; child = child->nextSibling()) {
0113         if (child->hasTagName(SVGNames::mpathTag)) {
0114             SVGMPathElement *mPath = static_cast<SVGMPathElement *>(child);
0115             SVGPathElement *pathElement = mPath->pathElement();
0116             if (pathElement) {
0117                 return pathElement->toPathData();
0118             }
0119             return Path();
0120         }
0121     }
0122     if (hasAttribute(SVGNames::pathAttr)) {
0123         return m_path;
0124     }
0125     return Path();
0126 }
0127 
0128 static bool parsePoint(const String &s, FloatPoint &point)
0129 {
0130     if (s.isEmpty()) {
0131         return false;
0132     }
0133     const UChar *cur = s.characters();
0134     const UChar *end = cur + s.length();
0135 
0136     if (!skipOptionalSpaces(cur, end)) {
0137         return false;
0138     }
0139 
0140     float x = 0.0f;
0141     if (!parseNumber(cur, end, x)) {
0142         return false;
0143     }
0144 
0145     float y = 0.0f;
0146     if (!parseNumber(cur, end, y)) {
0147         return false;
0148     }
0149 
0150     point = FloatPoint(x, y);
0151 
0152     // disallow anything except spaces at the end
0153     return !skipOptionalSpaces(cur, end);
0154 }
0155 
0156 void SVGAnimateMotionElement::resetToBaseValue(const String &)
0157 {
0158     if (!hasValidTarget()) {
0159         return;
0160     }
0161     SVGElement *target = targetElement();
0162     AffineTransform *transform = target->supplementalTransform();
0163     if (!transform) {
0164         return;
0165     }
0166     transform->reset();
0167 }
0168 
0169 bool SVGAnimateMotionElement::calculateFromAndToValues(const String &fromString, const String &toString)
0170 {
0171     parsePoint(fromString, m_fromPoint);
0172     parsePoint(toString, m_toPoint);
0173     return true;
0174 }
0175 
0176 bool SVGAnimateMotionElement::calculateFromAndByValues(const String &fromString, const String &byString)
0177 {
0178     parsePoint(fromString, m_fromPoint);
0179     FloatPoint byPoint;
0180     parsePoint(byString, byPoint);
0181     m_toPoint = FloatPoint(m_fromPoint.x() + byPoint.x(), m_fromPoint.y() + byPoint.y());
0182     return true;
0183 }
0184 
0185 void SVGAnimateMotionElement::calculateAnimatedValue(float percentage, unsigned repeat, SVGSMILElement *)
0186 {
0187     SVGElement *target = targetElement();
0188     if (!target) {
0189         return;
0190     }
0191     AffineTransform *transform = target->supplementalTransform();
0192     if (!transform) {
0193         return;
0194     }
0195 
0196     if (!isAdditive()) {
0197         transform->reset();
0198     }
0199 
0200     // FIXME: Implement accumulate.
0201 
0202     if (animationMode() == PathAnimation) {
0203         ASSERT(!animationPath().isEmpty());
0204         Path path = animationPath();
0205         float positionOnPath = path.length() * percentage;
0206         bool ok;
0207         FloatPoint position = path.pointAtLength(positionOnPath, ok);
0208         if (ok) {
0209             transform->translate(position.x(), position.y());
0210             RotateMode rotateMode = this->rotateMode();
0211             if (rotateMode == RotateAuto || rotateMode == RotateAutoReverse) {
0212                 float angle = path.normalAngleAtLength(positionOnPath, ok);
0213                 if (rotateMode == RotateAutoReverse) {
0214                     angle += 180.f;
0215                 }
0216                 transform->rotate(angle);
0217             }
0218         }
0219         return;
0220     }
0221     FloatSize diff = m_toPoint - m_fromPoint;
0222     transform->translate(diff.width() * percentage + m_fromPoint.x(), diff.height() * percentage + m_fromPoint.y());
0223 }
0224 
0225 void SVGAnimateMotionElement::applyResultsToTarget()
0226 {
0227     // We accumulate to the target element transform list so there is not much to do here.
0228     SVGElement *targetElement = this->targetElement();
0229     if (targetElement && targetElement->renderer()) {
0230         targetElement->renderer()->setNeedsLayout(true);
0231     }
0232 
0233     // ...except in case where we have additional instances in <use> trees.
0234     HashSet<SVGElementInstance *> *instances = document()->accessSVGExtensions()->instancesForElement(targetElement);
0235     if (!instances) {
0236         return;
0237     }
0238     HashSet<SVGElementInstance *>::iterator end = instances->end();
0239     for (HashSet<SVGElementInstance *>::iterator it = instances->begin(); it != end; ++it) {
0240         SVGElement *shadowTreeElement = (*it)->shadowTreeElement();
0241         ASSERT(shadowTreeElement);
0242         AffineTransform *transform = shadowTreeElement->supplementalTransform();
0243         AffineTransform *t = targetElement->supplementalTransform();
0244         transform->setMatrix(t->a(), t->b(), t->c(), t->d(), t->e(), t->f());
0245         if (shadowTreeElement->renderer()) {
0246             shadowTreeElement->renderer()->setNeedsLayout(true);
0247         }
0248     }
0249 }
0250 
0251 float SVGAnimateMotionElement::calculateDistance(const String &fromString, const String &toString)
0252 {
0253     FloatPoint from;
0254     FloatPoint to;
0255     if (!parsePoint(fromString, from)) {
0256         return -1.f;
0257     }
0258     if (!parsePoint(toString, to)) {
0259         return -1.f;
0260     }
0261     FloatSize diff = to - from;
0262     return sqrtf(diff.width() * diff.width() + diff.height() * diff.height());
0263 }
0264 
0265 }
0266 
0267 #endif // ENABLE(SVG)
0268