00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025 #include "config.h"
00026 #if ENABLE(SVG_ANIMATION)
00027 #include "SVGAnimationElement.h"
00028
00029 #include "CSSComputedStyleDeclaration.h"
00030 #include "CSSParser.h"
00031 #include "CSSPropertyNames.h"
00032 #include "Document.h"
00033 #include "Event.h"
00034 #include "EventListener.h"
00035 #include "FloatConversion.h"
00036 #include "HTMLNames.h"
00037 #include "SVGElementInstance.h"
00038 #include "SVGNames.h"
00039 #include "SVGURIReference.h"
00040 #include "SVGUseElement.h"
00041 #include "XLinkNames.h"
00042 #include <math.h>
00043
00044 using namespace std;
00045
00046 namespace WebCore {
00047
00048 SVGAnimationElement::SVGAnimationElement(const QualifiedName& tagName, Document* doc)
00049 : SVGSMILElement(tagName, doc)
00050 , SVGTests()
00051 , SVGExternalResourcesRequired()
00052 , m_animationValid(false)
00053 {
00054 }
00055
00056 SVGAnimationElement::~SVGAnimationElement()
00057 {
00058 }
00059
00060 static void parseKeyTimes(const String& parse, Vector<float>& result, bool verifyOrder)
00061 {
00062 result.clear();
00063 Vector<String> parseList;
00064 parse.split(';', parseList);
00065 for (unsigned n = 0; n < parseList.size(); ++n) {
00066 String timeString = parseList[n];
00067 bool ok;
00068 float time = timeString.toFloat(&ok);
00069 if (!ok || time < 0 || time > 1.f)
00070 goto fail;
00071 if (verifyOrder) {
00072 if (!n) {
00073 if (time != 0)
00074 goto fail;
00075 } else if (time < result.last())
00076 goto fail;
00077 }
00078 result.append(time);
00079 }
00080 return;
00081 fail:
00082 result.clear();
00083 }
00084
00085 static void parseKeySplines(const String& parse, Vector<UnitBezier>& result)
00086 {
00087 result.clear();
00088 Vector<String> parseList;
00089 parse.split(';', parseList);
00090 for (unsigned n = 0; n < parseList.size(); ++n) {
00091 Vector<String> parseSpline;
00092 parseList[n].split(',', parseSpline);
00093
00094 if (parseSpline.size() == 1)
00095 parseList[n].split(' ', parseSpline);
00096 if (parseSpline.size() != 4)
00097 goto fail;
00098 double curveValues[4];
00099 for (unsigned i = 0; i < 4; ++i) {
00100 String parseNumber = parseSpline[i];
00101 bool ok;
00102 curveValues[i] = parseNumber.toDouble(&ok);
00103 if (!ok || curveValues[i] < 0.0 || curveValues[i] > 1.0)
00104 goto fail;
00105 }
00106 result.append(UnitBezier(curveValues[0], curveValues[1], curveValues[2], curveValues[3]));
00107 }
00108 return;
00109 fail:
00110 result.clear();
00111 }
00112
00113 void SVGAnimationElement::parseMappedAttribute(MappedAttribute* attr)
00114 {
00115 if (attr->name() == SVGNames::valuesAttr)
00116 attr->value().string().split(';', m_values);
00117 else if (attr->name() == SVGNames::keyTimesAttr)
00118 parseKeyTimes(attr->value(), m_keyTimes, true);
00119 else if (attr->name() == SVGNames::keyPointsAttr && hasTagName(SVGNames::animateMotionTag)) {
00120
00121
00122 parseKeyTimes(attr->value(), m_keyPoints, false);
00123 } else if (attr->name() == SVGNames::keySplinesAttr)
00124 parseKeySplines(attr->value(), m_keySplines);
00125 else {
00126 if (SVGTests::parseMappedAttribute(attr))
00127 return;
00128 if (SVGExternalResourcesRequired::parseMappedAttribute(attr))
00129 return;
00130 SVGSMILElement::parseMappedAttribute(attr);
00131 }
00132 }
00133
00134 void SVGAnimationElement::attributeChanged(Attribute* attr, bool preserveDecls)
00135 {
00136
00137 m_animationValid = false;
00138 SVGSMILElement::attributeChanged(attr, preserveDecls);
00139 }
00140
00141 float SVGAnimationElement::getStartTime() const
00142 {
00143 return narrowPrecisionToFloat(intervalBegin().value());
00144 }
00145
00146 float SVGAnimationElement::getCurrentTime() const
00147 {
00148 return narrowPrecisionToFloat(elapsed().value());
00149 }
00150
00151 float SVGAnimationElement::getSimpleDuration(ExceptionCode&) const
00152 {
00153 return narrowPrecisionToFloat(simpleDuration().value());
00154 }
00155
00156 bool SVGAnimationElement::beginElement(ExceptionCode& ec)
00157 {
00158 return beginElementAt(0, ec);
00159 }
00160
00161 bool SVGAnimationElement::beginElementAt(float offset, ExceptionCode& ec)
00162 {
00163 addBeginTime(elapsed() + offset);
00164 return true;
00165 }
00166
00167 bool SVGAnimationElement::endElement(ExceptionCode& ec)
00168 {
00169 return endElementAt(0, ec);
00170 }
00171
00172 bool SVGAnimationElement::endElementAt(float offset, ExceptionCode& ec)
00173 {
00174 if (offset < 0)
00175 return false;
00176
00177 addEndTime(elapsed() + offset);
00178 return true;
00179 }
00180
00181 SVGAnimationElement::AnimationMode SVGAnimationElement::animationMode() const
00182 {
00183
00184 if (hasTagName(SVGNames::setTag))
00185 return ToAnimation;
00186 if (!animationPath().isEmpty())
00187 return PathAnimation;
00188 if (hasAttribute(SVGNames::valuesAttr))
00189 return ValuesAnimation;
00190 if (!toValue().isEmpty())
00191 return fromValue().isEmpty() ? ToAnimation : FromToAnimation;
00192 if (!byValue().isEmpty())
00193 return fromValue().isEmpty() ? ByAnimation : FromByAnimation;
00194 return NoAnimation;
00195 }
00196
00197 SVGAnimationElement::CalcMode SVGAnimationElement::calcMode() const
00198 {
00199 static const AtomicString discrete("discrete");
00200 static const AtomicString linear("linear");
00201 static const AtomicString paced("paced");
00202 static const AtomicString spline("spline");
00203 const AtomicString& value = getAttribute(SVGNames::calcModeAttr);
00204 if (value == discrete)
00205 return CalcModeDiscrete;
00206 if (value == linear)
00207 return CalcModeLinear;
00208 if (value == paced)
00209 return CalcModePaced;
00210 if (value == spline)
00211 return CalcModeSpline;
00212 return hasTagName(SVGNames::animateMotionTag) ? CalcModePaced : CalcModeLinear;
00213 }
00214
00215 SVGAnimationElement::AttributeType SVGAnimationElement::attributeType() const
00216 {
00217 static const AtomicString css("CSS");
00218 static const AtomicString xml("XML");
00219 const AtomicString& value = getAttribute(SVGNames::attributeTypeAttr);
00220 if (value == css)
00221 return AttributeTypeCSS;
00222 if (value == xml)
00223 return AttributeTypeXML;
00224 return AttributeTypeAuto;
00225 }
00226
00227 String SVGAnimationElement::toValue() const
00228 {
00229 return getAttribute(SVGNames::toAttr);
00230 }
00231
00232 String SVGAnimationElement::byValue() const
00233 {
00234 return getAttribute(SVGNames::byAttr);
00235 }
00236
00237 String SVGAnimationElement::fromValue() const
00238 {
00239 return getAttribute(SVGNames::fromAttr);
00240 }
00241
00242 bool SVGAnimationElement::isAdditive() const
00243 {
00244 static const AtomicString sum("sum");
00245 const AtomicString& value = getAttribute(SVGNames::additiveAttr);
00246 return value == sum || animationMode() == ByAnimation;
00247 }
00248
00249 bool SVGAnimationElement::isAccumulated() const
00250 {
00251 static const AtomicString sum("sum");
00252 const AtomicString& value = getAttribute(SVGNames::accumulateAttr);
00253 return value == sum && animationMode() != ToAnimation;
00254 }
00255
00256 bool SVGAnimationElement::hasValidTarget() const
00257 {
00258 return targetElement();
00259 }
00260
00261 bool SVGAnimationElement::attributeIsCSS(const String& attributeName)
00262 {
00263
00264
00265 unsigned id = cssPropertyID(attributeName);
00266
00267 if (id >= CSSPropertyClipPath && id <= CSSPropertyWritingMode)
00268 return true;
00269
00270 return id == CSSPropertyColor || id == CSSPropertyDisplay || id == CSSPropertyOpacity
00271 || (id >= CSSPropertyFont && id <= CSSPropertyFontWeight)
00272 || id == CSSPropertyOverflow || id == CSSPropertyVisibility;
00273 }
00274
00275 bool SVGAnimationElement::targetAttributeIsCSS() const
00276 {
00277 AttributeType type = attributeType();
00278 if (type == AttributeTypeCSS)
00279 return true;
00280 if (type == AttributeTypeXML)
00281 return false;
00282 return attributeIsCSS(attributeName());
00283 }
00284
00285 void SVGAnimationElement::setTargetAttributeAnimatedValue(const String& value)
00286 {
00287 if (!hasValidTarget())
00288 return;
00289 SVGElement* target = targetElement();
00290 String attributeName = this->attributeName();
00291 if (!target || attributeName.isEmpty() || value.isNull())
00292 return;
00293
00294
00295 if (target->isStyled())
00296 static_cast<SVGStyledElement*>(target)->setInstanceUpdatesBlocked(true);
00297
00298 ExceptionCode ec;
00299 bool isCSS = targetAttributeIsCSS();
00300 if (isCSS) {
00301
00302
00303 target->style()->setProperty(attributeName, value, "", ec);
00304 } else {
00305
00306
00307 target->setAttribute(attributeName, value, ec);
00308 }
00309
00310 if (target->isStyled())
00311 static_cast<SVGStyledElement*>(target)->setInstanceUpdatesBlocked(false);
00312
00313
00314 HashSet<SVGElementInstance*>* instances = document()->accessSVGExtensions()->instancesForElement(target);
00315 if (!instances)
00316 return;
00317 HashSet<SVGElementInstance*>::iterator end = instances->end();
00318 for (HashSet<SVGElementInstance*>::iterator it = instances->begin(); it != end; ++it) {
00319 SVGElement* shadowTreeElement = (*it)->shadowTreeElement();
00320 ASSERT(shadowTreeElement);
00321 if (isCSS)
00322 shadowTreeElement->style()->setProperty(attributeName, value, "", ec);
00323 else
00324 shadowTreeElement->setAttribute(attributeName, value, ec);
00325 (*it)->correspondingUseElement()->setChanged();
00326 }
00327 }
00328
00329 void SVGAnimationElement::calculateKeyTimesForCalcModePaced()
00330 {
00331 ASSERT(calcMode() == CalcModePaced);
00332 ASSERT(animationMode() == ValuesAnimation);
00333
00334 unsigned valuesCount = m_values.size();
00335 ASSERT(valuesCount > 1);
00336 Vector<float> keyTimesForPaced;
00337 float totalDistance = 0;
00338 keyTimesForPaced.append(0);
00339 for (unsigned n = 0; n < valuesCount - 1; ++n) {
00340
00341 float distance = calculateDistance(m_values[n], m_values[n + 1]);
00342 if (distance < 0)
00343 return;
00344 totalDistance += distance;
00345 keyTimesForPaced.append(distance);
00346 }
00347 if (!totalDistance)
00348 return;
00349
00350
00351 for (unsigned n = 1; n < keyTimesForPaced.size() - 1; ++n)
00352 keyTimesForPaced[n] = keyTimesForPaced[n - 1] + keyTimesForPaced[n] / totalDistance;
00353 keyTimesForPaced[keyTimesForPaced.size() - 1] = 1.f;
00354
00355
00356 m_keyTimes.swap(keyTimesForPaced);
00357 }
00358
00359 static inline double solveEpsilon(double duration) { return 1. / (200. * duration); }
00360
00361 float SVGAnimationElement::calculatePercentForSpline(float percent, unsigned splineIndex) const
00362 {
00363 ASSERT(calcMode() == CalcModeSpline);
00364 ASSERT(splineIndex < m_keySplines.size());
00365 UnitBezier bezier = m_keySplines[splineIndex];
00366 SMILTime duration = simpleDuration();
00367 if (!duration.isFinite())
00368 duration = 100.0;
00369 return narrowPrecisionToFloat(bezier.solve(percent, solveEpsilon(duration.value())));
00370 }
00371
00372 float SVGAnimationElement::calculatePercentFromKeyPoints(float percent) const
00373 {
00374 ASSERT(!m_keyPoints.isEmpty());
00375 ASSERT(calcMode() != CalcModePaced);
00376 unsigned keyTimesCount = m_keyTimes.size();
00377 ASSERT(keyTimesCount > 1);
00378 ASSERT(m_keyPoints.size() == keyTimesCount);
00379
00380 unsigned index;
00381 for (index = 1; index < keyTimesCount; ++index) {
00382 if (m_keyTimes[index] >= percent)
00383 break;
00384 }
00385 --index;
00386
00387 float fromPercent = m_keyTimes[index];
00388 float toPercent = m_keyTimes[index + 1];
00389 float fromKeyPoint = m_keyPoints[index];
00390 float toKeyPoint = m_keyPoints[index + 1];
00391
00392 if (calcMode() == CalcModeDiscrete)
00393 return percent == 1.0f ? toKeyPoint : fromKeyPoint;
00394
00395 float keyPointPercent = percent == 1.0f ? 1.0f : (percent - fromPercent) / (toPercent - fromPercent);
00396
00397 if (calcMode() == CalcModeSpline) {
00398 ASSERT(m_keySplines.size() == m_keyPoints.size() - 1);
00399 keyPointPercent = calculatePercentForSpline(keyPointPercent, index);
00400 }
00401 return (toKeyPoint - fromKeyPoint) * keyPointPercent + fromKeyPoint;
00402 }
00403
00404 void SVGAnimationElement::currentValuesFromKeyPoints(float percent, float& effectivePercent, String& from, String& to) const
00405 {
00406 ASSERT(!m_keyPoints.isEmpty());
00407 ASSERT(m_keyPoints.size() == m_keyTimes.size());
00408 ASSERT(calcMode() != CalcModePaced);
00409 effectivePercent = calculatePercentFromKeyPoints(percent);
00410 unsigned index = effectivePercent == 1.0f ? m_values.size() - 2 : static_cast<unsigned>(effectivePercent * (m_values.size() - 1));
00411 from = m_values[index];
00412 to = m_values[index + 1];
00413 }
00414
00415 void SVGAnimationElement::currentValuesForValuesAnimation(float percent, float& effectivePercent, String& from, String& to) const
00416 {
00417 unsigned valuesCount = m_values.size();
00418 ASSERT(m_animationValid);
00419 ASSERT(valuesCount > 1);
00420
00421 CalcMode calcMode = this->calcMode();
00422 if (!m_keyPoints.isEmpty() && calcMode != CalcModePaced)
00423 return currentValuesFromKeyPoints(percent, effectivePercent, from, to);
00424
00425 unsigned keyTimesCount = m_keyTimes.size();
00426 ASSERT(!keyTimesCount || valuesCount == keyTimesCount);
00427 ASSERT(!keyTimesCount || (keyTimesCount > 1 && m_keyTimes[0] == 0));
00428
00429 unsigned index;
00430 for (index = 1; index < keyTimesCount; ++index) {
00431 if (m_keyTimes[index] >= percent)
00432 break;
00433 }
00434 --index;
00435
00436 if (calcMode == CalcModeDiscrete) {
00437 if (!keyTimesCount)
00438 index = percent == 1.0f ? valuesCount - 1 : static_cast<unsigned>(percent * valuesCount);
00439 from = m_values[index];
00440 to = m_values[index];
00441 effectivePercent = 0.0f;
00442 return;
00443 }
00444
00445 float fromPercent;
00446 float toPercent;
00447 if (keyTimesCount) {
00448 fromPercent = m_keyTimes[index];
00449 toPercent = m_keyTimes[index + 1];
00450 } else {
00451 index = static_cast<unsigned>(percent * (valuesCount - 1));
00452 fromPercent = static_cast<float>(index) / (valuesCount - 1);
00453 toPercent = static_cast<float>(index + 1) / (valuesCount - 1);
00454 }
00455
00456 if (index == valuesCount - 1)
00457 --index;
00458 from = m_values[index];
00459 to = m_values[index + 1];
00460 ASSERT(toPercent > fromPercent);
00461 effectivePercent = percent == 1.0f ? 1.0f : (percent - fromPercent) / (toPercent - fromPercent);
00462
00463 if (calcMode == CalcModeSpline) {
00464 ASSERT(m_keySplines.size() == m_values.size() - 1);
00465 effectivePercent = calculatePercentForSpline(effectivePercent, index);
00466 }
00467 }
00468
00469 void SVGAnimationElement::startedActiveInterval()
00470 {
00471 m_animationValid = false;
00472
00473 if (!hasValidTarget())
00474 return;
00475
00476 AnimationMode animationMode = this->animationMode();
00477 if (animationMode == NoAnimation)
00478 return;
00479 if (animationMode == FromToAnimation)
00480 m_animationValid = calculateFromAndToValues(fromValue(), toValue());
00481 else if (animationMode == ToAnimation) {
00482
00483
00484 m_animationValid = calculateFromAndToValues(String(), toValue());
00485 } else if (animationMode == FromByAnimation)
00486 m_animationValid = calculateFromAndByValues(fromValue(), byValue());
00487 else if (animationMode == ByAnimation)
00488 m_animationValid = calculateFromAndByValues(String(), byValue());
00489 else if (animationMode == ValuesAnimation) {
00490 CalcMode calcMode = this->calcMode();
00491 m_animationValid = m_values.size() > 1
00492 && (calcMode == CalcModePaced || !hasAttribute(SVGNames::keyTimesAttr) || hasAttribute(SVGNames::keyPointsAttr) || (m_values.size() == m_keyTimes.size()))
00493 && (calcMode == CalcModeDiscrete || !m_keyTimes.size() || m_keyTimes.last() == 1.0)
00494 && (calcMode != CalcModeSpline || (m_keySplines.size() && (m_keySplines.size() == m_values.size() - 1) || m_keySplines.size() == m_keyPoints.size() - 1))
00495 && (!hasAttribute(SVGNames::keyPointsAttr) || (m_keyTimes.size() > 1 && m_keyTimes.size() == m_keyPoints.size()));
00496 if (calcMode == CalcModePaced && m_animationValid)
00497 calculateKeyTimesForCalcModePaced();
00498 } else if (animationMode == PathAnimation)
00499 m_animationValid = calcMode() == CalcModePaced || !hasAttribute(SVGNames::keyPointsAttr) || (m_keyTimes.size() > 1 && m_keyTimes.size() == m_keyPoints.size());
00500 }
00501
00502 void SVGAnimationElement::updateAnimation(float percent, unsigned repeat, SVGSMILElement* resultElement)
00503 {
00504 if (!m_animationValid)
00505 return;
00506
00507 float effectivePercent;
00508 if (animationMode() == ValuesAnimation) {
00509 String from;
00510 String to;
00511 currentValuesForValuesAnimation(percent, effectivePercent, from, to);
00512 if (from != m_lastValuesAnimationFrom || to != m_lastValuesAnimationTo ) {
00513 m_animationValid = calculateFromAndToValues(from, to);
00514 if (!m_animationValid)
00515 return;
00516 m_lastValuesAnimationFrom = from;
00517 m_lastValuesAnimationTo = to;
00518 }
00519 } else if (!m_keyPoints.isEmpty() && calcMode() != CalcModePaced)
00520 effectivePercent = calculatePercentFromKeyPoints(percent);
00521 else
00522 effectivePercent = percent;
00523
00524 calculateAnimatedValue(effectivePercent, repeat, resultElement);
00525 }
00526
00527 void SVGAnimationElement::endedActiveInterval()
00528 {
00529 }
00530
00531 }
00532
00533
00534 #endif // ENABLE(SVG_ANIMATION)
00535