add SurfaceView support for skottie on android
Change-Id: I62fe20cdaf735d09552349efb9a21b1ea72bd973
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/310136
Reviewed-by: Derek Sollenberger <djsollen@google.com>
Commit-Queue: Jorge Betancourt <jmbetancourt@google.com>
diff --git a/platform_tools/android/apps/skottie/skottielib/src/main/cpp/native-lib.cpp b/platform_tools/android/apps/skottie/skottielib/src/main/cpp/native-lib.cpp
index cce17d3..a612b41 100644
--- a/platform_tools/android/apps/skottie/skottielib/src/main/cpp/native-lib.cpp
+++ b/platform_tools/android/apps/skottie/skottielib/src/main/cpp/native-lib.cpp
@@ -178,7 +178,8 @@
jlong nativeProxy, jint width,
jint height,
jboolean wideColorGamut,
- jfloat progress) {
+ jfloat progress,
+ jint backgroundColor) {
ATRACE_NAME("SkottieDrawFrame");
if (!nativeProxy) {
return false;
@@ -220,7 +221,7 @@
nullptr, &props));
auto canvas = renderTarget->getCanvas();
- canvas->clear(SK_ColorTRANSPARENT);
+ canvas->clear(backgroundColor);
SkAutoCanvasRestore acr(canvas, true);
SkRect bounds = SkRect::MakeWH(width, height);
diff --git a/platform_tools/android/apps/skottie/skottielib/src/main/java/org/skia/skottie/SkottieRunner.java b/platform_tools/android/apps/skottie/skottielib/src/main/java/org/skia/skottie/SkottieRunner.java
index 925cce7..56cc117 100644
--- a/platform_tools/android/apps/skottie/skottielib/src/main/java/org/skia/skottie/SkottieRunner.java
+++ b/platform_tools/android/apps/skottie/skottielib/src/main/java/org/skia/skottie/SkottieRunner.java
@@ -14,6 +14,8 @@
import android.os.HandlerThread;
import android.util.Log;
import android.view.Choreographer;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
import android.view.TextureView;
import java.io.ByteArrayOutputStream;
@@ -82,12 +84,21 @@
}
/**
+ * Create a new animation by feeding data from "is" and replaying in a SurfaceView.
+ * State is controlled internally by SurfaceHolder.
+ */
+ public SkottieAnimation createAnimation(SurfaceView view, InputStream is, int backgroundColor) {
+ return new SkottieAnimationImpl(view, is, backgroundColor);
+ }
+
+ /**
* Pass a new SurfaceTexture: use this method only if managing TextureView outside
* SkottieRunner.
*/
public void updateAnimationSurface(Animatable animation, SurfaceTexture surfaceTexture,
int width, int height) {
- ((SkottieAnimationImpl) animation).updateSurface(surfaceTexture, width, height);
+ ((SkottieAnimationImpl) animation).setSurfaceTexture(surfaceTexture);
+ ((SkottieAnimationImpl) animation).updateSurface(width, height);
}
private SkottieRunner()
@@ -262,28 +273,44 @@
}
private class SkottieAnimationImpl implements SkottieAnimation, Choreographer.FrameCallback,
- TextureView.SurfaceTextureListener {
+ TextureView.SurfaceTextureListener, SurfaceHolder.Callback {
boolean mIsRunning = false;
SurfaceTexture mSurfaceTexture;
EGLSurface mEglSurface;
boolean mNewSurface = false;
+ SurfaceHolder mSurfaceHolder;
+ boolean mValidSurface = false;
private int mSurfaceWidth = 0;
private int mSurfaceHeight = 0;
+ private int mBackgroundColor;
private long mNativeProxy;
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
SkottieAnimationImpl(SurfaceTexture surfaceTexture, InputStream is) {
- init(surfaceTexture, is);
+ init(is);
+ mSurfaceTexture = surfaceTexture;
}
SkottieAnimationImpl(TextureView view, InputStream is) {
- init(view.getSurfaceTexture(), is);
+ init(is);
+ mSurfaceTexture = view.getSurfaceTexture();
view.setSurfaceTextureListener(this);
}
+ SkottieAnimationImpl(SurfaceView view, InputStream is, int backgroundColor) {
+ init(is);
+ mSurfaceHolder = view.getHolder();
+ mSurfaceHolder.addCallback(this);
+ mBackgroundColor = backgroundColor;
+ }
+
+ private void setSurfaceTexture(SurfaceTexture s) {
+ mSurfaceTexture = s;
+ }
+
private ByteBuffer convertToByteBuffer(InputStream is) throws IOException {
if (is instanceof FileInputStream) {
FileChannel fileChannel = ((FileInputStream)is).getChannel();
@@ -307,7 +334,7 @@
return buffer.asReadOnlyBuffer();
}
- private void init(SurfaceTexture surfaceTexture, InputStream is) {
+ private void init(InputStream is) {
ByteBuffer byteBuffer;
try {
@@ -319,7 +346,6 @@
long proxy = SkottieRunner.getInstance().getNativeProxy();
mNativeProxy = nCreateProxy(proxy, byteBuffer);
- mSurfaceTexture = surfaceTexture;
mDuration = nGetDuration(mNativeProxy);
mProgress = 0f;
}
@@ -335,10 +361,9 @@
}
}
- public void updateSurface(SurfaceTexture surfaceTexture, int width, int height) {
+ public void updateSurface(int width, int height) {
try {
runOnGLThread(() -> {
- mSurfaceTexture = surfaceTexture;
mSurfaceWidth = width;
mSurfaceHeight = height;
mNewSurface = true;
@@ -468,15 +493,16 @@
}
if (mEglSurface == null) {
+ // block for Texture Views
if (mSurfaceTexture != null) {
mEglSurface = mEgl.eglCreateWindowSurface(mEglDisplay, mEglConfig,
mSurfaceTexture, null);
- if (mEglSurface == null || mEglSurface == EGL10.EGL_NO_SURFACE) {
- // If failed to create a surface, log an error and stop the animation
- int error = mEgl.eglGetError();
- throw new RuntimeException("createWindowSurface failed "
- + GLUtils.getEGLErrorString(error));
- }
+ checkSurface();
+ // block for Surface Views
+ } else if (mSurfaceHolder != null) {
+ mEglSurface = mEgl.eglCreateWindowSurface(mEglDisplay, mEglConfig,
+ mSurfaceHolder, null);
+ checkSurface();
}
}
@@ -490,7 +516,7 @@
}
// only if nDrawFrames() returns true do we need to swap buffers
if(nDrawFrame(mNativeProxy, mSurfaceWidth, mSurfaceHeight, false,
- mProgress)) {
+ mProgress, mBackgroundColor)) {
if (!mEgl.eglSwapBuffers(mEglDisplay, mEglSurface)) {
int error = mEgl.eglGetError();
if (error == EGL10.EGL_BAD_SURFACE
@@ -528,6 +554,16 @@
}
}
+ private void checkSurface() throws RuntimeException {
+ // ensure eglSurface was created
+ if (mEglSurface == null || mEglSurface == EGL10.EGL_NO_SURFACE) {
+ // If failed to create a surface, log an error and stop the animation
+ int error = mEgl.eglGetError();
+ throw new RuntimeException("createWindowSurface failed "
+ + GLUtils.getEGLErrorString(error));
+ }
+ }
+
@Override
public void doFrame(long frameTimeNanos) {
if (mIsRunning) {
@@ -543,14 +579,17 @@
mAnimationStartTime += durationNS; // prevents overflow
}
}
-
- drawFrame();
+ if (mValidSurface) {
+ drawFrame();
+ }
}
@Override
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
// will be called on UI thread
- updateSurface(surface, width, height);
+ mValidSurface = true;
+ mSurfaceTexture = surface;
+ updateSurface(width, height);
}
@Override
@@ -563,6 +602,7 @@
public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
// will be called on UI thread
onSurfaceTextureAvailable(null, 0, 0);
+ mValidSurface = false;
return true;
}
@@ -571,10 +611,29 @@
}
+ // Inherited from SurfaceHolder
+ @Override
+ public void surfaceCreated(SurfaceHolder holder) {
+ mValidSurface = true;
+ }
+
+ @Override
+ public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
+ mSurfaceHolder = holder;
+ updateSurface(width, height);
+ }
+
+ @Override
+ public void surfaceDestroyed(SurfaceHolder holder) {
+ mValidSurface = false;
+ surfaceChanged(null, 0, 0, 0);
+ }
+
private native long nCreateProxy(long runner, ByteBuffer data);
private native void nDeleteProxy(long nativeProxy);
private native boolean nDrawFrame(long nativeProxy, int width, int height,
- boolean wideColorGamut, float progress);
+ boolean wideColorGamut, float progress,
+ int backgroundColor);
private native long nGetDuration(long nativeProxy);
}
diff --git a/platform_tools/android/apps/skottie/skottielib/src/main/java/org/skia/skottie/SkottieView.java b/platform_tools/android/apps/skottie/skottielib/src/main/java/org/skia/skottie/SkottieView.java
index e32b0d2..0a51f59 100644
--- a/platform_tools/android/apps/skottie/skottielib/src/main/java/org/skia/skottie/SkottieView.java
+++ b/platform_tools/android/apps/skottie/skottielib/src/main/java/org/skia/skottie/SkottieView.java
@@ -25,7 +25,8 @@
public class SkottieView extends FrameLayout {
private SkottieAnimation mAnimation;
- private View mbackingView;
+ private View mBackingView;
+ private int mBackgroundColor;
private static final int BACKING_VIEW_TEXTURE = 0;
private static final int BACKING_VIEW_SURFACE = 1;
@@ -39,17 +40,17 @@
try {
switch (a.getInteger(R.styleable.SkottieView_backing_view, -1)) {
case BACKING_VIEW_TEXTURE:
- mbackingView = new TextureView(context);
- ((TextureView) mbackingView).setOpaque(false);
+ mBackingView = new TextureView(context);
+ ((TextureView) mBackingView).setOpaque(false);
break;
case BACKING_VIEW_SURFACE:
- mbackingView = new SurfaceView(context);
- int bg = a.getColor(R.styleable.SkottieView_background_color, -1);
- if (bg == -1) {
+ mBackingView = new SurfaceView(context);
+ mBackgroundColor = a.getColor(R.styleable.SkottieView_background_color, -1);
+ if (mBackgroundColor == -1) {
throw new RuntimeException("background_color attribute "
+ "needed for SurfaceView");
}
- if (Color.alpha(bg) != 255) {
+ if (Color.alpha(mBackgroundColor) != 255) {
throw new RuntimeException("background_color cannot be transparent");
}
break;
@@ -69,37 +70,44 @@
// create the backing view
if (builder.advancedFeatures) {
// backing view must be SurfaceView
- mbackingView = new SurfaceView(context);
+ mBackingView = new SurfaceView(context);
} else {
- mbackingView = new TextureView(context);
- ((TextureView) mbackingView).setOpaque(false);
+ mBackingView = new TextureView(context);
+ ((TextureView) mBackingView).setOpaque(false);
}
initBackingView();
}
private void initBackingView() {
- mbackingView.setLayoutParams(new ViewGroup.LayoutParams(
+ mBackingView.setLayoutParams(new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT));
- addView(mbackingView);
+ addView(mBackingView);
}
//TODO handle SurfaceView
public void setSource(InputStream inputStream) {
- mAnimation = SkottieRunner.getInstance()
- .createAnimation(((TextureView) mbackingView), inputStream);
+ mAnimation = setSourceHelper(inputStream);
}
public void setSource(int resId) {
- InputStream inputStream = mbackingView.getResources().openRawResource(resId);
- mAnimation = SkottieRunner.getInstance()
- .createAnimation(((TextureView) mbackingView), inputStream);
+ InputStream inputStream = mBackingView.getResources().openRawResource(resId);
+ mAnimation = setSourceHelper(inputStream);
}
public void setSource(Context context, Uri uri) throws FileNotFoundException {
InputStream inputStream = context.getContentResolver().openInputStream(uri);
- mAnimation = SkottieRunner.getInstance()
- .createAnimation(((TextureView) mbackingView), inputStream);
+ mAnimation = setSourceHelper(inputStream);
+ }
+
+ private SkottieAnimation setSourceHelper(InputStream inputStream) {
+ if (mBackingView instanceof TextureView) {
+ return SkottieRunner.getInstance()
+ .createAnimation(((TextureView) mBackingView), inputStream);
+ } else {
+ return SkottieRunner.getInstance()
+ .createAnimation(((SurfaceView) mBackingView), inputStream, mBackgroundColor);
+ }
}
protected SkottieAnimation getSkottieAnimation() {