Chrome supporta createImageBitmap() in Chrome 50

Paul Lewis

La decodifica delle immagini per l'utilizzo con una tela è abbastanza comune, che si tratti di consentire agli utenti di personalizzare un avatar, ritagliare un'immagine o semplicemente aumentare lo zoom di una foto. Il problema con la decodifica delle immagini è che può richiedere un utilizzo intensivo della CPU e, a volte, può causare scatti o quadrettature. A partire da Chrome 50 (e in Firefox 42 e versioni successive) ora hai un'altra opzione: createImageBitmap(). Ti consente di decodificare un'immagine in background e di accedere a una nuova primitiva ImageBitmap, che puoi disegnare in una tela nello stesso modo in cui disegneresti un elemento <img>, un'altra tela o un video.

Disegno di blob con createImageBitmap()

Supponiamo che scarichi un'immagine blob con fetch() (o XHR) e vuoi disegnarla in un canvas. Senza createImageBitmap(), dovresti creare un elemento immagine e un URL BLOB per ottenere l'immagine in un formato utilizzabile. In questo modo, avrai un percorso molto più diretto per dipingere:

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

Questo approccio funziona anche con le immagini archiviate come blob in IndexedDB, rendendo i blob un formato intermedio pratico. Chrome 50 supporta anche il metodo .toBlob() negli elementi canvas, il che significa che puoi, ad esempio, generare blob dagli elementi canvas.

Utilizzo di createImageBitmap() nei worker web

Una delle funzionalità più interessanti di createImageBitmap() è che è disponibile anche nei worker, il che significa che ora puoi decodificare le immagini ovunque tu voglia. Se hai molte immagini da decodificare che consideri non essenziali, dovresti spedire i relativi URL a un web worker, che provvederà a scaricarli e a decodificarli secondo il tempo a disposizione. Poi li trasferirà di nuovo nel thread principale per il disegno in una tela.

Flusso di dati con createImageBitmap e web worker.

Il codice per eseguire questa operazione potrebbe avere il seguente aspetto:

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

Oggi, se chiami createImageBitmap() nel thread principale, è esattamente lì che verrà eseguita la decodifica. Tuttavia, è previsto che Chrome esegua automaticamente la decodifica in un altro thread, contribuendo a ridurre il carico di lavoro del thread principale. Nel frattempo, tuttavia, dovresti essere consapevole di eseguire la decodifica sul thread principale, in quanto si tratta di un lavoro impegnativo che potrebbe bloccare altre attività essenziali, come JavaScript, calcoli di stile, layout, disegno o compositing.

Una libreria di supporto

Per semplificare un po' le cose, ho creato una libreria di supporto che gestisce la decodifica su un worker, restituisce l'immagine decodificata al thread principale e la disegna in una tela. Naturalmente, puoi eseguire il reverse engineering e applicare il modello alle tue app. Il vantaggio principale è un maggiore controllo, ma (come al solito) comporta più codice, più da eseguire il debug e più casi limite da considerare rispetto all'utilizzo di un elemento <img>.

Se hai bisogno di un maggiore controllo sulla decodifica delle immagini, createImageBitmap() è il tuo nuovo migliore amico. Prova questa funzionalità in Chrome 50 e facci sapere come va.