fix(4x3): force frame stream to 4:3 sensor aspect via targetResolution

This commit is contained in:
2026-09-08 21:21:05 +07:00
parent 5a94978f33
commit dca4ba0b7c
+32 -1
View File
@@ -155,6 +155,35 @@ export default function Viewfinder({
// enough to pick the device up — no manual polling needed.
const device = useCameraDevice('back');
// CameraX negotiates the CameraFrameOutput aspect on its own (default 16:9 on
// this device), which center-crops the 4:3 sensor — so the ratio band could
// never display 100% of the sensor pixels. Force the frame stream to a 4:3
// (sensor aspect) resolution supported for 'stream' outputs; the photo output
// keeps its own full-res 4:3 size and FOV, so the viewfinder then matches the
// captured photo exactly. Best supported 4:3 size closest to 1280x960
// (720p-class, keeps the frame pipeline light); CameraX may still round it.
const frameTarget = useMemo(() => {
if (!device) return undefined;
try {
let best: { width: number; height: number } | undefined;
let bestScore = Infinity;
for (const s of device.getSupportedResolutions('stream')) {
const w = s.width;
const h = s.height;
if (!(w > 0 && h > 0)) continue;
if (Math.abs(w / h - 4 / 3) > 0.03) continue;
const score = Math.abs(Math.sqrt(w * h) - Math.sqrt(1280 * 960));
if (score < bestScore) {
bestScore = score;
best = { width: w, height: h };
}
}
return best ?? undefined;
} catch {
return undefined;
}
}, [device]);
const skiaImage = useImage(libraryImageUri || '');
// Wall-frame PNG artwork (project root). Loaded lazily so other frames pay
// no asset cost; source switches to '' when unselected (useImage → null).
@@ -629,7 +658,8 @@ export default function Viewfinder({
' ratio=' + aspectRatio +
' frame=' + selectedFrame +
' view=' + vw + 'x' + vh +
' feed=' + (cameraPhotoRect ? JSON.stringify([1, vw / vh, cameraPhotoRect.x / vw, cameraPhotoRect.y / vh, cameraPhotoRect.w / vw, cameraPhotoRect.h / vh]) : 'null')
' feed=' + (cameraPhotoRect ? JSON.stringify([1, vw / vh, cameraPhotoRect.x / vw, cameraPhotoRect.y / vh, cameraPhotoRect.w / vw, cameraPhotoRect.h / vh]) : 'null') +
' frameTarget=' + (frameTarget ? frameTarget.width + 'x' + frameTarget.height : 'null')
);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [region, regionBand, aspectRatio, selectedFrame, cameraPhotoRect]);
@@ -956,6 +986,7 @@ export default function Viewfinder({
isActive={appActive}
outputs={cameraOutputs}
pixelFormat="yuv"
targetResolution={frameTarget}
onStarted={() => setCameraActive(true)}
onFrame={handleFrame}
onError={onSessionError}