createImageBitmap() をサポート(Chrome 50)

キャンバスで使用するために画像をデコードすることは、ユーザーがアバターをカスタマイズしたり、画像を切り抜いたり、写真を拡大したりする場合によく行われます。画像のデコードは CPU 使用率が高くなるため、ジャンクやチェッカーボードが表示されることがあります。Chrome 50 以降(Firefox 42 以降)では、createImageBitmap() という別のオプションも使用できるようになりました。これにより、バックグラウンドで画像をデコードし、新しい ImageBitmap プリミティブにアクセスできます。このプリミティブは、<img> 要素、別のキャンバス、動画と同じようにキャンバスに描画できます。

createImageBitmap() によるぼかし描画

fetch()(または XHR)を使用して blob 画像をダウンロードし、キャンバスに描画するとします。createImageBitmap() がないと、画像要素と Blob URL を作成して、使用できる形式に画像を変換する必要があります。これにより、ペイントに直接アクセスできるようになります。

fetch(url)
    .then(response => response.blob())
    .then(blob => createImageBitmap(blob))
    .then(imageBitmap => ctx.drawImage(imageBitmap, 0, 0));

このアプローチは、IndexedDB に blob として保存されている画像も処理できるため、blob は便利な中間形式になります。Chrome 50 では、キャンバス要素の .toBlob() メソッドもサポートされています。つまり、キャンバス要素からブロブを生成できます。

ウェブ ワーカーでの createImageBitmap() の使用

createImageBitmap() の優れた機能の 1 つは、ワーカーでも使用できることです。つまり、任意の場所で画像をデコードできるようになりました。デコードする画像が大量にあり、それらが重要でないと思われる場合は、その URL を Web Worker に送信します。Web Worker は、時間があるときに画像をダウンロードしてデコードします。その後、メインスレッドに戻され、キャンバスに描画されます。

createImageBitmap とウェブワーカーを使用したデータフロー。

これを行うためのコードは次のようになります。

// In the worker.
fetch(imageURL)
    .then(response => response.blob())
    .then(blob => createImageBitmap(blob))
    .then(imageBitmap => {
    // Transfer the imageBitmap back to main thread.
    self.postMessage({ imageBitmap }, [imageBitmap]);
    }, err => {
    self.postMessage({ err });
    });

// In the main thread.
worker.onmessage = (evt) => {
    if (evt.data.err)
    throw new Error(evt.data.err);

    canvasContext.drawImage(evt.data.imageBitmap, 0, 0);
}

現在、メインスレッドで createImageBitmap() を呼び出すと、そのスレッドでデコードが行われます。ただし、Chrome が別のスレッドでデコードを自動的に行うことで、メインスレッドのワークロードを抑制する予定です。ただし、メインスレッドでデコードを行う場合は注意が必要です。デコードは負荷の高い処理であり、JavaScript、スタイル計算、レイアウト、ペイント、合成などの他の重要なタスクをブロックする可能性があります。

ヘルパー ライブラリ

作業を少し簡単にするために、ワーカーのデコードを処理し、デコードされた画像をメインスレッドに送り返してキャンバスに描画するヘルパー ライブラリを作成しました。もちろん、リバース エンジニアリングを行って独自のアプリに適用することもできます。主なメリットは制御の強化ですが、(通常どおり)<img> 要素を使用する場合よりもコードが増え、デバッグが増え、考慮すべきエッジケースが増えます。

画像デコードをより細かく制御する必要がある場合は、createImageBitmap() がおすすめです。Chrome 50 で試してみて、ご意見をお寄せください。