キャンバスで使用するために画像をデコードすることは、ユーザーがアバターをカスタマイズしたり、画像を切り抜いたり、写真を拡大したりする場合によく行われます。画像のデコードは 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 は、時間があるときに画像をダウンロードしてデコードします。その後、メインスレッドに転送してキャンバスに描画します。
コードは次のようになります。
// 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 で試してみて、ご意見をお寄せください。