move SkottieAnimation out of SkottieRunner to top level

This refactor adds a LOG_TAG and SkottieRunner member variable to SkottieAnimation so that it still has access to the EGL member variables.
The private keyword was removed from the SkottieRunner's EGL variables.

Methods in SkottieRunner that were made from private to protected:
getNativeProxy()
runOnGLThread()
Methods in SkottieAnimation that were made from private to protected:
SurfaceView constructor
setSurfaceTexture()

Change-Id: I9ddb167238fbc0e05f4d1cdcee67f6b288019e95
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/335667
Commit-Queue: Jorge Betancourt <jmbetancourt@google.com>
Reviewed-by: Florin Malita <fmalita@google.com>
This commit is contained in:
Jorge Betancourt 2020-11-23 16:42:03 -05:00 committed by Skia Commit-Bot
parent 641ff3b7fc
commit 4867834b85
4 changed files with 478 additions and 465 deletions

View File

@ -114,10 +114,10 @@ struct SkottieAnimation {
extern "C" JNIEXPORT jlong
JNICALL
Java_org_skia_skottie_SkottieRunner_00024SkottieAnimation_nCreateProxy(JNIEnv *env,
jobject clazz,
jlong runner,
jobject bufferObj) {
Java_org_skia_skottie_SkottieAnimation_nCreateProxy(JNIEnv *env,
jobject clazz,
jlong runner,
jobject bufferObj) {
if (!runner) {
return 0;
@ -163,8 +163,8 @@ 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,
jlong nativeProxy) {
Java_org_skia_skottie_SkottieAnimation_nDeleteProxy(JNIEnv *env, jclass clazz,
jlong nativeProxy) {
if (!nativeProxy) {
return;
}
@ -174,13 +174,13 @@ Java_org_skia_skottie_SkottieRunner_00024SkottieAnimation_nDeleteProxy(JNIEnv *e
extern "C" JNIEXPORT bool
JNICALL
Java_org_skia_skottie_SkottieRunner_00024SkottieAnimation_nDrawFrame(JNIEnv *env, jclass clazz,
jlong nativeProxy, jint width,
jint height,
jboolean wideColorGamut,
jfloat progress,
jint backgroundColor,
jboolean forceDraw) {
Java_org_skia_skottie_SkottieAnimation_nDrawFrame(JNIEnv *env, jclass clazz,
jlong nativeProxy, jint width,
jint height,
jboolean wideColorGamut,
jfloat progress,
jint backgroundColor,
jboolean forceDraw) {
ATRACE_NAME("SkottieDrawFrame");
if (!nativeProxy) {
return false;
@ -234,9 +234,9 @@ Java_org_skia_skottie_SkottieRunner_00024SkottieAnimation_nDrawFrame(JNIEnv *env
extern "C" JNIEXPORT jlong
JNICALL
Java_org_skia_skottie_SkottieRunner_00024SkottieAnimation_nGetDuration(JNIEnv *env,
jclass clazz,
jlong nativeProxy) {
Java_org_skia_skottie_SkottieAnimation_nGetDuration(JNIEnv *env,
jclass clazz,
jlong nativeProxy) {
if (!nativeProxy) {
return 0;
}

View File

@ -0,0 +1,455 @@
package org.skia.skottie;
import android.animation.Animator;
import android.animation.TimeInterpolator;
import android.graphics.SurfaceTexture;
import android.opengl.GLUtils;
import android.util.Log;
import android.view.Choreographer;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.TextureView;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.channels.FileChannel;
import javax.microedition.khronos.egl.EGL10;
import javax.microedition.khronos.egl.EGLSurface;
public class SkottieAnimation extends Animator implements Choreographer.FrameCallback,
TextureView.SurfaceTextureListener, SurfaceHolder.Callback {
private final SkottieRunner mRunner = SkottieRunner.getInstance();
private static final String LOG_TAG = "SkottiePlayer";
private boolean mIsRunning = false;
private SurfaceTexture mSurfaceTexture;
private EGLSurface mEglSurface;
private boolean mNewSurface = false;
private SurfaceHolder mSurfaceHolder;
boolean mValidSurface = false;
private int mRepeatCount;
private int mRepeatCounter;
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
SkottieAnimation(SurfaceTexture surfaceTexture, InputStream is) {
if (init(is)) {
mSurfaceTexture = surfaceTexture;
}
}
SkottieAnimation(TextureView view, InputStream is, int backgroundColor, int repeatCount) {
if (init(is)) {
mSurfaceTexture = view.getSurfaceTexture();
}
view.setSurfaceTextureListener(this);
mBackgroundColor = backgroundColor;
mRepeatCount = repeatCount;
mRepeatCounter = mRepeatCount;
}
SkottieAnimation(SurfaceView view, InputStream is, int backgroundColor, int repeatCount) {
if (init(is)) {
mSurfaceHolder = view.getHolder();
}
mSurfaceHolder.addCallback(this);
mBackgroundColor = backgroundColor;
mRepeatCount = repeatCount;
mRepeatCounter = mRepeatCount;
}
void setSurfaceTexture(SurfaceTexture s) {
mSurfaceTexture = s;
}
private ByteBuffer convertToByteBuffer(InputStream is) throws IOException {
if (is instanceof FileInputStream) {
FileChannel fileChannel = ((FileInputStream)is).getChannel();
return fileChannel.map(FileChannel.MapMode.READ_ONLY,
fileChannel.position(), fileChannel.size());
}
ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
byte[] tmpStorage = new byte[4096];
int bytesRead;
while ((bytesRead = is.read(tmpStorage, 0, tmpStorage.length)) != -1) {
byteStream.write(tmpStorage, 0, bytesRead);
}
byteStream.flush();
tmpStorage = byteStream.toByteArray();
ByteBuffer buffer = ByteBuffer.allocateDirect(tmpStorage.length);
buffer.order(ByteOrder.nativeOrder());
buffer.put(tmpStorage, 0, tmpStorage.length);
return buffer.asReadOnlyBuffer();
}
private boolean init(InputStream is) {
ByteBuffer byteBuffer;
try {
byteBuffer = convertToByteBuffer(is);
} catch (IOException e) {
Log.e(LOG_TAG, "failed to read input stream", e);
return false;
}
long proxy = mRunner.getNativeProxy();
mNativeProxy = nCreateProxy(proxy, byteBuffer);
mDuration = nGetDuration(mNativeProxy);
mProgress = 0f;
return true;
}
@Override
protected void finalize() throws Throwable {
try {
end();
nDeleteProxy(mNativeProxy);
mNativeProxy = 0;
} finally {
super.finalize();
}
}
// Always call this on GL thread
public void updateSurface(int width, int height) {
mSurfaceWidth = width;
mSurfaceHeight = height;
mNewSurface = true;
drawFrame();
}
@Override
public void start() {
try {
mRunner.runOnGLThread(() -> {
if (!mIsRunning) {
long currentTime = System.nanoTime();
mAnimationStartTime = currentTime - (long)(1000000 * mDuration * mProgress);
mIsRunning = true;
mNewSurface = true;
mRepeatCounter = mRepeatCount;
doFrame(currentTime);
}
});
}
catch (Throwable t) {
Log.e(LOG_TAG, "start failed", t);
throw new RuntimeException(t);
}
for (AnimatorListener l : this.getListeners()) {
l.onAnimationStart(this);
}
}
@Override
public void end() {
try {
mRunner.runOnGLThread(() -> {
mIsRunning = false;
if (mEglSurface != null) {
// Ensure we always have a valid surface & context.
mRunner.mEgl.eglMakeCurrent(mRunner.mEglDisplay, mRunner.mPBufferSurface,
mRunner.mPBufferSurface, mRunner.mEglContext);
mRunner.mEgl.eglDestroySurface(mRunner.mEglDisplay, mEglSurface);
mEglSurface = null;
}
});
}
catch (Throwable t) {
Log.e(LOG_TAG, "stop failed", t);
throw new RuntimeException(t);
}
for (AnimatorListener l : this.getListeners()) {
l.onAnimationEnd(this);
}
}
@Override
public void pause() {
try {
mRunner.runOnGLThread(() -> {
mIsRunning = false;
});
}
catch (Throwable t) {
Log.e(LOG_TAG, "pause failed", t);
throw new RuntimeException(t);
}
}
@Override
public void resume() {
try {
mRunner.runOnGLThread(() -> {
if (!mIsRunning) {
long currentTime = System.nanoTime();
mAnimationStartTime = currentTime - (long)(1000000 * mDuration * mProgress);
mIsRunning = true;
mNewSurface = true;
doFrame(currentTime);
}
});
}
catch (Throwable t) {
Log.e(LOG_TAG, "resume failed", t);
throw new RuntimeException(t);
}
}
// TODO: add support for start delay
@Override
public long getStartDelay() {
return 0;
}
// TODO: add support for start delay
@Override
public void setStartDelay(long startDelay) {
}
@Override
public Animator setDuration(long duration) {
return null;
}
@Override
public boolean isRunning() {
return mIsRunning;
}
@Override
public long getDuration() {
return mDuration;
}
@Override
public long getTotalDuration() {
if (mRepeatCount == -1) {
return DURATION_INFINITE;
}
// TODO: add start delay when implemented
return mDuration * (1 + mRepeatCount);
}
// TODO: support TimeInterpolators
@Override
public void setInterpolator(TimeInterpolator value) {
}
public void setProgress(float progress) {
try {
mRunner.runOnGLThread(() -> {
mProgress = progress;
if (mIsRunning) {
mAnimationStartTime = System.nanoTime()
- (long)(1000000 * mDuration * mProgress);
}
drawFrame();
});
}
catch (Throwable t) {
Log.e(LOG_TAG, "setProgress failed", t);
throw new RuntimeException(t);
}
}
public float getProgress() {
return mProgress;
}
private void drawFrame() {
try {
boolean forceDraw = false;
if (mNewSurface) {
forceDraw = true;
// if there is a new SurfaceTexture, we need to recreate the EGL surface.
if (mEglSurface != null) {
mRunner.mEgl.eglDestroySurface(mRunner.mEglDisplay, mEglSurface);
mEglSurface = null;
}
mNewSurface = false;
}
if (mEglSurface == null) {
// block for Texture Views
if (mSurfaceTexture != null) {
mEglSurface = mRunner.mEgl.eglCreateWindowSurface(mRunner.mEglDisplay,
mRunner.mEglConfig, mSurfaceTexture, null);
checkSurface();
// block for Surface Views
} else if (mSurfaceHolder != null) {
mEglSurface = mRunner.mEgl.eglCreateWindowSurface(mRunner.mEglDisplay,
mRunner.mEglConfig, mSurfaceHolder, null);
checkSurface();
}
}
if (mEglSurface != null) {
if (!mRunner.mEgl.eglMakeCurrent(mRunner.mEglDisplay, mEglSurface, mEglSurface,
mRunner.mEglContext)) {
// If eglMakeCurrent failed, recreate EGL surface on next frame.
Log.w(LOG_TAG, "eglMakeCurrent failed "
+ GLUtils.getEGLErrorString(mRunner.mEgl.eglGetError()));
mNewSurface = true;
return;
}
// only if nDrawFrames() returns true do we need to swap buffers
if(nDrawFrame(mNativeProxy, mSurfaceWidth, mSurfaceHeight, false,
mProgress, mBackgroundColor, forceDraw)) {
if (!mRunner.mEgl.eglSwapBuffers(mRunner.mEglDisplay, mEglSurface)) {
int error = mRunner.mEgl.eglGetError();
if (error == EGL10.EGL_BAD_SURFACE
|| error == EGL10.EGL_BAD_NATIVE_WINDOW) {
// For some reason our surface was destroyed. Recreate EGL surface
// on next frame.
mNewSurface = true;
// This really shouldn't happen, but if it does we can recover
// easily by just not trying to use the surface anymore
Log.w(LOG_TAG, "swapBuffers failed "
+ GLUtils.getEGLErrorString(error));
return;
}
// Some other fatal EGL error happened, log an error and stop the
// animation.
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.
mRunner.mEgl.eglMakeCurrent(mRunner.mEglDisplay, mRunner.mPBufferSurface,
mRunner.mPBufferSurface, mRunner.mEglContext);
mRunner.mEgl.eglDestroySurface(mRunner.mEglDisplay, mEglSurface);
mEglSurface = null;
}
}
} catch (Throwable t) {
Log.e(LOG_TAG, "drawFrame failed", t);
mIsRunning = false;
}
}
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 = mRunner.mEgl.eglGetError();
throw new RuntimeException("createWindowSurface failed "
+ GLUtils.getEGLErrorString(error));
}
}
@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
}
if (timeSinceAnimationStartNS > durationNS) {
if (mRepeatCounter > 0) {
mRepeatCounter--;
} else if (mRepeatCounter == 0) {
mIsRunning = false;
mProgress = 1;
}
}
}
if (mValidSurface) {
drawFrame();
}
}
@Override
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
// will be called on UI thread
try {
mRunner.runOnGLThread(() -> {
mSurfaceTexture = surface;
updateSurface(width, height);
mValidSurface = true;
});
}
catch (Throwable t) {
Log.e(LOG_TAG, "updateSurface failed", t);
throw new RuntimeException(t);
}
}
@Override
public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
// will be called on UI thread
onSurfaceTextureAvailable(surface, width, height);
}
@Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
// will be called on UI thread
onSurfaceTextureAvailable(null, 0, 0);
mValidSurface = false;
return true;
}
@Override
public void onSurfaceTextureUpdated(SurfaceTexture surfaceTexture) {
}
// Inherited from SurfaceHolder
@Override
public void surfaceCreated(SurfaceHolder holder) {
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
try {
mRunner.runOnGLThread(() -> {
mSurfaceHolder = holder;
updateSurface(width, height);
mValidSurface = true;
});
}
catch (Throwable t) {
Log.e(LOG_TAG, "updateSurface failed", t);
throw new RuntimeException(t);
}
}
@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,
int backgroundColor, boolean forceDraw);
private native long nGetDuration(long nativeProxy);
}

View File

@ -7,26 +7,16 @@
package org.skia.skottie;
import android.animation.Animator;
import android.animation.TimeInterpolator;
import android.graphics.SurfaceTexture;
import android.graphics.drawable.Animatable;
import android.opengl.GLUtils;
import android.os.Handler;
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;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.channels.FileChannel;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
@ -48,11 +38,11 @@ class SkottieRunner {
private HandlerThread mGLThreadLooper;
private Handler mGLThread;
private EGL10 mEgl;
private EGLDisplay mEglDisplay;
private EGLConfig mEglConfig;
private EGLContext mEglContext;
private EGLSurface mPBufferSurface;
EGL10 mEgl;
EGLDisplay mEglDisplay;
EGLConfig mEglConfig;
EGLContext mEglContext;
EGLSurface mPBufferSurface;
private long mNativeProxy;
static {
@ -128,7 +118,7 @@ class SkottieRunner {
}
}
private long getNativeProxy() { return mNativeProxy; }
long getNativeProxy() { return mNativeProxy; }
private class RunSignalAndCatch implements Runnable {
public Throwable error;
@ -152,7 +142,7 @@ class SkottieRunner {
}
}
private void runOnGLThread(Runnable r) throws Throwable {
void runOnGLThread(Runnable r) throws Throwable {
runOnGLThread(r, false);
}
@ -282,437 +272,6 @@ class SkottieRunner {
}
}
public class SkottieAnimation extends Animator implements Choreographer.FrameCallback,
TextureView.SurfaceTextureListener, SurfaceHolder.Callback {
boolean mIsRunning = false;
SurfaceTexture mSurfaceTexture;
EGLSurface mEglSurface;
boolean mNewSurface = false;
SurfaceHolder mSurfaceHolder;
boolean mValidSurface = false;
private int mRepeatCount;
private int mRepeatCounter;
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
private SkottieAnimation(SurfaceTexture surfaceTexture, InputStream is) {
if (init(is)) {
mSurfaceTexture = surfaceTexture;
}
}
private SkottieAnimation(TextureView view, InputStream is, int backgroundColor, int repeatCount) {
if (init(is)) {
mSurfaceTexture = view.getSurfaceTexture();
}
view.setSurfaceTextureListener(this);
mBackgroundColor = backgroundColor;
mRepeatCount = repeatCount;
mRepeatCounter = mRepeatCount;
}
private SkottieAnimation(SurfaceView view, InputStream is, int backgroundColor, int repeatCount) {
if (init(is)) {
mSurfaceHolder = view.getHolder();
}
mSurfaceHolder.addCallback(this);
mBackgroundColor = backgroundColor;
mRepeatCount = repeatCount;
mRepeatCounter = mRepeatCount;
}
private void setSurfaceTexture(SurfaceTexture s) {
mSurfaceTexture = s;
}
private ByteBuffer convertToByteBuffer(InputStream is) throws IOException {
if (is instanceof FileInputStream) {
FileChannel fileChannel = ((FileInputStream)is).getChannel();
return fileChannel.map(FileChannel.MapMode.READ_ONLY,
fileChannel.position(), fileChannel.size());
}
ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
byte[] tmpStorage = new byte[4096];
int bytesRead;
while ((bytesRead = is.read(tmpStorage, 0, tmpStorage.length)) != -1) {
byteStream.write(tmpStorage, 0, bytesRead);
}
byteStream.flush();
tmpStorage = byteStream.toByteArray();
ByteBuffer buffer = ByteBuffer.allocateDirect(tmpStorage.length);
buffer.order(ByteOrder.nativeOrder());
buffer.put(tmpStorage, 0, tmpStorage.length);
return buffer.asReadOnlyBuffer();
}
private boolean init(InputStream is) {
ByteBuffer byteBuffer;
try {
byteBuffer = convertToByteBuffer(is);
} catch (IOException e) {
Log.e(LOG_TAG, "failed to read input stream", e);
return false;
}
long proxy = SkottieRunner.getInstance().getNativeProxy();
mNativeProxy = nCreateProxy(proxy, byteBuffer);
mDuration = nGetDuration(mNativeProxy);
mProgress = 0f;
return true;
}
@Override
protected void finalize() throws Throwable {
try {
end();
nDeleteProxy(mNativeProxy);
mNativeProxy = 0;
} finally {
super.finalize();
}
}
// Always call this on GL thread
public void updateSurface(int width, int height) {
mSurfaceWidth = width;
mSurfaceHeight = height;
mNewSurface = true;
drawFrame();
}
@Override
public void start() {
try {
runOnGLThread(() -> {
if (!mIsRunning) {
long currentTime = System.nanoTime();
mAnimationStartTime = currentTime - (long)(1000000 * mDuration * mProgress);
mIsRunning = true;
mNewSurface = true;
mRepeatCounter = mRepeatCount;
doFrame(currentTime);
}
});
}
catch (Throwable t) {
Log.e(LOG_TAG, "start failed", t);
throw new RuntimeException(t);
}
for (AnimatorListener l : this.getListeners()) {
l.onAnimationStart(this);
}
}
@Override
public void end() {
try {
runOnGLThread(() -> {
mIsRunning = false;
if (mEglSurface != null) {
// 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, "stop failed", t);
throw new RuntimeException(t);
}
for (AnimatorListener l : this.getListeners()) {
l.onAnimationEnd(this);
}
}
@Override
public void pause() {
try {
runOnGLThread(() -> {
mIsRunning = false;
});
}
catch (Throwable t) {
Log.e(LOG_TAG, "pause failed", t);
throw new RuntimeException(t);
}
}
@Override
public void resume() {
try {
runOnGLThread(() -> {
if (!mIsRunning) {
long currentTime = System.nanoTime();
mAnimationStartTime = currentTime - (long)(1000000 * mDuration * mProgress);
mIsRunning = true;
mNewSurface = true;
doFrame(currentTime);
}
});
}
catch (Throwable t) {
Log.e(LOG_TAG, "resume failed", t);
throw new RuntimeException(t);
}
}
// TODO: add support for start delay
@Override
public long getStartDelay() {
return 0;
}
// TODO: add support for start delay
@Override
public void setStartDelay(long startDelay) {
}
@Override
public Animator setDuration(long duration) {
return null;
}
@Override
public boolean isRunning() {
return mIsRunning;
}
@Override
public long getDuration() {
return mDuration;
}
@Override
public long getTotalDuration() {
if (mRepeatCount == -1) {
return DURATION_INFINITE;
}
// TODO: add start delay when implemented
return mDuration * (1 + mRepeatCount);
}
// TODO: support TimeInterpolators
@Override
public void setInterpolator(TimeInterpolator value) {
}
public void setProgress(float progress) {
try {
runOnGLThread(() -> {
mProgress = progress;
if (mIsRunning) {
mAnimationStartTime = System.nanoTime()
- (long)(1000000 * mDuration * mProgress);
}
drawFrame();
});
}
catch (Throwable t) {
Log.e(LOG_TAG, "setProgress failed", t);
throw new RuntimeException(t);
}
}
public float getProgress() {
return mProgress;
}
private void drawFrame() {
try {
boolean forceDraw = false;
if (mNewSurface) {
forceDraw = true;
// if there is a new SurfaceTexture, we need to recreate the EGL surface.
if (mEglSurface != null) {
mEgl.eglDestroySurface(mEglDisplay, mEglSurface);
mEglSurface = null;
}
mNewSurface = false;
}
if (mEglSurface == null) {
// block for Texture Views
if (mSurfaceTexture != null) {
mEglSurface = mEgl.eglCreateWindowSurface(mEglDisplay, mEglConfig,
mSurfaceTexture, null);
checkSurface();
// block for Surface Views
} else if (mSurfaceHolder != null) {
mEglSurface = mEgl.eglCreateWindowSurface(mEglDisplay, mEglConfig,
mSurfaceHolder, null);
checkSurface();
}
}
if (mEglSurface != null) {
if (!mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
// If eglMakeCurrent failed, recreate EGL surface on next frame.
Log.w(LOG_TAG, "eglMakeCurrent failed "
+ GLUtils.getEGLErrorString(mEgl.eglGetError()));
mNewSurface = true;
return;
}
// only if nDrawFrames() returns true do we need to swap buffers
if(nDrawFrame(mNativeProxy, mSurfaceWidth, mSurfaceHeight, false,
mProgress, mBackgroundColor, forceDraw)) {
if (!mEgl.eglSwapBuffers(mEglDisplay, mEglSurface)) {
int error = mEgl.eglGetError();
if (error == EGL10.EGL_BAD_SURFACE
|| error == EGL10.EGL_BAD_NATIVE_WINDOW) {
// For some reason our surface was destroyed. Recreate EGL surface
// on next frame.
mNewSurface = true;
// This really shouldn't happen, but if it does we can recover
// easily by just not trying to use the surface anymore
Log.w(LOG_TAG, "swapBuffers failed "
+ GLUtils.getEGLErrorString(error));
return;
}
// Some other fatal EGL error happened, log an error and stop the
// animation.
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, "drawFrame failed", t);
mIsRunning = false;
}
}
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) {
// 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
}
if (timeSinceAnimationStartNS > durationNS) {
if (mRepeatCounter > 0) {
mRepeatCounter--;
} else if (mRepeatCounter == 0) {
mIsRunning = false;
mProgress = 1;
}
}
}
if (mValidSurface) {
drawFrame();
}
}
@Override
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
// will be called on UI thread
mValidSurface = true;
try {
runOnGLThread(() -> {
mSurfaceTexture = surface;
updateSurface(width, height);
});
}
catch (Throwable t) {
Log.e(LOG_TAG, "updateSurface failed", t);
throw new RuntimeException(t);
}
}
@Override
public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
// will be called on UI thread
onSurfaceTextureAvailable(surface, width, height);
}
@Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
// will be called on UI thread
onSurfaceTextureAvailable(null, 0, 0);
mValidSurface = false;
return true;
}
@Override
public void onSurfaceTextureUpdated(SurfaceTexture surfaceTexture) {
}
// Inherited from SurfaceHolder
@Override
public void surfaceCreated(SurfaceHolder holder) {
mValidSurface = true;
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
mValidSurface = true;
try {
runOnGLThread(() -> {
mSurfaceHolder = holder;
updateSurface(width, height);
});
}
catch (Throwable t) {
Log.e(LOG_TAG, "updateSurface failed", t);
throw new RuntimeException(t);
}
}
@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,
int backgroundColor, boolean forceDraw);
private native long nGetDuration(long nativeProxy);
}
private static native long nCreateProxy();
private static native void nDeleteProxy(long nativeProxy);
}

View File

@ -20,7 +20,6 @@ import android.view.ViewGroup;
import android.widget.FrameLayout;
import java.io.FileNotFoundException;
import java.io.InputStream;
import org.skia.skottie.SkottieRunner.SkottieAnimation;
import org.skia.skottielib.R;
public class SkottieView extends FrameLayout {