Skip to content

Commit

Permalink
Skottie Android demo app improvements
Browse files Browse the repository at this point in the history
Changed SkottieView base class to TextureView.
Implemented new SkottieAnimation interface, which allows to
get animation duration, set and get animation progress.
SkottieView will draw correctly even if animation is stopped.

Test: Ran Skottie app
Bug: skia:
Change-Id: I952793b75518107f515ddec8ad6524f4048f42e7
Reviewed-on: https://skia-review.googlesource.com/c/167945
Commit-Queue: Stan Iliev <stani@google.com>
Reviewed-by: Derek Sollenberger <djsollen@google.com>
Reviewed-by: Florin Malita <fmalita@chromium.org>
  • Loading branch information
staniliev authored and Skia Commit-Bot committed Nov 7, 2018
1 parent e475f4e commit 762ba10
Show file tree
Hide file tree
Showing 5 changed files with 156 additions and 90 deletions.
33 changes: 17 additions & 16 deletions platform_tools/android/apps/skottie/src/main/cpp/native-lib.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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) {

Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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);
Expand All @@ -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;
}
Original file line number Diff line number Diff line change
Expand Up @@ -58,14 +58,16 @@ private void populateGrid() {
};

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();
Expand Down Expand Up @@ -118,7 +120,7 @@ private void inflateView(SkottieView view) {
//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);
}
}
Expand All @@ -128,7 +130,7 @@ private void inflateView(SkottieView view) {
//stop and hide animations in the background
for (SkottieView anyView : mAnimations) {
if (anyView != view) {
anyView.stop();
anyView.getSkottieAnimation().stop();
anyView.setVisibility(View.INVISIBLE);
}
}
Expand Down Expand Up @@ -173,21 +175,22 @@ private void addView(SkottieView view, int row , int column, boolean addView) {

private void startAnimation() {
for (SkottieView view : mAnimations) {
view.start();
view.getSkottieAnimation().start();
}
}

private void stopAnimation() {
for (SkottieView view : mAnimations) {
view.stop();
view.getSkottieAnimation().stop();
}
}

private void addLottie(Uri uri) throws FileNotFoundException {
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);
Expand All @@ -197,7 +200,7 @@ public void onClick(View view){
}
});
addView(view, row, column, true);
view.start();
view.getSkottieAnimation().start();
} else {
stopAnimation();
mAnimationFiles.add(uri);
Expand Down
Original file line number Diff line number Diff line change
@@ -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();
}

Original file line number Diff line number Diff line change
Expand Up @@ -59,17 +59,17 @@ public static synchronized SkottieRunner getInstance() {
* 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);
}

/**
* Create a new animation by feeding data from "is" and replaying in a SurfaceTexture.
* 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);
}

/**
Expand All @@ -78,7 +78,7 @@ public Animatable createAnimation(SurfaceTexture surfaceTexture, InputStream is)
*/
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()
Expand Down Expand Up @@ -252,7 +252,7 @@ private void doFinishGL() {
}
}

private class SkottieAnimation implements Animatable, Choreographer.FrameCallback,
private class SkottieAnimationImpl implements SkottieAnimation, Choreographer.FrameCallback,
TextureView.SurfaceTextureListener {
boolean mIsRunning = false;
SurfaceTexture mSurfaceTexture;
Expand All @@ -264,12 +264,15 @@ private class SkottieAnimation implements Animatable, Choreographer.FrameCallbac
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);
}
Expand All @@ -280,6 +283,8 @@ private void init(SurfaceTexture surfaceTexture, InputStream is) {
long proxy = SkottieRunner.getInstance().getNativeProxy();
mNativeProxy = nCreateProxy(proxy, mInputStream, mTempStorage);
mSurfaceTexture = surfaceTexture;
mDuration = nGetDuration(mNativeProxy);
mProgress = 0f;
}

@Override
Expand All @@ -300,6 +305,8 @@ public void updateSurface(SurfaceTexture surfaceTexture, int width, int height)
mSurfaceWidth = width;
mSurfaceHeight = height;
mNewSurface = true;

drawFrame();
});
}
catch (Throwable t) {
Expand All @@ -313,9 +320,11 @@ public void start() {
try {
runOnGLThread(() -> {
if (!mIsRunning) {
long currentTime = System.nanoTime();
mAnimationStartTime = currentTime - (long)(1000000 * mDuration * mProgress);
mIsRunning = true;
mNewSurface = true;
Choreographer.getInstance().postFrameCallback(this);
doFrame(currentTime);
}
});
}
Expand Down Expand Up @@ -351,22 +360,35 @@ public boolean isRunning() {
}

@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) {
Expand Down Expand Up @@ -399,7 +421,7 @@ public void doFrame(long frameTimeNanos) {
}

nDrawFrame(mNativeProxy, mSurfaceWidth, mSurfaceHeight, false,
frameTimeNanos);
mProgress);
if (!mEgl.eglSwapBuffers(mEglDisplay, mEglSurface)) {
int error = mEgl.eglGetError();
if (error == EGL10.EGL_BAD_SURFACE
Expand All @@ -418,13 +440,41 @@ public void doFrame(long frameTimeNanos) {
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
Expand Down Expand Up @@ -452,7 +502,8 @@ public void onSurfaceTextureUpdated(SurfaceTexture surfaceTexture) {
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();
Expand Down
Loading

0 comments on commit 762ba10

Please sign in to comment.