domingo, 28 de febrero de 2016

Procesando imágenes con API CANVAS



API Canvas no sería nada sin la capacidad de procesar imágenes. Pero incluso cuando las imágenes son un elemento tan importante para una aplicación gráfica, solo un método nativo fue provisto para trabajar con ellas.

drawImage()

El método drawImage() es el único a cargo de dibujar una imagen en el lienzo. Sin embargo, este método puede recibir un número de valores que producen diferentes resultados. Estudiemos estas posibilidades:

drawImage(imágen, x, y) :Esta sintaxis es para dibujar una imagen en el lienzo en la posición declarada por x e y. El primer valor es una referencia a la imagen que será dibujada.
drawImage(imágen, x, y, ancho, alto) :Esta sintaxis nos permite escalar la imagen antes de dibujarla en el lienzo, cambiando su tamaño con los valores de los atributos ancho y alto. 
drawImage(imágen, x1, y1, ancho1, alto1, x2, y2, ancho2, alto2) :Esta es la sintaxis más compleja. Hay dos valores para cada parámetro. El propósito es cortar partes de la imagen y luego dibujarlas en el lienzo con un tamaño y una posición específica. Los valores x1 e y1 declaran la esquina superior izquierda de la parte de la imagen que será cortada. Los valores ancho1 y alto1 indican el tamaño de esta pieza. El resto de los valores (x2, y2, ancho2 y alto2) declaran el lugar donde la pieza será dibujada en el lienzo y su nuevo tamaño (el cual puede ser igual o diferente al original).

En cada caso, el primer atributo puede ser una referencia a una imagen en el mismo documento generada por métodos como getElementById(), o creando un nuevo objeto imagen usando métodos regulares de Javascript. No es posible usar una URL o cargar un archivo desde una fuente externa directamente con este método.

Código 7-22. Trabajando con imágenes.

function iniciar(){
var elemento=document.getElementById('lienzo');
lienzo=elemento.getContext('2d');
var imagen=new Image();
imagen.src="http://www.minkbooks.com/content/snow.jpg";
imagen.addEventListener("load", function(){
lienzo.drawImage(imagen,20,20)
}, false);
}
window.addEventListener("load", iniciar, false);

Comencemos con un simple ejemplo. El código 7-22 lo único que hace es cargar la imagen y dibujarla en el lienzo. Debido a que el lienzo solo puede dibujar imágenes que ya están completamente cargadas, necesitamos controlar esta situación escuchando al evento load. Agregamos una escucha para este evento y declaramos una función anónima para responder al mismo. El método drawImage() dentro de esta función dibujará la imagen cuando fue completamente cargada. 
Conceptos básicos: En el Código 7-22, dentro del método addEventListener(), usamos una función anónima en lugar de una referencia a una función normal. En casos como éste, cuando la función es pequeña, esta técnica vuelve al código más simple y fácil de entender.  

Código 7-23. Ajustando la imagen al tamaño del lienzo.

function iniciar(){
var elemento=document.getElementById('lienzo');
lienzo=elemento.getContext('2d');
var imagen=new Image();
imagen.src="http://www.minkbooks.com/content/snow.jpg";
imagen.addEventListener("load", function(){
lienzo.drawImage(imagen,0,0,elemento.width,elemento.height)
}, false);
}
window.addEventListener("load", iniciar, false);

En el Código 7-23, agregamos dos valores al método drawImage() utilizado previamente para cambiar el tamaño de la imagen. Las propiedades width y height retornan las medidas del lienzo, por lo que la imagen será estirada por este código hasta cubrir el lienzo por completo.

Código 7-24. Extrayendo, cambiando el tamaño y dibujando.

function iniciar(){
var elemento=document.getElementById('lienzo');
lienzo=elemento.getContext('2d');
var imagen=new Image();
imagen.src="http://www.minkbooks.com/content/snow.jpg";
imagen.addEventListener("load", function(){
lienzo.drawImage(imagen,135,30,50,50,0,0,200,200)
}, false);
}
window.addEventListener("load", iniciar, false);

En el Código 7-24 el código presenta la sintaxis más compleja del método drawImage(). 
Nueve valores fueron provistos para obtener una parte de la imagen original, cambiar su tamaño y luego dibujarla en el lienzo. Tomamos un cuadrado de la imagen original desde la posición 135,50, con un tamaño de 50,50 pixeles. Este bloque es redimensionado a 200,200 pixeles y finalmente dibujado en el lienzo en la posición 0,0.


Datos de imágenes

Cuando dijimos previamente que drawImage() era el único método disponible para dibujar imágenes en el lienzo, mentimos. Existen unos poderosos métodos para procesar imágenes en esta API que además pueden dibujarlas en el lienzo. Debido a que estos métodos no trabajan con imágenes sino con datos, nuestra declaración previa sigue siendo legítima. ¿Pero por qué desearíamos procesar datos en lugar de imágenes? 
Toda imagen puede ser representada por una sucesión de números enteros representando valores rgba (cuatro valores para cada pixel). Un grupo de valores con esta información resultará en un array unidimensional que puede ser usado luego para generar una imagen. La API Canvas ofrece tres métodos para manipular datos y procesar imágenes de este modo:

getImageData(x, y, ancho, alto) :Este método toma un rectángulo del lienzo del tamaño declarado por sus atributos y lo convierte en datos. Retorna un objeto que puede ser luego accedido por sus propiedades width, height y data.
putImageData(datosImagen, x, y) :Este método convierte a los datos en datosImagen en una imagen y dibuja la imagen en el lienzo en la posición especificada por x e y. Este es el opuesto a getImageData().
createImageData(ancho, alto) :Este método crea datos para representar una imagen vacía. Todos sus pixeles serán de color negro transparente. Puede también recibir datos como atributo (en lugar de los atributos ancho y alto) y utilizar las dimensiones tomadas de los datos provistos para crear la imagen. 

La posición de cada valor en el array es calculada con la fórmula (ancho×4×y)+(x×4). Éste será el primer valor del pixel (rojo); para el resto tenemos que agregar 1 al resultado (por ejemplo, (ancho×4×y)+(x×4)+1 para verde, (ancho×4×y)+(x×4)+2 para azul, y (ancho×4×y)+(x×4)+3 para el valor alpha (transparencia). Veamos esto en práctica:
IMPORTANTE: Debido a restricciones de seguridad, no se puede extraer información del elemento <canvas> luego de que una imagen tomada desde una fuente externa fue dibujada en el lienzo. Solo cuando el documento y la imagen corresponden a la misma fuente (URL) el método getImageData() trabajará adecuadamente. Por este motivo, para probar este ejemplo tendrá que descargar la imagen desde nuestro servidor en www.minkbooks.com/content/snow.jpg (o usar una imagen propia), y luego subir esta imagen, el archivo HTML y el archivo con el código Javascript a su propio servidor. Si simplemente trata de ejecutar el siguiente ejemplo en su ordenador sin seguir los pasos previos, no funcionará.

Código 7-25. Generando un negativo de la imagen.

function iniciar(){
var elemento=document.getElementById('lienzo');
lienzo=elemento.getContext('2d');
var imagen=new Image();
imagen.src="snow.jpg";
imagen.addEventListener("load", modificarimagen, false);
}

function modificarimagen(e){
imagen=e.target;
lienzo.drawImage(imagen,0,0);
var info=lienzo.getImageData(0,0,175,262);
var pos;
for(x=0;x<=175;x++){
for(y=0;y<=262;y++){
pos=(info.width*4*y)+(x*4);
info.data[pos]=255-info.data[pos];
info.data[pos+1]=255-info.data[pos+1];
info.data[pos+2]=255-info.data[pos+2];
}
}
lienzo.putImageData(info,0,0);
}
window.addEventListener("load", iniciar, false);

Esta vez tuvimos que crear una nueva función (en lugar de utilizar una función anónima) para procesar la imagen luego de que es cargada. Primero, la función modificarimagen() genera una referencia a la imagen aprovechando la propiedad target usada en publicaciones anteriores. En el siguiente paso, usando esta referencia y el método drawImage(), la imagen es dibujada en el lienzo en la posición 0,0. No hay nada inusual en esta parte del código, pero eso es algo que pronto va a cambiar. 
IMPORTANTE: Los archivos para este ejemplo deben ser subidos a su propio servidor para trabajar correctamente (incluyendo la imagen snow.jpg que puede descargar desde www.minkbooks.com/content/snow.jpg). 
La imagen utilizada en nuestro ejemplo tiene un tamaño de 350 pixeles de ancho por 262 pixeles de alto, por lo que usando el método getImageData() con los valores 0,0 para la esquina superior izquierda y 175,262 para el valor horizontal y vertical, estamos extrayendo solo la mitad izquierda de la imagen original. Estos datos son grabados dentro de la variable info.
Una vez que esta información fue recolectada, es momento de manipular cada pixel para obtener el resultado que queremos (en nuestro ejemplo esto será un negativo de este trozo de la imagen).
Debido a que cada color es declarado por un valor entre 0 y 255, el valor negativo es obtenido restando el valor real a 255 con la fórmula color=255-color. Para hacerlo con cada pixel de la imagen, debemos crear dos bucles for (uno para las columnas y otro para las filas) para obtener cada color original y calcular el valor del negativo correspondiente.
El bucle for para los valores x va desde 0 a 175 (el ancho de la parte de la imagen que extrajimos del lienzo) y el for para los valores y va desde 0 a 262 (el tamaño vertical de la imagen y también el tamaño vertical del trozo de imagen que estamos procesando).

Luego de que cada pixel es procesado, la variable info con los datos de la imagen es enviada al lienzo como una imagen usando el método putImageData(). La imagen es ubicada en la misma posición que la original, reemplazando la mitad izquierda de la imagen original por el negativo que acabamos de crear. 
El método getImageData() retorna un objeto que puede ser procesado a través de sus propiedades (width, height y data) o puede ser usado íntegro por el método putImageData().
Existe otra manera de extraer datos del lienzo que retorna el contenido en una cadena de texto codificada en base64. Esta cadena puede ser usada luego como fuente para otro lienzo, como fuente de un elemento HTML (por ejemplo, <img>), o incluso ser enviado al servidor o grabado en un archivo. El siguiente es el método incluido con este fin: 

toDataURL(tipo) :El elemento <canvas> tiene dos propiedades, width y height, y dos métodos: getContext() y toDataURL(). Este último método retorna datos en el formato data:url conteniendo una representación del contenido del lienzo en formato PNG (o el formato de imagen especificado en el atributo tipo). 

Más adelante en esta serie de publicaciones veremos algunos ejemplos de cómo usar toDataURL() y cómo puede ayudarnos a integrar esta API con otras. 
Conceptos básicos: Los datos del tipo data:url son datos que son presentados en forma de cadena de texto y pueden ser incluidos en nuestros documentos como si se tratara de datos tomados de fuentes externas (por ejemplo, la fuente para imágenes insertadas con la etiqueta <img>). 

Patrones

Los patrones son simples adiciones que pueden mejorar nuestros trazados. Con esta herramienta podemos agregar textura a nuestras figuras utilizando una imagen. El procedimiento es similar a la creación de gradientes; los patrones son creados por el método createPattern() y luego aplicados al trazado como si fuesen un color. 
createPattern(imágen, tipo) El atributo imágen es una referencia a la imagen que vamos a usar como patrón, y tipo configura el patrón por medio de cuatro valores: repeat, repeat-x, repeat-y y no-repeat.

Código 7-26. Agregando un patrón para nuestro trazado.

function iniciar(){
var elemento=document.getElementById('lienzo');
lienzo=elemento.getContext('2d');
var imagen=new Image();
imagen.src="http://www.minkbooks.com/content/bricks.jpg";
imagen.addEventListener("load", modificarimagen, false);
}
function modificarimagen(e){
imagen=e.target;
var patron=lienzo.createPattern(imagen,'repeat');
lienzo.fillStyle=patron;
lienzo.fillRect(0,0,500,300);
}
window.addEventListener("load", iniciar, false);

Hágalo usted mismo: Experimente con los diferentes valores disponibles para createPattern() y también utilizando otras figuras.



Espero haber ayudado en algo. Hasta la próxima oportunidad!

DESCARGAR PUBLICACIÓN










  

No hay comentarios:

Publicar un comentario en la entrada