Tutorial básico sobre gráficos en Pascal

Escrito por Nicolás Rozas Salgado


Objetivo:
El objetivo de este tutorial es ofrecer una introducción al mundo de la programación bajo gráficos para los usuarios de Turbo y Borland Pascal. Este tutorial no abarca todos los aspectos de la programacion con gráficos, sino que muestra en forma básica una introducción de este mundo a las personas que no poseen los conocimientos suficientes sobre este tema.

Desarrollo:
A través de este tutorial se explicará la utilización del modo gráfico para ser utilizado en aplicaciones DOS, y donde se tocarán diversos temas relacionados con la tarjeta gráfica. Para poder entender mejor esta introducción abarcaremos los puntos mas sencillos.

Hoy en día existen muchas tarjetas de video, muchas de las cuales pueden exhibir gráficos de hasta 16 millones de colores y a estas tarjetas las denominamos SVGA (Super VGA), pero estas tarjetas son descendientes de otras mas primarias: La VGA. VGA son siglas de Video Graphics Adaptor (Adaptador de gráficos de video), estas tarjetas tenían hasta 256 kb de memoria y hasta 256 colores, con el tiempo salieron las SVGA que basadas en las VGA ampliaron mucho más las posibilidades que existían en ese momento.
Las VGA en realidad hoy no son muy útiles comparados a las exigencias actuales, pero poder comprender su funcionamiento nos llevará a una mejor comprensión posterior cuando utilicemos una SVGA (Vuelvo a mencionarlo, la SVGA está basada en el modelo VGA y guarda muchas de sus características).
Cuando nació la tarjeta VGA se desarrolló tambien un estándar para poder utilizarlas, es decir que cada fabricante de tarjetas VGA debía seguir una norma específica. Entre esas normas están los modos gráficos de 16 y 256 colores, pero en este tutorial hablaremos de un modo gráfico en especial: El modo de 320x200 y 256 colores, ya que este es el más facil de comprender, es rápido y será la mejor opción por si deseamos ampliar nuestros conocimientos después para comprender el funcionamiento de las tarjetas con resoluciones y cantidades de colores más grandes.
Este modo (Que se le dice 320x200x256 o tambien se le suele decir "El modo 13h") se ha convertido en un modo gráfico muy utilizado por los programadores de juegos ya que podía ser utilizado en cualquier VGA, era el único existente con 256 colores, utilizarlo era muy sencillo y era un modo gráfico muy bueno (Era mejor usar el de 256 colores que el de 16 colores).

Ahora... ¿Cómo accedemos a este modo gráfico? O ¿Como lo activamos?
Utiliza la siguiente rutina:

Procedure IniciarGraficos; Assembler;
Asm
Mov Ax,13h {Especifica que utilizaremos el modo 320x200 pixeles, 256 colores}
Int 10h {Le enviamos esta orden a la tarjeta de video}
End;

Aqui se ve por que se le dice "Modo 13h". 13h es un número expresado en forma hexadecimal.
NOTA: Siempre ten cuidado cuando utilizas interrupciones (La instrucción INT) ya que si la usas mal puedes causar que por ejemplo la tarjeta de video no funcione adecuadamente e incluso podría pasarte que se dañe el monitor, siempre verifica y lee cuidadosamente antes de probar y experimentar con interrupciones.

Si queremos volver al modo texto utilizamos esta rutina:

Procedure TerminarGraficos; Assembler;
Asm
Mov Ax,3 {Especifica que utilizaremos el modo 80x25 caracteres, 16 colores, solo texto}
Int 10h {Le enviamos esta orden a la tarjeta de video}
End;

Verás que es igual que el primero pero con la diferencia que en vez de especificar 13h especificamos el 3. Con esto distinguimos que el valor "3" es para iniciar el modo texto y el 13h para los gráficos.

Volvamos con el modo 13h. Una vez que lo tenemos activado nos aparecerá una pantalla oscura. Pues bien... ¿Cómo la utilizamos? Bueno, dejamos aclarado en primer lugar que cuando trabajamos con gráficos tendremos que utilizar pixeles ¿Y qué es un pixel? Un pixel es un pequeño punto en la pantalla, para formar una imagen en la pantalla necesitaremos utilizar un grupo de pixeles, y esta es la forma como se crean las imágenes. Cuando trabajamos con gráficos especificamos los detalles del modo de acuerdo al ancho y alto de pixeles que pueden ser escritos y vistos, por ejemplo decir 320x200 significa 320 pixeles de ancho y 200 de alto, 640x480 significa 640 pixeles de ancho y 480 de alto, después están 800x600, 1024x768 y así sucesivamente. Volviendo al modo 320x200 de 256 colores, una vez que está activado podemos escribir los pixeles y eso formará una imagen en pantalla. Pues bien... ¿Como escribir un pixel? Cuando iniciamos el modo gráficos la tarjeta de video utiliza 64 Kb de memoria RAM en la cual los datos que sean escritos en ella serán mostrados inmediatamente en pantalla. Esta area de memoria que utiliza la tarjeta de video es siempre la región "A000:0" ¿Y como accedemos a esa area de memoria? En Pascal tenemos una instrucción para leer y escribir a memoria y se llama MEM. Por ejemplo, si queremos escribir el valor 255 a la dirección A000:0 especificamos esta instrucción:
MEM[$A000:0]:= 255; {Asigna el valor 255 a la region A000:0}

Nota: Observa que puse $A000. Cuando utilizas el simbolo $ en un programa en Pascal significa que el valor que estás utilizando está en formato hexadecimal, de hecho el valor A000 es una anotación hexadecimal (En realidad es mejor haber hablado de A000h en un principio, pero cuando se mencionan areas de memoria siempre se utilizan anotaciones hexadecimales (Por ejemplo si te dicen que escribas en el área 1234:5678 deberás usar mem[$1234:$5678] porque se están refiriendo a valores en hexadecimal y no en decimal). Fíjate también que estamos refiriéndonos a valores hexadecimales poniendo un símbolo $ o también escribiendo una "h" al final del valor, cuando trabajas en lenguaje Assembler bajo Pascal puedes utilizar las dos formas, pero cuando trabajas con instrucciones en formato Pascal (Por ejemplo, MEM) solo puedes utilizar el símbolo $.

Volvamos a este punto:

MEM[$A000:0]:= 255;

$A000 es la region de memoria, y lo que sigue a ":" es la posición desde esa región especificada, con esto pasado a lenguaje coloquial significa: "A la posición 0 del area de memoria $A000 le asigno el valor 255". Si esta instrucción se ejecuta cuando el modo gráfico está activado te aparecerá un pequeño punto blanco en el extremo superior izquierdo de la pantalla. Con esa instrucción escribes un pixel. Bueno... ¿Y cómo hago para escribir un pixel en un punto específico de la pantalla? Si bien en la pantalla podemos ver que se puede trabajar en formato (X,Y) la memoria no funciona así, sino que trabaja con valores absolutos de posición. ¿Que significa todo esto? Cuando activas el modo gráfico, en la region de memoria A000 puedes utilizar 64000 posiciones, cada una de esas posiciones equivale a un punto en la pantalla. La posición 0 es para la coordenada 0x, 0y, la posición 1 es para 1x, 0y, la posición 2 es para la coordenada 2x, 0y, la posición 3 es para 3x, 0y, y asi sucesivamente. La posición 320 es para 0x,1y, la posición 321 es para 1x, 1y, la posición 322 es para la coordenada 2x, 1y, y asi podriamos llegar a la ultima posición que es la 63999 que corresponde a 319x y 199y. ¿Y como convertir direcciones puestas en formato (X,Y) a direcciones absolutas? Utiliza esta fórmula:

(Y*320)+X

Con esto podemos definir un procedimiento que nos escriba un pixel en pantalla de acuerdo a la coordenada que le especifiquemos:

Procedure PutPixel(X,Y: Word; Valor: Byte);
Begin
Mem[$A000:(Y*320)+X]:= Valor;
End;

Como verás este procedimiento escribe un valor en una posición (X,Y) que nosotros le indiquemos, por ejemplo:
PutPixel(150,165,200);
Escribe en la posición 150, 165 (150x, 165y) el valor 200.

¿Y que es ese valor que nosotros tenemos que especificar?. Este valor es el pixel. Si especificas el valor 255, el pixel será de color blanco, si especificas el valor 0 será un pixel negro, si especificas el valor 1 será azul, el 2 será verde y así sucesivamente.

A continuación hay un programa de ejemplo que muestra todo lo visto hasta ahora: Inicia el modo gráfico, muestra un rectángulo azul, después uno verde, después uno rojo, después uno con un espectro de colores, y finalmente vuelve al modo texto y sale.



Uses Crt;

Procedure IniciarGraficos; Assembler;
Asm
Mov Ax,13h {Especifica que utilizaremos el modo 320x200 pixeles, 256 colores}
Int 10h {Le enviamos esta orden a la tarjeta de video}
End;

Procedure TerminarGraficos; Assembler;
Asm
Mov Ax,3 {Especifica que utilizaremos el modo 80x25 caracteres, 16 colores, solo texto}
Int 10h {Le enviamos esta orden a la tarjeta de video}
End;

Procedure PutPixel(X,Y: Word; Valor: Byte);
Begin
Mem[$A000:(Y*320)+X]:= Valor;
End;

Var X,Y: Word;
Begin
IniciarGraficos;
For Y:= 0 to 150 Do
For X:= 0 to 255 Do
PutPixel(X,Y,1);
ReadKey;
For Y:= 0 to 150 Do
For X:= 0 to 255 Do
PutPixel(X,Y,2);
ReadKey;
For Y:= 0 to 150 Do
For X:= 0 to 255 Do
PutPixel(X,Y,4);
ReadKey;
For Y:= 0 to 150 Do
For X:= 0 to 255 Do
PutPixel(X,Y,X);
ReadKey;
TerminarGraficos;
End.



También es posible modificar los colores que existen, por ejemplo, se puede hacer que el blanco sea un poco más grisaceo, un azul o un rojo más oscuro, etc. Y esto se puede hacer a través de la paleta de colores. Así como un pintor utiliza colores y los mezcla para formar gran variedad de tonalidades, tambien es posible para una aplicación en Pascal trabajar de la misma forma a través de la Paleta de Colores. Y así como un pintor utiliza colores primarios para crear otros colores, las aplicaciones deben hacer lo mismo para lograr una tonalidad de color específica.
La tarjeta de video trabaja con tres colores básicos: Rojo, verde y azul. Con solo estos tres colores se hace posible crear cualquier combinacion de colores, en el caso de las tarjetas de video estas permiten 64 tonalidades distintas de rojo, 64 para el verde y 64 para el azul, con esto se puede llegar a la idea que a través de las paletas se pueden lograr hasta 262144 combinaciones distintas de colores (64 * 64 * 64).
¿Cómo especifico una paleta de colores?
Como dijimos, las tarjetas de video aceptan hasta 64 tonalidades distintas para cada color básico. Estas tonalidades se aplican sobre un valor de pixel en particular. Por ejemplo, se puede modificar los valores de rojo, verde y azul del pixel con valor 15. Cuando utilizamos el valor 15 aparece un pixel blanco, pero por ejemplo podemos modificar ese blanco para que resulte un poco mas grisáceo.
Pues bien, hay varias formas de especificar los valores de rojo, verde y azul para un pixel en particular, y de esas formas utilizaremos la que es más rapida para la tarjeta de video: Comunicándonos directamente con ella.

Según las normas de VGA para establecer una paleta de colores en forma directa debemos seguir estos pasos:
1- Escribir en el puerto $3C8 1 byte con el valor del color a modificar
2- Escribir en $3C9 1 byte con la cantidad de rojo
3- Escribir en $3C9 1 byte con la cantidad de verde
4- Escribir en $3C9 1 byte con la cantidad de azul

Pasado a Pascal este procedimiento quedaría así:

Procedure ModificarPaleta(Color,Rojo,Verde,Azul:Byte);
Begin
Port[$3c8]:= Color;
Port[$3c9]:= Rojo;
Port[$3c9]:= Verde;
Port[$3c9]:= Azul;
End;

Veamos ejemplos acerca de como utilizar este procedimiento:

Este ejemplo asigna al color 120 los tonos más grandes de rojo, verde y azul. Esto forma el blanco.
ModificarPaleta(120,63,63,63);

Este ejemplo asigna al color 65 los valores 0,0 y 0 para rojo, verde y azul. Esto forma el negro. (El negro es la ausencia de todos los colores).
ModificarPaleta(65,0,0,0);

Este ejemplo asigna al color 205 los valores 63 para rojo, y 0 para verde y azul. Esto forma un rojo fuerte.
ModificarPaleta(205,63,0,0);

Observa que el valor más alto que puedes especificar para rojo, verde y azul es 63.