@shopify/react-native-skia 2.6.8 → 2.7.0
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/cpp/jni/include/JniSkiaBaseView.h +6 -4
- package/android/cpp/jni/include/JniSkiaPictureView.h +8 -6
- package/android/cpp/rnskia-android/OpenGLContext.h +20 -5
- package/android/cpp/rnskia-android/RNSkAndroidView.h +8 -8
- package/android/cpp/rnskia-android/RNSkOpenGLCanvasProvider.cpp +9 -5
- package/android/cpp/rnskia-android/RNSkOpenGLCanvasProvider.h +4 -2
- package/android/src/main/java/com/shopify/reactnative/skia/SkiaBaseView.java +48 -12
- package/android/src/main/java/com/shopify/reactnative/skia/SkiaBaseViewManager.java +5 -0
- package/android/src/main/java/com/shopify/reactnative/skia/SkiaPictureView.java +2 -2
- package/android/src/main/java/com/shopify/reactnative/skia/ViewScreenshotService.java +11 -7
- package/apple/MetalContext.h +7 -6
- package/apple/MetalLayerColorSpace.mm +39 -0
- package/apple/MetalLayerColorSpaceUtils.h +34 -0
- package/apple/MetalWindowContext.h +3 -1
- package/apple/MetalWindowContext.mm +9 -8
- package/apple/RNSkAppleView.h +6 -0
- package/apple/RNSkMetalCanvasProvider.h +2 -0
- package/apple/RNSkMetalCanvasProvider.mm +15 -2
- package/apple/SkiaCVPixelBufferUtils.mm +10 -0
- package/apple/SkiaPictureView.mm +1 -0
- package/apple/SkiaPictureViewManager.mm +5 -0
- package/apple/SkiaUIView.h +1 -0
- package/apple/SkiaUIView.mm +9 -0
- package/cpp/api/JsiSkSurface.h +8 -2
- package/cpp/rnskia/RNDawnContext.h +4 -3
- package/cpp/rnskia/RNDawnUtils.h +25 -6
- package/cpp/rnskia/RNDawnWindowContext.h +44 -10
- package/cpp/rnskia/RNMetalLayerColorSpace.h +16 -0
- package/cpp/rnskia/RNSkJsiViewApi.h +46 -24
- package/cpp/rnwgpu/SurfaceRegistry.h +10 -4
- package/lib/commonjs/Platform/Platform.web.js +2 -90
- package/lib/commonjs/Platform/Platform.web.js.map +1 -1
- package/lib/commonjs/mock/index.js +1 -0
- package/lib/commonjs/mock/index.js.map +1 -1
- package/lib/commonjs/renderer/Canvas.d.ts +10 -1
- package/lib/commonjs/renderer/Canvas.js +2 -0
- package/lib/commonjs/renderer/Canvas.js.map +1 -1
- package/lib/commonjs/skia/types/Surface/Surface.d.ts +5 -1
- package/lib/commonjs/skia/types/Surface/Surface.js.map +1 -1
- package/lib/commonjs/skia/web/JsiSkParagraphStyle.js +14 -3
- package/lib/commonjs/skia/web/JsiSkParagraphStyle.js.map +1 -1
- package/lib/commonjs/skia/web/JsiSkSurface.d.ts +1 -1
- package/lib/commonjs/skia/web/JsiSkSurface.js +2 -1
- package/lib/commonjs/skia/web/JsiSkSurface.js.map +1 -1
- package/lib/commonjs/sksg/Container.native.d.ts +1 -0
- package/lib/commonjs/sksg/Container.native.js +11 -0
- package/lib/commonjs/sksg/Container.native.js.map +1 -1
- package/lib/commonjs/sksg/Container.web.d.ts +1 -0
- package/lib/commonjs/sksg/Container.web.js +11 -0
- package/lib/commonjs/sksg/Container.web.js.map +1 -1
- package/lib/commonjs/specs/NativeSkiaModule.web.d.ts +2 -0
- package/lib/commonjs/specs/NativeSkiaModule.web.js +32 -7
- package/lib/commonjs/specs/NativeSkiaModule.web.js.map +1 -1
- package/lib/commonjs/specs/SkiaPictureViewNativeComponent.d.ts +1 -0
- package/lib/commonjs/specs/SkiaPictureViewNativeComponent.js.map +1 -1
- package/lib/commonjs/views/SkiaPictureView.js +2 -0
- package/lib/commonjs/views/SkiaPictureView.js.map +1 -1
- package/lib/commonjs/views/SkiaPictureView.web.js +279 -94
- package/lib/commonjs/views/SkiaPictureView.web.js.map +1 -1
- package/lib/commonjs/views/formats.d.ts +1 -0
- package/lib/commonjs/views/formats.js +28 -0
- package/lib/commonjs/views/formats.js.map +1 -0
- package/lib/commonjs/views/index.d.ts +1 -0
- package/lib/commonjs/views/index.js +11 -0
- package/lib/commonjs/views/index.js.map +1 -1
- package/lib/commonjs/views/types.d.ts +6 -0
- package/lib/commonjs/views/types.js.map +1 -1
- package/lib/module/Platform/Platform.web.js +3 -91
- package/lib/module/Platform/Platform.web.js.map +1 -1
- package/lib/module/mock/index.js +1 -0
- package/lib/module/mock/index.js.map +1 -1
- package/lib/module/renderer/Canvas.d.ts +10 -1
- package/lib/module/renderer/Canvas.js +2 -0
- package/lib/module/renderer/Canvas.js.map +1 -1
- package/lib/module/skia/types/Surface/Surface.d.ts +5 -1
- package/lib/module/skia/types/Surface/Surface.js.map +1 -1
- package/lib/module/skia/web/JsiSkParagraphStyle.js +14 -3
- package/lib/module/skia/web/JsiSkParagraphStyle.js.map +1 -1
- package/lib/module/skia/web/JsiSkSurface.d.ts +1 -1
- package/lib/module/skia/web/JsiSkSurface.js +2 -1
- package/lib/module/skia/web/JsiSkSurface.js.map +1 -1
- package/lib/module/sksg/Container.native.d.ts +1 -0
- package/lib/module/sksg/Container.native.js +11 -0
- package/lib/module/sksg/Container.native.js.map +1 -1
- package/lib/module/sksg/Container.web.d.ts +1 -0
- package/lib/module/sksg/Container.web.js +11 -0
- package/lib/module/sksg/Container.web.js.map +1 -1
- package/lib/module/specs/NativeSkiaModule.web.d.ts +2 -0
- package/lib/module/specs/NativeSkiaModule.web.js +32 -7
- package/lib/module/specs/NativeSkiaModule.web.js.map +1 -1
- package/lib/module/specs/SkiaPictureViewNativeComponent.d.ts +1 -0
- package/lib/module/specs/SkiaPictureViewNativeComponent.js.map +1 -1
- package/lib/module/views/SkiaPictureView.js +2 -0
- package/lib/module/views/SkiaPictureView.js.map +1 -1
- package/lib/module/views/SkiaPictureView.web.js +280 -95
- package/lib/module/views/SkiaPictureView.web.js.map +1 -1
- package/lib/module/views/formats.d.ts +1 -0
- package/lib/module/views/formats.js +22 -0
- package/lib/module/views/formats.js.map +1 -0
- package/lib/module/views/index.d.ts +1 -0
- package/lib/module/views/index.js +1 -0
- package/lib/module/views/index.js.map +1 -1
- package/lib/module/views/types.d.ts +6 -0
- package/lib/module/views/types.js.map +1 -1
- package/lib/typescript/lib/commonjs/Platform/Platform.web.d.ts +1 -2
- package/lib/typescript/lib/commonjs/mock/index.d.ts +1 -0
- package/lib/typescript/lib/commonjs/renderer/Canvas.d.ts +2 -1
- package/lib/typescript/lib/commonjs/skia/web/JsiSkSurface.d.ts +1 -1
- package/lib/typescript/lib/commonjs/sksg/Container.native.d.ts +1 -1
- package/lib/typescript/lib/commonjs/sksg/Container.web.d.ts +1 -1
- package/lib/typescript/lib/commonjs/views/formats.d.ts +2 -0
- package/lib/typescript/lib/module/Platform/Platform.web.d.ts +1 -3
- package/lib/typescript/lib/module/mock/index.d.ts +2 -2
- package/lib/typescript/lib/module/renderer/Canvas.d.ts +2 -1
- package/lib/typescript/lib/module/skia/web/JsiSkSurface.d.ts +1 -1
- package/lib/typescript/lib/module/sksg/Container.native.d.ts +1 -1
- package/lib/typescript/lib/module/sksg/Container.web.d.ts +1 -1
- package/lib/typescript/lib/module/views/formats.d.ts +1 -0
- package/lib/typescript/lib/module/views/index.d.ts +1 -0
- package/lib/typescript/src/renderer/Canvas.d.ts +10 -1
- package/lib/typescript/src/skia/types/Surface/Surface.d.ts +5 -1
- package/lib/typescript/src/skia/web/JsiSkSurface.d.ts +1 -1
- package/lib/typescript/src/sksg/Container.native.d.ts +1 -0
- package/lib/typescript/src/sksg/Container.web.d.ts +1 -0
- package/lib/typescript/src/specs/NativeSkiaModule.web.d.ts +2 -0
- package/lib/typescript/src/specs/SkiaPictureViewNativeComponent.d.ts +1 -0
- package/lib/typescript/src/views/formats.d.ts +1 -0
- package/lib/typescript/src/views/index.d.ts +1 -0
- package/lib/typescript/src/views/types.d.ts +6 -0
- package/package.json +4 -1
- package/react-native-skia.podspec +1 -0
- package/src/Platform/Platform.web.tsx +7 -99
- package/src/mock/index.ts +1 -0
- package/src/renderer/Canvas.tsx +11 -0
- package/src/skia/types/Surface/Surface.ts +5 -1
- package/src/skia/web/JsiSkParagraphStyle.ts +16 -2
- package/src/skia/web/JsiSkSurface.ts +2 -1
- package/src/sksg/Container.native.ts +12 -0
- package/src/sksg/Container.web.ts +12 -0
- package/src/specs/NativeSkiaModule.web.ts +39 -7
- package/src/specs/SkiaPictureViewNativeComponent.ts +1 -0
- package/src/views/SkiaPictureView.tsx +2 -0
- package/src/views/SkiaPictureView.web.tsx +318 -106
- package/src/views/formats.ts +21 -0
- package/src/views/index.ts +1 -0
- package/src/views/types.ts +7 -0
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); }
|
|
2
1
|
function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
|
|
3
2
|
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; }
|
|
4
3
|
function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
|
|
5
|
-
/* global HTMLCanvasElement */
|
|
6
|
-
import React, { useRef, useEffect, useCallback, useImperativeHandle } from "react";
|
|
4
|
+
/* global HTMLCanvasElement, ResizeObserver, MediaQueryList */
|
|
5
|
+
import React, { useRef, useEffect, useCallback, useImperativeHandle, useLayoutEffect } from "react";
|
|
7
6
|
import { JsiSkSurface } from "../skia/web/JsiSkSurface";
|
|
8
7
|
import { Platform } from "../Platform";
|
|
9
8
|
import { SkiaViewNativeId } from "./SkiaViewNativeId";
|
|
@@ -19,10 +18,26 @@ const dp2Pixel = (pd, rect) => {
|
|
|
19
18
|
};
|
|
20
19
|
};
|
|
21
20
|
class WebGLRenderer {
|
|
22
|
-
constructor(canvas
|
|
21
|
+
constructor(canvas) {
|
|
23
22
|
this.canvas = canvas;
|
|
24
|
-
this.pd = pd;
|
|
25
23
|
_defineProperty(this, "surface", null);
|
|
24
|
+
_defineProperty(this, "grContext", null);
|
|
25
|
+
_defineProperty(this, "contextHandle", 0);
|
|
26
|
+
_defineProperty(this, "pd", 1);
|
|
27
|
+
this.contextHandle = CanvasKit.GetWebGLContext(canvas);
|
|
28
|
+
if (!this.contextHandle) {
|
|
29
|
+
throw new Error("Could not create a WebGL context");
|
|
30
|
+
}
|
|
31
|
+
this.grContext = CanvasKit.MakeWebGLContext(this.contextHandle);
|
|
32
|
+
if (!this.grContext) {
|
|
33
|
+
CanvasKit.deleteContext(this.contextHandle);
|
|
34
|
+
this.contextHandle = 0;
|
|
35
|
+
throw new Error("Could not create a graphics context");
|
|
36
|
+
}
|
|
37
|
+
const ctx = canvas.getContext("webgl2");
|
|
38
|
+
if (ctx) {
|
|
39
|
+
ctx.drawingBufferColorSpace = "display-p3";
|
|
40
|
+
}
|
|
26
41
|
this.onResize();
|
|
27
42
|
}
|
|
28
43
|
makeImageSnapshot(picture, rect) {
|
|
@@ -36,17 +51,26 @@ class WebGLRenderer {
|
|
|
36
51
|
return this.surface.makeImageSnapshot(dp2Pixel(this.pd, rect));
|
|
37
52
|
}
|
|
38
53
|
onResize() {
|
|
54
|
+
var _this$surface;
|
|
39
55
|
const {
|
|
40
|
-
canvas
|
|
41
|
-
pd
|
|
56
|
+
canvas
|
|
42
57
|
} = this;
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
58
|
+
if (!this.grContext) {
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
this.pd = window.devicePixelRatio;
|
|
62
|
+
canvas.width = canvas.clientWidth * this.pd;
|
|
63
|
+
canvas.height = canvas.clientHeight * this.pd;
|
|
64
|
+
(_this$surface = this.surface) === null || _this$surface === void 0 || _this$surface.ref.delete();
|
|
65
|
+
this.surface = null;
|
|
66
|
+
if (canvas.width === 0 || canvas.height === 0) {
|
|
67
|
+
// The canvas hasn't been laid out yet (or is hidden). The view's
|
|
68
|
+
// ResizeObserver calls us again as soon as it has a size.
|
|
69
|
+
return;
|
|
49
70
|
}
|
|
71
|
+
// Reuse the existing WebGL context and GrDirectContext: only the surface
|
|
72
|
+
// needs to be recreated when the canvas is resized.
|
|
73
|
+
const surface = CanvasKit.MakeOnScreenGLSurface(this.grContext, canvas.width, canvas.height, CanvasKit.ColorSpace.SRGB);
|
|
50
74
|
if (!surface) {
|
|
51
75
|
throw new Error("Could not create surface");
|
|
52
76
|
}
|
|
@@ -57,39 +81,68 @@ class WebGLRenderer {
|
|
|
57
81
|
const canvas = this.surface.getCanvas();
|
|
58
82
|
canvas.clear(Float32Array.of(0, 0, 0, 0));
|
|
59
83
|
canvas.save();
|
|
60
|
-
canvas.scale(pd, pd);
|
|
84
|
+
canvas.scale(this.pd, this.pd);
|
|
61
85
|
canvas.drawPicture(picture);
|
|
62
86
|
canvas.restore();
|
|
63
87
|
this.surface.ref.flush();
|
|
64
88
|
}
|
|
65
89
|
}
|
|
66
90
|
dispose() {
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
this.
|
|
91
|
+
var _this$surface2, _this$canvas;
|
|
92
|
+
(_this$surface2 = this.surface) === null || _this$surface2 === void 0 || _this$surface2.ref.delete();
|
|
93
|
+
this.surface = null;
|
|
94
|
+
if (this.grContext) {
|
|
95
|
+
this.grContext.releaseResourcesAndAbandonContext();
|
|
96
|
+
this.grContext.delete();
|
|
97
|
+
this.grContext = null;
|
|
98
|
+
}
|
|
99
|
+
(_this$canvas = this.canvas) === null || _this$canvas === void 0 || (_this$canvas = _this$canvas.getContext("webgl2")) === null || _this$canvas === void 0 || (_this$canvas = _this$canvas.getExtension("WEBGL_lose_context")) === null || _this$canvas === void 0 || _this$canvas.loseContext();
|
|
100
|
+
if (this.contextHandle) {
|
|
101
|
+
// Unregister the context from CanvasKit's internal registry, otherwise
|
|
102
|
+
// it retains the canvas element (and its detached DOM tree) forever.
|
|
103
|
+
CanvasKit.deleteContext(this.contextHandle);
|
|
104
|
+
// Making the now-deleted handle current clears CanvasKit's
|
|
105
|
+
// current-context globals (GLctx/Module.ctx), which would otherwise
|
|
106
|
+
// keep referencing the context (and the canvas) until another surface
|
|
107
|
+
// becomes current. With a deleted handle this is a no-op that returns
|
|
108
|
+
// null without creating anything.
|
|
109
|
+
CanvasKit.MakeWebGLContext(this.contextHandle);
|
|
110
|
+
this.contextHandle = 0;
|
|
72
111
|
}
|
|
73
112
|
}
|
|
74
113
|
}
|
|
75
114
|
class StaticWebGLRenderer {
|
|
76
|
-
constructor(canvas
|
|
115
|
+
constructor(canvas) {
|
|
77
116
|
this.canvas = canvas;
|
|
78
|
-
this.pd = pd;
|
|
79
117
|
_defineProperty(this, "cachedImage", null);
|
|
118
|
+
_defineProperty(this, "pd", 1);
|
|
80
119
|
}
|
|
81
120
|
onResize() {
|
|
82
121
|
this.cachedImage = null;
|
|
83
122
|
}
|
|
84
123
|
renderPictureToSurface(picture) {
|
|
124
|
+
this.pd = window.devicePixelRatio;
|
|
125
|
+
if (this.canvas.clientWidth === 0 || this.canvas.clientHeight === 0) {
|
|
126
|
+
return null;
|
|
127
|
+
}
|
|
85
128
|
const tempCanvas = new OffscreenCanvas(this.canvas.clientWidth * this.pd, this.canvas.clientHeight * this.pd);
|
|
86
129
|
let surface = null;
|
|
130
|
+
let grContext = null;
|
|
131
|
+
let contextHandle = 0;
|
|
87
132
|
try {
|
|
88
|
-
|
|
133
|
+
contextHandle = CanvasKit.GetWebGLContext(tempCanvas);
|
|
134
|
+
if (!contextHandle) {
|
|
135
|
+
throw new Error("Could not create a WebGL context");
|
|
136
|
+
}
|
|
137
|
+
grContext = CanvasKit.MakeWebGLContext(contextHandle);
|
|
138
|
+
if (!grContext) {
|
|
139
|
+
throw new Error("Could not create a graphics context");
|
|
140
|
+
}
|
|
89
141
|
const ctx = tempCanvas.getContext("webgl2");
|
|
90
142
|
if (ctx) {
|
|
91
143
|
ctx.drawingBufferColorSpace = "display-p3";
|
|
92
144
|
}
|
|
145
|
+
const webglSurface = CanvasKit.MakeOnScreenGLSurface(grContext, tempCanvas.width, tempCanvas.height, CanvasKit.ColorSpace.SRGB);
|
|
93
146
|
if (!webglSurface) {
|
|
94
147
|
throw new Error("Could not create WebGL surface");
|
|
95
148
|
}
|
|
@@ -103,23 +156,34 @@ class StaticWebGLRenderer {
|
|
|
103
156
|
surface.ref.flush();
|
|
104
157
|
return {
|
|
105
158
|
surface,
|
|
106
|
-
tempCanvas
|
|
159
|
+
tempCanvas,
|
|
160
|
+
grContext,
|
|
161
|
+
contextHandle
|
|
107
162
|
};
|
|
108
163
|
} catch (error) {
|
|
109
|
-
|
|
110
|
-
surface
|
|
111
|
-
|
|
112
|
-
|
|
164
|
+
this.cleanupRenderResult({
|
|
165
|
+
surface,
|
|
166
|
+
tempCanvas,
|
|
167
|
+
grContext,
|
|
168
|
+
contextHandle
|
|
169
|
+
});
|
|
113
170
|
return null;
|
|
114
171
|
}
|
|
115
172
|
}
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
173
|
+
cleanupRenderResult(result) {
|
|
174
|
+
var _result$surface, _result$tempCanvas$ge;
|
|
175
|
+
(_result$surface = result.surface) === null || _result$surface === void 0 || _result$surface.ref.delete();
|
|
176
|
+
if (result.grContext) {
|
|
177
|
+
result.grContext.releaseResourcesAndAbandonContext();
|
|
178
|
+
result.grContext.delete();
|
|
179
|
+
}
|
|
180
|
+
(_result$tempCanvas$ge = result.tempCanvas.getContext("webgl2")) === null || _result$tempCanvas$ge === void 0 || (_result$tempCanvas$ge = _result$tempCanvas$ge.getExtension("WEBGL_lose_context")) === null || _result$tempCanvas$ge === void 0 || _result$tempCanvas$ge.loseContext();
|
|
181
|
+
if (result.contextHandle) {
|
|
182
|
+
// Unregister the context from CanvasKit's internal registry, otherwise
|
|
183
|
+
// it retains the OffscreenCanvas forever.
|
|
184
|
+
CanvasKit.deleteContext(result.contextHandle);
|
|
185
|
+
// Clear CanvasKit's current-context globals (see WebGLRenderer.dispose).
|
|
186
|
+
CanvasKit.MakeWebGLContext(result.contextHandle);
|
|
123
187
|
}
|
|
124
188
|
}
|
|
125
189
|
draw(picture) {
|
|
@@ -132,6 +196,7 @@ class StaticWebGLRenderer {
|
|
|
132
196
|
} = renderResult;
|
|
133
197
|
const ctx2d = this.canvas.getContext("2d");
|
|
134
198
|
if (!ctx2d) {
|
|
199
|
+
this.cleanupRenderResult(renderResult);
|
|
135
200
|
throw new Error("Could not get 2D context");
|
|
136
201
|
}
|
|
137
202
|
|
|
@@ -141,7 +206,7 @@ class StaticWebGLRenderer {
|
|
|
141
206
|
|
|
142
207
|
// Draw the tempCanvas scaled down to the display size
|
|
143
208
|
ctx2d.drawImage(tempCanvas, 0, 0, tempCanvas.width, tempCanvas.height, 0, 0, this.canvas.clientWidth * this.pd, this.canvas.clientHeight * this.pd);
|
|
144
|
-
this.
|
|
209
|
+
this.cleanupRenderResult(renderResult);
|
|
145
210
|
}
|
|
146
211
|
makeImageSnapshot(picture, rect) {
|
|
147
212
|
if (!this.cachedImage) {
|
|
@@ -149,17 +214,12 @@ class StaticWebGLRenderer {
|
|
|
149
214
|
if (!renderResult) {
|
|
150
215
|
return null;
|
|
151
216
|
}
|
|
152
|
-
const {
|
|
153
|
-
surface,
|
|
154
|
-
tempCanvas
|
|
155
|
-
} = renderResult;
|
|
156
217
|
try {
|
|
157
|
-
this.cachedImage = surface.makeImageSnapshot(dp2Pixel(this.pd, rect));
|
|
218
|
+
this.cachedImage = renderResult.surface.makeImageSnapshot(dp2Pixel(this.pd, rect));
|
|
158
219
|
} catch (error) {
|
|
159
220
|
console.error("Error creating image snapshot:", error);
|
|
160
221
|
} finally {
|
|
161
|
-
|
|
162
|
-
this.cleanupWebGLContext(tempCanvas);
|
|
222
|
+
this.cleanupRenderResult(renderResult);
|
|
163
223
|
}
|
|
164
224
|
}
|
|
165
225
|
return this.cachedImage;
|
|
@@ -170,23 +230,87 @@ class StaticWebGLRenderer {
|
|
|
170
230
|
this.cachedImage = null;
|
|
171
231
|
}
|
|
172
232
|
}
|
|
173
|
-
|
|
233
|
+
|
|
234
|
+
// Mirrors the event react-native-web synthesizes for onLayout.
|
|
235
|
+
const makeLayoutEvent = (x, y, width, height) => ({
|
|
236
|
+
timeStamp: Date.now(),
|
|
237
|
+
nativeEvent: {
|
|
238
|
+
layout: {
|
|
239
|
+
x,
|
|
240
|
+
y,
|
|
241
|
+
width,
|
|
242
|
+
height
|
|
243
|
+
}
|
|
244
|
+
},
|
|
245
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
246
|
+
// @ts-expect-error
|
|
247
|
+
currentTarget: 0,
|
|
248
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
249
|
+
// @ts-expect-error
|
|
250
|
+
target: 0,
|
|
251
|
+
bubbles: false,
|
|
252
|
+
cancelable: false,
|
|
253
|
+
defaultPrevented: false,
|
|
254
|
+
eventPhase: 0,
|
|
255
|
+
isDefaultPrevented() {
|
|
256
|
+
throw new Error("Method not supported on web.");
|
|
257
|
+
},
|
|
258
|
+
isPropagationStopped() {
|
|
259
|
+
throw new Error("Method not supported on web.");
|
|
260
|
+
},
|
|
261
|
+
persist() {
|
|
262
|
+
throw new Error("Method not supported on web.");
|
|
263
|
+
},
|
|
264
|
+
preventDefault() {
|
|
265
|
+
throw new Error("Method not supported on web.");
|
|
266
|
+
},
|
|
267
|
+
stopPropagation() {
|
|
268
|
+
throw new Error("Method not supported on web.");
|
|
269
|
+
},
|
|
270
|
+
isTrusted: true,
|
|
271
|
+
type: ""
|
|
272
|
+
});
|
|
174
273
|
export const SkiaPictureView = props => {
|
|
175
274
|
const {
|
|
176
|
-
ref
|
|
177
|
-
} = props;
|
|
178
|
-
const canvasRef = useRef(null);
|
|
179
|
-
const renderer = useRef(null);
|
|
180
|
-
const redrawRequestsRef = useRef(0);
|
|
181
|
-
const requestIdRef = useRef(0);
|
|
182
|
-
const pictureRef = useRef(null);
|
|
183
|
-
const {
|
|
275
|
+
ref,
|
|
184
276
|
picture,
|
|
185
277
|
onLayout
|
|
186
278
|
} = props;
|
|
187
|
-
const
|
|
188
|
-
|
|
279
|
+
const canvasRef = useRef(null);
|
|
280
|
+
const rendererRef = useRef(null);
|
|
281
|
+
const pictureRef = useRef(null);
|
|
282
|
+
// The rendering lifecycle deliberately does not depend on layout events:
|
|
283
|
+
// - The renderer is created synchronously on mount (the canvas element is
|
|
284
|
+
// guaranteed to exist in useLayoutEffect; a zero size at that point is
|
|
285
|
+
// fine, the surface is created once the canvas is measurable).
|
|
286
|
+
// - Redraw requests coalesce into a single animation frame. A picture
|
|
287
|
+
// dispatched before the canvas has a size stays in pictureRef (drawing
|
|
288
|
+
// while unmeasured is a no-op) and is painted by the resize path below
|
|
289
|
+
// once the canvas becomes measurable, so it is never lost.
|
|
290
|
+
// - A ResizeObserver on the canvas itself recreates the surface and repaints
|
|
291
|
+
// synchronously (its callbacks run after layout, before paint), and is
|
|
292
|
+
// also the source of the user-facing onLayout event.
|
|
293
|
+
const redrawPendingRef = useRef(false);
|
|
294
|
+
const frameRef = useRef(0);
|
|
295
|
+
const onLayoutRef = useRef(onLayout);
|
|
296
|
+
useLayoutEffect(() => {
|
|
297
|
+
onLayoutRef.current = onLayout;
|
|
298
|
+
}, [onLayout]);
|
|
299
|
+
const flushRedraw = useCallback(() => {
|
|
300
|
+
frameRef.current = 0;
|
|
301
|
+
if (redrawPendingRef.current && rendererRef.current && pictureRef.current) {
|
|
302
|
+
redrawPendingRef.current = false;
|
|
303
|
+
rendererRef.current.draw(pictureRef.current);
|
|
304
|
+
}
|
|
305
|
+
// If the renderer or picture isn't available yet, the request stays
|
|
306
|
+
// pending and is flushed by whichever arrives last.
|
|
189
307
|
}, []);
|
|
308
|
+
const redraw = useCallback(() => {
|
|
309
|
+
redrawPendingRef.current = true;
|
|
310
|
+
if (frameRef.current === 0) {
|
|
311
|
+
frameRef.current = requestAnimationFrame(flushRedraw);
|
|
312
|
+
}
|
|
313
|
+
}, [flushRedraw]);
|
|
190
314
|
const getSize = useCallback(() => {
|
|
191
315
|
var _canvasRef$current, _canvasRef$current2;
|
|
192
316
|
return {
|
|
@@ -199,8 +323,8 @@ export const SkiaPictureView = props => {
|
|
|
199
323
|
redraw();
|
|
200
324
|
}, [redraw]);
|
|
201
325
|
const makeImageSnapshot = useCallback(rect => {
|
|
202
|
-
if (
|
|
203
|
-
return
|
|
326
|
+
if (rendererRef.current && pictureRef.current) {
|
|
327
|
+
return rendererRef.current.makeImageSnapshot(pictureRef.current, rect);
|
|
204
328
|
}
|
|
205
329
|
return null;
|
|
206
330
|
}, []);
|
|
@@ -233,27 +357,98 @@ export const SkiaPictureView = props => {
|
|
|
233
357
|
callback(x, y, rect.width, rect.height);
|
|
234
358
|
}
|
|
235
359
|
}, []);
|
|
236
|
-
const
|
|
237
|
-
|
|
238
|
-
redrawRequestsRef.current = 0;
|
|
239
|
-
if (renderer.current && pictureRef.current) {
|
|
240
|
-
renderer.current.draw(pictureRef.current);
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
requestIdRef.current = requestAnimationFrame(tick);
|
|
244
|
-
}, []);
|
|
245
|
-
const onLayoutEvent = useCallback(evt => {
|
|
360
|
+
const isStatic = props.__destroyWebGLContextAfterRender === true;
|
|
361
|
+
useLayoutEffect(() => {
|
|
246
362
|
const canvas = canvasRef.current;
|
|
247
|
-
if (canvas) {
|
|
248
|
-
|
|
363
|
+
if (!canvas) {
|
|
364
|
+
return undefined;
|
|
365
|
+
}
|
|
366
|
+
const renderer = isStatic ? new StaticWebGLRenderer(canvas) : new WebGLRenderer(canvas);
|
|
367
|
+
rendererRef.current = renderer;
|
|
368
|
+
const drawPicture = () => {
|
|
249
369
|
if (pictureRef.current) {
|
|
250
|
-
|
|
370
|
+
redrawPendingRef.current = false;
|
|
371
|
+
renderer.draw(pictureRef.current);
|
|
251
372
|
}
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
373
|
+
};
|
|
374
|
+
|
|
375
|
+
// The renderer constructor already sized the surface from the current
|
|
376
|
+
// layout, so the observer's initial delivery only repaints if the size
|
|
377
|
+
// changed in between.
|
|
378
|
+
let lastWidth = canvas.clientWidth;
|
|
379
|
+
let lastHeight = canvas.clientHeight;
|
|
380
|
+
let lastPixelDensity = window.devicePixelRatio;
|
|
381
|
+
const resizeIfNeeded = () => {
|
|
382
|
+
const width = canvas.clientWidth;
|
|
383
|
+
const height = canvas.clientHeight;
|
|
384
|
+
const pixelDensity = window.devicePixelRatio;
|
|
385
|
+
if (width === lastWidth && height === lastHeight && pixelDensity === lastPixelDensity) {
|
|
386
|
+
return;
|
|
387
|
+
}
|
|
388
|
+
lastWidth = width;
|
|
389
|
+
lastHeight = height;
|
|
390
|
+
lastPixelDensity = pixelDensity;
|
|
391
|
+
renderer.onResize();
|
|
392
|
+
drawPicture();
|
|
393
|
+
};
|
|
394
|
+
const observer = new ResizeObserver(entries => {
|
|
395
|
+
resizeIfNeeded();
|
|
396
|
+
const layoutHandler = onLayoutRef.current;
|
|
397
|
+
if (layoutHandler) {
|
|
398
|
+
const {
|
|
399
|
+
left,
|
|
400
|
+
top,
|
|
401
|
+
width,
|
|
402
|
+
height
|
|
403
|
+
} = entries[0].contentRect;
|
|
404
|
+
// setTimeout 0 is taken from react-native-web (UIManager)
|
|
405
|
+
setTimeout(() => layoutHandler(makeLayoutEvent(left, top, width, height)), 0);
|
|
406
|
+
}
|
|
407
|
+
});
|
|
408
|
+
observer.observe(canvas);
|
|
409
|
+
|
|
410
|
+
// A pixel-density change with no CSS size change (browser zoom, moving
|
|
411
|
+
// the window to another display) doesn't trigger the ResizeObserver:
|
|
412
|
+
// watch it via matchMedia, re-arming the query for each new density.
|
|
413
|
+
// This degrades gracefully where unsupported: without matchMedia or
|
|
414
|
+
// MediaQueryList.addEventListener (older Safari), or where the query
|
|
415
|
+
// never matches because the resolution feature is unknown (Safari < 16),
|
|
416
|
+
// density-only changes simply don't repaint — everything else still does.
|
|
417
|
+
let media = null;
|
|
418
|
+
const onPixelDensityChange = () => {
|
|
419
|
+
resizeIfNeeded();
|
|
420
|
+
watchPixelDensity();
|
|
421
|
+
};
|
|
422
|
+
const watchPixelDensity = () => {
|
|
423
|
+
var _media;
|
|
424
|
+
(_media = media) === null || _media === void 0 || _media.removeEventListener("change", onPixelDensityChange);
|
|
425
|
+
media = null;
|
|
426
|
+
if (typeof window.matchMedia === "function") {
|
|
427
|
+
const query = window.matchMedia(`(resolution: ${window.devicePixelRatio}dppx)`);
|
|
428
|
+
if (typeof query.addEventListener === "function") {
|
|
429
|
+
media = query;
|
|
430
|
+
media.addEventListener("change", onPixelDensityChange);
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
};
|
|
434
|
+
watchPixelDensity();
|
|
435
|
+
|
|
436
|
+
// Paint any picture that was dispatched before the renderer existed.
|
|
437
|
+
drawPicture();
|
|
438
|
+
return () => {
|
|
439
|
+
var _media2;
|
|
440
|
+
observer.disconnect();
|
|
441
|
+
(_media2 = media) === null || _media2 === void 0 || _media2.removeEventListener("change", onPixelDensityChange);
|
|
442
|
+
rendererRef.current = null;
|
|
443
|
+
renderer.dispose();
|
|
444
|
+
};
|
|
445
|
+
}, [isStatic]);
|
|
446
|
+
useEffect(() => {
|
|
447
|
+
return () => {
|
|
448
|
+
cancelAnimationFrame(frameRef.current);
|
|
449
|
+
frameRef.current = 0;
|
|
450
|
+
};
|
|
451
|
+
}, []);
|
|
257
452
|
useImperativeHandle(ref, () => ({
|
|
258
453
|
setPicture,
|
|
259
454
|
getSize,
|
|
@@ -268,7 +463,8 @@ export const SkiaPictureView = props => {
|
|
|
268
463
|
useEffect(() => {
|
|
269
464
|
var _props$nativeID;
|
|
270
465
|
const nativeID = (_props$nativeID = props.nativeID) !== null && _props$nativeID !== void 0 ? _props$nativeID : `${SkiaViewNativeId.current++}`;
|
|
271
|
-
global.SkiaViewApi
|
|
466
|
+
const api = global.SkiaViewApi;
|
|
467
|
+
api.registerView(nativeID, {
|
|
272
468
|
setPicture,
|
|
273
469
|
getSize,
|
|
274
470
|
redraw,
|
|
@@ -276,35 +472,24 @@ export const SkiaPictureView = props => {
|
|
|
276
472
|
measure,
|
|
277
473
|
measureInWindow
|
|
278
474
|
});
|
|
279
|
-
}, [setPicture, getSize, redraw, makeImageSnapshot, measure, measureInWindow, props.nativeID]);
|
|
280
|
-
useEffect(() => {
|
|
281
|
-
if (props.picture) {
|
|
282
|
-
setPicture(props.picture);
|
|
283
|
-
}
|
|
284
|
-
}, [setPicture, props.picture]);
|
|
285
|
-
useEffect(() => {
|
|
286
|
-
tick();
|
|
287
475
|
return () => {
|
|
288
|
-
|
|
289
|
-
if (renderer.current) {
|
|
290
|
-
renderer.current.dispose();
|
|
291
|
-
renderer.current = null;
|
|
292
|
-
}
|
|
476
|
+
api.unregisterView(nativeID);
|
|
293
477
|
};
|
|
294
|
-
}, [
|
|
478
|
+
}, [setPicture, getSize, redraw, makeImageSnapshot, measure, measureInWindow, props.nativeID]);
|
|
295
479
|
useEffect(() => {
|
|
296
|
-
if (
|
|
297
|
-
|
|
480
|
+
if (picture) {
|
|
481
|
+
setPicture(picture);
|
|
298
482
|
}
|
|
299
|
-
}, [
|
|
483
|
+
}, [setPicture, picture]);
|
|
300
484
|
const {
|
|
301
|
-
debug
|
|
485
|
+
debug: _debug,
|
|
302
486
|
ref: _ref,
|
|
487
|
+
onLayout: _onLayout,
|
|
488
|
+
picture: _picture,
|
|
489
|
+
__destroyWebGLContextAfterRender: _isStatic,
|
|
303
490
|
...viewProps
|
|
304
491
|
} = props;
|
|
305
|
-
return /*#__PURE__*/React.createElement(Platform.View,
|
|
306
|
-
onLayout: onLayoutEvent
|
|
307
|
-
}), /*#__PURE__*/React.createElement("canvas", {
|
|
492
|
+
return /*#__PURE__*/React.createElement(Platform.View, viewProps, /*#__PURE__*/React.createElement("canvas", {
|
|
308
493
|
ref: canvasRef,
|
|
309
494
|
style: {
|
|
310
495
|
display: "block",
|