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.
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.
[…] mantenido por llops. Experimentos y artículos entorno a la plataforma flash y as3. « Jugando con pixels (I) […]
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 hace…
[…] esta oportunidad, me tome el atrevimiento de tomar prestado un ejemplo que vi en llops, le implemente un cambio, el picker de color y lo hice sobre un bitmap attachado de la libreria, no […]
[…] básica para manipular Bitmaps en Flash, pero necesarias si no se conoce mucho del tema: Jugando con Pixels (I), y Jugando con Pixels (II). Sirve también la documentación Livedocs de Adobe: Las clases Bitmap y […]
[…] Jugando con pixels 1 […]
[…] Después me centro durante un buen rato en la teoría del color en AS3, que desarrollé hace tiempo en el siguiente artículo: Jugando con pixels (I). […]