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 #include "core/svg/SVGAnimateMotionElement.h"
     25 
     26 #include "SVGNames.h"
     27 #include "core/rendering/RenderObject.h"
     28 #include "core/rendering/svg/RenderSVGResource.h"
     29 #include "core/rendering/svg/SVGPathData.h"
     30 #include "core/svg/SVGElementInstance.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 
     39 namespace WebCore {
     40 
     41 using namespace SVGNames;
     42 
     43 inline SVGAnimateMotionElement::SVGAnimateMotionElement(Document& document)
     44     : SVGAnimationElement(animateMotionTag, document)
     45     , m_hasToPointAtEndOfDuration(false)
     46 {
     47     setCalcMode(CalcModePaced);
     48     ScriptWrappable::init(this);
     49 }
     50 
     51 SVGAnimateMotionElement::~SVGAnimateMotionElement()
     52 {
     53     if (targetElement())
     54         clearAnimatedType(targetElement());
     55 }
     56 
     57 PassRefPtr<SVGAnimateMotionElement> SVGAnimateMotionElement::create(Document& document)
     58 {
     59     return adoptRef(new SVGAnimateMotionElement(document));
     60 }
     61 
     62 bool SVGAnimateMotionElement::hasValidAttributeType()
     63 {
     64     SVGElement* targetElement = this->targetElement();
     65     if (!targetElement)
     66         return false;
     67 
     68     // We don't have a special attribute name to verify the animation type. Check the element name instead.
     69     if (!targetElement->isSVGGraphicsElement())
     70         return false;
     71     // Spec: SVG 1.1 section 19.2.15
     72     // FIXME: svgTag is missing. Needs to be checked, if transforming <svg> could cause problems.
     73     if (targetElement->hasTagName(gTag)
     74         || targetElement->hasTagName(defsTag)
     75         || targetElement->hasTagName(useTag)
     76         || targetElement->hasTagName(SVGNames::imageTag)
     77         || targetElement->hasTagName(switchTag)
     78         || targetElement->hasTagName(pathTag)
     79         || targetElement->hasTagName(rectTag)
     80         || targetElement->hasTagName(circleTag)
     81         || targetElement->hasTagName(ellipseTag)
     82         || targetElement->hasTagName(lineTag)
     83         || targetElement->hasTagName(polylineTag)
     84         || targetElement->hasTagName(polygonTag)
     85         || targetElement->hasTagName(textTag)
     86         || targetElement->hasTagName(clipPathTag)
     87         || targetElement->hasTagName(maskTag)
     88         || targetElement->hasTagName(SVGNames::aTag)
     89         || targetElement->hasTagName(foreignObjectTag)
     90         )
     91         return true;
     92     return false;
     93 }
     94 
     95 bool SVGAnimateMotionElement::hasValidAttributeName()
     96 {
     97     // AnimateMotion does not use attributeName so it is always valid.
     98     return true;
     99 }
    100 
    101 bool SVGAnimateMotionElement::isSupportedAttribute(const QualifiedName& attrName)
    102 {
    103     DEFINE_STATIC_LOCAL(HashSet<QualifiedName>, supportedAttributes, ());
    104     if (supportedAttributes.isEmpty())
    105         supportedAttributes.add(SVGNames::pathAttr);
    106     return supportedAttributes.contains<SVGAttributeHashTranslator>(attrName);
    107 }
    108 
    109 void SVGAnimateMotionElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
    110 {
    111     if (!isSupportedAttribute(name)) {
    112         SVGAnimationElement::parseAttribute(name, value);
    113         return;
    114     }
    115 
    116     if (name == SVGNames::pathAttr) {
    117         m_path = Path();
    118         buildPathFromString(value, m_path);
    119         updateAnimationPath();
    120         return;
    121     }
    122 
    123     ASSERT_NOT_REACHED();
    124 }
    125 
    126 SVGAnimateMotionElement::RotateMode SVGAnimateMotionElement::rotateMode() const
    127 {
    128     DEFINE_STATIC_LOCAL(const AtomicString, autoVal, ("auto", AtomicString::ConstructFromLiteral));
    129     DEFINE_STATIC_LOCAL(const AtomicString, autoReverse, ("auto-reverse", AtomicString::ConstructFromLiteral));
    130     const AtomicString& rotate = getAttribute(SVGNames::rotateAttr);
    131     if (rotate == autoVal)
    132         return RotateAuto;
    133     if (rotate == autoReverse)
    134         return RotateAutoReverse;
    135     return RotateAngle;
    136 }
    137 
    138 void SVGAnimateMotionElement::updateAnimationPath()
    139 {
    140     m_animationPath = Path();
    141     bool foundMPath = false;
    142 
    143     for (Node* child = firstChild(); child; child = child->nextSibling()) {
    144         if (child->hasTagName(SVGNames::mpathTag)) {
    145             SVGMPathElement* mPath = toSVGMPathElement(child);
    146             SVGPathElement* pathElement = mPath->pathElement();
    147             if (pathElement) {
    148                 updatePathFromGraphicsElement(pathElement, m_animationPath);
    149                 foundMPath = true;
    150                 break;
    151             }
    152         }
    153     }
    154 
    155     if (!foundMPath && fastHasAttribute(SVGNames::pathAttr))
    156         m_animationPath = m_path;
    157 
    158     updateAnimationMode();
    159 }
    160 
    161 template<typename CharType>
    162 static bool parsePointInternal(const String& string, FloatPoint& point)
    163 {
    164     const CharType* ptr = string.getCharacters<CharType>();
    165     const CharType* end = ptr + string.length();
    166 
    167     if (!skipOptionalSVGSpaces(ptr, end))
    168         return false;
    169 
    170     float x = 0;
    171     if (!parseNumber(ptr, end, x))
    172         return false;
    173 
    174     float y = 0;
    175     if (!parseNumber(ptr, end, y))
    176         return false;
    177 
    178     point = FloatPoint(x, y);
    179 
    180     // disallow anything except spaces at the end
    181     return !skipOptionalSVGSpaces(ptr, end);
    182 }
    183 
    184 static bool parsePoint(const String& string, FloatPoint& point)
    185 {
    186     if (string.isEmpty())
    187         return false;
    188     if (string.is8Bit())
    189         return parsePointInternal<LChar>(string, point);
    190     return parsePointInternal<UChar>(string, point);
    191 }
    192 
    193 void SVGAnimateMotionElement::resetAnimatedType()
    194 {
    195     if (!hasValidAttributeType())
    196         return;
    197     SVGElement* targetElement = this->targetElement();
    198     if (!targetElement)
    199         return;
    200     if (AffineTransform* transform = targetElement->supplementalTransform())
    201         transform->makeIdentity();
    202 }
    203 
    204 void SVGAnimateMotionElement::clearAnimatedType(SVGElement* targetElement)
    205 {
    206     if (!targetElement)
    207         return;
    208 
    209     AffineTransform* transform = targetElement->supplementalTransform();
    210     if (!transform)
    211         return;
    212 
    213     transform->makeIdentity();
    214 
    215     if (RenderObject* targetRenderer = targetElement->renderer()) {
    216         targetRenderer->setNeedsTransformUpdate();
    217         RenderSVGResource::markForLayoutAndParentResourceInvalidation(targetRenderer);
    218     }
    219 }
    220 
    221 bool SVGAnimateMotionElement::calculateToAtEndOfDurationValue(const String& toAtEndOfDurationString)
    222 {
    223     parsePoint(toAtEndOfDurationString, m_toPointAtEndOfDuration);
    224     m_hasToPointAtEndOfDuration = true;
    225     return true;
    226 }
    227 
    228 bool SVGAnimateMotionElement::calculateFromAndToValues(const String& fromString, const String& toString)
    229 {
    230     m_hasToPointAtEndOfDuration = false;
    231     parsePoint(fromString, m_fromPoint);
    232     parsePoint(toString, m_toPoint);
    233     return true;
    234 }
    235 
    236 bool SVGAnimateMotionElement::calculateFromAndByValues(const String& fromString, const String& byString)
    237 {
    238     m_hasToPointAtEndOfDuration = false;
    239     if (animationMode() == ByAnimation && !isAdditive())
    240         return false;
    241     parsePoint(fromString, m_fromPoint);
    242     FloatPoint byPoint;
    243     parsePoint(byString, byPoint);
    244     m_toPoint = FloatPoint(m_fromPoint.x() + byPoint.x(), m_fromPoint.y() + byPoint.y());
    245     return true;
    246 }
    247 
    248 void SVGAnimateMotionElement::calculateAnimatedValue(float percentage, unsigned repeatCount, SVGSMILElement*)
    249 {
    250     SVGElement* targetElement = this->targetElement();
    251     if (!targetElement)
    252         return;
    253     AffineTransform* transform = targetElement->supplementalTransform();
    254     if (!transform)
    255         return;
    256 
    257     if (RenderObject* targetRenderer = targetElement->renderer())
    258         targetRenderer->setNeedsTransformUpdate();
    259 
    260     if (!isAdditive())
    261         transform->makeIdentity();
    262 
    263     if (animationMode() != PathAnimation) {
    264         FloatPoint toPointAtEndOfDuration = m_toPoint;
    265         if (isAccumulated() && repeatCount && m_hasToPointAtEndOfDuration)
    266             toPointAtEndOfDuration = m_toPointAtEndOfDuration;
    267 
    268         float animatedX = 0;
    269         animateAdditiveNumber(percentage, repeatCount, m_fromPoint.x(), m_toPoint.x(), toPointAtEndOfDuration.x(), animatedX);
    270 
    271         float animatedY = 0;
    272         animateAdditiveNumber(percentage, repeatCount, m_fromPoint.y(), m_toPoint.y(), toPointAtEndOfDuration.y(), animatedY);
    273 
    274         transform->translate(animatedX, animatedY);
    275         return;
    276     }
    277 
    278     ASSERT(!m_animationPath.isEmpty());
    279 
    280     bool ok = false;
    281     float positionOnPath = m_animationPath.length() * percentage;
    282     FloatPoint position;
    283     float angle;
    284     ok = m_animationPath.pointAndNormalAtLength(positionOnPath, position, angle);
    285     if (!ok)
    286         return;
    287 
    288     // Handle accumulate="sum".
    289     if (isAccumulated() && repeatCount) {
    290         FloatPoint positionAtEndOfDuration = m_animationPath.pointAtLength(m_animationPath.length(), ok);
    291         if (ok)
    292             position.move(positionAtEndOfDuration.x() * repeatCount, positionAtEndOfDuration.y() * repeatCount);
    293     }
    294 
    295     transform->translate(position.x(), position.y());
    296     RotateMode rotateMode = this->rotateMode();
    297     if (rotateMode != RotateAuto && rotateMode != RotateAutoReverse)
    298         return;
    299     if (rotateMode == RotateAutoReverse)
    300         angle += 180;
    301     transform->rotate(angle);
    302 }
    303 
    304 void SVGAnimateMotionElement::applyResultsToTarget()
    305 {
    306     // We accumulate to the target element transform list so there is not much to do here.
    307     SVGElement* targetElement = this->targetElement();
    308     if (!targetElement)
    309         return;
    310 
    311     if (RenderObject* renderer = targetElement->renderer())
    312         RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer);
    313 
    314     AffineTransform* t = targetElement->supplementalTransform();
    315     if (!t)
    316         return;
    317 
    318     // ...except in case where we have additional instances in <use> trees.
    319     const HashSet<SVGElementInstance*>& instances = targetElement->instancesForElement();
    320     const HashSet<SVGElementInstance*>::const_iterator end = instances.end();
    321     for (HashSet<SVGElementInstance*>::const_iterator it = instances.begin(); it != end; ++it) {
    322         SVGElement* shadowTreeElement = (*it)->shadowTreeElement();
    323         ASSERT(shadowTreeElement);
    324         AffineTransform* transform = shadowTreeElement->supplementalTransform();
    325         if (!transform)
    326             continue;
    327         transform->setMatrix(t->a(), t->b(), t->c(), t->d(), t->e(), t->f());
    328         if (RenderObject* renderer = shadowTreeElement->renderer()) {
    329             renderer->setNeedsTransformUpdate();
    330             RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer);
    331         }
    332     }
    333 }
    334 
    335 float SVGAnimateMotionElement::calculateDistance(const String& fromString, const String& toString)
    336 {
    337     FloatPoint from;
    338     FloatPoint to;
    339     if (!parsePoint(fromString, from))
    340         return -1;
    341     if (!parsePoint(toString, to))
    342         return -1;
    343     FloatSize diff = to - from;
    344     return sqrtf(diff.width() * diff.width() + diff.height() * diff.height());
    345 }
    346 
    347 void SVGAnimateMotionElement::updateAnimationMode()
    348 {
    349     if (!m_animationPath.isEmpty())
    350         setAnimationMode(PathAnimation);
    351     else
    352         SVGAnimationElement::updateAnimationMode();
    353 }
    354 
    355 }
    356