Files
lemonspace_app/components/canvas/canvas-media-utils.ts

97 lines
2.4 KiB
TypeScript

export async function getImageDimensions(
file: File,
): Promise<{ width: number; height: number }> {
const image = await decodeImageFile(file);
return { width: image.naturalWidth, height: image.naturalHeight };
}
export type ImagePreviewOptions = {
maxEdge?: number;
format?: string;
quality?: number;
};
export type CompressedImagePreview = {
blob: Blob;
width: number;
height: number;
};
export async function createCompressedImagePreview(
file: File,
options: ImagePreviewOptions = {},
): Promise<CompressedImagePreview> {
const maxEdge = options.maxEdge ?? 640;
const format = options.format ?? "image/webp";
const quality = options.quality ?? 0.75;
const image = await decodeImageFile(file);
const sourceWidth = image.naturalWidth;
const sourceHeight = image.naturalHeight;
if (!sourceWidth || !sourceHeight) {
throw new Error("Could not read image dimensions");
}
const scale = Math.min(1, maxEdge / Math.max(sourceWidth, sourceHeight));
const targetWidth = Math.max(1, Math.round(sourceWidth * scale));
const targetHeight = Math.max(1, Math.round(sourceHeight * scale));
const canvas = document.createElement("canvas");
canvas.width = targetWidth;
canvas.height = targetHeight;
const context = canvas.getContext("2d");
if (!context) {
throw new Error("Could not create canvas context");
}
context.drawImage(image, 0, 0, targetWidth, targetHeight);
const blob = await new Promise<Blob>((resolve, reject) => {
canvas.toBlob(
(result) => {
if (!result) {
reject(new Error("Could not encode preview image"));
return;
}
resolve(result);
},
format,
quality,
);
});
return {
blob,
width: targetWidth,
height: targetHeight,
};
}
async function decodeImageFile(file: File): Promise<HTMLImageElement> {
return new Promise((resolve, reject) => {
const objectUrl = URL.createObjectURL(file);
const image = new window.Image();
image.onload = () => {
const width = image.naturalWidth;
const height = image.naturalHeight;
URL.revokeObjectURL(objectUrl);
if (!width || !height) {
reject(new Error("Could not read image dimensions"));
return;
}
resolve(image);
};
image.onerror = () => {
URL.revokeObjectURL(objectUrl);
reject(new Error("Could not decode image"));
};
image.src = objectUrl;
});
}