@shopify/react-native-skia 2.2.14 → 2.2.16

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 (92) hide show
  1. package/apple/MetalContext.h +3 -25
  2. package/apple/MetalContext.mm +7 -3
  3. package/apple/MetalWindowContext.mm +11 -1
  4. package/apple/RNSkMetalCanvasProvider.mm +3 -6
  5. package/cpp/api/JsiSkImage.h +16 -1
  6. package/cpp/api/JsiSkSurface.h +17 -1
  7. package/cpp/api/JsiSkThreadSafeDeletion.h +105 -0
  8. package/cpp/api/recorder/Drawings.h +7 -7
  9. package/cpp/jsi/JsiHostObject.cpp +0 -27
  10. package/cpp/jsi/JsiHostObject.h +5 -5
  11. package/cpp/jsi/ViewProperty.h +34 -2
  12. package/cpp/rnskia/RNSkJsiViewApi.h +14 -24
  13. package/cpp/rnskia/RNSkPictureView.h +25 -2
  14. package/cpp/rnskia/RNSkView.h +7 -0
  15. package/lib/commonjs/Platform/Platform.js +2 -1
  16. package/lib/commonjs/Platform/Platform.js.map +1 -1
  17. package/lib/commonjs/Platform/Platform.web.js +3 -0
  18. package/lib/commonjs/Platform/Platform.web.js.map +1 -1
  19. package/lib/commonjs/__tests__/globalSetup.d.ts +1 -1
  20. package/lib/commonjs/renderer/Canvas.d.ts +1 -1
  21. package/lib/commonjs/renderer/Canvas.js +24 -1
  22. package/lib/commonjs/renderer/Canvas.js.map +1 -1
  23. package/lib/commonjs/renderer/__tests__/setup.d.ts +1 -1
  24. package/lib/commonjs/skia/core/SVG.web.js +1 -1
  25. package/lib/commonjs/skia/core/SVG.web.js.map +1 -1
  26. package/lib/commonjs/skia/types/Data/Data.d.ts +6 -1
  27. package/lib/commonjs/skia/types/Data/Data.js.map +1 -1
  28. package/lib/commonjs/sksg/Container.d.ts +2 -3
  29. package/lib/commonjs/sksg/Container.js +1 -1
  30. package/lib/commonjs/sksg/Container.js.map +1 -1
  31. package/lib/commonjs/sksg/Container.native.d.ts +3 -5
  32. package/lib/commonjs/sksg/Container.native.js +8 -15
  33. package/lib/commonjs/sksg/Container.native.js.map +1 -1
  34. package/lib/commonjs/sksg/Container.web.d.ts +2 -3
  35. package/lib/commonjs/sksg/Container.web.js +1 -1
  36. package/lib/commonjs/sksg/Container.web.js.map +1 -1
  37. package/lib/commonjs/sksg/Reconciler.d.ts +2 -3
  38. package/lib/commonjs/sksg/Reconciler.js +2 -2
  39. package/lib/commonjs/sksg/Reconciler.js.map +1 -1
  40. package/lib/module/Platform/Platform.js +2 -1
  41. package/lib/module/Platform/Platform.js.map +1 -1
  42. package/lib/module/Platform/Platform.web.js +3 -0
  43. package/lib/module/Platform/Platform.web.js.map +1 -1
  44. package/lib/module/__tests__/globalSetup.d.ts +1 -1
  45. package/lib/module/renderer/Canvas.d.ts +1 -1
  46. package/lib/module/renderer/Canvas.js +24 -1
  47. package/lib/module/renderer/Canvas.js.map +1 -1
  48. package/lib/module/renderer/__tests__/setup.d.ts +1 -1
  49. package/lib/module/skia/core/SVG.web.js +1 -1
  50. package/lib/module/skia/core/SVG.web.js.map +1 -1
  51. package/lib/module/skia/types/Data/Data.d.ts +6 -1
  52. package/lib/module/skia/types/Data/Data.js.map +1 -1
  53. package/lib/module/sksg/Container.d.ts +2 -3
  54. package/lib/module/sksg/Container.js +1 -1
  55. package/lib/module/sksg/Container.js.map +1 -1
  56. package/lib/module/sksg/Container.native.d.ts +3 -5
  57. package/lib/module/sksg/Container.native.js +8 -15
  58. package/lib/module/sksg/Container.native.js.map +1 -1
  59. package/lib/module/sksg/Container.web.d.ts +2 -3
  60. package/lib/module/sksg/Container.web.js +1 -1
  61. package/lib/module/sksg/Container.web.js.map +1 -1
  62. package/lib/module/sksg/Reconciler.d.ts +2 -3
  63. package/lib/module/sksg/Reconciler.js +2 -2
  64. package/lib/module/sksg/Reconciler.js.map +1 -1
  65. package/lib/typescript/lib/commonjs/sksg/Container.d.ts +1 -1
  66. package/lib/typescript/lib/commonjs/sksg/Container.native.d.ts +2 -3
  67. package/lib/typescript/lib/commonjs/sksg/Container.web.d.ts +1 -1
  68. package/lib/typescript/lib/commonjs/sksg/Reconciler.d.ts +1 -1
  69. package/lib/typescript/lib/module/sksg/Container.d.ts +1 -1
  70. package/lib/typescript/lib/module/sksg/Container.native.d.ts +2 -3
  71. package/lib/typescript/lib/module/sksg/Container.web.d.ts +1 -1
  72. package/lib/typescript/lib/module/sksg/Reconciler.d.ts +1 -1
  73. package/lib/typescript/src/__tests__/globalSetup.d.ts +1 -1
  74. package/lib/typescript/src/renderer/Canvas.d.ts +1 -1
  75. package/lib/typescript/src/renderer/__tests__/setup.d.ts +1 -1
  76. package/lib/typescript/src/skia/types/Data/Data.d.ts +6 -1
  77. package/lib/typescript/src/sksg/Container.d.ts +2 -3
  78. package/lib/typescript/src/sksg/Container.native.d.ts +3 -5
  79. package/lib/typescript/src/sksg/Container.web.d.ts +2 -3
  80. package/lib/typescript/src/sksg/Reconciler.d.ts +2 -3
  81. package/package.json +1 -1
  82. package/src/Platform/Platform.ts +3 -0
  83. package/src/Platform/Platform.web.tsx +3 -0
  84. package/src/__tests__/globalSetup.ts +5 -3
  85. package/src/renderer/Canvas.tsx +27 -5
  86. package/src/renderer/__tests__/setup.tsx +1 -1
  87. package/src/skia/core/SVG.web.ts +3 -0
  88. package/src/skia/types/Data/Data.ts +7 -1
  89. package/src/sksg/Container.native.ts +10 -27
  90. package/src/sksg/Container.ts +2 -8
  91. package/src/sksg/Container.web.ts +2 -8
  92. package/src/sksg/Reconciler.ts +3 -4
@@ -14,29 +14,6 @@
14
14
  #import <include/gpu/ganesh/mtl/GrMtlDirectContext.h>
15
15
  #import <include/gpu/ganesh/mtl/SkSurfaceMetal.h>
16
16
 
17
- class MetalSharedContext {
18
- public:
19
- static MetalSharedContext &getInstance() {
20
- static MetalSharedContext instance;
21
- return instance;
22
- }
23
-
24
- id<MTLDevice> getDevice() { return _device; }
25
-
26
- private:
27
- MetalSharedContext() {
28
- _device = MTLCreateSystemDefaultDevice();
29
- if (!_device) {
30
- throw std::runtime_error("Failed to create Metal device");
31
- }
32
- }
33
-
34
- MetalSharedContext(const MetalSharedContext &) = delete;
35
- MetalSharedContext &operator=(const MetalSharedContext &) = delete;
36
-
37
- id<MTLDevice> _device;
38
- };
39
-
40
17
  struct OffscreenRenderContext {
41
18
  id<MTLTexture> texture;
42
19
 
@@ -68,7 +45,7 @@ public:
68
45
  }
69
46
 
70
47
  sk_sp<SkSurface> MakeOffscreen(int width, int height) {
71
- auto device = MetalSharedContext::getInstance().getDevice();
48
+ auto device = _device;
72
49
  auto ctx = new OffscreenRenderContext(device, _directContext, _commandQueue,
73
50
  width, height);
74
51
 
@@ -115,7 +92,7 @@ public:
115
92
  std::unique_ptr<RNSkia::WindowContext>
116
93
  MakeWindow(CALayer *window, int width, int height,
117
94
  bool useP3ColorSpace = true) {
118
- auto device = MetalSharedContext::getInstance().getDevice();
95
+ auto device = _device;
119
96
  return std::make_unique<MetalWindowContext>(_directContext.get(), device,
120
97
  _commandQueue, window, width,
121
98
  height, useP3ColorSpace);
@@ -124,6 +101,7 @@ public:
124
101
  GrDirectContext *getDirectContext() { return _directContext.get(); }
125
102
 
126
103
  private:
104
+ id<MTLDevice> _device = nullptr;
127
105
  id<MTLCommandQueue> _commandQueue = nullptr;
128
106
  sk_sp<GrDirectContext> _directContext = nullptr;
129
107
 
@@ -18,11 +18,15 @@
18
18
  #pragma clang diagnostic pop
19
19
 
20
20
  MetalContext::MetalContext() {
21
- auto device = MetalSharedContext::getInstance().getDevice();
21
+ _device = MTLCreateSystemDefaultDevice();
22
+ if (!_device) {
23
+ throw std::runtime_error("Failed to create Metal device");
24
+ }
25
+
22
26
  _commandQueue =
23
- id<MTLCommandQueue>(CFRetain((GrMTLHandle)[device newCommandQueue]));
27
+ id<MTLCommandQueue>(CFRetain((GrMTLHandle)[_device newCommandQueue]));
24
28
  GrMtlBackendContext backendContext = {};
25
- backendContext.fDevice.reset((__bridge void *)device);
29
+ backendContext.fDevice.reset((__bridge void *)_device);
26
30
  backendContext.fQueue.reset((__bridge void *)_commandQueue);
27
31
  GrContextOptions grContextOptions; // set different options here.
28
32
 
@@ -26,10 +26,20 @@ MetalWindowContext::MetalWindowContext(GrDirectContext *directContext,
26
26
  _layer.drawableSize = CGSizeMake(width, height);
27
27
  BOOL supportsWideColor = NO;
28
28
  if (useP3ColorSpace) {
29
+ #if !TARGET_OS_OSX
29
30
  if (@available(iOS 10.0, *)) {
30
31
  supportsWideColor = [UIScreen mainScreen].traitCollection.displayGamut ==
31
32
  UIDisplayGamutP3;
32
33
  }
34
+ #else
35
+ if (@available(macOS 10.12, *)) {
36
+ NSScreen *screen = [NSScreen mainScreen];
37
+ NSColorSpace *displayP3 = [NSColorSpace displayP3ColorSpace];
38
+ if (screen.colorSpace && displayP3) {
39
+ supportsWideColor = [screen.colorSpace isEqual:displayP3];
40
+ }
41
+ }
42
+ #endif // !TARGET_OS_OSX
33
43
  }
34
44
  if (supportsWideColor) {
35
45
  CGColorSpaceRef colorSpace =
@@ -75,4 +85,4 @@ void MetalWindowContext::present() {
75
85
  [commandBuffer presentDrawable:_currentDrawable];
76
86
  [commandBuffer commit];
77
87
  _skSurface = nullptr;
78
- }
88
+ }
@@ -77,11 +77,7 @@ bool RNSkMetalCanvasProvider::renderToCanvas(
77
77
  // we try to render while in the background. (see above issue)
78
78
  return false;
79
79
  }
80
- }
81
- // Wrap in auto release pool since we want the system to clean up after
82
- // rendering and not wait until later - we've seen some example of memory
83
- // usage growing very fast in the simulator without this.
84
- @autoreleasepool {
80
+
85
81
  auto surface = _ctx->getSurface();
86
82
  if (!surface) {
87
83
  return false;
@@ -89,8 +85,9 @@ bool RNSkMetalCanvasProvider::renderToCanvas(
89
85
  auto canvas = surface->getCanvas();
90
86
  cb(canvas);
91
87
  _ctx->present();
88
+ return true;
92
89
  }
93
- return true;
90
+ return false;
94
91
  };
95
92
 
96
93
  void RNSkMetalCanvasProvider::setSize(int width, int height) {
@@ -8,6 +8,7 @@
8
8
  #include "JsiSkImageInfo.h"
9
9
  #include "JsiSkMatrix.h"
10
10
  #include "JsiSkShader.h"
11
+ #include "JsiSkThreadSafeDeletion.h"
11
12
  #include "third_party/base64.h"
12
13
 
13
14
  #include "JsiTextureInfo.h"
@@ -61,6 +62,9 @@ inline SkSamplingOptions SamplingOptionsFromValue(jsi::Runtime &runtime,
61
62
  }
62
63
 
63
64
  class JsiSkImage : public JsiSkWrappingSkPtrHostObject<SkImage> {
65
+ private:
66
+ ThreadSafeDeletion<SkImage> _deletionHandler;
67
+
64
68
  public:
65
69
  // TODO-API: Properties?
66
70
  JSI_HOST_FUNCTION(width) { return static_cast<double>(getObject()->width()); }
@@ -271,7 +275,18 @@ public:
271
275
  JsiSkImage(std::shared_ptr<RNSkPlatformContext> context,
272
276
  const sk_sp<SkImage> image)
273
277
  : JsiSkWrappingSkPtrHostObject<SkImage>(std::move(context),
274
- std::move(image)) {}
278
+ std::move(image)) {
279
+ // Drain any pending deletions when creating new images
280
+ ThreadSafeDeletion<SkImage>::drainDeletionQueue();
281
+ }
282
+
283
+ ~JsiSkImage() override {
284
+ // Handle thread-safe deletion
285
+ _deletionHandler.handleDeletion(getObject());
286
+ // Always clear the object to prevent base class destructor from deleting it
287
+ // handleDeletion takes full responsibility for the object's lifetime
288
+ setObject(nullptr);
289
+ }
275
290
 
276
291
  size_t getMemoryPressure() const override {
277
292
  auto image = getObject();
@@ -6,6 +6,7 @@
6
6
  #include <jsi/jsi.h>
7
7
 
8
8
  #include "JsiSkHostObjects.h"
9
+ #include "JsiSkThreadSafeDeletion.h"
9
10
  #include "JsiTextureInfo.h"
10
11
 
11
12
  #include "JsiSkCanvas.h"
@@ -28,11 +29,26 @@ namespace RNSkia {
28
29
  namespace jsi = facebook::jsi;
29
30
 
30
31
  class JsiSkSurface : public JsiSkWrappingSkPtrHostObject<SkSurface> {
32
+ private:
33
+ ThreadSafeDeletion<SkSurface> _deletionHandler;
34
+
31
35
  public:
32
36
  JsiSkSurface(std::shared_ptr<RNSkPlatformContext> context,
33
37
  sk_sp<SkSurface> surface)
34
38
  : JsiSkWrappingSkPtrHostObject<SkSurface>(std::move(context),
35
- std::move(surface)) {}
39
+ std::move(surface)),
40
+ _deletionHandler() {
41
+ // Drain any pending deletions when creating new surfaces
42
+ ThreadSafeDeletion<SkSurface>::drainDeletionQueue();
43
+ }
44
+
45
+ ~JsiSkSurface() override {
46
+ // Handle thread-safe deletion
47
+ _deletionHandler.handleDeletion(getObject());
48
+ // Always clear the object to prevent base class destructor from deleting it
49
+ // handleDeletion takes full responsibility for the object's lifetime
50
+ setObject(nullptr);
51
+ }
36
52
 
37
53
  EXPORT_JSI_API_TYPENAME(JsiSkSurface, Surface)
38
54
 
@@ -0,0 +1,105 @@
1
+ #pragma once
2
+
3
+ #include <functional>
4
+ #include <memory>
5
+ #include <mutex>
6
+ #include <queue>
7
+ #include <thread>
8
+
9
+ // Define this to disable thread-safe deletion (for testing purposes)
10
+ // #define DISABLE_THREAD_SAFE_DELETION
11
+
12
+ namespace RNSkia {
13
+
14
+ /**
15
+ * Utility class for managing thread-safe deletion of Skia objects.
16
+ * Ensures objects are deleted on the thread that created them.
17
+ */
18
+ template <typename T> class ThreadSafeDeletion {
19
+ private:
20
+ struct DeletionItem {
21
+ sk_sp<T> object;
22
+ std::thread::id creationThreadId;
23
+ };
24
+
25
+ static inline std::mutex _queueMutex;
26
+ static inline std::queue<DeletionItem> _deletionQueue;
27
+
28
+ std::thread::id _creationThreadId;
29
+
30
+ public:
31
+ ThreadSafeDeletion() : _creationThreadId(std::this_thread::get_id()) {}
32
+
33
+ /**
34
+ * Handles deletion of the object. If we're on the wrong thread,
35
+ * queues it for later deletion. Otherwise, allows immediate deletion.
36
+ * The object is always considered handled after this call.
37
+ */
38
+ void handleDeletion(sk_sp<T> object) {
39
+ if (!object) {
40
+ return;
41
+ }
42
+
43
+ #ifdef DISABLE_THREAD_SAFE_DELETION
44
+ // When disabled, allow immediate deletion
45
+ // This will likely cause crashes when objects are deleted on wrong thread
46
+ return;
47
+ #else
48
+ // Always try to drain the queue when handling deletions
49
+ drainDeletionQueue();
50
+
51
+ // Check if we're on the creation thread
52
+ if (std::this_thread::get_id() != _creationThreadId) {
53
+ // Queue for deletion on the correct thread
54
+ std::lock_guard<std::mutex> lock(_queueMutex);
55
+ _deletionQueue.push({object, _creationThreadId});
56
+ return;
57
+ }
58
+
59
+ // We're on the correct thread, allow immediate deletion via destructor
60
+ return;
61
+ #endif
62
+ }
63
+
64
+ /**
65
+ * Drains the deletion queue for objects created on the current thread.
66
+ * Should be called periodically (e.g., when creating new objects).
67
+ */
68
+ static void drainDeletionQueue() {
69
+ #ifndef DISABLE_THREAD_SAFE_DELETION
70
+ auto currentThreadId = std::this_thread::get_id();
71
+ std::queue<DeletionItem> remainingItems;
72
+
73
+ std::lock_guard<std::mutex> lock(_queueMutex);
74
+
75
+ // Process all items in the queue
76
+ while (!_deletionQueue.empty()) {
77
+ auto item = _deletionQueue.front();
78
+ _deletionQueue.pop();
79
+
80
+ // If this item belongs to the current thread, let it be deleted
81
+ if (item.creationThreadId == currentThreadId) {
82
+ // The sk_sp destructor will handle the cleanup
83
+ // Just let it go out of scope
84
+ } else {
85
+ // Keep items that belong to other threads
86
+ remainingItems.push(item);
87
+ }
88
+ }
89
+
90
+ // Put back items that couldn't be deleted
91
+ _deletionQueue = std::move(remainingItems);
92
+ #endif
93
+ }
94
+
95
+ /**
96
+ * Returns the number of items waiting for deletion.
97
+ * Useful for debugging and testing.
98
+ */
99
+ static size_t getPendingDeletionCount() {
100
+ std::lock_guard<std::mutex> lock(_queueMutex);
101
+ return _deletionQueue.size();
102
+ }
103
+ };
104
+
105
+ } // namespace RNSkia
@@ -385,17 +385,17 @@ public:
385
385
  for (size_t i = 0; i < shadowCount; i++) {
386
386
  auto shadowObj =
387
387
  shadowsArray.getValueAtIndex(runtime, i).asObject(runtime);
388
-
388
+
389
389
  // Create shadow directly in vector to avoid copy
390
390
  shadows.emplace_back();
391
391
  BoxShadowCmdProps &shadow = shadows.back();
392
392
 
393
- convertProperty(runtime, shadowObj, "dx", shadow.dx, variables);
394
- convertProperty(runtime, shadowObj, "dy", shadow.dy, variables);
395
- convertProperty(runtime, shadowObj, "spread", shadow.spread, variables);
396
- convertProperty(runtime, shadowObj, "blur", shadow.blur, variables);
397
- convertProperty(runtime, shadowObj, "color", shadow.color, variables);
398
- convertProperty(runtime, shadowObj, "inner", shadow.inner, variables);
393
+ convertProperty(runtime, shadowObj, "dx", shadow.dx, variables);
394
+ convertProperty(runtime, shadowObj, "dy", shadow.dy, variables);
395
+ convertProperty(runtime, shadowObj, "spread", shadow.spread, variables);
396
+ convertProperty(runtime, shadowObj, "blur", shadow.blur, variables);
397
+ convertProperty(runtime, shadowObj, "color", shadow.color, variables);
398
+ convertProperty(runtime, shadowObj, "inner", shadow.inner, variables);
399
399
  }
400
400
  }
401
401
 
@@ -2,35 +2,8 @@
2
2
  #include <functional>
3
3
  #include <vector>
4
4
 
5
- // To be able to find objects that aren't cleaned up correctly,
6
- // we can set this value to 1 and debug the constructor/destructor
7
- #define JSI_DEBUG_ALLOCATIONS 0
8
-
9
5
  namespace RNJsi {
10
6
 
11
- #if JSI_DEBUG_ALLOCATIONS
12
- int objCounter = 0;
13
- std::vector<JsiHostObject *> objects;
14
- #endif
15
-
16
- JsiHostObject::JsiHostObject() {
17
- #if JSI_DEBUG_ALLOCATIONS
18
- objects.push_back(this);
19
- objCounter++;
20
- #endif
21
- }
22
- JsiHostObject::~JsiHostObject() {
23
- #if JSI_DEBUG_ALLOCATIONS
24
- for (size_t i = 0; i < objects.size(); ++i) {
25
- if (objects.at(i) == this) {
26
- objects.erase(objects.begin() + i);
27
- break;
28
- }
29
- }
30
- objCounter--;
31
- #endif
32
- }
33
-
34
7
  void JsiHostObject::set(jsi::Runtime &rt, const jsi::PropNameID &name,
35
8
  const jsi::Value &value) {
36
9
 
@@ -47,7 +47,7 @@
47
47
  * Creates a JSI export function declaration
48
48
  */
49
49
  #define JSI_EXPORT_FUNC(CLASS, FUNCTION) \
50
- {#FUNCTION, (jsi::Value (JsiHostObject::*)( \
50
+ {#FUNCTION, (jsi::Value(JsiHostObject::*)( \
51
51
  jsi::Runtime & runtime, const jsi::Value &thisValue, \
52
52
  const jsi::Value *arguments, size_t)) & \
53
53
  CLASS::FUNCTION}
@@ -65,7 +65,7 @@
65
65
  * Creates a JSI export getter declaration
66
66
  */
67
67
  #define JSI_EXPORT_PROP_GET(CLASS, FUNCTION) \
68
- {#FUNCTION, (jsi::Value (JsiHostObject::*)(jsi::Runtime & runtime)) & \
68
+ {#FUNCTION, (jsi::Value(JsiHostObject::*)(jsi::Runtime & runtime)) & \
69
69
  CLASS::STR_CAT(STR_GET, FUNCTION)}
70
70
 
71
71
  /**
@@ -83,7 +83,7 @@
83
83
  */
84
84
  #define JSI_EXPORT_PROP_SET(CLASS, FUNCTION) \
85
85
  {#FUNCTION, \
86
- (void (JsiHostObject::*)(jsi::Runtime & runtime, const jsi::Value &)) & \
86
+ (void(JsiHostObject::*)(jsi::Runtime & runtime, const jsi::Value &)) & \
87
87
  CLASS::STR_CAT(STR_SET, FUNCTION)}
88
88
 
89
89
  /**
@@ -125,8 +125,8 @@ using JsiPropertySettersMap =
125
125
  */
126
126
  class JsiHostObject : public jsi::HostObject {
127
127
  public:
128
- JsiHostObject();
129
- ~JsiHostObject();
128
+ JsiHostObject() = default;
129
+ ~JsiHostObject() = default;
130
130
 
131
131
  /**
132
132
  * Overridden jsi::HostObject set property method
@@ -1,5 +1,6 @@
1
1
  #pragma once
2
2
 
3
+ #include <functional>
3
4
  #include <jsi/jsi.h>
4
5
  #include <memory>
5
6
  #include <string>
@@ -26,11 +27,42 @@ public:
26
27
  }
27
28
  }
28
29
 
29
- bool isNull() { return std::holds_alternative<nullptr_t>(_value); }
30
+ template <typename PlatformContext>
31
+ ViewProperty(jsi::Runtime &runtime, const jsi::Value &value,
32
+ PlatformContext platformContext, size_t nativeId) {
33
+ // Set the onSize callback with all the necessary context
34
+ _value = std::function<void(int, int)>(
35
+ [&runtime, platformContext, nativeId](int width, int height) {
36
+ jsi::Object size(runtime);
37
+ auto pd = platformContext->getPixelDensity();
38
+ size.setProperty(runtime, "width", jsi::Value(width / pd));
39
+ size.setProperty(runtime, "height", jsi::Value(height / pd));
40
+
41
+ // Get the stored shared value from global
42
+ std::string globalKey =
43
+ "__onSize_" + std::to_string(static_cast<int>(nativeId));
44
+ auto globalProp =
45
+ runtime.global().getProperty(runtime, globalKey.c_str());
46
+ if (!globalProp.isUndefined()) {
47
+ globalProp.asObject(runtime).setProperty(runtime, "value", size);
48
+ }
49
+ });
50
+ }
51
+
52
+ bool isNull() { return std::holds_alternative<std::nullptr_t>(_value); }
30
53
 
31
54
  sk_sp<SkPicture> getPicture() { return std::get<sk_sp<SkPicture>>(_value); }
32
55
 
56
+ std::variant<std::nullptr_t, std::function<void(int, int)>>
57
+ getOnSize() const {
58
+ if (std::holds_alternative<std::function<void(int, int)>>(_value)) {
59
+ return std::get<std::function<void(int, int)>>(_value);
60
+ }
61
+ return nullptr;
62
+ }
63
+
33
64
  private:
34
- std::variant<nullptr_t, sk_sp<SkPicture>> _value = nullptr;
65
+ std::variant<std::nullptr_t, sk_sp<SkPicture>, std::function<void(int, int)>>
66
+ _value = nullptr;
35
67
  };
36
68
  } // namespace RNJsi
@@ -10,6 +10,7 @@
10
10
  #include <vector>
11
11
 
12
12
  #include "JsiHostObject.h"
13
+ #include "RNSkPictureView.h"
13
14
  #include "RNSkPlatformContext.h"
14
15
  #include "RNSkView.h"
15
16
  #include "ViewProperty.h"
@@ -102,31 +103,20 @@ public:
102
103
  ViewRegistry::getInstance().withViewInfo(
103
104
  nativeId, [&](std::shared_ptr<RNSkViewInfo> info) {
104
105
  auto name = arguments[1].asString(runtime).utf8(runtime);
105
- if (name == "onSize" && isSharedValue(runtime, arguments[2])) {
106
- jsi::Object size(runtime);
107
- auto pd = _platformContext->getPixelDensity();
108
- auto w = info->view != nullptr
109
- ? std::max(info->view->getScaledWidth(), 0)
110
- : 0;
111
- auto h = info->view != nullptr
112
- ? std::max(info->view->getScaledHeight(), 0)
113
- : 0;
114
-
115
- size.setProperty(runtime, "width", w / pd);
116
- size.setProperty(runtime, "height", h / pd);
117
- arguments[2].asObject(runtime).setProperty(runtime, "value", size);
118
- } else {
106
+ info->props.insert_or_assign(
107
+ arguments[1].asString(runtime).utf8(runtime),
108
+ RNJsi::ViewProperty(runtime, arguments[2]));
109
+ if (info->props.find("onSize") == info->props.end()) {
119
110
  info->props.insert_or_assign(
120
- arguments[1].asString(runtime).utf8(runtime),
121
- RNJsi::ViewProperty(runtime, arguments[2]));
122
-
123
- // Now let's see if we have a view that we can update
124
- if (info->view != nullptr) {
125
- // Update view!
126
- info->view->setNativeId(nativeId);
127
- info->view->setJsiProperties(info->props);
128
- info->props.clear();
129
- }
111
+ "onSize", RNJsi::ViewProperty(runtime, arguments[2],
112
+ _platformContext, nativeId));
113
+ }
114
+ // Now let's see if we have a view that we can update
115
+ if (info->view != nullptr) {
116
+ // Update view!
117
+ info->view->setNativeId(nativeId);
118
+ info->view->setJsiProperties(info->props);
119
+ info->props.clear();
130
120
  }
131
121
  return nullptr; // Return type for template deduction
132
122
  });
@@ -5,6 +5,7 @@
5
5
  #include <mutex>
6
6
  #include <string>
7
7
  #include <unordered_map>
8
+ #include <variant>
8
9
  #include <vector>
9
10
 
10
11
  #include <jsi/jsi.h>
@@ -55,8 +56,25 @@ public:
55
56
  _requestRedraw();
56
57
  }
57
58
 
59
+ void setOnSize(
60
+ std::variant<std::nullptr_t, std::function<void(int, int)>> onSize) {
61
+ _onSize = onSize;
62
+ }
63
+
58
64
  private:
59
65
  bool performDraw(std::shared_ptr<RNSkCanvasProvider> canvasProvider) {
66
+ // Call onSize callback only if the size has changed
67
+ int currentWidth = canvasProvider->getWidth();
68
+ int currentHeight = canvasProvider->getHeight();
69
+
70
+ if (std::holds_alternative<std::function<void(int, int)>>(_onSize)) {
71
+ if (_lastWidth != currentWidth || _lastHeight != currentHeight) {
72
+ _lastWidth = currentWidth;
73
+ _lastHeight = currentHeight;
74
+ std::get<std::function<void(int, int)>>(_onSize)(currentWidth,
75
+ currentHeight);
76
+ }
77
+ }
60
78
  return canvasProvider->renderToCanvas([=, this](SkCanvas *canvas) {
61
79
  // Make sure to scale correctly
62
80
  auto pd = _platformContext->getPixelDensity();
@@ -72,6 +90,9 @@ private:
72
90
 
73
91
  std::shared_ptr<RNSkPlatformContext> _platformContext;
74
92
  sk_sp<SkPicture> _picture;
93
+ std::variant<std::nullptr_t, std::function<void(int, int)>> _onSize = nullptr;
94
+ int _lastWidth = -1;
95
+ int _lastHeight = -1;
75
96
  };
76
97
 
77
98
  class RNSkPictureView : public RNSkView {
@@ -88,7 +109,7 @@ public:
88
109
 
89
110
  void setJsiProperties(
90
111
  std::unordered_map<std::string, RNJsi::ViewProperty> &props) override {
91
-
112
+ // Base implementation - no onSize callback
92
113
  for (auto &prop : props) {
93
114
  if (prop.first == "picture") {
94
115
  if (prop.second.isNull()) {
@@ -97,10 +118,12 @@ public:
97
118
  ->setPicture(nullptr);
98
119
  continue;
99
120
  }
100
-
101
121
  // Save picture
102
122
  std::static_pointer_cast<RNSkPictureRenderer>(getRenderer())
103
123
  ->setPicture(prop.second.getPicture());
124
+ } else if (prop.first == "onSize") {
125
+ std::static_pointer_cast<RNSkPictureRenderer>(getRenderer())
126
+ ->setOnSize(prop.second.getOnSize());
104
127
  }
105
128
  }
106
129
  }
@@ -148,6 +148,13 @@ public:
148
148
  virtual void setJsiProperties(
149
149
  std::unordered_map<std::string, RNJsi::ViewProperty> &props) = 0;
150
150
 
151
+ virtual void
152
+ setJsiProperties(std::unordered_map<std::string, RNJsi::ViewProperty> &props,
153
+ std::function<jsi::Object(int, int)> onSize) {
154
+ // Default implementation just calls the base method, ignoring onSize
155
+ setJsiProperties(props);
156
+ }
157
+
151
158
  void requestRedraw() {
152
159
  if (!_redrawRequested) {
153
160
  _redrawRequested = true;
@@ -10,7 +10,8 @@ const Platform = exports.Platform = {
10
10
  OS: _reactNative.Platform.OS,
11
11
  PixelRatio: _reactNative.PixelRatio.get(),
12
12
  resolveAsset: source => {
13
- return (0, _types.isRNModule)(source) ? _reactNative.Image.resolveAssetSource(source).uri : source.default;
13
+ // eslint-disable-next-line no-nested-ternary
14
+ return (0, _types.isRNModule)(source) ? _reactNative.Image.resolveAssetSource(source).uri : "uri" in source ? source.uri : source.default;
14
15
  },
15
16
  findNodeHandle: _reactNative.findNodeHandle,
16
17
  View: _reactNative.View
@@ -1 +1 @@
1
- {"version":3,"names":["_reactNative","require","_types","Platform","exports","OS","RNPlatform","PixelRatio","get","resolveAsset","source","isRNModule","Image","resolveAssetSource","uri","default","findNodeHandle","View"],"sources":["Platform.ts"],"sourcesContent":["import {\n Image,\n PixelRatio,\n Platform as RNPlatform,\n findNodeHandle,\n View,\n} from \"react-native\";\n\nimport type { DataModule } from \"../skia/types\";\nimport { isRNModule } from \"../skia/types\";\n\nimport type { IPlatform } from \"./IPlatform\";\n\nexport const Platform: IPlatform = {\n OS: RNPlatform.OS,\n PixelRatio: PixelRatio.get(),\n resolveAsset: (source: DataModule) => {\n return isRNModule(source)\n ? Image.resolveAssetSource(source).uri\n : source.default;\n },\n findNodeHandle,\n View,\n};\n"],"mappings":";;;;;;AAAA,IAAAA,YAAA,GAAAC,OAAA;AASA,IAAAC,MAAA,GAAAD,OAAA;AAIO,MAAME,QAAmB,GAAAC,OAAA,CAAAD,QAAA,GAAG;EACjCE,EAAE,EAAEC,qBAAU,CAACD,EAAE;EACjBE,UAAU,EAAEA,uBAAU,CAACC,GAAG,CAAC,CAAC;EAC5BC,YAAY,EAAGC,MAAkB,IAAK;IACpC,OAAO,IAAAC,iBAAU,EAACD,MAAM,CAAC,GACrBE,kBAAK,CAACC,kBAAkB,CAACH,MAAM,CAAC,CAACI,GAAG,GACpCJ,MAAM,CAACK,OAAO;EACpB,CAAC;EACDC,cAAc,EAAdA,2BAAc;EACdC,IAAI,EAAJA;AACF,CAAC","ignoreList":[]}
1
+ {"version":3,"names":["_reactNative","require","_types","Platform","exports","OS","RNPlatform","PixelRatio","get","resolveAsset","source","isRNModule","Image","resolveAssetSource","uri","default","findNodeHandle","View"],"sources":["Platform.ts"],"sourcesContent":["import {\n Image,\n PixelRatio,\n Platform as RNPlatform,\n findNodeHandle,\n View,\n} from \"react-native\";\n\nimport type { DataModule } from \"../skia/types\";\nimport { isRNModule } from \"../skia/types\";\n\nimport type { IPlatform } from \"./IPlatform\";\n\nexport const Platform: IPlatform = {\n OS: RNPlatform.OS,\n PixelRatio: PixelRatio.get(),\n resolveAsset: (source: DataModule) => {\n // eslint-disable-next-line no-nested-ternary\n return isRNModule(source)\n ? Image.resolveAssetSource(source).uri\n : \"uri\" in source\n ? source.uri\n : source.default;\n },\n findNodeHandle,\n View,\n};\n"],"mappings":";;;;;;AAAA,IAAAA,YAAA,GAAAC,OAAA;AASA,IAAAC,MAAA,GAAAD,OAAA;AAIO,MAAME,QAAmB,GAAAC,OAAA,CAAAD,QAAA,GAAG;EACjCE,EAAE,EAAEC,qBAAU,CAACD,EAAE;EACjBE,UAAU,EAAEA,uBAAU,CAACC,GAAG,CAAC,CAAC;EAC5BC,YAAY,EAAGC,MAAkB,IAAK;IACpC;IACA,OAAO,IAAAC,iBAAU,EAACD,MAAM,CAAC,GACrBE,kBAAK,CAACC,kBAAkB,CAACH,MAAM,CAAC,CAACI,GAAG,GACpC,KAAK,IAAIJ,MAAM,GACfA,MAAM,CAACI,GAAG,GACVJ,MAAM,CAACK,OAAO;EACpB,CAAC;EACDC,cAAc,EAAdA,2BAAc;EACdC,IAAI,EAAJA;AACF,CAAC","ignoreList":[]}
@@ -149,6 +149,9 @@ const Platform = exports.Platform = {
149
149
  }
150
150
  throw new Error("Asset source is a number - this is not supported on the web");
151
151
  }
152
+ if ("uri" in source) {
153
+ return source.uri;
154
+ }
152
155
  return source.default;
153
156
  },
154
157
  findNodeHandle: () => {