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.
Files changed (120) hide show
  1. package/CHANGELOG.md +164 -0
  2. package/README.md +35 -0
  3. package/RNImageStitcher.podspec +8 -7
  4. package/android/build.gradle +0 -16
  5. package/android/src/main/cpp/CMakeLists.txt +2 -63
  6. package/android/src/main/cpp/image_stitcher_jni.cpp +14 -0
  7. package/android/src/main/cpp/keyframe_gate_jni.cpp +13 -0
  8. package/android/src/main/java/io/imagestitcher/rn/BatchStitcher.kt +285 -3
  9. package/android/src/main/java/io/imagestitcher/rn/IncrementalStitcher.kt +180 -1162
  10. package/android/src/main/java/io/imagestitcher/rn/KeyframeGate.kt +29 -0
  11. package/android/src/main/java/io/imagestitcher/rn/RNImageStitcherPackage.kt +0 -4
  12. package/android/src/main/java/io/imagestitcher/rn/RNSARCameraView.kt +129 -71
  13. package/android/src/main/java/io/imagestitcher/rn/RNSARSession.kt +49 -0
  14. package/cpp/keyframe_gate.cpp +82 -23
  15. package/cpp/keyframe_gate.hpp +31 -2
  16. package/cpp/stitcher.cpp +208 -28
  17. package/cpp/tests/CMakeLists.txt +18 -12
  18. package/cpp/tests/keyframe_timebudget_test.cpp +65 -0
  19. package/cpp/tests/warp_guard_test.cpp +48 -0
  20. package/cpp/warp_guard.hpp +41 -0
  21. package/dist/camera/Camera.d.ts +31 -16
  22. package/dist/camera/Camera.js +11 -3
  23. package/dist/camera/CameraView.js +93 -3
  24. package/dist/camera/CaptureStitchStatsToast.d.ts +15 -2
  25. package/dist/camera/CaptureStitchStatsToast.js +27 -7
  26. package/dist/camera/PanoramaSettings.d.ts +10 -223
  27. package/dist/camera/PanoramaSettings.js +6 -28
  28. package/dist/camera/PanoramaSettingsBridge.d.ts +1 -24
  29. package/dist/camera/PanoramaSettingsBridge.js +3 -102
  30. package/dist/camera/PanoramaSettingsModal.js +7 -1
  31. package/dist/camera/buildPanoramaInitialSettings.d.ts +11 -0
  32. package/dist/camera/buildPanoramaInitialSettings.js +4 -0
  33. package/dist/camera/cameraErrorMessages.d.ts +32 -0
  34. package/dist/camera/cameraErrorMessages.js +53 -0
  35. package/dist/camera/selectCaptureDevice.d.ts +5 -1
  36. package/dist/camera/selectCaptureDevice.js +22 -2
  37. package/dist/camera/useCapture.js +38 -0
  38. package/dist/index.d.ts +5 -8
  39. package/dist/index.js +11 -34
  40. package/dist/stitching/incremental.d.ts +1 -117
  41. package/dist/stitching/stitchVideo.d.ts +0 -35
  42. package/dist/types.d.ts +0 -87
  43. package/ios/Sources/RNImageStitcher/IncrementalStitcher.swift +96 -674
  44. package/ios/Sources/RNImageStitcher/IncrementalStitcherBridge.swift +9 -12
  45. package/ios/Sources/RNImageStitcher/KeyframeGate.swift +14 -0
  46. package/ios/Sources/RNImageStitcher/KeyframeGateBridge.h +7 -0
  47. package/ios/Sources/RNImageStitcher/KeyframeGateBridge.mm +6 -0
  48. package/ios/Sources/RNImageStitcher/OpenCVKeyframeCollector.h +2 -2
  49. package/ios/Sources/RNImageStitcher/OpenCVKeyframeCollector.mm +3 -3
  50. package/ios/Sources/RNImageStitcher/OpenCVStitcher.h +28 -60
  51. package/ios/Sources/RNImageStitcher/OpenCVStitcher.mm +180 -921
  52. package/ios/Sources/RNImageStitcher/RNSARCameraView.swift +82 -7
  53. package/ios/Sources/RNImageStitcher/RNSARSession.swift +10 -35
  54. package/ios/Sources/RNImageStitcher/Stitcher.swift +84 -35
  55. package/ios/Sources/RNImageStitcher/StitcherBridge.m +13 -0
  56. package/ios/Sources/RNImageStitcher/StitcherBridge.swift +132 -5
  57. package/package.json +3 -2
  58. package/src/camera/Camera.tsx +44 -23
  59. package/src/camera/CameraView.tsx +113 -4
  60. package/src/camera/CaptureStitchStatsToast.tsx +58 -14
  61. package/src/camera/PanoramaSettings.ts +16 -289
  62. package/src/camera/PanoramaSettingsBridge.ts +3 -114
  63. package/src/camera/PanoramaSettingsModal.tsx +14 -1
  64. package/src/camera/__tests__/PanoramaSettingsBridge.test.ts +3 -188
  65. package/src/camera/__tests__/buildPanoramaInitialSettings.test.ts +41 -0
  66. package/src/camera/__tests__/cameraErrorMessages.test.ts +76 -0
  67. package/src/camera/__tests__/selectCaptureDevice.test.ts +33 -0
  68. package/src/camera/buildPanoramaInitialSettings.ts +17 -0
  69. package/src/camera/cameraErrorMessages.ts +84 -0
  70. package/src/camera/selectCaptureDevice.ts +28 -3
  71. package/src/camera/useCapture.ts +44 -1
  72. package/src/index.ts +11 -40
  73. package/src/stitching/incremental.ts +3 -140
  74. package/src/stitching/stitchVideo.ts +0 -26
  75. package/src/types.ts +0 -95
  76. package/android/src/main/cpp/stitcher_jsi_install_jni.cpp +0 -227
  77. package/android/src/main/java/io/imagestitcher/rn/IncrementalFirstwinsEngine.kt +0 -1081
  78. package/android/src/main/java/io/imagestitcher/rn/StitcherJsiInstallerModule.kt +0 -103
  79. package/android/src/main/java/io/imagestitcher/rn/StitcherWorkletRuntime.kt +0 -256
  80. package/cpp/stitcher_frame_jsi.cpp +0 -214
  81. package/cpp/stitcher_frame_jsi.hpp +0 -108
  82. package/cpp/stitcher_proxy_jsi.cpp +0 -109
  83. package/cpp/stitcher_proxy_jsi.hpp +0 -46
  84. package/cpp/stitcher_worklet_dispatch.cpp +0 -103
  85. package/cpp/stitcher_worklet_dispatch.hpp +0 -71
  86. package/cpp/stitcher_worklet_registry.cpp +0 -91
  87. package/cpp/stitcher_worklet_registry.hpp +0 -146
  88. package/cpp/tests/stitcher_worklet_registry_test.cpp +0 -195
  89. package/dist/stitching/IncrementalStitcherView.d.ts +0 -41
  90. package/dist/stitching/IncrementalStitcherView.js +0 -157
  91. package/dist/stitching/StitcherWorkletRegistry.d.ts +0 -117
  92. package/dist/stitching/StitcherWorkletRegistry.js +0 -78
  93. package/dist/stitching/ensureStitcherProxyInstalled.d.ts +0 -8
  94. package/dist/stitching/ensureStitcherProxyInstalled.js +0 -81
  95. package/dist/stitching/useFrameProcessor.d.ts +0 -119
  96. package/dist/stitching/useFrameProcessor.js +0 -196
  97. package/dist/stitching/useFrameStream.d.ts +0 -34
  98. package/dist/stitching/useFrameStream.js +0 -234
  99. package/dist/stitching/useThrottledFrameProcessor.d.ts +0 -33
  100. package/dist/stitching/useThrottledFrameProcessor.js +0 -132
  101. package/ios/Sources/RNImageStitcher/OpenCVIncrementalStitcher.h +0 -474
  102. package/ios/Sources/RNImageStitcher/OpenCVIncrementalStitcher.mm +0 -1328
  103. package/ios/Sources/RNImageStitcher/OpenCVSlitScanStitcher.h +0 -103
  104. package/ios/Sources/RNImageStitcher/OpenCVSlitScanStitcher.mm +0 -3285
  105. package/ios/Sources/RNImageStitcher/RNSARWorkletRuntime.h +0 -128
  106. package/ios/Sources/RNImageStitcher/RNSARWorkletRuntime.mm +0 -313
  107. package/ios/Sources/RNImageStitcher/SaveFrameAsJpegPlugin.mm +0 -185
  108. package/ios/Sources/RNImageStitcher/StitcherFrameHostObject.h +0 -60
  109. package/ios/Sources/RNImageStitcher/StitcherFrameHostObject.mm +0 -214
  110. package/ios/Sources/RNImageStitcher/StitcherJsiInstaller.h +0 -42
  111. package/ios/Sources/RNImageStitcher/StitcherJsiInstaller.mm +0 -160
  112. package/src/stitching/IncrementalStitcherView.tsx +0 -198
  113. package/src/stitching/StitcherWorkletRegistry.ts +0 -156
  114. package/src/stitching/__tests__/StitcherWorkletRegistry.test.ts +0 -176
  115. package/src/stitching/__tests__/ensureStitcherProxyInstalled.test.ts +0 -94
  116. package/src/stitching/__tests__/useThrottledFrameProcessor.test.ts +0 -178
  117. package/src/stitching/ensureStitcherProxyInstalled.ts +0 -141
  118. package/src/stitching/useFrameProcessor.ts +0 -226
  119. package/src/stitching/useFrameStream.ts +0 -271
  120. package/src/stitching/useThrottledFrameProcessor.ts +0 -145
@@ -1,227 +0,0 @@
1
- // SPDX-License-Identifier: Apache-2.0
2
- //
3
- // stitcher_jsi_install_jni.cpp — JNI binding for the Android-side
4
- // JSI install (v0.8.0 Phase 4b.ii).
5
- //
6
- // Kotlin's `StitcherJsiInstallerModule.nativeInstall(jsiRuntimeRef)`
7
- // calls into this file. We unbox the `jsi::Runtime*` from the
8
- // Java `long` and hand it to the shared
9
- // `retailens::installStitcherProxy(runtime)` function which sets
10
- // `globalThis.__stitcherProxy`. Same destination as iOS — the
11
- // host object class lives in `cpp/stitcher_proxy_jsi.{hpp,cpp}`.
12
- //
13
- // ## Why a `long` ref, not a JSI handle wrapper class
14
- //
15
- // `ReactApplicationContext.getJavaScriptContextHolder()` returns a
16
- // `JavaScriptContextHolder` whose `.get()` returns a Java `long`
17
- // that's the raw pointer to the C++ `jsi::Runtime*`. Same
18
- // contract as worklets-core's `WorkletsModule.nativeInstall`
19
- // (verified at the same call site). Caller is responsible for
20
- // ensuring the runtime outlives this call — in practice, the
21
- // runtime IS the JS thread's runtime which lives the whole
22
- // process lifetime, so this is structurally always safe in our
23
- // usage.
24
- //
25
- // ## Threading
26
- //
27
- // Kotlin invokes this from a `@ReactMethod(isBlockingSynchronousMethod
28
- // = true)` so we're already on the JS thread. Synchronous JSI
29
- // access is safe.
30
-
31
- #include "stitcher_proxy_jsi.hpp"
32
-
33
- // v0.8.0 Phase 4b.iii — per-frame fan-out support. The shared
34
- // `dispatchToHostWorklets` posts to worklets-core's default context;
35
- // this JNI file's `nativeDispatchToHostWorklets` constructs the
36
- // `StitcherFrameData` from raw bytes + pose + dims and forwards it.
37
- #include "stitcher_frame_data.hpp"
38
- #include "stitcher_worklet_dispatch.hpp"
39
- #include "stitcher_worklet_registry.hpp"
40
-
41
- #include <react-native-worklets-core/WKTJsiWorkletContext.h>
42
-
43
- #include <jni.h>
44
- #include <jsi/jsi.h>
45
-
46
- #include <android/log.h>
47
-
48
- #include <cstdint>
49
- #include <cstring>
50
- #include <memory>
51
- #include <utility>
52
- #include <vector>
53
-
54
- #define LOG_TAG "StitcherJsiInstaller"
55
- #define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
56
- #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
57
-
58
- extern "C" JNIEXPORT jboolean JNICALL
59
- Java_io_imagestitcher_rn_StitcherJsiInstallerModule_nativeInstall(
60
- JNIEnv* /*env*/, jobject /*thiz*/, jlong jsiRuntimeRef) {
61
- if (jsiRuntimeRef == 0) {
62
- // ReactApplicationContext.getJavaScriptContextHolder().get()
63
- // returns 0 when the runtime isn't ready (rare — JS would have
64
- // had to call us before its own runtime was up; impossible in
65
- // practice). Defensive.
66
- return JNI_FALSE;
67
- }
68
- auto* runtime = reinterpret_cast<facebook::jsi::Runtime*>(jsiRuntimeRef);
69
- retailens::installStitcherProxy(*runtime);
70
- LOGI("installed globalThis.__stitcherProxy on main JS runtime.");
71
- return JNI_TRUE;
72
- }
73
-
74
- // ─── v0.8.0 Phase 4b.iii — Android NV21 PixelBufferReader ──────────
75
- //
76
- // Owns a heap-allocated `std::vector<uint8_t>` of pre-copied NV21
77
- // bytes. Constructed by `nativeDispatchToHostWorklets` after one
78
- // JNI byte-array copy from Kotlin; outlives the AR render thread
79
- // scope via `StitcherFrameData::pixelReader`'s `shared_ptr` —
80
- // dropped when the host object is invalidated.
81
-
82
- namespace {
83
-
84
- class AndroidNV21BufferReader : public retailens::PixelBufferReader {
85
- public:
86
- explicit AndroidNV21BufferReader(std::vector<uint8_t>&& bytes)
87
- : _bytes(std::move(bytes)) {}
88
-
89
- std::size_t byteSize() const override { return _bytes.size(); }
90
-
91
- std::size_t copyTo(uint8_t* dst, std::size_t maxBytes) override {
92
- if (dst == nullptr) return 0;
93
- std::size_t n = std::min(maxBytes, _bytes.size());
94
- if (n > 0) {
95
- std::memcpy(dst, _bytes.data(), n);
96
- }
97
- return n;
98
- }
99
-
100
- private:
101
- std::vector<uint8_t> _bytes;
102
- };
103
-
104
- } // namespace
105
-
106
- // ─── v0.8.0 Phase 4b.iii — registry count accessor ─────────────────
107
- //
108
- // Cheap (microsecond) accessor for the per-frame gate in
109
- // `RNSARCameraView.onDrawFrame`. Avoids the NV21 byte-pack cost
110
- // when no host worklets are registered AND no capture is active.
111
- // Same atomic-read the JSI host object's `count()` host function
112
- // goes through.
113
- extern "C" JNIEXPORT jint JNICALL
114
- Java_io_imagestitcher_rn_StitcherWorkletRuntime_nativeRegistryCount(
115
- JNIEnv* /*env*/, jobject /*thiz*/) {
116
- return static_cast<jint>(
117
- retailens::StitcherWorkletRegistry::shared().count());
118
- }
119
-
120
- // ─── v0.8.0 Phase 4b.iii — per-frame dispatch JNI binding ──────────
121
- //
122
- // Called from Kotlin's `StitcherWorkletRuntime.dispatchToHostWorklets`
123
- // after the first-party stitching block has returned (the AR-frame
124
- // data is still in scope on the Kotlin side because
125
- // `RNSARCameraView.onDrawFrame` reads the ARCore Frame, builds the
126
- // NV21 byte[], invokes first-party via `runFirstParty { ... }`,
127
- // THEN calls into here).
128
- //
129
- // The byte[] is COPIED into our owned vector — ARCore's pixel data
130
- // becomes inaccessible shortly after `onDrawFrame` returns, and our
131
- // async dispatch must outlive that scope. Cost: one ~3MB memcpy
132
- // per frame at 1080p NV21 (~90 MB/s at 30 fps; <5 ms on a mid-range
133
- // Android device). Fast-path early-exit when the registry is empty
134
- // skips the copy entirely.
135
- //
136
- // trackingState: Kotlin passes one of "" / "notAvailable" / "limited"
137
- // / "normal" (empty string = field unset → JS sees undefined).
138
- extern "C" JNIEXPORT void JNICALL
139
- Java_io_imagestitcher_rn_StitcherWorkletRuntime_nativeDispatchToHostWorklets(
140
- JNIEnv* env, jobject /*thiz*/,
141
- jbyteArray nv21Bytes,
142
- jint width, jint height,
143
- jdouble qx, jdouble qy, jdouble qz, jdouble qw,
144
- jdouble tx, jdouble ty, jdouble tz,
145
- jdouble timestampNs,
146
- jstring trackingState) {
147
- // Fast-path early-exit BEFORE the JNI byte-array copy. Saves the
148
- // ~3MB memcpy + JSI host object alloc on every frame in the
149
- // common first-party-only case.
150
- if (retailens::StitcherWorkletRegistry::shared().count() == 0) {
151
- return;
152
- }
153
-
154
- if (nv21Bytes == nullptr) {
155
- LOGE("nativeDispatchToHostWorklets: nv21Bytes is null");
156
- return;
157
- }
158
-
159
- const jsize byteLen = env->GetArrayLength(nv21Bytes);
160
- if (byteLen <= 0) {
161
- LOGE("nativeDispatchToHostWorklets: nv21Bytes is empty");
162
- return;
163
- }
164
-
165
- // Copy into our owned vector. `GetByteArrayRegion` is the
166
- // canonical "copy" path — `GetByteArrayElements + Release` MAY
167
- // pin the JVM array (zero-copy) but the contract isn't
168
- // guaranteed; we need our own buffer for the async dispatch
169
- // anyway, so the explicit copy is cleaner.
170
- std::vector<uint8_t> bytes(static_cast<std::size_t>(byteLen));
171
- env->GetByteArrayRegion(
172
- nv21Bytes, 0, byteLen,
173
- reinterpret_cast<jbyte*>(bytes.data()));
174
-
175
- // Extract trackingState string (may be null on the Kotlin side
176
- // for non-AR or pre-tracking frames — guard accordingly).
177
- std::string trackingStateStr;
178
- if (trackingState != nullptr) {
179
- const char* cs = env->GetStringUTFChars(trackingState, nullptr);
180
- if (cs != nullptr) {
181
- trackingStateStr = cs;
182
- env->ReleaseStringUTFChars(trackingState, cs);
183
- }
184
- }
185
-
186
- // Build StitcherFrameData. Field semantics match the iOS
187
- // `StitcherFrameHostObject::fromARFrame:pose:` factory; this is
188
- // the Android equivalent path.
189
- retailens::StitcherFrameData data;
190
- data.source = "ar";
191
- data.width = static_cast<int32_t>(width);
192
- data.height = static_cast<int32_t>(height);
193
- // ARCore's camera image is YUV_420_888 on Android, mapped to NV21
194
- // by the existing `YuvImageConverter.packNV21` path — the byte[]
195
- // we receive is interleaved Y then VU. Worklets gate on this
196
- // string identifier (`'yuv'` vs `'unknown'`); v0.8.0 always
197
- // emits `'yuv'` for AR mode on Android (NV21).
198
- data.pixelFormat = "yuv";
199
- // Android AR-mode camera image is always landscape-natural; the
200
- // mapping matches iOS' coarse two-value set. Hosts that need
201
- // exact display orientation read it from the device-orientation
202
- // sensors (see `useDeviceOrientation` hook).
203
- data.orientation = (width >= height) ? "landscape-right" : "portrait";
204
- data.timestampNs = timestampNs;
205
- data.qx = qx;
206
- data.qy = qy;
207
- data.qz = qz;
208
- data.qw = qw;
209
- data.tx = tx;
210
- data.ty = ty;
211
- data.tz = tz;
212
- data.hasTranslation = true; // AR mode always has translation
213
- data.arTrackingState = trackingStateStr;
214
- data.pixelReader =
215
- std::make_shared<AndroidNV21BufferReader>(std::move(bytes));
216
-
217
- // Dispatch on worklets-core's default context. That context is
218
- // initialised by JS' `Worklets.install()` (which runs at lib
219
- // bootstrap when worklets-core's module is imported); by the
220
- // time host worklets are registered, the default context is up.
221
- // The shared dispatch helper handles the registry snapshot,
222
- // host-object construction (inside the worklet thread), per-
223
- // worklet failure isolation, and invalidation.
224
- retailens::dispatchToHostWorklets(
225
- RNWorklet::JsiWorkletContext::getDefaultInstance(),
226
- std::move(data));
227
- }