26 #if ENABLE(SVG_ANIMATION)
29 #include "CSSComputedStyleDeclaration.h"
30 #include "CSSParser.h"
31 #include "CSSPropertyNames.h"
34 #include "EventListener.h"
36 #include "HTMLNames.h"
48 SVGAnimationElement::SVGAnimationElement(
const QualifiedName& tagName, Document* doc)
49 : SVGSMILElement(tagName, doc)
51 , SVGExternalResourcesRequired()
52 , m_animationValid(false)
56 SVGAnimationElement::~SVGAnimationElement()
60 static void parseKeyTimes(
const String& parse, Vector<float>& result,
bool verifyOrder)
63 Vector<String> parseList;
64 parse.split(
';', parseList);
65 for (
unsigned n = 0; n < parseList.size(); ++n) {
66 String timeString = parseList[n];
68 float time = timeString.toFloat(&ok);
69 if (!ok || time < 0 || time > 1.f)
75 }
else if (time < result.last())
85 static void parseKeySplines(
const String& parse, Vector<UnitBezier>& result)
88 Vector<String> parseList;
89 parse.split(
';', parseList);
90 for (
unsigned n = 0; n < parseList.size(); ++n) {
91 Vector<String> parseSpline;
92 parseList[n].split(
',', parseSpline);
94 if (parseSpline.size() == 1)
95 parseList[n].split(
' ', parseSpline);
96 if (parseSpline.size() != 4)
98 double curveValues[4];
99 for (
unsigned i = 0; i < 4; ++i) {
100 String parseNumber = parseSpline[i];
102 curveValues[i] = parseNumber.toDouble(&ok);
103 if (!ok || curveValues[i] < 0.0 || curveValues[i] > 1.0)
106 result.append(UnitBezier(curveValues[0], curveValues[1], curveValues[2], curveValues[3]));
113 void SVGAnimationElement::parseMappedAttribute(MappedAttribute* attr)
116 attr->value().string().split(
';', m_values);
118 parseKeyTimes(attr->value(), m_keyTimes,
true);
122 parseKeyTimes(attr->value(), m_keyPoints,
false);
124 parseKeySplines(attr->value(), m_keySplines);
126 if (SVGTests::parseMappedAttribute(attr))
128 if (SVGExternalResourcesRequired::parseMappedAttribute(attr))
130 SVGSMILElement::parseMappedAttribute(attr);
134 void SVGAnimationElement::attributeChanged(Attribute* attr,
bool preserveDecls)
137 m_animationValid =
false;
138 SVGSMILElement::attributeChanged(attr, preserveDecls);
141 float SVGAnimationElement::getStartTime()
const
146 float SVGAnimationElement::getCurrentTime()
const
151 float SVGAnimationElement::getSimpleDuration(ExceptionCode&)
const
156 bool SVGAnimationElement::beginElement(ExceptionCode& ec)
158 return beginElementAt(0, ec);
161 bool SVGAnimationElement::beginElementAt(
float offset, ExceptionCode& ec)
163 addBeginTime(elapsed() + offset);
167 bool SVGAnimationElement::endElement(ExceptionCode& ec)
169 return endElementAt(0, ec);
172 bool SVGAnimationElement::endElementAt(
float offset, ExceptionCode& ec)
177 addEndTime(elapsed() + offset);
181 SVGAnimationElement::AnimationMode SVGAnimationElement::animationMode()
const
186 if (!animationPath().isEmpty())
187 return PathAnimation;
189 return ValuesAnimation;
190 if (!toValue().isEmpty())
191 return fromValue().isEmpty() ? ToAnimation : FromToAnimation;
192 if (!byValue().isEmpty())
193 return fromValue().isEmpty() ? ByAnimation : FromByAnimation;
197 SVGAnimationElement::CalcMode SVGAnimationElement::calcMode()
const
199 static const AtomicString discrete(
"discrete");
200 static const AtomicString linear(
"linear");
201 static const AtomicString paced(
"paced");
202 static const AtomicString spline(
"spline");
204 if (value == discrete)
205 return CalcModeDiscrete;
207 return CalcModeLinear;
209 return CalcModePaced;
211 return CalcModeSpline;
215 SVGAnimationElement::AttributeType SVGAnimationElement::attributeType()
const
217 static const AtomicString css(
"CSS");
218 static const AtomicString xml(
"XML");
221 return AttributeTypeCSS;
223 return AttributeTypeXML;
224 return AttributeTypeAuto;
227 String SVGAnimationElement::toValue()
const
232 String SVGAnimationElement::byValue()
const
237 String SVGAnimationElement::fromValue()
const
242 bool SVGAnimationElement::isAdditive()
const
244 static const AtomicString sum(
"sum");
246 return value == sum || animationMode() == ByAnimation;
249 bool SVGAnimationElement::isAccumulated()
const
251 static const AtomicString sum(
"sum");
253 return value == sum && animationMode() != ToAnimation;
256 bool SVGAnimationElement::hasValidTarget()
const
258 return targetElement();
261 bool SVGAnimationElement::attributeIsCSS(
const String& attributeName)
265 unsigned id = cssPropertyID(attributeName);
267 if (
id >= CSSPropertyClipPath &&
id <= CSSPropertyWritingMode)
270 return id == CSSPropertyColor ||
id == CSSPropertyDisplay ||
id == CSSPropertyOpacity
271 || (
id >= CSSPropertyFont &&
id <= CSSPropertyFontWeight)
272 ||
id == CSSPropertyOverflow ||
id == CSSPropertyVisibility;
275 bool SVGAnimationElement::targetAttributeIsCSS()
const
277 AttributeType type = attributeType();
278 if (type == AttributeTypeCSS)
280 if (type == AttributeTypeXML)
282 return attributeIsCSS(attributeName());
285 void SVGAnimationElement::setTargetAttributeAnimatedValue(
const String& value)
287 if (!hasValidTarget())
289 SVGElement* target = targetElement();
290 String attributeName = this->attributeName();
291 if (!target || attributeName.isEmpty() || value.isNull())
295 if (target->isStyled())
296 static_cast<SVGStyledElement*>(target)->setInstanceUpdatesBlocked(
true);
299 bool isCSS = targetAttributeIsCSS();
303 target->style()->setProperty(attributeName, value,
"", ec);
307 target->setAttribute(attributeName, value, ec);
310 if (target->isStyled())
311 static_cast<SVGStyledElement*>(target)->setInstanceUpdatesBlocked(
false);
314 HashSet<SVGElementInstance*>* instances = document()->accessSVGExtensions()->instancesForElement(target);
317 HashSet<SVGElementInstance*>::iterator
end = instances->end();
318 for (HashSet<SVGElementInstance*>::iterator it = instances->begin(); it != end; ++it) {
319 SVGElement* shadowTreeElement = (*it)->shadowTreeElement();
320 ASSERT(shadowTreeElement);
322 shadowTreeElement->style()->setProperty(attributeName, value,
"", ec);
324 shadowTreeElement->setAttribute(attributeName, value, ec);
325 (*it)->correspondingUseElement()->setChanged();
329 void SVGAnimationElement::calculateKeyTimesForCalcModePaced()
331 ASSERT(calcMode() == CalcModePaced);
332 ASSERT(animationMode() == ValuesAnimation);
334 unsigned valuesCount = m_values.size();
335 ASSERT(valuesCount > 1);
336 Vector<float> keyTimesForPaced;
337 float totalDistance = 0;
338 keyTimesForPaced.append(0);
339 for (
unsigned n = 0; n < valuesCount - 1; ++n) {
341 float distance = calculateDistance(m_values[n], m_values[n + 1]);
344 totalDistance += distance;
345 keyTimesForPaced.append(distance);
351 for (
unsigned n = 1; n < keyTimesForPaced.size() - 1; ++n)
352 keyTimesForPaced[n] = keyTimesForPaced[n - 1] + keyTimesForPaced[n] / totalDistance;
353 keyTimesForPaced[keyTimesForPaced.size() - 1] = 1.f;
356 m_keyTimes.swap(keyTimesForPaced);
359 static inline double solveEpsilon(
double duration) {
return 1. / (200. * duration); }
361 float SVGAnimationElement::calculatePercentForSpline(
float percent,
unsigned splineIndex)
const
363 ASSERT(calcMode() == CalcModeSpline);
364 ASSERT(splineIndex < m_keySplines.size());
365 UnitBezier bezier = m_keySplines[splineIndex];
366 SMILTime duration = simpleDuration();
367 if (!duration.isFinite())
372 float SVGAnimationElement::calculatePercentFromKeyPoints(
float percent)
const
374 ASSERT(!m_keyPoints.isEmpty());
375 ASSERT(calcMode() != CalcModePaced);
376 unsigned keyTimesCount = m_keyTimes.size();
377 ASSERT(keyTimesCount > 1);
378 ASSERT(m_keyPoints.size() == keyTimesCount);
381 for (index = 1; index < keyTimesCount; ++index) {
382 if (m_keyTimes[index] >= percent)
387 float fromPercent = m_keyTimes[index];
388 float toPercent = m_keyTimes[index + 1];
389 float fromKeyPoint = m_keyPoints[index];
390 float toKeyPoint = m_keyPoints[index + 1];
392 if (calcMode() == CalcModeDiscrete)
393 return percent == 1.0f ? toKeyPoint : fromKeyPoint;
395 float keyPointPercent = percent == 1.0f ? 1.0f : (percent - fromPercent) / (toPercent - fromPercent);
397 if (calcMode() == CalcModeSpline) {
398 ASSERT(m_keySplines.size() == m_keyPoints.size() - 1);
399 keyPointPercent = calculatePercentForSpline(keyPointPercent, index);
401 return (toKeyPoint - fromKeyPoint) * keyPointPercent + fromKeyPoint;
404 void SVGAnimationElement::currentValuesFromKeyPoints(
float percent,
float& effectivePercent,
String& from,
String& to)
const
406 ASSERT(!m_keyPoints.isEmpty());
407 ASSERT(m_keyPoints.size() == m_keyTimes.size());
408 ASSERT(calcMode() != CalcModePaced);
409 effectivePercent = calculatePercentFromKeyPoints(percent);
410 unsigned index = effectivePercent == 1.0f ? m_values.size() - 2 :
static_cast<unsigned>(effectivePercent * (m_values.size() - 1));
411 from = m_values[index];
412 to = m_values[index + 1];
415 void SVGAnimationElement::currentValuesForValuesAnimation(
float percent,
float& effectivePercent,
String& from,
String& to)
const
417 unsigned valuesCount = m_values.size();
418 ASSERT(m_animationValid);
419 ASSERT(valuesCount > 1);
421 CalcMode calcMode = this->calcMode();
422 if (!m_keyPoints.isEmpty() && calcMode != CalcModePaced)
423 return currentValuesFromKeyPoints(percent, effectivePercent, from, to);
425 unsigned keyTimesCount = m_keyTimes.size();
426 ASSERT(!keyTimesCount || valuesCount == keyTimesCount);
427 ASSERT(!keyTimesCount || (keyTimesCount > 1 && m_keyTimes[0] == 0));
430 for (index = 1; index < keyTimesCount; ++index) {
431 if (m_keyTimes[index] >= percent)
436 if (calcMode == CalcModeDiscrete) {
438 index = percent == 1.0f ? valuesCount - 1 :
static_cast<unsigned>(percent * valuesCount);
439 from = m_values[index];
440 to = m_values[index];
441 effectivePercent = 0.0f;
448 fromPercent = m_keyTimes[index];
449 toPercent = m_keyTimes[index + 1];
451 index =
static_cast<unsigned>(percent * (valuesCount - 1));
452 fromPercent =
static_cast<float>(index) / (valuesCount - 1);
453 toPercent =
static_cast<float>(index + 1) / (valuesCount - 1);
456 if (index == valuesCount - 1)
458 from = m_values[index];
459 to = m_values[index + 1];
460 ASSERT(toPercent > fromPercent);
461 effectivePercent = percent == 1.0f ? 1.0f : (percent - fromPercent) / (toPercent - fromPercent);
463 if (calcMode == CalcModeSpline) {
464 ASSERT(m_keySplines.size() == m_values.size() - 1);
465 effectivePercent = calculatePercentForSpline(effectivePercent, index);
469 void SVGAnimationElement::startedActiveInterval()
471 m_animationValid =
false;
473 if (!hasValidTarget())
476 AnimationMode animationMode = this->animationMode();
477 if (animationMode == NoAnimation)
479 if (animationMode == FromToAnimation)
480 m_animationValid = calculateFromAndToValues(fromValue(), toValue());
481 else if (animationMode == ToAnimation) {
484 m_animationValid = calculateFromAndToValues(
String(), toValue());
485 }
else if (animationMode == FromByAnimation)
486 m_animationValid = calculateFromAndByValues(fromValue(), byValue());
487 else if (animationMode == ByAnimation)
488 m_animationValid = calculateFromAndByValues(
String(), byValue());
489 else if (animationMode == ValuesAnimation) {
490 CalcMode calcMode = this->calcMode();
491 m_animationValid = m_values.size() > 1
493 && (calcMode == CalcModeDiscrete || !m_keyTimes.size() || m_keyTimes.last() == 1.0)
494 && (calcMode != CalcModeSpline || (m_keySplines.size() && (m_keySplines.size() == m_values.size() - 1) || m_keySplines.size() == m_keyPoints.size() - 1))
495 && (!hasAttribute(
SVGNames::keyPointsAttr) || (m_keyTimes.size() > 1 && m_keyTimes.size() == m_keyPoints.size()));
496 if (calcMode == CalcModePaced && m_animationValid)
497 calculateKeyTimesForCalcModePaced();
498 }
else if (animationMode == PathAnimation)
499 m_animationValid = calcMode() == CalcModePaced || !hasAttribute(
SVGNames::keyPointsAttr) || (m_keyTimes.size() > 1 && m_keyTimes.size() == m_keyPoints.size());
502 void SVGAnimationElement::updateAnimation(
float percent,
unsigned repeat, SVGSMILElement* resultElement)
504 if (!m_animationValid)
507 float effectivePercent;
508 if (animationMode() == ValuesAnimation) {
511 currentValuesForValuesAnimation(percent, effectivePercent, from, to);
512 if (from != m_lastValuesAnimationFrom || to != m_lastValuesAnimationTo ) {
513 m_animationValid = calculateFromAndToValues(from, to);
514 if (!m_animationValid)
516 m_lastValuesAnimationFrom = from;
517 m_lastValuesAnimationTo = to;
519 }
else if (!m_keyPoints.isEmpty() && calcMode() != CalcModePaced)
520 effectivePercent = calculatePercentFromKeyPoints(percent);
522 effectivePercent = percent;
524 calculateAnimatedValue(effectivePercent, repeat, resultElement);
527 void SVGAnimationElement::endedActiveInterval()
534 #endif // ENABLE(SVG_ANIMATION)
DOM::QualifiedName attributeTypeAttr
DOM::QualifiedName keyTimesAttr
DOM::QualifiedName keyPointsAttr
DOM::QualifiedName valuesAttr
DOM::QualifiedName fromAttr
float narrowPrecisionToFloat(T)
DOM::QualifiedName calcModeAttr
DOM::QualifiedName setTag
DOM::QualifiedName toAttr
DOM::QualifiedName byAttr
DOM::QualifiedName additiveAttr
DOM::QualifiedName accumulateAttr
DOM::QualifiedName keySplinesAttr
DOM::QualifiedName animateMotionTag