10. Más sobre la paleta de colores.

Hasta ahora nos hemos limitado a utilizar el color 1, el color 2, etc., tomando dichos colores de la paleta estándar de un ordenador tipo PC.

Esto es muy limitado, comparado con lo que podríamos hacer: Un ordenador actual es capaz de utilizar millones de colores simultáneamente en la pantalla, pero es que incluso un ordenador de 1990 es capaz de mostrar 256 colore simultáneos, escogidos de una paleta de más de 250.000, de modo que incluso con un ordenador bastante antiguo, podríamos estar haciendo cosas bastante más vistosas que las que estamos haciendo.

Así que ya va siendo hora de ver cómo saber a qué numero corresponde cada color, de aprender a utilizar más colores de estos 256 "estándar" y de trabajar con una gama casi ilimitada de colores en un ordenador actual. 

En primer lugar, veamos a qué corresponden los números de color habitual (la "paleta estándar" de un ordenador que siga el patrón marcado por los primeros IBM PC) :

 Números de color   Equivale a
0 Negro
1 Azul
2 Verde
3 Cyan
4 Rojo
5 Violeta
6 Marrón
7 Gris claro
8 Gris oscuro
9 Azul claro
10 Verde claro
11 Cyan claro
12 Rojo claro
13 Violeta claro
14 Amarillo
15 Blanco

Estos son los 16 colores estándar en cualquier ordenador "tipo PC" que tenga pantalla a color (tarjeta gráfica CGA o superiores). Estos colores son los que se emplean en una pantalla de texto en el modo normal de 16 colores (como curiosidad, se puede observar que se trata de 8 colores básicos, con dos intensidades de brillo). Para no tener que recordar sus nombres, la mayoría de los compiladores de lenguajes como C y Pascal tienen definidas unas constantes (en inglés):

  Black = 0;
  Blue = 1;
  Green = 2;
  Cyan = 3;
  Red = 4;
  Magenta = 5;
  Brown = 6;
  LightGray = 7;
  DarkGray = 8;
  LightBlue = 9;
  LightGreen = 10;
  LightCyan = 11;
  LightRed = 12;
  LightMagenta = 13;
  Yellow = 14;
  White = 15;

Si queremos ver la apariencia de estos 16 colores y los otros 240 que forman la " paleta estándar VGA ", podemos crear un pequeño programita que muestre todos ellos. Por ejemplo, en 16 filas y 16 columnas, dibujando "recuadritos" de 8 puntos de ancho y 8 puntos de alto, así:

Esto lo conseguimos simplemente con una orden como esta:

  for (i=0; i<=15; i++)
    for (j=0; j<=15; j++)

      rectfill(
       screen, 
       j*10+80, i*10+20 ,j*10+88, i*10+28,
       palette_color[i * 16 + j]
      ); 

Además, con la mayoría de bibliotecas gráficas podemos modificar el color al que corresponde cada una de estas posiciones, indicando qué cantidad de rojo, verde y azul queremos que lo compongan. Tenemos 64 tonalidades posibles de cada uno de estos colores básicos, lo que nos permite utilizar nada menos que 262.144 tonos de color distintos. 

Por ejemplo, podríamos cambiar el color 15, que inicialmente es el blanco, para convertirlo en un rojo puro: basta asignarle 63 de rojo, 0 de verde, 0 de azul. La orden que lo permite en los lenguajes de Borland (como Turbo C++ y Turbo Pascal) es

setRGBpalette ( color, r, g, b);

donde "color" es el número del color de la paleta que queremos cambiar (15 en nuestro caso), r es la cantidad de rojo (red), g es la cantidad de verde (green) y b es la cantidad de azul (blue). Por tanto, para convertir el color 15 en rojo puro haríamos:

setRGBpalette ( 15, 63, 0, 0);

En el caso de Allegro, la construcción es algo más complicada, aunque no mucho:

void set_color(int index, const RGB *p);

donde el tipo RGB es un tipo predefinido en Allegro:

typedef struct RGB
{
   unsigned char r, g, b;
} RGB;

de modo que podemos crear nuestros colores RGB con 

   RGB negro = { 0,  0,  0  };
   RGB blanco = { 63, 63, 63 };
   RGB rojo = { 63,  0,  0  };
   RGB violeta = { 63, 0, 63 };

y entonces podríamos asignar cualquiera de estos colores a una de las posiciones de la paleta:

set_color(15, &violeta);

Una recomendación adicional es utilizar la orden "vsync()" antes de modificar un color, para sincronizar con el momento en el que se actualiza la información en pantalla, y así evitar que aparezca "nieve". Existe una variante de la orden "set_color", llamada "_set_color", que no necesita sincronizar con el barrido de la pantalla, pero a cambio sólo funciona en los modos estándar de una tarjeta gráfica VGA (320x200 puntos y algunos modos extendidos adicionales llamados "modos X") y no en los modos de alta resolución, por lo que no la usaremos.

También podemos jugar con toda la paleta de colores: memorizar la paleta de colores actual (para poder modificarla y recuperarla después), oscurecer la paleta, convertirla gradualmente en otra, etc.

Por ejemplo,

void fade_out(int velocidad);
Pasa gradualmente de la paleta de colores actual a una pantalla negra. La velocidad va desde 1 (la más lenta) hasta 64 (instantáneo). 

void fade_in(const PALETTE p, int velocidad);
Pasa gradualmente de una pantalla negra a la paleta de colores "p". La velocidad nuevamente va desde 1 (la más lenta) hasta 64 (instantáneo).

void fade_from(const PALETTE p1, const PALETTE p2, int velocidad);
Pasa de la paleta "p1" a la paleta "p2". 

En todos los casos, el tipo PALETTE está definido en Allegro, y se trata de un "array" de 256 estructuras de tipo "RGB". 

Podemos usar más de 256 colores (si nuestra tarjeta gráfica lo permite), cambiando al modo de pantalla correspondiente y empleando la orden "putpixel" habitual. El único cambio es indicar el color a partir de sus componentes r,g,b, con:

int makecol(int r, int g, int b);

Donde r, g, b  van desde 0 a 255 (y el propio Allegro se encargará de convertirlo al color "físico" correspondiente según el modo de pantalla que usemos)., por ejemplo:

putpixel(screen, 320, 200, makecol(255, 70, 50) );

Por otra parte, tenemos unas variantes del putpixel normal que son específicas para cada modo de pantalla, de 8 bits (256 colores), 15 bits (32.768 colores), 16 bits (65.536 colores), 24 bits (262.144 colores) o 32 bits (lo que se suele llamar "true color" - color auténtico):

void _putpixel(BITMAP *bmp, int x, int y, int color);
void _putpixel15(BITMAP *bmp, int x, int y, int color);
void _putpixel16(BITMAP *bmp, int x, int y, int color);
void _putpixel24(BITMAP *bmp, int x, int y, int color);
void _putpixel32(BITMAP *bmp, int x, int y, int color);

Estas variantes son más rápidas que el putpixel() "normal", pero tienen varios inconvenientes:

  • No comprueban si nos salimos de los límites de la pantalla (operación que se suele llamar "clipping", y que sí hace putpixel), de lo que nos deberemos encargar nosotros si no queremos que nuestro programa falle.
  • No funcionan en los modos extendidos (modos X) de una tarjeta VGA estándar.
  • Tampoco hacen caso al "modo de escritura" (que nosotros no hemos usado por ahora, pero que puede ser útil para conseguir ciertos efectos).