diff options
Diffstat (limited to 'platform_tools')
5 files changed, 156 insertions, 90 deletions
diff --git a/platform_tools/android/apps/skottie/src/main/cpp/native-lib.cpp b/platform_tools/android/apps/skottie/src/main/cpp/native-lib.cpp index d4b4a74544..e39f73395d 100644 --- a/platform_tools/android/apps/skottie/src/main/cpp/native-lib.cpp +++ b/platform_tools/android/apps/skottie/src/main/cpp/native-lib.cpp @@ -105,7 +105,7 @@ struct SkottieAnimation { extern "C" JNIEXPORT jlong JNICALL -Java_org_skia_skottie_SkottieRunner_00024SkottieAnimation_nCreateProxy(JNIEnv *env, jobject clazz, +Java_org_skia_skottie_SkottieRunner_00024SkottieAnimationImpl_nCreateProxy(JNIEnv *env, jobject clazz, jlong runner, jobject is, jbyteArray storage) { @@ -138,7 +138,7 @@ Java_org_skia_skottie_SkottieRunner_00024SkottieAnimation_nCreateProxy(JNIEnv *e extern "C" JNIEXPORT void JNICALL -Java_org_skia_skottie_SkottieRunner_00024SkottieAnimation_nDeleteProxy(JNIEnv *env, jclass clazz, +Java_org_skia_skottie_SkottieRunner_00024SkottieAnimationImpl_nDeleteProxy(JNIEnv *env, jclass clazz, jlong nativeProxy) { if (!nativeProxy) { return; @@ -149,11 +149,11 @@ Java_org_skia_skottie_SkottieRunner_00024SkottieAnimation_nDeleteProxy(JNIEnv *e extern "C" JNIEXPORT void JNICALL -Java_org_skia_skottie_SkottieRunner_00024SkottieAnimation_nDrawFrame(JNIEnv *env, jclass clazz, +Java_org_skia_skottie_SkottieRunner_00024SkottieAnimationImpl_nDrawFrame(JNIEnv *env, jclass clazz, jlong nativeProxy, jint width, jint height, jboolean wideColorGamut, - jlong frameTimeNanos) { + jfloat progress) { ATRACE_NAME("SkottieDrawFrame"); if (!nativeProxy) { return; @@ -188,18 +188,7 @@ Java_org_skia_skottie_SkottieRunner_00024SkottieAnimation_nDrawFrame(JNIEnv *env auto canvas = renderTarget->getCanvas(); canvas->clear(SK_ColorTRANSPARENT); if (skottieAnimation->mAnimation) { - SkMSec t = 0; - if (skottieAnimation->mTimeBase == 0.0f) { - // Reset the animation time. - skottieAnimation->mTimeBase = frameTimeNanos; - } else { - //convert from nanoseconds to milliseconds - t = (frameTimeNanos - skottieAnimation->mTimeBase) / 1000000; - } - //TODO: control repeat count - float intpart; - float animState = modff(t / skottieAnimation->mDuration, &intpart); - skottieAnimation->mAnimation->seek(animState); + skottieAnimation->mAnimation->seek(progress); SkAutoCanvasRestore acr(canvas, true); SkRect bounds = SkRect::MakeWH(width, height); @@ -208,3 +197,15 @@ Java_org_skia_skottie_SkottieRunner_00024SkottieAnimation_nDrawFrame(JNIEnv *env canvas->flush(); } + +extern "C" JNIEXPORT jlong +JNICALL +Java_org_skia_skottie_SkottieRunner_00024SkottieAnimationImpl_nGetDuration(JNIEnv *env, + jclass clazz, + jlong nativeProxy) { + if (!nativeProxy) { + return 0; + } + SkottieAnimation* skottieAnimation = reinterpret_cast<SkottieAnimation*>(nativeProxy); + return (jlong) skottieAnimation->mDuration; +} diff --git a/platform_tools/android/apps/skottie/src/main/java/org/skia/skottie/SkottieActivity.java b/platform_tools/android/apps/skottie/src/main/java/org/skia/skottie/SkottieActivity.java index 73afc3fe0d..4869476827 100644 --- a/platform_tools/android/apps/skottie/src/main/java/org/skia/skottie/SkottieActivity.java +++ b/platform_tools/android/apps/skottie/src/main/java/org/skia/skottie/SkottieActivity.java @@ -58,14 +58,16 @@ public class SkottieActivity extends Activity implements View.OnClickListener { }; for (int resId : rawAssets) { - SkottieView view = new SkottieView(this, getResources().openRawResource(resId)); + SkottieView view = new SkottieView(this); + view.setSource(getResources().openRawResource(resId)); mAnimations.add(view); } for (Uri uri : mAnimationFiles) { try { InputStream inputStream = getContentResolver().openInputStream(uri); - SkottieView view = new SkottieView(this, inputStream); + SkottieView view = new SkottieView(this); + view.setSource(inputStream); mAnimations.add(view); } catch (FileNotFoundException e) { e.printStackTrace(); @@ -118,7 +120,7 @@ public class SkottieActivity extends Activity implements View.OnClickListener { //start and show animations that were in the background for (SkottieView anyView : mAnimations) { if (anyView != oldView) { - anyView.start(); + anyView.getSkottieAnimation().start(); anyView.setVisibility(View.VISIBLE); } } @@ -128,7 +130,7 @@ public class SkottieActivity extends Activity implements View.OnClickListener { //stop and hide animations in the background for (SkottieView anyView : mAnimations) { if (anyView != view) { - anyView.stop(); + anyView.getSkottieAnimation().stop(); anyView.setVisibility(View.INVISIBLE); } } @@ -173,13 +175,13 @@ public class SkottieActivity extends Activity implements View.OnClickListener { private void startAnimation() { for (SkottieView view : mAnimations) { - view.start(); + view.getSkottieAnimation().start(); } } private void stopAnimation() { for (SkottieView view : mAnimations) { - view.stop(); + view.getSkottieAnimation().stop(); } } @@ -187,7 +189,8 @@ public class SkottieActivity extends Activity implements View.OnClickListener { InputStream inputStream = getContentResolver().openInputStream(uri); int animations = mAnimations.size(); if (animations < mRowCount * mColumnCount) { - SkottieView view = new SkottieView(this, inputStream); + SkottieView view = new SkottieView(this); + view.setSource(inputStream); int row = animations / mColumnCount, column = animations % mColumnCount; mAnimations.add(view); mAnimationFiles.add(uri); @@ -197,7 +200,7 @@ public class SkottieActivity extends Activity implements View.OnClickListener { } }); addView(view, row, column, true); - view.start(); + view.getSkottieAnimation().start(); } else { stopAnimation(); mAnimationFiles.add(uri); diff --git a/platform_tools/android/apps/skottie/src/main/java/org/skia/skottie/SkottieAnimation.java b/platform_tools/android/apps/skottie/src/main/java/org/skia/skottie/SkottieAnimation.java new file mode 100644 index 0000000000..4280e69d00 --- /dev/null +++ b/platform_tools/android/apps/skottie/src/main/java/org/skia/skottie/SkottieAnimation.java @@ -0,0 +1,30 @@ +package org.skia.skottie; + +import android.graphics.drawable.Animatable; +import android.support.annotation.FloatRange; + +public interface SkottieAnimation extends Animatable { + /** + * Gets animation duration. + * + * @return Animation duration in milliseconds. + */ + long getDuration(); + + /** + * Sets current animation progress. + * + * @param progress animation progress + * + */ + void setProgress(@FloatRange(from = 0, to = 1) float progress); + + + /** + * Gets current animation progress. + * + * @return animation progress. + */ + @FloatRange(from = 0, to = 1) float getProgress(); +} + diff --git a/platform_tools/android/apps/skottie/src/main/java/org/skia/skottie/SkottieRunner.java b/platform_tools/android/apps/skottie/src/main/java/org/skia/skottie/SkottieRunner.java index cd6da7287a..ebcd748aa9 100644 --- a/platform_tools/android/apps/skottie/src/main/java/org/skia/skottie/SkottieRunner.java +++ b/platform_tools/android/apps/skottie/src/main/java/org/skia/skottie/SkottieRunner.java @@ -59,8 +59,8 @@ public class SkottieRunner { * Create a new animation by feeding data from "is" and replaying in a TextureView. * TextureView is tracked internally for SurfaceTexture state. */ - public Animatable createAnimation(TextureView view, InputStream is) { - return new SkottieAnimation(view, is); + public SkottieAnimation createAnimation(TextureView view, InputStream is) { + return new SkottieAnimationImpl(view, is); } /** @@ -68,8 +68,8 @@ public class SkottieRunner { * SurfaceTexture is possibly taken from a TextureView and can be updated with * updateAnimationSurface. */ - public Animatable createAnimation(SurfaceTexture surfaceTexture, InputStream is) { - return new SkottieAnimation(surfaceTexture, is); + public SkottieAnimation createAnimation(SurfaceTexture surfaceTexture, InputStream is) { + return new SkottieAnimationImpl(surfaceTexture, is); } /** @@ -78,7 +78,7 @@ public class SkottieRunner { */ public void updateAnimationSurface(Animatable animation, SurfaceTexture surfaceTexture, int width, int height) { - ((SkottieAnimation) animation).updateSurface(surfaceTexture, width, height); + ((SkottieAnimationImpl) animation).updateSurface(surfaceTexture, width, height); } private SkottieRunner() @@ -252,7 +252,7 @@ public class SkottieRunner { } } - private class SkottieAnimation implements Animatable, Choreographer.FrameCallback, + private class SkottieAnimationImpl implements SkottieAnimation, Choreographer.FrameCallback, TextureView.SurfaceTextureListener { boolean mIsRunning = false; SurfaceTexture mSurfaceTexture; @@ -264,12 +264,15 @@ public class SkottieRunner { private long mNativeProxy; private InputStream mInputStream; private byte[] mTempStorage; + private long mDuration; // duration in ms of the animation + private float mProgress; // animation progress in the range of 0.0f to 1.0f + private long mAnimationStartTime; // time in System.nanoTime units, when started - SkottieAnimation(SurfaceTexture surfaceTexture, InputStream is) { + SkottieAnimationImpl(SurfaceTexture surfaceTexture, InputStream is) { init(surfaceTexture, is); } - SkottieAnimation(TextureView view, InputStream is) { + SkottieAnimationImpl(TextureView view, InputStream is) { init(view.getSurfaceTexture(), is); view.setSurfaceTextureListener(this); } @@ -280,6 +283,8 @@ public class SkottieRunner { long proxy = SkottieRunner.getInstance().getNativeProxy(); mNativeProxy = nCreateProxy(proxy, mInputStream, mTempStorage); mSurfaceTexture = surfaceTexture; + mDuration = nGetDuration(mNativeProxy); + mProgress = 0f; } @Override @@ -300,6 +305,8 @@ public class SkottieRunner { mSurfaceWidth = width; mSurfaceHeight = height; mNewSurface = true; + + drawFrame(); }); } catch (Throwable t) { @@ -313,9 +320,11 @@ public class SkottieRunner { try { runOnGLThread(() -> { if (!mIsRunning) { + long currentTime = System.nanoTime(); + mAnimationStartTime = currentTime - (long)(1000000 * mDuration * mProgress); mIsRunning = true; mNewSurface = true; - Choreographer.getInstance().postFrameCallback(this); + doFrame(currentTime); } }); } @@ -351,22 +360,35 @@ public class SkottieRunner { } @Override - public void doFrame(long frameTimeNanos) { + public long getDuration() { + return mDuration; + } + + @Override + public void setProgress(float progress) { try { - if (mIsRunning) { - // Schedule next frame. - Choreographer.getInstance().postFrameCallback(this); - } else { - // If animation stopped, release EGL surface. - if (mEglSurface != null) { - // Ensure we always have a valid surface & context. - mEgl.eglMakeCurrent(mEglDisplay, mPBufferSurface, mPBufferSurface, - mEglContext); - mEgl.eglDestroySurface(mEglDisplay, mEglSurface); - mEglSurface = null; + runOnGLThread(() -> { + mProgress = progress; + if (mIsRunning) { + mAnimationStartTime = System.nanoTime() + - (long)(1000000 * mDuration * mProgress); } - return; - } + drawFrame(); + }); + } + catch (Throwable t) { + Log.e(LOG_TAG, "setProgress failed", t); + throw new RuntimeException(t); + } + } + + @Override + public float getProgress() { + return mProgress; + } + + private void drawFrame() { + try { if (mNewSurface) { // if there is a new SurfaceTexture, we need to recreate the EGL surface. if (mEglSurface != null) { @@ -399,7 +421,7 @@ public class SkottieRunner { } nDrawFrame(mNativeProxy, mSurfaceWidth, mSurfaceHeight, false, - frameTimeNanos); + mProgress); if (!mEgl.eglSwapBuffers(mEglDisplay, mEglSurface)) { int error = mEgl.eglGetError(); if (error == EGL10.EGL_BAD_SURFACE @@ -418,14 +440,42 @@ public class SkottieRunner { throw new RuntimeException("Cannot swap buffers " + GLUtils.getEGLErrorString(error)); } + + // If animation stopped, release EGL surface. + if (!mIsRunning) { + // Ensure we always have a valid surface & context. + mEgl.eglMakeCurrent(mEglDisplay, mPBufferSurface, mPBufferSurface, + mEglContext); + mEgl.eglDestroySurface(mEglDisplay, mEglSurface); + mEglSurface = null; + } } } catch (Throwable t) { - Log.e(LOG_TAG, "doFrame failed", t); + Log.e(LOG_TAG, "drawFrame failed", t); mIsRunning = false; } } @Override + public void doFrame(long frameTimeNanos) { + if (mIsRunning) { + // Schedule next frame. + Choreographer.getInstance().postFrameCallback(this); + + // Advance animation. + long durationNS = mDuration * 1000000; + long timeSinceAnimationStartNS = frameTimeNanos - mAnimationStartTime; + long animationProgressNS = timeSinceAnimationStartNS % durationNS; + mProgress = animationProgressNS / (float)durationNS; + if (timeSinceAnimationStartNS > durationNS) { + mAnimationStartTime += durationNS; // prevents overflow + } + } + + drawFrame(); + } + + @Override public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) { // will be called on UI thread updateSurface(surface, width, height); @@ -452,7 +502,8 @@ public class SkottieRunner { private native long nCreateProxy(long runner, InputStream is, byte[] storage); private native void nDeleteProxy(long nativeProxy); private native void nDrawFrame(long nativeProxy, int width, int height, - boolean wideColorGamut, long frameTimeNanos); + boolean wideColorGamut, float progress); + private native long nGetDuration(long nativeProxy); } private static native long nCreateProxy(); diff --git a/platform_tools/android/apps/skottie/src/main/java/org/skia/skottie/SkottieView.java b/platform_tools/android/apps/skottie/src/main/java/org/skia/skottie/SkottieView.java index 5243e731d7..30602ea76b 100644 --- a/platform_tools/android/apps/skottie/src/main/java/org/skia/skottie/SkottieView.java +++ b/platform_tools/android/apps/skottie/src/main/java/org/skia/skottie/SkottieView.java @@ -8,63 +8,44 @@ package org.skia.skottie; import android.content.Context; -import android.graphics.drawable.Animatable; +import android.util.AttributeSet; import android.view.TextureView; -import android.view.ViewGroup; import java.io.InputStream; -public class SkottieView extends ViewGroup implements Animatable { +public class SkottieView extends TextureView { - private TextureView mTextureView; - private Animatable mAnimation; + private SkottieAnimation mAnimation; - public SkottieView(Context context, InputStream is) { + public SkottieView(Context context) { super(context); + init(); + } - mTextureView = new TextureView(context); - mTextureView.setOpaque(false); - mAnimation = SkottieRunner.getInstance().createAnimation(mTextureView, is); - addView(mTextureView, LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); + public SkottieView(Context context, AttributeSet attrs) { + super(context, attrs); + init(); } - /** - * Starts the animation. - */ - @Override - public void start() { - mAnimation.start(); + public SkottieView(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + init(); } - /** - * Stops the animation. - */ - @Override - public void stop() { - mAnimation.stop(); + public SkottieView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + init(); } - @Override - public boolean isRunning() { - return mAnimation.isRunning(); + private void init() { + setOpaque(false); } - /** - * Ask all children to measure themselves and compute the measurement of this - * layout based on the children. - */ - @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - mTextureView.measure(widthMeasureSpec, heightMeasureSpec); - int width = mTextureView.getMeasuredWidth(); - int height = mTextureView.getMeasuredHeight(); - setMeasuredDimension(width, height); + public void setSource(InputStream inputStream) { + mAnimation = SkottieRunner.getInstance().createAnimation(this, inputStream); } - @Override - protected void onLayout(boolean changed, int left, int top, int right, int bottom) { - if (changed) { // This is a new size or position for this view - mTextureView.layout(0, 0, right - left, bottom - top); - } + public SkottieAnimation getSkottieAnimation() { + return mAnimation; } } |