react-native-wgpu 0.5.5 → 0.5.6

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.
@@ -2,7 +2,8 @@ cmake_minimum_required(VERSION 3.4.1)
2
2
  project(RNWGPU)
3
3
 
4
4
  set (CMAKE_VERBOSE_MAKEFILE ON)
5
- set (CMAKE_CXX_STANDARD 17)
5
+ set (CMAKE_CXX_STANDARD 20)
6
+ set (CMAKE_CXX_STANDARD_REQUIRED True)
6
7
 
7
8
  set (PACKAGE_NAME "react-native-wgpu")
8
9
 
@@ -71,7 +71,7 @@ android {
71
71
  buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", "true"
72
72
  externalNativeBuild {
73
73
  cmake {
74
- cppFlags "-fexceptions", "-frtti", "-std=c++1y", "-DONANDROID"
74
+ cppFlags "-fexceptions", "-frtti", "-DONANDROID"
75
75
  abiFilters (*reactNativeArchitectures())
76
76
  arguments '-DANDROID_STL=c++_shared',
77
77
  "-DNODE_MODULES_DIR=${nodeModules}",
@@ -23,6 +23,43 @@ class AndroidPlatformContext : public PlatformContext {
23
23
  private:
24
24
  jobject _blobModule;
25
25
 
26
+ std::vector<uint8_t> resolveBlob(JNIEnv *env, const std::string &blobId,
27
+ double offset, double size) {
28
+ if (!_blobModule) {
29
+ throw std::runtime_error("BlobModule instance is null");
30
+ }
31
+
32
+ jclass blobModuleClass = env->GetObjectClass(_blobModule);
33
+ if (!blobModuleClass) {
34
+ throw std::runtime_error("Couldn't find BlobModule class");
35
+ }
36
+
37
+ jmethodID resolveMethod = env->GetMethodID(blobModuleClass, "resolve",
38
+ "(Ljava/lang/String;II)[B");
39
+ env->DeleteLocalRef(blobModuleClass);
40
+
41
+ if (!resolveMethod) {
42
+ throw std::runtime_error("Couldn't find resolve method in BlobModule");
43
+ }
44
+
45
+ jstring jBlobId = env->NewStringUTF(blobId.c_str());
46
+ jbyteArray blobData = (jbyteArray)env->CallObjectMethod(
47
+ _blobModule, resolveMethod, jBlobId, static_cast<jint>(offset),
48
+ static_cast<jint>(size));
49
+ env->DeleteLocalRef(jBlobId);
50
+
51
+ if (!blobData) {
52
+ throw std::runtime_error("Couldn't retrieve blob data");
53
+ }
54
+
55
+ jsize len = env->GetArrayLength(blobData);
56
+ std::vector<uint8_t> data(len);
57
+ env->GetByteArrayRegion(blobData, 0, len,
58
+ reinterpret_cast<jbyte *>(data.data()));
59
+ env->DeleteLocalRef(blobData);
60
+ return data;
61
+ }
62
+
26
63
  public:
27
64
  explicit AndroidPlatformContext(jobject blobModule)
28
65
  : _blobModule(blobModule) {}
@@ -52,188 +89,116 @@ public:
52
89
  throw std::runtime_error("Couldn't get JNI environment");
53
90
  }
54
91
 
55
- // Use the BlobModule instance from _blobModule
56
- if (!_blobModule) {
57
- throw std::runtime_error("BlobModule instance is null");
58
- }
92
+ auto data = resolveBlob(env, blobId, offset, size);
93
+ return createImageBitmapFromData(data);
94
+ }
59
95
 
60
- // Get the resolve method ID
61
- jclass blobModuleClass = env->GetObjectClass(_blobModule);
62
- if (!blobModuleClass) {
63
- throw std::runtime_error("Couldn't find BlobModule class");
64
- }
96
+ void createImageBitmapAsync(
97
+ std::string blobId, double offset, double size,
98
+ std::function<void(ImageData)> onSuccess,
99
+ std::function<void(std::string)> onError) override {
100
+ std::thread([this, blobId = std::move(blobId), offset, size,
101
+ onSuccess = std::move(onSuccess),
102
+ onError = std::move(onError)]() {
103
+ jni::Environment::ensureCurrentThreadIsAttached();
104
+ try {
105
+ JNIEnv *env = facebook::jni::Environment::current();
106
+ if (!env) {
107
+ throw std::runtime_error("Couldn't get JNI environment");
108
+ }
109
+ auto data = resolveBlob(env, blobId, offset, size);
110
+ auto result = createImageBitmapFromData(data);
111
+ onSuccess(std::move(result));
112
+ } catch (const std::exception &e) {
113
+ onError(e.what());
114
+ }
115
+ }).detach();
116
+ }
65
117
 
66
- jmethodID resolveMethod = env->GetMethodID(blobModuleClass, "resolve",
67
- "(Ljava/lang/String;II)[B");
68
- if (!resolveMethod) {
69
- throw std::runtime_error("Couldn't find resolve method in BlobModule");
70
- }
118
+ ImageData createImageBitmapFromData(std::span<const uint8_t> data) override {
119
+ jni::Environment::ensureCurrentThreadIsAttached();
71
120
 
72
- // Resolve the blob data
73
- jstring jBlobId = env->NewStringUTF(blobId.c_str());
74
- jbyteArray blobData = (jbyteArray)env->CallObjectMethod(
75
- _blobModule, resolveMethod, jBlobId, static_cast<jint>(offset),
76
- static_cast<jint>(size));
77
- env->DeleteLocalRef(jBlobId);
121
+ JNIEnv *env = facebook::jni::Environment::current();
122
+ if (!env) {
123
+ throw std::runtime_error("Couldn't get JNI environment");
124
+ }
78
125
 
79
- if (!blobData) {
80
- throw std::runtime_error("Couldn't retrieve blob data");
126
+ // Create jbyteArray from the raw bytes
127
+ jbyteArray byteArray = env->NewByteArray(static_cast<jsize>(data.size()));
128
+ if (!byteArray) {
129
+ throw std::runtime_error("Couldn't allocate byte array");
81
130
  }
131
+ env->SetByteArrayRegion(byteArray, 0, static_cast<jsize>(data.size()),
132
+ reinterpret_cast<const jbyte *>(data.data()));
82
133
 
83
- // Create a Bitmap from the blob data
134
+ // Decode via BitmapFactory
84
135
  jclass bitmapFactoryClass =
85
136
  env->FindClass("android/graphics/BitmapFactory");
137
+ if (!bitmapFactoryClass) {
138
+ env->DeleteLocalRef(byteArray);
139
+ throw std::runtime_error("Couldn't find BitmapFactory class");
140
+ }
86
141
  jmethodID decodeByteArrayMethod =
87
142
  env->GetStaticMethodID(bitmapFactoryClass, "decodeByteArray",
88
143
  "([BII)Landroid/graphics/Bitmap;");
89
- jint blobLength = env->GetArrayLength(blobData);
144
+ if (!decodeByteArrayMethod) {
145
+ env->DeleteLocalRef(byteArray);
146
+ env->DeleteLocalRef(bitmapFactoryClass);
147
+ throw std::runtime_error("Couldn't find decodeByteArray method");
148
+ }
149
+ jint length = static_cast<jint>(data.size());
90
150
  jobject bitmap = env->CallStaticObjectMethod(
91
- bitmapFactoryClass, decodeByteArrayMethod, blobData, 0, blobLength);
151
+ bitmapFactoryClass, decodeByteArrayMethod, byteArray, 0, length);
152
+ env->DeleteLocalRef(bitmapFactoryClass);
92
153
 
93
154
  if (!bitmap) {
94
- env->DeleteLocalRef(blobData);
155
+ env->DeleteLocalRef(byteArray);
95
156
  throw std::runtime_error("Couldn't decode image");
96
157
  }
97
158
 
98
- // Get bitmap info
99
159
  AndroidBitmapInfo bitmapInfo;
100
160
  if (AndroidBitmap_getInfo(env, bitmap, &bitmapInfo) !=
101
161
  ANDROID_BITMAP_RESULT_SUCCESS) {
102
- env->DeleteLocalRef(blobData);
162
+ env->DeleteLocalRef(byteArray);
103
163
  env->DeleteLocalRef(bitmap);
104
164
  throw std::runtime_error("Couldn't get bitmap info");
105
165
  }
106
166
 
107
- // Lock the bitmap pixels
108
167
  void *bitmapPixels;
109
168
  if (AndroidBitmap_lockPixels(env, bitmap, &bitmapPixels) !=
110
169
  ANDROID_BITMAP_RESULT_SUCCESS) {
111
- env->DeleteLocalRef(blobData);
170
+ env->DeleteLocalRef(byteArray);
112
171
  env->DeleteLocalRef(bitmap);
113
172
  throw std::runtime_error("Couldn't lock bitmap pixels");
114
173
  }
115
174
 
116
- // Copy the bitmap data
117
- std::vector<uint8_t> imageData(bitmapInfo.height * bitmapInfo.stride);
118
- memcpy(imageData.data(), bitmapPixels, imageData.size());
175
+ ImageData result;
176
+ result.width = static_cast<int>(bitmapInfo.width);
177
+ result.height = static_cast<int>(bitmapInfo.height);
178
+ result.data.resize(bitmapInfo.height * bitmapInfo.stride);
179
+ memcpy(result.data.data(), bitmapPixels, result.data.size());
119
180
 
120
- // Unlock the bitmap pixels
121
181
  AndroidBitmap_unlockPixels(env, bitmap);
122
182
 
123
- // Clean up JNI references
124
- env->DeleteLocalRef(blobData);
183
+ env->DeleteLocalRef(byteArray);
125
184
  env->DeleteLocalRef(bitmap);
126
185
 
127
- ImageData result;
128
- result.width = static_cast<int>(bitmapInfo.width);
129
- result.height = static_cast<int>(bitmapInfo.height);
130
- result.data = imageData;
131
186
  return result;
132
187
  }
133
188
 
134
- void createImageBitmapAsync(
135
- std::string blobId, double offset, double size,
136
- std::function<void(ImageData)> onSuccess,
189
+ void createImageBitmapFromDataAsync(
190
+ std::span<const uint8_t> data, std::function<void(ImageData)> onSuccess,
137
191
  std::function<void(std::string)> onError) override {
138
- // Capture blobModule for the background thread
139
- jobject blobModule = _blobModule;
140
-
141
- // Dispatch to a background thread
142
- std::thread([blobModule, blobId = std::move(blobId), offset, size,
192
+ std::thread([this, ownedData = std::vector<uint8_t>(data.begin(), data.end()),
143
193
  onSuccess = std::move(onSuccess),
144
- onError = std::move(onError)]() {
194
+ onError = std::move(onError)]() mutable {
145
195
  jni::Environment::ensureCurrentThreadIsAttached();
146
-
147
- JNIEnv *env = facebook::jni::Environment::current();
148
- if (!env) {
149
- onError("Couldn't get JNI environment");
150
- return;
151
- }
152
-
153
- if (!blobModule) {
154
- onError("BlobModule instance is null");
155
- return;
156
- }
157
-
158
- // Get the resolve method ID
159
- jclass blobModuleClass = env->GetObjectClass(blobModule);
160
- if (!blobModuleClass) {
161
- onError("Couldn't find BlobModule class");
162
- return;
163
- }
164
-
165
- jmethodID resolveMethod = env->GetMethodID(blobModuleClass, "resolve",
166
- "(Ljava/lang/String;II)[B");
167
- if (!resolveMethod) {
168
- onError("Couldn't find resolve method in BlobModule");
169
- return;
170
- }
171
-
172
- // Resolve the blob data
173
- jstring jBlobId = env->NewStringUTF(blobId.c_str());
174
- jbyteArray blobData = (jbyteArray)env->CallObjectMethod(
175
- blobModule, resolveMethod, jBlobId, static_cast<jint>(offset),
176
- static_cast<jint>(size));
177
- env->DeleteLocalRef(jBlobId);
178
-
179
- if (!blobData) {
180
- onError("Couldn't retrieve blob data");
181
- return;
196
+ try {
197
+ auto result = createImageBitmapFromData(ownedData);
198
+ onSuccess(std::move(result));
199
+ } catch (const std::exception &e) {
200
+ onError(e.what());
182
201
  }
183
-
184
- // Create a Bitmap from the blob data
185
- jclass bitmapFactoryClass =
186
- env->FindClass("android/graphics/BitmapFactory");
187
- jmethodID decodeByteArrayMethod =
188
- env->GetStaticMethodID(bitmapFactoryClass, "decodeByteArray",
189
- "([BII)Landroid/graphics/Bitmap;");
190
- jint blobLength = env->GetArrayLength(blobData);
191
- jobject bitmap = env->CallStaticObjectMethod(
192
- bitmapFactoryClass, decodeByteArrayMethod, blobData, 0, blobLength);
193
-
194
- if (!bitmap) {
195
- env->DeleteLocalRef(blobData);
196
- onError("Couldn't decode image");
197
- return;
198
- }
199
-
200
- // Get bitmap info
201
- AndroidBitmapInfo bitmapInfo;
202
- if (AndroidBitmap_getInfo(env, bitmap, &bitmapInfo) !=
203
- ANDROID_BITMAP_RESULT_SUCCESS) {
204
- env->DeleteLocalRef(blobData);
205
- env->DeleteLocalRef(bitmap);
206
- onError("Couldn't get bitmap info");
207
- return;
208
- }
209
-
210
- // Lock the bitmap pixels
211
- void *bitmapPixels;
212
- if (AndroidBitmap_lockPixels(env, bitmap, &bitmapPixels) !=
213
- ANDROID_BITMAP_RESULT_SUCCESS) {
214
- env->DeleteLocalRef(blobData);
215
- env->DeleteLocalRef(bitmap);
216
- onError("Couldn't lock bitmap pixels");
217
- return;
218
- }
219
-
220
- // Copy the bitmap data
221
- std::vector<uint8_t> imageData(bitmapInfo.height * bitmapInfo.stride);
222
- memcpy(imageData.data(), bitmapPixels, imageData.size());
223
-
224
- // Unlock the bitmap pixels
225
- AndroidBitmap_unlockPixels(env, bitmap);
226
-
227
- // Clean up JNI references
228
- env->DeleteLocalRef(blobData);
229
- env->DeleteLocalRef(bitmap);
230
-
231
- ImageData result;
232
- result.width = static_cast<int>(bitmapInfo.width);
233
- result.height = static_cast<int>(bitmapInfo.height);
234
- result.data = std::move(imageData);
235
-
236
- onSuccess(std::move(result));
237
202
  }).detach();
238
203
  }
239
204
  };
@@ -20,6 +20,12 @@ public:
20
20
  std::string blobId, double offset, double size,
21
21
  std::function<void(ImageData)> onSuccess,
22
22
  std::function<void(std::string)> onError) override;
23
+
24
+ ImageData createImageBitmapFromData(std::span<const uint8_t> data) override;
25
+
26
+ void createImageBitmapFromDataAsync(
27
+ std::span<const uint8_t> data, std::function<void(ImageData)> onSuccess,
28
+ std::function<void(std::string)> onError) override;
23
29
  };
24
30
 
25
31
  } // namespace rnwgpu
@@ -39,6 +39,10 @@ wgpu::Surface ApplePlatformContext::makeSurface(wgpu::Instance instance,
39
39
  return instance.CreateSurface(&surfaceDescriptor);
40
40
  }
41
41
 
42
+ static std::span<const uint8_t> nsDataToSpan(NSData *data) {
43
+ return {static_cast<const uint8_t *>(data.bytes), data.length};
44
+ }
45
+
42
46
  ImageData ApplePlatformContext::createImageBitmap(std::string blobId,
43
47
  double offset, double size) {
44
48
  RCTBlobManager *blobManager =
@@ -49,13 +53,51 @@ ImageData ApplePlatformContext::createImageBitmap(std::string blobId,
49
53
  size:(long)size];
50
54
 
51
55
  if (!blobData) {
52
- throw std::runtime_error("Couldn't retrive blob data");
56
+ throw std::runtime_error("Couldn't retrieve blob data");
57
+ }
58
+
59
+ return createImageBitmapFromData(nsDataToSpan(blobData));
60
+ }
61
+
62
+ void ApplePlatformContext::createImageBitmapAsync(
63
+ std::string blobId, double offset, double size,
64
+ std::function<void(ImageData)> onSuccess,
65
+ std::function<void(std::string)> onError) {
66
+ // Resolve blob on current thread (requires RCTBridge access)
67
+ RCTBlobManager *blobManager =
68
+ [[RCTBridge currentBridge] moduleForClass:RCTBlobManager.class];
69
+ NSData *blobData =
70
+ [blobManager resolve:[NSString stringWithUTF8String:blobId.c_str()]
71
+ offset:(long)offset
72
+ size:(long)size];
73
+
74
+ if (!blobData) {
75
+ onError("Couldn't retrieve blob data");
76
+ return;
53
77
  }
54
78
 
79
+ // blobData is alive during this synchronous call;
80
+ // createImageBitmapFromDataAsync copies the span before dispatching
81
+ createImageBitmapFromDataAsync(nsDataToSpan(blobData), std::move(onSuccess),
82
+ std::move(onError));
83
+ }
84
+
85
+ ImageData ApplePlatformContext::createImageBitmapFromData(
86
+ std::span<const uint8_t> data) {
87
+ // This avoids a copy by assuming the UIImage/NSImage constructors
88
+ // decode `nsData` eagerly before the memory for the wrapped `data`
89
+ // is freed.
90
+ //
91
+ // Since we get the `CGImageRef` from `image` and then throw
92
+ // it away, that's a fairly safe assumption.
93
+ NSData *nsData = [NSData dataWithBytesNoCopy:const_cast<uint8_t *>(data.data())
94
+ length:data.size()
95
+ freeWhenDone:NO];
96
+
55
97
  #if !TARGET_OS_OSX
56
- UIImage *image = [UIImage imageWithData:blobData];
98
+ UIImage *image = [UIImage imageWithData:nsData];
57
99
  #else
58
- NSImage *image = [[NSImage alloc] initWithData:blobData];
100
+ NSImage *image = [[NSImage alloc] initWithData:nsData];
59
101
  #endif
60
102
  if (!image) {
61
103
  throw std::runtime_error("Couldn't decode image");
@@ -72,94 +114,42 @@ ImageData ApplePlatformContext::createImageBitmap(std::string blobId,
72
114
  size_t height = CGImageGetHeight(cgImage);
73
115
  size_t bitsPerComponent = 8;
74
116
  size_t bytesPerRow = width * 4;
75
- std::vector<uint8_t> imageData(height * bytesPerRow);
117
+
118
+ ImageData result;
119
+ result.width = static_cast<int>(width);
120
+ result.height = static_cast<int>(height);
121
+ result.data.resize(height * bytesPerRow);
122
+ result.format = wgpu::TextureFormat::RGBA8Unorm;
76
123
 
77
124
  CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
78
125
  CGContextRef context = CGBitmapContextCreate(
79
- imageData.data(), width, height, bitsPerComponent, bytesPerRow,
126
+ result.data.data(), width, height, bitsPerComponent, bytesPerRow,
80
127
  colorSpace, kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);
81
128
 
82
129
  CGContextDrawImage(context, CGRectMake(0, 0, width, height), cgImage);
83
130
 
84
- // Now imageData contains a copy of the bitmap data
85
-
86
131
  CGContextRelease(context);
87
132
  CGColorSpaceRelease(colorSpace);
88
133
 
89
- // Use the copied data
90
- ImageData result;
91
- result.width = static_cast<int>(width);
92
- result.height = static_cast<int>(height);
93
- result.data = imageData;
94
- result.format = wgpu::TextureFormat::RGBA8Unorm;
95
134
  return result;
96
135
  }
97
136
 
98
- void ApplePlatformContext::createImageBitmapAsync(
99
- std::string blobId, double offset, double size,
100
- std::function<void(ImageData)> onSuccess,
137
+ void ApplePlatformContext::createImageBitmapFromDataAsync(
138
+ std::span<const uint8_t> data, std::function<void(ImageData)> onSuccess,
101
139
  std::function<void(std::string)> onError) {
102
- // Capture blob data on the current thread (requires RCTBridge access)
103
- RCTBlobManager *blobManager =
104
- [[RCTBridge currentBridge] moduleForClass:RCTBlobManager.class];
105
- NSData *blobData =
106
- [blobManager resolve:[NSString stringWithUTF8String:blobId.c_str()]
107
- offset:(long)offset
108
- size:(long)size];
140
+ // Copy span data into shared_ptr so the dispatch_async block owns the memory
141
+ auto ownedData =
142
+ std::make_shared<std::vector<uint8_t>>(data.begin(), data.end());
109
143
 
110
- if (!blobData) {
111
- onError("Couldn't retrieve blob data");
112
- return;
113
- }
114
-
115
- // Retain the data for the background block
116
- NSData *retainedData = [blobData copy];
117
-
118
- // Dispatch heavy image decoding work to a background queue
119
144
  dispatch_async(
120
145
  dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0), ^{
121
146
  @autoreleasepool {
122
- #if !TARGET_OS_OSX
123
- UIImage *image = [UIImage imageWithData:retainedData];
124
- #else
125
- NSImage *image = [[NSImage alloc] initWithData:retainedData];
126
- #endif
127
- if (!image) {
128
- onError("Couldn't decode image");
129
- return;
147
+ try {
148
+ auto result = createImageBitmapFromData(*ownedData);
149
+ onSuccess(std::move(result));
150
+ } catch (const std::exception &e) {
151
+ onError(e.what());
130
152
  }
131
-
132
- #if !TARGET_OS_OSX
133
- CGImageRef cgImage = image.CGImage;
134
- #else
135
- CGImageRef cgImage = [image CGImageForProposedRect:NULL
136
- context:NULL
137
- hints:NULL];
138
- #endif
139
- size_t width = CGImageGetWidth(cgImage);
140
- size_t height = CGImageGetHeight(cgImage);
141
- size_t bitsPerComponent = 8;
142
- size_t bytesPerRow = width * 4;
143
- std::vector<uint8_t> imageData(height * bytesPerRow);
144
-
145
- CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
146
- CGContextRef context = CGBitmapContextCreate(
147
- imageData.data(), width, height, bitsPerComponent, bytesPerRow,
148
- colorSpace,
149
- kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);
150
-
151
- CGContextDrawImage(context, CGRectMake(0, 0, width, height), cgImage);
152
-
153
- CGContextRelease(context);
154
- CGColorSpaceRelease(colorSpace);
155
-
156
- ImageData result;
157
- result.width = static_cast<int>(width);
158
- result.height = static_cast<int>(height);
159
- result.data = std::move(imageData);
160
- result.format = wgpu::TextureFormat::RGBA8Unorm;
161
-
162
- onSuccess(std::move(result));
163
153
  }
164
154
  });
165
155
  }
@@ -2,6 +2,7 @@
2
2
 
3
3
  #include <functional>
4
4
  #include <memory>
5
+ #include <span>
5
6
  #include <string>
6
7
  #include <vector>
7
8
 
@@ -31,6 +32,13 @@ public:
31
32
  std::string blobId, double offset, double size,
32
33
  std::function<void(ImageData)> onSuccess,
33
34
  std::function<void(std::string)> onError) = 0;
35
+
36
+ // Create ImageBitmap from raw encoded image bytes (PNG/JPEG/etc.)
37
+ virtual ImageData createImageBitmapFromData(std::span<const uint8_t> data) = 0;
38
+
39
+ virtual void createImageBitmapFromDataAsync(
40
+ std::span<const uint8_t> data, std::function<void(ImageData)> onSuccess,
41
+ std::function<void(std::string)> onError) = 0;
34
42
  };
35
43
 
36
44
  } // namespace rnwgpu
@@ -79,13 +79,73 @@ public:
79
79
  const jsi::Value & /*thisVal*/,
80
80
  const jsi::Value *args, size_t count) {
81
81
  if (count < 1) {
82
- throw jsi::JSError(runtime, "createImageBitmap requires a Blob argument");
82
+ throw jsi::JSError(runtime,
83
+ "createImageBitmap requires a Blob or ArrayBuffer "
84
+ "argument");
83
85
  }
84
86
 
85
- auto blob =
86
- JSIConverter<std::shared_ptr<Blob>>::fromJSI(runtime, args[0], false);
87
87
  auto platformContext = _platformContext;
88
88
  auto callInvoker = _callInvoker;
89
+
90
+ // Check if the argument is an ArrayBuffer or ArrayBufferView
91
+ // (TypedArray / DataView)
92
+ if (args[0].isObject()) {
93
+ auto obj = args[0].getObject(runtime);
94
+
95
+ std::span<const uint8_t> data;
96
+
97
+ if (obj.isArrayBuffer(runtime)) {
98
+ // Plain ArrayBuffer — use the full buffer
99
+ const auto &ab = obj.getArrayBuffer(runtime);
100
+ data = {ab.data(runtime), ab.size(runtime)};
101
+ } else if (obj.hasProperty(runtime, "buffer")) {
102
+ // TypedArray or DataView — respect byteOffset/byteLength
103
+ auto bufferVal = obj.getProperty(runtime, "buffer");
104
+ if (bufferVal.isObject() &&
105
+ bufferVal.getObject(runtime).isArrayBuffer(runtime)) {
106
+ const auto &ab =
107
+ bufferVal.getObject(runtime).getArrayBuffer(runtime);
108
+ auto byteOffset = static_cast<size_t>(
109
+ obj.getProperty(runtime, "byteOffset").asNumber());
110
+ auto byteLength = static_cast<size_t>(
111
+ obj.getProperty(runtime, "byteLength").asNumber());
112
+ data = {ab.data(runtime) + byteOffset, byteLength};
113
+ }
114
+ }
115
+
116
+ if (!data.empty()) {
117
+ // Copy bytes on the JS thread — the ArrayBuffer pointer is into
118
+ // JS-owned memory that can be GC'd
119
+ std::vector<uint8_t> dataCopy(data.begin(), data.end());
120
+
121
+ return Promise::createPromise(
122
+ runtime,
123
+ [platformContext, callInvoker,
124
+ dataCopy = std::move(dataCopy)](
125
+ jsi::Runtime & /*runtime*/,
126
+ std::shared_ptr<Promise> promise) mutable {
127
+ platformContext->createImageBitmapFromDataAsync(
128
+ dataCopy,
129
+ [callInvoker, promise](ImageData imageData) {
130
+ auto imageBitmap =
131
+ std::make_shared<ImageBitmap>(imageData);
132
+ callInvoker->invokeAsync([promise, imageBitmap]() {
133
+ promise->resolve(
134
+ JSIConverter<std::shared_ptr<ImageBitmap>>::toJSI(
135
+ promise->runtime, imageBitmap));
136
+ });
137
+ },
138
+ [callInvoker, promise](std::string error) {
139
+ callInvoker->invokeAsync(
140
+ [promise, error]() { promise->reject(error); });
141
+ });
142
+ });
143
+ }
144
+ }
145
+
146
+ // Fall through to existing Blob path
147
+ auto blob =
148
+ JSIConverter<std::shared_ptr<Blob>>::fromJSI(runtime, args[0], false);
89
149
  std::string blobId = blob->blobId;
90
150
  double offset = blob->offset;
91
151
  double size = blob->size;
@@ -13,5 +13,6 @@ declare global {
13
13
  DecodeToUTF8: (buffer: NodeJS.ArrayBufferView | ArrayBuffer) => string;
14
14
  createImageBitmap: typeof createImageBitmap;
15
15
  };
16
+ function createImageBitmap(image: ArrayBuffer | ArrayBufferView): Promise<ImageBitmap>;
16
17
  }
17
18
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAE7D,cAAc,QAAQ,CAAC;AAEvB,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;QACrB,YAAY,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,eAAe,GAAG,WAAW,KAAK,MAAM,CAAC;QACvE,iBAAiB,EAAE,OAAO,iBAAiB,CAAC;KAC7C,CAAC;CACH"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAE7D,cAAc,QAAQ,CAAC;AAEvB,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;QACrB,YAAY,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,eAAe,GAAG,WAAW,KAAK,MAAM,CAAC;QACvE,iBAAiB,EAAE,OAAO,iBAAiB,CAAC;KAC7C,CAAC;IAGF,SAAS,iBAAiB,CACxB,KAAK,EAAE,WAAW,GAAG,eAAe,GACnC,OAAO,CAAC,WAAW,CAAC,CAAC;CACzB"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-wgpu",
3
- "version": "0.5.5",
3
+ "version": "0.5.6",
4
4
  "description": "React Native WebGPU",
5
5
  "main": "lib/commonjs/index",
6
6
  "module": "lib/module/index",
@@ -35,7 +35,7 @@ Pod::Spec.new do |s|
35
35
  s.pod_target_xcconfig = {
36
36
  "HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT)/boost\" \"$(PODS_TARGET_SRCROOT)/cpp\"",
37
37
  "OTHER_CPLUSPLUSFLAGS" => "-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1",
38
- "CLANG_CXX_LANGUAGE_STANDARD" => "c++17"
38
+ "CLANG_CXX_LANGUAGE_STANDARD" => "c++20"
39
39
  }
40
40
  s.dependency "React-RCTFabric"
41
41
  s.dependency "React-Codegen"
package/src/index.tsx CHANGED
@@ -23,4 +23,9 @@ declare global {
23
23
  DecodeToUTF8: (buffer: NodeJS.ArrayBufferView | ArrayBuffer) => string;
24
24
  createImageBitmap: typeof createImageBitmap;
25
25
  };
26
+
27
+ // Extend createImageBitmap to accept ArrayBuffer/TypedArray (encoded image bytes)
28
+ function createImageBitmap(
29
+ image: ArrayBuffer | ArrayBufferView,
30
+ ): Promise<ImageBitmap>;
26
31
  }