@wordpress/upload-media
Version:
Core media upload logic.
202 lines (201 loc) • 5.49 kB
JavaScript
// 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