Home | History | Annotate | Download | only in svg
      1 /*
      2  * Copyright (C) 2004, 2005 Nikolas Zimmermann <zimmermann (at) kde.org>
      3  * Copyright (C) 2004, 2005, 2006, 2007 Rob Buis <buis (at) kde.org>
      4  * Copyright (C) 2007 Eric Seidel <eric (at) webkit.org>
      5  * Copyright (C) 2008 Apple Inc. All rights reserved.
      6  *
      7  * This library is free software; you can redistribute it and/or
      8  * modify it under the terms of the GNU Library General Public
      9  * License as published by the Free Software Foundation; either
     10  * version 2 of the License, or (at your option) any later version.
     11  *
     12  * This library is distributed in the hope that it will be useful,
     13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     15  * Library General Public License for more details.
     16  *
     17  * You should have received a copy of the GNU Library General Public License
     18  * along with this library; see the file COPYING.LIB.  If not, write to
     19  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     20  * Boston, MA 02110-1301, USA.
     21  */
     22 
     23 #include "config.h"
     24 
     25 #if ENABLE(SVG) && ENABLE(SVG_ANIMATION)
     26 #include "SVGAnimateTransformElement.h"
     27 
     28 #include "AffineTransform.h"
     29 #include "Attribute.h"
     30 #include "RenderObject.h"
     31 #include "RenderSVGResource.h"
     32 #include "SVGAngle.h"
     33 #include "SVGElementInstance.h"
     34 #include "SVGGradientElement.h"
     35 #include "SVGNames.h"
     36 #include "SVGParserUtilities.h"
     37 #include "SVGSVGElement.h"
     38 #include "SVGStyledTransformableElement.h"
     39 #include "SVGTextElement.h"
     40 #include "SVGTransform.h"
     41 #include "SVGTransformList.h"
     42 #include "SVGUseElement.h"
     43 #include <math.h>
     44 #include <wtf/MathExtras.h>
     45 
     46 using namespace std;
     47 
     48 namespace WebCore {
     49 
     50 inline SVGAnimateTransformElement::SVGAnimateTransformElement(const QualifiedName& tagName, Document* document)
     51     : SVGAnimationElement(tagName, document)
     52     , m_type(SVGTransform::SVG_TRANSFORM_UNKNOWN)
     53     , m_baseIndexInTransformList(0)
     54 {
     55 }
     56 
     57 PassRefPtr<SVGAnimateTransformElement> SVGAnimateTransformElement::create(const QualifiedName& tagName, Document* document)
     58 {
     59     return adoptRef(new SVGAnimateTransformElement(tagName, document));
     60 }
     61 
     62 bool SVGAnimateTransformElement::hasValidAttributeType() const
     63 {
     64     SVGElement* targetElement = this->targetElement();
     65     if (!targetElement)
     66         return false;
     67 
     68     return determineAnimatedAttributeType(targetElement) == AnimatedTransformList;
     69 }
     70 
     71 AnimatedAttributeType SVGAnimateTransformElement::determineAnimatedAttributeType(SVGElement* targetElement) const
     72 {
     73     ASSERT(targetElement);
     74 
     75     // Just transform lists can be animated with <animateTransform>
     76     // http://www.w3.org/TR/SVG/animate.html#AnimationAttributesAndProperties
     77     if (targetElement->animatedPropertyTypeForAttribute(attributeName()) != AnimatedTransformList)
     78         return AnimatedUnknown;
     79 
     80     return AnimatedTransformList;
     81 }
     82 
     83 void SVGAnimateTransformElement::parseMappedAttribute(Attribute* attr)
     84 {
     85     if (attr->name() == SVGNames::typeAttr) {
     86         if (attr->value() == "translate")
     87             m_type = SVGTransform::SVG_TRANSFORM_TRANSLATE;
     88         else if (attr->value() == "scale")
     89             m_type = SVGTransform::SVG_TRANSFORM_SCALE;
     90         else if (attr->value() == "rotate")
     91             m_type = SVGTransform::SVG_TRANSFORM_ROTATE;
     92         else if (attr->value() == "skewX")
     93             m_type = SVGTransform::SVG_TRANSFORM_SKEWX;
     94         else if (attr->value() == "skewY")
     95             m_type = SVGTransform::SVG_TRANSFORM_SKEWY;
     96     } else
     97         SVGAnimationElement::parseMappedAttribute(attr);
     98 }
     99 
    100 
    101 static SVGTransformList* transformListFor(SVGElement* element)
    102 {
    103     ASSERT(element);
    104     if (element->isStyledTransformable())
    105         return &static_cast<SVGStyledTransformableElement*>(element)->transform();
    106     if (element->hasTagName(SVGNames::textTag))
    107         return &static_cast<SVGTextElement*>(element)->transform();
    108     if (element->hasTagName(SVGNames::linearGradientTag) || element->hasTagName(SVGNames::radialGradientTag))
    109         return &static_cast<SVGGradientElement*>(element)->gradientTransform();
    110     // FIXME: Handle patternTransform, which is obviously missing!
    111     return 0;
    112 }
    113 
    114 void SVGAnimateTransformElement::resetToBaseValue(const String& baseValue)
    115 {
    116     SVGElement* targetElement = this->targetElement();
    117     if (!targetElement || determineAnimatedAttributeType(targetElement) == AnimatedUnknown)
    118         return;
    119 
    120     if (targetElement->hasTagName(SVGNames::linearGradientTag) || targetElement->hasTagName(SVGNames::radialGradientTag)) {
    121         targetElement->setAttribute(SVGNames::gradientTransformAttr, baseValue.isEmpty() ? "matrix(1 0 0 1 0 0)" : baseValue);
    122         return;
    123     }
    124 
    125     if (baseValue.isEmpty()) {
    126         if (SVGTransformList* list = transformListFor(targetElement))
    127             list->clear();
    128     } else
    129         targetElement->setAttribute(SVGNames::transformAttr, baseValue);
    130 }
    131 
    132 void SVGAnimateTransformElement::calculateAnimatedValue(float percentage, unsigned repeat, SVGSMILElement*)
    133 {
    134     SVGElement* targetElement = this->targetElement();
    135     if (!targetElement || determineAnimatedAttributeType(targetElement) == AnimatedUnknown)
    136         return;
    137     SVGTransformList* transformList = transformListFor(targetElement);
    138     ASSERT(transformList);
    139 
    140     if (!isAdditive())
    141         transformList->clear();
    142     if (isAccumulated() && repeat) {
    143         SVGTransform accumulatedTransform = SVGTransformDistance(m_fromTransform, m_toTransform).scaledDistance(repeat).addToSVGTransform(SVGTransform());
    144         transformList->append(accumulatedTransform);
    145     }
    146     SVGTransform transform = SVGTransformDistance(m_fromTransform, m_toTransform).scaledDistance(percentage).addToSVGTransform(m_fromTransform);
    147     transformList->append(transform);
    148 }
    149 
    150 bool SVGAnimateTransformElement::calculateFromAndToValues(const String& fromString, const String& toString)
    151 {
    152     m_fromTransform = parseTransformValue(fromString);
    153     if (!m_fromTransform.isValid())
    154         return false;
    155     m_toTransform = parseTransformValue(toString);
    156     return m_toTransform.isValid();
    157 }
    158 
    159 bool SVGAnimateTransformElement::calculateFromAndByValues(const String& fromString, const String& byString)
    160 {
    161     m_fromTransform = parseTransformValue(fromString);
    162     if (!m_fromTransform.isValid())
    163         return false;
    164     m_toTransform = SVGTransformDistance::addSVGTransforms(m_fromTransform, parseTransformValue(byString));
    165     return m_toTransform.isValid();
    166 }
    167 
    168 SVGTransform SVGAnimateTransformElement::parseTransformValue(const String& value) const
    169 {
    170     if (value.isEmpty())
    171         return SVGTransform(m_type);
    172     SVGTransform result;
    173     // FIXME: This is pretty dumb but parseTransformValue() wants those parenthesis.
    174     String parseString("(" + value + ")");
    175     const UChar* ptr = parseString.characters();
    176     SVGTransformable::parseTransformValue(m_type, ptr, ptr + parseString.length(), result); // ignoring return value
    177     return result;
    178 }
    179 
    180 void SVGAnimateTransformElement::applyResultsToTarget()
    181 {
    182     SVGElement* targetElement = this->targetElement();
    183     if (!targetElement || determineAnimatedAttributeType(targetElement) == AnimatedUnknown)
    184         return;
    185 
    186     // We accumulate to the target element transform list so there is not much to do here.
    187     if (RenderObject* renderer = targetElement->renderer()) {
    188         renderer->setNeedsTransformUpdate();
    189         RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer);
    190     }
    191 
    192     // ...except in case where we have additional instances in <use> trees.
    193     SVGTransformList* transformList = transformListFor(targetElement);
    194     if (!transformList)
    195         return;
    196 
    197     const HashSet<SVGElementInstance*>& instances = targetElement->instancesForElement();
    198     const HashSet<SVGElementInstance*>::const_iterator end = instances.end();
    199     for (HashSet<SVGElementInstance*>::const_iterator it = instances.begin(); it != end; ++it) {
    200         SVGElement* shadowTreeElement = (*it)->shadowTreeElement();
    201         ASSERT(shadowTreeElement);
    202         if (shadowTreeElement->isStyledTransformable())
    203             static_cast<SVGStyledTransformableElement*>(shadowTreeElement)->setTransformBaseValue(*transformList);
    204         else if (shadowTreeElement->hasTagName(SVGNames::textTag))
    205             static_cast<SVGTextElement*>(shadowTreeElement)->setTransformBaseValue(*transformList);
    206         else if (shadowTreeElement->hasTagName(SVGNames::linearGradientTag) || shadowTreeElement->hasTagName(SVGNames::radialGradientTag))
    207             static_cast<SVGGradientElement*>(shadowTreeElement)->setGradientTransformBaseValue(*transformList);
    208         // FIXME: Handle patternTransform, obviously missing!
    209         if (RenderObject* renderer = shadowTreeElement->renderer()) {
    210             renderer->setNeedsTransformUpdate();
    211             RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer);
    212         }
    213     }
    214 }
    215 
    216 float SVGAnimateTransformElement::calculateDistance(const String& fromString, const String& toString)
    217 {
    218     // FIXME: This is not correct in all cases. The spec demands that each component (translate x and y for example)
    219     // is paced separately. To implement this we need to treat each component as individual animation everywhere.
    220     SVGTransform from = parseTransformValue(fromString);
    221     if (!from.isValid())
    222         return -1;
    223     SVGTransform to = parseTransformValue(toString);
    224     if (!to.isValid() || from.type() != to.type())
    225         return -1;
    226     if (to.type() == SVGTransform::SVG_TRANSFORM_TRANSLATE) {
    227         FloatSize diff = to.translate() - from.translate();
    228         return sqrtf(diff.width() * diff.width() + diff.height() * diff.height());
    229     }
    230     if (to.type() == SVGTransform::SVG_TRANSFORM_ROTATE)
    231         return fabsf(to.angle() - from.angle());
    232     if (to.type() == SVGTransform::SVG_TRANSFORM_SCALE) {
    233         FloatSize diff = to.scale() - from.scale();
    234         return sqrtf(diff.width() * diff.width() + diff.height() * diff.height());
    235     }
    236     return -1;
    237 }
    238 
    239 }
    240 #endif // ENABLE(SVG)
    241