Chrome unterstützt createImageBitmap() in Chrome 50

Paul Lewis

Das Decodieren von Bildern für die Verwendung mit einem Canvas ist ziemlich üblich, z. B. um es Nutzenden zu ermöglichen, einen Avatar anzupassen, ein Bild zuzuschneiden oder nur ein Bild heranzuzoomen. Das Problem beim Decodieren von Bildern besteht darin, dass sie CPU-intensiv sein kann, was manchmal zu Verzögerungen oder Prüfvorgängen führen kann. Ab Chrome 50 (und in Firefox 42 und höher) hast du jetzt eine weitere Option: createImageBitmap(). Damit können Sie ein Bild im Hintergrund decodieren und Zugriff auf ein neues ImageBitmap-Primitive erhalten, das Sie auf einem Canvas zeichnen können, genauso wie mit einem <img>-Element, einem weiteren Canvas oder einem Video.

Blobs mit createImageBitmap() zeichnen

Angenommen, Sie laden ein Blob-Bild mit fetch() (oder XHR) herunter und möchten es auf einen Canvas zeichnen. Ohne createImageBitmap() müssten Sie ein Bildelement und eine Blob-URL erstellen, um das Bild in ein geeignetes Format zu bringen. So erhalten Sie einen viel direkteren Weg zum Malen:

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

Dieser Ansatz funktioniert auch mit Bildern, die als Blobs in IndexedDB gespeichert sind. Blobs sind also ein praktisches Zwischenformat. Chrome 50 unterstützt auch die .toBlob()-Methode für Canvas-Elemente. So können Sie beispielsweise Blobs aus Canvas-Elementen generieren.

createImageBitmap() in Web Workern verwenden

Eine der besten Funktionen von createImageBitmap() ist, dass es auch in Workern verfügbar ist. Das bedeutet, dass Sie Bilder jetzt überall decodieren können. Wenn Sie viele zu decodierende Bilder haben, die Sie für unwichtig halten, senden Sie die entsprechenden URLs an einen Web Worker, der die Bilder herunterladen und decodieren kann, sobald es Zeit ist. Sie werden dann zurück in den Hauptthread übertragen, um auf einem Canvas zu zeichnen.

Datenfluss mit createImageBitmap und Web Workern

Der entsprechende Code könnte etwa so aussehen:

// 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);
}

Wenn Sie heute createImageBitmap() im Hauptthread aufrufen, wird genau an dieser Stelle die Decodierung durchgeführt. Es ist jedoch geplant, dass Chrome die Decodierung in einem anderen Thread automatisch durchführt, um die Arbeitslast des Hauptthreads niedrig zu halten. In der Zwischenzeit sollten Sie die Decodierung im Hauptthread jedoch im Auge behalten, da diese intensive Arbeit andere wichtige Aufgaben wie JavaScript, Stilberechnungen, Layout, Malerei oder Compositing blockieren könnte.

Eine Hilfsbibliothek

Um das Leben etwas einfacher zu gestalten, habe ich eine Hilfsbibliothek erstellt, die das Decodieren eines Workers übernimmt, das decodierte Bild an den Hauptthread zurücksendet und es in einen Canvas zeichnet. Natürlich können Sie auch ein Reverse Engineering durchführen und das Modell auf Ihre eigenen Apps anwenden. Der größte Vorteil ist mehr Kontrolle, das aber (wie immer) mehr Code, mehr Fehler beim Debuggen und mehr Grenzfälle beinhaltet, als die Verwendung eines <img>-Elements zu berücksichtigen ist.

Wenn Sie mehr Kontrolle über die Bilddecodierung benötigen, ist createImageBitmap() die neue beste Freundin. Probiert es einfach mal in Chrome 50 aus und teilt uns mit, wie ihr es weitergeht!