La decodificación de imágenes para usarlas con un lienzo es bastante común, ya sea para permitir que los usuarios personalicen un avatar, recorten una imagen o simplemente acerquen una foto. El problema con la decodificación de imágenes es que puede ser intensivo en la CPU, lo que, a veces, puede provocar interrupciones o un efecto de tablero de ajedrez. A partir de Chrome 50 (y en Firefox 42 y versiones posteriores), ahora tienes otra opción: createImageBitmap()
. Te permite decodificar una imagen en segundo plano y obtener acceso a un nuevo primitivo ImageBitmap
, que puedes dibujar en un lienzo de la misma manera que lo harías con un elemento <img>
, otro lienzo o un video.
Cómo dibujar manchas con createImageBitmap()
Supongamos que descargas una imagen de blob con fetch()
(o XHR) y quieres dibujarla en un lienzo. Sin createImageBitmap()
, tendrías que crear un elemento de imagen y una URL de blob para que la imagen tenga un formato que puedas usar. Con ella, tienes un camino mucho más directo hacia la pintura:
fetch(url)
.then(response => response.blob())
.then(blob => createImageBitmap(blob))
.then(imageBitmap => ctx.drawImage(imageBitmap, 0, 0));
Este enfoque también funcionará con imágenes almacenadas como objetos blob en IndexedDB, lo que los convierte en un formato intermedio conveniente. Al parecer, Chrome 50 también admite el método .toBlob()
en elementos lienzo, lo que significa que puedes, por ejemplo, generar BLOB a partir de elementos lienzo.
Cómo usar createImageBitmap() en trabajadores web
Una de las funciones más interesantes de createImageBitmap()
es que también está disponible en trabajadores, lo que significa que ahora puedes decodificar imágenes donde quieras. Si tienes muchas imágenes que consideras no esenciales para decodificar, envía sus URLs a un trabajador web, que las descargará y decodificará a medida que el tiempo lo permita. Luego, los transferiría al subproceso principal para dibujar en un lienzo.
El código para hacerlo podría verse así:
// 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);
}
Hoy, si llamas a createImageBitmap()
en el subproceso principal, es exactamente allí donde se realizará la decodificación. Sin embargo, se planea que Chrome realice la decodificación automáticamente en otro subproceso, lo que ayudará a mantener inactiva la carga de trabajo del subproceso principal. Mientras tanto, debes tener en cuenta que debes realizar la decodificación en el subproceso principal, ya que es un trabajo intensivo que podría bloquear otras tareas esenciales, como JavaScript, los cálculos de estilo, el diseño, la pintura o la composición.
Una biblioteca auxiliar
Para simplificar un poco la vida, creé una biblioteca de ayuda que controla la decodificación en un trabajador, devuelve la imagen decodificada al subproceso principal y la dibuja en un lienzo. Por supuesto, no dudes en hacer ingeniería inversa y aplicar el modelo a tus propias apps. El mayor beneficio es tener más control, pero eso (como de costumbre) implica más código, más depuración y más casos extremos que considerar que usar un elemento <img>
.
Si necesitas más control con la decodificación de imágenes, createImageBitmap()
es tu nuevo mejor amigo. Pruébala en Chrome 50 y cuéntanos cómo te fue.