画像をデコードしてキャンバスで使用することは、ユーザーがアバターをカスタマイズしたり、画像を切り抜いたり、画像を拡大したりするだけの場合によく使用されます。画像のデコードに関する問題は、CPU 使用率が高くなる可能性があり、ジャンクやチェッカーボードが発生する可能性があることです。Chrome 50(および Firefox 42 以降)では、createImageBitmap()
という別のオプションを使用できるようになりました。これを使用すると、背景の画像をデコードして、新しい ImageBitmap
プリミティブにアクセスできます。このプリミティブは、<img>
要素、別のキャンバス、動画と同じようにキャンバスに描画できます。
createImageBitmap() を使用して blob を描画する
たとえば、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()
メソッドもサポートされているため、キャンバス要素から blob などを生成できます。
ウェブワーカーでの createImageBitmap() の使用
createImageBitmap()
の最も優れた機能の一つは、ワーカーでも使用できることです。つまり、任意の場所で画像をデコードできるようになりました。デコードする画像が大量にあり、重要ではないと思われる場合は、その URL をウェブワーカーに送ります。ウェブワーカーは、時間の許す限りダウンロードとデコードを行います。その後、メインスレッドに戻してキャンバスに描画します。
これを行うためのコードは次のようになります。
// 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 でお試しいただき、ぜひご感想をお聞かせください。