summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNicolas Roard <nicolasroard@google.com>2011-03-09 13:52:11 -0800
committerNicolas Roard <nicolasroard@google.com>2011-03-10 15:14:54 -0800
commitac8d9f13717dd67de94dc7ab18690d6da10b0c99 (patch)
tree0387c02be91f0e9b652a83f4c8502672ec2dfabb
parent95cb21b0cde2a64281360f9e45f31f049e583273 (diff)
downloadexternal_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
-rw-r--r--WebCore/platform/graphics/android/AndroidAnimation.cpp187
-rw-r--r--WebCore/platform/graphics/android/AndroidAnimation.h27
-rw-r--r--WebCore/platform/graphics/android/GraphicsLayerAndroid.cpp24
-rw-r--r--WebCore/platform/graphics/android/LayerAndroid.cpp38
-rw-r--r--WebCore/platform/graphics/android/LayerAndroid.h6
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;