Home | History | Annotate | Download | only in svg
      1 /*
      2  * Copyright (C) 2007 Eric Seidel <eric (at) webkit.org>
      3  * Copyright (C) 2007 Rob Buis <buis (at) 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) && ENABLE(SVG_ANIMATION)
     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 "SVGPathElement.h"
     35 #include "SVGPathParserFactory.h"
     36 #include "SVGTransformList.h"
     37 #include <wtf/MathExtras.h>
     38 #include <wtf/StdLibExtras.h>
     39 
     40 namespace WebCore {
     41 
     42 using namespace SVGNames;
     43 
     44 inline SVGAnimateMotionElement::SVGAnimateMotionElement(const QualifiedName& tagName, Document* document)
     45     : SVGAnimationElement(tagName, document)
     46     , m_baseIndexInTransformList(0)
     47     , m_angle(0)
     48 {
     49 }
     50 
     51 PassRefPtr<SVGAnimateMotionElement> SVGAnimateMotionElement::create(const QualifiedName& tagName, Document* document)
     52 {
     53     return adoptRef(new SVGAnimateMotionElement(tagName, document));
     54 }
     55 
     56 bool SVGAnimateMotionElement::hasValidAttributeType() const
     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->isStyledTransformable() && !targetElement->hasTagName(SVGNames::textTag))
     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     if (targetElement->hasTagName(gTag)
     68         || targetElement->hasTagName(defsTag)
     69         || targetElement->hasTagName(useTag)
     70         || targetElement->hasTagName(imageTag)
     71         || targetElement->hasTagName(switchTag)
     72         || targetElement->hasTagName(pathTag)
     73         || targetElement->hasTagName(rectTag)
     74         || targetElement->hasTagName(circleTag)
     75         || targetElement->hasTagName(ellipseTag)
     76         || targetElement->hasTagName(lineTag)
     77         || targetElement->hasTagName(polylineTag)
     78         || targetElement->hasTagName(polygonTag)
     79         || targetElement->hasTagName(textTag)
     80         || targetElement->hasTagName(clipPathTag)
     81         || targetElement->hasTagName(maskTag)
     82         || targetElement->hasTagName(aTag)
     83 #if ENABLE(SVG_FOREIGN_OBJECT)
     84         || targetElement->hasTagName(foreignObjectTag)
     85 #endif
     86         )
     87         return true;
     88     return false;
     89 }
     90 
     91 void SVGAnimateMotionElement::parseMappedAttribute(Attribute* attr)
     92 {
     93     if (attr->name() == SVGNames::pathAttr) {
     94         m_path = Path();
     95         SVGPathParserFactory* factory = SVGPathParserFactory::self();
     96         factory->buildPathFromString(attr->value(), m_path);
     97     } else
     98         SVGAnimationElement::parseMappedAttribute(attr);
     99 }
    100 
    101 SVGAnimateMotionElement::RotateMode SVGAnimateMotionElement::rotateMode() const
    102 {
    103     DEFINE_STATIC_LOCAL(const AtomicString, autoVal, ("auto"));
    104     DEFINE_STATIC_LOCAL(const AtomicString, autoReverse, ("auto-reverse"));
    105     String rotate = getAttribute(SVGNames::rotateAttr);
    106     if (rotate == autoVal)
    107         return RotateAuto;
    108     if (rotate == autoReverse)
    109         return RotateAutoReverse;
    110     return RotateAngle;
    111 }
    112 
    113 Path SVGAnimateMotionElement::animationPath() const
    114 {
    115     for (Node* child = firstChild(); child; child = child->nextSibling()) {
    116         if (child->hasTagName(SVGNames::mpathTag)) {
    117             SVGMPathElement* mPath = static_cast<SVGMPathElement*>(child);
    118             SVGPathElement* pathElement = mPath->pathElement();
    119             Path path;
    120             if (pathElement)
    121                 pathElement->toPathData(path);
    122             return path;
    123         }
    124     }
    125     if (hasAttribute(SVGNames::pathAttr))
    126         return m_path;
    127     return Path();
    128 }
    129 
    130 static bool parsePoint(const String& s, FloatPoint& point)
    131 {
    132     if (s.isEmpty())
    133         return false;
    134     const UChar* cur = s.characters();
    135     const UChar* end = cur + s.length();
    136 
    137     if (!skipOptionalSpaces(cur, end))
    138         return false;
    139 
    140     float x = 0;
    141     if (!parseNumber(cur, end, x))
    142         return false;
    143 
    144     float y = 0;
    145     if (!parseNumber(cur, end, y))
    146         return false;
    147 
    148     point = FloatPoint(x, y);
    149 
    150     // disallow anything except spaces at the end
    151     return !skipOptionalSpaces(cur, end);
    152 }
    153 
    154 void SVGAnimateMotionElement::resetToBaseValue(const String&)
    155 {
    156     if (!hasValidAttributeType())
    157         return;
    158     AffineTransform* transform = targetElement()->supplementalTransform();
    159     if (!transform)
    160         return;
    161     transform->makeIdentity();
    162 }
    163 
    164 bool SVGAnimateMotionElement::calculateFromAndToValues(const String& fromString, const String& toString)
    165 {
    166     parsePoint(fromString, m_fromPoint);
    167     parsePoint(toString, m_toPoint);
    168     return true;
    169 }
    170 
    171 bool SVGAnimateMotionElement::calculateFromAndByValues(const String& fromString, const String& byString)
    172 {
    173     parsePoint(fromString, m_fromPoint);
    174     FloatPoint byPoint;
    175     parsePoint(byString, byPoint);
    176     m_toPoint = FloatPoint(m_fromPoint.x() + byPoint.x(), m_fromPoint.y() + byPoint.y());
    177     return true;
    178 }
    179 
    180 void SVGAnimateMotionElement::calculateAnimatedValue(float percentage, unsigned, SVGSMILElement*)
    181 {
    182     SVGElement* targetElement = this->targetElement();
    183     if (!targetElement)
    184         return;
    185     AffineTransform* transform = targetElement->supplementalTransform();
    186     if (!transform)
    187         return;
    188 
    189     if (RenderObject* targetRenderer = targetElement->renderer())
    190         targetRenderer->setNeedsTransformUpdate();
    191 
    192     if (!isAdditive())
    193         transform->makeIdentity();
    194 
    195     // FIXME: Implement accumulate.
    196 
    197     if (animationMode() == PathAnimation) {
    198         ASSERT(!animationPath().isEmpty());
    199         Path path = animationPath();
    200         float positionOnPath = path.length() * percentage;
    201         bool ok;
    202         FloatPoint position = path.pointAtLength(positionOnPath, ok);
    203         if (ok) {
    204             transform->translate(position.x(), position.y());
    205             RotateMode rotateMode = this->rotateMode();
    206             if (rotateMode == RotateAuto || rotateMode == RotateAutoReverse) {
    207                 float angle = path.normalAngleAtLength(positionOnPath, ok);
    208                 if (rotateMode == RotateAutoReverse)
    209                     angle += 180;
    210                 transform->rotate(angle);
    211             }
    212         }
    213         return;
    214     }
    215     FloatSize diff = m_toPoint - m_fromPoint;
    216     transform->translate(diff.width() * percentage + m_fromPoint.x(), diff.height() * percentage + m_fromPoint.y());
    217 }
    218 
    219 void SVGAnimateMotionElement::applyResultsToTarget()
    220 {
    221     // We accumulate to the target element transform list so there is not much to do here.
    222     SVGElement* targetElement = this->targetElement();
    223     if (!targetElement)
    224         return;
    225 
    226     if (RenderObject* renderer = targetElement->renderer())
    227         RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer);
    228 
    229     AffineTransform* t = targetElement->supplementalTransform();
    230     if (!t)
    231         return;
    232 
    233     // ...except in case where we have additional instances in <use> trees.
    234     const HashSet<SVGElementInstance*>& instances = targetElement->instancesForElement();
    235     const HashSet<SVGElementInstance*>::const_iterator end = instances.end();
    236     for (HashSet<SVGElementInstance*>::const_iterator it = instances.begin(); it != end; ++it) {
    237         SVGElement* shadowTreeElement = (*it)->shadowTreeElement();
    238         ASSERT(shadowTreeElement);
    239         AffineTransform* transform = shadowTreeElement->supplementalTransform();
    240         if (!transform)
    241             continue;
    242         transform->setMatrix(t->a(), t->b(), t->c(), t->d(), t->e(), t->f());
    243         if (RenderObject* renderer = shadowTreeElement->renderer()) {
    244             renderer->setNeedsTransformUpdate();
    245             RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer);
    246         }
    247     }
    248 }
    249 
    250 float SVGAnimateMotionElement::calculateDistance(const String& fromString, const String& toString)
    251 {
    252     FloatPoint from;
    253     FloatPoint to;
    254     if (!parsePoint(fromString, from))
    255         return -1;
    256     if (!parsePoint(toString, to))
    257         return -1;
    258     FloatSize diff = to - from;
    259     return sqrtf(diff.width() * diff.width() + diff.height() * diff.height());
    260 }
    261 
    262 }
    263 #endif // ENABLE(SVG)
    264