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  * Copyright (C) 2009 Cameron McCormack <cam (at) mcc.id.au>
      7  * Copyright (C) Research In Motion Limited 2010. All rights reserved.
      8  *
      9  * This library is free software; you can redistribute it and/or
     10  * modify it under the terms of the GNU Library General Public
     11  * License as published by the Free Software Foundation; either
     12  * version 2 of the License, or (at your option) any later version.
     13  *
     14  * This library is distributed in the hope that it will be useful,
     15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     17  * Library General Public License for more details.
     18  *
     19  * You should have received a copy of the GNU Library General Public License
     20  * along with this library; see the file COPYING.LIB.  If not, write to
     21  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     22  * Boston, MA 02110-1301, USA.
     23  */
     24 
     25 #include "config.h"
     26 
     27 #if ENABLE(SVG_ANIMATION)
     28 #include "SVGAnimationElement.h"
     29 
     30 #include "Attribute.h"
     31 #include "CSSComputedStyleDeclaration.h"
     32 #include "CSSParser.h"
     33 #include "CSSPropertyNames.h"
     34 #include "Color.h"
     35 #include "Document.h"
     36 #include "Event.h"
     37 #include "EventListener.h"
     38 #include "FloatConversion.h"
     39 #include "HTMLNames.h"
     40 #include "PlatformString.h"
     41 #include "SVGElementInstance.h"
     42 #include "SVGNames.h"
     43 #include "SVGParserUtilities.h"
     44 #include "SVGStyledElement.h"
     45 #include "SVGURIReference.h"
     46 #include "SVGUseElement.h"
     47 #include "XLinkNames.h"
     48 #include <wtf/StdLibExtras.h>
     49 
     50 using namespace std;
     51 
     52 namespace WebCore {
     53 
     54 // Animated property definitions
     55 DEFINE_ANIMATED_BOOLEAN(SVGAnimationElement, SVGNames::externalResourcesRequiredAttr, ExternalResourcesRequired, externalResourcesRequired)
     56 
     57 SVGAnimationElement::SVGAnimationElement(const QualifiedName& tagName, Document* document)
     58     : SVGSMILElement(tagName, document)
     59     , m_animationValid(false)
     60 {
     61 }
     62 
     63 static void parseKeyTimes(const String& parse, Vector<float>& result, bool verifyOrder)
     64 {
     65     result.clear();
     66     Vector<String> parseList;
     67     parse.split(';', parseList);
     68     for (unsigned n = 0; n < parseList.size(); ++n) {
     69         String timeString = parseList[n];
     70         bool ok;
     71         float time = timeString.toFloat(&ok);
     72         if (!ok || time < 0 || time > 1)
     73             goto fail;
     74         if (verifyOrder) {
     75             if (!n) {
     76                 if (time)
     77                     goto fail;
     78             } else if (time < result.last())
     79                 goto fail;
     80         }
     81         result.append(time);
     82     }
     83     return;
     84 fail:
     85     result.clear();
     86 }
     87 
     88 static void parseKeySplines(const String& parse, Vector<UnitBezier>& result)
     89 {
     90     result.clear();
     91     if (parse.isEmpty())
     92         return;
     93     const UChar* cur = parse.characters();
     94     const UChar* end = cur + parse.length();
     95 
     96     skipOptionalSpaces(cur, end);
     97 
     98     bool delimParsed = false;
     99     while (cur < end) {
    100         delimParsed = false;
    101         float posA = 0;
    102         if (!parseNumber(cur, end, posA)) {
    103             result.clear();
    104             return;
    105         }
    106 
    107         float posB = 0;
    108         if (!parseNumber(cur, end, posB)) {
    109             result.clear();
    110             return;
    111         }
    112 
    113         float posC = 0;
    114         if (!parseNumber(cur, end, posC)) {
    115             result.clear();
    116             return;
    117         }
    118 
    119         float posD = 0;
    120         if (!parseNumber(cur, end, posD, false)) {
    121             result.clear();
    122             return;
    123         }
    124 
    125         skipOptionalSpaces(cur, end);
    126 
    127         if (cur < end && *cur == ';') {
    128             delimParsed = true;
    129             cur++;
    130         }
    131         skipOptionalSpaces(cur, end);
    132 
    133         result.append(UnitBezier(posA, posB, posC, posD));
    134     }
    135     if (!(cur == end && !delimParsed))
    136         result.clear();
    137 }
    138 
    139 void SVGAnimationElement::parseMappedAttribute(Attribute* attr)
    140 {
    141     if (attr->name() == SVGNames::valuesAttr)
    142         attr->value().string().split(';', m_values);
    143     else if (attr->name() == SVGNames::keyTimesAttr)
    144         parseKeyTimes(attr->value(), m_keyTimes, true);
    145     else if (attr->name() == SVGNames::keyPointsAttr && hasTagName(SVGNames::animateMotionTag)) {
    146         // This is specified to be an animateMotion attribute only but it is simpler to put it here
    147         // where the other timing calculatations are.
    148         parseKeyTimes(attr->value(), m_keyPoints, false);
    149     } else if (attr->name() == SVGNames::keySplinesAttr)
    150         parseKeySplines(attr->value(), m_keySplines);
    151     else {
    152         if (SVGTests::parseMappedAttribute(attr))
    153             return;
    154         if (SVGExternalResourcesRequired::parseMappedAttribute(attr))
    155             return;
    156         SVGSMILElement::parseMappedAttribute(attr);
    157     }
    158 }
    159 
    160 void SVGAnimationElement::attributeChanged(Attribute* attr, bool preserveDecls)
    161 {
    162     // Assumptions may not hold after an attribute change.
    163     m_animationValid = false;
    164     SVGSMILElement::attributeChanged(attr, preserveDecls);
    165 }
    166 
    167 void SVGAnimationElement::synchronizeProperty(const QualifiedName& attrName)
    168 {
    169     SVGSMILElement::synchronizeProperty(attrName);
    170 
    171     if (attrName == anyQName()) {
    172         synchronizeExternalResourcesRequired();
    173         SVGTests::synchronizeProperties(this, attrName);
    174         return;
    175     }
    176 
    177     if (SVGExternalResourcesRequired::isKnownAttribute(attrName))
    178         synchronizeExternalResourcesRequired();
    179     else if (SVGTests::isKnownAttribute(attrName))
    180         SVGTests::synchronizeProperties(this, attrName);
    181 }
    182 
    183 float SVGAnimationElement::getStartTime() const
    184 {
    185     return narrowPrecisionToFloat(intervalBegin().value());
    186 }
    187 
    188 float SVGAnimationElement::getCurrentTime() const
    189 {
    190     return narrowPrecisionToFloat(elapsed().value());
    191 }
    192 
    193 float SVGAnimationElement::getSimpleDuration(ExceptionCode&) const
    194 {
    195     return narrowPrecisionToFloat(simpleDuration().value());
    196 }
    197 
    198 void SVGAnimationElement::beginElement()
    199 {
    200     beginElementAt(0);
    201 }
    202 
    203 void SVGAnimationElement::beginElementAt(float offset)
    204 {
    205     addBeginTime(elapsed() + offset);
    206 }
    207 
    208 void SVGAnimationElement::endElement()
    209 {
    210     endElementAt(0);
    211 }
    212 
    213 void SVGAnimationElement::endElementAt(float offset)
    214 {
    215     addEndTime(elapsed() + offset);
    216 }
    217 
    218 SVGAnimationElement::AnimationMode SVGAnimationElement::animationMode() const
    219 {
    220     // http://www.w3.org/TR/2001/REC-smil-animation-20010904/#AnimFuncValues
    221     if (hasTagName(SVGNames::setTag))
    222         return ToAnimation;
    223     if (!animationPath().isEmpty())
    224         return PathAnimation;
    225     if (hasAttribute(SVGNames::valuesAttr))
    226         return ValuesAnimation;
    227     if (!toValue().isEmpty())
    228         return fromValue().isEmpty() ? ToAnimation : FromToAnimation;
    229     if (!byValue().isEmpty())
    230         return fromValue().isEmpty() ? ByAnimation : FromByAnimation;
    231     return NoAnimation;
    232 }
    233 
    234 SVGAnimationElement::CalcMode SVGAnimationElement::calcMode() const
    235 {
    236     DEFINE_STATIC_LOCAL(const AtomicString, discrete, ("discrete"));
    237     DEFINE_STATIC_LOCAL(const AtomicString, linear, ("linear"));
    238     DEFINE_STATIC_LOCAL(const AtomicString, paced, ("paced"));
    239     DEFINE_STATIC_LOCAL(const AtomicString, spline, ("spline"));
    240     const AtomicString& value = getAttribute(SVGNames::calcModeAttr);
    241     if (value == discrete)
    242         return CalcModeDiscrete;
    243     if (value == linear)
    244         return CalcModeLinear;
    245     if (value == paced)
    246         return CalcModePaced;
    247     if (value == spline)
    248         return CalcModeSpline;
    249     return hasTagName(SVGNames::animateMotionTag) ? CalcModePaced : CalcModeLinear;
    250 }
    251 
    252 SVGAnimationElement::AttributeType SVGAnimationElement::attributeType() const
    253 {
    254     DEFINE_STATIC_LOCAL(const AtomicString, css, ("CSS"));
    255     DEFINE_STATIC_LOCAL(const AtomicString, xml, ("XML"));
    256     const AtomicString& value = getAttribute(SVGNames::attributeTypeAttr);
    257     if (value == css)
    258         return AttributeTypeCSS;
    259     if (value == xml)
    260         return AttributeTypeXML;
    261     return AttributeTypeAuto;
    262 }
    263 
    264 String SVGAnimationElement::toValue() const
    265 {
    266     return getAttribute(SVGNames::toAttr);
    267 }
    268 
    269 String SVGAnimationElement::byValue() const
    270 {
    271     return getAttribute(SVGNames::byAttr);
    272 }
    273 
    274 String SVGAnimationElement::fromValue() const
    275 {
    276     return getAttribute(SVGNames::fromAttr);
    277 }
    278 
    279 bool SVGAnimationElement::isAdditive() const
    280 {
    281     DEFINE_STATIC_LOCAL(const AtomicString, sum, ("sum"));
    282     const AtomicString& value = getAttribute(SVGNames::additiveAttr);
    283     return value == sum || animationMode() == ByAnimation;
    284 }
    285 
    286 bool SVGAnimationElement::isAccumulated() const
    287 {
    288     DEFINE_STATIC_LOCAL(const AtomicString, sum, ("sum"));
    289     const AtomicString& value = getAttribute(SVGNames::accumulateAttr);
    290     return value == sum && animationMode() != ToAnimation;
    291 }
    292 
    293 bool SVGAnimationElement::isTargetAttributeCSSProperty(SVGElement* targetElement, const QualifiedName& attributeName)
    294 {
    295     ASSERT(targetElement);
    296     if (!targetElement->isStyled())
    297         return false;
    298 
    299     return SVGStyledElement::isAnimatableCSSProperty(attributeName);
    300 }
    301 
    302 void SVGAnimationElement::setTargetAttributeAnimatedValue(const String& value)
    303 {
    304     if (!hasValidAttributeType())
    305         return;
    306     SVGElement* targetElement = this->targetElement();
    307     QualifiedName attributeName = this->attributeName();
    308     if (!targetElement || attributeName == anyQName() || value.isNull())
    309         return;
    310 
    311     // We don't want the instance tree to get rebuild. Instances are updated in the loop below.
    312     if (targetElement->isStyled())
    313         static_cast<SVGStyledElement*>(targetElement)->setInstanceUpdatesBlocked(true);
    314 
    315     bool attributeIsCSSProperty = isTargetAttributeCSSProperty(targetElement, attributeName);
    316     // Stop animation, if attributeType is set to CSS by the user, but the attribute itself is not a CSS property.
    317     if (!attributeIsCSSProperty && attributeType() == AttributeTypeCSS)
    318         return;
    319 
    320     ExceptionCode ec;
    321     if (attributeIsCSSProperty) {
    322         // FIXME: This should set the override style, not the inline style.
    323         // Sadly override styles are not yet implemented.
    324         targetElement->style()->setProperty(attributeName.localName(), value, "", ec);
    325     } else {
    326         // FIXME: This should set the 'presentation' value, not the actual
    327         // attribute value. Whatever that means in practice.
    328         targetElement->setAttribute(attributeName, value, ec);
    329     }
    330 
    331     if (targetElement->isStyled())
    332         static_cast<SVGStyledElement*>(targetElement)->setInstanceUpdatesBlocked(false);
    333 
    334     // If the target element is used in an <use> instance tree, update that as well.
    335     const HashSet<SVGElementInstance*>& instances = targetElement->instancesForElement();
    336     const HashSet<SVGElementInstance*>::const_iterator end = instances.end();
    337     for (HashSet<SVGElementInstance*>::const_iterator it = instances.begin(); it != end; ++it) {
    338         SVGElement* shadowTreeElement = (*it)->shadowTreeElement();
    339         if (!shadowTreeElement)
    340             continue;
    341         if (attributeIsCSSProperty)
    342             shadowTreeElement->style()->setProperty(attributeName.localName(), value, "", ec);
    343         else
    344             shadowTreeElement->setAttribute(attributeName, value, ec);
    345         (*it)->correspondingUseElement()->setNeedsStyleRecalc();
    346     }
    347 }
    348 
    349 void SVGAnimationElement::calculateKeyTimesForCalcModePaced()
    350 {
    351     ASSERT(calcMode() == CalcModePaced);
    352     ASSERT(animationMode() == ValuesAnimation);
    353 
    354     unsigned valuesCount = m_values.size();
    355     ASSERT(valuesCount > 1);
    356     Vector<float> keyTimesForPaced;
    357     float totalDistance = 0;
    358     keyTimesForPaced.append(0);
    359     for (unsigned n = 0; n < valuesCount - 1; ++n) {
    360         // Distance in any units
    361         float distance = calculateDistance(m_values[n], m_values[n + 1]);
    362         if (distance < 0)
    363             return;
    364         totalDistance += distance;
    365         keyTimesForPaced.append(distance);
    366     }
    367     if (!totalDistance)
    368         return;
    369 
    370     // Normalize.
    371     for (unsigned n = 1; n < keyTimesForPaced.size() - 1; ++n)
    372         keyTimesForPaced[n] = keyTimesForPaced[n - 1] + keyTimesForPaced[n] / totalDistance;
    373     keyTimesForPaced[keyTimesForPaced.size() - 1] = 1;
    374 
    375     // Use key times calculated based on pacing instead of the user provided ones.
    376     m_keyTimes.swap(keyTimesForPaced);
    377 }
    378 
    379 static inline double solveEpsilon(double duration) { return 1 / (200 * duration); }
    380 
    381 unsigned SVGAnimationElement::calculateKeyTimesIndex(float percent) const
    382 {
    383     unsigned index;
    384     unsigned keyTimesCount = m_keyTimes.size();
    385     for (index = 1; index < keyTimesCount; ++index) {
    386         if (m_keyTimes[index] >= percent)
    387             break;
    388     }
    389     return --index;
    390 }
    391 
    392 float SVGAnimationElement::calculatePercentForSpline(float percent, unsigned splineIndex) const
    393 {
    394     ASSERT(calcMode() == CalcModeSpline);
    395     ASSERT(splineIndex < m_keySplines.size());
    396     UnitBezier bezier = m_keySplines[splineIndex];
    397     SMILTime duration = simpleDuration();
    398     if (!duration.isFinite())
    399         duration = 100.0;
    400     return narrowPrecisionToFloat(bezier.solve(percent, solveEpsilon(duration.value())));
    401 }
    402 
    403 float SVGAnimationElement::calculatePercentFromKeyPoints(float percent) const
    404 {
    405     ASSERT(!m_keyPoints.isEmpty());
    406     ASSERT(calcMode() != CalcModePaced);
    407     ASSERT(m_keyTimes.size() > 1);
    408     ASSERT(m_keyPoints.size() == m_keyTimes.size());
    409 
    410     unsigned index = calculateKeyTimesIndex(percent);
    411     float fromPercent = m_keyTimes[index];
    412     float toPercent = m_keyTimes[index + 1];
    413     float fromKeyPoint = m_keyPoints[index];
    414     float toKeyPoint = m_keyPoints[index + 1];
    415 
    416     if (calcMode() == CalcModeDiscrete)
    417         return percent == 1 ? toKeyPoint : fromKeyPoint;
    418 
    419     float keyPointPercent = percent == 1 ? 1 : (percent - fromPercent) / (toPercent - fromPercent);
    420 
    421     if (calcMode() == CalcModeSpline) {
    422         ASSERT(m_keySplines.size() == m_keyPoints.size() - 1);
    423         keyPointPercent = calculatePercentForSpline(keyPointPercent, index);
    424     }
    425     return (toKeyPoint - fromKeyPoint) * keyPointPercent + fromKeyPoint;
    426 }
    427 
    428 void SVGAnimationElement::currentValuesFromKeyPoints(float percent, float& effectivePercent, String& from, String& to) const
    429 {
    430     ASSERT(!m_keyPoints.isEmpty());
    431     ASSERT(m_keyPoints.size() == m_keyTimes.size());
    432     ASSERT(calcMode() != CalcModePaced);
    433     effectivePercent = calculatePercentFromKeyPoints(percent);
    434     unsigned index = effectivePercent == 1 ? m_values.size() - 2 : static_cast<unsigned>(effectivePercent * (m_values.size() - 1));
    435     from = m_values[index];
    436     to = m_values[index + 1];
    437 }
    438 
    439 void SVGAnimationElement::currentValuesForValuesAnimation(float percent, float& effectivePercent, String& from, String& to) const
    440 {
    441     unsigned valuesCount = m_values.size();
    442     ASSERT(m_animationValid);
    443     ASSERT(valuesCount > 1);
    444 
    445     CalcMode calcMode = this->calcMode();
    446     if (!m_keyPoints.isEmpty() && calcMode != CalcModePaced)
    447         return currentValuesFromKeyPoints(percent, effectivePercent, from, to);
    448 
    449     unsigned keyTimesCount = m_keyTimes.size();
    450     ASSERT(!keyTimesCount || valuesCount == keyTimesCount);
    451     ASSERT(!keyTimesCount || (keyTimesCount > 1 && !m_keyTimes[0]));
    452 
    453     unsigned index = calculateKeyTimesIndex(percent);
    454     if (calcMode == CalcModeDiscrete) {
    455         if (!keyTimesCount)
    456             index = percent == 1 ? valuesCount - 1 : static_cast<unsigned>(percent * valuesCount);
    457         from = m_values[index];
    458         to = m_values[index];
    459         effectivePercent = 0;
    460         return;
    461     }
    462 
    463     float fromPercent;
    464     float toPercent;
    465     if (keyTimesCount) {
    466         fromPercent = m_keyTimes[index];
    467         toPercent = m_keyTimes[index + 1];
    468     } else {
    469         index = static_cast<unsigned>(percent * (valuesCount - 1));
    470         fromPercent =  static_cast<float>(index) / (valuesCount - 1);
    471         toPercent =  static_cast<float>(index + 1) / (valuesCount - 1);
    472     }
    473 
    474     if (index == valuesCount - 1)
    475         --index;
    476     from = m_values[index];
    477     to = m_values[index + 1];
    478     ASSERT(toPercent > fromPercent);
    479     effectivePercent = percent == 1 ? 1 : (percent - fromPercent) / (toPercent - fromPercent);
    480 
    481     if (calcMode == CalcModeSpline) {
    482         ASSERT(m_keySplines.size() == m_values.size() - 1);
    483         effectivePercent = calculatePercentForSpline(effectivePercent, index);
    484     }
    485 }
    486 
    487 void SVGAnimationElement::startedActiveInterval()
    488 {
    489     m_animationValid = false;
    490 
    491     if (!hasValidAttributeType())
    492         return;
    493 
    494     // These validations are appropriate for all animation modes.
    495     if (hasAttribute(SVGNames::keyPointsAttr) && m_keyPoints.size() != m_keyTimes.size())
    496         return;
    497 
    498     AnimationMode animationMode = this->animationMode();
    499     CalcMode calcMode = this->calcMode();
    500     if (calcMode == CalcModeSpline) {
    501         unsigned splinesCount = m_keySplines.size() + 1;
    502         if ((hasAttribute(SVGNames::keyPointsAttr) && m_keyPoints.size() != splinesCount)
    503             || (animationMode == ValuesAnimation && m_values.size() != splinesCount))
    504             return;
    505     }
    506 
    507     String from = fromValue();
    508     String to = toValue();
    509     String by = byValue();
    510     if (animationMode == NoAnimation)
    511         return;
    512     if (animationMode == FromToAnimation)
    513         m_animationValid = calculateFromAndToValues(from, to);
    514     else if (animationMode == ToAnimation) {
    515         // For to-animations the from value is the current accumulated value from lower priority animations.
    516         // The value is not static and is determined during the animation.
    517         m_animationValid = calculateFromAndToValues(String(), to);
    518     } else if (animationMode == FromByAnimation)
    519         m_animationValid = calculateFromAndByValues(from, by);
    520     else if (animationMode == ByAnimation)
    521         m_animationValid = calculateFromAndByValues(String(), by);
    522     else if (animationMode == ValuesAnimation) {
    523         m_animationValid = m_values.size() > 1
    524             && (calcMode == CalcModePaced || !hasAttribute(SVGNames::keyTimesAttr) || hasAttribute(SVGNames::keyPointsAttr) || (m_values.size() == m_keyTimes.size()))
    525             && (calcMode == CalcModeDiscrete || !m_keyTimes.size() || m_keyTimes.last() == 1)
    526             && (calcMode != CalcModeSpline || ((m_keySplines.size() && (m_keySplines.size() == m_values.size() - 1)) || m_keySplines.size() == m_keyPoints.size() - 1))
    527             && (!hasAttribute(SVGNames::keyPointsAttr) || (m_keyTimes.size() > 1 && m_keyTimes.size() == m_keyPoints.size()));
    528         if (calcMode == CalcModePaced && m_animationValid)
    529             calculateKeyTimesForCalcModePaced();
    530     } else if (animationMode == PathAnimation)
    531         m_animationValid = calcMode == CalcModePaced || !hasAttribute(SVGNames::keyPointsAttr) || (m_keyTimes.size() > 1 && m_keyTimes.size() == m_keyPoints.size());
    532 }
    533 
    534 void SVGAnimationElement::updateAnimation(float percent, unsigned repeat, SVGSMILElement* resultElement)
    535 {
    536     if (!m_animationValid)
    537         return;
    538 
    539     float effectivePercent;
    540     CalcMode mode = calcMode();
    541     if (animationMode() == ValuesAnimation) {
    542         String from;
    543         String to;
    544         currentValuesForValuesAnimation(percent, effectivePercent, from, to);
    545         if (from != m_lastValuesAnimationFrom || to != m_lastValuesAnimationTo) {
    546             m_animationValid = calculateFromAndToValues(from, to);
    547             if (!m_animationValid)
    548                 return;
    549             m_lastValuesAnimationFrom = from;
    550             m_lastValuesAnimationTo = to;
    551         }
    552     } else if (!m_keyPoints.isEmpty() && mode != CalcModePaced)
    553         effectivePercent = calculatePercentFromKeyPoints(percent);
    554     else if (m_keyPoints.isEmpty() && mode == CalcModeSpline && m_keyTimes.size() > 1)
    555         effectivePercent = calculatePercentForSpline(percent, calculateKeyTimesIndex(percent));
    556     else
    557         effectivePercent = percent;
    558 
    559     calculateAnimatedValue(effectivePercent, repeat, resultElement);
    560 }
    561 
    562 void SVGAnimationElement::endedActiveInterval()
    563 {
    564 }
    565 
    566 }
    567 #endif // ENABLE(SVG_ANIMATION)
    568 
    569