diff options
author | Upstream <upstream-import@none> | 1970-01-12 13:46:40 +0000 |
---|---|---|
committer | Upstream <upstream-import@none> | 1970-01-12 13:46:40 +0000 |
commit | d8543bb6618c17b12da906afa77d216f58cf4058 (patch) | |
tree | c58dc05ed86825bd0ef8d305d58c8205106b540f /WebCore/svg/SVGAnimationElement.cpp | |
download | external_webkit-d8543bb6618c17b12da906afa77d216f58cf4058.zip external_webkit-d8543bb6618c17b12da906afa77d216f58cf4058.tar.gz external_webkit-d8543bb6618c17b12da906afa77d216f58cf4058.tar.bz2 |
external/webkit r30707
Diffstat (limited to 'WebCore/svg/SVGAnimationElement.cpp')
-rw-r--r-- | WebCore/svg/SVGAnimationElement.cpp | 747 |
1 files changed, 747 insertions, 0 deletions
diff --git a/WebCore/svg/SVGAnimationElement.cpp b/WebCore/svg/SVGAnimationElement.cpp new file mode 100644 index 0000000..3574306 --- /dev/null +++ b/WebCore/svg/SVGAnimationElement.cpp @@ -0,0 +1,747 @@ +/* + Copyright (C) 2004, 2005 Nikolas Zimmermann <wildfox@kde.org> + 2004, 2005, 2006, 2007 Rob Buis <buis@kde.org> + Copyright (C) 2007 Eric Seidel <eric@webkit.org> + + This file is part of the KDE project + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "config.h" +#if ENABLE(SVG) +#include "SVGAnimationElement.h" + +#include "CSSPropertyNames.h" +#include "Document.h" +#include "FloatConversion.h" +#include "SVGParserUtilities.h" +#include "SVGSVGElement.h" +#include "SVGURIReference.h" +#include "TimeScheduler.h" +#include "XLinkNames.h" +#include <float.h> +#include <math.h> +#include <wtf/MathExtras.h> +#include <wtf/Vector.h> + +using namespace std; + +namespace WebCore { + +SVGAnimationElement::SVGAnimationElement(const QualifiedName& tagName, Document* doc) + : SVGElement(tagName, doc) + , SVGTests() + , SVGExternalResourcesRequired() + , m_targetElement(0) + , m_connectedToTimer(false) + , m_currentTime(0.0) + , m_simpleDuration(0.0) + , m_fill(FILL_REMOVE) + , m_restart(RESTART_ALWAYS) + , m_calcMode(CALCMODE_LINEAR) + , m_additive(ADDITIVE_REPLACE) + , m_accumulate(ACCUMULATE_NONE) + , m_attributeType(ATTRIBUTETYPE_AUTO) + , m_max(0.0) + , m_min(0.0) + , m_end(0.0) + , m_begin(0.0) + , m_repetitions(0) + , m_repeatCount(0) +{ + +} + +SVGAnimationElement::~SVGAnimationElement() +{ +} + +bool SVGAnimationElement::hasValidTarget() const +{ + return targetElement(); +} + +SVGElement* SVGAnimationElement::targetElement() const +{ + if (!m_targetElement) { + Node *target = 0; + if (!m_href.isEmpty()) { + target = document()->getElementById(SVGURIReference::getTarget(m_href)); + } else if (parentNode()) { + // TODO : do we really need to skip non element nodes? Can that happen at all? + target = parentNode(); + while (target) { + if (target->nodeType() != ELEMENT_NODE) + target = target->parentNode(); + else + break; + } + } + if (target && target->isSVGElement()) + m_targetElement = static_cast<SVGElement*>(target); + } + + return m_targetElement; +} + +float SVGAnimationElement::getEndTime() const +{ + return narrowPrecisionToFloat(m_end); +} + +float SVGAnimationElement::getStartTime() const +{ + return narrowPrecisionToFloat(m_begin); +} + +float SVGAnimationElement::getCurrentTime() const +{ + return narrowPrecisionToFloat(m_currentTime); +} + +float SVGAnimationElement::getSimpleDuration(ExceptionCode&) const +{ + return narrowPrecisionToFloat(m_simpleDuration); +} + +void SVGAnimationElement::parseKeyNumbers(Vector<float>& keyNumbers, const String& value) +{ + float number = 0.0f; + + const UChar* ptr = value.characters(); + const UChar* end = ptr + value.length(); + skipOptionalSpaces(ptr, end); + while (ptr < end) { + if (!parseNumber(ptr, end, number, false)) + return; + keyNumbers.append(number); + + if (!skipOptionalSpaces(ptr, end) || *ptr != ';') + return; + ptr++; + skipOptionalSpaces(ptr, end); + } +} + +static void parseKeySplines(Vector<SVGAnimationElement::KeySpline>& keySplines, const String& value) +{ + float number = 0.0f; + SVGAnimationElement::KeySpline keySpline; + + const UChar* ptr = value.characters(); + const UChar* end = ptr + value.length(); + skipOptionalSpaces(ptr, end); + while (ptr < end) { + if (!(parseNumber(ptr, end, number, false) && skipOptionalSpaces(ptr, end))) + return; + keySpline.control1.setX(number); + if (!(parseNumber(ptr, end, number, false) && skipOptionalSpaces(ptr, end))) + return; + keySpline.control1.setY(number); + if (!(parseNumber(ptr, end, number, false) && skipOptionalSpaces(ptr, end))) + return; + keySpline.control2.setX(number); + if (!parseNumber(ptr, end, number, false)) + return; + keySpline.control2.setY(number); + keySplines.append(keySpline); + + if (!skipOptionalSpaces(ptr, end) || *ptr != ';') + return; + ptr++; + skipOptionalSpaces(ptr, end); + } +} + +void SVGAnimationElement::parseBeginOrEndValue(double& number, const String& value) +{ + // TODO: Don't use SVGStringList for parsing. + AtomicString dummy; + RefPtr<SVGStringList> valueList = SVGStringList::create(QualifiedName(dummy, dummy, dummy)); + valueList->parse(value, ';'); + + ExceptionCode ec = 0; + for (unsigned int i = 0; i < valueList->numberOfItems(); i++) { + String current = valueList->getItem(i, ec); + + if (current.startsWith("accessKey")) { + // Register keyDownEventListener for the character + String character = current.substring(current.length() - 2, 1); + // FIXME: Not implemented! Supposed to register accessKey character + } else if (current.startsWith("wallclock")) { + int firstBrace = current.find('('); + int secondBrace = current.find(')'); + + String wallclockValue = current.substring(firstBrace + 1, secondBrace - firstBrace - 2); + // FIXME: Not implemented! Supposed to use wallClock value + } else if (current.contains('.')) { + int dotPosition = current.find('.'); + + String element = current.substring(0, dotPosition); + String clockValue; + if (current.contains("begin")) + clockValue = current.substring(dotPosition + 6); + else if (current.contains("end")) + clockValue = current.substring(dotPosition + 4); + else if (current.contains("repeat")) + clockValue = current.substring(dotPosition + 7); + else { // DOM2 Event Reference + int plusMinusPosition = -1; + + if (current.contains('+')) + plusMinusPosition = current.find('+'); + else if (current.contains('-')) + plusMinusPosition = current.find('-'); + + String event = current.substring(dotPosition + 1, plusMinusPosition - dotPosition - 1); + clockValue = current.substring(dotPosition + event.length() + 1); + // FIXME: supposed to use DOM Event + } + } else { + number = parseClockValue(current); + if (!isIndefinite(number)) + number *= 1000.0; + // FIXME: supposed to set begin/end time + } + } +} + +void SVGAnimationElement::parseMappedAttribute(MappedAttribute* attr) +{ + if (attr->name().matches(XLinkNames::hrefAttr)) + m_href = attr->value(); + else if (attr->name() == SVGNames::attributeNameAttr) + m_attributeName = attr->value(); + else if (attr->name() == SVGNames::attributeTypeAttr) { + if (attr->value() == "CSS") + m_attributeType = ATTRIBUTETYPE_CSS; + else if (attr->value() == "XML") + m_attributeType = ATTRIBUTETYPE_XML; + else if (attr->value() == "auto") + m_attributeType = ATTRIBUTETYPE_AUTO; + } else if (attr->name() == SVGNames::beginAttr) + parseBeginOrEndValue(m_begin, attr->value()); + else if (attr->name() == SVGNames::endAttr) + parseBeginOrEndValue(m_end, attr->value()); + else if (attr->name() == SVGNames::durAttr) { + m_simpleDuration = parseClockValue(attr->value()); + if (!isIndefinite(m_simpleDuration)) + m_simpleDuration *= 1000.0; + } else if (attr->name() == SVGNames::minAttr) { + m_min = parseClockValue(attr->value()); + if (!isIndefinite(m_min)) + m_min *= 1000.0; + } else if (attr->name() == SVGNames::maxAttr) { + m_max = parseClockValue(attr->value()); + if (!isIndefinite(m_max)) + m_max *= 1000.0; + } else if (attr->name() == SVGNames::restartAttr) { + if (attr->value() == "whenNotActive") + m_restart = RESTART_WHENNOTACTIVE; + else if (attr->value() == "never") + m_restart = RESTART_NEVER; + else if (attr->value() == "always") + m_restart = RESTART_ALWAYS; + } else if (attr->name() == SVGNames::repeatCountAttr) { + if (attr->value() == "indefinite") + m_repeatCount = DBL_MAX; + else + m_repeatCount = attr->value().toDouble(); + } else if (attr->name() == SVGNames::repeatDurAttr) + m_repeatDur = attr->value(); + else if (attr->name() == SVGNames::fillAttr) { + if (attr->value() == "freeze") + m_fill = FILL_FREEZE; + else if (attr->value() == "remove") + m_fill = FILL_REMOVE; + } else if (attr->name() == SVGNames::calcModeAttr) { + if (attr->value() == "discrete") + m_calcMode = CALCMODE_DISCRETE; + else if (attr->value() == "linear") + m_calcMode = CALCMODE_LINEAR; + else if (attr->value() == "spline") + m_calcMode = CALCMODE_SPLINE; + else if (attr->value() == "paced") + m_calcMode = CALCMODE_PACED; + } else if (attr->name() == SVGNames::valuesAttr) { + m_values.clear(); + m_values = parseDelimitedString(attr->value(), ';'); + } else if (attr->name() == SVGNames::keyTimesAttr) { + m_keyTimes.clear(); + parseKeyNumbers(m_keyTimes, attr->value()); + } else if (attr->name() == SVGNames::keySplinesAttr) { + m_keySplines.clear(); + parseKeySplines(m_keySplines, attr->value()); + } else if (attr->name() == SVGNames::fromAttr) + m_from = attr->value(); + else if (attr->name() == SVGNames::toAttr) + m_to = attr->value(); + else if (attr->name() == SVGNames::byAttr) + m_by = attr->value(); + else if (attr->name() == SVGNames::additiveAttr) { + if (attr->value() == "sum") + m_additive = ADDITIVE_SUM; + else if (attr->value() == "replace") + m_additive = ADDITIVE_REPLACE; + } else if (attr->name() == SVGNames::accumulateAttr) { + if (attr->value() == "sum") + m_accumulate = ACCUMULATE_SUM; + else if (attr->value() == "none") + m_accumulate = ACCUMULATE_NONE; + } else { + if (SVGTests::parseMappedAttribute(attr)) + return; + if (SVGExternalResourcesRequired::parseMappedAttribute(attr)) + return; + SVGElement::parseMappedAttribute(attr); + } +} + +double SVGAnimationElement::parseClockValue(const String& data) +{ + String parse = data.stripWhiteSpace(); + + if (parse == "indefinite") + return DBL_MAX; + + double result; + + int doublePointOne = parse.find(':'); + int doublePointTwo = parse.find(':', doublePointOne + 1); + + if (doublePointOne != -1 && doublePointTwo != -1) { // Spec: "Full clock values" + unsigned hours = parse.substring(0, 2).toUIntStrict(); + unsigned minutes = parse.substring(3, 2).toUIntStrict(); + unsigned seconds = parse.substring(6, 2).toUIntStrict(); + unsigned milliseconds = 0; + + result = (3600 * hours) + (60 * minutes) + seconds; + + if (parse.find('.') != -1) { + String temp = parse.substring(9, 2); + milliseconds = temp.toUIntStrict(); + result += (milliseconds * (1 / pow(10.0, int(temp.length())))); + } + } else if (doublePointOne != -1 && doublePointTwo == -1) { // Spec: "Partial clock values" + unsigned minutes = parse.substring(0, 2).toUIntStrict(); + unsigned seconds = parse.substring(3, 2).toUIntStrict(); + unsigned milliseconds = 0; + + result = (60 * minutes) + seconds; + + if (parse.find('.') != -1) { + String temp = parse.substring(6, 2); + milliseconds = temp.toUIntStrict(); + result += (milliseconds * (1 / pow(10.0, int(temp.length())))); + } + } else { // Spec: "Timecount values" + int dotPosition = parse.find('.'); + + if (parse.endsWith("h")) { + if (dotPosition == -1) + result = parse.substring(0, parse.length() - 1).toUIntStrict() * 3600; + else { + result = parse.substring(0, dotPosition).toUIntStrict() * 3600; + String temp = parse.substring(dotPosition + 1, parse.length() - dotPosition - 2); + result += (3600.0 * temp.toUIntStrict()) * (1 / pow(10.0, int(temp.length()))); + } + } else if (parse.endsWith("min")) { + if (dotPosition == -1) + result = parse.substring(0, parse.length() - 3).toUIntStrict() * 60; + else { + result = parse.substring(0, dotPosition).toUIntStrict() * 60; + String temp = parse.substring(dotPosition + 1, parse.length() - dotPosition - 4); + result += (60.0 * temp.toUIntStrict()) * (1 / pow(10.0, int(temp.length()))); + } + } else if (parse.endsWith("ms")) { + if (dotPosition == -1) + result = parse.substring(0, parse.length() - 2).toUIntStrict() / 1000.0; + else { + result = parse.substring(0, dotPosition).toUIntStrict() / 1000.0; + String temp = parse.substring(dotPosition + 1, parse.length() - dotPosition - 3); + result += (temp.toUIntStrict() / 1000.0) * (1 / pow(10.0, int(temp.length()))); + } + } else if (parse.endsWith("s")) { + if (dotPosition == -1) + result = parse.substring(0, parse.length() - 1).toUIntStrict(); + else { + result = parse.substring(0, dotPosition).toUIntStrict(); + String temp = parse.substring(dotPosition + 1, parse.length() - dotPosition - 2); + result += temp.toUIntStrict() * (1 / pow(10.0, int(temp.length()))); + } + } else + result = parse.toDouble(); + } + + return result; +} + +void SVGAnimationElement::finishParsingChildren() +{ + if (ownerSVGElement()) + ownerSVGElement()->timeScheduler()->addTimer(this, lround(getStartTime())); + SVGElement::finishParsingChildren(); +} + +String SVGAnimationElement::targetAttributeAnimatedValue() const +{ + // FIXME: This method is not entirely correct + // It does not properly grab the true "animVal" instead grabs the baseVal (or something very close) + + if (!targetElement()) + return String(); + + SVGElement* target = targetElement(); + SVGStyledElement* styled = 0; + if (target && target->isStyled()) + styled = static_cast<SVGStyledElement*>(target); + + String ret; + + EAttributeType attributeType = static_cast<EAttributeType>(m_attributeType); + if (attributeType == ATTRIBUTETYPE_AUTO) { + attributeType = ATTRIBUTETYPE_XML; + + // Spec: The implementation should match the attributeName to an attribute + // for the target element. The implementation must first search through the + // list of CSS properties for a matching property name, and if none is found, + // search the default XML namespace for the element. + if (styled && styled->style()) { + if (styled->style()->getPropertyCSSValue(m_attributeName)) + attributeType = ATTRIBUTETYPE_CSS; + } + } + + if (attributeType == ATTRIBUTETYPE_CSS) { + if (styled && styled->style()) + ret = styled->style()->getPropertyValue(m_attributeName); + } + + if (attributeType == ATTRIBUTETYPE_XML || ret.isEmpty()) + ret = targetElement()->getAttribute(m_attributeName); + + return ret; +} + +void SVGAnimationElement::setTargetAttributeAnimatedValue(const String& value) +{ + // FIXME: This method is not entirely correct + // It does not properly set the "animVal", rather it sets the "baseVal" + SVGAnimationElement::setTargetAttribute(targetElement(), m_attributeName, value, static_cast<EAttributeType>(m_attributeType)); +} + +void SVGAnimationElement::setTargetAttribute(SVGElement* target, const String& name, const String& value, EAttributeType type) +{ + if (!target || name.isNull() || value.isNull()) + return; + + SVGStyledElement* styled = (target && target->isStyled()) ? static_cast<SVGStyledElement*>(target) : 0; + + EAttributeType attributeType = type; + if (type == ATTRIBUTETYPE_AUTO) { + // Spec: The implementation should match the attributeName to an attribute + // for the target element. The implementation must first search through the + // list of CSS properties for a matching property name, and if none is found, + // search the default XML namespace for the element. + if (styled && styled->style() && styled->style()->getPropertyCSSValue(name)) + attributeType = ATTRIBUTETYPE_CSS; + else + attributeType = ATTRIBUTETYPE_XML; + } + ExceptionCode ec = 0; + if (attributeType == ATTRIBUTETYPE_CSS && styled && styled->style()) + styled->style()->setProperty(name, value, "", ec); + else if (attributeType == ATTRIBUTETYPE_XML) + target->setAttribute(name, value, ec); +} + +String SVGAnimationElement::attributeName() const +{ + return m_attributeName; +} + +bool SVGAnimationElement::connectedToTimer() const +{ + return m_connectedToTimer; +} + +bool SVGAnimationElement::isFrozen() const +{ + return (m_fill == FILL_FREEZE); +} + +bool SVGAnimationElement::isAdditive() const +{ + return (m_additive == ADDITIVE_SUM) || (detectAnimationMode() == BY_ANIMATION); +} + +bool SVGAnimationElement::isAccumulated() const +{ + return (m_accumulate == ACCUMULATE_SUM) && (detectAnimationMode() != TO_ANIMATION); +} + +EAnimationMode SVGAnimationElement::detectAnimationMode() const +{ + if (hasAttribute(SVGNames::valuesAttr)) + return VALUES_ANIMATION; + else if ((!m_from.isEmpty() && !m_to.isEmpty()) || (!m_to.isEmpty())) { // to/from-to animation + if (!m_from.isEmpty()) // from-to animation + return FROM_TO_ANIMATION; + else + return TO_ANIMATION; + } else if ((m_from.isEmpty() && m_to.isEmpty() && !m_by.isEmpty()) || + (!m_from.isEmpty() && !m_by.isEmpty())) { // by/from-by animation + if (!m_from.isEmpty()) // from-by animation + return FROM_BY_ANIMATION; + else + return BY_ANIMATION; + } + + return NO_ANIMATION; +} + +double SVGAnimationElement::repeations() const +{ + return m_repetitions; +} + +bool SVGAnimationElement::isIndefinite(double value) +{ + return (value == DBL_MAX); +} + +void SVGAnimationElement::connectTimer() +{ + ASSERT(!m_connectedToTimer); + ownerSVGElement()->timeScheduler()->connectIntervalTimer(this); + m_connectedToTimer = true; +} + +void SVGAnimationElement::disconnectTimer() +{ + ASSERT(m_connectedToTimer); + ownerSVGElement()->timeScheduler()->disconnectIntervalTimer(this); + m_connectedToTimer = false; +} + +static double calculateTimePercentage(double elapsedSeconds, double start, double end, double duration, double repetitions) +{ + double percentage = 0.0; + double useElapsed = elapsedSeconds - (duration * repetitions); + + if (duration > 0.0 && end == 0.0) + percentage = 1.0 - (((start + duration) - useElapsed) / duration); + else if (duration > 0.0 && end != 0.0) { + if (duration > end) + percentage = 1.0 - (((start + end) - useElapsed) / end); + else + percentage = 1.0 - (((start + duration) - useElapsed) / duration); + } else if (duration == 0.0 && end != 0.0) + percentage = 1.0 - (((start + end) - useElapsed) / end); + + return percentage; +} + +static inline void adjustPercentagePastForKeySplines(const Vector<SVGAnimationElement::KeySpline>& keySplines, unsigned valueIndex, float& percentagePast) +{ + if (percentagePast == 0.0f) // values at key times need no spline adjustment + return; + const SVGAnimationElement::KeySpline& keySpline = keySplines[valueIndex]; + Path path; + path.moveTo(FloatPoint()); + path.addBezierCurveTo(keySpline.control1, keySpline.control2, FloatPoint(1.0f, 1.0f)); + // FIXME: This needs to use a y-at-x function on path, to compute the y value then multiply percentagePast by that value +} + +void SVGAnimationElement::valueIndexAndPercentagePastForDistance(float distancePercentage, unsigned& valueIndex, float& percentagePast) +{ + // Unspecified: animation elements which do not support CALCMODE_PACED, we just always show the first value + valueIndex = 0; + percentagePast = 0; +} + +float SVGAnimationElement::calculateTotalDistance() +{ + return 0; +} + +static inline void caculateValueIndexForKeyTimes(float timePercentage, const Vector<float>& keyTimes, unsigned& valueIndex, float& lastKeyTime, float& nextKeyTime) +{ + unsigned keyTimesCountMinusOne = keyTimes.size() - 1; + valueIndex = 0; + ASSERT(timePercentage >= keyTimes.first()); + while ((valueIndex < keyTimesCountMinusOne) && (timePercentage >= keyTimes[valueIndex + 1])) + valueIndex++; + + lastKeyTime = keyTimes[valueIndex]; + if (valueIndex < keyTimesCountMinusOne) + nextKeyTime = keyTimes[valueIndex + 1]; + else + nextKeyTime = lastKeyTime; +} + +bool SVGAnimationElement::isValidAnimation() const +{ + EAnimationMode animationMode = detectAnimationMode(); + if (!hasValidTarget() || (animationMode == NO_ANIMATION)) + return false; + if (animationMode == VALUES_ANIMATION) { + if (!m_values.size()) + return false; + if (m_keyTimes.size()) { + if ((m_values.size() != m_keyTimes.size()) || (m_keyTimes.first() != 0)) + return false; + if (((m_calcMode == CALCMODE_SPLINE) || (m_calcMode == CALCMODE_LINEAR)) && (m_keyTimes.last() != 1)) + return false; + float lastKeyTime = 0; + for (unsigned x = 0; x < m_keyTimes.size(); x++) { + if (m_keyTimes[x] < lastKeyTime || m_keyTimes[x] > 1) + return false; + } + } + if (m_keySplines.size()) { + if ((m_values.size() - 1) != m_keySplines.size()) + return false; + for (unsigned x = 0; x < m_keyTimes.size(); x++) + if (m_keyTimes[x] < 0 || m_keyTimes[x] > 1) + return false; + } + } + return true; +} + +void SVGAnimationElement::calculateValueIndexAndPercentagePast(float timePercentage, unsigned& valueIndex, float& percentagePast) +{ + ASSERT(timePercentage <= 1.0f); + ASSERT(isValidAnimation()); + EAnimationMode animationMode = detectAnimationMode(); + + // to-animations have their own special handling + if (animationMode == TO_ANIMATION) + return; + + // paced is special, caculates values based on distance instead of time + if (m_calcMode == CALCMODE_PACED) { + float totalDistance = calculateTotalDistance(); + float distancePercentage = totalDistance * timePercentage; + valueIndexAndPercentagePastForDistance(distancePercentage, valueIndex, percentagePast); + return; + } + + // Figure out what our current index is based on on time + // all further calculations are based on time percentages, to allow unifying keyTimes handling & normal animation + float lastKeyTimePercentage = 0; + float nextKeyTimePercentage = 0; + if (m_keyTimes.size() && (m_keyTimes.size() == m_values.size())) + caculateValueIndexForKeyTimes(timePercentage, m_keyTimes, valueIndex, lastKeyTimePercentage, nextKeyTimePercentage); + else { + unsigned lastPossibleIndex = (m_values.size() ? m_values.size() - 1: 1); + unsigned flooredValueIndex = static_cast<unsigned>(timePercentage * lastPossibleIndex); + valueIndex = flooredValueIndex; + lastKeyTimePercentage = flooredValueIndex / (float)lastPossibleIndex; + nextKeyTimePercentage = (flooredValueIndex + 1) / (float)lastPossibleIndex; + } + + // No further caculation is needed if we're exactly on an index. + if (timePercentage == lastKeyTimePercentage || lastKeyTimePercentage == nextKeyTimePercentage) { + percentagePast = 0.0f; + return; + } + + // otherwise we decide what percent after that index + if ((m_calcMode == CALCMODE_SPLINE) && (m_keySplines.size() == (m_values.size() - 1))) + adjustPercentagePastForKeySplines(m_keySplines, valueIndex, percentagePast); + else if (m_calcMode == CALCMODE_DISCRETE) + percentagePast = 0.0f; + else { // default (and fallback) mode: linear + float keyTimeSpan = nextKeyTimePercentage - lastKeyTimePercentage; + float timeSinceLastKeyTime = timePercentage - lastKeyTimePercentage; + percentagePast = (timeSinceLastKeyTime / keyTimeSpan); + } +} + +bool SVGAnimationElement::updateAnimationBaseValueFromElement() +{ + m_baseValue = targetAttributeAnimatedValue(); + return true; +} + +void SVGAnimationElement::applyAnimatedValueToElement() +{ + setTargetAttributeAnimatedValue(m_animatedValue); +} + +void SVGAnimationElement::handleTimerEvent(double elapsedSeconds, double timePercentage) +{ + timePercentage = min(timePercentage, 1.0); + if (!connectedToTimer()) { + connectTimer(); + return; + } + + // FIXME: accumulate="sum" will not work w/o code similar to this: +// if (isAccumulated() && repeations() != 0.0) +// accumulateForRepetitions(m_repetitions); + + EAnimationMode animationMode = detectAnimationMode(); + + unsigned valueIndex = 0; + float percentagePast = 0; + calculateValueIndexAndPercentagePast(narrowPrecisionToFloat(timePercentage), valueIndex, percentagePast); + + calculateFromAndToValues(animationMode, valueIndex); + + updateAnimatedValue(animationMode, narrowPrecisionToFloat(timePercentage), valueIndex, percentagePast); + + if (timePercentage == 1.0) { + if ((m_repeatCount > 0 && m_repetitions < m_repeatCount - 1) || isIndefinite(m_repeatCount)) { + m_repetitions++; + return; + } else + disconnectTimer(); + } +} + +bool SVGAnimationElement::updateAnimatedValueForElapsedSeconds(double elapsedSeconds) +{ + // FIXME: fill="freeze" will not work without saving off the m_stoppedTime in a stop() method and having code similar to this: +// if (isStopped()) { +// if (m_fill == FILL_FREEZE) +// elapsedSeconds = m_stoppedTime; +// else +// return false; +// } + + // Validate animation timing settings: + // #1 (duration > 0) -> fine + // #2 (duration <= 0.0 && end > 0) -> fine + if ((m_simpleDuration <= 0.0 && m_end <= 0.0) || (isIndefinite(m_simpleDuration) && m_end <= 0.0)) + return false; // Ignore dur="0" or dur="-neg" + + double percentage = calculateTimePercentage(elapsedSeconds, m_begin, m_end, m_simpleDuration, m_repetitions); + + if (percentage <= 1.0 || connectedToTimer()) + handleTimerEvent(elapsedSeconds, percentage); + + return true; // value was updated, need to apply +} + +} + +// vim:ts=4:noet +#endif // ENABLE(SVG) + |