File indexing completed on 2024-04-28 15:24:28
0001 /* 0002 Copyright (C) 2004, 2005 Nikolas Zimmermann <wildfox@kde.org> 0003 2004, 2005, 2006, 2007 Rob Buis <buis@kde.org> 0004 Copyright (C) 2007 Eric Seidel <eric@webkit.org> 0005 Copyright (C) 2008 Apple Inc. All rights reserved. 0006 0007 This file is part of the KDE project 0008 0009 This library is free software; you can redistribute it and/or 0010 modify it under the terms of the GNU Library General Public 0011 License as published by the Free Software Foundation; either 0012 version 2 of the License, or (at your option) any later version. 0013 0014 This library is distributed in the hope that it will be useful, 0015 but WITHOUT ANY WARRANTY; without even the implied warranty of 0016 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0017 Library General Public License for more details. 0018 0019 You should have received a copy of the GNU Library General Public License 0020 along with this library; see the file COPYING.LIB. If not, write to 0021 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 0022 Boston, MA 02110-1301, USA. 0023 */ 0024 0025 #if ENABLE(SVG_ANIMATION) 0026 #include "SVGAnimationElement.h" 0027 0028 #include "CSSComputedStyleDeclaration.h" 0029 #include "CSSParser.h" 0030 #include "CSSPropertyNames.h" 0031 #include "Document.h" 0032 #include "Event.h" 0033 #include "EventListener.h" 0034 #include "FloatConversion.h" 0035 #include "HTMLNames.h" 0036 #include "SVGElementInstance.h" 0037 #include "SVGNames.h" 0038 #include "SVGURIReference.h" 0039 #include "SVGUseElement.h" 0040 #include "XLinkNames.h" 0041 #include <math.h> 0042 0043 using namespace std; 0044 0045 namespace WebCore 0046 { 0047 0048 SVGAnimationElement::SVGAnimationElement(const QualifiedName &tagName, Document *doc) 0049 : SVGSMILElement(tagName, doc) 0050 , SVGTests() 0051 , SVGExternalResourcesRequired() 0052 , m_animationValid(false) 0053 { 0054 } 0055 0056 SVGAnimationElement::~SVGAnimationElement() 0057 { 0058 } 0059 0060 static void parseKeyTimes(const String &parse, Vector<float> &result, bool verifyOrder) 0061 { 0062 result.clear(); 0063 Vector<String> parseList; 0064 parse.split(';', parseList); 0065 for (unsigned n = 0; n < parseList.size(); ++n) { 0066 String timeString = parseList[n]; 0067 bool ok; 0068 float time = timeString.toFloat(&ok); 0069 if (!ok || time < 0 || time > 1.f) { 0070 goto fail; 0071 } 0072 if (verifyOrder) { 0073 if (!n) { 0074 if (time != 0) { 0075 goto fail; 0076 } 0077 } else if (time < result.last()) { 0078 goto fail; 0079 } 0080 } 0081 result.append(time); 0082 } 0083 return; 0084 fail: 0085 result.clear(); 0086 } 0087 0088 static void parseKeySplines(const String &parse, Vector<UnitBezier> &result) 0089 { 0090 result.clear(); 0091 Vector<String> parseList; 0092 parse.split(';', parseList); 0093 for (unsigned n = 0; n < parseList.size(); ++n) { 0094 Vector<String> parseSpline; 0095 parseList[n].split(',', parseSpline); 0096 // The spec says the sepator is a space, all tests use commas. Weird. 0097 if (parseSpline.size() == 1) { 0098 parseList[n].split(' ', parseSpline); 0099 } 0100 if (parseSpline.size() != 4) { 0101 goto fail; 0102 } 0103 double curveValues[4]; 0104 for (unsigned i = 0; i < 4; ++i) { 0105 String parseNumber = parseSpline[i]; 0106 bool ok; 0107 curveValues[i] = parseNumber.toDouble(&ok); 0108 if (!ok || curveValues[i] < 0.0 || curveValues[i] > 1.0) { 0109 goto fail; 0110 } 0111 } 0112 result.append(UnitBezier(curveValues[0], curveValues[1], curveValues[2], curveValues[3])); 0113 } 0114 return; 0115 fail: 0116 result.clear(); 0117 } 0118 0119 void SVGAnimationElement::parseMappedAttribute(MappedAttribute *attr) 0120 { 0121 if (attr->name() == SVGNames::valuesAttr) { 0122 attr->value().string().split(';', m_values); 0123 } else if (attr->name() == SVGNames::keyTimesAttr) { 0124 parseKeyTimes(attr->value(), m_keyTimes, true); 0125 } else if (attr->name() == SVGNames::keyPointsAttr && hasTagName(SVGNames::animateMotionTag)) { 0126 // This is specified to be an animateMotion attribute only but it is simpler to put it here 0127 // where the other timing calculatations are. 0128 parseKeyTimes(attr->value(), m_keyPoints, false); 0129 } else if (attr->name() == SVGNames::keySplinesAttr) { 0130 parseKeySplines(attr->value(), m_keySplines); 0131 } else { 0132 if (SVGTests::parseMappedAttribute(attr)) { 0133 return; 0134 } 0135 if (SVGExternalResourcesRequired::parseMappedAttribute(attr)) { 0136 return; 0137 } 0138 SVGSMILElement::parseMappedAttribute(attr); 0139 } 0140 } 0141 0142 void SVGAnimationElement::attributeChanged(Attribute *attr, bool preserveDecls) 0143 { 0144 // Assumptions may not hold after an attribute change. 0145 m_animationValid = false; 0146 SVGSMILElement::attributeChanged(attr, preserveDecls); 0147 } 0148 0149 float SVGAnimationElement::getStartTime() const 0150 { 0151 return narrowPrecisionToFloat(intervalBegin().value()); 0152 } 0153 0154 float SVGAnimationElement::getCurrentTime() const 0155 { 0156 return narrowPrecisionToFloat(elapsed().value()); 0157 } 0158 0159 float SVGAnimationElement::getSimpleDuration(ExceptionCode &) const 0160 { 0161 return narrowPrecisionToFloat(simpleDuration().value()); 0162 } 0163 0164 bool SVGAnimationElement::beginElement(ExceptionCode &ec) 0165 { 0166 return beginElementAt(0, ec); 0167 } 0168 0169 bool SVGAnimationElement::beginElementAt(float offset, ExceptionCode &ec) 0170 { 0171 addBeginTime(elapsed() + offset); 0172 return true; 0173 } 0174 0175 bool SVGAnimationElement::endElement(ExceptionCode &ec) 0176 { 0177 return endElementAt(0, ec); 0178 } 0179 0180 bool SVGAnimationElement::endElementAt(float offset, ExceptionCode &ec) 0181 { 0182 if (offset < 0) { 0183 return false; 0184 } 0185 0186 addEndTime(elapsed() + offset); 0187 return true; 0188 } 0189 0190 SVGAnimationElement::AnimationMode SVGAnimationElement::animationMode() const 0191 { 0192 // https://www.w3.org/TR/2001/REC-smil-animation-20010904/#AnimFuncValues 0193 if (hasTagName(SVGNames::setTag)) { 0194 return ToAnimation; 0195 } 0196 if (!animationPath().isEmpty()) { 0197 return PathAnimation; 0198 } 0199 if (hasAttribute(SVGNames::valuesAttr)) { 0200 return ValuesAnimation; 0201 } 0202 if (!toValue().isEmpty()) { 0203 return fromValue().isEmpty() ? ToAnimation : FromToAnimation; 0204 } 0205 if (!byValue().isEmpty()) { 0206 return fromValue().isEmpty() ? ByAnimation : FromByAnimation; 0207 } 0208 return NoAnimation; 0209 } 0210 0211 SVGAnimationElement::CalcMode SVGAnimationElement::calcMode() const 0212 { 0213 static const AtomicString discrete("discrete"); 0214 static const AtomicString linear("linear"); 0215 static const AtomicString paced("paced"); 0216 static const AtomicString spline("spline"); 0217 const AtomicString &value = getAttribute(SVGNames::calcModeAttr); 0218 if (value == discrete) { 0219 return CalcModeDiscrete; 0220 } 0221 if (value == linear) { 0222 return CalcModeLinear; 0223 } 0224 if (value == paced) { 0225 return CalcModePaced; 0226 } 0227 if (value == spline) { 0228 return CalcModeSpline; 0229 } 0230 return hasTagName(SVGNames::animateMotionTag) ? CalcModePaced : CalcModeLinear; 0231 } 0232 0233 SVGAnimationElement::AttributeType SVGAnimationElement::attributeType() const 0234 { 0235 static const AtomicString css("CSS"); 0236 static const AtomicString xml("XML"); 0237 const AtomicString &value = getAttribute(SVGNames::attributeTypeAttr); 0238 if (value == css) { 0239 return AttributeTypeCSS; 0240 } 0241 if (value == xml) { 0242 return AttributeTypeXML; 0243 } 0244 return AttributeTypeAuto; 0245 } 0246 0247 String SVGAnimationElement::toValue() const 0248 { 0249 return getAttribute(SVGNames::toAttr); 0250 } 0251 0252 String SVGAnimationElement::byValue() const 0253 { 0254 return getAttribute(SVGNames::byAttr); 0255 } 0256 0257 String SVGAnimationElement::fromValue() const 0258 { 0259 return getAttribute(SVGNames::fromAttr); 0260 } 0261 0262 bool SVGAnimationElement::isAdditive() const 0263 { 0264 static const AtomicString sum("sum"); 0265 const AtomicString &value = getAttribute(SVGNames::additiveAttr); 0266 return value == sum || animationMode() == ByAnimation; 0267 } 0268 0269 bool SVGAnimationElement::isAccumulated() const 0270 { 0271 static const AtomicString sum("sum"); 0272 const AtomicString &value = getAttribute(SVGNames::accumulateAttr); 0273 return value == sum && animationMode() != ToAnimation; 0274 } 0275 0276 bool SVGAnimationElement::hasValidTarget() const 0277 { 0278 return targetElement(); 0279 } 0280 0281 bool SVGAnimationElement::attributeIsCSS(const String &attributeName) 0282 { 0283 // FIXME: We should have a map of all SVG properties and their attribute types so we 0284 // could validate animations better. The spec is very vague about this. 0285 unsigned id = cssPropertyID(attributeName); 0286 // SVG range 0287 if (id >= CSSPropertyClipPath && id <= CSSPropertyWritingMode) { 0288 return true; 0289 } 0290 // Regular CSS properties also in SVG 0291 return id == CSSPropertyColor || id == CSSPropertyDisplay || id == CSSPropertyOpacity 0292 || (id >= CSSPropertyFont && id <= CSSPropertyFontWeight) 0293 || id == CSSPropertyOverflow || id == CSSPropertyVisibility; 0294 } 0295 0296 bool SVGAnimationElement::targetAttributeIsCSS() const 0297 { 0298 AttributeType type = attributeType(); 0299 if (type == AttributeTypeCSS) { 0300 return true; 0301 } 0302 if (type == AttributeTypeXML) { 0303 return false; 0304 } 0305 return attributeIsCSS(attributeName()); 0306 } 0307 0308 void SVGAnimationElement::setTargetAttributeAnimatedValue(const String &value) 0309 { 0310 if (!hasValidTarget()) { 0311 return; 0312 } 0313 SVGElement *target = targetElement(); 0314 String attributeName = this->attributeName(); 0315 if (!target || attributeName.isEmpty() || value.isNull()) { 0316 return; 0317 } 0318 0319 // We don't want the instance tree to get rebuild. Instances are updated in the loop below. 0320 if (target->isStyled()) { 0321 static_cast<SVGStyledElement *>(target)->setInstanceUpdatesBlocked(true); 0322 } 0323 0324 ExceptionCode ec; 0325 bool isCSS = targetAttributeIsCSS(); 0326 if (isCSS) { 0327 // FIXME: This should set the override style, not the inline style. 0328 // Sadly override styles are not yet implemented. 0329 target->style()->setProperty(attributeName, value, "", ec); 0330 } else { 0331 // FIXME: This should set the 'presentation' value, not the actual 0332 // attribute value. Whatever that means in practice. 0333 target->setAttribute(attributeName, value, ec); 0334 } 0335 0336 if (target->isStyled()) { 0337 static_cast<SVGStyledElement *>(target)->setInstanceUpdatesBlocked(false); 0338 } 0339 0340 // If the target element is used in an <use> instance tree, update that as well. 0341 HashSet<SVGElementInstance *> *instances = document()->accessSVGExtensions()->instancesForElement(target); 0342 if (!instances) { 0343 return; 0344 } 0345 HashSet<SVGElementInstance *>::iterator end = instances->end(); 0346 for (HashSet<SVGElementInstance *>::iterator it = instances->begin(); it != end; ++it) { 0347 SVGElement *shadowTreeElement = (*it)->shadowTreeElement(); 0348 ASSERT(shadowTreeElement); 0349 if (isCSS) { 0350 shadowTreeElement->style()->setProperty(attributeName, value, "", ec); 0351 } else { 0352 shadowTreeElement->setAttribute(attributeName, value, ec); 0353 } 0354 (*it)->correspondingUseElement()->setChanged(); 0355 } 0356 } 0357 0358 void SVGAnimationElement::calculateKeyTimesForCalcModePaced() 0359 { 0360 ASSERT(calcMode() == CalcModePaced); 0361 ASSERT(animationMode() == ValuesAnimation); 0362 0363 unsigned valuesCount = m_values.size(); 0364 ASSERT(valuesCount > 1); 0365 Vector<float> keyTimesForPaced; 0366 float totalDistance = 0; 0367 keyTimesForPaced.append(0); 0368 for (unsigned n = 0; n < valuesCount - 1; ++n) { 0369 // Distance in any units 0370 float distance = calculateDistance(m_values[n], m_values[n + 1]); 0371 if (distance < 0) { 0372 return; 0373 } 0374 totalDistance += distance; 0375 keyTimesForPaced.append(distance); 0376 } 0377 if (!totalDistance) { 0378 return; 0379 } 0380 0381 // Normalize. 0382 for (unsigned n = 1; n < keyTimesForPaced.size() - 1; ++n) { 0383 keyTimesForPaced[n] = keyTimesForPaced[n - 1] + keyTimesForPaced[n] / totalDistance; 0384 } 0385 keyTimesForPaced[keyTimesForPaced.size() - 1] = 1.f; 0386 0387 // Use key times calculated based on pacing instead of the user provided ones. 0388 m_keyTimes.swap(keyTimesForPaced); 0389 } 0390 0391 static inline double solveEpsilon(double duration) 0392 { 0393 return 1. / (200. * duration); 0394 } 0395 0396 float SVGAnimationElement::calculatePercentForSpline(float percent, unsigned splineIndex) const 0397 { 0398 ASSERT(calcMode() == CalcModeSpline); 0399 ASSERT(splineIndex < m_keySplines.size()); 0400 UnitBezier bezier = m_keySplines[splineIndex]; 0401 SMILTime duration = simpleDuration(); 0402 if (!duration.isFinite()) { 0403 duration = 100.0; 0404 } 0405 return narrowPrecisionToFloat(bezier.solve(percent, solveEpsilon(duration.value()))); 0406 } 0407 0408 float SVGAnimationElement::calculatePercentFromKeyPoints(float percent) const 0409 { 0410 ASSERT(!m_keyPoints.isEmpty()); 0411 ASSERT(calcMode() != CalcModePaced); 0412 unsigned keyTimesCount = m_keyTimes.size(); 0413 ASSERT(keyTimesCount > 1); 0414 ASSERT(m_keyPoints.size() == keyTimesCount); 0415 0416 unsigned index; 0417 for (index = 1; index < keyTimesCount; ++index) { 0418 if (m_keyTimes[index] >= percent) { 0419 break; 0420 } 0421 } 0422 --index; 0423 0424 float fromPercent = m_keyTimes[index]; 0425 float toPercent = m_keyTimes[index + 1]; 0426 float fromKeyPoint = m_keyPoints[index]; 0427 float toKeyPoint = m_keyPoints[index + 1]; 0428 0429 if (calcMode() == CalcModeDiscrete) { 0430 return percent == 1.0f ? toKeyPoint : fromKeyPoint; 0431 } 0432 0433 float keyPointPercent = percent == 1.0f ? 1.0f : (percent - fromPercent) / (toPercent - fromPercent); 0434 0435 if (calcMode() == CalcModeSpline) { 0436 ASSERT(m_keySplines.size() == m_keyPoints.size() - 1); 0437 keyPointPercent = calculatePercentForSpline(keyPointPercent, index); 0438 } 0439 return (toKeyPoint - fromKeyPoint) * keyPointPercent + fromKeyPoint; 0440 } 0441 0442 void SVGAnimationElement::currentValuesFromKeyPoints(float percent, float &effectivePercent, String &from, String &to) const 0443 { 0444 ASSERT(!m_keyPoints.isEmpty()); 0445 ASSERT(m_keyPoints.size() == m_keyTimes.size()); 0446 ASSERT(calcMode() != CalcModePaced); 0447 effectivePercent = calculatePercentFromKeyPoints(percent); 0448 unsigned index = effectivePercent == 1.0f ? m_values.size() - 2 : static_cast<unsigned>(effectivePercent * (m_values.size() - 1)); 0449 from = m_values[index]; 0450 to = m_values[index + 1]; 0451 } 0452 0453 void SVGAnimationElement::currentValuesForValuesAnimation(float percent, float &effectivePercent, String &from, String &to) const 0454 { 0455 unsigned valuesCount = m_values.size(); 0456 ASSERT(m_animationValid); 0457 ASSERT(valuesCount > 1); 0458 0459 CalcMode calcMode = this->calcMode(); 0460 if (!m_keyPoints.isEmpty() && calcMode != CalcModePaced) { 0461 return currentValuesFromKeyPoints(percent, effectivePercent, from, to); 0462 } 0463 0464 unsigned keyTimesCount = m_keyTimes.size(); 0465 ASSERT(!keyTimesCount || valuesCount == keyTimesCount); 0466 ASSERT(!keyTimesCount || (keyTimesCount > 1 && m_keyTimes[0] == 0)); 0467 0468 unsigned index; 0469 for (index = 1; index < keyTimesCount; ++index) { 0470 if (m_keyTimes[index] >= percent) { 0471 break; 0472 } 0473 } 0474 --index; 0475 0476 if (calcMode == CalcModeDiscrete) { 0477 if (!keyTimesCount) { 0478 index = percent == 1.0f ? valuesCount - 1 : static_cast<unsigned>(percent * valuesCount); 0479 } 0480 from = m_values[index]; 0481 to = m_values[index]; 0482 effectivePercent = 0.0f; 0483 return; 0484 } 0485 0486 float fromPercent; 0487 float toPercent; 0488 if (keyTimesCount) { 0489 fromPercent = m_keyTimes[index]; 0490 toPercent = m_keyTimes[index + 1]; 0491 } else { 0492 index = static_cast<unsigned>(percent * (valuesCount - 1)); 0493 fromPercent = static_cast<float>(index) / (valuesCount - 1); 0494 toPercent = static_cast<float>(index + 1) / (valuesCount - 1); 0495 } 0496 0497 if (index == valuesCount - 1) { 0498 --index; 0499 } 0500 from = m_values[index]; 0501 to = m_values[index + 1]; 0502 ASSERT(toPercent > fromPercent); 0503 effectivePercent = percent == 1.0f ? 1.0f : (percent - fromPercent) / (toPercent - fromPercent); 0504 0505 if (calcMode == CalcModeSpline) { 0506 ASSERT(m_keySplines.size() == m_values.size() - 1); 0507 effectivePercent = calculatePercentForSpline(effectivePercent, index); 0508 } 0509 } 0510 0511 void SVGAnimationElement::startedActiveInterval() 0512 { 0513 m_animationValid = false; 0514 0515 if (!hasValidTarget()) { 0516 return; 0517 } 0518 0519 AnimationMode animationMode = this->animationMode(); 0520 if (animationMode == NoAnimation) { 0521 return; 0522 } 0523 if (animationMode == FromToAnimation) { 0524 m_animationValid = calculateFromAndToValues(fromValue(), toValue()); 0525 } else if (animationMode == ToAnimation) { 0526 // For to-animations the from value is the current accumulated value from lower priority animations. 0527 // The value is not static and is determined during the animation. 0528 m_animationValid = calculateFromAndToValues(String(), toValue()); 0529 } else if (animationMode == FromByAnimation) { 0530 m_animationValid = calculateFromAndByValues(fromValue(), byValue()); 0531 } else if (animationMode == ByAnimation) { 0532 m_animationValid = calculateFromAndByValues(String(), byValue()); 0533 } else if (animationMode == ValuesAnimation) { 0534 CalcMode calcMode = this->calcMode(); 0535 m_animationValid = m_values.size() > 1 0536 && (calcMode == CalcModePaced || !hasAttribute(SVGNames::keyTimesAttr) || hasAttribute(SVGNames::keyPointsAttr) || (m_values.size() == m_keyTimes.size())) 0537 && (calcMode == CalcModeDiscrete || !m_keyTimes.size() || m_keyTimes.last() == 1.0) 0538 && (calcMode != CalcModeSpline || (m_keySplines.size() && (m_keySplines.size() == m_values.size() - 1) || m_keySplines.size() == m_keyPoints.size() - 1)) 0539 && (!hasAttribute(SVGNames::keyPointsAttr) || (m_keyTimes.size() > 1 && m_keyTimes.size() == m_keyPoints.size())); 0540 if (calcMode == CalcModePaced && m_animationValid) { 0541 calculateKeyTimesForCalcModePaced(); 0542 } 0543 } else if (animationMode == PathAnimation) { 0544 m_animationValid = calcMode() == CalcModePaced || !hasAttribute(SVGNames::keyPointsAttr) || (m_keyTimes.size() > 1 && m_keyTimes.size() == m_keyPoints.size()); 0545 } 0546 } 0547 0548 void SVGAnimationElement::updateAnimation(float percent, unsigned repeat, SVGSMILElement *resultElement) 0549 { 0550 if (!m_animationValid) { 0551 return; 0552 } 0553 0554 float effectivePercent; 0555 if (animationMode() == ValuesAnimation) { 0556 String from; 0557 String to; 0558 currentValuesForValuesAnimation(percent, effectivePercent, from, to); 0559 if (from != m_lastValuesAnimationFrom || to != m_lastValuesAnimationTo) { 0560 m_animationValid = calculateFromAndToValues(from, to); 0561 if (!m_animationValid) { 0562 return; 0563 } 0564 m_lastValuesAnimationFrom = from; 0565 m_lastValuesAnimationTo = to; 0566 } 0567 } else if (!m_keyPoints.isEmpty() && calcMode() != CalcModePaced) { 0568 effectivePercent = calculatePercentFromKeyPoints(percent); 0569 } else { 0570 effectivePercent = percent; 0571 } 0572 0573 calculateAnimatedValue(effectivePercent, repeat, resultElement); 0574 } 0575 0576 void SVGAnimationElement::endedActiveInterval() 0577 { 0578 } 0579 0580 } 0581 0582 #endif // ENABLE(SVG_ANIMATION) 0583