react-native-webgpu 0.6.1 → 0.6.3

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 CHANGED
@@ -8,20 +8,6 @@ WebGPU for React Native, powered by Dawn.
8
8
 
9
9
  [Installation instructions](https://wcandillon.github.io/react-native-webgpu/docs/getting-started/installation)
10
10
 
11
- ## Expo config plugin
12
-
13
- The package ships with an Expo config plugin that disables Metal API validation in the iOS Xcode scheme, which is required to run WebGPU on the iOS Simulator (Metal API validation reports false positives for Dawn that do not occur on device). Because the plugin is part of the package, plugin and package versions are always aligned.
14
-
15
- ```json
16
- {
17
- "expo": {
18
- "plugins": ["react-native-webgpu"]
19
- }
20
- }
21
- ```
22
-
23
- See the [Expo guide](https://wcandillon.github.io/react-native-webgpu/docs/getting-started/expo#config-plugin) for details.
24
-
25
11
  ## Contributing
26
12
 
27
- For detailed information on library development, building, testing, and contributing guidelines, please see [CONTRIBUTING.md](packages/webgpu/CONTRIBUTING.md).
13
+ For detailed information on library development, building, testing, and contributing guidelines, please see [CONTRIBUTING.md](packages/webgpu/CONTRIBUTING.md).
@@ -179,6 +179,9 @@ public:
179
179
  result.height = static_cast<int>(bitmapInfo.height);
180
180
  result.data.resize(bitmapInfo.height * bitmapInfo.stride);
181
181
  memcpy(result.data.data(), bitmapPixels, result.data.size());
182
+ // BitmapFactory hands back premultiplied ARGB_8888 pixels; createImageBitmap
183
+ // converts to the representation requested by premultiplyAlpha.
184
+ result.premultiplied = true;
182
185
 
183
186
  AndroidBitmap_unlockPixels(env, bitmap);
184
187
 
@@ -4,6 +4,7 @@
4
4
 
5
5
  #import <AVFoundation/AVFoundation.h>
6
6
  #import <CoreVideo/CoreVideo.h>
7
+ #import <ImageIO/ImageIO.h>
7
8
  #import <React/RCTBlobManager.h>
8
9
  #import <React/RCTBridge+Private.h>
9
10
  #import <ReactCommon/RCTTurboModule.h>
@@ -88,36 +89,32 @@ void ApplePlatformContext::createImageBitmapAsync(
88
89
 
89
90
  ImageData
90
91
  ApplePlatformContext::createImageBitmapFromData(std::span<const uint8_t> data) {
91
- // This avoids a copy by assuming the UIImage/NSImage constructors
92
- // decode `nsData` eagerly before the memory for the wrapped `data`
93
- // is freed.
94
- //
95
- // Since we get the `CGImageRef` from `image` and then throw
96
- // it away, that's a fairly safe assumption.
92
+ // All formats are decoded through ImageIO. Apple's imaging stack always
93
+ // premultiplies alpha at decode, so the result is flagged premultiplied and
94
+ // createImageBitmap / copyExternalImageToTexture convert from there. This
95
+ // means premultiplyAlpha "none" is a lossy round trip for low-alpha pixels
96
+ // (as it is on Android); the snapshot suites compare with pixelmatch
97
+ // tolerance to absorb it.
97
98
  NSData *nsData =
98
99
  [NSData dataWithBytesNoCopy:const_cast<uint8_t *>(data.data())
99
100
  length:data.size()
100
101
  freeWhenDone:NO];
101
102
 
102
- #if !TARGET_OS_OSX
103
- UIImage *image = [UIImage imageWithData:nsData];
104
- #else
105
- NSImage *image = [[NSImage alloc] initWithData:nsData];
106
- #endif
107
- if (!image) {
103
+ CGImageSourceRef imageSource =
104
+ CGImageSourceCreateWithData((__bridge CFDataRef)nsData, NULL);
105
+ if (imageSource == NULL) {
106
+ throw std::runtime_error("Couldn't create image source");
107
+ }
108
+ NSDictionary *decodeOptions = @{(id)kCGImageSourceShouldCache : @NO};
109
+ CGImageRef cgImage = CGImageSourceCreateImageAtIndex(
110
+ imageSource, 0, (__bridge CFDictionaryRef)decodeOptions);
111
+ CFRelease(imageSource);
112
+ if (cgImage == NULL) {
108
113
  throw std::runtime_error("Couldn't decode image");
109
114
  }
110
115
 
111
- #if !TARGET_OS_OSX
112
- CGImageRef cgImage = image.CGImage;
113
- #else
114
- CGImageRef cgImage = [image CGImageForProposedRect:NULL
115
- context:NULL
116
- hints:NULL];
117
- #endif
118
116
  size_t width = CGImageGetWidth(cgImage);
119
117
  size_t height = CGImageGetHeight(cgImage);
120
- size_t bitsPerComponent = 8;
121
118
  size_t bytesPerRow = width * 4;
122
119
 
123
120
  ImageData result;
@@ -126,16 +123,18 @@ ApplePlatformContext::createImageBitmapFromData(std::span<const uint8_t> data) {
126
123
  result.data.resize(height * bytesPerRow);
127
124
  result.format = wgpu::TextureFormat::RGBA8Unorm;
128
125
 
126
+ // The draw premultiplies alpha; flag the result premultiplied so
127
+ // createImageBitmap and copyExternalImageToTexture convert consistently.
129
128
  CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
130
129
  CGContextRef context = CGBitmapContextCreate(
131
- result.data.data(), width, height, bitsPerComponent, bytesPerRow,
132
- colorSpace, kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);
133
-
130
+ result.data.data(), width, height, 8, bytesPerRow, colorSpace,
131
+ kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);
134
132
  CGContextDrawImage(context, CGRectMake(0, 0, width, height), cgImage);
135
-
136
133
  CGContextRelease(context);
137
134
  CGColorSpaceRelease(colorSpace);
135
+ CGImageRelease(cgImage);
138
136
 
137
+ result.premultiplied = true;
139
138
  return result;
140
139
  }
141
140
 
@@ -9,6 +9,7 @@
9
9
  #include <memory>
10
10
  #include <mutex>
11
11
  #include <optional>
12
+ #include <stdexcept>
12
13
  #include <string>
13
14
  #include <type_traits>
14
15
  #include <unordered_map>
@@ -48,6 +49,12 @@ public:
48
49
 
49
50
  void registerInstaller(const std::string &brand, InstallerFunc installer) {
50
51
  std::lock_guard<std::mutex> lock(_mutex);
52
+ if (_installers.count(brand) != 0) {
53
+ // The brand is the key unbox() uses to rebuild objects on worklet
54
+ // runtimes - a duplicate would let one class hijack another's unboxing.
55
+ throw std::runtime_error("Duplicate native object brand registered: " +
56
+ brand);
57
+ }
51
58
  _installers[brand] = std::move(installer);
52
59
  }
53
60
 
@@ -235,6 +242,10 @@ public:
235
242
  * Each NativeObject<Derived> type has its own static cache.
236
243
  * Uses StaticRuntimeAwareCache to properly handle runtime lifecycle
237
244
  * and hot reload (where the main runtime is destroyed and recreated).
245
+ *
246
+ * Callers must hold getPrototypeCacheMutex(): the cache is reached
247
+ * concurrently from the main JS thread (create()) and from worklet
248
+ * runtime threads (BoxedWebGPUObject::unbox() -> installPrototype()).
238
249
  */
239
250
  static RuntimeAwareCache<PrototypeCacheEntry> &
240
251
  getPrototypeCache(jsi::Runtime &runtime) {
@@ -242,11 +253,22 @@ public:
242
253
  return cache.get(runtime);
243
254
  }
244
255
 
256
+ /**
257
+ * Per-class mutex guarding getPrototypeCache() and prototype
258
+ * installation. Serializes the StaticRuntimeAwareCache pointer swap
259
+ * (hot reload) and the per-runtime cache lookups.
260
+ */
261
+ static std::mutex &getPrototypeCacheMutex() {
262
+ static std::mutex mutex;
263
+ return mutex;
264
+ }
265
+
245
266
  /**
246
267
  * Ensure the prototype is installed for this runtime.
247
268
  * Called automatically by create(), but can be called manually.
248
269
  */
249
270
  static void installPrototype(jsi::Runtime &runtime) {
271
+ std::lock_guard<std::mutex> lock(getPrototypeCacheMutex());
250
272
  auto &entry = getPrototypeCache(runtime).get(runtime);
251
273
  if (entry.prototype.has_value()) {
252
274
  return; // Already installed
@@ -278,6 +300,49 @@ public:
278
300
  defineProperty.call(runtime, prototype, toStringTag, descriptor);
279
301
  }
280
302
 
303
+ // Install a generic toJSON so JSON.stringify works: data properties live
304
+ // as getters on this shared prototype, and JSON.stringify only serializes
305
+ // own enumerable properties (so it would otherwise produce {}).
306
+ auto toJSON = jsi::Function::createFromHostFunction(
307
+ runtime, jsi::PropNameID::forUtf8(runtime, "toJSON"), 0,
308
+ [](jsi::Runtime &rt, const jsi::Value &thisVal,
309
+ const jsi::Value * /*args*/, size_t /*count*/) -> jsi::Value {
310
+ auto thisObj = thisVal.getObject(rt);
311
+ auto objectCtor = rt.global().getPropertyAsObject(rt, "Object");
312
+ auto getPrototypeOf =
313
+ objectCtor.getPropertyAsFunction(rt, "getPrototypeOf");
314
+ auto proto = getPrototypeOf.call(rt, thisObj);
315
+ jsi::Object result(rt);
316
+ if (!proto.isObject()) {
317
+ return std::move(result);
318
+ }
319
+ auto getOwnPropertyNames =
320
+ objectCtor.getPropertyAsFunction(rt, "getOwnPropertyNames");
321
+ auto names =
322
+ getOwnPropertyNames.call(rt, proto).getObject(rt).getArray(rt);
323
+ size_t length = names.size(rt);
324
+ for (size_t i = 0; i < length; i++) {
325
+ auto nameValue = names.getValueAtIndex(rt, i);
326
+ if (!nameValue.isString()) {
327
+ continue;
328
+ }
329
+ auto name = nameValue.getString(rt).utf8(rt);
330
+ if (name == "constructor" || name == "toJSON") {
331
+ continue;
332
+ }
333
+ // Read off `this` so prototype getters evaluate against the
334
+ // object's native state. Getters that throw keep throwing.
335
+ auto value = thisObj.getProperty(rt, name.c_str());
336
+ if (value.isObject() && value.getObject(rt).isFunction(rt)) {
337
+ // Skip methods - JSON.stringify would drop them anyway
338
+ continue;
339
+ }
340
+ result.setProperty(rt, name.c_str(), value);
341
+ }
342
+ return std::move(result);
343
+ });
344
+ prototype.setProperty(runtime, "toJSON", toJSON);
345
+
281
346
  // Cache the prototype
282
347
  entry.prototype = std::move(prototype);
283
348
  }
@@ -303,6 +368,7 @@ public:
303
368
 
304
369
  installPrototype(runtime);
305
370
 
371
+ std::lock_guard<std::mutex> lock(getPrototypeCacheMutex());
306
372
  auto &entry = getPrototypeCache(runtime).get(runtime);
307
373
  if (!entry.prototype.has_value()) {
308
374
  return;
@@ -346,14 +412,17 @@ public:
346
412
  obj.setNativeState(runtime, instance);
347
413
 
348
414
  // Set prototype
349
- auto &entry = getPrototypeCache(runtime).get(runtime);
350
- if (entry.prototype.has_value()) {
351
- // Use Object.setPrototypeOf to set the prototype
352
- auto objectCtor =
353
- runtime.global().getPropertyAsObject(runtime, "Object");
354
- auto setPrototypeOf =
355
- objectCtor.getPropertyAsFunction(runtime, "setPrototypeOf");
356
- setPrototypeOf.call(runtime, obj, *entry.prototype);
415
+ {
416
+ std::lock_guard<std::mutex> lock(getPrototypeCacheMutex());
417
+ auto &entry = getPrototypeCache(runtime).get(runtime);
418
+ if (entry.prototype.has_value()) {
419
+ // Use Object.setPrototypeOf to set the prototype
420
+ auto objectCtor =
421
+ runtime.global().getPropertyAsObject(runtime, "Object");
422
+ auto setPrototypeOf =
423
+ objectCtor.getPropertyAsFunction(runtime, "setPrototypeOf");
424
+ setPrototypeOf.call(runtime, obj, *entry.prototype);
425
+ }
357
426
  }
358
427
 
359
428
  // Set memory pressure hint for GC
@@ -2,6 +2,6 @@
2
2
 
3
3
  namespace rnwgpu {
4
4
 
5
- jsi::Runtime *BaseRuntimeAwareCache::_mainRuntime = nullptr;
5
+ std::atomic<jsi::Runtime *> BaseRuntimeAwareCache::_mainRuntime{nullptr};
6
6
 
7
7
  } // namespace rnwgpu
@@ -2,8 +2,10 @@
2
2
 
3
3
  #include <jsi/jsi.h>
4
4
 
5
+ #include <atomic>
5
6
  #include <cassert>
6
7
  #include <memory>
8
+ #include <mutex>
7
9
  #include <unordered_map>
8
10
  #include <utility>
9
11
 
@@ -15,17 +17,22 @@ namespace jsi = facebook::jsi;
15
17
 
16
18
  class BaseRuntimeAwareCache {
17
19
  public:
18
- static void setMainJsRuntime(jsi::Runtime *rt) { _mainRuntime = rt; }
20
+ static void setMainJsRuntime(jsi::Runtime *rt) {
21
+ // Atomic: hot reload rewrites this from the JS thread while worklet
22
+ // runtime threads read it concurrently.
23
+ _mainRuntime.store(rt, std::memory_order_release);
24
+ }
19
25
  static jsi::Runtime *getMainJsRuntime() {
20
- assert(_mainRuntime != nullptr &&
26
+ auto *rt = _mainRuntime.load(std::memory_order_acquire);
27
+ assert(rt != nullptr &&
21
28
  "Expected main Javascript runtime to be set in the "
22
29
  "BaseRuntimeAwareCache class.");
23
30
 
24
- return _mainRuntime;
31
+ return rt;
25
32
  }
26
33
 
27
34
  private:
28
- static jsi::Runtime *_mainRuntime;
35
+ static std::atomic<jsi::Runtime *> _mainRuntime;
29
36
  };
30
37
 
31
38
  /**
@@ -56,7 +63,10 @@ class RuntimeAwareCache : public BaseRuntimeAwareCache,
56
63
  public:
57
64
  void onRuntimeDestroyed(jsi::Runtime *rt) override {
58
65
  if (getMainJsRuntime() != rt) {
59
- // We are removing a secondary runtime
66
+ // We are removing a secondary runtime. This is invoked by
67
+ // RuntimeLifecycleMonitor on the destroyed runtime's thread, which may
68
+ // run concurrently with get() on another runtime's thread.
69
+ std::lock_guard<std::mutex> lock(_secondaryCachesMutex);
60
70
  _secondaryRuntimeCaches.erase(rt);
61
71
  }
62
72
  }
@@ -75,6 +85,14 @@ public:
75
85
  if (getMainJsRuntime() == &rt) {
76
86
  return _primaryCache;
77
87
  } else {
88
+ // Guard the secondary map: it can be mutated concurrently by get()
89
+ // on a worklet runtime's thread and by onRuntimeDestroyed() when
90
+ // another secondary runtime is torn down. The main-runtime path above
91
+ // stays lock-free. References into the map remain valid after the
92
+ // lock is released (unordered_map never invalidates references on
93
+ // insert/erase of other keys, and this runtime's entry can only be
94
+ // erased from this runtime's own thread).
95
+ std::lock_guard<std::mutex> lock(_secondaryCachesMutex);
78
96
  if (_secondaryRuntimeCaches.count(&rt) == 0) {
79
97
  // we only add listener when the secondary runtime is used, this assumes
80
98
  // that the secondary runtime is terminated first. This lets us avoid
@@ -88,11 +106,12 @@ public:
88
106
  T cache;
89
107
  _secondaryRuntimeCaches.emplace(&rt, std::move(cache));
90
108
  }
109
+ return _secondaryRuntimeCaches.at(&rt);
91
110
  }
92
- return _secondaryRuntimeCaches.at(&rt);
93
111
  }
94
112
 
95
113
  private:
114
+ std::mutex _secondaryCachesMutex;
96
115
  std::unordered_map<void *, T> _secondaryRuntimeCaches;
97
116
  T _primaryCache;
98
117
  };
@@ -16,6 +16,13 @@ struct ImageData {
16
16
  size_t width;
17
17
  size_t height;
18
18
  wgpu::TextureFormat format;
19
+ // Whether the RGB channels are premultiplied by alpha. Each platform decoder
20
+ // records the representation it produced; createImageBitmap then converts to
21
+ // the representation requested through premultiplyAlpha, and
22
+ // copyExternalImageToTexture converts again to match the destination's
23
+ // premultipliedAlpha. Defaults to true because most native decoders
24
+ // (CoreGraphics, Android Bitmap) hand back premultiplied pixels.
25
+ bool premultiplied = true;
19
26
  };
20
27
 
21
28
  // Pixel layout of a VideoFrame. Determines whether the underlying surface is
@@ -128,26 +128,40 @@ void GPUQueue::copyExternalImageToTexture(
128
128
  throw std::runtime_error("Invalid input for GPUQueue::writeTexture()");
129
129
  }
130
130
 
131
- if (source->flipY.value_or(false)) {
132
- // Calculate the row size and total size
131
+ const bool flipY = source->flipY.value_or(false);
132
+ // premultipliedAlpha defaults to false per the WebGPU spec: an untagged
133
+ // destination expects straight alpha. Convert only when the ImageBitmap's
134
+ // representation differs, using the same rounding as the reference client.
135
+ const bool sourcePremultiplied = source->source->getPremultiplied();
136
+ const bool destinationPremultiplied =
137
+ destination->premultipliedAlpha.value_or(false);
138
+ const bool needsAlphaConversion =
139
+ bytesPerPixel == 4 && sourcePremultiplied != destinationPremultiplied;
140
+
141
+ if (flipY || needsAlphaConversion) {
133
142
  uint32_t rowSize = bytesPerPixel * source->source->getWidth();
134
143
  uint32_t totalSize = source->source->getSize();
135
144
 
136
- // Create a new buffer for the flipped data
137
- std::vector<uint8_t> flippedData(totalSize);
138
-
139
- // Flip the data vertically
140
- for (uint32_t row = 0; row < source->source->getHeight(); ++row) {
141
- std::memcpy(flippedData.data() +
142
- (source->source->getHeight() - 1 - row) * rowSize,
143
- static_cast<const uint8_t *>(source->source->getData()) +
144
- row * rowSize,
145
- rowSize);
145
+ // Stage a mutable copy so flipping and/or alpha conversion never touch the
146
+ // ImageBitmap's backing store.
147
+ std::vector<uint8_t> staged(totalSize);
148
+ const uint8_t *src =
149
+ static_cast<const uint8_t *>(source->source->getData());
150
+ if (flipY) {
151
+ for (uint32_t row = 0; row < source->source->getHeight(); ++row) {
152
+ std::memcpy(staged.data() +
153
+ (source->source->getHeight() - 1 - row) * rowSize,
154
+ src + row * rowSize, rowSize);
155
+ }
156
+ } else {
157
+ std::memcpy(staged.data(), src, totalSize);
158
+ }
159
+ if (needsAlphaConversion) {
160
+ convertAlpha(staged.data(), totalSize, sourcePremultiplied,
161
+ destinationPremultiplied);
146
162
  }
147
- // Use the flipped data for writing to texture
148
- _instance.WriteTexture(&dst, flippedData.data(), totalSize, &layout, &sz);
163
+ _instance.WriteTexture(&dst, staged.data(), totalSize, &layout, &sz);
149
164
  } else {
150
-
151
165
  _instance.WriteTexture(&dst, source->source->getData(),
152
166
  source->source->getSize(), &layout, &sz);
153
167
  }
@@ -11,6 +11,33 @@ namespace rnwgpu {
11
11
 
12
12
  namespace jsi = facebook::jsi;
13
13
 
14
+ // Convert RGBA8 pixel data in place between straight and premultiplied alpha.
15
+ // Uses the same integer rounding as the web reference polyfill in the test
16
+ // harness (setup.ts, convertAlpha) so the native result is bit-exact with the
17
+ // dawn.node client. A no-op when the source and destination representations
18
+ // already match.
19
+ inline void convertAlpha(uint8_t *data, size_t byteLength,
20
+ bool sourcePremultiplied,
21
+ bool destinationPremultiplied) {
22
+ if (sourcePremultiplied == destinationPremultiplied) {
23
+ return;
24
+ }
25
+ for (size_t i = 0; i + 3 < byteLength; i += 4) {
26
+ const uint32_t alpha = data[i + 3];
27
+ for (size_t channel = 0; channel < 3; channel++) {
28
+ const uint32_t value = data[i + channel];
29
+ if (destinationPremultiplied) {
30
+ data[i + channel] = static_cast<uint8_t>((value * alpha + 127) / 255);
31
+ } else if (alpha == 0) {
32
+ data[i + channel] = 0;
33
+ } else {
34
+ const uint32_t straight = (value * 255 + (alpha >> 1)) / alpha;
35
+ data[i + channel] = static_cast<uint8_t>(straight > 255 ? 255 : straight);
36
+ }
37
+ }
38
+ }
39
+ }
40
+
14
41
  class ImageBitmap : public NativeObject<ImageBitmap> {
15
42
  public:
16
43
  static constexpr const char *CLASS_NAME = "ImageBitmap";
@@ -26,6 +53,11 @@ public:
26
53
 
27
54
  size_t getSize() { return _imageData.data.size(); }
28
55
 
56
+ // Whether the stored pixels are premultiplied by alpha. Used by
57
+ // copyExternalImageToTexture to decide whether a conversion to the
58
+ // destination's premultipliedAlpha representation is needed.
59
+ bool getPremultiplied() { return _imageData.premultiplied; }
60
+
29
61
  void close() {
30
62
  _imageData.data.clear();
31
63
  _imageData.data.shrink_to_fit();
@@ -90,6 +90,41 @@ public:
90
90
  auto platformContext = _platformContext;
91
91
  auto callInvoker = _callInvoker;
92
92
 
93
+ // Resolve the requested alpha representation from the ImageBitmapOptions.
94
+ // The options bag is the second argument for createImageBitmap(source,
95
+ // options) and the sixth for the crop-rect overload createImageBitmap(
96
+ // source, sx, sy, sw, sh, options). premultiplyAlpha defaults to
97
+ // "default", which (like "premultiply") stores premultiplied pixels; only
98
+ // "none" keeps straight alpha. The other options (crop rect, resize,
99
+ // imageOrientation, colorSpaceConversion) are not yet implemented natively.
100
+ bool wantPremultiplied = true;
101
+ const jsi::Value *optionsArg = nullptr;
102
+ if (count >= 2 && args[1].isObject()) {
103
+ optionsArg = &args[1];
104
+ } else if (count >= 6 && args[5].isObject()) {
105
+ optionsArg = &args[5];
106
+ }
107
+ if (optionsArg != nullptr) {
108
+ auto options = optionsArg->getObject(runtime);
109
+ if (options.hasProperty(runtime, "premultiplyAlpha")) {
110
+ auto value = options.getProperty(runtime, "premultiplyAlpha");
111
+ if (value.isString() &&
112
+ value.getString(runtime).utf8(runtime) == "none") {
113
+ wantPremultiplied = false;
114
+ }
115
+ }
116
+ }
117
+
118
+ // Bring the decoded pixels into the representation requested via
119
+ // premultiplyAlpha before wrapping them in an ImageBitmap.
120
+ auto toRequestedAlpha = [wantPremultiplied](ImageData &imageData) {
121
+ if (imageData.premultiplied != wantPremultiplied) {
122
+ convertAlpha(imageData.data.data(), imageData.data.size(),
123
+ imageData.premultiplied, wantPremultiplied);
124
+ imageData.premultiplied = wantPremultiplied;
125
+ }
126
+ };
127
+
93
128
  // Check if the argument is an ArrayBuffer or ArrayBufferView
94
129
  // (TypedArray / DataView). Only a real buffer source is run through the
95
130
  // ArrayBuffer converter, which validates byteOffset/byteLength against the
@@ -119,12 +154,14 @@ public:
119
154
 
120
155
  return Promise::createPromise(
121
156
  runtime,
122
- [platformContext, callInvoker, dataCopy = std::move(dataCopy)](
157
+ [platformContext, callInvoker, toRequestedAlpha,
158
+ dataCopy = std::move(dataCopy)](
123
159
  jsi::Runtime & /*runtime*/,
124
160
  std::shared_ptr<Promise> promise) mutable {
125
161
  platformContext->createImageBitmapFromDataAsync(
126
162
  dataCopy,
127
- [callInvoker, promise](ImageData imageData) {
163
+ [callInvoker, promise, toRequestedAlpha](ImageData imageData) {
164
+ toRequestedAlpha(imageData);
128
165
  auto imageBitmap = std::make_shared<ImageBitmap>(imageData);
129
166
  callInvoker->invokeAsync([promise, imageBitmap]() {
130
167
  promise->resolve(
@@ -149,11 +186,12 @@ public:
149
186
 
150
187
  return Promise::createPromise(
151
188
  runtime,
152
- [platformContext, callInvoker, blobId, offset,
189
+ [platformContext, callInvoker, toRequestedAlpha, blobId, offset,
153
190
  size](jsi::Runtime & /*runtime*/, std::shared_ptr<Promise> promise) {
154
191
  platformContext->createImageBitmapAsync(
155
192
  blobId, offset, size,
156
- [callInvoker, promise](ImageData imageData) {
193
+ [callInvoker, promise, toRequestedAlpha](ImageData imageData) {
194
+ toRequestedAlpha(imageData);
157
195
  auto imageBitmap = std::make_shared<ImageBitmap>(imageData);
158
196
  callInvoker->invokeAsync([promise, imageBitmap]() {
159
197
  promise->resolve(
@@ -37,6 +37,6 @@ declare global {
37
37
  destroy(): void;
38
38
  readonly yuvToRgbMatrix: number[];
39
39
  }
40
- function createImageBitmap(image: ArrayBuffer | ArrayBufferView): Promise<ImageBitmap>;
40
+ function createImageBitmap(image: ArrayBuffer | ArrayBufferView, options?: ImageBitmapOptions): Promise<ImageBitmap>;
41
41
  }
42
42
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,cAAc,EACd,wBAAwB,EACxB,wBAAwB,EACxB,sBAAsB,EACtB,gCAAgC,EAChC,YAAY,EACZ,eAAe,EACf,WAAW,EACX,gBAAgB,EAChB,sBAAsB,EACvB,MAAM,SAAS,CAAC;AAEjB,cAAc,QAAQ,CAAC;AACvB,cAAc,aAAa,CAAC;AAC5B,cAAc,WAAW,CAAC;AAC1B,YAAY,EACV,gBAAgB,EAChB,WAAW,EACX,cAAc,EACd,wBAAwB,EACxB,wBAAwB,EACxB,mBAAmB,EACnB,kBAAkB,EAClB,sBAAsB,EACtB,gCAAgC,EAChC,oCAAoC,EACpC,sBAAsB,EACtB,wBAAwB,EACxB,wBAAwB,GACzB,MAAM,SAAS,CAAC;AAEjB,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,SAAS;QACjB,GAAG,EAAE,GAAG,CAAC;KACV;IAED,IAAI,SAAS,EAAE,SAAS,CAAC;IAEzB,IAAI,QAAQ,EAAE;QACZ,GAAG,EAAE,GAAG,CAAC;QACT,MAAM,EAAE,OAAO,CAAC;QAChB,gBAAgB,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,YAAY,CAAC;QACtD,uBAAuB,EAAE,CACvB,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,KACX,eAAe,CAAC;QAGrB,cAAc,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;QAC5C,YAAY,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,eAAe,GAAG,WAAW,KAAK,MAAM,CAAC;QACvE,iBAAiB,EAAE,OAAO,iBAAiB,CAAC;QAC5C,cAAc,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,gBAAgB,CAAC;QACnD,oBAAoB,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,gBAAgB,CAAC;QAI1E,gCAAgC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,gBAAgB,CAAC;QACxE,iBAAiB,EAAE,CACjB,IAAI,EAAE,MAAM,EACZ,WAAW,CAAC,EAAE,sBAAsB,KACjC,WAAW,CAAC;QACjB,kBAAkB,EAAE,MAAM,MAAM,CAAC;KAClC,CAAC;IAEF,UAAU,SAAS;QACjB,yBAAyB,CACvB,UAAU,EAAE,gCAAgC,GAC3C,sBAAsB,CAAC;QAC1B,iBAAiB,CAAC,UAAU,EAAE,wBAAwB,GAAG,cAAc,CAAC;KACzE;IAID,UAAU,mBAAmB;QAC3B,WAAW,CAAC,EAAE,wBAAwB,CAAC;KACxC;IAMD,UAAU,4BAA4B;QACpC,QAAQ,CAAC,EAAE,CAAC,GAAG,EAAE,GAAG,GAAG,GAAG,GAAG,CAAC;QAC9B,QAAQ,CAAC,EAAE,OAAO,CAAC;KACpB;IASD,UAAU,kBAAkB;QAC1B,OAAO,IAAI,IAAI,CAAC;QAShB,QAAQ,CAAC,cAAc,EAAE,MAAM,EAAE,CAAC;KACnC;IAGD,SAAS,iBAAiB,CACxB,KAAK,EAAE,WAAW,GAAG,eAAe,GACnC,OAAO,CAAC,WAAW,CAAC,CAAC;CACzB"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,cAAc,EACd,wBAAwB,EACxB,wBAAwB,EACxB,sBAAsB,EACtB,gCAAgC,EAChC,YAAY,EACZ,eAAe,EACf,WAAW,EACX,gBAAgB,EAChB,sBAAsB,EACvB,MAAM,SAAS,CAAC;AAEjB,cAAc,QAAQ,CAAC;AACvB,cAAc,aAAa,CAAC;AAC5B,cAAc,WAAW,CAAC;AAC1B,YAAY,EACV,gBAAgB,EAChB,WAAW,EACX,cAAc,EACd,wBAAwB,EACxB,wBAAwB,EACxB,mBAAmB,EACnB,kBAAkB,EAClB,sBAAsB,EACtB,gCAAgC,EAChC,oCAAoC,EACpC,sBAAsB,EACtB,wBAAwB,EACxB,wBAAwB,GACzB,MAAM,SAAS,CAAC;AAEjB,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,SAAS;QACjB,GAAG,EAAE,GAAG,CAAC;KACV;IAED,IAAI,SAAS,EAAE,SAAS,CAAC;IAEzB,IAAI,QAAQ,EAAE;QACZ,GAAG,EAAE,GAAG,CAAC;QACT,MAAM,EAAE,OAAO,CAAC;QAChB,gBAAgB,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,YAAY,CAAC;QACtD,uBAAuB,EAAE,CACvB,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,KACX,eAAe,CAAC;QAGrB,cAAc,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;QAC5C,YAAY,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,eAAe,GAAG,WAAW,KAAK,MAAM,CAAC;QACvE,iBAAiB,EAAE,OAAO,iBAAiB,CAAC;QAC5C,cAAc,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,gBAAgB,CAAC;QACnD,oBAAoB,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,gBAAgB,CAAC;QAI1E,gCAAgC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,gBAAgB,CAAC;QACxE,iBAAiB,EAAE,CACjB,IAAI,EAAE,MAAM,EACZ,WAAW,CAAC,EAAE,sBAAsB,KACjC,WAAW,CAAC;QACjB,kBAAkB,EAAE,MAAM,MAAM,CAAC;KAClC,CAAC;IAEF,UAAU,SAAS;QACjB,yBAAyB,CACvB,UAAU,EAAE,gCAAgC,GAC3C,sBAAsB,CAAC;QAC1B,iBAAiB,CAAC,UAAU,EAAE,wBAAwB,GAAG,cAAc,CAAC;KACzE;IAID,UAAU,mBAAmB;QAC3B,WAAW,CAAC,EAAE,wBAAwB,CAAC;KACxC;IAMD,UAAU,4BAA4B;QACpC,QAAQ,CAAC,EAAE,CAAC,GAAG,EAAE,GAAG,GAAG,GAAG,GAAG,CAAC;QAC9B,QAAQ,CAAC,EAAE,OAAO,CAAC;KACpB;IASD,UAAU,kBAAkB;QAC1B,OAAO,IAAI,IAAI,CAAC;QAShB,QAAQ,CAAC,cAAc,EAAE,MAAM,EAAE,CAAC;KACnC;IAGD,SAAS,iBAAiB,CACxB,KAAK,EAAE,WAAW,GAAG,eAAe,EACpC,OAAO,CAAC,EAAE,kBAAkB,GAC3B,OAAO,CAAC,WAAW,CAAC,CAAC;CACzB"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-webgpu",
3
- "version": "0.6.1",
3
+ "version": "0.6.3",
4
4
  "description": "React Native WebGPU",
5
5
  "main": "lib/commonjs/index",
6
6
  "module": "lib/module/index",
@@ -23,7 +23,8 @@ Pod::Spec.new do |s|
23
23
 
24
24
  # The VideoPlayer API uses AVFoundation / CoreMedia, and shared-texture
25
25
  # surfaces use CoreVideo (CVPixelBuffer). Link them so their symbols resolve.
26
- s.frameworks = "AVFoundation", "CoreMedia", "CoreVideo"
26
+ # ImageIO provides CGImageSource, the image decoder behind createImageBitmap.
27
+ s.frameworks = "AVFoundation", "CoreMedia", "CoreVideo", "ImageIO"
27
28
 
28
29
  s.pod_target_xcconfig = {
29
30
  'HEADER_SEARCH_PATHS' => '$(PODS_TARGET_SRCROOT)/cpp',
package/src/index.tsx CHANGED
@@ -110,5 +110,6 @@ declare global {
110
110
  // Extend createImageBitmap to accept ArrayBuffer/TypedArray (encoded image bytes)
111
111
  function createImageBitmap(
112
112
  image: ArrayBuffer | ArrayBufferView,
113
+ options?: ImageBitmapOptions,
113
114
  ): Promise<ImageBitmap>;
114
115
  }