Chrome oferece suporte a createImageBitmap() no Chrome 50

Decodificar imagens para usar com uma tela é bastante comum, seja para permitir que os usuários personalizem um avatar, cortem uma imagem ou aumentem o zoom de uma imagem. O problema com a decodificação de imagens é que isso pode consumir muita CPU, e isso às vezes pode significar instabilidade ou quadriculação. A partir do Chrome 50 (e no Firefox 42+), agora você tem outra opção: createImageBitmap(). Ela permite que você decodifique uma imagem em segundo plano e tenha acesso a um novo primitivo ImageBitmap, que pode ser desenhado em uma tela da mesma forma que faria com um elemento <img>, outra tela ou um vídeo.

Como desenhar blobs com createImageBitmap()

Digamos que você faça o download de uma imagem blob com fetch() (ou XHR) e queira desenhá-la em uma tela. Sem createImageBitmap(), você teria que criar um elemento de imagem e um URL de Blob para colocar a imagem em um formato que você pudesse usar. Com ela, você tem um caminho muito mais direto para a pintura:

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

Essa abordagem também funciona com imagens armazenadas como blobs no IndexedDB, tornando os blobs um formato intermediário conveniente. Como acontece, o Chrome 50 também suporta o método .toBlob() em elementos de tela, o que significa que você pode, por exemplo, gerar blobs a partir de elementos de tela.

Como usar createImageBitmap() em web workers

Um dos melhores recursos do createImageBitmap() é que ele também está disponível em workers. Isso significa que agora você pode decodificar imagens sempre que quiser. Se você tiver muitas imagens para decodificar que não são essenciais, envie os URLs delas para um Web Worker, que as decodifica e faz o download quando o tempo permitir. Em seguida, ele é transferido de volta para a linha de execução principal para que sejam desenhados em uma tela.

Fluxo de dados com createImageBitmap e web workers.

O código para fazer isso pode ser semelhante a este:

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

Hoje, se você chamar createImageBitmap() na linha de execução principal, a decodificação será feita exatamente nesse ponto. No entanto, planejamos fazer com que o Chrome faça a decodificação automaticamente em outra linha de execução, ajudando a manter a carga de trabalho da linha de execução principal reduzida. Enquanto isso, no entanto, tenha cuidado para decodificar na linha de execução principal, já que esse é um trabalho intenso que pode bloquear outras tarefas essenciais, como JavaScript, cálculos de estilo, layout, pintura ou composição.

Uma biblioteca auxiliar

Para simplificar um pouco a vida, criei uma biblioteca auxiliar que processa a decodificação em um worker, envia de volta a imagem decodificada para a linha de execução principal e a desenha em uma tela. Fique à vontade para fazer engenharia reversa e aplicar o modelo nos seus próprios apps. O principal benefício é mais controle, mas isso, como de costume, vem com mais código, mais para depurar e mais casos extremos a serem considerados do que o uso de um elemento <img>.

Se você precisa de mais controle com a decodificação de imagens, createImageBitmap() é seu novo melhor amigo. Confira essa novidade no Chrome 50 e conte-nos como você se sai!