fix(fe): canvas fallback for decodeOrientedBitmap when createImageBitmap is absent
The LAN dev URL is a non-secure, sometimes restricted browser where createImageBitmap may be undefined; the pick-calibration -> mask decode would then reject. Fall back to a canvas + Image decode returning an ImageBitmap. Contract unchanged (still returns ImageBitmap).
This commit is contained in:
@@ -4,17 +4,19 @@
|
||||
*
|
||||
* Matched-set / DJI JPEGs often carry EXIF Orientation 8 (stored landscape,
|
||||
* display portrait). `new Image()` and bare `createImageBitmap(blob)` can
|
||||
* disagree across browsers → axis arrow space ≠ mask bitset space.
|
||||
* disagree across browsers -> axis arrow space != mask 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.
|
||||
* Contract: always request `imageOrientation: 'from-image'` when supported.
|
||||
* The dev environment is LAN-HTTP (a non-secure, sometimes restricted
|
||||
* browser) where `createImageBitmap` may be absent, so fall back to a
|
||||
* canvas + Image decode that returns an ImageBitmap.
|
||||
*/
|
||||
export const ORIENTED_BITMAP_OPTIONS: ImageBitmapOptions = {
|
||||
imageOrientation: 'from-image',
|
||||
};
|
||||
|
||||
export async function decodeOrientedBitmap(blob: Blob): Promise<ImageBitmap> {
|
||||
if (typeof createImageBitmap === 'function') {
|
||||
try {
|
||||
return await createImageBitmap(blob, ORIENTED_BITMAP_OPTIONS);
|
||||
} catch (first) {
|
||||
@@ -25,3 +27,31 @@ export async function decodeOrientedBitmap(blob: Blob): Promise<ImageBitmap> {
|
||||
}
|
||||
}
|
||||
}
|
||||
return decodeViaCanvas(blob);
|
||||
}
|
||||
|
||||
/** Canvas + Image decode for engines without createImageBitmap. */
|
||||
async function decodeViaCanvas(blob: Blob): Promise<ImageBitmap> {
|
||||
const url = URL.createObjectURL(blob);
|
||||
try {
|
||||
const img = await loadImage(url);
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.width = img.naturalWidth;
|
||||
canvas.height = img.naturalHeight;
|
||||
const ctx = canvas.getContext('2d', { willReadFrequently: true });
|
||||
if (!ctx) throw new Error('2d context unavailable');
|
||||
ctx.drawImage(img, 0, 0);
|
||||
return await createImageBitmap(canvas);
|
||||
} finally {
|
||||
URL.revokeObjectURL(url);
|
||||
}
|
||||
}
|
||||
|
||||
function loadImage(url: string): Promise<HTMLImageElement> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const img = new Image();
|
||||
img.onload = () => resolve(img);
|
||||
img.onerror = () => reject(new Error('image decode failed'));
|
||||
img.src = url;
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user