react-native-image-stitcher 0.14.2 → 0.15.1
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/CHANGELOG.md +164 -0
- package/README.md +35 -0
- package/RNImageStitcher.podspec +8 -7
- package/android/build.gradle +0 -16
- package/android/src/main/cpp/CMakeLists.txt +2 -63
- package/android/src/main/cpp/image_stitcher_jni.cpp +14 -0
- package/android/src/main/cpp/keyframe_gate_jni.cpp +13 -0
- package/android/src/main/java/io/imagestitcher/rn/BatchStitcher.kt +285 -3
- package/android/src/main/java/io/imagestitcher/rn/IncrementalStitcher.kt +180 -1162
- package/android/src/main/java/io/imagestitcher/rn/KeyframeGate.kt +29 -0
- package/android/src/main/java/io/imagestitcher/rn/RNImageStitcherPackage.kt +0 -4
- package/android/src/main/java/io/imagestitcher/rn/RNSARCameraView.kt +129 -71
- package/android/src/main/java/io/imagestitcher/rn/RNSARSession.kt +49 -0
- package/cpp/keyframe_gate.cpp +82 -23
- package/cpp/keyframe_gate.hpp +31 -2
- package/cpp/stitcher.cpp +208 -28
- package/cpp/tests/CMakeLists.txt +18 -12
- package/cpp/tests/keyframe_timebudget_test.cpp +65 -0
- package/cpp/tests/warp_guard_test.cpp +48 -0
- package/cpp/warp_guard.hpp +41 -0
- package/dist/camera/Camera.d.ts +31 -16
- package/dist/camera/Camera.js +11 -3
- package/dist/camera/CameraView.js +93 -3
- package/dist/camera/CaptureStitchStatsToast.d.ts +15 -2
- package/dist/camera/CaptureStitchStatsToast.js +27 -7
- package/dist/camera/PanoramaSettings.d.ts +10 -223
- package/dist/camera/PanoramaSettings.js +6 -28
- package/dist/camera/PanoramaSettingsBridge.d.ts +1 -24
- package/dist/camera/PanoramaSettingsBridge.js +3 -102
- package/dist/camera/PanoramaSettingsModal.js +7 -1
- package/dist/camera/buildPanoramaInitialSettings.d.ts +11 -0
- package/dist/camera/buildPanoramaInitialSettings.js +4 -0
- package/dist/camera/cameraErrorMessages.d.ts +32 -0
- package/dist/camera/cameraErrorMessages.js +53 -0
- package/dist/camera/selectCaptureDevice.d.ts +5 -1
- package/dist/camera/selectCaptureDevice.js +22 -2
- package/dist/camera/useCapture.js +38 -0
- package/dist/index.d.ts +5 -8
- package/dist/index.js +11 -34
- package/dist/stitching/incremental.d.ts +1 -117
- package/dist/stitching/stitchVideo.d.ts +0 -35
- package/dist/types.d.ts +0 -87
- package/ios/Sources/RNImageStitcher/IncrementalStitcher.swift +96 -674
- package/ios/Sources/RNImageStitcher/IncrementalStitcherBridge.swift +9 -12
- package/ios/Sources/RNImageStitcher/KeyframeGate.swift +14 -0
- package/ios/Sources/RNImageStitcher/KeyframeGateBridge.h +7 -0
- package/ios/Sources/RNImageStitcher/KeyframeGateBridge.mm +6 -0
- package/ios/Sources/RNImageStitcher/OpenCVKeyframeCollector.h +2 -2
- package/ios/Sources/RNImageStitcher/OpenCVKeyframeCollector.mm +3 -3
- package/ios/Sources/RNImageStitcher/OpenCVStitcher.h +28 -60
- package/ios/Sources/RNImageStitcher/OpenCVStitcher.mm +180 -921
- package/ios/Sources/RNImageStitcher/RNSARCameraView.swift +82 -7
- package/ios/Sources/RNImageStitcher/RNSARSession.swift +10 -35
- package/ios/Sources/RNImageStitcher/Stitcher.swift +84 -35
- package/ios/Sources/RNImageStitcher/StitcherBridge.m +13 -0
- package/ios/Sources/RNImageStitcher/StitcherBridge.swift +132 -5
- package/package.json +3 -2
- package/src/camera/Camera.tsx +44 -23
- package/src/camera/CameraView.tsx +113 -4
- package/src/camera/CaptureStitchStatsToast.tsx +58 -14
- package/src/camera/PanoramaSettings.ts +16 -289
- package/src/camera/PanoramaSettingsBridge.ts +3 -114
- package/src/camera/PanoramaSettingsModal.tsx +14 -1
- package/src/camera/__tests__/PanoramaSettingsBridge.test.ts +3 -188
- package/src/camera/__tests__/buildPanoramaInitialSettings.test.ts +41 -0
- package/src/camera/__tests__/cameraErrorMessages.test.ts +76 -0
- package/src/camera/__tests__/selectCaptureDevice.test.ts +33 -0
- package/src/camera/buildPanoramaInitialSettings.ts +17 -0
- package/src/camera/cameraErrorMessages.ts +84 -0
- package/src/camera/selectCaptureDevice.ts +28 -3
- package/src/camera/useCapture.ts +44 -1
- package/src/index.ts +11 -40
- package/src/stitching/incremental.ts +3 -140
- package/src/stitching/stitchVideo.ts +0 -26
- package/src/types.ts +0 -95
- package/android/src/main/cpp/stitcher_jsi_install_jni.cpp +0 -227
- package/android/src/main/java/io/imagestitcher/rn/IncrementalFirstwinsEngine.kt +0 -1081
- package/android/src/main/java/io/imagestitcher/rn/StitcherJsiInstallerModule.kt +0 -103
- package/android/src/main/java/io/imagestitcher/rn/StitcherWorkletRuntime.kt +0 -256
- package/cpp/stitcher_frame_jsi.cpp +0 -214
- package/cpp/stitcher_frame_jsi.hpp +0 -108
- package/cpp/stitcher_proxy_jsi.cpp +0 -109
- package/cpp/stitcher_proxy_jsi.hpp +0 -46
- package/cpp/stitcher_worklet_dispatch.cpp +0 -103
- package/cpp/stitcher_worklet_dispatch.hpp +0 -71
- package/cpp/stitcher_worklet_registry.cpp +0 -91
- package/cpp/stitcher_worklet_registry.hpp +0 -146
- package/cpp/tests/stitcher_worklet_registry_test.cpp +0 -195
- package/dist/stitching/IncrementalStitcherView.d.ts +0 -41
- package/dist/stitching/IncrementalStitcherView.js +0 -157
- package/dist/stitching/StitcherWorkletRegistry.d.ts +0 -117
- package/dist/stitching/StitcherWorkletRegistry.js +0 -78
- package/dist/stitching/ensureStitcherProxyInstalled.d.ts +0 -8
- package/dist/stitching/ensureStitcherProxyInstalled.js +0 -81
- package/dist/stitching/useFrameProcessor.d.ts +0 -119
- package/dist/stitching/useFrameProcessor.js +0 -196
- package/dist/stitching/useFrameStream.d.ts +0 -34
- package/dist/stitching/useFrameStream.js +0 -234
- package/dist/stitching/useThrottledFrameProcessor.d.ts +0 -33
- package/dist/stitching/useThrottledFrameProcessor.js +0 -132
- package/ios/Sources/RNImageStitcher/OpenCVIncrementalStitcher.h +0 -474
- package/ios/Sources/RNImageStitcher/OpenCVIncrementalStitcher.mm +0 -1328
- package/ios/Sources/RNImageStitcher/OpenCVSlitScanStitcher.h +0 -103
- package/ios/Sources/RNImageStitcher/OpenCVSlitScanStitcher.mm +0 -3285
- package/ios/Sources/RNImageStitcher/RNSARWorkletRuntime.h +0 -128
- package/ios/Sources/RNImageStitcher/RNSARWorkletRuntime.mm +0 -313
- package/ios/Sources/RNImageStitcher/SaveFrameAsJpegPlugin.mm +0 -185
- package/ios/Sources/RNImageStitcher/StitcherFrameHostObject.h +0 -60
- package/ios/Sources/RNImageStitcher/StitcherFrameHostObject.mm +0 -214
- package/ios/Sources/RNImageStitcher/StitcherJsiInstaller.h +0 -42
- package/ios/Sources/RNImageStitcher/StitcherJsiInstaller.mm +0 -160
- package/src/stitching/IncrementalStitcherView.tsx +0 -198
- package/src/stitching/StitcherWorkletRegistry.ts +0 -156
- package/src/stitching/__tests__/StitcherWorkletRegistry.test.ts +0 -176
- package/src/stitching/__tests__/ensureStitcherProxyInstalled.test.ts +0 -94
- package/src/stitching/__tests__/useThrottledFrameProcessor.test.ts +0 -178
- package/src/stitching/ensureStitcherProxyInstalled.ts +0 -141
- package/src/stitching/useFrameProcessor.ts +0 -226
- package/src/stitching/useFrameStream.ts +0 -271
- package/src/stitching/useThrottledFrameProcessor.ts +0 -145
|
@@ -1,109 +0,0 @@
|
|
|
1
|
-
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
-
//
|
|
3
|
-
// stitcher_proxy_jsi.cpp — shared C++ JSI host object that exposes
|
|
4
|
-
// the v0.8.0 `__stitcherProxy` API to JS. See header for the
|
|
5
|
-
// surface + threading rules.
|
|
6
|
-
|
|
7
|
-
#include "stitcher_proxy_jsi.hpp"
|
|
8
|
-
|
|
9
|
-
#include "stitcher_worklet_registry.hpp"
|
|
10
|
-
|
|
11
|
-
#include <memory>
|
|
12
|
-
#include <string>
|
|
13
|
-
#include <vector>
|
|
14
|
-
|
|
15
|
-
namespace retailens {
|
|
16
|
-
|
|
17
|
-
namespace {
|
|
18
|
-
|
|
19
|
-
class StitcherProxyHostObject : public facebook::jsi::HostObject {
|
|
20
|
-
public:
|
|
21
|
-
facebook::jsi::Value get(facebook::jsi::Runtime& rt,
|
|
22
|
-
const facebook::jsi::PropNameID& propName) override {
|
|
23
|
-
using facebook::jsi::Function;
|
|
24
|
-
using facebook::jsi::JSError;
|
|
25
|
-
using facebook::jsi::PropNameID;
|
|
26
|
-
using facebook::jsi::String;
|
|
27
|
-
using facebook::jsi::Value;
|
|
28
|
-
|
|
29
|
-
const std::string name = propName.utf8(rt);
|
|
30
|
-
|
|
31
|
-
if (name == "install") {
|
|
32
|
-
// install(workletFn) → string ID. The host function captures
|
|
33
|
-
// nothing; the registry is a process-scope singleton.
|
|
34
|
-
auto fn = [](facebook::jsi::Runtime& runtime,
|
|
35
|
-
const Value& /*thisVal*/, const Value* args,
|
|
36
|
-
size_t count) -> Value {
|
|
37
|
-
if (count < 1) {
|
|
38
|
-
throw JSError(runtime,
|
|
39
|
-
"[StitcherProxy] install() requires 1 argument (worklet "
|
|
40
|
-
"function); got 0");
|
|
41
|
-
}
|
|
42
|
-
if (!args[0].isObject() ||
|
|
43
|
-
!args[0].getObject(runtime).isFunction(runtime)) {
|
|
44
|
-
throw JSError(runtime,
|
|
45
|
-
"[StitcherProxy] install() argument must be a function "
|
|
46
|
-
"decorated with 'worklet'");
|
|
47
|
-
}
|
|
48
|
-
// The WorkletInvoker ctor extracts the worklet metadata
|
|
49
|
-
// (`__workletHash` etc.) and throws if absent. Propagate
|
|
50
|
-
// to JS so misuse fails loudly.
|
|
51
|
-
std::string id =
|
|
52
|
-
StitcherWorkletRegistry::shared().install(runtime, args[0]);
|
|
53
|
-
return String::createFromUtf8(runtime, id);
|
|
54
|
-
};
|
|
55
|
-
return Function::createFromHostFunction(
|
|
56
|
-
rt, PropNameID::forUtf8(rt, "install"), 1, std::move(fn));
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
if (name == "uninstall") {
|
|
60
|
-
auto fn = [](facebook::jsi::Runtime& runtime,
|
|
61
|
-
const Value& /*thisVal*/, const Value* args,
|
|
62
|
-
size_t count) -> Value {
|
|
63
|
-
if (count < 1 || !args[0].isString()) {
|
|
64
|
-
// No throw — match the JS-side registry's permissive
|
|
65
|
-
// uninstall semantics; missing/bad ID is a no-op.
|
|
66
|
-
return Value::undefined();
|
|
67
|
-
}
|
|
68
|
-
std::string id = args[0].getString(runtime).utf8(runtime);
|
|
69
|
-
StitcherWorkletRegistry::shared().uninstall(id);
|
|
70
|
-
return Value::undefined();
|
|
71
|
-
};
|
|
72
|
-
return Function::createFromHostFunction(
|
|
73
|
-
rt, PropNameID::forUtf8(rt, "uninstall"), 1, std::move(fn));
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
if (name == "count") {
|
|
77
|
-
auto fn = [](facebook::jsi::Runtime& runtime,
|
|
78
|
-
const Value& /*thisVal*/, const Value* /*args*/,
|
|
79
|
-
size_t /*count*/) -> Value {
|
|
80
|
-
return Value(static_cast<double>(
|
|
81
|
-
StitcherWorkletRegistry::shared().count()));
|
|
82
|
-
};
|
|
83
|
-
return Function::createFromHostFunction(
|
|
84
|
-
rt, PropNameID::forUtf8(rt, "count"), 0, std::move(fn));
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
return Value::undefined();
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
std::vector<facebook::jsi::PropNameID> getPropertyNames(
|
|
91
|
-
facebook::jsi::Runtime& rt) override {
|
|
92
|
-
std::vector<facebook::jsi::PropNameID> names;
|
|
93
|
-
names.push_back(facebook::jsi::PropNameID::forUtf8(rt, "install"));
|
|
94
|
-
names.push_back(facebook::jsi::PropNameID::forUtf8(rt, "uninstall"));
|
|
95
|
-
names.push_back(facebook::jsi::PropNameID::forUtf8(rt, "count"));
|
|
96
|
-
return names;
|
|
97
|
-
}
|
|
98
|
-
};
|
|
99
|
-
|
|
100
|
-
} // namespace
|
|
101
|
-
|
|
102
|
-
void installStitcherProxy(facebook::jsi::Runtime& runtime) {
|
|
103
|
-
auto proxy = std::make_shared<StitcherProxyHostObject>();
|
|
104
|
-
runtime.global().setProperty(
|
|
105
|
-
runtime, "__stitcherProxy",
|
|
106
|
-
facebook::jsi::Object::createFromHostObject(runtime, proxy));
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
} // namespace retailens
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
-
//
|
|
3
|
-
// stitcher_proxy_jsi.hpp — shared C++ JSI host object that's exposed
|
|
4
|
-
// as `globalThis.__stitcherProxy` on the main JS runtime.
|
|
5
|
-
//
|
|
6
|
-
// Originally inlined as an anonymous-namespace class in iOS'
|
|
7
|
-
// `StitcherJsiInstaller.mm` (v0.8.0 Phase 4b.i). Phase 4b.ii lifts
|
|
8
|
-
// it into shared C++ so the Android JNI installer reuses the same
|
|
9
|
-
// `install` / `uninstall` / `count` host functions verbatim — the
|
|
10
|
-
// JSI dispatch is identical across platforms (matches the
|
|
11
|
-
// `StitcherFrame` host object's design).
|
|
12
|
-
//
|
|
13
|
-
// Platform-specific code (Obj-C++ on iOS, JNI on Android) only
|
|
14
|
-
// owns the bootstrap: get a handle to the main JS runtime, then
|
|
15
|
-
// call `retailens::installStitcherProxy(runtime)`.
|
|
16
|
-
//
|
|
17
|
-
// ## Surface
|
|
18
|
-
//
|
|
19
|
-
// __stitcherProxy.install(workletFn) → string ID
|
|
20
|
-
// __stitcherProxy.uninstall(id) → undefined
|
|
21
|
-
// __stitcherProxy.count() → number (diagnostic)
|
|
22
|
-
//
|
|
23
|
-
// `install` wraps the worklet into a `RNWorklet::WorkletInvoker`
|
|
24
|
-
// and stores it in the process-scope C++
|
|
25
|
-
// `retailens::StitcherWorkletRegistry`. The AR worklet runtime
|
|
26
|
-
// (iOS' `RNSARWorkletRuntime`, Android's `StitcherWorkletRuntime`)
|
|
27
|
-
// reads from that registry to fan out per-frame invocations.
|
|
28
|
-
|
|
29
|
-
#pragma once
|
|
30
|
-
|
|
31
|
-
#include <jsi/jsi.h>
|
|
32
|
-
|
|
33
|
-
namespace retailens {
|
|
34
|
-
|
|
35
|
-
/// Install `globalThis.__stitcherProxy` on the supplied runtime.
|
|
36
|
-
/// Idempotent — re-installing overwrites the existing global with
|
|
37
|
-
/// a fresh host object; the underlying `StitcherWorkletRegistry`
|
|
38
|
-
/// state is unaffected.
|
|
39
|
-
///
|
|
40
|
-
/// Thread: must be called from a thread that owns `runtime`.
|
|
41
|
-
/// Typically called once at lib bootstrap from a synchronous JS
|
|
42
|
-
/// bridge method (iOS: `RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD`;
|
|
43
|
-
/// Android: `@ReactMethod(isBlockingSynchronousMethod = true)`).
|
|
44
|
-
void installStitcherProxy(facebook::jsi::Runtime& runtime);
|
|
45
|
-
|
|
46
|
-
} // namespace retailens
|
|
@@ -1,103 +0,0 @@
|
|
|
1
|
-
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
-
//
|
|
3
|
-
// stitcher_worklet_dispatch.cpp — implementation of the v0.8.0
|
|
4
|
-
// Phase 4b.iii per-frame fan-out helper. See header for contract.
|
|
5
|
-
|
|
6
|
-
#include "stitcher_worklet_dispatch.hpp"
|
|
7
|
-
|
|
8
|
-
#include "stitcher_frame_jsi.hpp"
|
|
9
|
-
#include "stitcher_worklet_registry.hpp"
|
|
10
|
-
|
|
11
|
-
#include <react-native-worklets-core/WKTJsiWorklet.h>
|
|
12
|
-
#include <react-native-worklets-core/WKTJsiWorkletContext.h>
|
|
13
|
-
|
|
14
|
-
#include <exception>
|
|
15
|
-
#include <memory>
|
|
16
|
-
#include <utility>
|
|
17
|
-
#include <vector>
|
|
18
|
-
|
|
19
|
-
#if defined(__ANDROID__)
|
|
20
|
-
#include <android/log.h>
|
|
21
|
-
#define DISPATCH_LOG_ERROR(...) \
|
|
22
|
-
__android_log_print(ANDROID_LOG_ERROR, "StitcherWorkletDispatch", __VA_ARGS__)
|
|
23
|
-
#else
|
|
24
|
-
// iOS path uses `os_log` from its caller (RNSARWorkletRuntime.mm);
|
|
25
|
-
// this shared helper isn't invoked from iOS in v0.8.0. Fall back
|
|
26
|
-
// to fprintf for any non-Android build that picks this up.
|
|
27
|
-
#include <cstdio>
|
|
28
|
-
#define DISPATCH_LOG_ERROR(...) std::fprintf(stderr, __VA_ARGS__)
|
|
29
|
-
#endif
|
|
30
|
-
|
|
31
|
-
namespace retailens {
|
|
32
|
-
|
|
33
|
-
void dispatchToHostWorklets(RNWorklet::JsiWorkletContext* context,
|
|
34
|
-
StitcherFrameData data) {
|
|
35
|
-
// Fast-path early-exit when no host worklets are registered.
|
|
36
|
-
// The Android caller (`StitcherWorkletRuntime.dispatchToHostWorklets`)
|
|
37
|
-
// already runs in a hot per-frame loop; saving the host-object
|
|
38
|
-
// alloc + dispatch hop on every frame is meaningful — typical
|
|
39
|
-
// first-party-only deployments will hit this path.
|
|
40
|
-
auto invokers = StitcherWorkletRegistry::shared().snapshot();
|
|
41
|
-
if (invokers.empty()) {
|
|
42
|
-
return;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
if (context == nullptr) {
|
|
46
|
-
DISPATCH_LOG_ERROR(
|
|
47
|
-
"dispatchToHostWorklets: context is null; "
|
|
48
|
-
"did Worklets.install() run on the JS side?");
|
|
49
|
-
return;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
// Build the JSI host object on the worklet thread (inside the
|
|
53
|
-
// lambda) so JSI access happens on the target runtime.
|
|
54
|
-
// `StitcherFrameJsiHostObject::create` uses the `make_shared`-via-
|
|
55
|
-
// factory pattern (required by `shared_from_this()` inside the
|
|
56
|
-
// `toArrayBuffer` lambda); see its header.
|
|
57
|
-
//
|
|
58
|
-
// Capture `data` by-move so the StitcherFrameData (including the
|
|
59
|
-
// pixel reader's shared_ptr) lives until the lambda runs.
|
|
60
|
-
// Capture `invokers` by-move as well.
|
|
61
|
-
context->invokeOnWorkletThread(
|
|
62
|
-
[invokers = std::move(invokers), data = std::move(data)](
|
|
63
|
-
RNWorklet::JsiWorkletContext* /*ctx*/,
|
|
64
|
-
facebook::jsi::Runtime& rt) mutable {
|
|
65
|
-
auto hostObj = StitcherFrameJsiHostObject::create(std::move(data));
|
|
66
|
-
facebook::jsi::Object frameJsi =
|
|
67
|
-
facebook::jsi::Object::createFromHostObject(rt, hostObj);
|
|
68
|
-
facebook::jsi::Value frameVal(rt, frameJsi);
|
|
69
|
-
|
|
70
|
-
for (const auto& entry : invokers) {
|
|
71
|
-
if (!entry.invoker) continue;
|
|
72
|
-
try {
|
|
73
|
-
entry.invoker->call(rt, facebook::jsi::Value::undefined(),
|
|
74
|
-
&frameVal, 1);
|
|
75
|
-
} catch (const facebook::jsi::JSError& jsErr) {
|
|
76
|
-
// Per-worklet failure isolation: one host worklet
|
|
77
|
-
// throwing must NOT stop the lib's own path or other
|
|
78
|
-
// host worklets. Log + continue. Same three-level
|
|
79
|
-
// catch hierarchy iOS' `RNSARWorkletRuntime` uses.
|
|
80
|
-
DISPATCH_LOG_ERROR(
|
|
81
|
-
"host worklet '%s' threw JS error: %s",
|
|
82
|
-
entry.id.c_str(), jsErr.what());
|
|
83
|
-
} catch (const std::exception& e) {
|
|
84
|
-
DISPATCH_LOG_ERROR(
|
|
85
|
-
"host worklet '%s' threw native exception: %s",
|
|
86
|
-
entry.id.c_str(), e.what());
|
|
87
|
-
} catch (...) {
|
|
88
|
-
DISPATCH_LOG_ERROR(
|
|
89
|
-
"host worklet '%s' threw unknown exception",
|
|
90
|
-
entry.id.c_str());
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
// Invalidate after all worklets finish. Releases the
|
|
95
|
-
// PixelBufferReader's shared_ptr, which (when its refcount
|
|
96
|
-
// drops to 0) drops the underlying buffer — for Android
|
|
97
|
-
// that's the std::vector of copied NV21 bytes; for iOS it
|
|
98
|
-
// would be the CFBridgingRetain'd ARFrame.
|
|
99
|
-
hostObj->invalidate();
|
|
100
|
-
});
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
} // namespace retailens
|
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
-
//
|
|
3
|
-
// stitcher_worklet_dispatch.hpp — shared C++ helper that fans out a
|
|
4
|
-
// `StitcherFrameData` to every host worklet registered in the
|
|
5
|
-
// process-scope `retailens::StitcherWorkletRegistry`.
|
|
6
|
-
//
|
|
7
|
-
// v0.8.0 Phase 4b.iii — used by Android's per-frame fan-out path
|
|
8
|
-
// (`StitcherWorkletRuntime.dispatchToHostWorklets` → JNI binding →
|
|
9
|
-
// this function). Designed to be platform-neutral so iOS' inline
|
|
10
|
-
// dispatch in `RNSARWorkletRuntime.mm` could refactor onto this
|
|
11
|
-
// helper in a later cleanup pass.
|
|
12
|
-
//
|
|
13
|
-
// ## Threading
|
|
14
|
-
//
|
|
15
|
-
// `dispatchToHostWorklets` is safe to call from ANY thread. It
|
|
16
|
-
// posts a lambda onto `context`'s worklet thread via
|
|
17
|
-
// `JsiWorkletContext::invokeOnWorkletThread`. The caller's thread
|
|
18
|
-
// returns immediately (async); the lambda runs later on the
|
|
19
|
-
// worklet thread.
|
|
20
|
-
//
|
|
21
|
-
// `data` is moved into the lambda — including the
|
|
22
|
-
// `std::shared_ptr<PixelBufferReader>` which owns the pixel bytes.
|
|
23
|
-
// The reader (and any underlying buffer it holds) lives until the
|
|
24
|
-
// dispatch lambda completes + the resulting jsi::Object is GC'd by
|
|
25
|
-
// the worklet runtime.
|
|
26
|
-
|
|
27
|
-
#pragma once
|
|
28
|
-
|
|
29
|
-
#include "stitcher_frame_data.hpp"
|
|
30
|
-
|
|
31
|
-
#include <jsi/jsi.h>
|
|
32
|
-
|
|
33
|
-
namespace RNWorklet {
|
|
34
|
-
class JsiWorkletContext;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
namespace retailens {
|
|
38
|
-
|
|
39
|
-
/// Fan out a `StitcherFrameData` to every registered host worklet.
|
|
40
|
-
///
|
|
41
|
-
/// Behaviour:
|
|
42
|
-
///
|
|
43
|
-
/// 1. Fast-path early-exit when `StitcherWorkletRegistry::shared()`
|
|
44
|
-
/// is empty. No host object is constructed; the caller's
|
|
45
|
-
/// thread returns immediately.
|
|
46
|
-
/// 2. Otherwise, the function snapshots the registry, constructs
|
|
47
|
-
/// a `StitcherFrameJsiHostObject` (deferred until inside the
|
|
48
|
-
/// worklet-thread lambda so JSI access happens on the
|
|
49
|
-
/// target runtime), and dispatches via
|
|
50
|
-
/// `context->invokeOnWorkletThread(...)`.
|
|
51
|
-
/// 3. Each registered `RNWorklet::WorkletInvoker` is called with
|
|
52
|
-
/// the host object as its single argument. Per-worklet failure
|
|
53
|
-
/// isolation: exceptions thrown by one invoker do NOT stop
|
|
54
|
-
/// the next invoker (each call is try/catch'd).
|
|
55
|
-
/// 4. After all invokers return, the host object is invalidated;
|
|
56
|
-
/// its underlying `PixelBufferReader` is released so the
|
|
57
|
-
/// caller-provided buffer (NV21 bytes / CVPixelBuffer / etc.)
|
|
58
|
-
/// can be reclaimed.
|
|
59
|
-
///
|
|
60
|
-
/// @param context Worklet runtime to dispatch on. On Android this
|
|
61
|
-
/// is typically `RNWorklet::JsiWorkletContext::
|
|
62
|
-
/// getDefaultInstance()` (worklets-core's default,
|
|
63
|
-
/// set up by `Worklets.install()`). iOS uses its
|
|
64
|
-
/// own context (`RNSARWorkletRuntime::_ctx`).
|
|
65
|
-
/// MUST be non-null and initialized.
|
|
66
|
-
/// @param data Frame data + pixel reader. Moved into the
|
|
67
|
-
/// worklet-thread lambda.
|
|
68
|
-
void dispatchToHostWorklets(RNWorklet::JsiWorkletContext* context,
|
|
69
|
-
StitcherFrameData data);
|
|
70
|
-
|
|
71
|
-
} // namespace retailens
|
|
@@ -1,91 +0,0 @@
|
|
|
1
|
-
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
-
//
|
|
3
|
-
// stitcher_worklet_registry.cpp — implementation of the v0.8.0
|
|
4
|
-
// Phase 4b native worklet registry. See header for the public
|
|
5
|
-
// contract + threading rules.
|
|
6
|
-
|
|
7
|
-
#include "stitcher_worklet_registry.hpp"
|
|
8
|
-
|
|
9
|
-
// Cross-platform worklets-core header include. On iOS the
|
|
10
|
-
// CocoaPods setup publishes worklets-core headers via
|
|
11
|
-
// `HEADER_SEARCH_PATHS` at the root of `Pods/Headers/Public/`,
|
|
12
|
-
// so `<WKTJsiWorklet.h>` works. On Android the prefab puts
|
|
13
|
-
// headers under a `react-native-worklets-core/` subdirectory of
|
|
14
|
-
// the include path (matches the prefab name). The angled
|
|
15
|
-
// namespace-prefixed include works on BOTH — `<x/y.h>` resolves
|
|
16
|
-
// to `Pods/Headers/Public/x/y.h` on iOS (CocoaPods auto-creates
|
|
17
|
-
// symlinked subdirs per pod) and to `build/headers/.../x/y.h` on
|
|
18
|
-
// Android. Pattern lifted from vc's
|
|
19
|
-
// `node_modules/react-native-vision-camera/android/src/main/cpp/`.
|
|
20
|
-
#include <react-native-worklets-core/WKTJsiWorklet.h>
|
|
21
|
-
|
|
22
|
-
#include <algorithm>
|
|
23
|
-
#include <sstream>
|
|
24
|
-
|
|
25
|
-
namespace retailens {
|
|
26
|
-
|
|
27
|
-
StitcherWorkletRegistry& StitcherWorkletRegistry::shared() {
|
|
28
|
-
static StitcherWorkletRegistry s_instance;
|
|
29
|
-
return s_instance;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
std::string StitcherWorkletRegistry::install(
|
|
33
|
-
facebook::jsi::Runtime& mainRuntime,
|
|
34
|
-
const facebook::jsi::Value& workletValue) {
|
|
35
|
-
// Construct the invoker outside the lock — the WorkletInvoker
|
|
36
|
-
// constructor calls into worklets-core which acquires its own
|
|
37
|
-
// locks; nesting our lock around that would invite a deadlock if
|
|
38
|
-
// worklets-core ever called back into our code synchronously.
|
|
39
|
-
auto invoker = std::make_shared<RNWorklet::WorkletInvoker>(
|
|
40
|
-
mainRuntime, workletValue);
|
|
41
|
-
|
|
42
|
-
std::lock_guard<std::mutex> lock(_mutex);
|
|
43
|
-
std::ostringstream idStream;
|
|
44
|
-
idStream << "host-" << _nextId++;
|
|
45
|
-
std::string id = idStream.str();
|
|
46
|
-
_entries.push_back({id, std::move(invoker)});
|
|
47
|
-
return id;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
void StitcherWorkletRegistry::uninstall(const std::string& id) {
|
|
51
|
-
std::lock_guard<std::mutex> lock(_mutex);
|
|
52
|
-
// erase-remove with a predicate (single-pass O(n) — n is tiny in
|
|
53
|
-
// practice, typically 0-3).
|
|
54
|
-
_entries.erase(
|
|
55
|
-
std::remove_if(_entries.begin(), _entries.end(),
|
|
56
|
-
[&id](const StitcherWorkletEntry& e) {
|
|
57
|
-
return e.id == id;
|
|
58
|
-
}),
|
|
59
|
-
_entries.end());
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
std::vector<StitcherWorkletEntry> StitcherWorkletRegistry::snapshot() {
|
|
63
|
-
std::lock_guard<std::mutex> lock(_mutex);
|
|
64
|
-
// Copy the vector — entries hold shared_ptrs so this is O(n)
|
|
65
|
-
// refcount bumps, no deep copies. Returning by value lets the
|
|
66
|
-
// caller iterate without holding the lock.
|
|
67
|
-
return _entries;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
std::size_t StitcherWorkletRegistry::count() {
|
|
71
|
-
std::lock_guard<std::mutex> lock(_mutex);
|
|
72
|
-
return _entries.size();
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
void StitcherWorkletRegistry::_resetForTests() {
|
|
76
|
-
std::lock_guard<std::mutex> lock(_mutex);
|
|
77
|
-
_entries.clear();
|
|
78
|
-
_nextId = 0;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
std::string StitcherWorkletRegistry::_installEntryForTests(
|
|
82
|
-
std::shared_ptr<RNWorklet::WorkletInvoker> invoker) {
|
|
83
|
-
std::lock_guard<std::mutex> lock(_mutex);
|
|
84
|
-
std::ostringstream idStream;
|
|
85
|
-
idStream << "host-" << _nextId++;
|
|
86
|
-
std::string id = idStream.str();
|
|
87
|
-
_entries.push_back({id, std::move(invoker)});
|
|
88
|
-
return id;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
} // namespace retailens
|
|
@@ -1,146 +0,0 @@
|
|
|
1
|
-
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
-
//
|
|
3
|
-
// stitcher_worklet_registry.hpp — process-scope native registry of
|
|
4
|
-
// host-supplied worklets (v0.8.0 Phase 4b).
|
|
5
|
-
//
|
|
6
|
-
// ## What this is
|
|
7
|
-
//
|
|
8
|
-
// The native-side counterpart to the JS-side `StitcherWorkletRegistry`
|
|
9
|
-
// singleton (`src/stitching/StitcherWorkletRegistry.ts`). When the
|
|
10
|
-
// public `useFrameProcessor` hook is mounted from JS, it calls into a
|
|
11
|
-
// JSI installable (`globalThis.__stitcherProxy.install(workletFn)`)
|
|
12
|
-
// that wraps the worklet's JSI value into a
|
|
13
|
-
// `RNWorklet::WorkletInvoker` and stores it here. Unmount calls
|
|
14
|
-
// `__stitcherProxy.uninstall(id)` which removes the entry.
|
|
15
|
-
//
|
|
16
|
-
// The AR worklet runtime's per-frame dispatch (`RNSARWorkletRuntime::
|
|
17
|
-
// dispatchFrame:pose:` on iOS) reads from this registry to fan out
|
|
18
|
-
// invocations across all registered host worklets. The vc-mode
|
|
19
|
-
// path (non-AR) does NOT touch this registry — vision-camera owns
|
|
20
|
-
// the Frame Processor runtime in that mode and our public hook
|
|
21
|
-
// passes the worklet through to vc unchanged.
|
|
22
|
-
//
|
|
23
|
-
// ## Threading
|
|
24
|
-
//
|
|
25
|
-
// `install` / `uninstall` are called from the main JS thread
|
|
26
|
-
// (`useFrameProcessor`'s `useEffect` body).
|
|
27
|
-
//
|
|
28
|
-
// `snapshot` is called from the AR session callback thread (the
|
|
29
|
-
// caller thread of `RNSARWorkletRuntime::dispatchFrame:pose:`). The
|
|
30
|
-
// snapshot returns shared_ptrs, so even if `uninstall` races on the
|
|
31
|
-
// JS thread between the snapshot and the worklet runtime's
|
|
32
|
-
// invocation, the WorkletInvoker stays alive until the caller drops
|
|
33
|
-
// the shared_ptr. WorkletInvoker itself does NOT need its caller
|
|
34
|
-
// to live on a particular thread — the `call` method takes the
|
|
35
|
-
// target `jsi::Runtime&` as an argument, so callers from any
|
|
36
|
-
// thread can invoke it on any runtime they own.
|
|
37
|
-
//
|
|
38
|
-
// Mutation is serialised through `_mutex` (std::mutex). Reads via
|
|
39
|
-
// `snapshot` lock briefly to copy the entry vector; that's microseconds
|
|
40
|
-
// at most (registry typically has 0-3 entries). No worklet invocation
|
|
41
|
-
// happens under the lock.
|
|
42
|
-
//
|
|
43
|
-
// ## Lifetime
|
|
44
|
-
//
|
|
45
|
-
// The registry is a `static`-local singleton, constructed on first
|
|
46
|
-
// `shared()` call (function-static init = thread-safe per the C++11
|
|
47
|
-
// memory model). It outlives every JS / native runtime in the
|
|
48
|
-
// process. Entries are only added by `install` and only removed by
|
|
49
|
-
// `uninstall` — no GC, no weak refs. Hosts that bypass the
|
|
50
|
-
// `useFrameProcessor` hook and call `install` directly without ever
|
|
51
|
-
// calling `uninstall` leak entries (matches the JS-side singleton's
|
|
52
|
-
// contract).
|
|
53
|
-
//
|
|
54
|
-
// ## Why a singleton (not per-runtime / per-AR-session)
|
|
55
|
-
//
|
|
56
|
-
// The AR worklet runtime (`RNSARWorkletRuntime` / `StitcherWorkletRuntime`)
|
|
57
|
-
// is itself a process-scope singleton. The hook lifecycle is
|
|
58
|
-
// per-component-mount. Registering against a per-AR-session registry
|
|
59
|
-
// would mean re-registering each time AR mode starts — but the host
|
|
60
|
-
// worklet's identity hasn't changed. Process-scope = "registry
|
|
61
|
-
// matches the host's mental model of `useFrameProcessor` semantics".
|
|
62
|
-
|
|
63
|
-
#pragma once
|
|
64
|
-
|
|
65
|
-
#include <jsi/jsi.h>
|
|
66
|
-
|
|
67
|
-
#include <memory>
|
|
68
|
-
#include <mutex>
|
|
69
|
-
#include <string>
|
|
70
|
-
#include <utility>
|
|
71
|
-
#include <vector>
|
|
72
|
-
|
|
73
|
-
// Forward-declare to avoid pulling the whole worklets-core header
|
|
74
|
-
// into every translation unit that just needs to hold an invoker
|
|
75
|
-
// pointer. The .cpp includes the full header.
|
|
76
|
-
namespace RNWorklet {
|
|
77
|
-
class WorkletInvoker;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
namespace retailens {
|
|
81
|
-
|
|
82
|
-
/// One registered host worklet. Public so callers iterating via
|
|
83
|
-
/// `snapshot` can read both the ID and the invoker.
|
|
84
|
-
struct StitcherWorkletEntry {
|
|
85
|
-
std::string id;
|
|
86
|
-
std::shared_ptr<RNWorklet::WorkletInvoker> invoker;
|
|
87
|
-
};
|
|
88
|
-
|
|
89
|
-
class StitcherWorkletRegistry {
|
|
90
|
-
public:
|
|
91
|
-
/// Process-scope singleton. Thread-safe lazy init via C++11
|
|
92
|
-
/// function-static.
|
|
93
|
-
static StitcherWorkletRegistry& shared();
|
|
94
|
-
|
|
95
|
-
/// Install a host worklet. Wraps the JSI value (the worklet's
|
|
96
|
-
/// `'worklet'`-decorated function from the main JS runtime) into
|
|
97
|
-
/// a `WorkletInvoker` and stores it. Returns a stable string ID
|
|
98
|
-
/// the caller passes to `uninstall`.
|
|
99
|
-
///
|
|
100
|
-
/// Thread: must be called from a thread that owns `mainRuntime`
|
|
101
|
-
/// (typically the main JS thread). The wrapped `WorkletInvoker`
|
|
102
|
-
/// can then be invoked from any thread on any runtime via
|
|
103
|
-
/// `call(rt, ...)`.
|
|
104
|
-
std::string install(facebook::jsi::Runtime& mainRuntime,
|
|
105
|
-
const facebook::jsi::Value& workletValue);
|
|
106
|
-
|
|
107
|
-
/// Remove a previously-installed entry by ID. No-op for unknown
|
|
108
|
-
/// IDs (matches the JS-side `StitcherWorkletRegistry` semantics).
|
|
109
|
-
void uninstall(const std::string& id);
|
|
110
|
-
|
|
111
|
-
/// Snapshot the current entries. The returned vector holds
|
|
112
|
-
/// shared_ptrs to `WorkletInvoker`; mutations against the
|
|
113
|
-
/// registry after `snapshot` returns do not affect the snapshot.
|
|
114
|
-
/// Callers iterate without holding the registry lock.
|
|
115
|
-
std::vector<StitcherWorkletEntry> snapshot();
|
|
116
|
-
|
|
117
|
-
/// Current entry count. Used by `dispatchFrame:` for the
|
|
118
|
-
/// fast-path early-exit (no fan-out cost when no host worklets
|
|
119
|
-
/// are registered).
|
|
120
|
-
std::size_t count();
|
|
121
|
-
|
|
122
|
-
/// Test-only — clear all entries. Used by C++ unit tests; not
|
|
123
|
-
/// exposed through the JSI surface.
|
|
124
|
-
void _resetForTests();
|
|
125
|
-
|
|
126
|
-
/// Test-only — install a pre-constructed entry directly, bypassing
|
|
127
|
-
/// the JSI runtime path. Mirrors `install` but accepts an already-
|
|
128
|
-
/// constructed (or null) `WorkletInvoker` so tests can exercise
|
|
129
|
-
/// `count`/`snapshot`/`uninstall`/thread-safety without standing
|
|
130
|
-
/// up a full JSI runtime + worklets-core stack. Tests typically
|
|
131
|
-
/// pass `nullptr` — the registry never dereferences the pointer.
|
|
132
|
-
/// Not exposed through the JSI surface.
|
|
133
|
-
std::string _installEntryForTests(
|
|
134
|
-
std::shared_ptr<RNWorklet::WorkletInvoker> invoker);
|
|
135
|
-
|
|
136
|
-
private:
|
|
137
|
-
StitcherWorkletRegistry() = default;
|
|
138
|
-
StitcherWorkletRegistry(const StitcherWorkletRegistry&) = delete;
|
|
139
|
-
StitcherWorkletRegistry& operator=(const StitcherWorkletRegistry&) = delete;
|
|
140
|
-
|
|
141
|
-
std::mutex _mutex;
|
|
142
|
-
std::vector<StitcherWorkletEntry> _entries;
|
|
143
|
-
int _nextId = 0;
|
|
144
|
-
};
|
|
145
|
-
|
|
146
|
-
} // namespace retailens
|