File indexing completed on 2024-04-28 15:24:27

0001 /*
0002     Copyright (C) 2004, 2005 Nikolas Zimmermann <wildfox@kde.org>
0003                   2004, 2005, 2006 Rob Buis <buis@kde.org>
0004     Copyright (C) 2008 Apple Inc. All rights reserved.
0005 
0006     This file is part of the KDE project
0007 
0008     This library is free software; you can redistribute it and/or
0009     modify it under the terms of the GNU Library General Public
0010     License as published by the Free Software Foundation; either
0011     version 2 of the License, or (at your option) any later version.
0012 
0013     This library is distributed in the hope that it will be useful,
0014     but WITHOUT ANY WARRANTY; without even the implied warranty of
0015     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0016     Library General Public License for more details.
0017 
0018     You should have received a copy of the GNU Library General Public License
0019     along with this library; see the file COPYING.LIB.  If not, write to
0020     the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0021     Boston, MA 02110-1301, USA.
0022 */
0023 
0024 #if ENABLE(SVG) && ENABLE(SVG_ANIMATION)
0025 #include "SVGAnimateElement.h"
0026 
0027 #include "ColorDistance.h"
0028 #include "FloatConversion.h"
0029 #include "SVGColor.h"
0030 #include "SVGParserUtilities.h"
0031 #include <math.h>
0032 
0033 using namespace std;
0034 
0035 namespace WebCore
0036 {
0037 
0038 SVGAnimateElement::SVGAnimateElement(const QualifiedName &tagName, Document *doc)
0039     : SVGAnimationElement(tagName, doc)
0040     , m_propertyType(StringProperty)
0041     , m_fromNumber(0)
0042     , m_toNumber(0)
0043     , m_animatedNumber(numeric_limits<double>::infinity())
0044 {
0045 }
0046 
0047 SVGAnimateElement::~SVGAnimateElement()
0048 {
0049 }
0050 
0051 static bool parseNumberValueAndUnit(const String &in, double &value, String &unit)
0052 {
0053     // FIXME: These are from top of my head, figure out all property types that can be animated as numbers.
0054     unsigned unitLength = 0;
0055     String parse = in.stripWhiteSpace();
0056     if (parse.endsWith("%")) {
0057         unitLength = 1;
0058     } else if (parse.endsWith("px") || parse.endsWith("pt") || parse.endsWith("em")) {
0059         unitLength = 2;
0060     } else if (parse.endsWith("deg") || parse.endsWith("rad")) {
0061         unitLength = 3;
0062     } else if (parse.endsWith("grad")) {
0063         unitLength = 4;
0064     }
0065     String newUnit = parse.right(unitLength);
0066     String number = parse.left(parse.length() - unitLength);
0067     if (!unit.isEmpty() && newUnit != unit || number.isEmpty()) {
0068         return false;
0069     }
0070     UChar last = number[number.length() - 1];
0071     if (last < '0' || last > '9') {
0072         return false;
0073     }
0074     unit = newUnit;
0075     bool ok;
0076     value = number.toDouble(&ok);
0077     return ok;
0078 }
0079 
0080 SVGAnimateElement::PropertyType SVGAnimateElement::determinePropertyType(const String &attribute) const
0081 {
0082     // FIXME: We need a full property table for figuring this out reliably.
0083     if (hasTagName(SVGNames::animateColorTag)) {
0084         return ColorProperty;
0085     }
0086     if (attribute == "color" || attribute == "fill" || attribute == "stroke") {
0087         return ColorProperty;
0088     }
0089     return NumberProperty;
0090 }
0091 
0092 void SVGAnimateElement::calculateAnimatedValue(float percentage, unsigned repeat, SVGSMILElement *resultElement)
0093 {
0094     ASSERT(percentage >= 0.f && percentage <= 1.f);
0095     ASSERT(resultElement);
0096     if (hasTagName(SVGNames::setTag)) {
0097         percentage = 1.f;
0098     }
0099     if (!resultElement->hasTagName(SVGNames::animateTag) && !resultElement->hasTagName(SVGNames::animateColorTag)
0100             && !resultElement->hasTagName(SVGNames::setTag)) {
0101         return;
0102     }
0103     SVGAnimateElement *results = static_cast<SVGAnimateElement *>(resultElement);
0104     // Can't accumulate over a string property.
0105     if (results->m_propertyType == StringProperty && m_propertyType != StringProperty) {
0106         return;
0107     }
0108     if (m_propertyType == NumberProperty) {
0109         // To animation uses contributions from the lower priority animations as the base value.
0110         if (animationMode() == ToAnimation) {
0111             m_fromNumber = results->m_animatedNumber;
0112         }
0113 
0114         double number = (m_toNumber - m_fromNumber) * percentage + m_fromNumber;
0115 
0116         // FIXME: This is not correct for values animation.
0117         if (isAccumulated() && repeat) {
0118             number += m_toNumber * repeat;
0119         }
0120         if (isAdditive() && animationMode() != ToAnimation) {
0121             results->m_animatedNumber += number;
0122         } else {
0123             results->m_animatedNumber = number;
0124         }
0125         return;
0126     }
0127     if (m_propertyType == ColorProperty) {
0128         if (animationMode() == ToAnimation) {
0129             m_fromColor = results->m_animatedColor;
0130         }
0131         Color color = ColorDistance(m_fromColor, m_toColor).scaledDistance(percentage).addToColorAndClamp(m_fromColor);
0132         // FIXME: Accumulate colors.
0133         if (isAdditive() && animationMode() != ToAnimation) {
0134             results->m_animatedColor = ColorDistance::addColorsAndClamp(results->m_animatedColor, color);
0135         } else {
0136             results->m_animatedColor = color;
0137         }
0138         return;
0139     }
0140     AnimationMode animationMode = this->animationMode();
0141     ASSERT(animationMode == FromToAnimation || animationMode == ToAnimation || animationMode == ValuesAnimation);
0142     if ((animationMode == FromToAnimation && percentage > 0.5f) || animationMode == ToAnimation || percentage == 1.0f) {
0143         results->m_animatedString = m_toString;
0144     } else {
0145         results->m_animatedString = m_fromString;
0146     }
0147     // Higher priority replace animation overrides any additive results so far.
0148     results->m_propertyType = StringProperty;
0149 }
0150 
0151 bool SVGAnimateElement::calculateFromAndToValues(const String &fromString, const String &toString)
0152 {
0153     // FIXME: Needs more solid way determine target attribute type.
0154     m_propertyType = determinePropertyType(attributeName());
0155     if (m_propertyType == ColorProperty) {
0156         m_fromColor = SVGColor::colorFromRGBColorString(fromString);
0157         m_toColor = SVGColor::colorFromRGBColorString(toString);
0158         if (m_fromColor.isValid() && m_toColor.isValid()) {
0159             return true;
0160         }
0161     } else if (m_propertyType == NumberProperty) {
0162         m_numberUnit = String();
0163         if (parseNumberValueAndUnit(toString, m_toNumber, m_numberUnit)) {
0164             // For to-animations the from number is calculated later
0165             if (animationMode() == ToAnimation || parseNumberValueAndUnit(fromString, m_fromNumber, m_numberUnit)) {
0166                 return true;
0167             }
0168         }
0169     }
0170     m_fromString = fromString;
0171     m_toString = toString;
0172     m_propertyType = StringProperty;
0173     return true;
0174 }
0175 
0176 bool SVGAnimateElement::calculateFromAndByValues(const String &fromString, const String &byString)
0177 {
0178     ASSERT(!hasTagName(SVGNames::setTag));
0179     m_propertyType = determinePropertyType(attributeName());
0180     if (m_propertyType == ColorProperty) {
0181         m_fromColor = fromString.isEmpty() ? Color() : SVGColor::colorFromRGBColorString(fromString);
0182         m_toColor = ColorDistance::addColorsAndClamp(m_fromColor, SVGColor::colorFromRGBColorString(byString));
0183         if (!m_fromColor.isValid() || !m_toColor.isValid()) {
0184             return false;
0185         }
0186     } else {
0187         m_numberUnit = String();
0188         m_fromNumber = 0;
0189         if (!fromString.isEmpty() && !parseNumberValueAndUnit(fromString, m_fromNumber, m_numberUnit)) {
0190             return false;
0191         }
0192         if (!parseNumberValueAndUnit(byString, m_toNumber, m_numberUnit)) {
0193             return false;
0194         }
0195         m_toNumber += m_fromNumber;
0196     }
0197     return true;
0198 }
0199 
0200 void SVGAnimateElement::resetToBaseValue(const String &baseString)
0201 {
0202     m_animatedString = baseString;
0203     m_propertyType = determinePropertyType(attributeName());
0204     if (m_propertyType == ColorProperty) {
0205         m_animatedColor = baseString.isEmpty() ? Color() : SVGColor::colorFromRGBColorString(baseString);
0206         if (m_animatedColor.isValid()) {
0207             return;
0208         }
0209     } else if (m_propertyType == NumberProperty) {
0210         if (baseString.isEmpty()) {
0211             m_animatedNumber = 0;
0212             m_numberUnit = String();
0213             return;
0214         }
0215         if (parseNumberValueAndUnit(baseString, m_animatedNumber, m_numberUnit)) {
0216             return;
0217         }
0218     }
0219     m_propertyType = StringProperty;
0220 }
0221 
0222 void SVGAnimateElement::applyResultsToTarget()
0223 {
0224     String valueToApply;
0225     if (m_propertyType == ColorProperty) {
0226         valueToApply = m_animatedColor.name();
0227     } else if (m_propertyType == NumberProperty) {
0228         valueToApply = String::number(m_animatedNumber) + m_numberUnit;
0229     } else {
0230         valueToApply = m_animatedString;
0231     }
0232 
0233     setTargetAttributeAnimatedValue(valueToApply);
0234 }
0235 
0236 float SVGAnimateElement::calculateDistance(const String &fromString, const String &toString)
0237 {
0238     m_propertyType = determinePropertyType(attributeName());
0239     if (m_propertyType == NumberProperty) {
0240         double from;
0241         double to;
0242         String unit;
0243         if (!parseNumberValueAndUnit(fromString, from, unit)) {
0244             return -1.f;
0245         }
0246         if (!parseNumberValueAndUnit(toString, to, unit)) {
0247             return -1.f;
0248         }
0249         return narrowPrecisionToFloat(fabs(to - from));
0250     } else if (m_propertyType == ColorProperty) {
0251         Color from = SVGColor::colorFromRGBColorString(fromString);
0252         if (!from.isValid()) {
0253             return -1.f;
0254         }
0255         Color to = SVGColor::colorFromRGBColorString(toString);
0256         if (!to.isValid()) {
0257             return -1.f;
0258         }
0259         return ColorDistance(from, to).distance();
0260     }
0261     return -1.f;
0262 }
0263 
0264 }
0265 
0266 #endif // ENABLE(SVG)
0267