All files / src/lib heic.ts

2.43% Statements 1/41
0% Branches 0/14
0% Functions 0/8
2.56% Lines 1/39

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 722x                                                                                                                                              
"use client";
 
import { buildJpegFileName, MAX_UPLOAD_BYTES } from "@/lib/uploads";
 
async function loadImage(file: File): Promise<HTMLImageElement> {
  const objectUrl = URL.createObjectURL(file);
  try {
    const image = new Image();
    image.decoding = "async";
    await new Promise<void>((resolve, reject) => {
      image.onload = () => resolve();
      image.onerror = () => reject(new Error("Image decode failed"));
      image.src = objectUrl;
    });
    return image;
  } finally {
    URL.revokeObjectURL(objectUrl);
  }
}
 
async function canvasToJpegBlob(canvas: HTMLCanvasElement, quality: number): Promise<Blob> {
  return new Promise((resolve, reject) => {
    canvas.toBlob(
      (blob) => (blob ? resolve(blob) : reject(new Error("JPEG conversion failed"))),
      "image/jpeg",
      quality,
    );
  });
}
 
export async function convertHeifToJpeg(file: File): Promise<File> {
  const image = await loadImage(file);
  let width = image.naturalWidth;
  let height = image.naturalHeight;
  let smallestBlob: Blob | null = null;
 
  while (true) {
    const canvas = document.createElement("canvas");
    canvas.width = Math.max(1, Math.round(width));
    canvas.height = Math.max(1, Math.round(height));
    const context = canvas.getContext("2d");
    if (!context) {
      throw new Error("Canvas is unavailable for image conversion");
    }
    context.drawImage(image, 0, 0, canvas.width, canvas.height);
    for (const quality of [0.85, 0.75, 0.65, 0.55]) {
      const blob = await canvasToJpegBlob(canvas, quality);
      if (!smallestBlob || blob.size < smallestBlob.size) {
        smallestBlob = blob;
      }
      if (blob.size <= MAX_UPLOAD_BYTES) {
        return new File([blob], buildJpegFileName(file.name), {
          type: "image/jpeg",
          lastModified: file.lastModified,
        });
      }
    }
    if (Math.max(width, height) <= 2000) {
      break;
    }
    width *= 0.85;
    height *= 0.85;
  }
  if (!smallestBlob) {
    throw new Error("JPEG conversion failed");
  }
  return new File([smallestBlob], buildJpegFileName(file.name), {
    type: "image/jpeg",
    lastModified: file.lastModified,
  });
}