react-native-image-stitcher 0.16.1 → 0.17.0

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 (38) hide show
  1. package/CHANGELOG.md +66 -0
  2. package/README.md +16 -34
  3. package/RNImageStitcher.podspec +26 -1
  4. package/android/build.gradle +54 -0
  5. package/android/src/main/cpp/CMakeLists.txt +46 -3
  6. package/android/src/main/cpp/stitcher_jsi_install_jni.cpp +227 -0
  7. package/android/src/main/java/io/imagestitcher/rn/RNImageStitcherPackage.kt +6 -0
  8. package/android/src/main/java/io/imagestitcher/rn/RNSARCameraView.kt +55 -6
  9. package/android/src/main/java/io/imagestitcher/rn/StitcherJsiInstallerModule.kt +103 -0
  10. package/android/src/main/java/io/imagestitcher/rn/StitcherWorkletRuntime.kt +256 -0
  11. package/cpp/stitcher_frame_jsi.cpp +214 -0
  12. package/cpp/stitcher_frame_jsi.hpp +108 -0
  13. package/cpp/stitcher_proxy_jsi.cpp +109 -0
  14. package/cpp/stitcher_proxy_jsi.hpp +46 -0
  15. package/cpp/stitcher_worklet_dispatch.cpp +103 -0
  16. package/cpp/stitcher_worklet_dispatch.hpp +71 -0
  17. package/cpp/stitcher_worklet_registry.cpp +91 -0
  18. package/cpp/stitcher_worklet_registry.hpp +146 -0
  19. package/dist/camera/ARCameraView.d.ts +20 -0
  20. package/dist/camera/ARCameraView.js +23 -1
  21. package/dist/camera/Camera.d.ts +12 -0
  22. package/dist/camera/Camera.js +2 -2
  23. package/dist/camera/CaptureMemoryPill.d.ts +4 -3
  24. package/dist/camera/CaptureMemoryPill.js +4 -3
  25. package/dist/stitching/ensureStitcherProxyInstalled.d.ts +8 -0
  26. package/dist/stitching/ensureStitcherProxyInstalled.js +81 -0
  27. package/ios/Sources/RNImageStitcher/RNSARSession.swift +44 -6
  28. package/ios/Sources/RNImageStitcher/RNSARWorkletRuntime.h +128 -0
  29. package/ios/Sources/RNImageStitcher/RNSARWorkletRuntime.mm +313 -0
  30. package/ios/Sources/RNImageStitcher/StitcherFrameHostObject.h +60 -0
  31. package/ios/Sources/RNImageStitcher/StitcherFrameHostObject.mm +214 -0
  32. package/ios/Sources/RNImageStitcher/StitcherJsiInstaller.h +42 -0
  33. package/ios/Sources/RNImageStitcher/StitcherJsiInstaller.mm +160 -0
  34. package/package.json +1 -1
  35. package/src/camera/ARCameraView.tsx +51 -2
  36. package/src/camera/Camera.tsx +15 -0
  37. package/src/camera/CaptureMemoryPill.tsx +4 -3
  38. package/src/stitching/ensureStitcherProxyInstalled.ts +141 -0
@@ -0,0 +1,141 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+
3
+ import { NativeModules } from 'react-native';
4
+
5
+ /**
6
+ * v0.8.0 Phase 4b — one-shot installer that asks the native side
7
+ * to install `globalThis.__stitcherProxy` on the main JS runtime.
8
+ *
9
+ * ## When this runs
10
+ *
11
+ * The first call to `useFrameProcessor` triggers this. Idempotent:
12
+ * once the global is installed, subsequent calls short-circuit.
13
+ *
14
+ * ## What it does
15
+ *
16
+ * Calls into the platform-native `StitcherJsiInstaller` RN module
17
+ * which is registered with a `RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(install)`
18
+ * on iOS (see `ios/Sources/RNImageStitcher/StitcherJsiInstaller.mm`)
19
+ * and — Phase 4b.ii — an analogous Kotlin TurboModule on Android.
20
+ *
21
+ * The native module reaches into the main JS runtime via
22
+ * `RCTCxxBridge.runtime` (iOS) / the equivalent Android JSI access
23
+ * pattern and installs a host object on `globalThis.__stitcherProxy`
24
+ * exposing `install(workletFn)` / `uninstall(id)` / `count()`.
25
+ *
26
+ * ## Failure modes (and what happens then)
27
+ *
28
+ * 1. **Module not registered** (Android in Phase 4b.i; old iOS
29
+ * builds without the new pod files). `NativeModules
30
+ * .StitcherJsiInstaller` is `undefined`. This function returns
31
+ * `false` and the hook falls back to the JS-side
32
+ * `StitcherWorkletRegistry` — host worklets are registered
33
+ * on the JS side but never fan out to AR mode. No crash, no
34
+ * regression vs. Phase 4a.
35
+ *
36
+ * 2. **JSI runtime unreachable** (e.g., remote debug mode). The
37
+ * sync method returns `false`. Same JS-side-registry fallback.
38
+ *
39
+ * 3. **Native install succeeds but global not yet visible.**
40
+ * The native call is SYNCHRONOUS (`BLOCKING_SYNCHRONOUS_METHOD`),
41
+ * so by the time the function returns the global is installed.
42
+ * No race here.
43
+ *
44
+ * ## Why a separate module
45
+ *
46
+ * The install method is a one-time runtime bootstrap, not a
47
+ * per-call API. Putting it on its own RN module (vs. on the
48
+ * existing `StitcherBridge` / `IncrementalStitcherBridge`) keeps
49
+ * the responsibility surface narrow and the failure mode easy
50
+ * to diagnose ("`__stitcherProxy` not installed" → check
51
+ * `StitcherJsiInstaller` module registration first).
52
+ */
53
+
54
+ interface StitcherJsiInstallerModule {
55
+ install(): boolean;
56
+ }
57
+
58
+ /**
59
+ * `__DEV__` is RN's global dev-flag. Guard the read with `typeof`
60
+ * so the helper works in any environment that imports it without
61
+ * defining __DEV__ (jest, SSR, custom tooling). Same pattern RN's
62
+ * own debug code uses.
63
+ */
64
+ function isDev(): boolean {
65
+ return typeof __DEV__ !== 'undefined' && __DEV__;
66
+ }
67
+
68
+ let installed = false;
69
+
70
+ export function ensureStitcherProxyInstalled(): boolean {
71
+ if (installed) return true;
72
+ // Already installed by an earlier hook mount. Cheap fast-path.
73
+ if (typeof (globalThis as { __stitcherProxy?: unknown }).__stitcherProxy !== 'undefined') {
74
+ installed = true;
75
+ return true;
76
+ }
77
+
78
+ const mod = (NativeModules as { StitcherJsiInstaller?: StitcherJsiInstallerModule })
79
+ .StitcherJsiInstaller;
80
+ if (mod == null || typeof mod.install !== 'function') {
81
+ // Module not present — Android until Phase 4b.ii lands, or
82
+ // an old iOS build. Surface this once at debug-info level so
83
+ // the host can see "your worklets are JS-registered only" in
84
+ // logcat / Console.app without a noisy per-frame warning.
85
+ if (isDev() && !warnedAboutMissingModule) {
86
+ warnedAboutMissingModule = true;
87
+ console.info(
88
+ '[react-native-image-stitcher] StitcherJsiInstaller native ' +
89
+ 'module not found; host worklets registered in JS-side ' +
90
+ 'registry only. AR-mode dispatch requires the native install ' +
91
+ '(iOS Phase 4b.i — included in v0.8.0; Android Phase 4b.ii ' +
92
+ '— follow-up release).',
93
+ );
94
+ }
95
+ return false;
96
+ }
97
+
98
+ try {
99
+ const ok = mod.install();
100
+ if (!ok) {
101
+ // Native module ran but couldn't install (JSI runtime
102
+ // unreachable). Same fallback as the missing-module case.
103
+ if (isDev() && !warnedAboutFailedInstall) {
104
+ warnedAboutFailedInstall = true;
105
+ console.info(
106
+ '[react-native-image-stitcher] StitcherJsiInstaller.install() ' +
107
+ 'returned false (JSI runtime unreachable — remote debug ' +
108
+ 'mode?). Falling back to JS-side host worklet registry.',
109
+ );
110
+ }
111
+ return false;
112
+ }
113
+ installed = true;
114
+ return true;
115
+ } catch (err) {
116
+ if (isDev() && !warnedAboutFailedInstall) {
117
+ warnedAboutFailedInstall = true;
118
+ console.info(
119
+ '[react-native-image-stitcher] StitcherJsiInstaller.install() ' +
120
+ 'threw: ' +
121
+ String(err) +
122
+ '. Falling back to JS-side host worklet registry.',
123
+ );
124
+ }
125
+ return false;
126
+ }
127
+ }
128
+
129
+ let warnedAboutMissingModule = false;
130
+ let warnedAboutFailedInstall = false;
131
+
132
+ /**
133
+ * Test-only — reset module-internal state. Used by jest to allow
134
+ * multiple test cases to re-trigger the install path independently.
135
+ * NOT exported from `src/index.ts`.
136
+ */
137
+ export function _resetStitcherProxyInstallStateForTests(): void {
138
+ installed = false;
139
+ warnedAboutMissingModule = false;
140
+ warnedAboutFailedInstall = false;
141
+ }