react-native-skia 3.0.2 → 3.0.4
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/README.md +19 -2
- package/android/build.gradle +15 -7
- package/android/src/main/java/com/reactnative/skia/BackingViewKind.java +24 -0
- package/android/src/main/java/com/reactnative/skia/FrameScheduler.java +104 -0
- package/android/src/main/java/com/reactnative/skia/SkiaView.java +55 -44
- package/android/src/test/java/com/reactnative/skia/FrameSchedulerTest.java +186 -0
- package/apple/MetalLayerColorSpace.mm +10 -8
- package/apple/RNSkApplePlatformContext.h +12 -0
- package/apple/RNSkApplePlatformContext.mm +17 -0
- package/apple/RNSkMetalCanvasProvider.mm +4 -2
- package/apple/RNSkiaModule.mm +10 -0
- package/cpp/rnskia/RNDawnContext.h +3 -2
- package/cpp/rnskia/RNDawnUtils.h +12 -0
- package/cpp/rnskia/RNDawnWindowContext.cpp +2 -1
- package/cpp/rnskia/RNDawnWindowContext.h +15 -7
- package/cpp/rnskia/RNMetalLayerColorSpace.h +4 -2
- package/cpp/rnskia/RNSkCanvasProvider.h +18 -8
- package/cpp/rnskia/RNSkGraphiteTarget.h +20 -7
- package/cpp/rnskia/RNSkPlatformContext.h +8 -0
- package/cpp/rnskia/RNSkView.h +53 -29
- package/lib/commonjs/external/reanimated/interpolators.js +1 -1
- package/lib/commonjs/external/reanimated/interpolators.js.map +1 -1
- package/lib/commonjs/renderer/Canvas.js +1 -1
- package/lib/commonjs/renderer/Canvas.js.map +1 -1
- package/lib/module/external/reanimated/interpolators.js +1 -1
- package/lib/module/external/reanimated/interpolators.js.map +1 -1
- package/lib/module/renderer/Canvas.js +1 -1
- package/lib/module/renderer/Canvas.js.map +1 -1
- package/package.json +1 -1
- package/react-native-skia.podspec +1 -1
- package/src/external/reanimated/interpolators.ts +1 -1
- package/src/renderer/Canvas.tsx +1 -1
|
@@ -242,6 +242,23 @@ void RNSkApplePlatformContext::raiseError(const std::exception &err) {
|
|
|
242
242
|
RCTFatal(RCTErrorWithMessage([NSString stringWithUTF8String:err.what()]));
|
|
243
243
|
}
|
|
244
244
|
|
|
245
|
+
bool RNSkApplePlatformContext::mainScreenSupportsP3() {
|
|
246
|
+
static bool supportsP3 = false;
|
|
247
|
+
static dispatch_once_t onceToken;
|
|
248
|
+
dispatch_once(&onceToken, ^{
|
|
249
|
+
#if !TARGET_OS_OSX
|
|
250
|
+
supportsP3 =
|
|
251
|
+
[UIScreen mainScreen].traitCollection.displayGamut == UIDisplayGamutP3;
|
|
252
|
+
#else
|
|
253
|
+
NSColorSpace *screenColorSpace = [NSScreen mainScreen].colorSpace;
|
|
254
|
+
supportsP3 =
|
|
255
|
+
screenColorSpace != nil &&
|
|
256
|
+
[screenColorSpace isEqual:[NSColorSpace displayP3ColorSpace]];
|
|
257
|
+
#endif // !TARGET_OS_OSX
|
|
258
|
+
});
|
|
259
|
+
return supportsP3;
|
|
260
|
+
}
|
|
261
|
+
|
|
245
262
|
sk_sp<SkSurface>
|
|
246
263
|
RNSkApplePlatformContext::makeOffscreenSurface(int width, int height,
|
|
247
264
|
bool useP3ColorSpace) {
|
|
@@ -94,8 +94,9 @@ void RNSkMetalCanvasProvider::setSize(int width, int height) {
|
|
|
94
94
|
// product like 1169.9999 gives the pixel size the layout means.
|
|
95
95
|
int w = static_cast<int>(std::lround(width * _context->getPixelDensity()));
|
|
96
96
|
int h = static_cast<int>(std::lround(height * _context->getPixelDensity()));
|
|
97
|
-
_ctx = RNSkia::DawnContext::getInstance().MakeWindow(
|
|
98
|
-
|
|
97
|
+
_ctx = RNSkia::DawnContext::getInstance().MakeWindow(
|
|
98
|
+
(__bridge void *)_layer, w, h, _highBitDepth,
|
|
99
|
+
_context->prefersP3ColorSpace());
|
|
99
100
|
{
|
|
100
101
|
auto *window = static_cast<RNSkia::DawnWindowContext *>(_ctx.get());
|
|
101
102
|
std::lock_guard<std::mutex> lock(_targetInfoMutex);
|
|
@@ -104,6 +105,7 @@ void RNSkMetalCanvasProvider::setSize(int width, int height) {
|
|
|
104
105
|
_targetInfo.width = window->getWidth();
|
|
105
106
|
_targetInfo.height = window->getHeight();
|
|
106
107
|
_targetInfo.colorType = window->getColorType();
|
|
108
|
+
_targetInfo.useP3ColorSpace = window->usesP3ColorSpace();
|
|
107
109
|
_targetInfo.textureInfo = window->getTextureInfo();
|
|
108
110
|
_hasTargetInfo = true;
|
|
109
111
|
}
|
package/apple/RNSkiaModule.mm
CHANGED
|
@@ -39,6 +39,16 @@ RCT_EXPORT_MODULE()
|
|
|
39
39
|
return YES;
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
+
- (instancetype)init {
|
|
43
|
+
if (self = [super init]) {
|
|
44
|
+
// Created on the main queue (see requiresMainQueueSetup): the screen is
|
|
45
|
+
// asked for its color gamut here, where UIKit may be used. The platform
|
|
46
|
+
// context is created on the JS thread and only reads the answer.
|
|
47
|
+
RNSkia::RNSkApplePlatformContext::mainScreenSupportsP3();
|
|
48
|
+
}
|
|
49
|
+
return self;
|
|
50
|
+
}
|
|
51
|
+
|
|
42
52
|
- (void)
|
|
43
53
|
installJSIBindingsWithRuntime:(facebook::jsi::Runtime &)runtime
|
|
44
54
|
callInvoker:
|
|
@@ -406,7 +406,8 @@ public:
|
|
|
406
406
|
|
|
407
407
|
// Create onscreen surface with window
|
|
408
408
|
std::unique_ptr<WindowContext> MakeWindow(void *window, int width, int height,
|
|
409
|
-
bool highBitDepth = false
|
|
409
|
+
bool highBitDepth = false,
|
|
410
|
+
bool useP3ColorSpace = false) {
|
|
410
411
|
// 1. Create Surface
|
|
411
412
|
wgpu::SurfaceDescriptor surfaceDescriptor;
|
|
412
413
|
#ifdef __APPLE__
|
|
@@ -422,7 +423,7 @@ public:
|
|
|
422
423
|
wgpu::Instance(instance->Get()).CreateSurface(&surfaceDescriptor);
|
|
423
424
|
return std::make_unique<DawnWindowContext>(
|
|
424
425
|
getRecorder(), backendContext.fDevice, surface, window, width, height,
|
|
425
|
-
highBitDepth);
|
|
426
|
+
highBitDepth, useP3ColorSpace);
|
|
426
427
|
}
|
|
427
428
|
|
|
428
429
|
skgpu::graphite::Recorder *getRecorder() {
|
package/cpp/rnskia/RNDawnUtils.h
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
#include "dawn/native/DawnNative.h"
|
|
10
10
|
|
|
11
|
+
#include "include/core/SkColorSpace.h"
|
|
11
12
|
#include "include/core/SkColorType.h"
|
|
12
13
|
#include "include/gpu/graphite/dawn/DawnBackendContext.h"
|
|
13
14
|
#include "utils/RNSkLog.h"
|
|
@@ -42,6 +43,17 @@ static const wgpu::TextureFormat HighBitDepthTextureFormat =
|
|
|
42
43
|
wgpu::TextureFormat::RGB10A2Unorm;
|
|
43
44
|
#endif
|
|
44
45
|
|
|
46
|
+
// The color space a view renders in: Display P3 (with the sRGB transfer
|
|
47
|
+
// function) where the platform prefers it (a wide gamut display on Apple
|
|
48
|
+
// platforms, see RNSkPlatformContext::prefersP3ColorSpace), sRGB otherwise.
|
|
49
|
+
// Colors are managed either way: content looks the same in both, Display P3
|
|
50
|
+
// only adds the colors sRGB cannot represent.
|
|
51
|
+
inline sk_sp<SkColorSpace> viewColorSpace(bool useP3ColorSpace) {
|
|
52
|
+
return useP3ColorSpace ? SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB,
|
|
53
|
+
SkNamedGamut::kDisplayP3)
|
|
54
|
+
: SkColorSpace::MakeSRGB();
|
|
55
|
+
}
|
|
56
|
+
|
|
45
57
|
// Usage requested for a window texture when the surface supports it (see
|
|
46
58
|
// DawnWindowContext::supportedSurfaceUsage), and assumed for a Graphite
|
|
47
59
|
// recording made before its window exists: TextureBinding lets a render pass
|
|
@@ -29,7 +29,8 @@ bool DawnWindowContext::presentRecordings(
|
|
|
29
29
|
auto backendTex = skgpu::graphite::BackendTextures::MakeDawn(texture.Get());
|
|
30
30
|
SkSurfaceProps surfaceProps;
|
|
31
31
|
auto surface = SkSurfaces::WrapBackendTexture(
|
|
32
|
-
_recorder, backendTex,
|
|
32
|
+
_recorder, backendTex, DawnUtils::viewColorSpace(_useP3ColorSpace),
|
|
33
|
+
&surfaceProps);
|
|
33
34
|
if (!surface) {
|
|
34
35
|
return false;
|
|
35
36
|
}
|
|
@@ -30,9 +30,11 @@ class DawnWindowContext : public WindowContext {
|
|
|
30
30
|
public:
|
|
31
31
|
DawnWindowContext(skgpu::graphite::Recorder *recorder, wgpu::Device device,
|
|
32
32
|
wgpu::Surface surface, void *nativeSurface, int width,
|
|
33
|
-
int height, bool highBitDepth = false
|
|
33
|
+
int height, bool highBitDepth = false,
|
|
34
|
+
bool useP3ColorSpace = false)
|
|
34
35
|
: _recorder(recorder), _device(device), _surface(surface),
|
|
35
|
-
_nativeSurface(nativeSurface),
|
|
36
|
+
_nativeSurface(nativeSurface), _useP3ColorSpace(useP3ColorSpace),
|
|
37
|
+
_width(width), _height(height) {
|
|
36
38
|
_format = DawnUtils::PreferredTextureFormat;
|
|
37
39
|
_colorType = DawnUtils::PreferedColorType;
|
|
38
40
|
if (highBitDepth) {
|
|
@@ -59,10 +61,10 @@ public:
|
|
|
59
61
|
skgpu::graphite::SampleCount::k1, skgpu::Mipmapped::kNo, _format,
|
|
60
62
|
texture.GetUsage(), wgpu::TextureAspect::All);
|
|
61
63
|
auto backendTex = skgpu::graphite::BackendTextures::MakeDawn(texture.Get());
|
|
62
|
-
sk_sp<SkColorSpace> colorSpace = SkColorSpace::MakeSRGB();
|
|
63
64
|
SkSurfaceProps surfaceProps;
|
|
64
65
|
auto surface = SkSurfaces::WrapBackendTexture(
|
|
65
|
-
_recorder, backendTex, _colorType,
|
|
66
|
+
_recorder, backendTex, _colorType,
|
|
67
|
+
DawnUtils::viewColorSpace(_useP3ColorSpace), &surfaceProps);
|
|
66
68
|
return surface;
|
|
67
69
|
}
|
|
68
70
|
|
|
@@ -80,6 +82,10 @@ public:
|
|
|
80
82
|
|
|
81
83
|
SkColorType getColorType() const { return _colorType; }
|
|
82
84
|
|
|
85
|
+
// Whether the window is in Display P3 rather than sRGB: a deferred canvas
|
|
86
|
+
// must be recorded in the color space of the window it is replayed onto.
|
|
87
|
+
bool usesP3ColorSpace() const { return _useP3ColorSpace; }
|
|
88
|
+
|
|
83
89
|
// The texture description a deferred canvas must be recorded with to be
|
|
84
90
|
// replayed onto this window.
|
|
85
91
|
skgpu::graphite::TextureInfo getTextureInfo() const {
|
|
@@ -114,9 +120,10 @@ private:
|
|
|
114
120
|
#endif
|
|
115
121
|
_surface.Configure(&config);
|
|
116
122
|
#ifdef __APPLE__
|
|
117
|
-
//
|
|
118
|
-
// the
|
|
119
|
-
|
|
123
|
+
// The layer is tagged with the gamut the window renders in; float formats
|
|
124
|
+
// need the (gamma-encoded) extended variant so the values display
|
|
125
|
+
// identically to the 8-bit path.
|
|
126
|
+
applyCAMetalLayerColorSpace(_nativeSurface, _format, _useP3ColorSpace);
|
|
120
127
|
#endif
|
|
121
128
|
}
|
|
122
129
|
|
|
@@ -161,6 +168,7 @@ private:
|
|
|
161
168
|
wgpu::Device _device;
|
|
162
169
|
wgpu::Surface _surface;
|
|
163
170
|
[[maybe_unused]] void *_nativeSurface;
|
|
171
|
+
bool _useP3ColorSpace;
|
|
164
172
|
wgpu::TextureFormat _format;
|
|
165
173
|
wgpu::TextureUsage _usage = wgpu::TextureUsage::RenderAttachment;
|
|
166
174
|
SkColorType _colorType;
|
|
@@ -7,9 +7,11 @@
|
|
|
7
7
|
namespace RNSkia {
|
|
8
8
|
|
|
9
9
|
// Tags the CAMetalLayer with the colorspace matching the configured texture
|
|
10
|
-
// format. Implemented in
|
|
10
|
+
// format and the gamut the view renders in. Implemented in
|
|
11
|
+
// apple/MetalLayerColorSpace.mm.
|
|
11
12
|
void applyCAMetalLayerColorSpace(void *nativeSurface,
|
|
12
|
-
wgpu::TextureFormat format
|
|
13
|
+
wgpu::TextureFormat format,
|
|
14
|
+
bool useP3ColorSpace);
|
|
13
15
|
|
|
14
16
|
} // namespace RNSkia
|
|
15
17
|
|
|
@@ -24,15 +24,19 @@
|
|
|
24
24
|
namespace RNSkia {
|
|
25
25
|
|
|
26
26
|
/**
|
|
27
|
-
* Describes the texture a recording is replayed onto: the pixel size
|
|
28
|
-
* format of a window (or offscreen) surface. A deferred
|
|
29
|
-
* against this description, so it can be created before
|
|
30
|
-
* exists.
|
|
27
|
+
* Describes the texture a recording is replayed onto: the pixel size, the
|
|
28
|
+
* format and the color space of a window (or offscreen) surface. A deferred
|
|
29
|
+
* canvas is recorded against this description, so it can be created before
|
|
30
|
+
* the surface itself exists.
|
|
31
31
|
*/
|
|
32
32
|
struct RNSkGraphiteTargetInfo {
|
|
33
33
|
int width = 0;
|
|
34
34
|
int height = 0;
|
|
35
35
|
SkColorType colorType = kUnknown_SkColorType;
|
|
36
|
+
// Display P3 rather than sRGB (see DawnUtils::viewColorSpace). The colors
|
|
37
|
+
// of a recording are converted to the color space of its target when it is
|
|
38
|
+
// recorded, not when it is replayed.
|
|
39
|
+
bool useP3ColorSpace = false;
|
|
36
40
|
skgpu::graphite::TextureInfo textureInfo;
|
|
37
41
|
};
|
|
38
42
|
|
|
@@ -80,8 +84,8 @@ public:
|
|
|
80
84
|
/**
|
|
81
85
|
Draws an image at the origin of the target texture and presents it. Same
|
|
82
86
|
thread and return value as presentRecordings(). Used for a recording whose
|
|
83
|
-
size differs from the target's: it is replayed into a
|
|
84
|
-
|
|
87
|
+
size or color space differs from the target's: it is replayed into a
|
|
88
|
+
texture of its own first (see RNSkView::present).
|
|
85
89
|
*/
|
|
86
90
|
virtual bool presentImage(const sk_sp<SkImage> &image) = 0;
|
|
87
91
|
|
|
@@ -106,14 +110,18 @@ private:
|
|
|
106
110
|
|
|
107
111
|
/**
|
|
108
112
|
* An offscreen surface the content of a view is replayed into for a snapshot.
|
|
113
|
+
* A recording is replayed as is, so the surface takes the color space of the
|
|
114
|
+
* recordings it is meant for.
|
|
109
115
|
*/
|
|
110
116
|
class RNSkOffscreenCanvasProvider : public RNSkCanvasProvider {
|
|
111
117
|
public:
|
|
112
118
|
RNSkOffscreenCanvasProvider(
|
|
113
119
|
const std::shared_ptr<RNSkPlatformContext> &context, int width,
|
|
114
|
-
int height)
|
|
120
|
+
int height, bool useP3ColorSpace = false)
|
|
115
121
|
: _width(width), _height(height), _pd(context->getPixelDensity()),
|
|
116
|
-
|
|
122
|
+
_useP3ColorSpace(useP3ColorSpace),
|
|
123
|
+
_surface(
|
|
124
|
+
context->makeOffscreenSurface(width, height, useP3ColorSpace)) {}
|
|
117
125
|
|
|
118
126
|
virtual ~RNSkOffscreenCanvasProvider() = default;
|
|
119
127
|
|
|
@@ -175,6 +183,7 @@ public:
|
|
|
175
183
|
info->width = _width;
|
|
176
184
|
info->height = _height;
|
|
177
185
|
info->colorType = colorType;
|
|
186
|
+
info->useP3ColorSpace = _useP3ColorSpace;
|
|
178
187
|
info->textureInfo = skgpu::graphite::TextureInfos::MakeDawn(
|
|
179
188
|
skgpu::graphite::DawnTextureInfo(
|
|
180
189
|
skgpu::graphite::SampleCount::k1, skgpu::Mipmapped::kNo,
|
|
@@ -214,6 +223,7 @@ private:
|
|
|
214
223
|
int _width;
|
|
215
224
|
int _height;
|
|
216
225
|
float _pd;
|
|
226
|
+
bool _useP3ColorSpace;
|
|
217
227
|
sk_sp<SkSurface> _surface;
|
|
218
228
|
bool _cleared = false;
|
|
219
229
|
};
|
|
@@ -78,9 +78,21 @@ public:
|
|
|
78
78
|
_target.textureInfo.canBeFulfilledBy(target.textureInfo);
|
|
79
79
|
}
|
|
80
80
|
|
|
81
|
+
/**
|
|
82
|
+
Whether the recording was made in the color space of the given target. The
|
|
83
|
+
colors of a recording are converted when it is recorded: replayed onto a
|
|
84
|
+
target in another color space, they would be read in the wrong gamut. Such
|
|
85
|
+
a recording is replayed through a texture in its own color space instead,
|
|
86
|
+
which is then drawn, and converted, onto the target (see
|
|
87
|
+
RNSkView::present).
|
|
88
|
+
*/
|
|
89
|
+
bool hasColorSpaceOf(const RNSkGraphiteTargetInfo &target) const {
|
|
90
|
+
return _target.useP3ColorSpace == target.useP3ColorSpace;
|
|
91
|
+
}
|
|
92
|
+
|
|
81
93
|
/** Whether the recording can be replayed straight onto the given target. */
|
|
82
94
|
bool isCompatibleWith(const RNSkGraphiteTargetInfo &target) const {
|
|
83
|
-
return hasSizeOf(target) && hasFormatOf(target);
|
|
95
|
+
return hasSizeOf(target) && hasFormatOf(target) && hasColorSpaceOf(target);
|
|
84
96
|
}
|
|
85
97
|
|
|
86
98
|
private:
|
|
@@ -153,9 +165,9 @@ public:
|
|
|
153
165
|
}
|
|
154
166
|
_recorder = std::move(recorder);
|
|
155
167
|
}
|
|
156
|
-
auto imageInfo =
|
|
157
|
-
|
|
158
|
-
|
|
168
|
+
auto imageInfo = SkImageInfo::Make(
|
|
169
|
+
target.width, target.height, target.colorType, kPremul_SkAlphaType,
|
|
170
|
+
DawnUtils::viewColorSpace(target.useP3ColorSpace));
|
|
159
171
|
auto *canvas =
|
|
160
172
|
_recorder->recorder->makeDeferredCanvas(imageInfo, target.textureInfo);
|
|
161
173
|
if (canvas == nullptr) {
|
|
@@ -271,9 +283,9 @@ private:
|
|
|
271
283
|
/**
|
|
272
284
|
The target to record against: the view's surface when it has one, else
|
|
273
285
|
the pixel size the platform laid the view out with, else the layout size
|
|
274
|
-
JS measured; in the last two cases with the format the
|
|
275
|
-
created with, following the same rules (see
|
|
276
|
-
SkiaView.java).
|
|
286
|
+
JS measured; in the last two cases with the format and the color space
|
|
287
|
+
the surface will be created with, following the same rules (see
|
|
288
|
+
DawnWindowContext and SkiaView.java).
|
|
277
289
|
*/
|
|
278
290
|
RNSkGraphiteTargetInfo resolveTargetInfo() {
|
|
279
291
|
std::shared_ptr<RNSkCanvasProvider> provider;
|
|
@@ -313,6 +325,7 @@ private:
|
|
|
313
325
|
}
|
|
314
326
|
info.colorType = highBitDepth ? DawnUtils::HighBitDepthColorType
|
|
315
327
|
: DawnUtils::PreferedColorType;
|
|
328
|
+
info.useP3ColorSpace = _context->prefersP3ColorSpace();
|
|
316
329
|
info.textureInfo = skgpu::graphite::TextureInfos::MakeDawn(
|
|
317
330
|
skgpu::graphite::DawnTextureInfo(
|
|
318
331
|
skgpu::graphite::SampleCount::k1, skgpu::Mipmapped::kNo,
|
|
@@ -191,6 +191,14 @@ public:
|
|
|
191
191
|
*/
|
|
192
192
|
float getPixelDensity() { return _pixelDensity; }
|
|
193
193
|
|
|
194
|
+
/**
|
|
195
|
+
* Whether views render in Display P3 rather than sRGB: true on Apple
|
|
196
|
+
* platforms when the main screen has a wide color gamut. The answer does
|
|
197
|
+
* not change over the lifetime of the context, so a frame can be recorded
|
|
198
|
+
* for a view before its surface exists.
|
|
199
|
+
*/
|
|
200
|
+
virtual bool prefersP3ColorSpace() { return false; }
|
|
201
|
+
|
|
194
202
|
private:
|
|
195
203
|
float _pixelDensity;
|
|
196
204
|
std::shared_ptr<react::CallInvoker> _callInvoker;
|
package/cpp/rnskia/RNSkView.h
CHANGED
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
#pragma clang diagnostic ignored "-Wdocumentation"
|
|
23
23
|
|
|
24
24
|
#include "include/core/SkCanvas.h"
|
|
25
|
+
#include "include/core/SkColorSpace.h"
|
|
25
26
|
#include "include/core/SkImage.h"
|
|
26
27
|
#include "include/core/SkRect.h"
|
|
27
28
|
|
|
@@ -37,7 +38,8 @@ namespace jsi = facebook::jsi;
|
|
|
37
38
|
* (SkiaViewApi.makeGraphiteContext), or <Canvas> hands over a recorder (or a
|
|
38
39
|
* picture) that the render thread pool records for the view. Either way the
|
|
39
40
|
* recordings are queued on the target; the view presents them onto its canvas
|
|
40
|
-
* provider on the main thread,
|
|
41
|
+
* provider on the main thread, once the platform view's surface can show a new
|
|
42
|
+
* frame (see setFrameScheduler).
|
|
41
43
|
*
|
|
42
44
|
* Threading: the registry side (RNSkJsiViewApi) runs on the JS thread, except
|
|
43
45
|
* applyUpdates() which a Reanimated mapper calls on the UI runtime; snapshots
|
|
@@ -208,19 +210,34 @@ public:
|
|
|
208
210
|
/**
|
|
209
211
|
Renders the view into an offscreen surface: declarative content is
|
|
210
212
|
replayed on the calling thread with the latest values, so the snapshot
|
|
211
|
-
does not wait for a frame; otherwise the current frame is replayed.
|
|
213
|
+
does not wait for a frame; otherwise the current frame is replayed. A
|
|
214
|
+
snapshot is in sRGB whichever color space the view renders in: a frame
|
|
215
|
+
recorded in Display P3 is replayed into a surface in that color space and
|
|
216
|
+
the image is converted.
|
|
212
217
|
*/
|
|
213
218
|
sk_sp<SkImage> makeImageSnapshot(SkRect *bounds) {
|
|
214
|
-
auto provider = std::make_shared<RNSkOffscreenCanvasProvider>(
|
|
215
|
-
_platformContext, getScaledWidth(), getScaledHeight());
|
|
216
219
|
if (_producer->hasContent()) {
|
|
220
|
+
auto provider = std::make_shared<RNSkOffscreenCanvasProvider>(
|
|
221
|
+
_platformContext, getScaledWidth(), getScaledHeight());
|
|
217
222
|
if (auto *canvas = provider->getCanvas()) {
|
|
218
223
|
_producer->renderInto(canvas, _platformContext->getPixelDensity());
|
|
219
224
|
}
|
|
220
|
-
|
|
221
|
-
|
|
225
|
+
return provider->makeSnapshot(bounds);
|
|
226
|
+
}
|
|
227
|
+
auto frame = getLastFrame();
|
|
228
|
+
const bool useP3ColorSpace =
|
|
229
|
+
frame != nullptr && frame->getTarget().useP3ColorSpace;
|
|
230
|
+
auto provider = std::make_shared<RNSkOffscreenCanvasProvider>(
|
|
231
|
+
_platformContext, getScaledWidth(), getScaledHeight(), useP3ColorSpace);
|
|
232
|
+
if (frame != nullptr) {
|
|
233
|
+
renderFrame(provider, frame);
|
|
234
|
+
}
|
|
235
|
+
auto image = provider->makeSnapshot(bounds);
|
|
236
|
+
if (image != nullptr && useP3ColorSpace) {
|
|
237
|
+
// A raster image: converted on the CPU, no recorder involved.
|
|
238
|
+
image = image->makeColorSpace(nullptr, SkColorSpace::MakeSRGB(), {});
|
|
222
239
|
}
|
|
223
|
-
return
|
|
240
|
+
return image;
|
|
224
241
|
}
|
|
225
242
|
|
|
226
243
|
/** Width of the surface, in pixels. */
|
|
@@ -232,8 +249,10 @@ public:
|
|
|
232
249
|
// Platform view side -------------------------------------------------------
|
|
233
250
|
|
|
234
251
|
/**
|
|
235
|
-
Installed by the platform view
|
|
236
|
-
|
|
252
|
+
Installed by the platform view, which calls presentFrame() once its surface
|
|
253
|
+
can show a new frame: on the display link on iOS, straight away on macOS,
|
|
254
|
+
and on Android as its FrameScheduler decides. Main thread. Without one,
|
|
255
|
+
frames are presented as soon as the main thread gets to them.
|
|
237
256
|
*/
|
|
238
257
|
void setFrameScheduler(std::function<void()> scheduler) {
|
|
239
258
|
_frameScheduler = std::move(scheduler);
|
|
@@ -249,10 +268,10 @@ public:
|
|
|
249
268
|
}
|
|
250
269
|
|
|
251
270
|
/**
|
|
252
|
-
|
|
253
|
-
are waiting, so that the
|
|
254
|
-
present failed (the app is in the background), so that the recordings
|
|
255
|
-
tried again on the next frame rather than left waiting for a redraw.
|
|
271
|
+
Presents the queued recordings (see setFrameScheduler for when). Returns
|
|
272
|
+
whether more are waiting, so that the platform view asks again: also when
|
|
273
|
+
the present failed (the app is in the background), so that the recordings
|
|
274
|
+
are tried again on the next frame rather than left waiting for a redraw.
|
|
256
275
|
Without a surface the queue is left alone: the surface presents it when
|
|
257
276
|
it appears.
|
|
258
277
|
*/
|
|
@@ -297,11 +316,8 @@ private:
|
|
|
297
316
|
return _target;
|
|
298
317
|
}
|
|
299
318
|
|
|
300
|
-
/**
|
|
301
|
-
|
|
302
|
-
provider (a snapshot surface). Any thread.
|
|
303
|
-
*/
|
|
304
|
-
bool renderLastFrame(const std::shared_ptr<RNSkCanvasProvider> &provider) {
|
|
319
|
+
/** The frame on screen, or the one about to be, if any. Any thread. */
|
|
320
|
+
std::shared_ptr<RNSkGraphiteRecording> getLastFrame() {
|
|
305
321
|
std::shared_ptr<RNSkGraphiteTarget> target;
|
|
306
322
|
std::shared_ptr<RNSkGraphiteRecording> recording;
|
|
307
323
|
{
|
|
@@ -314,9 +330,12 @@ private:
|
|
|
314
330
|
recording = latest;
|
|
315
331
|
}
|
|
316
332
|
}
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
333
|
+
return recording;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
/** Replays a frame onto another provider (a snapshot surface). Any thread. */
|
|
337
|
+
bool renderFrame(const std::shared_ptr<RNSkCanvasProvider> &provider,
|
|
338
|
+
const std::shared_ptr<RNSkGraphiteRecording> &recording) {
|
|
320
339
|
RNSkGraphiteTargetInfo targetInfo;
|
|
321
340
|
if (!provider->getTargetInfo(&targetInfo)) {
|
|
322
341
|
return false;
|
|
@@ -335,8 +354,8 @@ private:
|
|
|
335
354
|
const RNSkGraphiteTargetInfo &targetInfo,
|
|
336
355
|
const std::vector<std::shared_ptr<RNSkGraphiteRecording>> &recordings,
|
|
337
356
|
bool remember) {
|
|
338
|
-
// Recordings of the target's size are replayed straight
|
|
339
|
-
//
|
|
357
|
+
// Recordings of the target's size and color space are replayed straight
|
|
358
|
+
// onto it, as one batch; any other goes through a texture of its own.
|
|
340
359
|
std::vector<skgpu::graphite::Recording *> batch;
|
|
341
360
|
std::shared_ptr<RNSkGraphiteRecording> last;
|
|
342
361
|
bool success = true;
|
|
@@ -355,13 +374,14 @@ private:
|
|
|
355
374
|
"different surface format");
|
|
356
375
|
continue;
|
|
357
376
|
}
|
|
358
|
-
if (recording->hasSizeOf(targetInfo)
|
|
377
|
+
if (recording->hasSizeOf(targetInfo) &&
|
|
378
|
+
recording->hasColorSpaceOf(targetInfo)) {
|
|
359
379
|
batch.push_back(recording->get());
|
|
360
380
|
last = recording;
|
|
361
381
|
continue;
|
|
362
382
|
}
|
|
363
383
|
flush();
|
|
364
|
-
if (
|
|
384
|
+
if (presentThroughTexture(provider, recording)) {
|
|
365
385
|
last = recording;
|
|
366
386
|
} else {
|
|
367
387
|
success = false;
|
|
@@ -385,13 +405,17 @@ private:
|
|
|
385
405
|
rounds the layout to pixels on its own terms), and the view may have been
|
|
386
406
|
resized since. The recording is replayed into a texture of its own size
|
|
387
407
|
and that texture is drawn onto the target, so the frame shows rather than
|
|
388
|
-
being dropped; the copy only happens while the sizes differ.
|
|
408
|
+
being dropped; the copy only happens while the sizes differ. The same goes
|
|
409
|
+
for a recording made in another color space than the target's: the
|
|
410
|
+
texture is in the color space of the recording, and drawing it converts
|
|
411
|
+
the colors.
|
|
389
412
|
*/
|
|
390
|
-
bool
|
|
391
|
-
|
|
413
|
+
bool presentThroughTexture(
|
|
414
|
+
const std::shared_ptr<RNSkCanvasProvider> &provider,
|
|
415
|
+
const std::shared_ptr<RNSkGraphiteRecording> &recording) {
|
|
392
416
|
const auto &size = recording->getTarget();
|
|
393
417
|
auto intermediate = std::make_shared<RNSkOffscreenCanvasProvider>(
|
|
394
|
-
_platformContext, size.width, size.height);
|
|
418
|
+
_platformContext, size.width, size.height, size.useP3ColorSpace);
|
|
395
419
|
RNSkGraphiteTargetInfo info;
|
|
396
420
|
if (!intermediate->getTargetInfo(&info) ||
|
|
397
421
|
!recording->isCompatibleWith(info)) {
|
|
@@ -82,7 +82,7 @@ const usePathInterpolation = (value, input, outputRange, options) => {
|
|
|
82
82
|
// Handle the case where not all paths are interpolable
|
|
83
83
|
// For example, throw an error or return early
|
|
84
84
|
throw new Error(`Not all paths in the output range are interpolable.
|
|
85
|
-
See: https://
|
|
85
|
+
See: https://wcandillon.github.io/react-native-skia/docs/animations/hooks#usepathinterpolation`);
|
|
86
86
|
}
|
|
87
87
|
return useInterpolator(() => _skia.Skia.Path.Make(), value, _animation.interpolatePaths, input, outputRange, options);
|
|
88
88
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["_react","require","_animation","_skia","_Offscreen","_ReanimatedProxy","_interopRequireDefault","e","__esModule","default","notifyChange","value","isOnMainThread","_value","exports","usePathValue","cb","init","transform","builderInit","useMemo","Skia","PathBuilder","Make","pathInit","Path","builder","Rea","useSharedValue","path","useDerivedValue","reset","undefined","addPath","result","build","useClock","clock","callback","useCallback","info","timeSinceFirstFrame","useFrameCallback","useInterpolator","factory","interpolator","input","output","options","useAnimatedReaction","val","usePathInterpolation","outputRange","allPathsInterpolable","slice","every","isInterpolatable","Error","interpolatePaths","useVectorInterpolation","Point","interpolateVector"],"sources":["interpolators.ts"],"sourcesContent":["import type {\n ExtrapolationType,\n FrameInfo,\n SharedValue,\n} from \"react-native-reanimated\";\nimport { useCallback, useMemo } from \"react\";\n\nimport type { SkPath, SkPathBuilder, SkPoint } from \"../../skia/types\";\nimport { interpolatePaths, interpolateVector } from \"../../animation\";\nimport { Skia } from \"../../skia\";\nimport { isOnMainThread } from \"../../renderer/Offscreen\";\n\nimport Rea from \"./ReanimatedProxy\";\n\nexport const notifyChange = <T>(value: SharedValue<T>) => {\n \"worklet\";\n if (isOnMainThread()) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n (value as any)._value = value.value;\n }\n};\n\n/**\n * Hook for creating animated paths using PathBuilder.\n * The callback receives a mutable PathBuilder that can be used to construct the path.\n * The resulting immutable SkPath is stored in a shared value.\n *\n * @param cb - Callback that receives the PathBuilder to construct the path\n * @param init - Optional initial path to add to the builder\n * @param transform - Optional transform function applied to the built path\n */\nexport const usePathValue = (\n cb: (builder: SkPathBuilder) => void,\n init?: SkPath,\n transform?: (path: SkPath) => SkPath\n) => {\n const builderInit = useMemo(() => Skia.PathBuilder.Make(), []);\n const pathInit = useMemo(() => Skia.Path.Make(), []);\n const builder = Rea.useSharedValue(builderInit);\n const path = Rea.useSharedValue(pathInit);\n Rea.useDerivedValue(() => {\n builder.value.reset();\n if (init !== undefined) {\n builder.value.addPath(init);\n }\n cb(builder.value);\n let result = builder.value.build();\n if (transform !== undefined) {\n result = transform(result);\n }\n path.value = result;\n notifyChange(path);\n });\n return path;\n};\n\nexport const useClock = () => {\n const clock = Rea.useSharedValue(0);\n const callback = useCallback(\n (info: FrameInfo) => {\n \"worklet\";\n clock.value = info.timeSinceFirstFrame;\n },\n [clock]\n );\n Rea.useFrameCallback(callback);\n return clock;\n};\n\n/**\n * @worklet\n */\ntype Interpolator<T> = (\n value: number,\n input: number[],\n output: T[],\n options: ExtrapolationType,\n result: T\n) => T;\n\nconst useInterpolator = <T>(\n factory: () => T,\n value: SharedValue<number>,\n interpolator: Interpolator<T>,\n input: number[],\n output: T[],\n options?: ExtrapolationType\n) => {\n // eslint-disable-next-line react-hooks/exhaustive-deps\n const init = useMemo(() => factory(), []);\n const result = Rea.useSharedValue(init);\n Rea.useAnimatedReaction(\n () => value.value,\n (val) => {\n result.value = interpolator(val, input, output, options, result.value);\n notifyChange(result);\n },\n [input, output, options]\n );\n return result;\n};\n\nexport const usePathInterpolation = (\n value: SharedValue<number>,\n input: number[],\n outputRange: SkPath[],\n options?: ExtrapolationType\n) => {\n // Check if all paths in outputRange are interpolable\n const allPathsInterpolable = outputRange\n .slice(1)\n .every((path) => outputRange[0].isInterpolatable(path));\n if (!allPathsInterpolable) {\n // Handle the case where not all paths are interpolable\n // For example, throw an error or return early\n throw new Error(\n `Not all paths in the output range are interpolable.\nSee: https://
|
|
1
|
+
{"version":3,"names":["_react","require","_animation","_skia","_Offscreen","_ReanimatedProxy","_interopRequireDefault","e","__esModule","default","notifyChange","value","isOnMainThread","_value","exports","usePathValue","cb","init","transform","builderInit","useMemo","Skia","PathBuilder","Make","pathInit","Path","builder","Rea","useSharedValue","path","useDerivedValue","reset","undefined","addPath","result","build","useClock","clock","callback","useCallback","info","timeSinceFirstFrame","useFrameCallback","useInterpolator","factory","interpolator","input","output","options","useAnimatedReaction","val","usePathInterpolation","outputRange","allPathsInterpolable","slice","every","isInterpolatable","Error","interpolatePaths","useVectorInterpolation","Point","interpolateVector"],"sources":["interpolators.ts"],"sourcesContent":["import type {\n ExtrapolationType,\n FrameInfo,\n SharedValue,\n} from \"react-native-reanimated\";\nimport { useCallback, useMemo } from \"react\";\n\nimport type { SkPath, SkPathBuilder, SkPoint } from \"../../skia/types\";\nimport { interpolatePaths, interpolateVector } from \"../../animation\";\nimport { Skia } from \"../../skia\";\nimport { isOnMainThread } from \"../../renderer/Offscreen\";\n\nimport Rea from \"./ReanimatedProxy\";\n\nexport const notifyChange = <T>(value: SharedValue<T>) => {\n \"worklet\";\n if (isOnMainThread()) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n (value as any)._value = value.value;\n }\n};\n\n/**\n * Hook for creating animated paths using PathBuilder.\n * The callback receives a mutable PathBuilder that can be used to construct the path.\n * The resulting immutable SkPath is stored in a shared value.\n *\n * @param cb - Callback that receives the PathBuilder to construct the path\n * @param init - Optional initial path to add to the builder\n * @param transform - Optional transform function applied to the built path\n */\nexport const usePathValue = (\n cb: (builder: SkPathBuilder) => void,\n init?: SkPath,\n transform?: (path: SkPath) => SkPath\n) => {\n const builderInit = useMemo(() => Skia.PathBuilder.Make(), []);\n const pathInit = useMemo(() => Skia.Path.Make(), []);\n const builder = Rea.useSharedValue(builderInit);\n const path = Rea.useSharedValue(pathInit);\n Rea.useDerivedValue(() => {\n builder.value.reset();\n if (init !== undefined) {\n builder.value.addPath(init);\n }\n cb(builder.value);\n let result = builder.value.build();\n if (transform !== undefined) {\n result = transform(result);\n }\n path.value = result;\n notifyChange(path);\n });\n return path;\n};\n\nexport const useClock = () => {\n const clock = Rea.useSharedValue(0);\n const callback = useCallback(\n (info: FrameInfo) => {\n \"worklet\";\n clock.value = info.timeSinceFirstFrame;\n },\n [clock]\n );\n Rea.useFrameCallback(callback);\n return clock;\n};\n\n/**\n * @worklet\n */\ntype Interpolator<T> = (\n value: number,\n input: number[],\n output: T[],\n options: ExtrapolationType,\n result: T\n) => T;\n\nconst useInterpolator = <T>(\n factory: () => T,\n value: SharedValue<number>,\n interpolator: Interpolator<T>,\n input: number[],\n output: T[],\n options?: ExtrapolationType\n) => {\n // eslint-disable-next-line react-hooks/exhaustive-deps\n const init = useMemo(() => factory(), []);\n const result = Rea.useSharedValue(init);\n Rea.useAnimatedReaction(\n () => value.value,\n (val) => {\n result.value = interpolator(val, input, output, options, result.value);\n notifyChange(result);\n },\n [input, output, options]\n );\n return result;\n};\n\nexport const usePathInterpolation = (\n value: SharedValue<number>,\n input: number[],\n outputRange: SkPath[],\n options?: ExtrapolationType\n) => {\n // Check if all paths in outputRange are interpolable\n const allPathsInterpolable = outputRange\n .slice(1)\n .every((path) => outputRange[0].isInterpolatable(path));\n if (!allPathsInterpolable) {\n // Handle the case where not all paths are interpolable\n // For example, throw an error or return early\n throw new Error(\n `Not all paths in the output range are interpolable.\nSee: https://wcandillon.github.io/react-native-skia/docs/animations/hooks#usepathinterpolation`\n );\n }\n return useInterpolator(\n () => Skia.Path.Make(),\n value,\n interpolatePaths,\n input,\n outputRange,\n options\n );\n};\n\nexport const useVectorInterpolation = (\n value: SharedValue<number>,\n input: number[],\n outputRange: SkPoint[],\n options?: ExtrapolationType\n) =>\n useInterpolator(\n () => Skia.Point(0, 0),\n value,\n interpolateVector,\n input,\n outputRange,\n options\n );\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -56,7 +56,7 @@ const useCanvasRoot = ({
|
|
|
56
56
|
onLayout
|
|
57
57
|
}) => {
|
|
58
58
|
if (onLayout && _Platform.Platform.OS !== "web") {
|
|
59
|
-
console.error("<Canvas onLayout={onLayout} /> is not supported on the new architecture, to fix the issue, see: https://
|
|
59
|
+
console.error("<Canvas onLayout={onLayout} /> is not supported on the new architecture, to fix the issue, see: https://wcandillon.github.io/react-native-skia/docs/canvas/overview/#canvas-size");
|
|
60
60
|
}
|
|
61
61
|
const viewRef = useCanvasRefPriv(null);
|
|
62
62
|
// Native ID
|