1package org.skia.skottie;
3import android.animation.Animator;
4import android.animation.TimeInterpolator;
5import android.graphics.SurfaceTexture;
8import android.view.Choreographer;
9import android.view.SurfaceHolder;
13import java.io.ByteArrayOutputStream;
14import java.io.FileInputStream;
15import java.io.IOException;
16import java.io.InputStream;
17import java.nio.ByteBuffer;
18import java.nio.ByteOrder;
19import java.nio.channels.FileChannel;
20import java.util.ArrayList;
22import javax.microedition.khronos.egl.EGL10;
23import javax.microedition.khronos.egl.EGLSurface;
26 TextureView.SurfaceTextureListener, SurfaceHolder.
Callback {
34 private static final String LOG_TAG =
"SkottiePlayer";
36 private boolean mIsRunning =
false;
37 private SurfaceTexture mSurfaceTexture;
38 private EGLSurface mEglSurface;
39 private boolean mNewSurface =
false;
40 private SurfaceHolder mSurfaceHolder;
41 private int mRepeatCount;
42 private int mRepeatCounter;
44 private int mBackgroundColor;
45 private long mNativeProxy;
46 private long mDuration;
47 private float mProgress;
48 private long mAnimationStartTime;
52 mSurfaceTexture = surfaceTexture;
55 SkottieAnimation(TextureView view, InputStream is,
int backgroundColor,
int repeatCount) {
57 mSurfaceTexture = view.getSurfaceTexture();
59 view.setSurfaceTextureListener(
this);
60 mBackgroundColor = backgroundColor;
61 mRepeatCount = repeatCount;
62 mRepeatCounter = mRepeatCount;
65 SkottieAnimation(SurfaceView view, InputStream is,
int backgroundColor,
int repeatCount) {
67 mSurfaceHolder = view.getHolder();
69 mSurfaceHolder.addCallback(
this);
70 mBackgroundColor = backgroundColor;
71 mRepeatCount = repeatCount;
72 mRepeatCounter = mRepeatCount;
79 private ByteBuffer convertToByteBuffer(InputStream is)
throws IOException {
80 if (is instanceof FileInputStream) {
81 FileChannel fileChannel = ((FileInputStream)is).getChannel();
82 return fileChannel.map(FileChannel.MapMode.READ_ONLY,
83 fileChannel.position(), fileChannel.size());
86 ByteArrayOutputStream byteStream =
new ByteArrayOutputStream();
87 byte[] tmpStorage =
new byte[4096];
89 while ((bytesRead = is.read(tmpStorage, 0, tmpStorage.length)) != -1) {
90 byteStream.write(tmpStorage, 0, bytesRead);
94 tmpStorage = byteStream.toByteArray();
96 ByteBuffer
buffer = ByteBuffer.allocateDirect(tmpStorage.length);
97 buffer.order(ByteOrder.nativeOrder());
98 buffer.put(tmpStorage, 0, tmpStorage.length);
99 return buffer.asReadOnlyBuffer();
102 private boolean init(InputStream is) {
104 ByteBuffer byteBuffer;
106 byteBuffer = convertToByteBuffer(is);
107 }
catch (IOException
e) {
108 Log.e(LOG_TAG,
"failed to read input stream",
e);
113 mNativeProxy = nCreateProxy(proxy, byteBuffer);
114 mDuration = nGetDuration(mNativeProxy);
119 private void notifyAnimationEnd() {
120 if (this.getListeners() !=
null) {
123 ArrayList<Animator.AnimatorListener> listeners =
124 new ArrayList<Animator.AnimatorListener>(this.getListeners());
125 for (AnimatorListener l : listeners) {
126 l.onAnimationEnd(
this);
135 nDeleteProxy(mNativeProxy);
144 config.mSurfaceWidth =
width;
145 config.mSurfaceHeight =
height;
155 long currentTime = System.nanoTime();
156 mAnimationStartTime = currentTime - (long)(1000000 * mDuration * mProgress);
159 mRepeatCounter = mRepeatCount;
164 catch (Throwable t) {
166 throw new RuntimeException(t);
168 if (this.getListeners() !=
null) {
169 for (AnimatorListener l : this.getListeners()) {
170 l.onAnimationStart(
this);
180 if (mEglSurface !=
null) {
189 catch (Throwable t) {
191 throw new RuntimeException(t);
193 notifyAnimationEnd();
203 catch (Throwable t) {
205 throw new RuntimeException(t);
214 long currentTime = System.nanoTime();
215 mAnimationStartTime = currentTime - (long)(1000000 * mDuration * mProgress);
222 catch (Throwable t) {
224 throw new RuntimeException(t);
257 if (mRepeatCount == -1) {
258 return DURATION_INFINITE;
261 return mDuration * (1 + mRepeatCount);
273 mProgress = progress;
275 mAnimationStartTime = System.nanoTime()
276 - (
long)(1000000 * mDuration * mProgress);
281 catch (Throwable t) {
283 throw new RuntimeException(t);
291 private void drawFrame() {
293 boolean forceDraw =
false;
297 if (mEglSurface !=
null) {
304 if (mEglSurface ==
null) {
306 if (mSurfaceTexture !=
null) {
311 }
else if (mSurfaceHolder !=
null) {
318 if (mEglSurface !=
null) {
319 if (!mRunner.
mEgl.eglMakeCurrent(mRunner.
mEglDisplay, mEglSurface, mEglSurface,
322 Log.w(LOG_TAG,
"eglMakeCurrent failed "
323 + GLUtils.getEGLErrorString(mRunner.
mEgl.eglGetError()));
329 mProgress, mBackgroundColor, forceDraw)) {
332 if (
error == EGL10.EGL_BAD_SURFACE
333 ||
error == EGL10.EGL_BAD_NATIVE_WINDOW) {
339 Log.w(LOG_TAG,
"swapBuffers failed "
340 + GLUtils.getEGLErrorString(
error));
346 throw new RuntimeException(
"Cannot swap buffers "
347 + GLUtils.getEGLErrorString(
error));
361 }
catch (Throwable t) {
362 Log.e(LOG_TAG,
"drawFrame failed", t);
367 private void checkSurface() throws RuntimeException {
369 if (mEglSurface ==
null || mEglSurface == EGL10.EGL_NO_SURFACE) {
372 throw new RuntimeException(
"createWindowSurface failed "
373 + GLUtils.getEGLErrorString(
error));
381 Choreographer.getInstance().postFrameCallback(
this);
384 long durationNS = mDuration * 1000000;
385 long timeSinceAnimationStartNS = frameTimeNanos - mAnimationStartTime;
386 long animationProgressNS = timeSinceAnimationStartNS % durationNS;
387 mProgress = animationProgressNS / (
float)durationNS;
388 if (timeSinceAnimationStartNS > durationNS) {
389 mAnimationStartTime += durationNS;
391 if (timeSinceAnimationStartNS > durationNS) {
392 if (mRepeatCounter > 0) {
394 }
else if (mRepeatCounter == 0) {
397 notifyAnimationEnd();
413 config.mValidSurface =
true;
416 catch (Throwable t) {
418 throw new RuntimeException(t);
432 config.mValidSurface =
false;
450 mSurfaceHolder = holder;
452 config.mValidSurface =
true;
455 catch (Throwable t) {
457 throw new RuntimeException(t);
463 config.mValidSurface =
false;
477 private native
long nCreateProxy(
long runner, ByteBuffer
data);
478 private native
void nDeleteProxy(
long nativeProxy);
479 private native
boolean nDrawFrame(
long nativeProxy,
int width,
int height,
480 boolean wideColorGamut,
float progress,
481 int backgroundColor,
boolean forceDraw);
482 private native
long nGetDuration(
long nativeProxy);
void setSurfaceTexture(SurfaceTexture s)
void setProgress(float progress)
Config getBackingViewConfig()
SkottieAnimation(SurfaceTexture surfaceTexture, InputStream is)
void doFrame(long frameTimeNanos)
void setInterpolator(TimeInterpolator value)
void onSurfaceTextureUpdated(SurfaceTexture surfaceTexture)
void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height)
void setStartDelay(long startDelay)
Animator setDuration(long duration)
void updateSurface(int width, int height)
SkottieAnimation(TextureView view, InputStream is, int backgroundColor, int repeatCount)
void setBackingViewConfig(Config config)
SkottieAnimation(SurfaceView view, InputStream is, int backgroundColor, int repeatCount)
void surfaceCreated(SurfaceHolder holder)
void surfaceDestroyed(SurfaceHolder holder)
void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height)
boolean onSurfaceTextureDestroyed(SurfaceTexture surface)
void surfaceChanged(SurfaceHolder holder, int format, int width, int height)
void runOnGLThread(Runnable r)
EGLSurface mPBufferSurface
static synchronized SkottieRunner getInstance()
const uint8_t uint32_t uint32_t GError ** error
uint32_t uint32_t * format
void Log(const char *format,...) SK_PRINTF_LIKE(1
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir Path to the cache directory This is different from the persistent_cache_path in embedder which is used for Skia shader cache icu native lib Path to the library file that exports the ICU data vm service The hostname IP address on which the Dart VM Service should be served If not defaults to or::depending on whether ipv6 is specified vm service A custom Dart VM Service port The default is to pick a randomly available open port disable vm Disable the Dart VM Service The Dart VM Service is never available in release mode disable vm service Disable mDNS Dart VM Service publication Bind to the IPv6 localhost address for the Dart VM Service Ignored if vm service host is set endless trace buffer
std::function< void(MTLRenderPipelineDescriptor *)> Callback
std::shared_ptr< const fml::Mapping > data