react-native-image-stitcher 0.15.1 → 0.15.2

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 CHANGED
@@ -16,6 +16,29 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
16
16
 
17
17
  ## [Unreleased]
18
18
 
19
+ ## [0.15.2] — 2026-06-11
20
+
21
+ ### Fixed
22
+
23
+ - **Sharp non-AR camera preview (WYSIWYG follow-up).** The v0.15.1
24
+ letterbox pinned the vision-camera format by aspect ratio only, so
25
+ `useCameraFormat` could settle on a degenerate 4:3 format — observed as
26
+ a 192×144 video stream on the iPhone 16 Pro — rendering the preview as
27
+ upscaled mush behind a full-resolution capture. The format filter now
28
+ also requests `{ videoResolution: 'max' }`, so among 4:3 formats the
29
+ highest-resolution one is chosen: a sharp preview plus full-res frames
30
+ into the non-AR stitcher, with aspect kept as the top-priority filter so
31
+ 4:3 capture parity holds. A bounded target (e.g. 1920×1440) is
32
+ deliberately avoided — the nearest such format on the iPhone 16 Pro is
33
+ 10-bit-only (`x420`/`x422`), which the frame processor's 8-bit
34
+ `420v`/`420f` pipeline rejects with `device/pixel-format-not-supported`;
35
+ vision-camera exposes no per-format pixel formats to JS, so `'max'`
36
+ (empirically the device's 8-bit full-res format) is the robust choice.
37
+ Tap-to-photo stills are capped at ~12 MP (`photoResolution: 4032×3024`,
38
+ lowest priority) so the iPhone 16 Pro's max-video format doesn't default
39
+ to a 24 MP still — the panorama path uses the video stream, not
40
+ `takePhoto`, so the cap costs nothing there.
41
+
19
42
  ## [0.15.1] — 2026-06-08
20
43
 
21
44
  ### Fixed
@@ -102,9 +102,47 @@ exports.CameraView = (0, react_1.forwardRef)(function CameraView({ device, flash
102
102
  // aspect on essentially every phone camera (incl. ultra-wide), so a
103
103
  // matching format is virtually always available; `useCameraFormat`
104
104
  // returns the closest match and never throws.
105
+ //
106
+ // Resolution preference matters too: filtering on aspect ALONE lets
107
+ // vision-camera settle on whatever 4:3 format sorts first — observed as
108
+ // a 192×144 VIDEO stream on the iPhone 16 Pro (the photo still uses the
109
+ // format's full-res photo dims, so you'd get a sharp capture behind a
110
+ // mush preview). So we also request the highest video resolution.
111
+ //
112
+ // Why `'max'` and not a bounded target like 1920×1440? We tried the
113
+ // bounded target and it FAILED on the iPhone 16 Pro: the nearest
114
+ // 1920×1440 format is a 10-bit format (pixel formats x420 / x422 only —
115
+ // and it is NOT flagged HDR, so the `videoHdr` filter can't dodge it).
116
+ // The frame processor + the stitcher's CV pipeline need 8-bit
117
+ // `420v`/`420f`, so vision-camera raises
118
+ // `device/pixel-format-not-supported` and silently falls back to a
119
+ // default pixel format — breaking non-AR stitching. vision-camera does
120
+ // NOT expose a format's supported pixel formats to JS (no
121
+ // `pixelFormats` field; `FormatFilter` has no pixel-format key), so we
122
+ // can't select an 8-bit format by inspection. Empirically the device's
123
+ // MAX 4:3 video format is 8-bit (420v/420f) on the iPhone 16 Pro, and
124
+ // Android formats are near-universally 8-bit YUV_420_888, so `'max'` is
125
+ // the robust choice: a sharp preview on a frame-processor-compatible
126
+ // pipeline. Trade-off: the max format tends to run at 30 fps (fine for
127
+ // hold-to-pan) and feeds full-res frames to the non-AR gate — if that
128
+ // ever shows up as dropped frames we can downscale for the gate
129
+ // natively while keeping full-res keyframes. Aspect stays the
130
+ // top-priority filter, so 4:3 WYSIWYG parity holds on every device.
131
+ //
132
+ // Still resolution is capped at ~12 MP. The max-video 4:3 format pairs
133
+ // with a 24 MP photo (5712×4284) on the iPhone 16 Pro by default — 2×
134
+ // the file size + per-capture memory for no benefit on the panorama
135
+ // path (which uses the VIDEO stream, not takePhoto). `photoResolution`
136
+ // is the LOWEST-priority filter, so it only breaks ties between equal
137
+ // max-video formats (e.g. the 12 MP-photo vs 24 MP-photo variants that
138
+ // share the same 4032×3024 video) — it never trades preview/stitch
139
+ // sharpness for a smaller still. 4032×3024 = 12 MP at 4:3; nearest-
140
+ // match keeps stills near there on any device.
105
141
  const format = (0, react_native_vision_camera_1.useCameraFormat)(device ?? undefined, [
106
142
  { photoAspectRatio: 4 / 3 },
107
143
  { videoAspectRatio: 4 / 3 },
144
+ { videoResolution: 'max' },
145
+ { photoResolution: { width: 4032, height: 3024 } },
108
146
  ]);
109
147
  // Measured size of our container, so we can size the <Camera> view to
110
148
  // the largest box of the capture's aspect ratio that fits inside it
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-image-stitcher",
3
- "version": "0.15.1",
3
+ "version": "0.15.2",
4
4
  "description": "Pose-aware panorama capture + stitching for React Native. One <Camera> component, both tap-to-photo and hold-to-pan modes, both AR-backed and IMU-fallback capture paths.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -163,9 +163,47 @@ export const CameraView = forwardRef<Camera | null, CameraViewProps>(function Ca
163
163
  // aspect on essentially every phone camera (incl. ultra-wide), so a
164
164
  // matching format is virtually always available; `useCameraFormat`
165
165
  // returns the closest match and never throws.
166
+ //
167
+ // Resolution preference matters too: filtering on aspect ALONE lets
168
+ // vision-camera settle on whatever 4:3 format sorts first — observed as
169
+ // a 192×144 VIDEO stream on the iPhone 16 Pro (the photo still uses the
170
+ // format's full-res photo dims, so you'd get a sharp capture behind a
171
+ // mush preview). So we also request the highest video resolution.
172
+ //
173
+ // Why `'max'` and not a bounded target like 1920×1440? We tried the
174
+ // bounded target and it FAILED on the iPhone 16 Pro: the nearest
175
+ // 1920×1440 format is a 10-bit format (pixel formats x420 / x422 only —
176
+ // and it is NOT flagged HDR, so the `videoHdr` filter can't dodge it).
177
+ // The frame processor + the stitcher's CV pipeline need 8-bit
178
+ // `420v`/`420f`, so vision-camera raises
179
+ // `device/pixel-format-not-supported` and silently falls back to a
180
+ // default pixel format — breaking non-AR stitching. vision-camera does
181
+ // NOT expose a format's supported pixel formats to JS (no
182
+ // `pixelFormats` field; `FormatFilter` has no pixel-format key), so we
183
+ // can't select an 8-bit format by inspection. Empirically the device's
184
+ // MAX 4:3 video format is 8-bit (420v/420f) on the iPhone 16 Pro, and
185
+ // Android formats are near-universally 8-bit YUV_420_888, so `'max'` is
186
+ // the robust choice: a sharp preview on a frame-processor-compatible
187
+ // pipeline. Trade-off: the max format tends to run at 30 fps (fine for
188
+ // hold-to-pan) and feeds full-res frames to the non-AR gate — if that
189
+ // ever shows up as dropped frames we can downscale for the gate
190
+ // natively while keeping full-res keyframes. Aspect stays the
191
+ // top-priority filter, so 4:3 WYSIWYG parity holds on every device.
192
+ //
193
+ // Still resolution is capped at ~12 MP. The max-video 4:3 format pairs
194
+ // with a 24 MP photo (5712×4284) on the iPhone 16 Pro by default — 2×
195
+ // the file size + per-capture memory for no benefit on the panorama
196
+ // path (which uses the VIDEO stream, not takePhoto). `photoResolution`
197
+ // is the LOWEST-priority filter, so it only breaks ties between equal
198
+ // max-video formats (e.g. the 12 MP-photo vs 24 MP-photo variants that
199
+ // share the same 4032×3024 video) — it never trades preview/stitch
200
+ // sharpness for a smaller still. 4032×3024 = 12 MP at 4:3; nearest-
201
+ // match keeps stills near there on any device.
166
202
  const format = useCameraFormat(device ?? undefined, [
167
203
  { photoAspectRatio: 4 / 3 },
168
204
  { videoAspectRatio: 4 / 3 },
205
+ { videoResolution: 'max' },
206
+ { photoResolution: { width: 4032, height: 3024 } },
169
207
  ]);
170
208
 
171
209
  // Measured size of our container, so we can size the <Camera> view to