1/* 2 Copyright (C) 2004, 2005 Nikolas Zimmermann <wildfox@kde.org> 3 2004, 2005, 2006 Rob Buis <buis@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#if ENABLE(SVG) && ENABLE(SVG_ANIMATION) 24#include "SVGAnimateElement.h" 25 26#include "ColorDistance.h" 27#include "FloatConversion.h" 28#include "SVGColor.h" 29#include "SVGParserUtilities.h" 30#include "SVGPathSegList.h" 31#include <math.h> 32 33using namespace std; 34 35namespace WebCore { 36 37SVGAnimateElement::SVGAnimateElement(const QualifiedName& tagName, Document* doc) 38 : SVGAnimationElement(tagName, doc) 39 , m_propertyType(StringProperty) 40 , m_fromNumber(0) 41 , m_toNumber(0) 42 , m_animatedNumber(numeric_limits<double>::infinity()) 43{ 44} 45 46SVGAnimateElement::~SVGAnimateElement() 47{ 48} 49 50static bool parseNumberValueAndUnit(const String& in, double& value, String& unit) 51{ 52 // FIXME: These are from top of my head, figure out all property types that can be animated as numbers. 53 unsigned unitLength = 0; 54 String parse = in.stripWhiteSpace(); 55 if (parse.endsWith("%")) 56 unitLength = 1; 57 else if (parse.endsWith("px") || parse.endsWith("pt") || parse.endsWith("em")) 58 unitLength = 2; 59 else if (parse.endsWith("deg") || parse.endsWith("rad")) 60 unitLength = 3; 61 else if (parse.endsWith("grad")) 62 unitLength = 4; 63 String newUnit = parse.right(unitLength); 64 String number = parse.left(parse.length() - unitLength); 65 if ((!unit.isEmpty() && newUnit != unit) || number.isEmpty()) 66 return false; 67 UChar last = number[number.length() - 1]; 68 if (last < '0' || last > '9') 69 return false; 70 unit = newUnit; 71 bool ok; 72 value = number.toDouble(&ok); 73 return ok; 74} 75 76SVGAnimateElement::PropertyType SVGAnimateElement::determinePropertyType(const String& attribute) const 77{ 78 // FIXME: We need a full property table for figuring this out reliably. 79 if (hasTagName(SVGNames::animateColorTag)) 80 return ColorProperty; 81 if (attribute == "d") 82 return PathProperty; 83 if (attribute == "color" || attribute == "fill" || attribute == "stroke") 84 return ColorProperty; 85 return NumberProperty; 86} 87 88void SVGAnimateElement::calculateAnimatedValue(float percentage, unsigned repeat, SVGSMILElement* resultElement) 89{ 90 ASSERT(percentage >= 0.f && percentage <= 1.f); 91 ASSERT(resultElement); 92 if (hasTagName(SVGNames::setTag)) 93 percentage = 1.f; 94 if (!resultElement->hasTagName(SVGNames::animateTag) && !resultElement->hasTagName(SVGNames::animateColorTag) 95 && !resultElement->hasTagName(SVGNames::setTag)) 96 return; 97 SVGAnimateElement* results = static_cast<SVGAnimateElement*>(resultElement); 98 // Can't accumulate over a string property. 99 if (results->m_propertyType == StringProperty && m_propertyType != StringProperty) 100 return; 101 if (m_propertyType == NumberProperty) { 102 // To animation uses contributions from the lower priority animations as the base value. 103 if (animationMode() == ToAnimation) 104 m_fromNumber = results->m_animatedNumber; 105 106 double number = (m_toNumber - m_fromNumber) * percentage + m_fromNumber; 107 108 // FIXME: This is not correct for values animation. 109 if (isAccumulated() && repeat) 110 number += m_toNumber * repeat; 111 if (isAdditive() && animationMode() != ToAnimation) 112 results->m_animatedNumber += number; 113 else 114 results->m_animatedNumber = number; 115 return; 116 } 117 if (m_propertyType == ColorProperty) { 118 if (animationMode() == ToAnimation) 119 m_fromColor = results->m_animatedColor; 120 Color color = ColorDistance(m_fromColor, m_toColor).scaledDistance(percentage).addToColorAndClamp(m_fromColor); 121 // FIXME: Accumulate colors. 122 if (isAdditive() && animationMode() != ToAnimation) 123 results->m_animatedColor = ColorDistance::addColorsAndClamp(results->m_animatedColor, color); 124 else 125 results->m_animatedColor = color; 126 return; 127 } 128 AnimationMode animationMode = this->animationMode(); 129 if (m_propertyType == PathProperty) { 130 if (percentage == 0) 131 results->m_animatedPath = m_fromPath; 132 else if (percentage == 1.f) 133 results->m_animatedPath = m_toPath; 134 else { 135 if (m_fromPath && m_toPath) 136 results->m_animatedPath = SVGPathSegList::createAnimated(m_fromPath.get(), m_toPath.get(), percentage); 137 else 138 results->m_animatedPath.clear(); 139 // Fall back to discrete animation if the paths are not compatible 140 if (!results->m_animatedPath) 141 results->m_animatedPath = ((animationMode == FromToAnimation && percentage > 0.5f) || animationMode == ToAnimation || percentage == 1.0f) 142 ? m_toPath : m_fromPath; 143 } 144 return; 145 } 146 ASSERT(animationMode == FromToAnimation || animationMode == ToAnimation || animationMode == ValuesAnimation); 147 if ((animationMode == FromToAnimation && percentage > 0.5f) || animationMode == ToAnimation || percentage == 1.0f) 148 results->m_animatedString = m_toString; 149 else 150 results->m_animatedString = m_fromString; 151 // Higher priority replace animation overrides any additive results so far. 152 results->m_propertyType = StringProperty; 153} 154 155bool SVGAnimateElement::calculateFromAndToValues(const String& fromString, const String& toString) 156{ 157 // FIXME: Needs more solid way determine target attribute type. 158 m_propertyType = determinePropertyType(attributeName()); 159 if (m_propertyType == ColorProperty) { 160 m_fromColor = SVGColor::colorFromRGBColorString(fromString); 161 m_toColor = SVGColor::colorFromRGBColorString(toString); 162 if (m_fromColor.isValid() && m_toColor.isValid()) 163 return true; 164 } else if (m_propertyType == NumberProperty) { 165 m_numberUnit = String(); 166 if (parseNumberValueAndUnit(toString, m_toNumber, m_numberUnit)) { 167 // For to-animations the from number is calculated later 168 if (animationMode() == ToAnimation || parseNumberValueAndUnit(fromString, m_fromNumber, m_numberUnit)) 169 return true; 170 } 171 } else if (m_propertyType == PathProperty) { 172 m_fromPath = SVGPathSegList::create(SVGNames::dAttr); 173 if (pathSegListFromSVGData(m_fromPath.get(), fromString)) { 174 m_toPath = SVGPathSegList::create(SVGNames::dAttr); 175 if (pathSegListFromSVGData(m_toPath.get(), toString)) 176 return true; 177 } 178 m_fromPath.clear(); 179 m_toPath.clear(); 180 } 181 m_fromString = fromString; 182 m_toString = toString; 183 m_propertyType = StringProperty; 184 return true; 185} 186 187bool SVGAnimateElement::calculateFromAndByValues(const String& fromString, const String& byString) 188{ 189 ASSERT(!hasTagName(SVGNames::setTag)); 190 m_propertyType = determinePropertyType(attributeName()); 191 if (m_propertyType == ColorProperty) { 192 m_fromColor = fromString.isEmpty() ? Color() : SVGColor::colorFromRGBColorString(fromString); 193 m_toColor = ColorDistance::addColorsAndClamp(m_fromColor, SVGColor::colorFromRGBColorString(byString)); 194 if (!m_fromColor.isValid() || !m_toColor.isValid()) 195 return false; 196 } else { 197 m_numberUnit = String(); 198 m_fromNumber = 0; 199 if (!fromString.isEmpty() && !parseNumberValueAndUnit(fromString, m_fromNumber, m_numberUnit)) 200 return false; 201 if (!parseNumberValueAndUnit(byString, m_toNumber, m_numberUnit)) 202 return false; 203 m_toNumber += m_fromNumber; 204 } 205 return true; 206} 207 208void SVGAnimateElement::resetToBaseValue(const String& baseString) 209{ 210 m_animatedString = baseString; 211 m_propertyType = determinePropertyType(attributeName()); 212 if (m_propertyType == ColorProperty) { 213 m_animatedColor = baseString.isEmpty() ? Color() : SVGColor::colorFromRGBColorString(baseString); 214 if (m_animatedColor.isValid()) 215 return; 216 } else if (m_propertyType == NumberProperty) { 217 if (baseString.isEmpty()) { 218 m_animatedNumber = 0; 219 m_numberUnit = String(); 220 return; 221 } 222 if (parseNumberValueAndUnit(baseString, m_animatedNumber, m_numberUnit)) 223 return; 224 } else if (m_propertyType == PathProperty) { 225 m_animatedPath.clear(); 226 return; 227 } 228 m_propertyType = StringProperty; 229} 230 231void SVGAnimateElement::applyResultsToTarget() 232{ 233 String valueToApply; 234 if (m_propertyType == ColorProperty) 235 valueToApply = m_animatedColor.name(); 236 else if (m_propertyType == NumberProperty) 237 valueToApply = String::number(m_animatedNumber) + m_numberUnit; 238 else if (m_propertyType == PathProperty) { 239 if (!m_animatedPath || !m_animatedPath->numberOfItems()) 240 valueToApply = m_animatedString; 241 else { 242 // We need to keep going to string and back because we are currently only able to paint 243 // "processed" paths where complex shapes are replaced with simpler ones. Path 244 // morphing needs to be done with unprocessed paths. 245 // FIXME: This could be optimized if paths were not processed at parse time. 246 unsigned itemCount = m_animatedPath->numberOfItems(); 247 ExceptionCode ec; 248 for (unsigned n = 0; n < itemCount; ++n) { 249 RefPtr<SVGPathSeg> segment = m_animatedPath->getItem(n, ec); 250 valueToApply.append(segment->toString() + " "); 251 } 252 } 253 } else 254 valueToApply = m_animatedString; 255 256 setTargetAttributeAnimatedValue(valueToApply); 257} 258 259float SVGAnimateElement::calculateDistance(const String& fromString, const String& toString) 260{ 261 m_propertyType = determinePropertyType(attributeName()); 262 if (m_propertyType == NumberProperty) { 263 double from; 264 double to; 265 String unit; 266 if (!parseNumberValueAndUnit(fromString, from, unit)) 267 return -1.f; 268 if (!parseNumberValueAndUnit(toString, to, unit)) 269 return -1.f; 270 return narrowPrecisionToFloat(fabs(to - from)); 271 } else if (m_propertyType == ColorProperty) { 272 Color from = SVGColor::colorFromRGBColorString(fromString); 273 if (!from.isValid()) 274 return -1.f; 275 Color to = SVGColor::colorFromRGBColorString(toString); 276 if (!to.isValid()) 277 return -1.f; 278 return ColorDistance(from, to).distance(); 279 } 280 return -1.f; 281} 282 283} 284 285// vim:ts=4:noet 286#endif // ENABLE(SVG) 287 288