diff options
author | Michael Brasser <michael.brasser@nokia.com> | 2012-02-03 12:26:37 +1000 |
---|---|---|
committer | Qt by Nokia <qt-info@nokia.com> | 2012-02-07 05:32:47 +0100 |
commit | ce3dee765c858a0b573d468ef8fee6b838e576d1 (patch) | |
tree | b7026a061b0b58bac6af30eaab6e610c5ebeb504 /src/quick/util/qdeclarativespringanimation.cpp | |
parent | 0ca9d3f0f720e1933379ef40bc5c29253e21cba0 (diff) |
Add and use new animation backend.
The new backend improves performance, and allows us to create
multiple running animation jobs from a single Transition. It is
based off of the existing Qt animation framework.
Change-Id: Id1d0162f6e5c65bf31267f3f9f2042c354375d57
Reviewed-by: Yunqiao Yin <charles.yin@nokia.com>
Diffstat (limited to 'src/quick/util/qdeclarativespringanimation.cpp')
-rw-r--r-- | src/quick/util/qdeclarativespringanimation.cpp | 370 |
1 files changed, 250 insertions, 120 deletions
diff --git a/src/quick/util/qdeclarativespringanimation.cpp b/src/quick/util/qdeclarativespringanimation.cpp index 27ef5bd62e..40408bb112 100644 --- a/src/quick/util/qdeclarativespringanimation.cpp +++ b/src/quick/util/qdeclarativespringanimation.cpp @@ -43,6 +43,7 @@ #include "qdeclarativeanimation_p_p.h" #include <private/qdeclarativeproperty_p.h> +#include "private/qparallelanimationgroupjob_p.h" #include <QtCore/qdebug.h> @@ -51,29 +52,39 @@ #include <limits.h> #include <math.h> -QT_BEGIN_NAMESPACE +#define DELAY_STOP_TIMER_INTERVAL 32 +QT_BEGIN_NAMESPACE -class QDeclarativeSpringAnimationPrivate : public QDeclarativePropertyAnimationPrivate +class QDeclarativeSpringAnimationPrivate; +class Q_AUTOTEST_EXPORT QSpringAnimation : public QAbstractAnimationJob { - Q_DECLARE_PUBLIC(QDeclarativeSpringAnimation) + Q_DISABLE_COPY(QSpringAnimation) public: - - - struct SpringAnimation { - SpringAnimation() - : currentValue(0), to(0), velocity(0), start(0), duration(0) {} - qreal currentValue; - qreal to; - qreal velocity; - int start; - int duration; + QSpringAnimation(QDeclarativeSpringAnimationPrivate * = 0); + + ~QSpringAnimation(); + int duration() const; + void restart(); + void init(); + + qreal currentValue; + qreal to; + qreal velocity; + int startTime; + int dura; + int lastTime; + int stopTime; + enum Mode { + Track, + Velocity, + Spring }; - QHash<QDeclarativeProperty, SpringAnimation> activeAnimations; + Mode mode; + QDeclarativeProperty target; - qreal maxVelocity; qreal velocityms; - int lastTime; + qreal maxVelocity; qreal mass; qreal spring; qreal damping; @@ -82,75 +93,161 @@ public: bool useMass : 1; bool haveModulus : 1; + bool useDelta : 1; + typedef QHash<QDeclarativeProperty, QSpringAnimation*> ActiveAnimationHash; - enum Mode { - Track, - Velocity, - Spring - }; - Mode mode; + void clearTemplate() { animationTemplate = 0; } +protected: + virtual void updateCurrentTime(int time); + virtual void updateState(QAbstractAnimationJob::State, QAbstractAnimationJob::State); + +private: + QDeclarativeSpringAnimationPrivate *animationTemplate; +}; + +class QDeclarativeSpringAnimationPrivate : public QDeclarativePropertyAnimationPrivate +{ + Q_DECLARE_PUBLIC(QDeclarativeSpringAnimation) +public: QDeclarativeSpringAnimationPrivate() - : maxVelocity(0), velocityms(0), lastTime(0) - , mass(1.0), spring(0.), damping(0.), epsilon(0.01) - , modulus(0.0), useMass(false), haveModulus(false) - , mode(Track), clock(0) - { } - - void tick(int time); - bool animate(const QDeclarativeProperty &property, SpringAnimation &animation, int elapsed); + : QDeclarativePropertyAnimationPrivate() + , velocityms(0) + , maxVelocity(0) + , mass(1.0) + , spring(0.) + , damping(0.) + , epsilon(0.01) + , modulus(0.0) + , useMass(false) + , haveModulus(false) + , mode(QSpringAnimation::Track) + { elapsed.start(); } + void updateMode(); + qreal velocityms; + qreal maxVelocity; + qreal mass; + qreal spring; + qreal damping; + qreal epsilon; + qreal modulus; + + bool useMass : 1; + bool haveModulus : 1; + QSpringAnimation::Mode mode; - typedef QTickAnimationProxy<QDeclarativeSpringAnimationPrivate, &QDeclarativeSpringAnimationPrivate::tick> Clock; - Clock *clock; + QSpringAnimation::ActiveAnimationHash activeAnimations; + QElapsedTimer elapsed; }; -void QDeclarativeSpringAnimationPrivate::tick(int time) +QSpringAnimation::QSpringAnimation(QDeclarativeSpringAnimationPrivate *priv) + : QAbstractAnimationJob() + , currentValue(0) + , to(0) + , velocity(0) + , startTime(0) + , dura(0) + , lastTime(0) + , stopTime(-1) + , mode(Track) + , velocityms(0) + , maxVelocity(0) + , mass(1.0) + , spring(0.) + , damping(0.) + , epsilon(0.01) + , modulus(0.0) + , useMass(false) + , haveModulus(false) + , useDelta(false) + , animationTemplate(priv) +{ +} + +QSpringAnimation::~QSpringAnimation() +{ + if (animationTemplate) { + if (target.object()) { + QSpringAnimation::ActiveAnimationHash::iterator it = + animationTemplate->activeAnimations.find(target); + if (it != animationTemplate->activeAnimations.end() && it.value() == this) + animationTemplate->activeAnimations.erase(it); + } else { + //target is no longer valid, need to search linearly + QSpringAnimation::ActiveAnimationHash::iterator it; + for (it = animationTemplate->activeAnimations.begin(); it != animationTemplate->activeAnimations.end(); ++it) { + if (it.value() == this) { + animationTemplate->activeAnimations.erase(it); + break; + } + } + } + } +} + +int QSpringAnimation::duration() const +{ + return -1; +} + +void QSpringAnimation::restart() +{ + if (isRunning() || (stopTime != -1 && (animationTemplate->elapsed.elapsed() - stopTime) < DELAY_STOP_TIMER_INTERVAL)) { + useDelta = true; + init(); + lastTime = 0; + } else { + useDelta = false; + //init() will be triggered when group starts + } +} + +void QSpringAnimation::init() +{ + lastTime = startTime = 0; + stopTime = -1; +} + +void QSpringAnimation::updateCurrentTime(int time) { if (mode == Track) { - clock->stop(); + stop(); return; } - int elapsed = time - lastTime; + + int elapsed = useDelta ? QDeclarativeAnimationTimer::instance()->currentDelta() : time - lastTime; + if (useDelta) { + startTime = time - elapsed; + useDelta = false; + } + if (!elapsed) return; + int count = elapsed / 16; + if (mode == Spring) { if (elapsed < 16) // capped at 62fps. return; - int count = elapsed / 16; lastTime = time - (elapsed - count * 16); } else { lastTime = time; } - QMutableHashIterator<QDeclarativeProperty, SpringAnimation> it(activeAnimations); - while (it.hasNext()) { - it.next(); - if (animate(it.key(), it.value(), elapsed)) - it.remove(); - } - - if (activeAnimations.isEmpty()) - clock->stop(); -} - -bool QDeclarativeSpringAnimationPrivate::animate(const QDeclarativeProperty &property, SpringAnimation &animation, int elapsed) -{ - qreal srcVal = animation.to; + qreal srcVal = to; - bool stop = false; + bool stopped = false; if (haveModulus) { - animation.currentValue = fmod(animation.currentValue, modulus); + currentValue = fmod(currentValue, modulus); srcVal = fmod(srcVal, modulus); } if (mode == Spring) { // Real men solve the spring DEs using RK4. // We'll do something much simpler which gives a result that looks fine. - int count = elapsed / 16; for (int i = 0; i < count; ++i) { - qreal diff = srcVal - animation.currentValue; + qreal diff = srcVal - currentValue; if (haveModulus && qAbs(diff) > modulus / 2) { if (diff < 0) diff += modulus; @@ -158,31 +255,31 @@ bool QDeclarativeSpringAnimationPrivate::animate(const QDeclarativeProperty &pro diff -= modulus; } if (useMass) - animation.velocity = animation.velocity + (spring * diff - damping * animation.velocity) / mass; + velocity = velocity + (spring * diff - damping * velocity) / mass; else - animation.velocity = animation.velocity + spring * diff - damping * animation.velocity; + velocity = velocity + spring * diff - damping * velocity; if (maxVelocity > 0.) { // limit velocity - if (animation.velocity > maxVelocity) - animation.velocity = maxVelocity; - else if (animation.velocity < -maxVelocity) - animation.velocity = -maxVelocity; + if (velocity > maxVelocity) + velocity = maxVelocity; + else if (velocity < -maxVelocity) + velocity = -maxVelocity; } - animation.currentValue += animation.velocity * 16.0 / 1000.0; + currentValue += velocity * 16.0 / 1000.0; if (haveModulus) { - animation.currentValue = fmod(animation.currentValue, modulus); - if (animation.currentValue < 0.0) - animation.currentValue += modulus; + currentValue = fmod(currentValue, modulus); + if (currentValue < 0.0) + currentValue += modulus; } } - if (qAbs(animation.velocity) < epsilon && qAbs(srcVal - animation.currentValue) < epsilon) { - animation.velocity = 0.0; - animation.currentValue = srcVal; - stop = true; + if (qAbs(velocity) < epsilon && qAbs(srcVal - currentValue) < epsilon) { + velocity = 0.0; + currentValue = srcVal; + stopped = true; } } else { qreal moveBy = elapsed * velocityms; - qreal diff = srcVal - animation.currentValue; + qreal diff = srcVal - currentValue; if (haveModulus && qAbs(diff) > modulus / 2) { if (diff < 0) diff += modulus; @@ -190,45 +287,54 @@ bool QDeclarativeSpringAnimationPrivate::animate(const QDeclarativeProperty &pro diff -= modulus; } if (diff > 0) { - animation.currentValue += moveBy; + currentValue += moveBy; if (haveModulus) - animation.currentValue = fmod(animation.currentValue, modulus); + currentValue = fmod(currentValue, modulus); } else { - animation.currentValue -= moveBy; - if (haveModulus && animation.currentValue < 0.0) - animation.currentValue = fmod(animation.currentValue, modulus) + modulus; + currentValue -= moveBy; + if (haveModulus && currentValue < 0.0) + currentValue = fmod(currentValue, modulus) + modulus; } - if (lastTime - animation.start >= animation.duration) { - animation.currentValue = animation.to; - stop = true; + if (lastTime - startTime >= dura) { + currentValue = to; + stopped = true; } } - qreal old_to = animation.to; + qreal old_to = to; - QDeclarativePropertyPrivate::write(property, animation.currentValue, + QDeclarativePropertyPrivate::write(target, currentValue, QDeclarativePropertyPrivate::BypassInterceptor | QDeclarativePropertyPrivate::DontRemoveBinding); - return (stop && old_to == animation.to); // do not stop if we got restarted + if (stopped && old_to == to) { // do not stop if we got restarted + stopTime = animationTemplate->elapsed.elapsed(); + stop(); + } +} + +void QSpringAnimation::updateState(QAbstractAnimationJob::State newState, QAbstractAnimationJob::State /*oldState*/) +{ + if (newState == QAbstractAnimationJob::Running) + init(); } void QDeclarativeSpringAnimationPrivate::updateMode() { if (spring == 0. && maxVelocity == 0.) - mode = Track; + mode = QSpringAnimation::Track; else if (spring > 0.) - mode = Spring; + mode = QSpringAnimation::Spring; else { - mode = Velocity; - QHash<QDeclarativeProperty, SpringAnimation>::iterator it; + mode = QSpringAnimation::Velocity; + QSpringAnimation::ActiveAnimationHash::iterator it; for (it = activeAnimations.begin(); it != activeAnimations.end(); ++it) { - SpringAnimation &animation = *it; - animation.start = lastTime; - qreal dist = qAbs(animation.currentValue - animation.to); + QSpringAnimation *animation = *it; + animation->startTime = animation->lastTime; + qreal dist = qAbs(animation->currentValue - animation->to); if (haveModulus && dist > modulus / 2) dist = modulus - fmod(dist, modulus); - animation.duration = dist / velocityms; + animation->dura = dist / velocityms; } } } @@ -264,12 +370,15 @@ void QDeclarativeSpringAnimationPrivate::updateMode() QDeclarativeSpringAnimation::QDeclarativeSpringAnimation(QObject *parent) : QDeclarativeNumberAnimation(*(new QDeclarativeSpringAnimationPrivate),parent) { - Q_D(QDeclarativeSpringAnimation); - d->clock = new QDeclarativeSpringAnimationPrivate::Clock(d, this); } QDeclarativeSpringAnimation::~QDeclarativeSpringAnimation() { + Q_D(QDeclarativeSpringAnimation); + QSpringAnimation::ActiveAnimationHash::iterator it; + for (it = d->activeAnimations.begin(); it != d->activeAnimations.end(); ++it) { + it.value()->clearTemplate(); + } } /*! @@ -415,48 +524,69 @@ void QDeclarativeSpringAnimation::setMass(qreal mass) } } -void QDeclarativeSpringAnimation::transition(QDeclarativeStateActions &actions, - QDeclarativeProperties &modified, - TransitionDirection direction) +QAbstractAnimationJob* QDeclarativeSpringAnimation::transition(QDeclarativeStateActions &actions, + QDeclarativeProperties &modified, + TransitionDirection direction) { Q_D(QDeclarativeSpringAnimation); Q_UNUSED(direction); - if (d->clock->state() != QAbstractAnimation::Running) { - d->lastTime = 0; - } - - QDeclarativeNumberAnimation::transition(actions, modified, direction); - - if (!d->actions) - return; + QParallelAnimationGroupJob *wrapperGroup = new QParallelAnimationGroupJob(); + + QDeclarativeStateActions dataActions = QDeclarativeNumberAnimation::createTransitionActions(actions, modified); + if (!dataActions.isEmpty()) { + QSet<QAbstractAnimationJob*> anims; + for (int i = 0; i < dataActions.size(); ++i) { + QSpringAnimation *animation; + bool needsRestart = false; + const QDeclarativeProperty &property = dataActions.at(i).property; + if (d->activeAnimations.contains(property)) { + animation = d->activeAnimations[property]; + needsRestart = true; + } else { + animation = new QSpringAnimation(d); + d->activeAnimations.insert(property, animation); + animation->target = property; + } + wrapperGroup->appendAnimation(initInstance(animation)); + + animation->to = dataActions.at(i).toValue.toReal(); + animation->startTime = 0; + animation->velocityms = d->velocityms; + animation->mass = d->mass; + animation->spring = d->spring; + animation->damping = d->damping; + animation->epsilon = d->epsilon; + animation->modulus = d->modulus; + animation->useMass = d->useMass; + animation->haveModulus = d->haveModulus; + animation->mode = d->mode; + animation->dura = -1; + animation->maxVelocity = d->maxVelocity; - if (!d->actions->isEmpty()) { - for (int i = 0; i < d->actions->size(); ++i) { - const QDeclarativeProperty &property = d->actions->at(i).property; - QDeclarativeSpringAnimationPrivate::SpringAnimation &animation - = d->activeAnimations[property]; - animation.to = d->actions->at(i).toValue.toReal(); - animation.start = d->lastTime; if (d->fromIsDefined) - animation.currentValue = d->actions->at(i).fromValue.toReal(); + animation->currentValue = dataActions.at(i).fromValue.toReal(); else - animation.currentValue = property.read().toReal(); - if (d->mode == QDeclarativeSpringAnimationPrivate::Velocity) { - qreal dist = qAbs(animation.currentValue - animation.to); + animation->currentValue = property.read().toReal(); + if (animation->mode == QSpringAnimation::Velocity) { + qreal dist = qAbs(animation->currentValue - animation->to); if (d->haveModulus && dist > d->modulus / 2) dist = d->modulus - fmod(dist, d->modulus); - animation.duration = dist / d->velocityms; + animation->dura = dist / animation->velocityms; + } + + if (needsRestart) + animation->restart(); + anims.insert(animation); + } + foreach (QSpringAnimation *anim, d->activeAnimations.values()){ + if (!anims.contains(anim)) { + anim->clearTemplate(); + d->activeAnimations.remove(anim->target); } } } -} - - -QAbstractAnimation *QDeclarativeSpringAnimation::qtAnimation() -{ - Q_D(QDeclarativeSpringAnimation); - return d->clock; + return wrapperGroup; } QT_END_NAMESPACE |