@shopify/react-native-skia 0.1.199 → 0.1.201

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (24) hide show
  1. package/android/CMakeLists.txt +1 -1
  2. package/android/cpp/jni/include/JniSkiaBaseView.h +74 -13
  3. package/android/cpp/jni/include/JniSkiaDomView.h +20 -12
  4. package/android/cpp/jni/include/JniSkiaDrawView.h +20 -14
  5. package/android/cpp/jni/include/JniSkiaPictureView.h +24 -15
  6. package/android/cpp/rnskia-android/RNSkAndroidPlatformContext.h +2 -2
  7. package/android/cpp/rnskia-android/RNSkOpenGLCanvasProvider.cpp +41 -44
  8. package/android/cpp/rnskia-android/RNSkOpenGLCanvasProvider.h +4 -6
  9. package/android/cpp/rnskia-android/SkiaOpenGLHelper.h +310 -0
  10. package/android/cpp/rnskia-android/SkiaOpenGLSurfaceFactory.cpp +132 -0
  11. package/android/cpp/rnskia-android/SkiaOpenGLSurfaceFactory.h +125 -0
  12. package/android/src/main/java/com/shopify/reactnative/skia/SkiaBaseView.java +80 -11
  13. package/android/src/main/java/com/shopify/reactnative/skia/SkiaDomView.java +2 -0
  14. package/android/src/main/java/com/shopify/reactnative/skia/SkiaDrawView.java +2 -0
  15. package/android/src/main/java/com/shopify/reactnative/skia/SkiaPictureView.java +3 -0
  16. package/android/src/main/java/com/shopify/reactnative/skia/ViewScreenshotService.java +7 -5
  17. package/cpp/api/JsiSkHostObjects.h +0 -10
  18. package/cpp/rnskia/RNSkJsView.cpp +35 -10
  19. package/cpp/rnskia/RNSkJsiViewApi.h +1 -1
  20. package/cpp/rnskia/RNSkView.h +9 -9
  21. package/ios/RNSkia-iOS/ViewScreenshotService.mm +1 -0
  22. package/package.json +2 -3
  23. package/android/cpp/rnskia-android/SkiaOpenGLRenderer.cpp +0 -347
  24. package/android/cpp/rnskia-android/SkiaOpenGLRenderer.h +0 -124
@@ -2,14 +2,13 @@ package com.shopify.reactnative.skia;
2
2
 
3
3
  import android.content.Context;
4
4
  import android.graphics.SurfaceTexture;
5
+ import android.util.Log;
5
6
  import android.view.MotionEvent;
6
7
  import android.view.Surface;
7
8
  import android.view.TextureView;
8
9
 
9
10
  import com.facebook.jni.annotations.DoNotStrip;
10
- import com.facebook.react.uimanager.PointerEvents;
11
11
  import com.facebook.react.views.view.ReactViewGroup;
12
-
13
12
  public abstract class SkiaBaseView extends ReactViewGroup implements TextureView.SurfaceTextureListener {
14
13
 
15
14
  @DoNotStrip
@@ -18,12 +17,43 @@ public abstract class SkiaBaseView extends ReactViewGroup implements TextureView
18
17
 
19
18
  public SkiaBaseView(Context context) {
20
19
  super(context);
20
+ // TODO: Remove if we find another solution for first frame rendering
21
+ //setWillNotDraw(!shouldRenderFirstFrameAsBitmap());
21
22
  mTexture = new TextureView(context);
22
23
  mTexture.setSurfaceTextureListener(this);
23
24
  mTexture.setOpaque(false);
24
25
  addView(mTexture);
25
26
  }
26
27
 
28
+ /*@Override
29
+ TODO: Remove if we find another solution for first frame rendering
30
+ protected void onDraw(Canvas canvas) {
31
+ super.onDraw(canvas);
32
+
33
+ // If we haven't got a surface yet, let's ask the view to
34
+ // draw into a bitmap and then render the bitmap. This method
35
+ // is typically only called once - for the first frame, and
36
+ // then the surface will be available and all rendering will
37
+ // be done directly to the surface itself.
38
+ if (shouldRenderFirstFrameAsBitmap() && mSurface == null) {
39
+ int width = getWidth();
40
+ int height = getHeight();
41
+
42
+ if (width > 0 && height > 0) {
43
+ Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
44
+ Bitmap result = (Bitmap) renderToBitmap(bitmap, width, height);
45
+
46
+ canvas.drawBitmap(
47
+ result,
48
+ new Rect(0, 0, width, height),
49
+ new Rect(0, 0, width, height),
50
+ null);
51
+
52
+ bitmap.recycle();
53
+ }
54
+ }
55
+ }*/
56
+
27
57
  @Override
28
58
  protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
29
59
  super.onLayout(changed, left, top, right, bottom);
@@ -102,28 +132,53 @@ public abstract class SkiaBaseView extends ReactViewGroup implements TextureView
102
132
 
103
133
  @Override
104
134
  public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
135
+ Log.i("SkiaBaseView", "onSurfaceTextureAvailable " + width + "/" + height);
105
136
  mSurface = new Surface(surface);
106
137
  surfaceAvailable(mSurface, width, height);
138
+
139
+ /*
140
+ TODO: Remove if we find another solution for first frame rendering
141
+ // Clear rendered bitmap when the surface texture has rendered
142
+ // We'll post a message to the main loop asking to invalidate
143
+ if (shouldRenderFirstFrameAsBitmap()) {
144
+ postUpdate(new AtomicInteger());
145
+ }*/
107
146
  }
108
147
 
148
+ /**
149
+ * This method is a way for us to clear the bitmap rendered on the first frame
150
+ * after at least 16 frames have passed - to avoid seeing blinks on the screen caused by
151
+ * TextureView frame sync issues. This is a hack to avoid those pesky blinks. Have no
152
+ * idea on how to sync the TextureView OpenGL updates.
153
+ * @param counter
154
+ */
155
+ /*
156
+ TODO: Remove if we find another solution for first frame rendering
157
+ void postUpdate(AtomicInteger counter) {
158
+ counter.getAndIncrement();
159
+ if (counter.get() > 16) {
160
+ invalidate();
161
+ } else {
162
+ this.post(() -> {
163
+ postUpdate(counter);
164
+ });
165
+ }
166
+ }*/
167
+
109
168
  @Override
110
169
  public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
170
+ Log.i("SkiaBaseView", "onSurfaceTextureSizeChanged " + width + "/" + height);
111
171
  surfaceSizeChanged(width, height);
112
172
  }
113
173
 
114
174
  @Override
115
175
  public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
116
- // Notify the native side
117
- surfaceDestroyed();
176
+ Log.i("SkiaBaseView", "onSurfaceTextureDestroyed");
118
177
  // https://developer.android.com/reference/android/view/TextureView.SurfaceTextureListener#onSurfaceTextureDestroyed(android.graphics.SurfaceTexture)
119
- // Invoked when the specified SurfaceTexture is about to be destroyed. If returns true,
120
- // no rendering should happen inside the surface texture after this method is invoked.
121
- // We've measured this and it seems like we need to call release and return true - and
122
- // then handle the issue with this being ripped out underneath the native layer in the C++
123
- // code.
178
+ surfaceDestroyed();
124
179
  mSurface.release();
125
- // Return true - we promise that no more rendering will be done now.
126
- return true;
180
+ mSurface = null;
181
+ return false;
127
182
  }
128
183
 
129
184
  @Override
@@ -131,6 +186,17 @@ public abstract class SkiaBaseView extends ReactViewGroup implements TextureView
131
186
  // Nothing special to do here
132
187
  }
133
188
 
189
+ /**
190
+ * Returns true if the view is able to directly render on the
191
+ * main thread. This can f.ex then be used to create a first frame
192
+ * render of the view. Returns true by default - override if not.
193
+ */
194
+ /*
195
+ TODO: Remove if we find another solution for first frame rendering
196
+ protected boolean shouldRenderFirstFrameAsBitmap() {
197
+ return false;
198
+ }*/
199
+
134
200
  protected abstract void surfaceAvailable(Object surface, int width, int height);
135
201
 
136
202
  protected abstract void surfaceSizeChanged(int width, int height);
@@ -146,4 +212,7 @@ public abstract class SkiaBaseView extends ReactViewGroup implements TextureView
146
212
  protected abstract void registerView(int nativeId);
147
213
 
148
214
  protected abstract void unregisterView();
215
+
216
+ // TODO: Remove if we find another solution for first frame rendering
217
+ // protected native Object renderToBitmap(Object bitmap, int width, int height);
149
218
  }
@@ -42,4 +42,6 @@ public class SkiaDomView extends SkiaBaseView {
42
42
 
43
43
  protected native void unregisterView();
44
44
 
45
+ // TODO: Remove if we find another solution for first frame rendering
46
+ // protected native Object renderToBitmap(Object bitmap, int width, int height);
45
47
  }
@@ -42,4 +42,6 @@ public class SkiaDrawView extends SkiaBaseView {
42
42
 
43
43
  protected native void unregisterView();
44
44
 
45
+ // TODO: Remove if we find another solution for first frame rendering
46
+ // protected native Object renderToBitmap(Object bitmap, int width, int height);
45
47
  }
@@ -42,4 +42,7 @@ public class SkiaPictureView extends SkiaBaseView {
42
42
 
43
43
  protected native void unregisterView();
44
44
 
45
+ // TODO: Remove if we find another solution for first frame rendering
46
+ // protected native Object renderToBitmap(Object bitmap, int width, int height);
47
+
45
48
  }
@@ -22,22 +22,24 @@ import com.facebook.react.bridge.ReactContext;
22
22
  import com.facebook.react.uimanager.UIManagerModule;
23
23
 
24
24
  import java.util.ArrayList;
25
- import java.util.Collections;
26
- import java.util.LinkedList;
27
25
  import java.util.List;
28
26
  import java.util.concurrent.CountDownLatch;
29
27
  import java.util.concurrent.TimeUnit;
30
28
 
31
-
32
29
  public class ViewScreenshotService {
33
30
  private static final long SURFACE_VIEW_READ_PIXELS_TIMEOUT = 5;
34
31
  private static final String TAG = "SkiaScreenshot";
35
32
 
36
33
  public static Bitmap makeViewScreenshotFromTag(ReactContext context, int tag) {
37
34
  UIManagerModule uiManager = context.getNativeModule(UIManagerModule.class);
38
- View view = uiManager.resolveView(tag);
35
+ View view = null;
36
+ try {
37
+ view = uiManager.resolveView(tag);
38
+ } catch (RuntimeException e) {
39
+ context.handleException(e);
40
+ }
39
41
  if (view == null) {
40
- throw new RuntimeException("Could not resolve view from view tag " + tag);
42
+ return null;
41
43
  }
42
44
 
43
45
  // Measure and get size of view
@@ -81,16 +81,6 @@ protected:
81
81
  */
82
82
  virtual void releaseResources() = 0;
83
83
 
84
- /**
85
- Throws a runtime error if this method is called after the object has been
86
- disposed.
87
- */
88
- void ensureNotDisposed() {
89
- if (_isDisposed) {
90
- throw std::runtime_error("API Object accessed after it was disposed");
91
- }
92
- }
93
-
94
84
  private:
95
85
  void safeDispose() {
96
86
  if (!_isDisposed) {
@@ -36,17 +36,41 @@ bool RNSkJsRenderer::tryRender(
36
36
 
37
37
  void RNSkJsRenderer::renderImmediate(
38
38
  std::shared_ptr<RNSkCanvasProvider> canvasProvider) {
39
+ // Get start time to be able to calculate animations etc.
39
40
  std::chrono::milliseconds ms =
40
41
  std::chrono::duration_cast<std::chrono::milliseconds>(
41
42
  std::chrono::system_clock::now().time_since_epoch());
42
- canvasProvider->renderToCanvas([&](SkCanvas *canvas) {
43
- // Create jsi canvas
44
- auto jsiCanvas = std::make_shared<JsiSkCanvas>(_platformContext);
45
- jsiCanvas->setCanvas(canvas);
46
43
 
47
- drawInJsiCanvas(std::move(jsiCanvas), canvasProvider->getScaledWidth(),
48
- canvasProvider->getScaledHeight(), ms.count() / 1000);
44
+ std::condition_variable cv;
45
+ std::mutex m;
46
+ std::unique_lock<std::mutex> lock(m);
47
+
48
+ // We need to render on the javascript thread but block
49
+ // until we're done rendering. Render immediate is used
50
+ // to make images from the canvas.
51
+ _platformContext->runOnJavascriptThread([canvasProvider, ms, &cv, &m,
52
+ weakSelf = weak_from_this()]() {
53
+ // Lock
54
+ std::unique_lock<std::mutex> lock(m);
55
+
56
+ auto self = weakSelf.lock();
57
+ if (self) {
58
+ canvasProvider->renderToCanvas([self, ms,
59
+ canvasProvider](SkCanvas *canvas) {
60
+ // Create jsi canvas
61
+ auto jsiCanvas = std::make_shared<JsiSkCanvas>(self->_platformContext);
62
+ jsiCanvas->setCanvas(canvas);
63
+
64
+ self->drawInJsiCanvas(
65
+ std::move(jsiCanvas), canvasProvider->getScaledWidth(),
66
+ canvasProvider->getScaledHeight(), ms.count() / 1000);
67
+ });
68
+ }
69
+
70
+ cv.notify_one();
49
71
  });
72
+
73
+ cv.wait(lock);
50
74
  }
51
75
 
52
76
  void RNSkJsRenderer::setDrawCallback(
@@ -99,12 +123,13 @@ void RNSkJsRenderer::performDraw(
99
123
 
100
124
  if (_gpuDrawingLock->try_lock()) {
101
125
 
102
- // Post drawing message to the render thread where the picture recorded
126
+ // Post drawing message to the main thread where the picture recorded
103
127
  // will be sent to the GPU/backend for rendering to screen.
128
+ // TODO: Which thread should we render on? I think it should be main thread!
104
129
  auto gpuLock = _gpuDrawingLock;
105
- _platformContext->runOnRenderThread([weakSelf = weak_from_this(),
106
- p = std::move(p), gpuLock,
107
- canvasProvider]() {
130
+ _platformContext->runOnMainThread([weakSelf = weak_from_this(),
131
+ p = std::move(p), gpuLock,
132
+ canvasProvider]() {
108
133
  auto self = weakSelf.lock();
109
134
  if (self) {
110
135
  // Draw the picture recorded on the real GPU canvas
@@ -162,7 +162,7 @@ public:
162
162
  if (info->view != nullptr) {
163
163
  if (count > 1 && !arguments[1].isUndefined() && !arguments[1].isNull()) {
164
164
  auto rect = JsiSkRect::fromValue(runtime, arguments[1]);
165
- image = info->view->makeImageSnapshot(rect);
165
+ image = info->view->makeImageSnapshot(rect.get());
166
166
  } else {
167
167
  image = info->view->makeImageSnapshot(nullptr);
168
168
  }
@@ -86,11 +86,11 @@ protected:
86
86
  bool _showDebugOverlays;
87
87
  };
88
88
 
89
- class RNSkImageCanvasProvider : public RNSkCanvasProvider {
89
+ class RNSkOffscreenCanvasProvider : public RNSkCanvasProvider {
90
90
  public:
91
- RNSkImageCanvasProvider(std::shared_ptr<RNSkPlatformContext> context,
92
- std::function<void()> requestRedraw, float width,
93
- float height)
91
+ RNSkOffscreenCanvasProvider(std::shared_ptr<RNSkPlatformContext> context,
92
+ std::function<void()> requestRedraw, float width,
93
+ float height)
94
94
  : RNSkCanvasProvider(requestRedraw), _width(width), _height(height) {
95
95
  _surface = context->makeOffscreenSurface(_width, _height);
96
96
  }
@@ -98,7 +98,7 @@ public:
98
98
  /**
99
99
  Returns a snapshot of the current surface/canvas
100
100
  */
101
- sk_sp<SkImage> makeSnapshot(std::shared_ptr<SkRect> bounds) {
101
+ sk_sp<SkImage> makeSnapshot(SkRect *bounds) {
102
102
  sk_sp<SkImage> image;
103
103
  if (bounds != nullptr) {
104
104
  SkIRect b = SkIRect::MakeXYWH(bounds->x(), bounds->y(), bounds->width(),
@@ -273,9 +273,9 @@ public:
273
273
  /**
274
274
  Renders the view into an SkImage instead of the screen.
275
275
  */
276
- sk_sp<SkImage> makeImageSnapshot(std::shared_ptr<SkRect> bounds) {
276
+ sk_sp<SkImage> makeImageSnapshot(SkRect *bounds) {
277
277
 
278
- auto provider = std::make_shared<RNSkImageCanvasProvider>(
278
+ auto provider = std::make_shared<RNSkOffscreenCanvasProvider>(
279
279
  getPlatformContext(), std::bind(&RNSkView::requestRedraw, this),
280
280
  _canvasProvider->getScaledWidth(), _canvasProvider->getScaledHeight());
281
281
 
@@ -283,6 +283,8 @@ public:
283
283
  return provider->makeSnapshot(bounds);
284
284
  }
285
285
 
286
+ std::shared_ptr<RNSkRenderer> getRenderer() { return _renderer; }
287
+
286
288
  protected:
287
289
  std::shared_ptr<RNSkPlatformContext> getPlatformContext() {
288
290
  return _platformContext;
@@ -290,7 +292,6 @@ protected:
290
292
  std::shared_ptr<RNSkCanvasProvider> getCanvasProvider() {
291
293
  return _canvasProvider;
292
294
  }
293
- std::shared_ptr<RNSkRenderer> getRenderer() { return _renderer; }
294
295
 
295
296
  /**
296
297
  Ends an ongoing beginDrawCallback loop for this view. This method is made
@@ -399,7 +400,6 @@ private:
399
400
 
400
401
  size_t _drawingLoopId = 0;
401
402
  std::atomic<int> _redrawRequestCounter = {1};
402
- bool _initialDrawingDone = false;
403
403
  };
404
404
 
405
405
  } // namespace RNSkia
@@ -24,6 +24,7 @@
24
24
  auto view = [_uiManager viewForReactTag:viewTag];
25
25
  if (view == NULL) {
26
26
  RCTFatal(RCTErrorWithMessage(@"Could not find view with tag"));
27
+ return nullptr;
27
28
  }
28
29
 
29
30
  // Get size
package/package.json CHANGED
@@ -7,7 +7,7 @@
7
7
  "setup-skia-web": "./scripts/setup-canvaskit.js"
8
8
  },
9
9
  "title": "React Native Skia",
10
- "version": "0.1.199",
10
+ "version": "0.1.201",
11
11
  "description": "High-performance React Native Graphics using Skia",
12
12
  "main": "lib/module/index.js",
13
13
  "files": [
@@ -42,8 +42,7 @@
42
42
  "lint": "eslint . --ext .ts,.tsx --max-warnings 0 --cache",
43
43
  "test": "jest",
44
44
  "e2e": "E2E=true yarn test -i e2e",
45
- "build": "bob build && merge-dirs lib/typescript/src lib/commonjs && merge-dirs lib/typescript/src lib/module",
46
- "postinstall": "node scripts/install-npm.js"
45
+ "build": "bob build && merge-dirs lib/typescript/src lib/commonjs && merge-dirs lib/typescript/src lib/module"
47
46
  },
48
47
  "repository": {
49
48
  "type": "git",