react-native-wgpu 0.5.4 → 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.
- package/android/CMakeLists.txt +2 -1
- package/android/build.gradle +1 -1
- package/android/cpp/AndroidPlatformContext.h +104 -139
- package/apple/ApplePlatformContext.h +6 -0
- package/apple/ApplePlatformContext.mm +62 -72
- package/cpp/rnwgpu/PlatformContext.h +8 -0
- package/cpp/rnwgpu/api/RNWebGPU.h +63 -3
- package/cpp/rnwgpu/api/descriptors/GPUBindGroupEntry.h +6 -0
- package/lib/commonjs/external/reanimated/registerWebGPUForReanimated.js +1 -0
- package/lib/commonjs/external/reanimated/registerWebGPUForReanimated.js.map +1 -1
- package/lib/module/external/reanimated/registerWebGPUForReanimated.js +1 -0
- package/lib/module/external/reanimated/registerWebGPUForReanimated.js.map +1 -1
- package/lib/typescript/lib/commonjs/external/reanimated/registerWebGPUForReanimated.d.ts.map +1 -1
- package/lib/typescript/lib/module/external/reanimated/registerWebGPUForReanimated.d.ts.map +1 -1
- package/lib/typescript/src/external/reanimated/registerWebGPUForReanimated.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +1 -0
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/package.json +1 -1
- package/react-native-wgpu.podspec +1 -1
- package/src/external/ModuleProxy.ts +1 -1
- package/src/external/reanimated/registerWebGPUForReanimated.ts +4 -4
- package/src/index.tsx +5 -0
package/android/CMakeLists.txt
CHANGED
package/android/build.gradle
CHANGED
|
@@ -71,7 +71,7 @@ android {
|
|
|
71
71
|
buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", "true"
|
|
72
72
|
externalNativeBuild {
|
|
73
73
|
cmake {
|
|
74
|
-
cppFlags "-fexceptions", "-frtti", "-
|
|
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
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
}
|
|
92
|
+
auto data = resolveBlob(env, blobId, offset, size);
|
|
93
|
+
return createImageBitmapFromData(data);
|
|
94
|
+
}
|
|
59
95
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
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
|
-
|
|
67
|
-
|
|
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
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
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
|
-
|
|
80
|
-
|
|
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
|
-
//
|
|
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
|
-
|
|
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,
|
|
151
|
+
bitmapFactoryClass, decodeByteArrayMethod, byteArray, 0, length);
|
|
152
|
+
env->DeleteLocalRef(bitmapFactoryClass);
|
|
92
153
|
|
|
93
154
|
if (!bitmap) {
|
|
94
|
-
env->DeleteLocalRef(
|
|
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(
|
|
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(
|
|
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
|
-
|
|
117
|
-
|
|
118
|
-
|
|
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
|
-
|
|
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
|
|
135
|
-
std::
|
|
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
|
-
|
|
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
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
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
|
|
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:
|
|
98
|
+
UIImage *image = [UIImage imageWithData:nsData];
|
|
57
99
|
#else
|
|
58
|
-
NSImage *image = [[NSImage alloc] initWithData:
|
|
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
|
-
|
|
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
|
-
|
|
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::
|
|
99
|
-
std::
|
|
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
|
-
//
|
|
103
|
-
|
|
104
|
-
|
|
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
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
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,
|
|
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;
|
|
@@ -46,6 +46,12 @@ template <> struct JSIConverter<std::shared_ptr<rnwgpu::GPUBindGroupEntry>> {
|
|
|
46
46
|
} else if (obj.hasNativeState<rnwgpu::GPUTextureView>(runtime)) {
|
|
47
47
|
result->textureView =
|
|
48
48
|
obj.getNativeState<rnwgpu::GPUTextureView>(runtime);
|
|
49
|
+
} else if (obj.hasNativeState<rnwgpu::GPUBuffer>(runtime)) {
|
|
50
|
+
// Support passing GPUBuffer directly as resource (auto-wrap in
|
|
51
|
+
// GPUBufferBinding)
|
|
52
|
+
auto binding = std::make_shared<rnwgpu::GPUBufferBinding>();
|
|
53
|
+
binding->buffer = obj.getNativeState<rnwgpu::GPUBuffer>(runtime);
|
|
54
|
+
result->buffer = binding;
|
|
49
55
|
} else {
|
|
50
56
|
result->buffer = JSIConverter<
|
|
51
57
|
std::shared_ptr<rnwgpu::GPUBufferBinding>>::fromJSI(runtime,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["isRegistered","registerWebGPUForReanimated","registerCustomSerializable","require","name","determine","value","__webgpuIsWebGPUObject","pack","__webgpuBox","unpack","boxed","unbox","exports"],"sourceRoot":"../../../../src","sources":["external/reanimated/registerWebGPUForReanimated.ts"],"mappings":";;;;;;AAAA;;
|
|
1
|
+
{"version":3,"names":["isRegistered","registerWebGPUForReanimated","registerCustomSerializable","require","name","determine","value","__webgpuIsWebGPUObject","pack","__webgpuBox","unpack","boxed","unbox","exports"],"sourceRoot":"../../../../src","sources":["external/reanimated/registerWebGPUForReanimated.ts"],"mappings":";;;;;;AAAA;;AAMA,IAAIA,YAAY,GAAG,KAAK;;AAExB;AACA;AACA;AACA;AACA;AACA;AACO,MAAMC,2BAA2B,GAAGA,CAAA,KAAM;EAC/C,IAAID,YAAY,EAAE;IAChB;EACF;EACAA,YAAY,GAAG,IAAI;EAEnB,IAAI;IACF;IACA,MAAM;MAAEE;IAA2B,CAAC,GAAGC,OAAO,CAAC,uBAAuB,CAAC;IAEvED,0BAA0B,CAAC;MACzBE,IAAI,EAAE,QAAQ;MACdC,SAAS,EAAGC,KAAa,IAAsB;QAC7C,SAAS;;QACT,OAAOC,sBAAsB,CAACD,KAAK,CAAC;MACtC,CAAC;MACDE,IAAI,EAAGF,KAAa,IAAK;QACvB,SAAS;;QACT,OAAOG,WAAW,CAACH,KAAK,CAAC;MAC3B,CAAC;MACDI,MAAM,EAAGC,KAA8B,IAAK;QAC1C,SAAS;;QACT,OAAOA,KAAK,CAACC,KAAK,CAAC,CAAC;MACtB;IACF,CAAC,CAAC;EACJ,CAAC,CAAC,MAAM;IACN;EAAA;AAEJ,CAAC;AAACC,OAAA,CAAAZ,2BAAA,GAAAA,2BAAA","ignoreList":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["isRegistered","registerWebGPUForReanimated","registerCustomSerializable","require","name","determine","value","__webgpuIsWebGPUObject","pack","__webgpuBox","unpack","boxed","unbox"],"sourceRoot":"../../../../src","sources":["external/reanimated/registerWebGPUForReanimated.ts"],"mappings":"AAAA;;
|
|
1
|
+
{"version":3,"names":["isRegistered","registerWebGPUForReanimated","registerCustomSerializable","require","name","determine","value","__webgpuIsWebGPUObject","pack","__webgpuBox","unpack","boxed","unbox"],"sourceRoot":"../../../../src","sources":["external/reanimated/registerWebGPUForReanimated.ts"],"mappings":"AAAA;;AAMA,IAAIA,YAAY,GAAG,KAAK;;AAExB;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,MAAMC,2BAA2B,GAAGA,CAAA,KAAM;EAC/C,IAAID,YAAY,EAAE;IAChB;EACF;EACAA,YAAY,GAAG,IAAI;EAEnB,IAAI;IACF;IACA,MAAM;MAAEE;IAA2B,CAAC,GAAGC,OAAO,CAAC,uBAAuB,CAAC;IAEvED,0BAA0B,CAAC;MACzBE,IAAI,EAAE,QAAQ;MACdC,SAAS,EAAGC,KAAa,IAAsB;QAC7C,SAAS;;QACT,OAAOC,sBAAsB,CAACD,KAAK,CAAC;MACtC,CAAC;MACDE,IAAI,EAAGF,KAAa,IAAK;QACvB,SAAS;;QACT,OAAOG,WAAW,CAACH,KAAK,CAAC;MAC3B,CAAC;MACDI,MAAM,EAAGC,KAA8B,IAAK;QAC1C,SAAS;;QACT,OAAOA,KAAK,CAACC,KAAK,CAAC,CAAC;MACtB;IACF,CAAC,CAAC;EACJ,CAAC,CAAC,MAAM;IACN;EAAA;AAEJ,CAAC","ignoreList":[]}
|
package/lib/typescript/lib/commonjs/external/reanimated/registerWebGPUForReanimated.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registerWebGPUForReanimated.d.ts","sourceRoot":"","sources":["../../../../../commonjs/external/reanimated/registerWebGPUForReanimated.js"],"names":[],"mappings":";AAUA;;;;;GAKG;AACH,
|
|
1
|
+
{"version":3,"file":"registerWebGPUForReanimated.d.ts","sourceRoot":"","sources":["../../../../../commonjs/external/reanimated/registerWebGPUForReanimated.js"],"names":[],"mappings":";AAUA;;;;;GAKG;AACH,oDA+BC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registerWebGPUForReanimated.d.ts","sourceRoot":"","sources":["../../../../../module/external/reanimated/registerWebGPUForReanimated.js"],"names":[],"mappings":"AAUO,
|
|
1
|
+
{"version":3,"file":"registerWebGPUForReanimated.d.ts","sourceRoot":"","sources":["../../../../../module/external/reanimated/registerWebGPUForReanimated.js"],"names":[],"mappings":"AAUO,oDA+BN"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registerWebGPUForReanimated.d.ts","sourceRoot":"","sources":["../../../../../src/external/reanimated/registerWebGPUForReanimated.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"registerWebGPUForReanimated.d.ts","sourceRoot":"","sources":["../../../../../src/external/reanimated/registerWebGPUForReanimated.ts"],"names":[],"mappings":"AAQA;;;;;GAKG;AACH,eAAO,MAAM,2BAA2B,YA4BvC,CAAC"}
|
|
@@ -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;
|
|
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
|
@@ -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++
|
|
38
|
+
"CLANG_CXX_LANGUAGE_STANDARD" => "c++20"
|
|
39
39
|
}
|
|
40
40
|
s.dependency "React-RCTFabric"
|
|
41
41
|
s.dependency "React-Codegen"
|
|
@@ -6,7 +6,7 @@ type ImportType = ReturnType<typeof require>;
|
|
|
6
6
|
* This is useful for lazily requiring optional dependencies.
|
|
7
7
|
*/
|
|
8
8
|
export const createModuleProxy = <TModule>(
|
|
9
|
-
getModule: () => ImportType
|
|
9
|
+
getModule: () => ImportType
|
|
10
10
|
): TModule => {
|
|
11
11
|
const holder: { module: TModule | undefined } = { module: undefined };
|
|
12
12
|
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
// Declare global WebGPU worklet helper functions (installed by native module)
|
|
2
2
|
declare function __webgpuIsWebGPUObject(obj: unknown): boolean;
|
|
3
|
-
declare function __webgpuBox(
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
};
|
|
3
|
+
declare function __webgpuBox(
|
|
4
|
+
obj: object
|
|
5
|
+
): { unbox: () => object; __boxedWebGPU: true };
|
|
7
6
|
|
|
8
7
|
let isRegistered = false;
|
|
9
8
|
|
|
@@ -20,6 +19,7 @@ export const registerWebGPUForReanimated = () => {
|
|
|
20
19
|
isRegistered = true;
|
|
21
20
|
|
|
22
21
|
try {
|
|
22
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
23
23
|
const { registerCustomSerializable } = require("react-native-worklets");
|
|
24
24
|
|
|
25
25
|
registerCustomSerializable({
|
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
|
}
|