コンテンツにスキップ

OffscreenCanvasで画像処理する

  • Web Worker
  • Offscreen Canvas
  • Blob
  • Bitmap
worker.types.ts
export interface WorkerRequestBody {
buffer: ArrayBuffer;
type: string;
size: {
height: number;
width: number;
};
}
export interface WorkerResponseBody {
buffer: ArrayBuffer;
contentType: string;
}
worker.ts
async function imageProcessing(
data: WorkerRequestBody
): Promise<WorkerResponseBody> {
const { buffer, type, size } = data;
// ArrayBufferからBlobに戻す
const blob = new Blob([buffer], { type });
// OffscreenCanvasを生成
const canvas = new OffscreenCanvas(size.width, size.height);
// BlobからBitmapを作成
const bitmap = await createImageBitmap(blob);
// CanvasRenderingContext2Dを取得。(エラー処理が適当なのはサンプルなので)
const ctx = canvas.getContext("2d");
if (!ctx) throw new Error("");
// 画像をCanvasに描画。OffscreenCanvasを生成時に画像サイズを指定しているため、第4,5引数を省略しても問題ない
ctx.drawImage(bitmap, 0, 0);
// convertToBlob関数で変換。この例ではWebP固定。
const webp = await canvas.convertToBlob({
type: "image/webp",
quality: 0.85,
});
// WebPのBlobオブジェクトをArrayBufferに変換
const buf = await webp.arrayBuffer();
return {
buffer: buf,
contentType: "image/webp",
};
}
self.addEventListener("message", async (e: MessageEvent<WorkerRequestBody>) => {
const output = await imageProcessing(e.data);
// ArrayBufferをUIスレッドに移譲するため、第二引数を指定し、所有権を渡す
self.postMessage(output, [output.buffer]);
});

Worker に画像を渡して、変換後の画像を受け取る

Section titled “Worker に画像を渡して、変換後の画像を受け取る”

※ 不要なコンテキストは省略しています ※ Vite などモダンな開発環境での実行を想定した書き方です

main.ts
import MyWorker from "./worker.ts";
const worker = new MyWorker();
const fileInput = document.querySelector<HTMLInputElement>("#js-file-input");
worker.addEventListener("message", (e: MessageEvent<WorkerResponseBody>) => {
const { buffer, contentType } = e.data;
const blob = new Blob([buffer], { type: contentType });
// do something
});
fileInput.addEventListener("change", (e) => {
const [file] = Array.from(fileInput.files);
if (!file) return;
const buffer = await file.arrayBuffer();
// NOTE: よしなに
const type = "";
// NOTE: 画像の取得方法に合わせたサイズの計算処理を使って算出(EXIFとか)
const size = { height: 1080, width: 1980 }
// WebWorkerに送信&所有権移譲
worker.postMessage(
{
buffer,
type,
size
},
[buffer]
);
});