namespace WebCore {
+static int gUniqueId;
+
static long gDebugAndroidAnimationInstances;
long AndroidAnimation::instancesCount()
, m_timingFunction(animation->timingFunction())
, m_type(type)
, m_operations(operations)
+ , m_uniqueId(++gUniqueId)
+ , m_hasFinished(false)
{
ASSERT(m_timingFunction);
- if (!static_cast<int>(beginTime)) // time not set
- m_beginTime = WTF::currentTime();
-
gDebugAndroidAnimationInstances++;
}
, m_name(anim->name())
, m_type(anim->m_type)
, m_operations(anim->m_operations)
+ , m_uniqueId(anim->m_uniqueId)
+ , m_hasFinished(anim->m_hasFinished)
{
gDebugAndroidAnimationInstances++;
}
gDebugAndroidAnimationInstances--;
}
-double AndroidAnimation::elapsedTime(double time)
+void AndroidAnimation::suggestBeginTime(double time)
{
- if (m_beginTime <= 0.000001) // overflow or not correctly set
+ if (m_beginTime <= 0.000001) // overflow or not yet set
m_beginTime = time;
+}
- m_elapsedTime = time - m_beginTime;
+double AndroidAnimation::elapsedTime(double time)
+{
+ suggestBeginTime(time);
+ double elapsedTime = time - m_beginTime;
if (m_duration <= 0)
m_duration = 0.000001;
- if (m_elapsedTime < 0) // animation not yet started.
+ if (elapsedTime < 0) // animation not yet started.
return 0;
- return m_elapsedTime;
+ return elapsedTime;
}
bool AndroidAnimation::checkIterationsAndProgress(double time, float* finalProgress)
// If not infinite, return false if we are done
if (m_iterationCount > 0 && progress > dur) {
*finalProgress = 1.0;
+ if (!m_hasFinished) {
+ // first time past duration, continue with progress 1.0 so the
+ // element's final position lines up with it's last keyframe
+ m_hasFinished = true;
+ return true;
+ }
+
return false;
}
return true;
}
- if (progress >= 1) {
+ if (progress > 1) {
if (!m_fillsForwards)
return false;
progress = 1;
virtual ~AndroidAnimation();
virtual PassRefPtr<AndroidAnimation> copy() = 0;
+ void suggestBeginTime(double time);
double elapsedTime(double time);
void pickValues(double progress, int* start, int* end);
bool checkIterationsAndProgress(double time, float* finalProgress);
AnimatedPropertyID type() { return m_type; }
bool fillsBackwards() { return m_fillsBackwards; }
bool fillsForwards() { return m_fillsForwards; }
+ int uniqueId() { return m_uniqueId; }
+ double beginTime() { return m_beginTime; }
protected:
double m_beginTime;
- double m_elapsedTime;
double m_duration;
bool m_fillsBackwards;
bool m_fillsForwards;
String m_name;
AnimatedPropertyID m_type;
KeyframeValueList* m_operations;
+ int m_uniqueId;
+ bool m_hasFinished;
};
class AndroidOpacityAnimation : public AndroidAnimation {
, m_goingLeft(false)
, m_expandedTileBoundsX(0)
, m_expandedTileBoundsY(0)
+ , m_highEndGfx(false)
, m_scale(1)
, m_layersRenderingMode(kAllTextures)
- , m_highEndGfx(false)
{
m_viewport.setEmpty();
m_futureViewportTileBounds.setEmpty();
bool GLWebViewState::drawGL(IntRect& rect, SkRect& viewport, IntRect* invalRect,
IntRect& webViewRect, int titleBarHeight,
- IntRect& clip, float scale, bool* buffersSwappedPtr)
+ IntRect& clip, float scale,
+ bool* treesSwappedPtr, bool* newTreeHasAnimPtr)
{
m_scale = scale;
TilesManager::instance()->getProfiler()->nextFrame(viewport.fLeft,
bool fastSwap = isScrolling() || m_layersRenderingMode == kSingleSurfaceRendering;
ret |= m_treeManager.drawGL(currentTime, rect, viewport,
scale, fastSwap,
- buffersSwappedPtr, &nbTexturesNeeded);
+ treesSwappedPtr, newTreeHasAnimPtr,
+ &nbTexturesNeeded);
if (!ret)
resetFrameworkInval();
bool drawGL(IntRect& rect, SkRect& viewport, IntRect* invalRect,
IntRect& webViewRect, int titleBarHeight,
- IntRect& clip, float scale, bool* buffersSwappedPtr);
+ IntRect& clip, float scale,
+ bool* treesSwappedPtr, bool* newTreeHasAnimPtr);
#ifdef MEASURES_PERF
void dumpMeasures();
void GraphicsLayerAndroid::setBackfaceVisibility(bool b)
{
+ if (b == m_backfaceVisibility)
+ return;
+
GraphicsLayer::setBackfaceVisibility(b);
m_contentLayer->setBackfaceVisibility(b);
askForSync();
void GraphicsLayerAndroid::setBackgroundColor(const Color& color)
{
- if (color == m_backgroundColor)
+ if (color == m_backgroundColor && m_backgroundColorSet)
return;
LOG("(%x) setBackgroundColor", this);
GraphicsLayer::setBackgroundColor(color);
void GraphicsLayerAndroid::clearBackgroundColor()
{
+ if (!m_backgroundColorSet)
+ return;
+
LOG("(%x) clearBackgroundColor", this);
GraphicsLayer::clearBackgroundColor();
askForSync();
m_texture->setDrawingLayer(isDrawing ? this : 0);
m_texture->clearPaintingLayer();
}
+
+ // tell auto-initializing animations to start now
+ KeyframesMap::const_iterator localBegin = m_animations.begin();
+ KeyframesMap::const_iterator localEnd = m_animations.end();
+ for (KeyframesMap::const_iterator localIt = localBegin; localIt != localEnd; ++localIt)
+ (localIt->second)->suggestBeginTime(WTF::currentTime());
}
void LayerAndroid::setIsPainting(Layer* drawingTree)
for (int i = 0; i < count; i++)
this->getChild(i)->setIsPainting(drawingTree);
- obtainTextureForPainting(static_cast<LayerAndroid*>(drawingTree));
+
+ LayerAndroid* drawingLayer = 0;
+ if (drawingTree)
+ drawingLayer = static_cast<LayerAndroid*>(drawingTree)->findById(uniqueId());
+
+ copyAnimationStartTimes(drawingLayer);
+ obtainTextureForPainting(drawingLayer);
+}
+
+void LayerAndroid::copyAnimationStartTimes(LayerAndroid* oldLayer)
+{
+ if (!oldLayer)
+ return;
+
+ // copy animation start times, if applicable
+ KeyframesMap::const_iterator localBegin = m_animations.begin();
+ KeyframesMap::const_iterator localEnd = m_animations.end();
+ for (KeyframesMap::const_iterator localIt = localBegin; localIt != localEnd; ++localIt) {
+ KeyframesMap::const_iterator oldBegin = oldLayer->m_animations.begin();
+ KeyframesMap::const_iterator oldEnd = oldLayer->m_animations.end();
+ for (KeyframesMap::const_iterator oldIt = oldBegin; oldIt != oldEnd; ++oldIt) {
+ if ((localIt->second)->uniqueId() == (oldIt->second)->uniqueId()) {
+ // animations are identical, try to copy start time of the old
+ // one, which will have been initialized some time in the past
+ (localIt->second)->suggestBeginTime((oldIt->second)->beginTime());
+ }
+ }
+ }
}
void LayerAndroid::mergeInvalsInto(Layer* replacementTree)
return false;
}
-void LayerAndroid::obtainTextureForPainting(LayerAndroid* drawingTree)
+void LayerAndroid::obtainTextureForPainting(LayerAndroid* drawingLayer)
{
if (!needsTexture())
return;
m_texture = 0;
}
} else {
- if (drawingTree) {
- LayerAndroid* drawingLayer = drawingTree->findById(uniqueId());
- if (drawingLayer) {
- // if a previous tree had the same layer, paint with that painted surface
- m_texture = drawingLayer->m_texture;
- }
+ if (drawingLayer) {
+ // if a previous tree had the same layer, paint with that painted surface
+ m_texture = drawingLayer->m_texture;
}
if (!m_texture)
friend void android::cleanupImageRefs(LayerAndroid* layer);
PaintedSurface* texture() { return m_texture; }
- void obtainTextureForPainting(LayerAndroid* drawingTree);
+ void obtainTextureForPainting(LayerAndroid* drawingLayer);
// Update layers using another tree. Only works for basic properties
// such as the position, the transform. Return true if anything more
friend class CachedLayer::Debug; // debugging access only
#endif
+ void copyAnimationStartTimes(LayerAndroid* oldLayer);
void findInner(FindState&) const;
bool prepareContext(bool force = false);
void clipInner(SkTDArray<SkRect>* region, const SkRect& local) const;
// or start painting it if we aren't
void TreeManager::updateWithTree(Layer* newTree, bool brandNew)
{
+ XLOG("updateWithTree - %p, has children %d, has animations %d",
+ newTree, newTree && newTree->countChildren(),
+ newTree && newTree->countChildren()
+ ? static_cast<LayerAndroid*>(newTree->getChild(0))->hasAnimations() : 0);
// can't have a queued tree unless have a painting tree too
ASSERT(m_paintingTree || !m_queuedTree);
if (!newTree || brandNew) {
clearTrees();
if (brandNew) {
- m_animationOffset = 0;
- m_isAnimating = false;
- m_lastFrameTime = WTF::currentTime();
-
m_paintingTree = newTree;
m_paintingTree->setIsPainting(m_drawingTree);
}
// have a queued tree, copy over invals so the regions are
// eventually repainted
m_queuedTree->mergeInvalsInto(newTree);
+
+ XLOG("DISCARDING tree - %p, has children %d, has animations %d",
+ newTree, newTree && newTree->countChildren(),
+ newTree && newTree->countChildren()
+ ? static_cast<LayerAndroid*>(newTree->getChild(0))->hasAnimations() : 0);
}
SkSafeUnref(m_queuedTree);
m_queuedTree = newTree;
bool TreeManager::drawGL(double currentTime, IntRect& viewRect,
SkRect& visibleRect, float scale,
- bool enterFastSwapMode, bool* buffersSwappedPtr,
+ bool enterFastSwapMode, bool* treesSwappedPtr, bool* newTreeHasAnimPtr,
TexturesResult* texturesResultPtr)
{
m_fastSwapMode |= enterFastSwapMode;
if (m_paintingTree) {
ret |= m_paintingTree->prepare(currentTime, viewRect,
visibleRect, scale);
+ LayerAndroid* laTree = 0;
if (m_paintingTree->countChildren()) {
- LayerAndroid* laTree = static_cast<LayerAndroid*>(m_paintingTree->getChild(0));
+ laTree = static_cast<LayerAndroid*>(m_paintingTree->getChild(0));
laTree->computeTexturesAmount(texturesResultPtr);
}
if (/*!m_fastSwapMode && */ m_paintingTree->isReady()) {
XLOG("have painting tree %p ready, swapping!", m_paintingTree);
didTreeSwap = true;
swap();
- if (buffersSwappedPtr)
- *buffersSwappedPtr = true;
+ if (treesSwappedPtr)
+ *treesSwappedPtr = true;
+ if (laTree && newTreeHasAnimPtr)
+ *newTreeHasAnimPtr = laTree->hasAnimations();
}
} else if (m_drawingTree) {
XLOG("preparing drawing tree %p", m_drawingTree);
}
}
- if (!m_isAnimating) {
- m_animationOffset += currentTime - m_lastFrameTime;
-#ifdef ANIM_DEBUG
- XLOGC("adding to %f", m_animationOffset);
-#endif
- }
if (m_drawingTree) {
bool drawingReady = didTreeSwap || m_drawingTree->isReady();
- if (drawingReady || m_fastSwapMode)
+ if (didTreeSwap || m_fastSwapMode || (drawingReady && !m_paintingTree))
m_drawingTree->swapTiles();
if (drawingReady) {
}
if (m_drawingTree->countChildren()) {
-#ifdef ANIM_DEBUG
- XLOGC("drawing tree %p with animation time offset of %f, locked %d",
- m_drawingTree, m_animationOffset, m_isAnimating);
-#endif
LayerAndroid* laTree = static_cast<LayerAndroid*>(m_drawingTree->getChild(0));
- m_isAnimating = laTree->evaluateAnimations(currentTime - m_animationOffset);
- if (!m_isAnimating)
- m_animationOffset = 0;
- ret |= m_isAnimating;
- } else if (!m_paintingTree) {
- m_animationOffset = 0;
- m_isAnimating = false;
+ ret |= laTree->evaluateAnimations(currentTime);
}
XLOG("drawing tree %p", m_drawingTree);
ret |= m_drawingTree->drawGL(viewRect, visibleRect, scale);
m_paintingTree->state()->drawBackground(defaultBackground);
}
- m_lastFrameTime = currentTime;
-
if (m_paintingTree) {
XLOG("still have painting tree %p", m_paintingTree);
return true;
bool drawGL(double currentTime, IntRect& viewRect,
SkRect& visibleRect, float scale,
- bool enterFastSwapMode, bool* buffersSwappedPtr,
+ bool enterFastSwapMode, bool* treesSwappedPtr, bool* newTreeHasAnimPtr,
TexturesResult* texturesResultPtr);
void drawCanvas(SkCanvas* canvas, bool drawLayers);
Layer* m_queuedTree;
bool m_fastSwapMode;
-
- double m_animationOffset;
- double m_lastFrameTime;
- bool m_isAnimating;
};
} // namespace WebCore
return false;
if (AnimationController* animController = renderer->animation()) {
+#if PLATFORM(ANDROID)
+ // android renders an opacity animation much faster if it's composited
+ return (animController->isRunningAnimationOnRenderer(renderer, CSSPropertyOpacity))
+#else
return (animController->isRunningAnimationOnRenderer(renderer, CSSPropertyOpacity) && inCompositingMode())
+#endif
|| animController->isRunningAnimationOnRenderer(renderer, CSSPropertyWebkitTransform);
}
return false;
ChromeClientAndroid* chromeC = static_cast<ChromeClientAndroid*>(m_mainFrame->page()->chrome()->client());
GraphicsLayerAndroid* root = static_cast<GraphicsLayerAndroid*>(chromeC->layersSync());
if (root) {
- root->notifyClientAnimationStarted();
LayerAndroid* updatedLayer = root->contentLayer();
return layers->updateWithTree(updatedLayer);
}
return true;
}
+void WebViewCore::notifyAnimationStarted()
+{
+ // We notify webkit that the animations have begun
+ // TODO: handle case where not all have begun
+ ChromeClientAndroid* chromeC = static_cast<ChromeClientAndroid*>(m_mainFrame->page()->chrome()->client());
+ GraphicsLayerAndroid* root = static_cast<GraphicsLayerAndroid*>(chromeC->layersSync());
+ if (root)
+ root->notifyClientAnimationStarted();
+
+}
+
BaseLayerAndroid* WebViewCore::createBaseLayer(SkRegion* region)
{
BaseLayerAndroid* base = new BaseLayerAndroid();
ChromeClientAndroid* chromeC = static_cast<ChromeClientAndroid*>(m_mainFrame->page()->chrome()->client());
GraphicsLayerAndroid* root = static_cast<GraphicsLayerAndroid*>(chromeC->layersSync());
if (root) {
- root->notifyClientAnimationStarted();
LayerAndroid* copyLayer = new LayerAndroid(*root->contentLayer());
base->addChild(copyLayer);
copyLayer->unref();
m_groupForVisitedLinks->addVisitedLink(string, length);
}
-static bool UpdateLayers(JNIEnv *env, jobject obj, jint jbaseLayer)
+static bool UpdateLayers(JNIEnv *env, jobject obj, jint nativeClass, jint jbaseLayer)
{
- WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ WebViewCore* viewImpl = (WebViewCore*) nativeClass;
BaseLayerAndroid* baseLayer = (BaseLayerAndroid*) jbaseLayer;
if (baseLayer) {
LayerAndroid* root = static_cast<LayerAndroid*>(baseLayer->getChild(0));
return true;
}
+static void NotifyAnimationStarted(JNIEnv *env, jobject obj, jint nativeClass)
+{
+ WebViewCore* viewImpl = (WebViewCore*) nativeClass;
+ viewImpl->notifyAnimationStarted();
+}
+
static jint RecordContent(JNIEnv *env, jobject obj, jobject region, jobject pt)
{
#ifdef ANDROID_INSTRUMENT
(void*) UpdateFrameCache },
{ "nativeGetContentMinPrefWidth", "()I",
(void*) GetContentMinPrefWidth },
- { "nativeUpdateLayers", "(I)Z",
+ { "nativeUpdateLayers", "(II)Z",
(void*) UpdateLayers },
+ { "nativeNotifyAnimationStarted", "(I)V",
+ (void*) NotifyAnimationStarted },
{ "nativeRecordContent", "(Landroid/graphics/Region;Landroid/graphics/Point;)I",
(void*) RecordContent },
{ "setViewportSettingsFromNative", "()V",
// as we are calling layersSync().
BaseLayerAndroid* createBaseLayer(SkRegion*);
bool updateLayers(LayerAndroid*);
+ void notifyAnimationStarted();
int textWrapWidth() const { return m_textWrapWidth; }
float scale() const { return m_scale; }
m_javaGlue.m_viewInvalidateRect = GetJMethod(env, clazz, "viewInvalidate", "(IIII)V");
m_javaGlue.m_postInvalidateDelayed = GetJMethod(env, clazz,
"viewInvalidateDelayed", "(JIIII)V");
- m_javaGlue.m_pageSwapCallback = GetJMethod(env, clazz, "pageSwapCallback", "()V");
+ m_javaGlue.m_pageSwapCallback = GetJMethod(env, clazz, "pageSwapCallback", "(Z)V");
m_javaGlue.m_inFullScreenMode = GetJMethod(env, clazz, "inFullScreenMode", "()Z");
m_javaGlue.m_getTextHandleScale = GetJMethod(env, clazz, "getTextHandleScale", "()F");
env->DeleteLocalRef(clazz);
// once the correct scale is set
if (!m_visibleRect.hasValidCoordinates())
return false;
- bool pagesSwapped = false;
+ bool treesSwapped = false;
+ bool newTreeHasAnim = false;
bool ret = m_glWebViewState->drawGL(viewRect, m_visibleRect, invalRect,
webViewRect, titleBarHeight, clip, scale,
- &pagesSwapped);
- if (m_pageSwapCallbackRegistered && pagesSwapped) {
+ &treesSwapped, &newTreeHasAnim);
+ if (treesSwapped && (m_pageSwapCallbackRegistered || newTreeHasAnim)) {
m_pageSwapCallbackRegistered = false;
LOG_ASSERT(m_javaGlue.m_obj, "A java object was not associated with this native WebView!");
JNIEnv* env = JSC::Bindings::getJNIEnv();
AutoJObject javaObject = m_javaGlue.object(env);
if (javaObject.get()) {
- env->CallVoidMethod(javaObject.get(), m_javaGlue.m_pageSwapCallback);
+ env->CallVoidMethod(javaObject.get(), m_javaGlue.m_pageSwapCallback, newTreeHasAnim);
checkException(env);
}
}