@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.
- package/android/CMakeLists.txt +1 -1
- package/android/cpp/jni/include/JniSkiaBaseView.h +74 -13
- package/android/cpp/jni/include/JniSkiaDomView.h +20 -12
- package/android/cpp/jni/include/JniSkiaDrawView.h +20 -14
- package/android/cpp/jni/include/JniSkiaPictureView.h +24 -15
- package/android/cpp/rnskia-android/RNSkAndroidPlatformContext.h +2 -2
- package/android/cpp/rnskia-android/RNSkOpenGLCanvasProvider.cpp +41 -44
- package/android/cpp/rnskia-android/RNSkOpenGLCanvasProvider.h +4 -6
- package/android/cpp/rnskia-android/SkiaOpenGLHelper.h +310 -0
- package/android/cpp/rnskia-android/SkiaOpenGLSurfaceFactory.cpp +132 -0
- package/android/cpp/rnskia-android/SkiaOpenGLSurfaceFactory.h +125 -0
- package/android/src/main/java/com/shopify/reactnative/skia/SkiaBaseView.java +80 -11
- package/android/src/main/java/com/shopify/reactnative/skia/SkiaDomView.java +2 -0
- package/android/src/main/java/com/shopify/reactnative/skia/SkiaDrawView.java +2 -0
- package/android/src/main/java/com/shopify/reactnative/skia/SkiaPictureView.java +3 -0
- package/android/src/main/java/com/shopify/reactnative/skia/ViewScreenshotService.java +7 -5
- package/cpp/api/JsiSkHostObjects.h +0 -10
- package/cpp/rnskia/RNSkJsView.cpp +35 -10
- package/cpp/rnskia/RNSkJsiViewApi.h +1 -1
- package/cpp/rnskia/RNSkView.h +9 -9
- package/ios/RNSkia-iOS/ViewScreenshotService.mm +1 -0
- package/package.json +2 -3
- package/android/cpp/rnskia-android/SkiaOpenGLRenderer.cpp +0 -347
- 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
|
-
|
117
|
-
surfaceDestroyed();
|
176
|
+
Log.i("SkiaBaseView", "onSurfaceTextureDestroyed");
|
118
177
|
// https://developer.android.com/reference/android/view/TextureView.SurfaceTextureListener#onSurfaceTextureDestroyed(android.graphics.SurfaceTexture)
|
119
|
-
|
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
|
-
|
126
|
-
return
|
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,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 =
|
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
|
-
|
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
|
-
|
48
|
-
|
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
|
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->
|
106
|
-
|
107
|
-
|
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
|
}
|
package/cpp/rnskia/RNSkView.h
CHANGED
@@ -86,11 +86,11 @@ protected:
|
|
86
86
|
bool _showDebugOverlays;
|
87
87
|
};
|
88
88
|
|
89
|
-
class
|
89
|
+
class RNSkOffscreenCanvasProvider : public RNSkCanvasProvider {
|
90
90
|
public:
|
91
|
-
|
92
|
-
|
93
|
-
|
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(
|
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(
|
276
|
+
sk_sp<SkImage> makeImageSnapshot(SkRect *bounds) {
|
277
277
|
|
278
|
-
auto provider = std::make_shared<
|
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
|
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.
|
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",
|