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.
Files changed (32) hide show
  1. package/README.md +19 -2
  2. package/android/build.gradle +15 -7
  3. package/android/src/main/java/com/reactnative/skia/BackingViewKind.java +24 -0
  4. package/android/src/main/java/com/reactnative/skia/FrameScheduler.java +104 -0
  5. package/android/src/main/java/com/reactnative/skia/SkiaView.java +55 -44
  6. package/android/src/test/java/com/reactnative/skia/FrameSchedulerTest.java +186 -0
  7. package/apple/MetalLayerColorSpace.mm +10 -8
  8. package/apple/RNSkApplePlatformContext.h +12 -0
  9. package/apple/RNSkApplePlatformContext.mm +17 -0
  10. package/apple/RNSkMetalCanvasProvider.mm +4 -2
  11. package/apple/RNSkiaModule.mm +10 -0
  12. package/cpp/rnskia/RNDawnContext.h +3 -2
  13. package/cpp/rnskia/RNDawnUtils.h +12 -0
  14. package/cpp/rnskia/RNDawnWindowContext.cpp +2 -1
  15. package/cpp/rnskia/RNDawnWindowContext.h +15 -7
  16. package/cpp/rnskia/RNMetalLayerColorSpace.h +4 -2
  17. package/cpp/rnskia/RNSkCanvasProvider.h +18 -8
  18. package/cpp/rnskia/RNSkGraphiteTarget.h +20 -7
  19. package/cpp/rnskia/RNSkPlatformContext.h +8 -0
  20. package/cpp/rnskia/RNSkView.h +53 -29
  21. package/lib/commonjs/external/reanimated/interpolators.js +1 -1
  22. package/lib/commonjs/external/reanimated/interpolators.js.map +1 -1
  23. package/lib/commonjs/renderer/Canvas.js +1 -1
  24. package/lib/commonjs/renderer/Canvas.js.map +1 -1
  25. package/lib/module/external/reanimated/interpolators.js +1 -1
  26. package/lib/module/external/reanimated/interpolators.js.map +1 -1
  27. package/lib/module/renderer/Canvas.js +1 -1
  28. package/lib/module/renderer/Canvas.js.map +1 -1
  29. package/package.json +1 -1
  30. package/react-native-skia.podspec +1 -1
  31. package/src/external/reanimated/interpolators.ts +1 -1
  32. 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((__bridge void *)_layer,
98
- w, h, _highBitDepth);
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
  }
@@ -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() {
@@ -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, SkColorSpace::MakeSRGB(), &surfaceProps);
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), _width(width), _height(height) {
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, colorSpace, &surfaceProps);
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
- // Float formats need the layer tagged as (gamma-encoded) extended sRGB so
118
- // the sRGB-encoded values display identically to the 8-bit path.
119
- applyCAMetalLayerColorSpace(_nativeSurface, _format);
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 apple/MetalLayerColorSpace.mm.
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 and the
28
- * format of a window (or offscreen) surface. A deferred canvas is recorded
29
- * against this description, so it can be created before the surface itself
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 texture of its own
84
- size first (see RNSkView::present).
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
- _surface(context->makeOffscreenSurface(width, height)) {}
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
- SkImageInfo::Make(target.width, target.height, target.colorType,
158
- kPremul_SkAlphaType, SkColorSpace::MakeSRGB());
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 surface will be
275
- created with, following the same rules (see DawnWindowContext and
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;
@@ -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, aligned with the platform's display link.
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
- } else {
221
- renderLastFrame(provider);
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 provider->makeSnapshot(bounds);
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: arms its display link. Main thread. Without
236
- one, frames are presented as soon as the main thread gets to them.
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
- Display link tick: presents the queued recordings. Returns whether more
253
- are waiting, so that the caller keeps its display link armed: also when the
254
- present failed (the app is in the background), so that the recordings are
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
- Replays the frame on screen, or the one about to be, onto another
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
- if (recording == nullptr) {
318
- return false;
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 onto it, as one
339
- // batch; one of another size goes through a texture of its own size.
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 (presentResized(provider, recording)) {
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 presentResized(const std::shared_ptr<RNSkCanvasProvider> &provider,
391
- const std::shared_ptr<RNSkGraphiteRecording> &recording) {
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://shopify.github.io/react-native-skia/docs/animations/hooks#usepathinterpolation`);
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://shopify.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":[]}
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://shopify.github.io/react-native-skia/docs/canvas/overview/#getting-the-canvas-size");
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