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 apenas aumentem o zoom em uma imagem. O problema com a decodificação de imagens é que isso pode consumir muita CPU e, às vezes, significar instabilidade ou tabuleiro de xadrez. A partir do Chrome 50 (e no Firefox 42+), agora você tem outra opção: createImageBitmap(). Ele permite decodificar uma imagem em segundo plano e ter acesso a uma nova primitiva ImageBitmap, que pode ser desenhada em uma tela da mesma forma que 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ê precisaria criar um elemento de imagem e um URL do Blob para que a imagem ficasse em um formato que pudesse ser usado. Com ele, o caminho para a pintura é muito mais direto:

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. O Chrome 50 também oferece suporte ao método .toBlob() em elementos de tela, o que significa que você pode, por exemplo, gerar blobs de elementos de tela.

Como usar createImageBitmap() em workers da Web

Um dos recursos mais legais do createImageBitmap() é que ele também está disponível em workers, o que significa que agora você pode decodificar imagens onde quiser. Se você tiver muitas imagens para decodificar que considera não essenciais, envie os URLs delas para um worker da Web, que fará o download e a decodificação conforme o tempo permitir. Em seguida, ele as transfere de volta para a linha de execução principal para renderização em uma tela.

Fluxo de dados com createImageBitmap e workers da Web.

O código para fazer isso pode ficar assim:

// 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, é exatamente aí que a decodificação será feita. No entanto, os planos são para 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 baixa. Enquanto isso, lembre-se de fazer a decodificação na linha de execução principal, porque é um trabalho intenso que pode bloquear outras tarefas essenciais, como JavaScript, cálculos de estilo, layout, pintura ou composição.

Uma biblioteca auxiliar

Para facilitar um pouco as coisas, criei uma biblioteca auxiliar que processa a decodificação em um worker, envia a imagem decodificada de volta para a linha de execução principal e a desenha em uma tela. É claro que você pode fazer engenharia reversa e aplicar o modelo aos seus próprios apps. O principal benefício é mais controle, mas isso (como de costume) vem com mais código, mais para depuração e mais casos extremos a serem considerados do que o uso de um elemento <img>.

Se você precisar de mais controle com a decodificação de imagens, createImageBitmap() será seu novo melhor amigo. Confira no Chrome 50 e nos conte o que achou.