diff options
author | Nicolas Roard <nicolasroard@google.com> | 2011-03-09 13:52:11 -0800 |
---|---|---|
committer | Nicolas Roard <nicolasroard@google.com> | 2011-03-10 15:14:54 -0800 |
commit | ac8d9f13717dd67de94dc7ab18690d6da10b0c99 (patch) | |
tree | 0387c02be91f0e9b652a83f4c8502672ec2dfabb | |
parent | 95cb21b0cde2a64281360f9e45f31f049e583273 (diff) | |
download | external_webkit-ac8d9f13717dd67de94dc7ab18690d6da10b0c99.zip external_webkit-ac8d9f13717dd67de94dc7ab18690d6da10b0c99.tar.gz external_webkit-ac8d9f13717dd67de94dc7ab18690d6da10b0c99.tar.bz2 |
Fix CSS animations
- we were overriding existing animations.
- implement correctly the removeAnimations functions
- refactor AndroidAnimation (share common code)
- fix how we use the timing functions for the animations
- implements timing functions per keyframes
bug:2453890
Change-Id: I367d708338c270171eeaacc7e2fb3729eb78c444
5 files changed, 153 insertions, 129 deletions
diff --git a/WebCore/platform/graphics/android/AndroidAnimation.cpp b/WebCore/platform/graphics/android/AndroidAnimation.cpp index 3280d07..a064d05 100644 --- a/WebCore/platform/graphics/android/AndroidAnimation.cpp +++ b/WebCore/platform/graphics/android/AndroidAnimation.cpp @@ -54,17 +54,17 @@ long AndroidAnimation::instancesCount() return gDebugAndroidAnimationInstances; } -AndroidAnimation::AndroidAnimation(AndroidAnimationType type, +AndroidAnimation::AndroidAnimation(AnimatedPropertyID type, const Animation* animation, + KeyframeValueList* operations, double beginTime) : m_beginTime(beginTime) , m_duration(animation->duration()) , m_iterationCount(animation->iterationCount()) - , m_currentIteration(0) , m_direction(animation->direction()) - , m_currentDirection(false) , m_timingFunction(animation->timingFunction()) , m_type(type) + , m_operations(operations) { ASSERT(m_timingFunction); @@ -78,11 +78,10 @@ AndroidAnimation::AndroidAnimation(AndroidAnimation* anim) : m_beginTime(anim->m_beginTime) , m_duration(anim->m_duration) , m_iterationCount(anim->m_iterationCount) - , m_currentIteration(0) , m_direction(anim->m_direction) - , m_currentDirection(false) , m_timingFunction(anim->m_timingFunction) , m_type(anim->m_type) + , m_operations(anim->m_operations) { gDebugAndroidAnimationInstances++; } @@ -92,7 +91,7 @@ AndroidAnimation::~AndroidAnimation() gDebugAndroidAnimationInstances--; } -float AndroidAnimation::currentProgress(double time) +double AndroidAnimation::elapsedTime(double time) { if (m_beginTime <= 0.000001) // overflow or not correctly set m_beginTime = time; @@ -105,37 +104,69 @@ float AndroidAnimation::currentProgress(double time) if (m_elapsedTime < 0) // animation not yet started. return 0; - return m_elapsedTime / m_duration; + return m_elapsedTime; } bool AndroidAnimation::checkIterationsAndProgress(double time, float* finalProgress) { - float progress = currentProgress(time); + double progress = elapsedTime(time); + double dur = m_duration; + if (m_iterationCount > 0) + dur *= m_iterationCount; - int currentIteration = static_cast<int>(progress); - if (currentIteration != m_currentIteration) - if (m_direction == Animation::AnimationDirectionAlternate) - swapDirection(); - - m_currentIteration = currentIteration; - progress -= m_currentIteration; + if (m_duration <= 0) + return false; - if ((m_currentIteration >= m_iterationCount) - && (m_iterationCount != Animation::IterationCountInfinite)) + // If not infinite, return false if we are done + if (m_iterationCount > 0 && progress > dur) return false; - if (m_timingFunction->isCubicBezierTimingFunction()) { - CubicBezierTimingFunction* bezierFunction = static_cast<CubicBezierTimingFunction*>(m_timingFunction.get()); + double fractionalTime = progress / m_duration; + int integralTime = static_cast<int>(fractionalTime); + + fractionalTime -= integralTime; + + if ((m_direction == Animation::AnimationDirectionAlternate) && (integralTime & 1)) + fractionalTime = 1 - fractionalTime; + + *finalProgress = fractionalTime; + return true; +} + +double AndroidAnimation::applyTimingFunction(float from, float to, double progress, + const TimingFunction* tf) +{ + double fractionalTime = progress; + double offset = from; + double scale = 1.0 / (to - from); + + if (scale != 1 || offset) + fractionalTime = (fractionalTime - offset) * scale; + + const TimingFunction* timingFunction = tf; + + if (!timingFunction) + timingFunction = m_timingFunction.get(); + + if (timingFunction && timingFunction->isCubicBezierTimingFunction()) { + const CubicBezierTimingFunction* bezierFunction = static_cast<const CubicBezierTimingFunction*>(timingFunction); UnitBezier bezier(bezierFunction->x1(), bezierFunction->y1(), bezierFunction->x2(), bezierFunction->y2()); if (m_duration > 0) - progress = bezier.solve(progress, 1.0f / (200.0f * m_duration)); + fractionalTime = bezier.solve(fractionalTime, 1.0f / (200.0f * m_duration)); + } else if (timingFunction && timingFunction->isStepsTimingFunction()) { + const StepsTimingFunction* stepFunction = static_cast<const StepsTimingFunction*>(timingFunction); + if (stepFunction->stepAtStart()) { + fractionalTime = (floor(stepFunction->numberOfSteps() * fractionalTime) + 1) / stepFunction->numberOfSteps(); + if (fractionalTime > 1.0) + fractionalTime = 1.0; + } else { + fractionalTime = floor(stepFunction->numberOfSteps() * fractionalTime) / stepFunction->numberOfSteps(); + } } - - *finalProgress = progress; - return true; + return fractionalTime; } PassRefPtr<AndroidOpacityAnimation> AndroidOpacityAnimation::create( @@ -150,14 +181,12 @@ PassRefPtr<AndroidOpacityAnimation> AndroidOpacityAnimation::create( AndroidOpacityAnimation::AndroidOpacityAnimation(const Animation* animation, KeyframeValueList* operations, double beginTime) - : AndroidAnimation(AndroidAnimation::OPACITY, animation, beginTime) - , m_operations(operations) + : AndroidAnimation(AnimatedPropertyOpacity, animation, operations, beginTime) { } AndroidOpacityAnimation::AndroidOpacityAnimation(AndroidOpacityAnimation* anim) : AndroidAnimation(anim) - , m_operations(anim->m_operations) { } @@ -166,41 +195,45 @@ PassRefPtr<AndroidAnimation> AndroidOpacityAnimation::copy() return adoptRef(new AndroidOpacityAnimation(this)); } -bool AndroidOpacityAnimation::evaluate(LayerAndroid* layer, double time) +void AndroidAnimation::pickValues(double progress, int* start, int* end) { - float progress; - if (!checkIterationsAndProgress(time, &progress)) - return false; - - if (progress < 0) // we still want to be evaluated until we get progress > 0 - return true; - - // First, we need to get the from and to values - - FloatAnimationValue* fromValue = 0; - FloatAnimationValue* toValue = 0; - - float distance = 0; + float distance = -1; unsigned int foundAt = 0; for (unsigned int i = 0; i < m_operations->size(); i++) { - FloatAnimationValue* value = (FloatAnimationValue*) m_operations->at(i); - float opacity = (float) value->value(); + const AnimationValue* value = m_operations->at(i); float key = value->keyTime(); float d = progress - key; - XLOG("[%d] Key %.2f, opacity %.4f", i, key, opacity); - if (!fromValue || (d > 0 && d < distance && i + 1 < m_operations->size())) { - fromValue = value; + if (distance == -1 || (d >= 0 && d < distance && i + 1 < m_operations->size())) { distance = d; foundAt = i; } } + *start = foundAt; + if (foundAt + 1 < m_operations->size()) - toValue = (FloatAnimationValue*) m_operations->at(foundAt + 1); + *end = foundAt + 1; else - toValue = fromValue; + *end = foundAt; +} - XLOG("[layer %d] fromValue %x, key %.2f, toValue %x, key %.2f for progress %.2f", +bool AndroidOpacityAnimation::evaluate(LayerAndroid* layer, double time) +{ + float progress; + if (!checkIterationsAndProgress(time, &progress)) + return false; + + if (progress < 0) // we still want to be evaluated until we get progress > 0 + return true; + + // First, we need to get the from and to values + + int from, to; + pickValues(progress, &from, &to); + FloatAnimationValue* fromValue = (FloatAnimationValue*) m_operations->at(from); + FloatAnimationValue* toValue = (FloatAnimationValue*) m_operations->at(to); + + XLOG("[layer %d] opacity fromValue %x, key %.2f, toValue %x, key %.2f for progress %.2f", layer->uniqueId(), fromValue, fromValue->keyTime(), toValue, toValue->keyTime(), progress); @@ -208,18 +241,14 @@ bool AndroidOpacityAnimation::evaluate(LayerAndroid* layer, double time) // We now have the correct two values to work with, let's compute the // progress value - float delta = toValue->keyTime() - fromValue->keyTime(); - float rprogress = (progress - fromValue->keyTime()) / delta; - XLOG("We picked keys %.2f to %.2f for progress %.2f, real progress %.2f", - fromValue->keyTime(), toValue->keyTime(), progress, rprogress); - progress = rprogress; - - float from = (float) fromValue->value(); - float to = (float) toValue->value(); - float value = from + ((to - from) * progress); + const TimingFunction* timingFunction = fromValue->timingFunction(); + progress = applyTimingFunction(fromValue->keyTime(), toValue->keyTime(), + progress, timingFunction); + float value = fromValue->value() + ((toValue->value() - fromValue->value()) * progress); layer->setOpacity(value); - XLOG("AndroidOpacityAnimation::evaluate(%p, %p, %L) value=%.6f", this, layer, time, value); + + XLOG("AndroidOpacityAnimation::evaluate(%p, %p, %.2f) value=%.6f", this, layer, time, value); return true; } @@ -234,14 +263,12 @@ PassRefPtr<AndroidTransformAnimation> AndroidTransformAnimation::create( AndroidTransformAnimation::AndroidTransformAnimation(const Animation* animation, KeyframeValueList* operations, double beginTime) - : AndroidAnimation(AndroidAnimation::TRANSFORM, animation, beginTime) - , m_operations(operations) + : AndroidAnimation(AnimatedPropertyWebkitTransform, animation, operations, beginTime) { } AndroidTransformAnimation::AndroidTransformAnimation(AndroidTransformAnimation* anim) : AndroidAnimation(anim) - , m_operations(anim->m_operations) { } @@ -269,28 +296,10 @@ bool AndroidTransformAnimation::evaluate(LayerAndroid* layer, double time) // First, we need to get the from and to values - TransformAnimationValue* fromValue = 0; - TransformAnimationValue* toValue = 0; - - float distance = 0; - unsigned int foundAt = 0; - for (unsigned int i = 0; i < m_operations->size(); i++) { - TransformAnimationValue* value = (TransformAnimationValue*) m_operations->at(i); - TransformOperations* values = (TransformOperations*) value->value(); - float key = value->keyTime(); - float d = progress - key; - XLOG("[%d] Key %.2f, %d values", i, key, values->size()); - if (!fromValue || (d > 0 && d < distance && i + 1 < m_operations->size())) { - fromValue = value; - distance = d; - foundAt = i; - } - } - - if (foundAt + 1 < m_operations->size()) - toValue = (TransformAnimationValue*) m_operations->at(foundAt + 1); - else - toValue = fromValue; + int from, to; + pickValues(progress, &from, &to); + TransformAnimationValue* fromValue = (TransformAnimationValue*) m_operations->at(from); + TransformAnimationValue* toValue = (TransformAnimationValue*) m_operations->at(to); XLOG("[layer %d] fromValue %x, key %.2f, toValue %x, key %.2f for progress %.2f", layer->uniqueId(), @@ -300,12 +309,12 @@ bool AndroidTransformAnimation::evaluate(LayerAndroid* layer, double time) // We now have the correct two values to work with, let's compute the // progress value - float delta = toValue->keyTime() - fromValue->keyTime(); - float rprogress = (progress - fromValue->keyTime()) / delta; - XLOG("We picked keys %.2f to %.2f for progress %.2f, real progress %.2f", - fromValue->keyTime(), toValue->keyTime(), progress, rprogress); - progress = rprogress; - + const TimingFunction* timingFunction = fromValue->timingFunction(); + float p = applyTimingFunction(fromValue->keyTime(), toValue->keyTime(), + progress, timingFunction); + XLOG("progress %.2f => %.2f from: %.2f to: %.2f", progress, p, fromValue->keyTime(), + toValue->keyTime()); + progress = p; // With both values and the progress, we also need to check out that // the operations are compatible (i.e. we are animating the same number diff --git a/WebCore/platform/graphics/android/AndroidAnimation.h b/WebCore/platform/graphics/android/AndroidAnimation.h index d682103..4f84799 100644 --- a/WebCore/platform/graphics/android/AndroidAnimation.h +++ b/WebCore/platform/graphics/android/AndroidAnimation.h @@ -35,38 +35,35 @@ class TimingFunction; class AndroidAnimation : public RefCounted<AndroidAnimation> { public: - enum AndroidAnimationType { - UNDEFINED, - OPACITY, - TRANSFORM - }; - AndroidAnimation(AndroidAnimationType type, + AndroidAnimation(AnimatedPropertyID type, const Animation* animation, + KeyframeValueList* operations, double beginTime); AndroidAnimation(AndroidAnimation* anim); virtual ~AndroidAnimation(); virtual PassRefPtr<AndroidAnimation> copy() = 0; - float currentProgress(double time); + double elapsedTime(double time); + void pickValues(double progress, int* start, int* end); bool checkIterationsAndProgress(double time, float* finalProgress); - virtual void swapDirection() { m_currentDirection = !m_currentDirection; } + double applyTimingFunction(float from, float to, double progress, + const TimingFunction* timingFunction); virtual bool evaluate(LayerAndroid* layer, double time) = 0; static long instancesCount(); void setName(const String& name) { m_name = name; } String name() { return m_name; } - AndroidAnimationType type() { return m_type; } + AnimatedPropertyID type() { return m_type; } protected: double m_beginTime; double m_elapsedTime; double m_duration; int m_iterationCount; - int m_currentIteration; int m_direction; - bool m_currentDirection; RefPtr<TimingFunction> m_timingFunction; String m_name; - AndroidAnimationType m_type; + AnimatedPropertyID m_type; + KeyframeValueList* m_operations; }; class AndroidOpacityAnimation : public AndroidAnimation { @@ -81,9 +78,6 @@ class AndroidOpacityAnimation : public AndroidAnimation { virtual PassRefPtr<AndroidAnimation> copy(); virtual bool evaluate(LayerAndroid* layer, double time); - - private: - KeyframeValueList* m_operations; }; class AndroidTransformAnimation : public AndroidAnimation { @@ -100,9 +94,6 @@ class AndroidTransformAnimation : public AndroidAnimation { virtual PassRefPtr<AndroidAnimation> copy(); virtual bool evaluate(LayerAndroid* layer, double time); - - private: - KeyframeValueList* m_operations; }; } // namespace WebCore diff --git a/WebCore/platform/graphics/android/GraphicsLayerAndroid.cpp b/WebCore/platform/graphics/android/GraphicsLayerAndroid.cpp index 54346e5..ba9f295 100644 --- a/WebCore/platform/graphics/android/GraphicsLayerAndroid.cpp +++ b/WebCore/platform/graphics/android/GraphicsLayerAndroid.cpp @@ -609,7 +609,7 @@ bool GraphicsLayerAndroid::repaint() } } - TLOG("(%x) repaint() on (%.2f,%.2f) contentlayer(%.2f,%.2f,%.2f,%.2f)paintGraphicsLayer called!", + LOG("(%x) repaint() on (%.2f,%.2f) contentlayer(%.2f,%.2f,%.2f,%.2f)paintGraphicsLayer called!", this, m_size.width(), m_size.height(), m_contentLayer->getPosition().fX, m_contentLayer->getPosition().fY, @@ -722,23 +722,22 @@ bool GraphicsLayerAndroid::createAnimationFromKeyframes(const KeyframeValueList& bool isKeyframe = valueList.size() > 2; TLOG("createAnimationFromKeyframes(%d), name(%s) beginTime(%.2f)", isKeyframe, keyframesName.latin1().data(), beginTime); - // TODO: handles keyframe animations correctly switch (valueList.property()) { case AnimatedPropertyInvalid: break; case AnimatedPropertyWebkitTransform: break; case AnimatedPropertyBackgroundColor: break; - case AnimatedPropertyOpacity: { MLOG("ANIMATEDPROPERTYOPACITY"); KeyframeValueList* operationsList = new KeyframeValueList(AnimatedPropertyOpacity); for (unsigned int i = 0; i < valueList.size(); i++) { - FloatAnimationValue* originalValue = (FloatAnimationValue*)valueList.at(i); - FloatAnimationValue* value = new FloatAnimationValue(originalValue->keyTime(), - originalValue->value(), 0); - // TODO: pass the timing function originalValue->timingFunction()); - operationsList->insert(value); + FloatAnimationValue* originalValue = (FloatAnimationValue*)valueList.at(i); + PassRefPtr<TimingFunction> timingFunction(const_cast<TimingFunction*>(originalValue->timingFunction())); + FloatAnimationValue* value = new FloatAnimationValue(originalValue->keyTime(), + originalValue->value(), + timingFunction); + operationsList->insert(value); } RefPtr<AndroidOpacityAnimation> anim = AndroidOpacityAnimation::create(animation, @@ -776,9 +775,10 @@ bool GraphicsLayerAndroid::createTransformAnimationsFromKeyframes(const Keyframe KeyframeValueList* operationsList = new KeyframeValueList(AnimatedPropertyWebkitTransform); for (unsigned int i = 0; i < valueList.size(); i++) { TransformAnimationValue* originalValue = (TransformAnimationValue*)valueList.at(i); + PassRefPtr<TimingFunction> timingFunction(const_cast<TimingFunction*>(originalValue->timingFunction())); TransformAnimationValue* value = new TransformAnimationValue(originalValue->keyTime(), - originalValue->value(), 0); - // TODO: pass the timing function originalValue->timingFunction()); + originalValue->value(), + timingFunction); operationsList->insert(value); } @@ -801,14 +801,14 @@ bool GraphicsLayerAndroid::createTransformAnimationsFromKeyframes(const Keyframe void GraphicsLayerAndroid::removeAnimationsForProperty(AnimatedPropertyID anID) { TLOG("NRO removeAnimationsForProperty(%d)", anID); - m_contentLayer->removeAnimation(propertyIdToString(anID)); + m_contentLayer->removeAnimationsForProperty(anID); askForSync(); } void GraphicsLayerAndroid::removeAnimationsForKeyframes(const String& keyframesName) { TLOG("NRO removeAnimationsForKeyframes(%s)", keyframesName.latin1().data()); - m_contentLayer->removeAnimation(keyframesName); + m_contentLayer->removeAnimationsForKeyframes(keyframesName); askForSync(); } diff --git a/WebCore/platform/graphics/android/LayerAndroid.cpp b/WebCore/platform/graphics/android/LayerAndroid.cpp index 7d68f03..76fb2f2 100644 --- a/WebCore/platform/graphics/android/LayerAndroid.cpp +++ b/WebCore/platform/graphics/android/LayerAndroid.cpp @@ -132,8 +132,10 @@ LayerAndroid::LayerAndroid(const LayerAndroid& layer) : SkLayer(layer), addChild(layer.getChild(i)->copy())->unref(); KeyframesMap::const_iterator end = layer.m_animations.end(); - for (KeyframesMap::const_iterator it = layer.m_animations.begin(); it != end; ++it) - m_animations.add((it->second)->name(), (it->second)->copy()); + for (KeyframesMap::const_iterator it = layer.m_animations.begin(); it != end; ++it) { + pair<String, int> key((it->second)->name(), (it->second)->type()); + m_animations.add(key, (it->second)->copy()); + } #ifdef DEBUG_COUNT ClassTracker::instance()->increment("LayerAndroid"); @@ -243,15 +245,35 @@ bool LayerAndroid::evaluateAnimations(double time) const void LayerAndroid::addAnimation(PassRefPtr<AndroidAnimation> prpAnim) { RefPtr<AndroidAnimation> anim = prpAnim; - RefPtr<AndroidAnimation> currentAnim = m_animations.get(anim->name()); - if (currentAnim && currentAnim->type() == anim->type()) - removeAnimation(anim->name()); - m_animations.add(anim->name(), anim); + pair<String, int> key(anim->name(), anim->type()); + removeAnimationsForProperty(anim->type()); + m_animations.add(key, anim); +} + +void LayerAndroid::removeAnimationsForProperty(AnimatedPropertyID property) +{ + KeyframesMap::const_iterator end = m_animations.end(); + Vector<pair<String, int> > toDelete; + for (KeyframesMap::const_iterator it = m_animations.begin(); it != end; ++it) { + if ((it->second)->type() == property) + toDelete.append(it->first); + } + + for (unsigned int i = 0; i < toDelete.size(); i++) + m_animations.remove(toDelete[i]); } -void LayerAndroid::removeAnimation(const String& name) +void LayerAndroid::removeAnimationsForKeyframes(const String& name) { - m_animations.remove(name); + KeyframesMap::const_iterator end = m_animations.end(); + Vector<pair<String, int> > toDelete; + for (KeyframesMap::const_iterator it = m_animations.begin(); it != end; ++it) { + if ((it->second)->name() == name) + toDelete.append(it->first); + } + + for (unsigned int i = 0; i < toDelete.size(); i++) + m_animations.remove(toDelete[i]); } // We only use the bounding rect of the layer as mask... diff --git a/WebCore/platform/graphics/android/LayerAndroid.h b/WebCore/platform/graphics/android/LayerAndroid.h index 42356e9..56f5c6b 100644 --- a/WebCore/platform/graphics/android/LayerAndroid.h +++ b/WebCore/platform/graphics/android/LayerAndroid.h @@ -22,6 +22,7 @@ #include "FloatPoint.h" #include "FloatPoint3D.h" #include "FloatRect.h" +#include "GraphicsLayerClient.h" #include "LayerTexture.h" #include "RefPtr.h" #include "SkColor.h" @@ -179,7 +180,8 @@ public: SkPicture* recordContext(); void addAnimation(PassRefPtr<AndroidAnimation> anim); - void removeAnimation(const String& name); + void removeAnimationsForProperty(AnimatedPropertyID property); + void removeAnimationsForKeyframes(const String& name); bool evaluateAnimations() const; bool evaluateAnimations(double time) const; bool hasAnimations() const; @@ -301,7 +303,7 @@ private: SkBitmapRef* m_contentsImage; - typedef HashMap<String, RefPtr<AndroidAnimation> > KeyframesMap; + typedef HashMap<pair<String, int>, RefPtr<AndroidAnimation> > KeyframesMap; KeyframesMap m_animations; SkPicture* m_extra; int m_uniqueId; |