+----------------------------------------------------
	Tutorial de programacin grfica
		     por: FAC
	--------------------------------
		 Parte 2: Paleta
----------------------------------------------------


Bienvenidos a la segunda parte del tutorial. Esta parte trata de la paleta
de colores y algunos efectos que se pueden lograr con solo mover los colores
en la paleta. Tambin empezaremos a juntar todas las rutinas y procedimientos
en una unidad para no tener que escribirlas en cada programa.

Esta segunda parte tal vez est un poco retrasada de tiempo. Esto es debido
a la repentina explosin de mi monitor :(... Si quieren ponerse en contacto
conmigo, escriban a:

		  ganzo@galia.fc.uaslp.mx


Recuerden que pueden conseguir los tutoriales en la siguiente direccion:

FTP:   galia.fc.uaslp.mx/pub/tutorial

Y proximamente habra un pagina de WWW donde se podran encontrar todos
los tutoriales.



QUE ES LA PALETA DE COLORES???


Una tarjeta VGA despliega los colores de la siguiente forma: se hace una
combinacin de los tres colores primarios: rojo, verde y azul, con una
cierta intensidad para cada color y de esta forma se producen los colores.

La intensidad de cada color va de 0 a 63, por lo tanto se pueden producir
hasta 262,144 colores (64 elevado a la 3).

Pero en el modo 13 no se pueden usar los 262,144 colores al mismo tiempo,
sino que de todos esos colores, nosotros escogemos 256 colores con los que
vamos a trabajar. A estos 256 colores se les llama la PALETA.

Aunque parezca lo contrario, esto tiene muchas ventajas. Por ejemplo,
digamos que el color 3 de la paleta es rojo, y digamos que dibujamos
varios rectngulos en la pantalla con el color numero 3, o sea, rojo.
Si despus de dibujar los rectngulos cambiamos el color 3 a azul,
entonces TODO lo que habamos dibujado con el color 3 cambiar a azul.
(o sea que todos los rectngulos que eran rojos, seran ahora azules).
La ventaja de esto es que podemos cambiar el color de MUCHOS pxels sin
tener que volver a dibujar ni siquiera uno.

Utilizando esta ventaja, se pueden realizar algunos efectos interesantes,
pero tenemos que saber como cambiar los colores de la paleta.



COMO LEER Y ESCRIBIR EN LA PALETA VGA:


Siempre hay que tener en cuenta que la paleta es como un SUBCONJUNTO de
todos los colores que podemos tener y los colores de la paleta son los
nicos que podemos tener en la pantalla.

Cuando a alguna funcin que dibuja algo en la pantalla le pasamos como
parmetro un color, en realidad le estamos pasando el ndice en la paleta
del color que queremos, pero ese color podria tener cualquier valor de
intensidad para rojo, verde y azul.


Para leer y/o escribir en la paleta, tenemos que hacer uso de los puertos
de la tarjeta VGA. Primero escribimos el ndice del color que queremos
cambiar ($3C7 para lectura, $3C8 para escritura) y despus leemos o
escribimos en el puerto $3C9 los valores de rojo, verde y azul, en ese
orden. Los procedimientos son los siguientes:


procedure GetPal(color : byte, var red, green, blue : byte);
begin
     port[$3C7] := color;
     red := port[$3C9];
     green := port[$3C9];
     blue := port[$3C9];
end;

procedure SetPal(color, red, green, blue : byte);
begin
     port[$3C8] := color;
     port[$3C9] := red;
     port[$3C9] := green;
     port[$3C9] := blue;
end;


Asi que de ahora en adelante usaremos el procedimiento GetPal para leer
los valores de rojo, verde y azul de algun color en la paleta y el
procedimiento SetPal para fijar los valores.

Ejemplo:

	SetPal(1, 63, 0, 0);    { Hace que el color 1 sea rojo }
	PutPixel(100, 100, 1);  { Dibuja un punto rojo en (100, 100) }
	SetPal(1, 0, 63, 0);    { Cambia el color del punto rojo a verde }


Las siguientes intensidades corresponden a colores comunes:

	R       G       B       color
-------------------------------------------------
	0       0       0       negro
	63      0       0       rojo intenso
	0       63      0       verde intenso
	0       0       0       azul intenso
	63      63      0       amarillo
	63      0       63      magenta
	0       63      63      azul cielo
	63      63      63      blanco


Obviamente, la intensidad de un color no tiene que ser 0 o 63 unicamente:

	var i : byte;

	begin
	     for i := 0 to 63 do
	     begin
		  SetPal(i, i, i, i);
		  SetPal(i+64, i, 0, 0);
		  SetPal(i+128, 0, i, 0);
		  SetPal(i+192, 0, 0, i);
	     end;
	     ... { tu programa va aqui }
	end;


El programa anterior produce una paleta con un degradado de grises en los
colores 0 a 63, un degradado de rojos en los colores 64 a 127, un
degradado de verdes en los colores 128 a 191 y un degradado de azules
en los colores 192 a 255. El programa paleta1.pas utiliza esta paleta
y luego dibuja 256 lneas verticales. Despues cambia la paleta sin volver
a dibujar las lineas y automticamente todos los colores cambian en la
pantalla.


Ok, pero para qu se puede utilizar todo esto? Bueno, con la simple
alteracin de la paleta se pueden lograr efectos muy buenos. Vamos
a empezar por lo bsico:


COMO OSCURECER UNA PANTALLA LENTAMENTE:

Seguramente todos han visto este efecto. En algn juego o algn demo
en que aparece una imagen y despus desaparece, pero no instantneamente,
sino que se oscurece poco a poco.

Para lograr este efecto (tambin llamado Fade Out) slo hay que hacer
un ciclo de 0 a 63 y en cada vuelta hay que decrementar en 1 los valores
de rojo, verde y azul de TODOS los colores.

La nica precaucin que se debe tener es que si algn valor de R, G o B
ya vale cero, entonces lo dejamos como est. El procedimiento es este:


   procedure FadeOut;

   var i, j : byte;  { necesitamos 2 contadores }
       r, g, b : byte; { valores de rojo, verde y azul }

   begin
	for i := 0 to 63 do
	    for j := 0 to 255 do
	    begin
		 GetPal(j, r, g, b);
		 if r > 0 then dec(r);
		 if g > 0 then dec(g);
		 if b > 0 then dec(b);
		 SetPal(j, r, g, b);
	    end;
   end;


Fcil, no? Si se quiere, se puede poner un delay(n) despus de ciclo j,
o sea, despus de cambiar la paleta entera, para hacer el Fade Out ms
tardado.

Tambin se puede incrementar la intensidad de cada color en lugar de
disminurla, de esa forma de iluminara la pantalla hasta quedar en
blanco. Por ejemplo:

	    if r < 63 then inc(r);


COMO ILUMINAR LA PANTALLA LENTAMENTE:

OK, esto es lo mismo, pero al revs. Lo que sucede aqu es que originalmente
la pantalla est en negro y entonces poco a poco se va iluminando hasta
que cada color alcanza su intensidad final.

La nica diferencia es que necesitamos la informacin de la paleta final.
Para eso, vamos a definir un nuevo tipo de dato en el que almacenaremos
una paleta entera:

    type TPalette = array[0..255, 0..2] of byte;

El primer ndice en TPalette indica el nmero de color en la paleta,
el segundo ndice se usa para los valores de rojo, verde y azul,
respectivamente.


Ahora solo hay que hacer un ciclo de 0 a 63 y dentro de ese ciclo
comparamos los valores r, g, b de cada color de la paleta con el valor
que tenemos almacenado en nuestra variable. Si la intensidad del color
en la paleta es menor que la intensidad en la variable, entonces aumentamos
la intensidad de la paleta. De esta forma, cada color aumentar su intensidad
hasta que sea igual a la que est almacenada en la variable.

Si, bueno, pero cmo se codifica todo esto? Bueno, vamos a hacer un
procedimiento que tome como parmetro la paleta final de nuestra imagen
y que realize el Fade In...


  procedure FadeIn(final : TPalette);

  var i, j : byte;
      r, g, b : byte;

  begin
       for i := 0 to 63 do
	   for j := 0 to 255 do
	   begin
		GetPal(j, r, g, b);
		if r < final[j, 0] then inc(r);
		if g < final[j, 1] then inc(g);
		if b < final[j, 2] then inc(b);
		SetPal(j, r, g, b);
	   end;
  end;



El programa paleta2.pas muestra los efectos Fade In y Fade Out en accin.

Si ya le echaron un ojo al cdigo fuente de los ejemplos, notarn que se
est utilizando una unidad llamada MODE_13.PAS. Esta unidad contiene los
procedimientos ms comunes para trabajar en el modo 13, como son iniciar
el modo grfico, regresar el modo texto, dibujar pxels, leer o modificar
los colores de la paleta, etc...

Esta unidad ir creciendo con el tiempo y llegar a tener procedimientos
para hacer lneas, crculos, trangulos slidos y algunos efectos.

Ustedes pueden usar la unidad MODE_13.TPU en sus programas y aadir o
modificar cualquier procedimiento, pero si usan esa unidad tienen que
darme crdito en su programa y mandarmelo por e-mail.


ROTACION DE LA PALETA:


Lo siguiente que veremos ser cmo utilizar la paleta para obtener una
ilusin de movimiento. Supongamos que tenemos nuestra paleta con los
10 primeros colores en negro, al igual que el fondo, el cual est pintado
con un color que NO est entre los primeros 10. De forma que si cambiamos
los 10 primeros colores, el color de fondo NO cambiar.

Ahora supongamos que dibujamos 10 puntos consecutivos con los colores del
0 al 9. De forma que el primer pxel tiene el color 0, el siguiente pxel
a la derecha tiene el color 1 y as sucesivamente hasta llegar al ltimo
pxel, el cual tiene el color 9. Obviamente, puesto que los colores 0 a 9
son iguales que el color de fondo, los pxels son invisibles para nosotros.

Qu pasara si hacemos que el color 0 sea blanco? Pues entonces el primer
pxel que dibujamos se volvera blanco. Ahora supongamos que hacemos un
movimiento de 10 primeros colores de la paleta de forma que el color 9 sea
igual al color 8, el color 8 sea igual al color 7, el color 7... y por
ltimo el color 0 sera igual al color 9. A esto se le llama una ROTACION
de la paleta, y puesto que todos los colores (menos el ltimo) se movieron
hacia el siguiente, entonces le llamaremos una ROTACION HACIA ADELANTE.

Bueno, pero cmo han quedado los colores ahora?

El color 0 (que era blanco) ahora vuelve a ser negro (lo que era antes el
color 9). El color 1 ahora es blanco, ya que tom su nuevo valor a partir
del color 0. Todos los dems colores quedan en negro.

Y puesto que ahora el color 0 es negro, entonces el pxel que estaba blanco
vuelve a estar negro, pero el pxel siguiente, que tiene el color 1, se
vuelve blanco. Esto da la ilusin de que el pxel se ha movido, aunque
en realidad no hemos vuelto a dibujar ningn pxel. Si realizamos otra vez
la rotacin de paleta, el pxel parecer moverse otra vez. Y si hacemos
la rotacin dentro de un ciclo, el pxel permanecer en movimiento.

Para mover un pxel, lo ms sencillo sera primero borrarlo y luego
redibujarlo en su nueva posicin. Pero qu pasa si en lugar de pxels
queremos mover lneas o figuras grandes. Sera muy tardado borrar y redibujar
un rectngulo o un crculo. En cambio la rotacin de paleta nos permite
dar la ilusin de movimiento si tener que dibujar un solo pxel.

Aqu les presento un procedimiento que realiza una rotacin hacia adelante
en cualquier rango de colores de la paleta. Tambin se puede implementar
una rotacin hacia atrs (de hecho, se implementa en el programa de ejemplo),
pero la forma de hacerlo es igual, por lo tanto no voy a explicarlo.


     { Rotacin de paleta hacia adelante }
     procedure RotateForward(var pal : TPalette; first, last : byte);

     var rl, gl, bl, i : byte;

     begin
	  rl := pal[last, 0];   { Almacenamos los colores del ltimo }
	  bl := pal[last, 1];   { color que vamos a rotar }
	  gl := pal[last, 2];

	  for i := last downto (first+1) do
	  begin
	       pal[i, 0] := pal[i-1, 0];  { Hacemos que cada color sea }
	       pal[i, 1] := pal[i-1, 1];  { igual al color anterior en }
	       pal[i, 2] := pal[i-1, 2];  { la paleta }
	  end;

	  pal[first, 0] := rl;   { Hacemos que el primer color }
	  pal[first, 1] := bl;   { sea igual al que era el ltimo }
	  pal[first, 2] := gl;
     end;


Ntese que no se hace ninguna comprobacin de lmites en los valores del
primer y ltimo color que se va a rotar. Hacer algo como

       RotateForward(mipaleta, 10, 1);

NO rotara los colores de 1 a 10, sino que simplemente hara el color 10
igual al 1. La forma correcta de hacer la rotacin sera:

       RotateForward(mipaleta, 1, 10);

Otra cosa a notar es que en realidad no rotamos la paleta, sino que rotamos
una variable de tipo TPalette. Esto se hace as porque es ms rpido hacer
movimientos en la memoria convencional que accesos de puertos. Por lo tanto,
para realizar la rotacin de la paleta que vemos en la pantalla hay que
hacer lo siguiente:

      GetPalette(mipaleta); { Almacenamos la paleta entera en una variable }
      ...
      RotateForward(mipaleta, 1 ,10);
      SetPalette(mipaleta); { Restauramos la paleta entera }
      ...
      RotateForward(mipaleta, 1, 10);
      SetPalette(mipaleta);
      ...
      { etc... }


Los procedimientos GetPalette y SetPalette, almacenan y restauran la paleta
entera en una variable de tipo TPalette o desde ella. Estos procedimientos
son parte de MODE_13.PAS y son muy sencillos, as que no voy a explicarlos.

El programa paleta3.pas muestra algunos ejemplos de cmo se puede usar la
rotacin de paleta para obtener movimiento.


RETRAZADO VERTICAL:

En el ejemplo paleta3.pas se hacen muchas rotaciones de paleta. Esto a veces
ocasiona parpadeos en la pantalla que se deben a que en el momento en que
alteramos la paleta, la imgen en la pantalla an no se ha dibujado
completamente.

El rayo de electrones que dibuja la pantalla se mueve siempre desde la
esquina superior izquierda hasta la esquina inferior derecha, entonces se
desactiva y regresa a la esquina superior izquierda para volver a dibujar
la pantalla.

Digamos que mientras cambiamos la paleta de colores, el rayo va a la mitad
de la pantalla. Lo que pasa entonces es que la mitad superior queda con
la paleta inicial (antes de rotarla) y la mitad inferior se dibuja con la
paleta ya rotada. Esto ocasiona un parpadeo y adems parece que una parte
de la pantalla se desplaza.

La forma de evitar esto es cambiar la paleta exactamente en el momento en
el que el rayo de electrones regresa a la posicin superior izquierda, ya
que de este modo, cualquier cambio producido en la paleta se mostrar hasta
la siguiente vez que el rayo dibuje la pantalla.

El instante en que el rayo de electrones llega a la parte inferior de la
pantalla y se desactiva para moverse a la parte superior se llama retrazo
vertical. Mediante los registros de la tarjeta VGA, nosotros podemos saber
si en algn momento se est efectuando un retrazo vertical o no. De esta
forma, haremos que el programa espere a que se realice un retrazo antes
de efectuar cambios en la paleta.

Esta tcnica tambin se puede usar en animaciones en las que realmente se
dibujan o mueven los pxels en la pantalla. El nico inconveniente es que
al esperar al retrazo vertical, el programa se hace ms lento, pero realmente
no es muy notable con los procesadores actuales. Adems, si no se sincroniza
el programa con retrazo, se producen efectos no muy agradables.

En la unidad MODE_13.PAS se incluye el procedimiento VRetrace, el cual no
hace mas que esperar a que se origine un retrazo vertical, por lo tanto,
antes de modificar la paleta se llama primero a VRetrace.

Intenten ejecutar el programa paleta3.pas borrando la lnea que contiene
el VRetrace, antes de modificar la paleta y vern la distorsin que se
produce en la imgen.

En alguno de los siguientes tutoriales (tal vez el cuarto) veremos cmo
usar algo que se conoce como pantallas virtuales, con lo cual se elimina
tambin el parpadeo que se produce al borrar y redibujar algun objeto.


EJERCICIOS:

	   - Inventen algo. Sean creativos y divirtanse.


CONCLUSION:

Eso es todo. No se olviden de escribir sus dudas, comentarios, opiniones y
sugerencias a:

	    ganzo@galia.fc.uaslp.mx

Recuerden que si no se comunican conmigo, yo pensar que les vale m...
todos estos tutoriales y que quieren ser unos simples usuarios de Windows
por el resto de su vida.

El siguiente tutorial ser sobre el dibujo de crculos y lneas y el uso
de tablas precalculadas. Y tal vez despus hablaremos de pantallas virtuales
y de cmo cargar una imgen PCX. Si quieren ms, compren un libro :)


Ah, por cierto, si alguien desea que los tutoriales le lleguen cada semana
por e-mail, dganme para inclurlos en el mailing-list.


El Ganzo se despide hasta que arreglen su monitor...
