UNPKG

@wordpress/upload-media

Version:
202 lines (201 loc) 5.49 kB
// packages/upload-media/src/canvas-utils.ts import { getFileBasename } from "./utils.mjs"; import { parseHeic } from "./heic-parser.mjs"; async function canvasConvertToJpeg(file, quality = 0.82) { const baseName = getFileBasename(file.name); try { const bitmap = await createImageBitmap(file); try { const canvas = new OffscreenCanvas(bitmap.width, bitmap.height); const ctx = canvas.getContext("2d"); if (!ctx) { throw new Error("Could not get canvas 2d context"); } ctx.drawImage(bitmap, 0, 0); const jpegBlob = await canvas.convertToBlob({ type: "image/jpeg", quality }); return new File([jpegBlob], `${baseName}.jpg`, { type: "image/jpeg" }); } finally { bitmap.close(); } } catch { } if (typeof ImageDecoder !== "undefined") { const supported = await ImageDecoder.isTypeSupported(file.type); if (supported) { const decoder = new ImageDecoder({ type: file.type, data: file.stream() }); try { const { image: videoFrame } = await decoder.decode(); try { const canvas = new OffscreenCanvas( videoFrame.displayWidth, videoFrame.displayHeight ); const ctx = canvas.getContext("2d"); if (!ctx) { throw new Error("Could not get canvas 2d context"); } ctx.drawImage(videoFrame, 0, 0); const jpegBlob = await canvas.convertToBlob({ type: "image/jpeg", quality }); return new File([jpegBlob], `${baseName}.jpg`, { type: "image/jpeg" }); } finally { videoFrame.close(); } } finally { decoder.close(); } } } if (typeof VideoDecoder !== "undefined") { try { const heicData = parseHeic(await file.arrayBuffer()); const support = await VideoDecoder.isConfigSupported({ codec: heicData.codecString }); if (support.supported) { const canvas = new OffscreenCanvas( heicData.outputWidth, heicData.outputHeight ); const ctx = canvas.getContext("2d"); if (!ctx) { throw new Error("Could not get canvas 2d context"); } for (const tile of heicData.tiles) { const frame = await decodeHevcFrame( heicData.codecString, heicData.description, heicData.tileWidth, heicData.tileHeight, tile.data ); try { ctx.drawImage(frame, tile.x, tile.y); } finally { frame.close(); } } const outputCanvas = heicData.rotation !== 0 ? applyRotation(canvas, heicData.rotation) : applyExifOrientation( canvas, heicData.exifOrientation ); const jpegBlob = await outputCanvas.convertToBlob({ type: "image/jpeg", quality }); return new File([jpegBlob], `${baseName}.jpg`, { type: "image/jpeg" }); } } catch { } } throw new Error( "This browser cannot decode HEIC images. Please use Safari or convert to JPEG before uploading." ); } function applyRotation(source, rotation) { if (rotation === 0) { return source; } const swap = rotation === 90 || rotation === 270; const w = swap ? source.height : source.width; const h = swap ? source.width : source.height; const rotated = new OffscreenCanvas(w, h); const ctx = rotated.getContext("2d"); if (!ctx) { return source; } ctx.translate(w / 2, h / 2); ctx.rotate(-rotation * Math.PI / 180); ctx.drawImage(source, -source.width / 2, -source.height / 2); return rotated; } function applyExifOrientation(source, orientation) { if (orientation <= 1 || orientation > 8) { return source; } const { width: sw, height: sh } = source; const swap = orientation >= 5; const out = new OffscreenCanvas(swap ? sh : sw, swap ? sw : sh); const ctx = out.getContext("2d"); if (!ctx) { return source; } switch (orientation) { case 2: ctx.transform(-1, 0, 0, 1, sw, 0); break; case 3: ctx.transform(-1, 0, 0, -1, sw, sh); break; case 4: ctx.transform(1, 0, 0, -1, 0, sh); break; case 5: ctx.transform(0, 1, 1, 0, 0, 0); break; case 6: ctx.transform(0, 1, -1, 0, sh, 0); break; case 7: ctx.transform(0, -1, -1, 0, sh, sw); break; case 8: ctx.transform(0, -1, 1, 0, 0, sw); break; } ctx.drawImage(source, 0, 0); return out; } function decodeHevcFrame(codec, description, width, height, data) { return new Promise((resolve, reject) => { const decoder = new VideoDecoder({ output: (frame) => { decoder.close(); resolve(frame); }, error: (e) => { if (decoder.state !== "closed") { decoder.close(); } reject(e); } }); decoder.configure({ codec, codedWidth: width, codedHeight: height, description }); decoder.decode( new EncodedVideoChunk({ type: "key", timestamp: 0, data }) ); decoder.flush().catch((e) => { if (decoder.state !== "closed") { decoder.close(); } reject(e); }); }); } export { canvasConvertToJpeg }; //# sourceMappingURL=canvas-utils.mjs.map