/** * Shared decode for capture photos so PhotoStage, mask worker, inventory, and * calculate all share one EXIF-oriented pixel space. * * Matched-set / DJI JPEGs often carry EXIF Orientation 8 (stored landscape, * display portrait). `new Image()` and bare `createImageBitmap(blob)` can * disagree across browsers → pink axis arrow space ≠ mask/overlay bitset space. * * Contract: always request `imageOrientation: 'from-image'`. If the runtime * rejects that option (older engines), fall back to bare `createImageBitmap`; * engines that already apply EXIF by default still match Image/canvas size. * Engines that never apply EXIF may still diverge after fallback — document * that path rather than inventing a software rotate. */ export const ORIENTED_BITMAP_OPTIONS: ImageBitmapOptions = { imageOrientation: 'from-image', }; export async function decodeOrientedBitmap(blob: Blob): Promise { try { return await createImageBitmap(blob, ORIENTED_BITMAP_OPTIONS); } catch (first) { try { return await createImageBitmap(blob); } catch { throw first; } } }