Home | History | Annotate | Download | only in svg
      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