@shopify/react-native-skia 0.1.199 → 0.1.201

Sign up to get free protection for your applications and to get access to all the features.
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",