Flutter Engine
The Flutter Engine
SkottieRunner.java
Go to the documentation of this file.
1/*
2 * Copyright 2018 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8package org.skia.skottie;
9
10import android.graphics.SurfaceTexture;
11import android.graphics.drawable.Animatable;
12import android.opengl.GLUtils;
13import android.os.Handler;
14import android.os.HandlerThread;
15import android.util.Log;
16import android.view.SurfaceView;
17import android.view.TextureView;
18
19import java.io.InputStream;
20import java.util.concurrent.CountDownLatch;
21import java.util.concurrent.TimeUnit;
22import java.util.concurrent.TimeoutException;
23
24import javax.microedition.khronos.egl.EGL10;
25import javax.microedition.khronos.egl.EGLConfig;
26import javax.microedition.khronos.egl.EGLContext;
27import javax.microedition.khronos.egl.EGLDisplay;
28import javax.microedition.khronos.egl.EGLSurface;
29
31 private static final int EGL_CONTEXT_CLIENT_VERSION = 0x3098;
32 private static final int EGL_OPENGL_ES2_BIT = 4;
33 private static final int STENCIL_BUFFER_SIZE = 8;
34 private static final long TIME_OUT_MS = 10000;
35 private static final String LOG_TAG = "SkottiePlayer";
36
37 private static SkottieRunner sInstance;
38
39 private HandlerThread mGLThreadLooper;
40 private Handler mGLThread;
41 EGL10 mEgl;
42 EGLDisplay mEglDisplay;
43 EGLConfig mEglConfig;
44 EGLContext mEglContext;
45 EGLSurface mPBufferSurface;
46 private long mNativeProxy;
47
48 static {
49 System.loadLibrary("skottie_android");
50 }
51 /**
52 * Gets SkottieRunner singleton instance.
53 */
54 public static synchronized SkottieRunner getInstance() {
55 if (sInstance == null) {
56 sInstance = new SkottieRunner();
57 }
58 return sInstance;
59 }
60
61 /**
62 * Create a new animation by feeding data from "is" and replaying in a TextureView.
63 * TextureView is tracked internally for SurfaceTexture state.
64 */
65 public SkottieAnimation createAnimation(TextureView view, InputStream is, int backgroundColor, int repeatCount) {
66 return new SkottieAnimation(view, is, backgroundColor, repeatCount);
67 }
68
69 /**
70 * Create a new animation by feeding data from "is" and replaying in a SurfaceTexture.
71 * SurfaceTexture is possibly taken from a TextureView and can be updated with
72 * updateAnimationSurface.
73 */
74 public SkottieAnimation createAnimation(SurfaceTexture surfaceTexture, InputStream is) {
75 return new SkottieAnimation(surfaceTexture, is);
76 }
77
78 /**
79 * Create a new animation by feeding data from "is" and replaying in a SurfaceView.
80 * State is controlled internally by SurfaceHolder.
81 */
82 public SkottieAnimation createAnimation(SurfaceView view, InputStream is, int backgroundColor, int repeatCount) {
83 return new SkottieAnimation(view, is, backgroundColor, repeatCount);
84 }
85
86 /**
87 * Pass a new SurfaceTexture: use this method only if managing TextureView outside
88 * SkottieRunner.
89 */
90 public void updateAnimationSurface(Animatable animation, SurfaceTexture surfaceTexture,
91 int width, int height) {
92 try {
93 runOnGLThread(() -> {
94 ((SkottieAnimation) animation).setSurfaceTexture(surfaceTexture);
95 ((SkottieAnimation) animation).updateSurface(width, height);
96 });
97 }
98 catch (Throwable t) {
99 Log.e(LOG_TAG, "update failed", t);
100 throw new RuntimeException(t);
101 }
102 }
103
104 private SkottieRunner()
105 {
106 mGLThreadLooper = new HandlerThread("SkottieAnimator");
107 mGLThreadLooper.start();
108 mGLThread = new Handler(mGLThreadLooper.getLooper());
109 initGl();
110 }
111
112 @Override
113 protected void finalize() throws Throwable {
114 try {
115 runOnGLThread(this::doFinishGL);
116 } finally {
117 super.finalize();
118 }
119 }
120
121 long getNativeProxy() { return mNativeProxy; }
122
123 private class RunSignalAndCatch implements Runnable {
124 public Throwable error;
125 private Runnable mRunnable;
126 private CountDownLatch mFence;
127
128 RunSignalAndCatch(Runnable run, CountDownLatch fence) {
129 mRunnable = run;
130 mFence = fence;
131 }
132
133 @Override
134 public void run() {
135 try {
136 mRunnable.run();
137 } catch (Throwable t) {
138 error = t;
139 } finally {
140 mFence.countDown();
141 }
142 }
143 }
144
145 void runOnGLThread(Runnable r) throws Throwable {
146 runOnGLThread(r, false);
147 }
148
149 private void runOnGLThread(Runnable r, boolean postAtFront) throws Throwable {
150
151 CountDownLatch fence = new CountDownLatch(1);
152 RunSignalAndCatch wrapper = new RunSignalAndCatch(r, fence);
153 if (postAtFront) {
154 mGLThread.postAtFrontOfQueue(wrapper);
155 } else {
156 mGLThread.post(wrapper);
157 }
158 if (!fence.await(TIME_OUT_MS, TimeUnit.MILLISECONDS)) {
159 throw new TimeoutException();
160 }
161 if (wrapper.error != null) {
162 throw wrapper.error;
163 }
164 }
165
166 private void initGl()
167 {
168 try {
169 runOnGLThread(mDoInitGL);
170 }
171 catch (Throwable t) {
172 Log.e(LOG_TAG, "initGl failed", t);
173 throw new RuntimeException(t);
174 }
175 }
176
177 private Runnable mDoInitGL = new Runnable() {
178 @Override
179 public void run() {
180 mEgl = (EGL10) EGLContext.getEGL();
181
182 mEglDisplay = mEgl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
183 if (mEglDisplay == EGL10.EGL_NO_DISPLAY) {
184 throw new RuntimeException("eglGetDisplay failed "
185 + GLUtils.getEGLErrorString(mEgl.eglGetError()));
186 }
187
188 int[] version = new int[2];
189 if (!mEgl.eglInitialize(mEglDisplay, version)) {
190 throw new RuntimeException("eglInitialize failed " +
191 GLUtils.getEGLErrorString(mEgl.eglGetError()));
192 }
193
194 mEglConfig = chooseEglConfig();
195 if (mEglConfig == null) {
196 throw new RuntimeException("eglConfig not initialized");
197 }
198
200
201 int[] attribs = new int[] {
202 EGL10.EGL_WIDTH, 1,
203 EGL10.EGL_HEIGHT, 1,
204 EGL10.EGL_NONE
205 };
206
207 mPBufferSurface = mEgl.eglCreatePbufferSurface(mEglDisplay, mEglConfig, attribs);
208 if (mPBufferSurface == null || mPBufferSurface == EGL10.EGL_NO_SURFACE) {
209 int error = mEgl.eglGetError();
210 throw new RuntimeException("createPbufferSurface failed "
211 + GLUtils.getEGLErrorString(error));
212 }
213
215 throw new RuntimeException("eglMakeCurrent failed "
216 + GLUtils.getEGLErrorString(mEgl.eglGetError()));
217 }
218
219 mNativeProxy = nCreateProxy();
220 }
221 };
222
223 EGLContext createContext(EGL10 egl, EGLDisplay eglDisplay, EGLConfig eglConfig) {
224 int[] attrib_list = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL10.EGL_NONE };
225 return egl.eglCreateContext(eglDisplay, eglConfig, EGL10.EGL_NO_CONTEXT, attrib_list);
226 }
227
228 private EGLConfig chooseEglConfig() {
229 int[] configsCount = new int[1];
230 EGLConfig[] configs = new EGLConfig[1];
231 int[] configSpec = getConfig();
232 if (!mEgl.eglChooseConfig(mEglDisplay, configSpec, configs, 1, configsCount)) {
233 throw new IllegalArgumentException("eglChooseConfig failed " +
234 GLUtils.getEGLErrorString(mEgl.eglGetError()));
235 } else if (configsCount[0] > 0) {
236 return configs[0];
237 }
238 return null;
239 }
240
241 private int[] getConfig() {
242 return new int[] {
243 EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
244 EGL10.EGL_RED_SIZE, 8,
245 EGL10.EGL_GREEN_SIZE, 8,
246 EGL10.EGL_BLUE_SIZE, 8,
247 EGL10.EGL_ALPHA_SIZE, 8,
248 EGL10.EGL_DEPTH_SIZE, 0,
249 EGL10.EGL_STENCIL_SIZE, STENCIL_BUFFER_SIZE,
250 EGL10.EGL_NONE
251 };
252 }
253
254 private void doFinishGL() {
255 nDeleteProxy(mNativeProxy);
256 mNativeProxy = 0;
257 if (mEglDisplay != null) {
258 if (mEglContext != null) {
259 mEgl.eglDestroyContext(mEglDisplay, mEglContext);
260 mEglContext = null;
261 }
262 if (mPBufferSurface != null) {
263 mEgl.eglDestroySurface(mEglDisplay, mPBufferSurface);
264 mPBufferSurface = null;
265 }
266
267 mEgl.eglMakeCurrent(mEglDisplay, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_SURFACE,
268 EGL10.EGL_NO_CONTEXT);
269
270 mEgl.eglTerminate(mEglDisplay);
271 mEglDisplay = null;
272 }
273 }
274
275 public void setMaxCacheSize(int maxCacheSize) {
276 nSetMaxCacheSize(maxCacheSize, getNativeProxy());
277 }
278
279
280 private static native long nCreateProxy();
281 private static native void nDeleteProxy(long nativeProxy);
282 private static native void nSetMaxCacheSize(int maxCacheSize, long mNativeProxy);
283}
284
SkottieAnimation createAnimation(SurfaceView view, InputStream is, int backgroundColor, int repeatCount)
SkottieAnimation createAnimation(TextureView view, InputStream is, int backgroundColor, int repeatCount)
SkottieAnimation createAnimation(SurfaceTexture surfaceTexture, InputStream is)
void setMaxCacheSize(int maxCacheSize)
void updateAnimationSurface(Animatable animation, SurfaceTexture surfaceTexture, int width, int height)
static synchronized SkottieRunner getInstance()
EGLContext createContext(EGL10 egl, EGLDisplay eglDisplay, EGLConfig eglConfig)
const uint8_t uint32_t uint32_t GError ** error
void Log(const char *format,...) SK_PRINTF_LIKE(1
Definition: TestRunner.cpp:137
Definition: run.py:1
def run(cmd)
Definition: run.py:14
#define STENCIL_BUFFER_SIZE
Definition: native-lib.cpp:47
int32_t height
int32_t width
#define LOG_TAG
Definition: logging.h:11