diff --git a/example/src/Examples/Stickers/Stickers.tsx b/example/src/Examples/Stickers/Stickers.tsx
index 6c590c0a54..dc1c2bc18a 100644
--- a/example/src/Examples/Stickers/Stickers.tsx
+++ b/example/src/Examples/Stickers/Stickers.tsx
@@ -33,10 +33,12 @@ export const Stickers = () => {
);
diff --git a/externals/depot_tools b/externals/depot_tools
index 911ccd864c..fd6e527668 160000
--- a/externals/depot_tools
+++ b/externals/depot_tools
@@ -1 +1 @@
-Subproject commit 911ccd864c77a8d9c91066858dfbd4517f9e83e2
+Subproject commit fd6e527668c2010f96a0a95e7e057994827def92
diff --git a/externals/skia b/externals/skia
index 89907a0ce7..ab212df482 160000
--- a/externals/skia
+++ b/externals/skia
@@ -1 +1 @@
-Subproject commit 89907a0ce7c0c883898b8c88b55b7b7f733f7058
+Subproject commit ab212df482c8fd5b1c1fb302717876d542549624
diff --git a/package/android/cpp/rnskia-android/RNSkAndroidView.h b/package/android/cpp/rnskia-android/RNSkAndroidView.h
index bfb41ccbc5..b9e7dbf67d 100644
--- a/package/android/cpp/rnskia-android/RNSkAndroidView.h
+++ b/package/android/cpp/rnskia-android/RNSkAndroidView.h
@@ -56,8 +56,6 @@ class RNSkAndroidView : public T, public RNSkBaseAndroidView {
void surfaceSizeChanged(int width, int height) override {
std::static_pointer_cast(T::getCanvasProvider())
->surfaceSizeChanged(width, height);
- // This is only need for the first time to frame, this renderImmediate call
- // will invoke updateTexImage for the previous frame
RNSkView::renderImmediate();
}
diff --git a/package/android/cpp/rnskia-android/RNSkOpenGLCanvasProvider.cpp b/package/android/cpp/rnskia-android/RNSkOpenGLCanvasProvider.cpp
index e3d92aa3ea..c5e7ae9cd0 100644
--- a/package/android/cpp/rnskia-android/RNSkOpenGLCanvasProvider.cpp
+++ b/package/android/cpp/rnskia-android/RNSkOpenGLCanvasProvider.cpp
@@ -39,7 +39,6 @@ bool RNSkOpenGLCanvasProvider::renderToCanvas(
if (!_surfaceHolder->makeCurrent()) {
return false;
}
- _surfaceHolder->updateTexImage();
// Draw into canvas using callback
cb(surface->getCanvas());
diff --git a/package/android/cpp/rnskia-android/SkiaOpenGLSurfaceFactory.h b/package/android/cpp/rnskia-android/SkiaOpenGLSurfaceFactory.h
index b9c12fc893..4c48f4a01a 100644
--- a/package/android/cpp/rnskia-android/SkiaOpenGLSurfaceFactory.h
+++ b/package/android/cpp/rnskia-android/SkiaOpenGLSurfaceFactory.h
@@ -44,32 +44,13 @@ class ThreadContextHolder {
*/
class WindowSurfaceHolder {
public:
- WindowSurfaceHolder(jobject jSurfaceTexture, int width, int height)
+ WindowSurfaceHolder(jobject jSurface, int width, int height)
: _width(width), _height(height) {
JNIEnv *env = facebook::jni::Environment::current();
- _jSurfaceTexture = env->NewGlobalRef(jSurfaceTexture);
- jclass surfaceClass = env->FindClass("android/view/Surface");
- jmethodID surfaceConstructor = env->GetMethodID(
- surfaceClass, "", "(Landroid/graphics/SurfaceTexture;)V");
- // Create a new Surface instance
- jobject jSurface =
- env->NewObject(surfaceClass, surfaceConstructor, jSurfaceTexture);
-
- jclass surfaceTextureClass = env->GetObjectClass(_jSurfaceTexture);
- _updateTexImageMethod =
- env->GetMethodID(surfaceTextureClass, "updateTexImage", "()V");
-
- // Acquire the native window from the Surface
_window = ANativeWindow_fromSurface(env, jSurface);
- // Clean up local references
- env->DeleteLocalRef(jSurface);
- env->DeleteLocalRef(surfaceClass);
- env->DeleteLocalRef(surfaceTextureClass);
}
~WindowSurfaceHolder() {
- JNIEnv *env = facebook::jni::Environment::current();
- env->DeleteGlobalRef(_jSurfaceTexture);
ANativeWindow_release(_window);
}
@@ -81,20 +62,6 @@ class WindowSurfaceHolder {
*/
sk_sp getSurface();
- void updateTexImage() {
- JNIEnv *env = facebook::jni::Environment::current();
-
- // Call updateTexImage on the SurfaceTexture object
- env->CallVoidMethod(_jSurfaceTexture, _updateTexImageMethod);
-
- // Check for exceptions
- if (env->ExceptionCheck()) {
- RNSkLogger::logToConsole("updateAndRelease() failed. The exception above "
- "can safely be ignored");
- env->ExceptionClear();
- }
- }
-
/**
* Resizes the surface
* @param width
@@ -132,9 +99,7 @@ class WindowSurfaceHolder {
private:
ANativeWindow *_window;
sk_sp _skSurface = nullptr;
- jobject _jSurfaceTexture = nullptr;
EGLSurface _glSurface = EGL_NO_SURFACE;
- jmethodID _updateTexImageMethod = nullptr;
int _width = 0;
int _height = 0;
};
diff --git a/package/android/src/main/java/com/shopify/reactnative/skia/PlatformContext.java b/package/android/src/main/java/com/shopify/reactnative/skia/PlatformContext.java
index 6d62047a21..9e6d5d2432 100644
--- a/package/android/src/main/java/com/shopify/reactnative/skia/PlatformContext.java
+++ b/package/android/src/main/java/com/shopify/reactnative/skia/PlatformContext.java
@@ -49,19 +49,19 @@ private byte[] getStreamAsBytes(InputStream is) throws IOException {
}
private void postFrameLoop() {
- Choreographer.FrameCallback frameCallback = new Choreographer.FrameCallback() {
- @Override
- public void doFrame(long frameTimeNanos) {
- if (_drawLoopActive) {
- Choreographer.getInstance().postFrameCallback(this);
- }
- if (_isPaused) {
- return;
- }
- notifyDrawLoop();
- }
- };
- Choreographer.getInstance().postFrameCallback(frameCallback);
+// Choreographer.FrameCallback frameCallback = new Choreographer.FrameCallback() {
+// @Override
+// public void doFrame(long frameTimeNanos) {
+// if (_drawLoopActive) {
+// Choreographer.getInstance().postFrameCallback(this);
+// }
+// if (_isPaused) {
+// return;
+// }
+// notifyDrawLoop();
+// }
+// };
+// Choreographer.getInstance().postFrameCallback(frameCallback);
}
diff --git a/package/android/src/main/java/com/shopify/reactnative/skia/SkiaBaseView.java b/package/android/src/main/java/com/shopify/reactnative/skia/SkiaBaseView.java
index 0aeedd4442..4e4900c1ba 100644
--- a/package/android/src/main/java/com/shopify/reactnative/skia/SkiaBaseView.java
+++ b/package/android/src/main/java/com/shopify/reactnative/skia/SkiaBaseView.java
@@ -1,24 +1,51 @@
package com.shopify.reactnative.skia;
+import android.annotation.SuppressLint;
import android.content.Context;
-import android.graphics.SurfaceTexture;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.PixelFormat;
+import android.media.Image;
+import android.media.ImageReader;
+import android.os.Build;
import android.util.Log;
+import android.view.Choreographer;
import android.view.MotionEvent;
-import android.view.TextureView;
+import android.view.ViewGroup;
+import android.hardware.HardwareBuffer;
-import com.facebook.react.views.view.ReactViewGroup;
+import androidx.annotation.RequiresApi;
-public abstract class SkiaBaseView extends ReactViewGroup implements TextureView.SurfaceTextureListener {
- private TextureView mTexture;
+import com.facebook.react.views.view.ReactViewGroup;
+@RequiresApi(api = Build.VERSION_CODES.Q)
+public abstract class SkiaBaseView extends ReactViewGroup implements Choreographer.FrameCallback {
+ private ImageReader mImageReader = null;
+ private Bitmap mBitmap = null;
private String tag = "SkiaView";
+ private Choreographer choreographer;
+ private Paint paint = new Paint();
+ private Matrix matrix = new Matrix();
+
+ @SuppressLint("WrongConstant")
public SkiaBaseView(Context context) {
super(context);
- mTexture = new TextureView(context);
- mTexture.setSurfaceTextureListener(this);
- mTexture.setOpaque(false);
- addView(mTexture);
+ LayoutParams params = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
+ setLayoutParams(params);
+ setWillNotDraw(false);
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+ if (mBitmap != null) {
+ end = System.nanoTime();
+ Log.i(tag, "render time: " + (end - start) / 1000000 + "ms");
+ canvas.drawBitmap(mBitmap, matrix, paint);
+ }
}
public void destroySurface() {
@@ -26,29 +53,46 @@ public void destroySurface() {
surfaceDestroyed();
}
- private void createSurfaceTexture() {
- // This API Level is >= 26, we created our own SurfaceTexture to have a faster time to first frame
- if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
- Log.i(tag, "Create SurfaceTexture");
- SurfaceTexture surface = new SurfaceTexture(false);
- mTexture.setSurfaceTexture(surface);
- this.onSurfaceTextureAvailable(surface, this.getMeasuredWidth(), this.getMeasuredHeight());
- }
- }
-
+ @SuppressLint("WrongConstant")
@Override
- protected void onAttachedToWindow() {
- super.onAttachedToWindow();
- if (this.getMeasuredWidth() == 0) {
- createSurfaceTexture();
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ Log.i(tag, "onLayout " + this.getMeasuredWidth() + "/" + this.getMeasuredHeight());
+ super.onLayout(changed, left, top, right, bottom);
+ // TODO: fix lifecycle there
+ if (mImageReader != null) {
+ // mImageReader.close();
+ // surfaceSizeChanged(getMeasuredWidth(), getMeasuredHeight());
+ } else {
+ long usage = HardwareBuffer.USAGE_CPU_READ_RARELY |
+ HardwareBuffer.USAGE_CPU_WRITE_RARELY |
+ HardwareBuffer.USAGE_GPU_COLOR_OUTPUT |
+ HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE;
+ mImageReader = ImageReader.newInstance(getMeasuredWidth(), getMeasuredHeight(), PixelFormat.RGBA_8888, 2, usage);
+ surfaceAvailable(mImageReader.getSurface(), getMeasuredWidth(), getMeasuredHeight());
+ choreographer = Choreographer.getInstance();
+ choreographer.postFrameCallback(this);
}
}
+ long start;
+ long end;
+
@Override
- protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
- Log.i(tag, "onLayout " + this.getMeasuredWidth() + "/" + this.getMeasuredHeight());
- super.onLayout(changed, left, top, right, bottom);
- mTexture.layout(0, 0, this.getMeasuredWidth(), this.getMeasuredHeight());
+ public void doFrame(long frameTimeNanos) {
+ choreographer.postFrameCallback(this);
+ if (mImageReader.getSurface() != null) {
+ // TODO: use drawFrame() instead
+ surfaceSizeChanged(getMeasuredWidth(), getMeasuredHeight());
+ try (Image image = mImageReader.acquireLatestImage()) {
+ if (image != null) {
+ start = System.nanoTime();
+ HardwareBuffer hb = image.getHardwareBuffer();
+ mBitmap = Bitmap.wrapHardwareBuffer(hb, null);
+ hb.close();
+ invalidate();
+ }
+ }
+ }
}
@Override
@@ -121,40 +165,6 @@ private static int motionActionToType(int action) {
return actionType;
}
- @Override
- public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
- Log.i(tag, "onSurfaceTextureAvailable " + width + "/" + height);
- surfaceAvailable(surface, width, height);
- }
-
- @Override
- public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
- Log.i(tag, "onSurfaceTextureSizeChanged " + width + "/" + height);
- surfaceSizeChanged(width, height);
- }
-
- @Override
- public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
- Log.i(tag, "onSurfaceTextureDestroyed");
- // https://developer.android.com/reference/android/view/TextureView.SurfaceTextureListener#onSurfaceTextureDestroyed(android.graphics.SurfaceTexture)
- destroySurface();
- // Because of React Native Screens (which dettach the view), we always keep the surface alive.
- // If not, Texture view will recreate the texture surface by itself and
- // we will lose the fast first time to frame.
- // We only delete the surface when the view is dropped (destroySurface invoked by SkiaBaseViewManager);
- createSurfaceTexture();
- return false;
- }
-
- //private long _prevTimestamp = 0;
- @Override
- public void onSurfaceTextureUpdated(SurfaceTexture surface) {
-// long timestamp = surface.getTimestamp();
-// long frameDuration = (timestamp - _prevTimestamp)/1000000;
-// Log.i(tag, "onSurfaceTextureUpdated "+frameDuration+"ms");
-// _prevTimestamp = timestamp;
- }
-
protected abstract void surfaceAvailable(Object surface, int width, int height);
protected abstract void surfaceSizeChanged(int width, int height);