Jan 20, 2008

Jugando con pixels (I)

Empieza aquí una nueva serie de artículos, esta vez dedicados a la programación bitmap, y más concretamente, enfocados en la manipulación de imágenes a nivel de píxel. En la categoría Lab encontrarás una muestra de las cosas que se pueden hacer jugando con pixels.

La programación bitmap es uno de los temas más extensos y complejos que se pueden encontrar en AS3. Por otro lado, también es uno de los temas más apasionantes de este lenguaje, y merece la pena invertir horas en su aprendizaje.

El mayor problema que me encontré cuando decidí aprender sobre esta temática es la poca información que existe al respecto (poca en inglés, y prácticamente nula en español), así que al final, la mayoría de cosas que aprendí fue en base a experimentación y a invertir días enteros intentando descifrar códigos que encontraba por la red. Espero con estos artículos cubrir un poco el vacío existente en este campo. Vamos allá.

Nota: antes de sumergirnos en el código, es muy importante comprender cómo se componen las imágenes y los pixels, por eso he preferido dedicar este primer capítulo a la teoría de estos temas.

Introducción

La clase BitmapData permite trabajar con la información de una imagen. Fue introducida por Macromedia en Flash 8, y junto ella un nuevo abanico de posibilidades para manipular imágenes, añadir filtros, etc. Fue probablemente uno de los añadidos más importantes del lenguaje hasta la fecha.

Cabe recordar que Flash siempre ha sido una herramienta que trabaja con vectores, y hasta la llegada de Flash 8 apenas se podía hacer nada con las imágenes. Todo se reducía a poder cargarlas dinámicamente.

Veamos un poco esto dos formatos.

Vectores vs mapa de bits

Un vector es un gráfico que se construye en base a una descripción matemática. Los vectores trabajan con primitivas básicas como puntos, líneas y círculos. Por ejemplo, para dibujar una línea se necesita un punto inicial (x0,y0) y un punto final (x1,y1). La información que haya entre estos los puntos es generada en base a una ecuación.

Debido a esta forma de trabajar, un vector se puede escalar tanto como se desee sin perder calidad (se ve igual de nítido siempre), ya que lo único que se hace es alejar o acercar los puntos que conforman la figura. Esto permite tener archivos tan grandes como se quiera sin que aumente el peso del mismo, ya que siempre baraja la misma información.

Por el contrario, una imagen o mapa de bits (bitmap en inglés), es una cuadrícula de valores de color. Cada celda representa un pixel, que es la menor unidad en la que se descompone una imagen. Una imagen de 20×20 pixels tiene 400 valores de información, describiendo cada uno un color. En cuanto se intenta escalar un bitmap, el pixel se convierte en un rectángulo mayor y la imagen se distorsiona (se ve pixelada). El peso de una imagen depende entonces del tamaño de la misma, ya que a mayor tamaño mayor número de pixels y mayor número de información para guardar.

Pero no todo son desventajas para los bitmaps. Para imágenes complejas, se necesitarían tantos vectores para describir la figuras y colores que el tamaño resultante del archivo sería mucho mayor. Además, al tener que calcular las ecuaciones para dibujar el vector, todo el trabajo recae en el procesador del ordenador, con lo que la CPU se puede resentir de manera notable. Esto ha sido muy corriente en Flash, cuando veíamos animaciones espectaculares que se ralentizaban en ordenadores menos potentes.

Para más información sobre estos formatos, como siempre, a la wiki.

Valores de color

Como ya hemos dicho, un pixel es un minúsculo cuadrado de color. Es muy importante entender bien cómo se compone el color para poder trabajar con las imágenes, así que intentemos asimilar bien los siguientes conceptos.

Nota: aunque intentaré explicarlo lo más detallado posible, asumo que el lector tiene un conocimiento básico del sistema binario y hexadecimal.

En ActionScript el color se representa como un entero de 32 bits (4 bytes), y esto nos proporciona un rango de 4.294.967.296 (2^32) valores. Cada uno de estos 4 bytes representa un canal de color: 3 de ellos son los colores primarios: rojo, verde y azul, lo que se conoce en inglés como RGB (red, green, blue), y el cuarto representa la transparencia o color Alfa. La intensidad de cada color viene determinada por un valor comprendido entre 0 y 255. La intensidad total es 255 mientras que 0 representa la ausencia de color.

Este rango no es aleatorio. De 0 a 255 significa que existen 256 valores, que es el número que obtenemos si elevamos 2^8.
8 son los bits que hay en cada canal, o lo que es lo mismo, el byte de cada canal. Cada color en ActionScript tienes 32 bits o 4 bytes.

La intensidad en los colores RGB combinada forma las diferentes gamas de colores, mientras que la intensidad en el canal Alpha proporciona la transparencia. Así pues, con los 3 bytes (24 bits) de RGB tenemos un rango de 16.777.216 de valores (2^24), y a cada uno de estos valores le podemos dar 256 niveles de transparencia.

Al formar un color, se empieza por el canal alfa, seguido del rojo, verde y azul:

  • Alfa: bits del 24 al 31
  • Rojo: bits del 16 al 23
  • Verde: bits del 8 al 15
  • Azul: bits del 0 al 7

Por ejemplo, una representación del color azul puro sería:
Alfa = 255, Rojo = 0, Verde = 0, Azul = 255

En binario quedaría como sigue:
11111111 – 00000000 – 00000000 – 11111111 -> 32 bits

Y en decimal tendríamos el valor:
4.278.190.335

Es evidente que un número así no es muy fácil de recordar, por eso cuando hablamos de colores web se trabaja en formato hexadecimal. Un número hexadecimal comprende los 10 números de nuestro formato decimal más 6, de ahí que se conozca como base16:
0-1-2-3-4-5-6-7-8-9-A(10)-B(11)-C(12)-D(13)-E(14)-F(15)

Para indicar que es hexadecimal, se utiliza el prefijo 0x.
Algunos ejemplos de números hexadecimales (se multiplica por 16 por ser su base):

0x00 representa 0 -> 0*16 + 0*1 = 0 + 0 = 0

0x15 representa 21 -> 1*16 + 5*1 = 16 + 5 = 21

0x6A representa 106 -> 6*16 + 10*1 = 96 + 10 = 106

0xFF representa 255 -> 15*16 + 15*1 = 240 + 15 = 255

Entendiendo cómo funciona, es fácil deducir que un número hexadecimal de 32 bits se representa como 4 pares de valores hexadecimales:

0x FF FF FF FF
prefijo alfa rojo verde azul

Finalmente, ya podemos ver que nuestro color azul se representaría como:
0xFF0000FF

Esta nomenclatura es mucho más intuitiva y nos ayuda a leer e interpretar fácilmente una gran variedad de colores.

Para acabar de asimilar perfectamente estos conceptos, he credo un pequeño selector de color en el que se pueden modificar por separado los valores de cada canal y leer los resultados en formato decimal y hexadecimal.

This movie requires Flash Player 9

Descargar ejemplo

Vamos a fijarnos en algunas cosas:

  • Cuando los colores primarios (RGB) tienen el valor 0, el color es negro puro
  • Cuando los colores primarios (RGB) tienen el valor 255, es blanco puro
  • Un color sólo es opaco cuando el canal alfa tiene el valor 255. Cualquier otro valor nos proporciona un color transparente.
  • Si los 4 canales están a 255, vemos que el número decimal es 4.294.967.295, y si le sumamos el 0 inicial tenemos el rango de colores de ActionScript que comentábamos al principio: 4.294.967.296
  • El modelo RGB se basa en la síntesis aditiva, por eso la suma de los colores puros dan estos otros colores:
    – Rojo y Verde = Amarillo (0x FF FF FF 00)
    – Rojo y Azul = Púrpura (0x FF FF 00 FF)
    – Verde y Azul = Cyan (0x FF 00 FF FF)

Por último, indicar que en AS3 los colores se representa con el tipo de dato uint, que es un entero de 32 bits sin signo (siempre es positivo). Al no admitir valores negativos, el valor máximo es el doble que el de un tipo int, que admite 32 bits con signo.

Rango uint: de 0 a 4,294,967,295 (2^32-1)
Rango int: de -2,147,483,648 (-2^31) hasta 2,147,483,647 (2^31-1).

Un color se declararía así:

[as3]var color_rojo:uint = 0xFFFF0000;[/as3]

Colores sin transparencia

Hay que advertir que en la mayoría de los casos, cuando manipulamos objetos en ActionScript, el canal alfa no se modifica mediante el color, sino mediante la propiedad alpha (que define DisplayObject). Por ejemplo, cuando utilizamos el método beginFill de la propiedad graphics, vemos que la firma es

beginFill (color:uint, alpha:Number = 1.0):void

Aunque el tipo del color sea uint, se ignora la transparencia, ya que se aplica mediante su propiedad alpha. Si queremos que el color sea rojo, es lo mismo usar 0xFFFF0000 que 0xFF0000. Si tiene 4 pares hexadecimales, el primer par no se tiene en cuenta.

Veamos un ejemplo donde pasamos un canal alpha semitransparente, con valor 0x55. Al probar este código, vemos que el rojo es completamente opaco, ya que el canal alfa se ha ignorado:

[as3]var s:Shape = new Shape();
s.graphics.beginFill((0x55FF0000,1);
s.graphics.drawCircle(0,0,10);
s.graphics.endFill();
addChild(s);[/as3]

Esta es la razón por la que al definir colores en CSS, en Graphics, etc. se utilizan colores de 3 pares hexadecimales (0xFF0000). Como norma general, sólo se utiliza el canal alpha cuando trabajamos con bitmaps que admiten transparencia.

Resumiendo

Aunque la teoría siempre es pesada, un buen entendimiento de estos conceptos es clave para manipular las imágenes. El siguiente capítulo será más divertido, ya que nos meteremos de lleno en las clases Bitmap y BitmapData.

Información del artículo

Post publicado el 20 de January de 2008 a las 19:00 por llops

Categorias: Artículos

Etiquetas: , , , , ,

Comparte

6 trackbacks

8 comentarios

  • Incrível, eu adorei essa primeira parte, muito boa me esclareceu coisas que como disse não se encontram na rede. Concerteza vou continuar acompanhando esses artigos.

    Parabéns, e continue com esse belo trabalho!.

    Cool, i’ll worship this first step, very good. That did more clear several things than how you say isn’t found in the web. Certainly i go it continue following these articles.

    Congratulations and continue with this pretty work.

  • spasmos

    Buenas,

    Solo se me ocurre una palabra… GRACIAS!!!

    Llevo mucho tiempo intentando entender esta clase en AS2. Ahora que decido ponerme con AS3, es un lujo contar con una información tan detallada y en castellano. No abandones el blog y sigue maravillandonos con esta info…muy necesitada en los desarrolladores de habla hispana atascados por la poca información que hay en nuestro idioma.

    :)

  • Hi guys, thanks for your comments :)

    Next chapter soon!

  • martin

    muy bueno, muchas gracias por compartir tus conocimientos.
    Martin.

  • Muy clarificante, execelente trabajo, gracias.

  • Ditmar

    A caray ahora entiendo del porque los colores estan en Hexadécimal

  • fusa

    Muchas gracias por el articulo, exelente, estoy comenzando ha crear un editor de imagenes, y estos articulos me viene de lujo

  • earmc

    Hey man , muchas gracias desde Colombia. La verdad estos articulos me caen de perlas, creo que explicas muy bien y tienes informacion muy interesante.Gracias y sigue adelante.