diff options
29 files changed, 1242 insertions, 164 deletions
diff --git a/core/java/android/view/RenderNode.java b/core/java/android/view/RenderNode.java index 3dc09c4..8b80c3e0 100644 --- a/core/java/android/view/RenderNode.java +++ b/core/java/android/view/RenderNode.java @@ -20,6 +20,9 @@ import android.annotation.NonNull; import android.graphics.Matrix; import android.graphics.Outline; +import java.util.ArrayList; +import java.util.List; + /** * <p>A display list records a series of graphics related operations and can replay * them later. Display lists are usually built by recording operations on a @@ -176,11 +179,24 @@ public class RenderNode { private boolean mValid; private final long mNativeRenderNode; + // We need to keep a strong reference to all running animators to ensure that + // they can call removeAnimator when they have finished, as the native-side + // object can only hold a WeakReference<> to avoid leaking memory due to + // cyclic references. + private List<RenderNodeAnimator> mActiveAnimators; + private RenderNode(String name) { mNativeRenderNode = nCreate(name); } /** + * @see RenderNode#adopt(long) + */ + private RenderNode(long nativePtr) { + mNativeRenderNode = nativePtr; + } + + /** * Creates a new display list that can be used to record batches of * drawing operations. * @@ -195,6 +211,17 @@ public class RenderNode { } /** + * Adopts an existing native render node. + * + * Note: This will *NOT* incRef() on the native object, however it will + * decRef() when it is destroyed. The caller should have already incRef'd it + */ + public static RenderNode adopt(long nativePtr) { + return new RenderNode(nativePtr); + } + + + /** * Starts recording a display list for the render node. All * operations performed on the returned canvas are recorded and * stored in this display list. @@ -822,6 +849,23 @@ public class RenderNode { } /////////////////////////////////////////////////////////////////////////// + // Animations + /////////////////////////////////////////////////////////////////////////// + + public void addAnimator(RenderNodeAnimator animator) { + if (mActiveAnimators == null) { + mActiveAnimators = new ArrayList<RenderNodeAnimator>(); + } + mActiveAnimators.add(animator); + nAddAnimator(mNativeRenderNode, animator.getNativeAnimator()); + } + + public void removeAnimator(RenderNodeAnimator animator) { + nRemoveAnimator(mNativeRenderNode, animator.getNativeAnimator()); + mActiveAnimators.remove(animator); + } + + /////////////////////////////////////////////////////////////////////////// // Native methods /////////////////////////////////////////////////////////////////////////// @@ -896,6 +940,13 @@ public class RenderNode { private static native void nOutput(long renderNode); /////////////////////////////////////////////////////////////////////////// + // Animations + /////////////////////////////////////////////////////////////////////////// + + private static native void nAddAnimator(long renderNode, long animatorPtr); + private static native void nRemoveAnimator(long renderNode, long animatorPtr); + + /////////////////////////////////////////////////////////////////////////// // Finalization /////////////////////////////////////////////////////////////////////////// diff --git a/core/java/android/view/RenderNodeAnimator.java b/core/java/android/view/RenderNodeAnimator.java new file mode 100644 index 0000000..b70ae3d --- /dev/null +++ b/core/java/android/view/RenderNodeAnimator.java @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.view; + +import android.util.SparseIntArray; + +import java.lang.ref.WeakReference; + +/** + * @hide + */ +public final class RenderNodeAnimator { + + // Keep in sync with enum RenderProperty in Animator.h + private static final int TRANSLATION_X = 0; + private static final int TRANSLATION_Y = 1; + private static final int TRANSLATION_Z = 2; + private static final int SCALE_X = 3; + private static final int SCALE_Y = 4; + private static final int ROTATION = 5; + private static final int ROTATION_X = 6; + private static final int ROTATION_Y = 7; + private static final int X = 8; + private static final int Y = 9; + private static final int Z = 10; + private static final int ALPHA = 11; + + // ViewPropertyAnimator uses a mask for its values, we need to remap them + // to the enum values here. RenderPropertyAnimator can't use the mask values + // directly as internally it uses a lookup table so it needs the values to + // be sequential starting from 0 + private static final SparseIntArray sViewPropertyAnimatorMap = new SparseIntArray(15) {{ + put(ViewPropertyAnimator.TRANSLATION_X, TRANSLATION_X); + put(ViewPropertyAnimator.TRANSLATION_Y, TRANSLATION_Y); + put(ViewPropertyAnimator.TRANSLATION_Z, TRANSLATION_Z); + put(ViewPropertyAnimator.SCALE_X, SCALE_X); + put(ViewPropertyAnimator.SCALE_Y, SCALE_Y); + put(ViewPropertyAnimator.ROTATION, ROTATION); + put(ViewPropertyAnimator.ROTATION_X, ROTATION_X); + put(ViewPropertyAnimator.ROTATION_Y, ROTATION_Y); + put(ViewPropertyAnimator.X, X); + put(ViewPropertyAnimator.Y, Y); + put(ViewPropertyAnimator.Z, Z); + put(ViewPropertyAnimator.ALPHA, ALPHA); + }}; + + // Keep in sync DeltaValueType in Animator.h + private static final int DELTA_TYPE_ABSOLUTE = 0; + private static final int DELTA_TYPE_DELTA = 1; + + private RenderNode mTarget; + private long mNativePtr; + + public int mapViewPropertyToRenderProperty(int viewProperty) { + return sViewPropertyAnimatorMap.get(viewProperty); + } + + public RenderNodeAnimator(int property, int deltaType, float deltaValue) { + mNativePtr = nCreateAnimator(new WeakReference<RenderNodeAnimator>(this), + property, deltaType, deltaValue); + } + + public void start(View target) { + mTarget = target.mRenderNode; + mTarget.addAnimator(this); + // Kick off a frame to start the process + target.invalidateViewProperty(true, false); + } + + public void cancel() { + mTarget.removeAnimator(this); + } + + public void setDuration(int duration) { + nSetDuration(mNativePtr, duration); + } + + long getNativeAnimator() { + return mNativePtr; + } + + private void onFinished() { + mTarget.removeAnimator(this); + } + + // Called by native + private static void callOnFinished(WeakReference<RenderNodeAnimator> weakThis) { + RenderNodeAnimator animator = weakThis.get(); + if (animator != null) { + animator.onFinished(); + } + } + + @Override + protected void finalize() throws Throwable { + try { + nUnref(mNativePtr); + mNativePtr = 0; + } finally { + super.finalize(); + } + } + + private static native long nCreateAnimator(WeakReference<RenderNodeAnimator> weakThis, + int property, int deltaValueType, float deltaValue); + private static native void nSetDuration(long nativePtr, int duration); + private static native void nUnref(long nativePtr); +} diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java index c7a6d41..eaec8ab 100644 --- a/core/java/android/view/ThreadedRenderer.java +++ b/core/java/android/view/ThreadedRenderer.java @@ -50,7 +50,7 @@ import java.io.PrintWriter; public class ThreadedRenderer extends HardwareRenderer { private static final String LOGTAG = "ThreadedRenderer"; - private static final Rect NULL_RECT = new Rect(-1, -1, -1, -1); + private static final Rect NULL_RECT = new Rect(); private int mWidth, mHeight; private long mNativeProxy; @@ -58,9 +58,10 @@ public class ThreadedRenderer extends HardwareRenderer { private RenderNode mRootNode; ThreadedRenderer(boolean translucent) { - mNativeProxy = nCreateProxy(translucent); - mRootNode = RenderNode.create("RootNode"); + long rootNodePtr = nCreateRootRenderNode(); + mRootNode = RenderNode.adopt(rootNodePtr); mRootNode.setClipToBounds(false); + mNativeProxy = nCreateProxy(translucent, rootNodePtr); } @Override @@ -202,8 +203,7 @@ public class ThreadedRenderer extends HardwareRenderer { if (dirty == null) { dirty = NULL_RECT; } - nDrawDisplayList(mNativeProxy, mRootNode.getNativeDisplayList(), - dirty.left, dirty.top, dirty.right, dirty.bottom); + nSyncAndDrawFrame(mNativeProxy, dirty.left, dirty.top, dirty.right, dirty.bottom); } @Override @@ -293,7 +293,8 @@ public class ThreadedRenderer extends HardwareRenderer { /** @hide */ public static native void postToRenderThread(Runnable runnable); - private static native long nCreateProxy(boolean translucent); + private static native long nCreateRootRenderNode(); + private static native long nCreateProxy(boolean translucent, long rootRenderNode); private static native void nDeleteProxy(long nativeProxy); private static native boolean nInitialize(long nativeProxy, Surface window); @@ -302,7 +303,7 @@ public class ThreadedRenderer extends HardwareRenderer { private static native void nSetup(long nativeProxy, int width, int height); private static native void nSetDisplayListData(long nativeProxy, long displayList, long newData); - private static native void nDrawDisplayList(long nativeProxy, long displayList, + private static native void nSyncAndDrawFrame(long nativeProxy, int dirtyLeft, int dirtyTop, int dirtyRight, int dirtyBottom); private static native void nRunWithGlContext(long nativeProxy, Runnable runnable); private static native void nDestroyCanvasAndSurface(long nativeProxy); diff --git a/core/java/android/view/ViewPropertyAnimator.java b/core/java/android/view/ViewPropertyAnimator.java index b1aa7b2..11d2622 100644 --- a/core/java/android/view/ViewPropertyAnimator.java +++ b/core/java/android/view/ViewPropertyAnimator.java @@ -133,19 +133,19 @@ public class ViewPropertyAnimator { * Constants used to associate a property being requested and the mechanism used to set * the property (this class calls directly into View to set the properties in question). */ - private static final int NONE = 0x0000; - private static final int TRANSLATION_X = 0x0001; - private static final int TRANSLATION_Y = 0x0002; - private static final int TRANSLATION_Z = 0x0004; - private static final int SCALE_X = 0x0008; - private static final int SCALE_Y = 0x0010; - private static final int ROTATION = 0x0020; - private static final int ROTATION_X = 0x0040; - private static final int ROTATION_Y = 0x0080; - private static final int X = 0x0100; - private static final int Y = 0x0200; - private static final int Z = 0x0400; - private static final int ALPHA = 0x0800; + static final int NONE = 0x0000; + static final int TRANSLATION_X = 0x0001; + static final int TRANSLATION_Y = 0x0002; + static final int TRANSLATION_Z = 0x0004; + static final int SCALE_X = 0x0008; + static final int SCALE_Y = 0x0010; + static final int ROTATION = 0x0020; + static final int ROTATION_X = 0x0040; + static final int ROTATION_Y = 0x0080; + static final int X = 0x0100; + static final int Y = 0x0200; + static final int Z = 0x0400; + static final int ALPHA = 0x0800; private static final int TRANSFORM_MASK = TRANSLATION_X | TRANSLATION_Y | TRANSLATION_Z | SCALE_X | SCALE_Y | ROTATION | ROTATION_X | ROTATION_Y | X | Y | Z; diff --git a/core/jni/Android.mk b/core/jni/Android.mk index 711f28a..ee59c8a 100644 --- a/core/jni/Android.mk +++ b/core/jni/Android.mk @@ -61,6 +61,7 @@ LOCAL_SRC_FILES:= \ android_view_MotionEvent.cpp \ android_view_PointerIcon.cpp \ android_view_RenderNode.cpp \ + android_view_RenderNodeAnimator.cpp \ android_view_VelocityTracker.cpp \ android_text_AndroidCharacter.cpp \ android_text_AndroidBidi.cpp \ diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index aa635c6..f964cd2 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -120,6 +120,7 @@ extern int register_android_graphics_Xfermode(JNIEnv* env); extern int register_android_graphics_pdf_PdfDocument(JNIEnv* env); extern int register_android_view_DisplayEventReceiver(JNIEnv* env); extern int register_android_view_RenderNode(JNIEnv* env); +extern int register_android_view_RenderNodeAnimator(JNIEnv* env); extern int register_android_view_GraphicBuffer(JNIEnv* env); extern int register_android_view_GLES20Canvas(JNIEnv* env); extern int register_android_view_GLRenderer(JNIEnv* env); @@ -1193,6 +1194,7 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_android_graphics_Graphics), REG_JNI(register_android_view_DisplayEventReceiver), REG_JNI(register_android_view_RenderNode), + REG_JNI(register_android_view_RenderNodeAnimator), REG_JNI(register_android_view_GraphicBuffer), REG_JNI(register_android_view_GLES20Canvas), REG_JNI(register_android_view_GLRenderer), diff --git a/core/jni/android_view_RenderNode.cpp b/core/jni/android_view_RenderNode.cpp index 3ad2ae5..4715c26 100644 --- a/core/jni/android_view_RenderNode.cpp +++ b/core/jni/android_view_RenderNode.cpp @@ -23,6 +23,7 @@ #include <nativehelper/JNIHelp.h> #include <android_runtime/AndroidRuntime.h> +#include <Animator.h> #include <DisplayListRenderer.h> #include <RenderNode.h> @@ -438,6 +439,25 @@ static jfloat android_view_RenderNode_getPivotY(JNIEnv* env, return renderNode->stagingProperties().getPivotY(); } +// ---------------------------------------------------------------------------- +// RenderProperties - Animations +// ---------------------------------------------------------------------------- + +static void android_view_RenderNode_addAnimator(JNIEnv* env, jobject clazz, + jlong renderNodePtr, jlong animatorPtr) { + RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr); + RenderPropertyAnimator* animator = reinterpret_cast<RenderPropertyAnimator*>(animatorPtr); + renderNode->addAnimator(animator); +} + +static void android_view_RenderNode_removeAnimator(JNIEnv* env, jobject clazz, + jlong renderNodePtr, jlong animatorPtr) { + RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr); + RenderPropertyAnimator* animator = reinterpret_cast<RenderPropertyAnimator*>(animatorPtr); + renderNode->removeAnimator(animator); +} + + #endif // USE_OPENGL_RENDERER // ---------------------------------------------------------------------------- @@ -513,6 +533,9 @@ static JNINativeMethod gMethods[] = { { "nGetPivotX", "(J)F", (void*) android_view_RenderNode_getPivotX }, { "nGetPivotY", "(J)F", (void*) android_view_RenderNode_getPivotY }, + + { "nAddAnimator", "(JJ)V", (void*) android_view_RenderNode_addAnimator }, + { "nRemoveAnimator", "(JJ)V", (void*) android_view_RenderNode_removeAnimator }, #endif }; diff --git a/core/jni/android_view_RenderNodeAnimator.cpp b/core/jni/android_view_RenderNodeAnimator.cpp new file mode 100644 index 0000000..35cdf60 --- /dev/null +++ b/core/jni/android_view_RenderNodeAnimator.cpp @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "OpenGLRenderer" + +#include "android_view_RenderNodeAnimator.h" + +#include "jni.h" +#include "GraphicsJNI.h" +#include <nativehelper/JNIHelp.h> +#include <android_runtime/AndroidRuntime.h> + +#include <Animator.h> +#include <Interpolator.h> +#include <RenderProperties.h> + +namespace android { + +using namespace uirenderer; + +static struct { + jclass clazz; + + jmethodID callOnFinished; +} gRenderNodeAnimatorClassInfo; + +static JNIEnv* getEnv(JavaVM* vm) { + JNIEnv* env; + if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) { + return 0; + } + return env; +} + +RenderNodeAnimator::RenderNodeAnimator(JNIEnv* env, jobject weakThis, + RenderProperty property, DeltaValueType deltaType, float delta) + : RenderPropertyAnimator(property, deltaType, delta) { + mWeakThis = env->NewGlobalRef(weakThis); + env->GetJavaVM(&mJvm); +} + +RenderNodeAnimator::~RenderNodeAnimator() { + JNIEnv* env = getEnv(mJvm); + env->DeleteGlobalRef(mWeakThis); + mWeakThis = NULL; +} + +void RenderNodeAnimator::callOnFinished() { + JNIEnv* env = getEnv(mJvm); + env->CallStaticVoidMethod( + gRenderNodeAnimatorClassInfo.clazz, + gRenderNodeAnimatorClassInfo.callOnFinished, + mWeakThis); +} + +static jlong createAnimator(JNIEnv* env, jobject clazz, jobject weakThis, + jint property, jint deltaType, jfloat deltaValue) { + LOG_ALWAYS_FATAL_IF(property < 0 || property > RenderNodeAnimator::ALPHA, + "Invalid property %d", property); + LOG_ALWAYS_FATAL_IF(deltaType != RenderPropertyAnimator::DELTA + && deltaType != RenderPropertyAnimator::ABSOLUTE, + "Invalid delta type %d", deltaType); + + RenderNodeAnimator* animator = new RenderNodeAnimator(env, weakThis, + static_cast<RenderPropertyAnimator::RenderProperty>(property), + static_cast<RenderPropertyAnimator::DeltaValueType>(deltaType), + deltaValue); + animator->incStrong(0); + return reinterpret_cast<jlong>( animator ); +} + +static void setDuration(JNIEnv* env, jobject clazz, jlong animatorPtr, jint duration) { + LOG_ALWAYS_FATAL_IF(duration < 0, "Duration cannot be negative"); + RenderNodeAnimator* animator = reinterpret_cast<RenderNodeAnimator*>(animatorPtr); + animator->setDuration(duration); +} + +static void unref(JNIEnv* env, jobject clazz, jlong objPtr) { + VirtualLightRefBase* obj = reinterpret_cast<VirtualLightRefBase*>(objPtr); + obj->decStrong(0); +} + +// ---------------------------------------------------------------------------- +// JNI Glue +// ---------------------------------------------------------------------------- + +const char* const kClassPathName = "android/view/RenderNodeAnimator"; + +static JNINativeMethod gMethods[] = { + { "nCreateAnimator", "(Ljava/lang/ref/WeakReference;IIF)J", (void*) createAnimator }, + { "nSetDuration", "(JI)V", (void*) setDuration }, + { "nUnref", "(J)V", (void*) unref }, +}; + +#define FIND_CLASS(var, className) \ + var = env->FindClass(className); \ + LOG_FATAL_IF(! var, "Unable to find class " className); + +#define GET_STATIC_METHOD_ID(var, clazz, methodName, methodDescriptor) \ + var = env->GetStaticMethodID(clazz, methodName, methodDescriptor); \ + LOG_FATAL_IF(! var, "Unable to find method " methodName); + +int register_android_view_RenderNodeAnimator(JNIEnv* env) { + FIND_CLASS(gRenderNodeAnimatorClassInfo.clazz, kClassPathName); + gRenderNodeAnimatorClassInfo.clazz = jclass(env->NewGlobalRef(gRenderNodeAnimatorClassInfo.clazz)); + + GET_STATIC_METHOD_ID(gRenderNodeAnimatorClassInfo.callOnFinished, gRenderNodeAnimatorClassInfo.clazz, + "callOnFinished", "(Ljava/lang/ref/WeakReference;)V"); + + return AndroidRuntime::registerNativeMethods(env, kClassPathName, gMethods, NELEM(gMethods)); +} + + +} // namespace android diff --git a/core/jni/android_view_RenderNodeAnimator.h b/core/jni/android_view_RenderNodeAnimator.h new file mode 100644 index 0000000..d84003f --- /dev/null +++ b/core/jni/android_view_RenderNodeAnimator.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "jni.h" + +#include <Animator.h> + +namespace android { + +class RenderNodeAnimator : public uirenderer::RenderPropertyAnimator { +public: + RenderNodeAnimator(JNIEnv* env, jobject callbackObject, + RenderProperty property, DeltaValueType deltaType, float delta); + virtual ~RenderNodeAnimator(); + + void callOnFinished(); + +private: + JavaVM* mJvm; + jobject mWeakThis; +}; + +} diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp index b5f489d..58fc1e1 100644 --- a/core/jni/android_view_ThreadedRenderer.cpp +++ b/core/jni/android_view_ThreadedRenderer.cpp @@ -16,6 +16,8 @@ #define LOG_TAG "ThreadedRenderer" +#include <algorithm> + #include "jni.h" #include <nativehelper/JNIHelp.h> #include <android_runtime/AndroidRuntime.h> @@ -24,6 +26,8 @@ #include <android_runtime/android_view_Surface.h> #include <system/window.h> +#include "android_view_RenderNodeAnimator.h" +#include <RenderNode.h> #include <renderthread/RenderProxy.h> #include <renderthread/RenderTask.h> #include <renderthread/RenderThread.h> @@ -63,15 +67,75 @@ private: jobject mRunnable; }; +class InvokeAnimationListeners : public MessageHandler { +public: + InvokeAnimationListeners(std::vector< sp<RenderNodeAnimator> >& animators) { + mAnimators.swap(animators); + } + + static void callOnFinished(const sp<RenderNodeAnimator>& animator) { + animator->callOnFinished(); + } + + virtual void handleMessage(const Message& message) { + std::for_each(mAnimators.begin(), mAnimators.end(), callOnFinished); + mAnimators.clear(); + } + +private: + std::vector< sp<RenderNodeAnimator> > mAnimators; +}; + +class RootRenderNode : public RenderNode, public AnimationListener { +public: + RootRenderNode() : RenderNode() { + mLooper = Looper::getForThread(); + LOG_ALWAYS_FATAL_IF(!mLooper.get(), + "Must create RootRenderNode on a thread with a looper!"); + } + + virtual ~RootRenderNode() {} + + void onAnimationFinished(const sp<RenderPropertyAnimator>& animator) { + mFinishedAnimators.push_back( + reinterpret_cast<RenderNodeAnimator*>(animator.get())); + } + + virtual void prepareTree(TreeInfo& info) { + info.animationListener = this; + RenderNode::prepareTree(info); + info.animationListener = NULL; + + // post all the finished stuff + if (mFinishedAnimators.size()) { + sp<InvokeAnimationListeners> message + = new InvokeAnimationListeners(mFinishedAnimators); + mLooper->sendMessage(message, 0); + } + } + +private: + sp<Looper> mLooper; + std::vector< sp<RenderNodeAnimator> > mFinishedAnimators; +}; + static void android_view_ThreadedRenderer_postToRenderThread(JNIEnv* env, jobject clazz, jobject jrunnable) { RenderTask* task = new JavaTask(env, jrunnable); RenderThread::getInstance().queue(task); } +static jlong android_view_ThreadedRenderer_createRootRenderNode(JNIEnv* env, jobject clazz) { + RootRenderNode* node = new RootRenderNode(); + node->incStrong(0); + node->setName("RootRenderNode"); + return reinterpret_cast<jlong>(node); +} + static jlong android_view_ThreadedRenderer_createProxy(JNIEnv* env, jobject clazz, - jboolean translucent) { - return (jlong) new RenderProxy(translucent); + jboolean translucent, jlong rootRenderNodePtr) { + RenderNode* rootRenderNode = reinterpret_cast<RenderNode*>(rootRenderNodePtr); + return (jlong) new RenderProxy(translucent, rootRenderNode); } static void android_view_ThreadedRenderer_deleteProxy(JNIEnv* env, jobject clazz, @@ -113,12 +177,11 @@ static void android_view_ThreadedRenderer_setup(JNIEnv* env, jobject clazz, proxy->setup(width, height); } -static void android_view_ThreadedRenderer_drawDisplayList(JNIEnv* env, jobject clazz, - jlong proxyPtr, jlong displayListPtr, jint dirtyLeft, jint dirtyTop, +static void android_view_ThreadedRenderer_syncAndDrawFrame(JNIEnv* env, jobject clazz, + jlong proxyPtr, jint dirtyLeft, jint dirtyTop, jint dirtyRight, jint dirtyBottom) { RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); - RenderNode* displayList = reinterpret_cast<RenderNode*>(displayListPtr); - proxy->drawDisplayList(displayList, dirtyLeft, dirtyTop, dirtyRight, dirtyBottom); + proxy->syncAndDrawFrame(dirtyLeft, dirtyTop, dirtyRight, dirtyBottom); } static void android_view_ThreadedRenderer_destroyCanvasAndSurface(JNIEnv* env, jobject clazz, @@ -187,13 +250,14 @@ const char* const kClassPathName = "android/view/ThreadedRenderer"; static JNINativeMethod gMethods[] = { #ifdef USE_OPENGL_RENDERER { "postToRenderThread", "(Ljava/lang/Runnable;)V", (void*) android_view_ThreadedRenderer_postToRenderThread }, - { "nCreateProxy", "(Z)J", (void*) android_view_ThreadedRenderer_createProxy }, + { "nCreateRootRenderNode", "()J", (void*) android_view_ThreadedRenderer_createRootRenderNode }, + { "nCreateProxy", "(ZJ)J", (void*) android_view_ThreadedRenderer_createProxy }, { "nDeleteProxy", "(J)V", (void*) android_view_ThreadedRenderer_deleteProxy }, { "nInitialize", "(JLandroid/view/Surface;)Z", (void*) android_view_ThreadedRenderer_initialize }, { "nUpdateSurface", "(JLandroid/view/Surface;)V", (void*) android_view_ThreadedRenderer_updateSurface }, { "nPauseSurface", "(JLandroid/view/Surface;)V", (void*) android_view_ThreadedRenderer_pauseSurface }, { "nSetup", "(JII)V", (void*) android_view_ThreadedRenderer_setup }, - { "nDrawDisplayList", "(JJIIII)V", (void*) android_view_ThreadedRenderer_drawDisplayList }, + { "nSyncAndDrawFrame", "(JIIII)V", (void*) android_view_ThreadedRenderer_syncAndDrawFrame }, { "nDestroyCanvasAndSurface", "(J)V", (void*) android_view_ThreadedRenderer_destroyCanvasAndSurface }, { "nInvokeFunctor", "(JJZ)V", (void*) android_view_ThreadedRenderer_invokeFunctor }, { "nRunWithGlContext", "(JLjava/lang/Runnable;)V", (void*) android_view_ThreadedRenderer_runWithGlContext }, diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk index 52be531..d324439 100644 --- a/libs/hwui/Android.mk +++ b/libs/hwui/Android.mk @@ -11,6 +11,7 @@ ifeq ($(USE_OPENGL_RENDERER),true) font/CacheTexture.cpp \ font/Font.cpp \ AmbientShadow.cpp \ + Animator.cpp \ AssetAtlas.cpp \ FontRenderer.cpp \ GammaFontRenderer.cpp \ @@ -25,6 +26,7 @@ ifeq ($(USE_OPENGL_RENDERER),true) FboCache.cpp \ GradientCache.cpp \ Image.cpp \ + Interpolator.cpp \ Layer.cpp \ LayerCache.cpp \ LayerRenderer.cpp \ @@ -66,6 +68,8 @@ ifeq ($(USE_OPENGL_RENDERER),true) $(LOCAL_PATH)/../../include/utils \ external/skia/src/core + include external/stlport/libstlport.mk + LOCAL_CFLAGS += -DUSE_OPENGL_RENDERER -DEGL_EGLEXT_PROTOTYPES -DGL_GLEXT_PROTOTYPES LOCAL_CFLAGS += -Wno-unused-parameter LOCAL_MODULE_CLASS := SHARED_LIBRARIES diff --git a/libs/hwui/Animator.cpp b/libs/hwui/Animator.cpp new file mode 100644 index 0000000..ee16586 --- /dev/null +++ b/libs/hwui/Animator.cpp @@ -0,0 +1,240 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "RT-Animator" + +#include "Animator.h" + +#include <set> + +#include "RenderProperties.h" + +namespace android { +namespace uirenderer { + +/************************************************************ + * Private header + ************************************************************/ + +typedef void (RenderProperties::*SetFloatProperty)(float value); +typedef float (RenderProperties::*GetFloatProperty)() const; + +struct PropertyAccessors { + GetFloatProperty getter; + SetFloatProperty setter; +}; + +// Maps RenderProperty enum to accessors +static const PropertyAccessors PROPERTY_ACCESSOR_LUT[] = { + {&RenderProperties::getTranslationX, &RenderProperties::setTranslationX }, + {&RenderProperties::getTranslationY, &RenderProperties::setTranslationY }, + {&RenderProperties::getTranslationZ, &RenderProperties::setTranslationZ }, + {&RenderProperties::getScaleX, &RenderProperties::setScaleX }, + {&RenderProperties::getScaleY, &RenderProperties::setScaleY }, + {&RenderProperties::getRotation, &RenderProperties::setRotation }, + {&RenderProperties::getRotationX, &RenderProperties::setRotationX }, + {&RenderProperties::getRotationY, &RenderProperties::setRotationY }, + {&RenderProperties::getX, &RenderProperties::setX }, + {&RenderProperties::getY, &RenderProperties::setY }, + {&RenderProperties::getZ, &RenderProperties::setZ }, + {&RenderProperties::getAlpha, &RenderProperties::setAlpha }, +}; + +// Helper class to contain generic animator helpers +class BaseAnimator { +public: + BaseAnimator(); + virtual ~BaseAnimator(); + + void setInterpolator(Interpolator* interpolator); + void setDuration(nsecs_t durationInMs); + + bool isFinished() { return mPlayState == FINISHED; } + +protected: + // This is the main animation entrypoint that subclasses should call + // to generate the onAnimation* lifecycle events + // Returns true if the animation has finished, false otherwise + bool animateFrame(nsecs_t frameTime); + + // Called when PlayState switches from PENDING to RUNNING + virtual void onAnimationStarted() {} + virtual void onAnimationUpdated(float fraction) = 0; + virtual void onAnimationFinished() {} + +private: + enum PlayState { + PENDING, + RUNNING, + FINISHED, + }; + + Interpolator* mInterpolator; + PlayState mPlayState; + long mStartTime; + long mDuration; +}; + +// Hide the base classes & private bits from the exported RenderPropertyAnimator +// in this Impl class so that subclasses of RenderPropertyAnimator don't require +// knowledge of the inner guts but only the public virtual methods. +// Animates a single property +class RenderPropertyAnimatorImpl : public BaseAnimator { +public: + RenderPropertyAnimatorImpl(GetFloatProperty getter, SetFloatProperty setter, + RenderPropertyAnimator::DeltaValueType deltaType, float delta); + ~RenderPropertyAnimatorImpl(); + + bool animate(RenderProperties* target, TreeInfo& info); + +protected: + virtual void onAnimationStarted(); + virtual void onAnimationUpdated(float fraction); + +private: + // mTarget is only valid inside animate() + RenderProperties* mTarget; + GetFloatProperty mGetter; + SetFloatProperty mSetter; + + RenderPropertyAnimator::DeltaValueType mDeltaValueType; + float mDeltaValue; + float mFromValue; +}; + +RenderPropertyAnimator::RenderPropertyAnimator(RenderProperty property, + DeltaValueType deltaType, float deltaValue) { + PropertyAccessors pa = PROPERTY_ACCESSOR_LUT[property]; + mImpl = new RenderPropertyAnimatorImpl(pa.getter, pa.setter, deltaType, deltaValue); +} + +RenderPropertyAnimator::~RenderPropertyAnimator() { + delete mImpl; + mImpl = NULL; +} + +void RenderPropertyAnimator::setInterpolator(Interpolator* interpolator) { + mImpl->setInterpolator(interpolator); +} + +void RenderPropertyAnimator::setDuration(nsecs_t durationInMs) { + mImpl->setDuration(durationInMs); +} + +bool RenderPropertyAnimator::isFinished() { + return mImpl->isFinished(); +} + +bool RenderPropertyAnimator::animate(RenderProperties* target, TreeInfo& info) { + return mImpl->animate(target, info); +} + + +/************************************************************ + * Base animator + ************************************************************/ + +BaseAnimator::BaseAnimator() + : mInterpolator(0) + , mPlayState(PENDING) + , mStartTime(0) + , mDuration(300) { + +} + +BaseAnimator::~BaseAnimator() { + setInterpolator(NULL); +} + +void BaseAnimator::setInterpolator(Interpolator* interpolator) { + delete mInterpolator; + mInterpolator = interpolator; +} + +void BaseAnimator::setDuration(nsecs_t duration) { + mDuration = duration; +} + +bool BaseAnimator::animateFrame(nsecs_t frameTime) { + if (mPlayState == PENDING) { + mPlayState = RUNNING; + mStartTime = frameTime; + // No interpolator was set, use the default + if (!mInterpolator) { + setInterpolator(Interpolator::createDefaultInterpolator()); + } + onAnimationStarted(); + } + + float fraction = 1.0f; + if (mPlayState == RUNNING) { + fraction = mDuration > 0 ? (float)(frameTime - mStartTime) / mDuration : 1.0f; + if (fraction >= 1.0f) { + fraction = 1.0f; + mPlayState = FINISHED; + } + } + fraction = mInterpolator->interpolate(fraction); + onAnimationUpdated(fraction); + + if (mPlayState == FINISHED) { + onAnimationFinished(); + return true; + } + return false; +} + +/************************************************************ + * RenderPropertyAnimator + ************************************************************/ + +RenderPropertyAnimatorImpl::RenderPropertyAnimatorImpl( + GetFloatProperty getter, SetFloatProperty setter, + RenderPropertyAnimator::DeltaValueType deltaType, float delta) + : mTarget(0) + , mGetter(getter) + , mSetter(setter) + , mDeltaValueType(deltaType) + , mDeltaValue(delta) + , mFromValue(-1) { +} + +RenderPropertyAnimatorImpl::~RenderPropertyAnimatorImpl() { +} + +bool RenderPropertyAnimatorImpl::animate(RenderProperties* target, TreeInfo& info) { + mTarget = target; + bool finished = animateFrame(info.frameTimeMs); + mTarget = NULL; + return finished; +} + +void RenderPropertyAnimatorImpl::onAnimationStarted() { + mFromValue = (mTarget->*mGetter)(); + + if (mDeltaValueType == RenderPropertyAnimator::ABSOLUTE) { + mDeltaValue = (mDeltaValue - mFromValue); + mDeltaValueType = RenderPropertyAnimator::DELTA; + } +} + +void RenderPropertyAnimatorImpl::onAnimationUpdated(float fraction) { + float value = mFromValue + (mDeltaValue * fraction); + (mTarget->*mSetter)(value); +} + +} /* namespace uirenderer */ +} /* namespace android */ diff --git a/libs/hwui/Animator.h b/libs/hwui/Animator.h new file mode 100644 index 0000000..1c8361b --- /dev/null +++ b/libs/hwui/Animator.h @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef ANIMATOR_H +#define ANIMATOR_H + +#include <cutils/compiler.h> + +#include "Interpolator.h" +#include "TreeInfo.h" +#include "utils/VirtualLightRefBase.h" + +namespace android { +namespace uirenderer { + +class RenderProperties; +class RenderPropertyAnimatorImpl; + +class RenderPropertyAnimator : public VirtualLightRefBase { +public: + // Since the UI thread doesn't necessarily know what the current values + // actually are and thus can't do the calculations, this is used to inform + // the animator how to lazy-resolve the input value + enum DeltaValueType { + // The delta value represents an absolute value endpoint + // mDeltaValue needs to be recalculated to be mDelta = (mDelta - fromValue) + // in onAnimationStarted() + ABSOLUTE = 0, + // The final value represents an offset from the current value + // No recalculation is needed + DELTA, + }; + + enum RenderProperty { + TRANSLATION_X = 0, + TRANSLATION_Y, + TRANSLATION_Z, + SCALE_X, + SCALE_Y, + ROTATION, + ROTATION_X, + ROTATION_Y, + X, + Y, + Z, + ALPHA, + }; + + ANDROID_API void setInterpolator(Interpolator* interpolator); + ANDROID_API void setDuration(nsecs_t durationInMs); + ANDROID_API bool isFinished(); + + bool animate(RenderProperties* target, TreeInfo& info); + +protected: + ANDROID_API RenderPropertyAnimator(RenderProperty property, DeltaValueType deltaType, + float deltaValue); + ANDROID_API virtual ~RenderPropertyAnimator(); + +private: + RenderPropertyAnimatorImpl* mImpl; +}; + +} /* namespace uirenderer */ +} /* namespace android */ + +#endif /* ANIMATOR_H */ diff --git a/libs/hwui/Interpolator.cpp b/libs/hwui/Interpolator.cpp new file mode 100644 index 0000000..004ddf5 --- /dev/null +++ b/libs/hwui/Interpolator.cpp @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "Interpolator.h" + +#include <math.h> + +namespace android { +namespace uirenderer { + +Interpolator* Interpolator::createDefaultInterpolator() { + return new AccelerateDecelerateInterpolator(); +} + +float AccelerateDecelerateInterpolator::interpolate(float input) { + return (float)(cosf((input + 1) * M_PI) / 2.0f) + 0.5f; +} + +} /* namespace uirenderer */ +} /* namespace android */ diff --git a/libs/hwui/Interpolator.h b/libs/hwui/Interpolator.h new file mode 100644 index 0000000..2cfb60c --- /dev/null +++ b/libs/hwui/Interpolator.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef INTERPOLATOR_H +#define INTERPOLATOR_H + +namespace android { +namespace uirenderer { + +class Interpolator { +public: + virtual ~Interpolator() {} + + virtual float interpolate(float input) = 0; + + static Interpolator* createDefaultInterpolator(); + +protected: + Interpolator() {} +}; + +class AccelerateDecelerateInterpolator : public Interpolator { +public: + AccelerateDecelerateInterpolator() {} + virtual ~AccelerateDecelerateInterpolator() {} + + virtual float interpolate(float input); +}; + +} /* namespace uirenderer */ +} /* namespace android */ + +#endif /* INTERPOLATOR_H */ diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp index 838e5ac..dcd6bda 100644 --- a/libs/hwui/RenderNode.cpp +++ b/libs/hwui/RenderNode.cpp @@ -18,6 +18,8 @@ #include "RenderNode.h" +#include <algorithm> + #include <SkCanvas.h> #include <algorithm> @@ -54,7 +56,8 @@ RenderNode::RenderNode() : mNeedsPropertiesSync(false) , mNeedsDisplayListDataSync(false) , mDisplayListData(0) - , mStagingDisplayListData(0) { + , mStagingDisplayListData(0) + , mNeedsAnimatorsSync(false) { } RenderNode::~RenderNode() { @@ -97,15 +100,32 @@ void RenderNode::prepareTree(TreeInfo& info) { } void RenderNode::prepareTreeImpl(TreeInfo& info) { - pushStagingChanges(info); + if (info.performStagingPush) { + pushStagingChanges(info); + } + if (info.evaluateAnimations) { + evaluateAnimations(info); + } prepareSubTree(info, mDisplayListData); } +static bool is_finished(const sp<RenderPropertyAnimator>& animator) { + return animator->isFinished(); +} + void RenderNode::pushStagingChanges(TreeInfo& info) { if (mNeedsPropertiesSync) { mNeedsPropertiesSync = false; mProperties = mStagingProperties; } + if (mNeedsAnimatorsSync) { + mAnimators.reserve(mStagingAnimators.size()); + std::vector< sp<RenderPropertyAnimator> >::iterator it; + // hint: this means copy_if_not() + it = std::remove_copy_if(mStagingAnimators.begin(), mStagingAnimators.end(), + mAnimators.begin(), is_finished); + mAnimators.resize(std::distance(mAnimators.begin(), it)); + } if (mNeedsDisplayListDataSync) { mNeedsDisplayListDataSync = false; // Do a push pass on the old tree to handle freeing DisplayListData @@ -119,6 +139,34 @@ void RenderNode::pushStagingChanges(TreeInfo& info) { } } +class AnimateFunctor { +public: + AnimateFunctor(RenderProperties* target, TreeInfo& info) + : mTarget(target), mInfo(info) {} + + bool operator() (sp<RenderPropertyAnimator>& animator) { + bool finished = animator->animate(mTarget, mInfo); + if (finished && mInfo.animationListener) { + mInfo.animationListener->onAnimationFinished(animator); + } + return finished; + } +private: + RenderProperties* mTarget; + TreeInfo& mInfo; +}; + +void RenderNode::evaluateAnimations(TreeInfo& info) { + if (!mAnimators.size()) return; + + AnimateFunctor functor(&mProperties, info); + std::vector< sp<RenderPropertyAnimator> >::iterator newEnd; + newEnd = std::remove_if(mAnimators.begin(), mAnimators.end(), functor); + mAnimators.erase(newEnd, mAnimators.end()); + mProperties.updateMatrix(); + info.hasAnimations |= mAnimators.size(); +} + void RenderNode::prepareSubTree(TreeInfo& info, DisplayListData* subtree) { if (subtree) { TextureCache& cache = Caches::getInstance().textureCache; diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h index b9edbe5..294f436 100644 --- a/libs/hwui/RenderNode.h +++ b/libs/hwui/RenderNode.h @@ -20,6 +20,9 @@ #define LOG_TAG "OpenGLRenderer" #endif +#include <set> +#include <vector> + #include <SkCamera.h> #include <SkMatrix.h> @@ -41,6 +44,7 @@ #include "DeferredDisplayList.h" #include "DisplayList.h" #include "RenderProperties.h" +#include "TreeInfo.h" #include "utils/VirtualLightRefBase.h" class SkBitmap; @@ -65,17 +69,6 @@ class SaveOp; class RestoreToCountOp; class DrawDisplayListOp; -struct TreeInfo { - TreeInfo() - : hasFunctors(false) - , prepareTextures(false) - {} - - bool hasFunctors; - bool prepareTextures; - // TODO: Damage calculations? Flag to skip staging pushes for RT animations? -}; - /** * Primary class for storing recorded canvas commands, as well as per-View/ViewGroup display properties. * @@ -91,7 +84,7 @@ struct TreeInfo { class RenderNode : public VirtualLightRefBase { public: ANDROID_API RenderNode(); - ANDROID_API ~RenderNode(); + ANDROID_API virtual ~RenderNode(); // See flags defined in DisplayList.java enum ReplayFlag { @@ -152,7 +145,19 @@ public: return properties().getHeight(); } - ANDROID_API void prepareTree(TreeInfo& info); + ANDROID_API virtual void prepareTree(TreeInfo& info); + + // UI thread only! + ANDROID_API void addAnimator(const sp<RenderPropertyAnimator>& animator) { + mStagingAnimators.insert(animator); + mNeedsAnimatorsSync = true; + } + + // UI thread only! + ANDROID_API void removeAnimator(const sp<RenderPropertyAnimator>& animator) { + mStagingAnimators.erase(animator); + mNeedsAnimatorsSync = true; + } private: typedef key_value_pair_t<float, DrawDisplayListOp*> ZDrawDisplayListOpPair; @@ -214,6 +219,7 @@ private: void prepareTreeImpl(TreeInfo& info); void pushStagingChanges(TreeInfo& info); + void evaluateAnimations(TreeInfo& info); void prepareSubTree(TreeInfo& info, DisplayListData* subtree); String8 mName; @@ -226,6 +232,10 @@ private: DisplayListData* mDisplayListData; DisplayListData* mStagingDisplayListData; + bool mNeedsAnimatorsSync; + std::set< sp<RenderPropertyAnimator> > mStagingAnimators; + std::vector< sp<RenderPropertyAnimator> > mAnimators; + /** * Draw time state - these properties are only set and used during rendering */ diff --git a/libs/hwui/RenderProperties.cpp b/libs/hwui/RenderProperties.cpp index 9ec7297..99de1fc 100644 --- a/libs/hwui/RenderProperties.cpp +++ b/libs/hwui/RenderProperties.cpp @@ -51,7 +51,8 @@ RenderProperties::PrimitiveFields::PrimitiveFields() RenderProperties::ComputedFields::ComputedFields() : mTransformMatrix(NULL) - , mClipPath(NULL) { + , mClipPath(NULL) + , mClipPathOp(SkRegion::kIntersect_Op) { } RenderProperties::ComputedFields::~ComputedFields() { diff --git a/libs/hwui/RenderProperties.h b/libs/hwui/RenderProperties.h index 8fc2dd0..6fc8bce 100644 --- a/libs/hwui/RenderProperties.h +++ b/libs/hwui/RenderProperties.h @@ -16,7 +16,9 @@ #ifndef RENDERNODEPROPERTIES_H #define RENDERNODEPROPERTIES_H +#include <algorithm> #include <stddef.h> +#include <vector> #include <cutils/compiler.h> #include <androidfw/ResourceTypes.h> @@ -24,6 +26,7 @@ #include <SkMatrix.h> #include <SkRegion.h> +#include "Animator.h" #include "Rect.h" #include "RevealClip.h" #include "Outline.h" @@ -149,6 +152,31 @@ public: return mPrimitiveFields.mTranslationZ; } + // Animation helper + void setX(float value) { + setTranslationX(value - getLeft()); + } + + // Animation helper + float getX() const { + return getLeft() + getTranslationX(); + } + + // Animation helper + void setY(float value) { + setTranslationY(value - getTop()); + } + + // Animation helper + float getY() const { + return getTop() + getTranslationY(); + } + + // Animation helper + void setZ(float value) { + setTranslationZ(value - getElevation()); + } + float getZ() const { return getElevation() + getTranslationZ(); } @@ -457,7 +485,6 @@ private: bool mCaching; } mPrimitiveFields; - // mCameraDistance isn't in mPrimitiveFields as it has a complex setter SkMatrix* mStaticMatrix; SkMatrix* mAnimationMatrix; diff --git a/libs/hwui/TreeInfo.h b/libs/hwui/TreeInfo.h new file mode 100644 index 0000000..8957607 --- /dev/null +++ b/libs/hwui/TreeInfo.h @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef TREEINFO_H +#define TREEINFO_H + +#include <cutils/compiler.h> +#include <utils/Timers.h> +#include <utils/StrongPointer.h> + +namespace android { +namespace uirenderer { + +class RenderPropertyAnimator; + +class AnimationListener { +public: + ANDROID_API virtual void onAnimationFinished(const sp<RenderPropertyAnimator>&) = 0; +protected: + ANDROID_API virtual ~AnimationListener() {} +}; + +struct TreeInfo { + // The defaults here should be safe for everyone but DrawFrameTask to use as-is. + TreeInfo() + : hasFunctors(false) + , prepareTextures(false) + , performStagingPush(true) + , frameTimeMs(0) + , evaluateAnimations(false) + , hasAnimations(false) + , animationListener(0) + {} + + bool hasFunctors; + bool prepareTextures; + bool performStagingPush; + + // Animations + nsecs_t frameTimeMs; + bool evaluateAnimations; + // This is only updated if evaluateAnimations is true + bool hasAnimations; + AnimationListener* animationListener; + + // TODO: Damage calculations +}; + +} /* namespace uirenderer */ +} /* namespace android */ + +#endif /* TREEINFO_H */ diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index 5ce7ba6..63f4b95 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -308,18 +308,20 @@ bool GlobalContext::enableDirtyRegions(EGLSurface surface) { return value == EGL_BUFFER_PRESERVED; } -CanvasContext::CanvasContext(bool translucent) +CanvasContext::CanvasContext(bool translucent, RenderNode* rootRenderNode) : mRenderThread(RenderThread::getInstance()) , mEglSurface(EGL_NO_SURFACE) , mDirtyRegionsEnabled(false) , mOpaque(!translucent) , mCanvas(0) - , mHaveNewSurface(false) { + , mHaveNewSurface(false) + , mRootRenderNode(rootRenderNode) { mGlobalContext = GlobalContext::get(); } CanvasContext::~CanvasContext() { destroyCanvasAndSurface(); + mRenderThread.removeFrameCallback(this); } void CanvasContext::destroyCanvasAndSurface() { @@ -403,7 +405,16 @@ void CanvasContext::processLayerUpdates(const Vector<DeferredLayerUpdater*>* lay } } -void CanvasContext::drawDisplayList(RenderNode* displayList, Rect* dirty) { +void CanvasContext::prepareTree(TreeInfo& info) { + mRootRenderNode->prepareTree(info); + + if (info.hasAnimations && !info.hasFunctors) { + // TODO: Functors + mRenderThread.postFrameCallback(this); + } +} + +void CanvasContext::draw(Rect* dirty) { LOG_ALWAYS_FATAL_IF(!mCanvas || mEglSurface == EGL_NO_SURFACE, "drawDisplayList called on a context with no canvas or surface!"); @@ -417,7 +428,7 @@ void CanvasContext::drawDisplayList(RenderNode* displayList, Rect* dirty) { } status_t status; - if (dirty) { + if (dirty && !dirty->isEmpty()) { status = mCanvas->prepareDirty(dirty->left, dirty->top, dirty->right, dirty->bottom, mOpaque); } else { @@ -425,7 +436,7 @@ void CanvasContext::drawDisplayList(RenderNode* displayList, Rect* dirty) { } Rect outBounds; - status |= mCanvas->drawDisplayList(displayList, outBounds); + status |= mCanvas->drawDisplayList(mRootRenderNode.get(), outBounds); // TODO: Draw debug info // TODO: Performance tracking @@ -437,6 +448,20 @@ void CanvasContext::drawDisplayList(RenderNode* displayList, Rect* dirty) { } } +// Called by choreographer to do an RT-driven animation +void CanvasContext::doFrame(nsecs_t frameTimeNanos) { + ATRACE_CALL(); + + TreeInfo info; + info.evaluateAnimations = true; + info.frameTimeMs = nanoseconds_to_milliseconds(frameTimeNanos); + info.performStagingPush = false; + info.prepareTextures = false; + + prepareTree(info); + draw(NULL); +} + void CanvasContext::invokeFunctor(Functor* functor) { ATRACE_CALL(); DrawGlInfo::Mode mode = DrawGlInfo::kModeProcessNoContext; diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h index a3fe591..0873ad4 100644 --- a/libs/hwui/renderthread/CanvasContext.h +++ b/libs/hwui/renderthread/CanvasContext.h @@ -25,6 +25,7 @@ #include "../RenderNode.h" #include "RenderTask.h" +#include "RenderThread.h" #define FUNCTOR_PROCESS_DELAY 4 @@ -39,15 +40,13 @@ class Layer; namespace renderthread { class GlobalContext; -class CanvasContext; -class RenderThread; // This per-renderer class manages the bridge between the global EGL context // and the render surface. -class CanvasContext { +class CanvasContext : public IFrameCallback { public: - CanvasContext(bool translucent); - ~CanvasContext(); + CanvasContext(bool translucent, RenderNode* rootRenderNode); + virtual ~CanvasContext(); bool initialize(EGLNativeWindowType window); void updateSurface(EGLNativeWindowType window); @@ -55,9 +54,13 @@ public: void setup(int width, int height); void makeCurrent(); void processLayerUpdates(const Vector<DeferredLayerUpdater*>* layerUpdaters, TreeInfo& info); - void drawDisplayList(RenderNode* displayList, Rect* dirty); + void prepareTree(TreeInfo& info); + void draw(Rect* dirty); void destroyCanvasAndSurface(); + // IFrameCallback, Chroreographer-driven frame callback entry point + virtual void doFrame(nsecs_t frameTimeNanos); + bool copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap); void invokeFunctor(Functor* functor); @@ -82,6 +85,8 @@ private: bool mOpaque; OpenGLRenderer* mCanvas; bool mHaveNewSurface; + + const sp<RenderNode> mRootRenderNode; }; } /* namespace renderthread */ diff --git a/libs/hwui/renderthread/DrawFrameTask.cpp b/libs/hwui/renderthread/DrawFrameTask.cpp index f542d43..ff4be71 100644 --- a/libs/hwui/renderthread/DrawFrameTask.cpp +++ b/libs/hwui/renderthread/DrawFrameTask.cpp @@ -30,7 +30,7 @@ namespace android { namespace uirenderer { namespace renderthread { -DrawFrameTask::DrawFrameTask() : mContext(0), mRenderNode(0) { +DrawFrameTask::DrawFrameTask() : mContext(0) { } DrawFrameTask::~DrawFrameTask() { @@ -55,25 +55,17 @@ void DrawFrameTask::removeLayer(DeferredLayerUpdater* layer) { } } -void DrawFrameTask::setRenderNode(RenderNode* renderNode) { - LOG_ALWAYS_FATAL_IF(!mContext, "Lifecycle violation, there's no context to setRenderNode with!"); - - mRenderNode = renderNode; -} - void DrawFrameTask::setDirty(int left, int top, int right, int bottom) { mDirty.set(left, top, right, bottom); } void DrawFrameTask::drawFrame(RenderThread* renderThread) { - LOG_ALWAYS_FATAL_IF(!mRenderNode.get(), "Cannot drawFrame with no render node!"); LOG_ALWAYS_FATAL_IF(!mContext, "Cannot drawFrame with no CanvasContext!"); postAndWait(renderThread); // Reset the single-frame data mDirty.setEmpty(); - mRenderNode = 0; } void DrawFrameTask::postAndWait(RenderThread* renderThread) { @@ -88,8 +80,7 @@ void DrawFrameTask::run() { bool canUnblockUiThread = syncFrameState(); // Grab a copy of everything we need - Rect dirtyCopy(mDirty); - sp<RenderNode> renderNode = mRenderNode; + Rect dirty(mDirty); CanvasContext* context = mContext; // From this point on anything in "this" is *UNSAFE TO ACCESS* @@ -97,15 +88,20 @@ void DrawFrameTask::run() { unblockUiThread(); } - drawRenderNode(context, renderNode.get(), &dirtyCopy); + context->draw(&dirty); if (!canUnblockUiThread) { unblockUiThread(); } } -static void prepareTreeInfo(TreeInfo& info) { +static void initTreeInfo(TreeInfo& info) { info.prepareTextures = true; + info.performStagingPush = true; + info.evaluateAnimations = true; + // TODO: Get this from Choreographer + nsecs_t frameTimeNs = systemTime(CLOCK_MONOTONIC); + info.frameTimeMs = nanoseconds_to_milliseconds(frameTimeNs); } bool DrawFrameTask::syncFrameState() { @@ -113,9 +109,9 @@ bool DrawFrameTask::syncFrameState() { mContext->makeCurrent(); Caches::getInstance().textureCache.resetMarkInUse(); TreeInfo info; - prepareTreeInfo(info); + initTreeInfo(info); mContext->processLayerUpdates(&mLayers, info); - mRenderNode->prepareTree(info); + mContext->prepareTree(info); // If prepareTextures is false, we ran out of texture cache space return !info.hasFunctors && info.prepareTextures; } @@ -125,16 +121,6 @@ void DrawFrameTask::unblockUiThread() { mSignal.signal(); } -void DrawFrameTask::drawRenderNode(CanvasContext* context, RenderNode* renderNode, Rect* dirty) { - ATRACE_CALL(); - - if (dirty->bottom == -1 && dirty->left == -1 - && dirty->top == -1 && dirty->right == -1) { - dirty = 0; - } - context->drawDisplayList(renderNode, dirty); -} - } /* namespace renderthread */ } /* namespace uirenderer */ } /* namespace android */ diff --git a/libs/hwui/renderthread/DrawFrameTask.h b/libs/hwui/renderthread/DrawFrameTask.h index 055d4cf..c280868 100644 --- a/libs/hwui/renderthread/DrawFrameTask.h +++ b/libs/hwui/renderthread/DrawFrameTask.h @@ -53,7 +53,6 @@ public: void addLayer(DeferredLayerUpdater* layer); void removeLayer(DeferredLayerUpdater* layer); - void setRenderNode(RenderNode* renderNode); void setDirty(int left, int top, int right, int bottom); void drawFrame(RenderThread* renderThread); @@ -63,7 +62,6 @@ private: void postAndWait(RenderThread* renderThread); bool syncFrameState(); void unblockUiThread(); - static void drawRenderNode(CanvasContext* context, RenderNode* renderNode, Rect* dirty); Mutex mLock; Condition mSignal; @@ -73,7 +71,6 @@ private: /********************************************* * Single frame data *********************************************/ - sp<RenderNode> mRenderNode; Rect mDirty; /********************************************* diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp index ce490f1..87886e6 100644 --- a/libs/hwui/renderthread/RenderProxy.cpp +++ b/libs/hwui/renderthread/RenderProxy.cpp @@ -51,15 +51,16 @@ namespace renderthread { MethodInvokeRenderTask* task = new MethodInvokeRenderTask((RunnableMethod) Bridge_ ## method); \ ARGS(method) *args = (ARGS(method) *) task->payload() -CREATE_BRIDGE1(createContext, bool translucent) { - return new CanvasContext(args->translucent); +CREATE_BRIDGE2(createContext, bool translucent, RenderNode* rootRenderNode) { + return new CanvasContext(args->translucent, args->rootRenderNode); } -RenderProxy::RenderProxy(bool translucent) +RenderProxy::RenderProxy(bool translucent, RenderNode* rootRenderNode) : mRenderThread(RenderThread::getInstance()) , mContext(0) { SETUP_TASK(createContext); args->translucent = translucent; + args->rootRenderNode = rootRenderNode; mContext = (CanvasContext*) postAndWait(task); mDrawFrameTask.setContext(mContext); } @@ -133,9 +134,8 @@ void RenderProxy::setup(int width, int height) { post(task); } -void RenderProxy::drawDisplayList(RenderNode* displayList, +void RenderProxy::syncAndDrawFrame( int dirtyLeft, int dirtyTop, int dirtyRight, int dirtyBottom) { - mDrawFrameTask.setRenderNode(displayList); mDrawFrameTask.setDirty(dirtyLeft, dirtyTop, dirtyRight, dirtyBottom); mDrawFrameTask.drawFrame(&mRenderThread); } diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h index a112493..eab1395 100644 --- a/libs/hwui/renderthread/RenderProxy.h +++ b/libs/hwui/renderthread/RenderProxy.h @@ -56,14 +56,14 @@ class RenderProxyBridge; */ class ANDROID_API RenderProxy { public: - ANDROID_API RenderProxy(bool translucent); + ANDROID_API RenderProxy(bool translucent, RenderNode* rootNode); ANDROID_API virtual ~RenderProxy(); ANDROID_API bool initialize(const sp<ANativeWindow>& window); ANDROID_API void updateSurface(const sp<ANativeWindow>& window); ANDROID_API void pauseSurface(const sp<ANativeWindow>& window); ANDROID_API void setup(int width, int height); - ANDROID_API void drawDisplayList(RenderNode* displayList, + ANDROID_API void syncAndDrawFrame( int dirtyLeft, int dirtyTop, int dirtyRight, int dirtyBottom); ANDROID_API void destroyCanvasAndSurface(); diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp index 212f475..e95707a 100644 --- a/libs/hwui/renderthread/RenderThread.cpp +++ b/libs/hwui/renderthread/RenderThread.cpp @@ -18,9 +18,11 @@ #include "RenderThread.h" +#include <gui/DisplayEventReceiver.h> +#include <utils/Log.h> + #include "CanvasContext.h" #include "RenderProxy.h" -#include <utils/Log.h> namespace android { using namespace uirenderer::renderthread; @@ -29,6 +31,14 @@ ANDROID_SINGLETON_STATIC_INSTANCE(RenderThread); namespace uirenderer { namespace renderthread { +// Number of events to read at a time from the DisplayEventReceiver pipe. +// The value should be large enough that we can quickly drain the pipe +// using just a few large reads. +static const size_t EVENT_BUFFER_SIZE = 100; + +// Slight delay to give the UI time to push us a new frame before we replay +static const int DISPATCH_FRAME_CALLBACKS_DELAY = 0; + TaskQueue::TaskQueue() : mHead(0), mTail(0) {} RenderTask* TaskQueue::next() { @@ -103,8 +113,25 @@ void TaskQueue::remove(RenderTask* task) { } } +class DispatchFrameCallbacks : public RenderTask { +private: + RenderThread* mRenderThread; +public: + DispatchFrameCallbacks(RenderThread* rt) : mRenderThread(rt) {} + + virtual void run() { + mRenderThread->dispatchFrameCallbacks(); + } +}; + RenderThread::RenderThread() : Thread(true), Singleton<RenderThread>() - , mNextWakeup(LLONG_MAX) { + , mNextWakeup(LLONG_MAX) + , mDisplayEventReceiver(0) + , mVsyncRequested(false) + , mFrameCallbackTaskPending(false) + , mFrameCallbackTask(0) + , mFrameTime(0) { + mFrameCallbackTask = new DispatchFrameCallbacks(this); mLooper = new Looper(false); run("RenderThread"); } @@ -112,10 +139,86 @@ RenderThread::RenderThread() : Thread(true), Singleton<RenderThread>() RenderThread::~RenderThread() { } +void RenderThread::initializeDisplayEventReceiver() { + LOG_ALWAYS_FATAL_IF(mDisplayEventReceiver, "Initializing a second DisplayEventReceiver?"); + mDisplayEventReceiver = new DisplayEventReceiver(); + status_t status = mDisplayEventReceiver->initCheck(); + LOG_ALWAYS_FATAL_IF(status != NO_ERROR, "Initialization of DisplayEventReceiver " + "failed with status: %d", status); + + // Register the FD + mLooper->addFd(mDisplayEventReceiver->getFd(), 0, + Looper::EVENT_INPUT, RenderThread::displayEventReceiverCallback, this); +} + +int RenderThread::displayEventReceiverCallback(int fd, int events, void* data) { + if (events & (Looper::EVENT_ERROR | Looper::EVENT_HANGUP)) { + ALOGE("Display event receiver pipe was closed or an error occurred. " + "events=0x%x", events); + return 0; // remove the callback + } + + if (!(events & Looper::EVENT_INPUT)) { + ALOGW("Received spurious callback for unhandled poll event. " + "events=0x%x", events); + return 1; // keep the callback + } + + reinterpret_cast<RenderThread*>(data)->drainDisplayEventQueue(); + + return 1; // keep the callback +} + +static nsecs_t latestVsyncEvent(DisplayEventReceiver* receiver) { + DisplayEventReceiver::Event buf[EVENT_BUFFER_SIZE]; + nsecs_t latest = 0; + ssize_t n; + while ((n = receiver->getEvents(buf, EVENT_BUFFER_SIZE)) > 0) { + for (ssize_t i = 0; i < n; i++) { + const DisplayEventReceiver::Event& ev = buf[i]; + switch (ev.header.type) { + case DisplayEventReceiver::DISPLAY_EVENT_VSYNC: + latest = ev.header.timestamp; + break; + } + } + } + if (n < 0) { + ALOGW("Failed to get events from display event receiver, status=%d", status_t(n)); + } + return latest; +} + +void RenderThread::drainDisplayEventQueue() { + nsecs_t vsyncEvent = latestVsyncEvent(mDisplayEventReceiver); + if (vsyncEvent > 0) { + mVsyncRequested = false; + mFrameTime = vsyncEvent; + if (!mFrameCallbackTaskPending) { + mFrameCallbackTaskPending = true; + //queueDelayed(mFrameCallbackTask, DISPATCH_FRAME_CALLBACKS_DELAY); + queue(mFrameCallbackTask); + } + } +} + +void RenderThread::dispatchFrameCallbacks() { + mFrameCallbackTaskPending = false; + + std::set<IFrameCallback*> callbacks; + mFrameCallbacks.swap(callbacks); + + for (std::set<IFrameCallback*>::iterator it = callbacks.begin(); it != callbacks.end(); it++) { + (*it)->doFrame(mFrameTime); + } +} + bool RenderThread::threadLoop() { + initializeDisplayEventReceiver(); + int timeoutMillis = -1; for (;;) { - int result = mLooper->pollAll(timeoutMillis); + int result = mLooper->pollOnce(timeoutMillis); LOG_ALWAYS_FATAL_IF(result == Looper::POLL_ERROR, "RenderThread Looper POLL_ERROR!"); @@ -159,6 +262,20 @@ void RenderThread::remove(RenderTask* task) { mQueue.remove(task); } +void RenderThread::postFrameCallback(IFrameCallback* callback) { + mFrameCallbacks.insert(callback); + if (!mVsyncRequested) { + mVsyncRequested = true; + status_t status = mDisplayEventReceiver->requestNextVsync(); + LOG_ALWAYS_FATAL_IF(status != NO_ERROR, + "requestNextVsync failed with status: %d", status); + } +} + +void RenderThread::removeFrameCallback(IFrameCallback* callback) { + mFrameCallbacks.erase(callback); +} + RenderTask* RenderThread::nextTask(nsecs_t* nextWakeup) { AutoMutex _lock(mLock); RenderTask* next = mQueue.peek(); diff --git a/libs/hwui/renderthread/RenderThread.h b/libs/hwui/renderthread/RenderThread.h index e444aa0..b93dfd6 100644 --- a/libs/hwui/renderthread/RenderThread.h +++ b/libs/hwui/renderthread/RenderThread.h @@ -18,6 +18,10 @@ #define RENDERTHREAD_H_ #include "RenderTask.h" + +#include <memory> +#include <set> + #include <cutils/compiler.h> #include <utils/Looper.h> #include <utils/Mutex.h> @@ -25,9 +29,13 @@ #include <utils/Thread.h> namespace android { +class DisplayEventReceiver; + namespace uirenderer { namespace renderthread { +class DispatchFrameCallbacks; + class TaskQueue { public: TaskQueue(); @@ -42,6 +50,15 @@ private: RenderTask* mTail; }; +// Mimics android.view.Choreographer.FrameCallback +class IFrameCallback { +public: + virtual void doFrame(nsecs_t frameTimeNanos) = 0; + +protected: + ~IFrameCallback() {} +}; + class ANDROID_API RenderThread : public Thread, public Singleton<RenderThread> { public: // RenderThread takes complete ownership of tasks that are queued @@ -50,15 +67,25 @@ public: void queueDelayed(RenderTask* task, int delayMs); void remove(RenderTask* task); + // Mimics android.view.Choreographer + void postFrameCallback(IFrameCallback* callback); + void removeFrameCallback(IFrameCallback* callback); + protected: virtual bool threadLoop(); private: friend class Singleton<RenderThread>; + friend class DispatchFrameCallbacks; RenderThread(); virtual ~RenderThread(); + void initializeDisplayEventReceiver(); + static int displayEventReceiverCallback(int fd, int events, void* data); + void drainDisplayEventQueue(); + void dispatchFrameCallbacks(); + // Returns the next task to be run. If this returns NULL nextWakeup is set // to the time to requery for the nextTask to run. mNextWakeup is also // set to this time @@ -69,6 +96,13 @@ private: nsecs_t mNextWakeup; TaskQueue mQueue; + + DisplayEventReceiver* mDisplayEventReceiver; + bool mVsyncRequested; + std::set<IFrameCallback*> mFrameCallbacks; + bool mFrameCallbackTaskPending; + DispatchFrameCallbacks* mFrameCallbackTask; + nsecs_t mFrameTime; }; } /* namespace renderthread */ diff --git a/tests/RenderThreadTest/src/com/example/renderthread/MainActivity.java b/tests/RenderThreadTest/src/com/example/renderthread/MainActivity.java index 09531fd..1d209dd 100644 --- a/tests/RenderThreadTest/src/com/example/renderthread/MainActivity.java +++ b/tests/RenderThreadTest/src/com/example/renderthread/MainActivity.java @@ -1,17 +1,13 @@ package com.example.renderthread; -import android.animation.TimeInterpolator; import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.os.Handler; -import android.os.SystemClock; -import android.view.RenderNode; import android.view.HardwareRenderer; -import android.view.ThreadedRenderer; +import android.view.RenderNodeAnimator; import android.view.View; -import android.view.animation.AccelerateDecelerateInterpolator; import android.widget.AdapterView; import android.widget.AdapterView.OnItemClickListener; import android.widget.ListView; @@ -23,6 +19,8 @@ import java.util.Map; public class MainActivity extends Activity implements OnItemClickListener { + static final int TRANSLATION_Y = 1; + static final int DELTA_TYPE_DELTA = 1; static final int DURATION = 400; static final String KEY_NAME = "name"; @@ -66,82 +64,21 @@ public class MainActivity extends Activity implements OnItemClickListener { } } - private static class DisplayListAnimator { - private static final TimeInterpolator sDefaultInterpolator = - new AccelerateDecelerateInterpolator(); - - RenderNode mDisplayList; - float mFromValue; - float mDelta; - long mDuration = DURATION * 2; - long mStartTime; - - DisplayListAnimator(View view, float translateXBy) { - mDelta = translateXBy; - mFromValue = view.getTranslationY(); - mDisplayList = view.getDisplayList(); - } - - boolean animate(long currentTime) { - if (mStartTime == 0) mStartTime = currentTime; - - float fraction = (float)(currentTime - mStartTime) / mDuration; - if (fraction > 1) { - return false; - } - fraction = sDefaultInterpolator.getInterpolation(fraction); - float translation = mFromValue + (mDelta * fraction); - mDisplayList.setTranslationY(translation); - return fraction < 1f; - } - } - - private static class AnimationExecutor implements Runnable { - DisplayListAnimator[] mAnimations; - ThreadedRenderer mRenderer; - - AnimationExecutor(ThreadedRenderer renderer, DisplayListAnimator[] animations) { - mRenderer = renderer; - mAnimations = animations; - ThreadedRenderer.postToRenderThread(this); - } - - @Override - public void run() { - boolean hasMore = false; - long now = SystemClock.uptimeMillis(); - for (DisplayListAnimator animator : mAnimations) { - hasMore |= animator.animate(now); - } - mRenderer.repeatLastDraw(); - if (hasMore) { - ThreadedRenderer.postToRenderThread(this); - } - } - - } - @Override public void onItemClick(final AdapterView<?> adapterView, View clickedView, int clickedPosition, long clickedId) { int topPosition = adapterView.getFirstVisiblePosition(); int dy = adapterView.getHeight(); - final DisplayListAnimator[] animators = new DisplayListAnimator[adapterView.getChildCount()]; for (int i = 0; i < adapterView.getChildCount(); i++) { int pos = topPosition + i; View child = adapterView.getChildAt(i); float delta = (pos - clickedPosition) * 1.1f; if (delta == 0) delta = -1; - animators[i] = new DisplayListAnimator(child, dy * delta); + RenderNodeAnimator animator = new RenderNodeAnimator( + TRANSLATION_Y, DELTA_TYPE_DELTA, dy * delta); + animator.setDuration(DURATION); + animator.start(child); } - adapterView.invalidate(); - adapterView.post(new Runnable() { - - @Override - public void run() { - new AnimationExecutor((ThreadedRenderer) adapterView.getHardwareRenderer(), animators); - } - }); //mHandler.postDelayed(mLaunchActivity, (long) (DURATION * .4)); mLaunchActivity.run(); } |