Chrome obsługuje createImageBitmap() w Chrome 50

Dekodowanie obrazów na potrzeby obszaru roboczego jest dość powszechne, niezależnie od tego, czy chodzi o dostosowanie awatara, przycięcie obrazu czy po prostu jego powiększenie. Problem z dekodowaniem obrazów polega na tym, że może ono mocno obciążać procesor i czasem wiązać się z zacinaniem lub szachem. Od Chrome 50 (i przeglądarki Firefox w wersji 42 i nowszych) dostępne są kolejne opcje: createImageBitmap(). Pozwala na dekodowanie obrazu w tle i uzyskanie dostępu do nowego elementu podstawowego ImageBitmap, który możesz rysować w obszarze roboczym tak samo jak element <img>, inny obiekt canvas czy film.

Rysowanie obiektów blob za pomocą metody createImageBitmap()

Załóżmy, że pobierzesz obraz bloba za pomocą fetch() (lub XHR) i chcesz narysować go w obszarze roboczym. Bez funkcji createImageBitmap() trzeba byłoby utworzyć element obrazu i adres URL obiektu blob, aby pobrać obraz do odpowiedniego formatu. Tak właśnie wygląda malowanie:

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

Ta metoda będzie też działać z obrazami przechowywanymi jako obiekty blob w IndexedDB, dzięki czemu są one wygodnym formatem pośrednim. Chrome 50 obsługuje także metodę .toBlob() w przypadku elementów canvas, co oznacza, że możesz na przykład generować bloby z elementów canvas.

Używanie metody createImageBitmap() w instancjach roboczych

Jedną z największych zalet createImageBitmap() jest to, że jest też dostępna w obszarach roboczych, co oznacza, że teraz możesz dekodować obrazy w dowolnym miejscu. Jeśli masz do zdekodowania dużo obrazów, które Twoim zdaniem są zbędne, musisz wysłać ich adresy URL do Web Worker, który w miarę możliwości je pobierze i dekoduje. Następnie są przenoszone z powrotem do głównego wątku, gdzie rysuje się w obszarze roboczym.

Przepływ danych z createImageBitmap i instancjami roboczymi.

Przykładowy kod może wyglądać tak:

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

Dzisiaj, jeśli wywołasz polecenie createImageBitmap() w wątku głównym, właśnie tam zostanie przeprowadzone dekodowanie. Plany mają jednak na celu automatyczne dekodowanie przez Chrome w innym wątku, co pozwoli ograniczyć obciążenie głównego wątku. Tymczasem pamiętaj o dekodowaniu wątku głównego, ponieważ jest to intensywna praca, która może zablokować inne podstawowe zadania, takie jak JavaScript, obliczanie stylu, układ, malowanie czy komponowanie.

Biblioteka pomocnicza

Dla uproszczenia utworzyliśmy bibliotekę pomocniczą, która obsługuje dekodowanie w środowisku roboczym, następnie wysyła zdekodowany obraz do wątku głównego i rysuje go w obszarze roboczym. Oczywiście możesz oczywiście przeprowadzić analizę wsteczną i zastosować model w swoich aplikacjach. Główną zaletą jest większa kontrola, ale (jak zwykle) wiąże się to z większą ilością kodu, większą ilością debugowania i większą liczbą przypadków skrajnych, które należy wziąć pod uwagę niż w przypadku elementu <img>.

Jeśli chcesz mieć większą kontrolę przy dekodowaniu obrazów, createImageBitmap() to Twój najlepszy przyjaciel. Wypróbuj ją w Chrome 50 i daj nam znać, jak sobie poradzisz.