Advertisement
  1. Web Design
  2. HTML/CSS
  3. HTML

Canvas desde cero: Manipulación de píxel

Scroll to top
Read Time: 13 min
This post is part of a series called Canvas From Scratch.
Canvas From Scratch: Transformations and Gradients

() translation by (you can also view the original English article)

En el último artículo, aprendiste todo lo referente a las transformaciones, las sombras y los degradados. Hoy, voy a mostrarte cómo manipular píxeles en canvas; desde simplemente acceder a los valores de color, hasta editar imágenes dentro de canvas como si lo hicieses en un editor de fotos.

Esta es posiblemente una de las características más poderosas incorporadas directamente en canvas, y una vez lo hayas aprendido, te garantizo que tendrás toda una gama de emocionantes ideas.


Configuración

Vas a usar la misma plantilla HTML de los artículos anteriores, así que abre tu editor favorito y copia en el siguiente código:

1
<!DOCTYPE html>
2
3
<html>
4
    <head>
5
        <title>Canvas from scratch</title>
6
        <meta charset="utf-8">
7
8
        <script src="https://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>
9
10
        <script>
11
            $(document).ready(function() {
12
                var canvas = document.getElementById("myCanvas");
13
                var ctx = canvas.getContext("2d");
14
            });
15
        </script>
16
    </head>
17
18
    <body>
19
        <canvas id="myCanvas" width="500" height="500">
20
            <!-- Insert fallback content here -->
21
        </canvas>
22
    </body>
23
</html>

Esto no es más que una página HTML básica con un elemento canvas y algo de JavaScript que se ejecuta después de que se haya cargado el DOM. Nada complejo.


Colocar una imagen en canvas

Puedes manipular píxeles con cualquier cosa dibujada en canvas, pero en este tutorial, usarás imágenes. Esto se debe en parte a que es importante mostrarte cómo cargar imágenes en canvas, pero también porque la capacidad de realizar la manipulación de imágenes (por ejemplo, la edición de fotos) es una excelente característica más de esta tecnología.

Antes de mostrarte cómo acceder a los valores de píxel, coloquemos una imagen en canvas. Siéntete libre de utilizar cualquier imagen que desees, aunque en este ejemplo, yo voy a utilizar una de mis propias fotos de Flickr.

One of my photos from FlickrOne of my photos from FlickrOne of my photos from Flickr

Si lo deseas tienes permiso para utilizar esta foto, que puedes descargar en una variedad de tamaños.

Cargar una imagen en canvas requiere dos pasos. La primera es cargar la imagen en un elemento HTML image, que se puede hacer mediante HTML o mediante la creación de un nuevo elemento DOM directamente dentro de JavaScript. En este ejemplo, vas a crear un nuevo elemento DOM: es muy simple:

1
var image = new Image();
2
image.src = "sample.jpg";
3
$(image).load(function() {
4
5
});

Todo lo que estás haciendo aquí es crear un nuevo elemento Image DOM y asignarlo a una variable. A continuación, utiliza esa variable para cargar la imagen estableciendo el atributo src de la imagen en la ruta correcta. Vale la pena señalar que podrías cargar una imagen remota usando esta técnica, pero esto nos plantearía algunos problemas más adelante, así que por ahora nos quedaremos con una imagen almacenada localmente. El último paso consiste en escuchar el evento load que se desencadenará tan pronto como la imagen haya terminado de cargarse y esté disponible para su uso.

Una vez esté cargada la imagen, puedes colocarla en canvas en un solo paso. Todo lo que tienes que hacer es pasar la variable image que acabas de crear en una llamada al método drawImage del contexto de representación 2d. Colócalo dentro del evento de carga image, de la siguiente manera:

1
$(image).load(function() {
2
    ctx.drawImage(image, 0, 0);
3
});

En este caso, el método drawImage toma tres argumentos; un elemento de imagen, así como los valores de coordenadas x e y para colocar la imagen en canvas. Esto dibujará la imagen a tamaño completo (500px para esta imagen) y en la posición especificada:

Placing an imagePlacing an imagePlacing an image

Sin embargo, drawImage puede tomar otros dos argumentos que definen el ancho y el alto para dibujar la imagen, así:

1
ctx.drawImage(image, 0, 0, 250, 166);

Esto dibujaría la imagen a la mitad del tamaño original (250px para esta imagen):

Placing and resizing an imagePlacing and resizing an imagePlacing and resizing an image

Incluso puedes llevar las cosas un paso más allá y usar los nueve argumentos completos para drawImage para dibujar solo una pequeña parte de la imagen original, así:

1
ctx.drawImage(image, 0, 0, 200, 200, 0, 0, 500, 500);

Esto tomaría un cuadrado de 200px desde la parte superior izquierda de la imagen y lo dibujaría en canvas en un cuadrado de 500px:

Placing only part of an imagePlacing only part of an imagePlacing only part of an image

En el pseudo-código, los nueve argumentos drawImage completos se pueden describir así (s significa origen, y d significado destino):

1
ctx.drawImage(image, sx, sy, sw, sh, dx, dy, dw, dh);

Y el resultado se visualiza en la siguiente ilustración:

Taking drawImage to the extremeTaking drawImage to the extremeTaking drawImage to the extreme

Simple, ¿verdad? Con toda honestidad, nada en canvas es tan complicado una vez lo descompones y miras las piezas individualmente.


Acceso a los valores de píxel

Ahora que tienes una imagen en canvas es el momento de acceder a los píxeles para que puedas manipularlos. Sin embargo, olvidemos manipularlos por ahora y concentrémonos únicamente en acceder a ellos, ya que el concepto tarda un poco en comprenderse.

Problemas de seguridad

Si deseas acceder a píxeles mediante canvas, debes tener en cuenta las limitaciones de seguridad que existen. Estas limitaciones solo te permiten acceder a los datos de las imágenes cargadas en el mismo dominio que JavaScript. Esto te impide acceder a una imagen desde un servidor remoto y luego analizar sus píxeles, aunque hay una manera de evitarlo, de algún tipo. Desafortunadamente, no todos los navegadores tratan JavaScript y las imágenes ejecutadas localmente desde el sistema de archivos (es decir, sin un nombre de dominio) como si procediesen de un mismo dominio, por lo que es posible que recibas errores de seguridad. Para evitar esto, debes realizar el resto de este tutorial en un entorno de desarrollo local (como MAMP, WAMP o XAMPP) o un servidor Web remoto y acceder a los archivos mediante un nombre de dominio (como ejemplo.com).

Con esto solucionado, ¡vamos a empezar y a acceder a algunos píxeles!

Acceder a píxeles es un poco extraño

Como mencioné al principio de esta sección, la comprensión de cómo se accede a los valores de píxel en canvas lleva un poco de tiempo. Esto se debe a la forma en que los píxeles son almacenados por canvas; ¡no se almacenan como píxeles enteros en absoluto! En su lugar, los píxeles se dividen en cuatro valores independientes (rojo, verde, azul y alfa) y estos valores se almacenan en una matriz unidimensional con todos los valores de color para el resto de píxeles. Debido a esto, no puedes solicitar los datos de un píxel determinado, al menos no de forma predeterminada. Déjame explicarte.

Para tener acceso a píxeles en canvas, debes llamar al método getImageData del contexto de representación 2d, de la siguiente manera:

1
var imageData = ctx.getImageData(x, y, width, height);

Este método toma cuatro argumentos que describen un área rectangular del lienzo del que desea obtener los datos de píxeles; un origen x e y, seguido de una anchura y una altura. Devuelve un CanvasPixelArray que contiene todos los valores de color de los píxeles dentro del área definida. Lo primero que hay que notar con CanvasPixelArray es que cada píxel tiene cuatro valores de color, por lo que el índice del primer valor de color para cada píxel dentro de la matriz será un múltiplo de 4 (0 para el primer valor del primer píxel, 4 para el primer valor del segundo, etc.):

Index values in the CanvasPixelArrayIndex values in the CanvasPixelArrayIndex values in the CanvasPixelArray

Lo interesante de esta matriz (o molesto, dependiendo de cómo lo mires) es que no hay ningún concepto de posición de coordenadas (x, y), lo que significa que recuperar valores de color para un píxel específico es un poco más difícil que acceder a una matriz bidimensional (por ejemplo, usando pixelArray[0][3] para acceder al píxel en (1, 4)). En lugar de eso, necesitas usar una pequeña fórmula que sea realmente muy fácil de entender una vez explicada correctamente:

1
var redValueForPixel = ((y - 1) * (width * 4)) + ((x - 1) * 4);
Accessing a specific pixel from the CanvasPixelArrayAccessing a specific pixel from the CanvasPixelArrayAccessing a specific pixel from the CanvasPixelArray

¿Puedes averiguar qué está pasando aquí? Vamos a desglosarlo y fingir que queremos obtener los valores de color de píxel para el píxel más interno en una cuadrícula de 3x3 píxeles, el píxel en (2, 2).

Si nos fijamos en las dos imágenes anteriores se puede ver que los valores de color para este píxel comenzará en el índice 16, pero para resolver esto con código necesitas hacer dos cosas; primero calcular el índice al principio de la fila en la que se encuentra el píxel (la posición y) y, a continuación, añadir a ese índice el número de valores de color que existen entre el píxel y el principio de la fila (la posición x). Es un poco alocado, pero ten paciencia con ello.

La primera parte es fácil, ya sabes que existen cuatro valores de color por píxel, y ya conoces el ancho de la cuadrícula (3 píxeles). Para calcular el índice del píxel en la fila y (2), pasa estos valores a través de la primera parte de la fórmula, que tendría este aspecto:

1
((2 - 1) * (3 * 4))

Esto te da un índice de 12, que verás que coincide con el primer píxel de la segunda fila de las anteriores imágenes. Hasta ahora, bien.

El siguiente paso consiste en calcular el número de valores de color que existen antes del píxel que deseas de esta fila. Para ello, basta con multiplicar el número de píxeles antes del que deseas por cuatro. Simple. En este caso, la segunda parte de la fórmula tendría este aspecto:

1
((2 - 1) * 4)

Si lo deseas puedes hacer el cálculo, pero la respuesta es 4, que cuando es añadido al anterior valor te da un índice de 16. Genial, ¿no?

No me preocuparía demasiado por entenderlo completamente, solo sé que esta increíble fórmula existe para que puedas obtener fácilmente el índice del valor de color rojo para cualquier píxel. Para obtener el índice de los otros valores de color de un píxel (verde, azul o alfa), solo tienes que añadir 1, 2 o 3 respectivamente al índice calculado.

Poner esto en práctica

Ahora que ya sabes cómo tomar cualquier píxel que quieras, vamos a poner esto en práctica y a acceder a los valores de color de una imagen para cambiar el color de un fondo del sitio web. Este tipo de técnica funcionaría muy bien como selector de color para una aplicación web de edición de fotos.

El código de este ejemplo es bastante sencillo, así que vamos a atacarlo todo de una sola vez:

1
var image = new Image();
2
image.src = "sample.jpg";
3
$(image).load(function() {
4
    ctx.drawImage(image, 0, 0);
5
});
6
7
$(canvas).click(function(e) {
8
    var canvasOffset = $(canvas).offset();
9
    var canvasX = Math.floor(e.pageX-canvasOffset.left);
10
    var canvasY = Math.floor(e.pageY-canvasOffset.top);
11
12
    var imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
13
    var pixels = imageData.data;
14
    var pixelRedIndex = ((canvasY - 1) * (imageData.width * 4)) + ((canvasX - 1) * 4);
15
    var pixelcolor = "rgba("+pixels[pixelRedIndex]+", "+pixels[pixelRedIndex+1]+", "+pixels[pixelRedIndex+2]+", "+pixels[pixelRedIndex+3]+")";
16
17
    $("body").css("backgroundColor", pixelcolor);
18
});

Reconocerás las primeras líneas de los anteriores ejemplos. Todo lo nuevo está dentro del controlador de clics en el elemento canvas, que utiliza un pequeño trozo de jQuery para indicarte cuándo se ha hecho clic sobre canvas.

Dentro del controlador de clics deseas averiguar el píxel en el que el mouse ha hecho clic sobre canvas. Para ello, primero debes calcular el desplazamiento en píxeles de la posición superior izquierda del lienzo desde el borde superior izquierdo de la ventana del explorador, para ello puedes usar el método offset de jQuery. A continuación, puedes deducir el píxel en el que se ha hace clic en canvas restando el desplazamiento de la posición del mouse del evento click (pageX y pageY). Definitivamente debes pasar algún tiempo leyendo en el evento de clic de JavaScript si quieres entender esto aún más.

Las cuatro líneas siguientes capturan CanvasPixelArray para canvas (getImageData), lo almacenan en una variable, encuentran el índice del valor de color rojo para el píxel en el que se hizo clic calculándolo con la fórmula que viste antes y, a continuación, almacenan los valores de color de píxel como una cadena CSS rgba. Por último, el último paso es establecer el color de fondo del elemento body al del píxel sobre el que se ha hecho clic.

Creating a basic color pickerCreating a basic color pickerCreating a basic color picker

Y con eso terminaste. Pruébalo por ti mismo; haz clic sobre la imagen canvas y observa como cambia de color el fondo del sitio web. Si no funciona, asegúrate de que estás ejecutando la demostración en un servidor con un nombre de dominio, tal y como se ha descrito en la sección sobre problemas de seguridad.

Ha sido un largo viaje, pero ahora puedes recuperar rápida y fácilmente los valores de color de cualquier píxel en canvas. ¿Te dije que también puedes cambiar los valores de color de los píxeles en canvas? ¿No lo hice? ¡Vaya! Echemos un vistazo ahora a eso, es genial.


Aplicar efectos a las imágenes

Ahora que puedes acceder a los valores de color de píxel de canvas, cambiarlos es muy sencillo. De hecho, cambiar esos valores de color es tan simple como cambiar los valores de CanvasPixelArray y, a continuación, volver a dibujarlos en canvas. Echemos un vistazo a cómo hacer esto.

El primer paso consiste en configurar el código como lo hiciste en la anterior sección. Este código carga una imagen, la dibuja en canvas y, a continuación, toma los datos de píxeles:

1
var image = new Image();
2
image.src = "sample.jpg";
3
$(image).load(function() {
4
    ctx.drawImage(image, 0, 0);
5
6
    var imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
7
    var pixels = imageData.data;
8
    var numPixels = imageData.width * imageData.height;
9
});

Hasta ahora, bien. El siguiente paso consiste en recorrer en bucle cada píxel de canvas y cambiar sus valores de color. En este ejemplo vas a invertir los colores deduciendo el valor de color actual (entre 0 y 255) de 255:

1
for (var i = 0; i < numPixels; i++) {
2
    pixels[i*4] = 255-pixels[i*4]; // Red

3
    pixels[i*4+1] = 255-pixels[i*4+1]; // Green

4
    pixels[i*4+2] = 255-pixels[i*4+2]; // Blue

5
};

Aquí no pasa nada extraño; simplemente estás multiplicando el número de píxel (i) por 4 para obtener el índice del valor de color rojo para ese píxel en CanvasPixelArray. Al añadir 1 o 2 a ese número podrás obtener y cambiar los valores de color verde y azul respectivamente.

Por último, todo lo que necesitas hacer ahora es borrar el canvas (para deshacerte de la imagen normal) y, a continuación, utilizar el método putImageData del contexto de representación 2d para dibujar el CanvasPixelArray guardado en canvas:

1
ctx.clearRect(0, 0, canvas.width, canvas.height);
2
ctx.putImageData(imageData, 0, 0);

Y eso es honestamente todo lo que hay que hacer; recarga tu navegador y échale un vistazo por tí mismo. Genial, ¿no?

Inverting the pixels of an imageInverting the pixels of an imageInverting the pixels of an image

Resumiendo

Existen muchas más cosas en cuanto a la manipulación de píxeles en canvas, pero espero que hayas experimentado lo suficiente en este artículo como para abrirte el apetito de más. Te animo a ampliar la exploración sobre esta área y ver qué más puedes hacer con los píxeles. ¿por qué? Porque todas las técnicas que se apoyan en la manipulación de píxeles pueden utilizarse para el vídeo HTML5, además de para las imágenes. ¡Eso es genial!

En el siguiente artículo, el último de esta serie, vamos a echar un vistazo a canvas desde otra perspectiva. Esta vez, aprenderás a animar en el lienzo, lo que te dará los conceptos básicos necesarios para crear dibujos animados, animaciones y juegos. Este es indudablemente mi uso favorito de lienzo.

Advertisement
Did you find this post useful?
Want a weekly email summary?
Subscribe below and we’ll send you a weekly email summary of all new Web Design tutorials. Never miss out on learning about the next big thing.
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.