037a4b870a
PhotoStage used new Image() while mask worker and calculate used bare createImageBitmap; Orientation-8 matched-set JPEGs can disagree across those paths so the approved pink axis is not the overlay (ux,uy). One decodeOrientedBitmap helper always requests imageOrientation from-image (with bare fallback), wired into PhotoStage, mask.worker, and session calculate/siblings.
30 lines
1.1 KiB
TypeScript
30 lines
1.1 KiB
TypeScript
/**
|
|
* 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<ImageBitmap> {
|
|
try {
|
|
return await createImageBitmap(blob, ORIENTED_BITMAP_OPTIONS);
|
|
} catch (first) {
|
|
try {
|
|
return await createImageBitmap(blob);
|
|
} catch {
|
|
throw first;
|
|
}
|
|
}
|
|
}
|