Tutorial básico sobre utilización de gráficos con la unidad "Graph"

Escrito por Nicolás Rozas Salgado


Objetivo:
El objetivo de este tutorial es brindar los conocimientos a aquellos con interés de desarrollar aplicaciones que utilicen gráficos. Este tutorial se basa en la unidad "Graph" incluida en las versiones de Turbo y Borland Pascal.


Desarrollo:
Borland Pascal tiene una linda unidad para tratamiento de gráficos. Esta unidad es "GRAPH.TPU". Asumiremos para ello que tienes Turbo / Borland Pascal 7.0 (Si tienes la versión 5, 5.5 o 6 tal vez sea necesario modificarlo un poco). Hay que asumir también que tienes una tarjeta de video compatible con VGA (Si tu PC es al menos una 486 o superior, entonces es muy probable que la tarjeta sea compatible). Tendrás que verificar que tengas el archivo EGAVGA.BGI. Todos los archivos de fuentes pueden ser útiles si deseas hacer programas que necesiten muchas fuentes (Los archivos de fuentes tienen la extensión *.CHR). Si no los tienes es porque tu versión de Turbo / Borland Pascal no está completa.

Lo que se va a ver en este tutorial es simplemente el tratamiento con gráficos. Desafortunadamente la unidad Graph solo provee modos gráficos de 16 colores, pero por ahora será suficiente (Igualmente, ya veremos posteriormente a cómo utilizar los modos de 256 colores). La unidad es sencilla de utilizar, así que no es necesario inmiscuirse mucho en la forma con la que trabaja la unidad, lo cual en consecuencia nos es útil para aprender lo básico antes de apuntar más "hacia arriba".

La única resolución que se adoptará en este tutorial es 640x480 (16 colores). Esta es la resolución más grande que se puede utilizar con EGAVGA.BGI.

Iniciando y cerrando:
Lo primero que hay que hacer antes de iniciar los gráficos es iniciar el controlador. Cuando el controlador se inicie, el modo gráfico se habrá establecido también.

Cuando se termine de utilizar los gráficos, lo que hay que hacer es "cerrar" el controlador, con ello se volverá al modo de texto inmediatamente.
El siguiente código hace ese trabajo:


Uses Crt, Graph;
Const BGIDir = '.\BGI';

Procedure Iniciar;
Var Gd, Gm: Integer;
Begin
  Gd:=VGA; Gm:=VGAHi; {Especificamos aquí el adaptador y el modo}
  InitGraph(Gd, Gm, BGIDir); {Inicia el modo gráfico}
  Gd:= GraphResult; {Guardo el resultado de la operación en "Gd"}
  If Gd <> GrOK Then {¿Hubo algún error?}
  Begin {Sí}
    Writeln('Hubo un error iniciando el modo gráfico.');
    WriteLn('La descripción del error es el siguiente: ');
    WriteLn(GraphErrorMsg(Gd)); 
    ReadKey;
    Halt;
  End;
End;

Procedure Cerrar;
Begin
  CloseGraph;
End;

Begin
  Iniciar;
  ReadKey;
  Cerrar;
End.

La parte principal del procedimiento "Iniciar" es "InitGraph". Este acepta tres parámetros: Los primeros dos son el adaptador y el modo gráfico (El adaptador gráfico es VGA y el modo es VGAHi (640x480x16)). El tercer parámetro es la carpeta donde está el controlador EGAVGA.BGI, en nuestro caso la carpeta está definida como una constante (BGIDir), pero habrá que cambiarle el valor si EGAVGA.BGI no se encuentra en esa carpeta.

Después de llamar a "InitGraph", necesitaremos detectar si se ha podido establecer el modo gráfico o no. Este valor se puede obtener a través de "GraphResult", el problema es que el valor que devuelve "GraphResult" se elimina después de llamarla (Como pasa con "IOResult"), así que debemos guardar el valor en alguna variable entera. En nuestro caso utilizamos "Gd" que no se utiliza más después de "InitGraph". Si hay un error (Esto quiere decir, que "Gd" no es "grOk") podemos pasarle el valor de "Gd" al procedimiento "GraphErrorMsg" (Cuyo mensaje saldrá escrito en inglés).

Luego viene la parte que cierra, que incluye solo a "CloseGraph". Después de llamar a "CloseGraph", el modo texto es reestablecido.

A partir de aquí utilizaremos las instrucciones gráficas después de llamar a "Iniciar" y antes de llamar a "Cerrar". El programa escrito arriba solo utiliza "ReadKey", así que lo que hace en definitiva el programa es iniciar el modo gráfico, espera a que se presione una tecla, vuelve al modo texto y finalmente sale.

Comandos básicos para gráficos y colores:
Supongamos que una vez que ya tenemos el modo gráfico activado deseamos dibujar una linea. Entonces debemos agregar la siguiente instrucción antes de "ReadKey":

Line(0, 0, 639, 479)

Con esto podrás ver una linea diagonal a través de la pantalla. Este procedimiento dibuja una linea desde (0, 0) hasta (639, 479). En el modo gráfico que estamos utilizando los valores permitidos para las coordenadas deben estar entre 0 a 639 para X, y 0 a 479 para Y. Intentar dibujar fuera de estos rangos no tiene efecto alguno.
Si antes de "Line" escribes "SetColor(12)" la linea quedará pintada de verde en vez de blanco. "SetColor" establece el color que se va a utilizar. Los rangos válidos están entre 0 y 15.

Otros comandos de la unidad:

Rectangle(X1,Y1,X2,Y2) Dibuja un rectángulo de coordenadas (X1 Y1) hasta (X2 Y2).

Circle(X,Y,Radio) Dibuja un círculo con centro en (X Y) y de radio en el parámetro "Radio".

Ellipse(X,Y,AnguloInicial,AnguloFinal,RadioX,RadioY) Dibuja un arco desde "AnguloInicial" hasta "AnguloFinal" y con radio establecido en "RadioX" y "RadioY". El centro de ese arco se define con los parámetros "X" e "Y".

FillEllipse(X,Y,RadioX,RadioY) Dibuja una elipse rellena con centro en (X,Y) y con radio establecido en "RadioX" y "RadioY".

Arc(X,Y,AnguloInicial,AnguloFinal,Radio) Dibuja un arco desde "AnguloInicial" hasta "AnguloFinal" y con radio establecido en "Radio". El centro del arco se define con los parámetros "X" e "Y".

Bar(X1,Y1,X2,Y2) Dibuja una barra (Como las de las tablas de barras) desde (X1,Y1) hasta (X2,Y2).

Bar3D(X1,Y1,X2,Y2,Profundidas,Tope) Igual a "Bar" con la diferencia de poder dibujarla en tres dimensiones. "Profundidad" se refiere a la longitud de la barra en la tercera coordenada. "Tope" debe ser de tipo "Boolean", si es "True" la barra tendrá un tope dibujado, de lo contrario especifica "False" a este parámetro.

Nota: Para especificar el color que se debe utilizar en el momento de dibujar alguna de las figuras mencionadas, es necesario utilizar el procedimiento "SetColor" y pasar como parámetro el color a utilizar (Cuyo rango debe estar entre 0 y 15). Ejemplo: "SetColor(1);"

Comandos para fuentes y textos:
Contrario a lo que sucede en modo texto, en modo gráficos no debemos utilizar "Write" o "WriteLn" para escribir algo a la pantalla. En su lugar utilizamos "OutText" y "OutTextXY". La diferencia entre ellos es que "OutTextXY" emplea coordenadas "X" e "Y" directamente y "OutText" depende de la posición del "cursor virtual" (Definido a través del procedimiento "MoveTo"). Prueba cómo funciona agregando las siguientes instrucciones:
OutText('Hola a todos!!!');
OutTextXY(100,150,'Hola de nuevo!!!');

También es posible cambiar la fuente que se está utilizando. Primero, hay que asegurarse de que tienes los archivos de fuentes. Estos se encuentran en el directorio BGI con la extensión CHR (Busca los archivos TRIP.CHR, LITT.CHR, SANS.CHR, y GOTH.CHR. Si están en la misma carpeta que EGAVGA.BGI las fuentes podrán ser utilizadas).
Para cambiar la fuente utilizada por "OutText" y "OutTextXY" tienes que utilizar previamente el procedimiento "SetTextStyle".

"SetTextStyle" acepta tres parámetros numéricos. El primero es el tipo de fuente (Especifica para ello valores entre 0 y 9). El segundo es la dirección (0 si es horizontal y 1 si es vertical). El último es el tamaño de la fuente, cuyo rango oscila entre 1 y 10.

Otro procedimiento es "SetTextJustify", cuya tarea es modificar la justificación del texto. Acepta dos parámetros: El primero es la justificación horizontal y el otro es la justificación vertical. Para ello puedes utilizar las siguientes constantes ya definidas dentro de GRAPH.TPU:

Horizontal: LeftText (A la izquierda)     CenterText (Al medio)      RightText (A la derecha)
Vertical: TopText (Hacia arriba)     CenterText (Al medio)      BottomText (Hacia abajo)

Ejemplo: "SetTextJustify(CenterText,TopText);" mostrará el texto centrado y puesto "hacia arriba".
También es posible modificar el tamaño de la fuente de otra forma, utilizando el procedimiento "SetUserCharSize". Este procedimiento toma cuatro parámetros, que podríamos mencionarlos como A, B, C y D. Con este procedimiento, el texto será agrandado (A Div B) veces horizontalmente y (C Div D) veces verticalmente. Por ejemplo:

SetUserCharSize(2,1,1,1);
OutTextXY(100,100,'Tanto como gordo');
SetUserCharSize(1,1,2,1);
OutTextXY(100,140,'Tanto como flaco');


Limpiar y limitar la pantalla:
Contrario a lo que pasa en la unidad CRT, en modo gráfico no puedes utilizar "ClrScr". En su lugar utiliza "ClearDevice".
Tampoco es posible utilizar "Window" para definir una región de pantalla adonde podamos dibujar, pero en modo gráfico este procedimiento se reemplaza con "SetViewPort". Por ejemplo, si queremos limitar la región de uso a las coordenadas (100, 150) y (400, 300) entonces utilizamos "SetViewPort(100,150,400,300);". Notar que a partir del momento en que se llama a "SetViewPort" todas las instrucciones dibujarán solo en esa región definida tomando como extremo el (0,0). Como ejemplo, si utilizo "Line(0,0,10,15)" la linea no será dibujada tomando las coordenadas de la pantalla, sino que tomará las definidas en "SetViewPort".
Si lo que deseas ahora es volver a utilizar toda la pantalla vuelve a llamar a "SetViewPort" especificando esta vez toda la región de la pantalla, es decir "SetViewPort(0,0,639,479);".

Pixeles y paletas:
Con la unidad Graph puedes dibujar si quieres un simple punto en la pantalla. Ese punto en la pantalla se llama "pixel" y la pantalla que utilizamos dispone de un total de 640 pixeles de ancho y 480 pixeles de alto, es decir ¡Unos 307.200 pixeles pueden ser vistos en la pantalla al mismo tiempo!
Para escribir un pixel puedes utilizar el procedimiento "PutPixel" que toma tres parámetros: Los dos primeros son las coordenadas (X,Y) a dibujar (No lo olvides: X debe estar entre 0 y 639, e Y debe estar entre 0 y 479). El tercero es el color (Aquí no se utiliza "SetColor").

Ejemplo: "PutPixel(0,0,14);" Dibuja un pixel amarillo en el extremo izquierdo de la pantalla.
Como un complemento a "PutPixel" está "GetPixel". "GetPixel" es una función que devuelve el valor de un pixel que está en una posición (X,Y) pasados como parámetros.

También es posible cambiar los valores de paleta de un pixel en particular a través de "SetRGBPalette". (Si no comprendes en este momento para qué se utilizan las paletas recuerda que cada color se forma con la combinación de tres colores primarios. En una PC esos colores son el rojo, el verde y el azul. Cada valor de pixel tiene definido sus valores de rojo, de verde y de azul; una vez definidos estos valores la tarjeta de video los asigna al pixel en pantalla inmediatamente)."SetRGBPalette" toma cuatro parámetros: El primero de ellos es el valor del pixel y los otros tres corresponden a las tonalidades de rojo, verde y azul (Cada una de ellas deben ser especificadas en valores que oscilan entre 0 a 255).

Normalmente, como ocurre en los modos de 256 colores, cada número de pixel tiene asignada una entrada en la tabla de paletas con un número de índice igual al valor del píxel. Lamentablemente, por una razón interna de la arquitectura de la tarjeta VGA, esto no es así en el modo de 16 colores que estamos utilizando. El siguiente arreglo de constantes definidas posee el número de la entrada que le corresponde en realidad a cada píxel en particular:

Const Entradas_VGA: Array[0..15] Of Byte = (0,1,2,3,4,5,20,7,56,57,58,59,60,61,62,63);

Comprendo que esto puede ser confuso y explicar los motivos de esta arquitectura puede resultar en aun más confusión, así que lo recomendable es crear un procedimiento que se encargue de asignar el valor real de cada píxel sin preocuparnos más por el detalle:

Procedure EstablecerPaleta(Pixel,Rojo,Verde,Azul: Byte);
Const
Entradas_VGA: Array[0..15] Of Byte = (0,1,2,3,4,5,20,7,56,57,58,59,60,61,62,63);
Begin
SetRGBPalette(Entradas_VGA[Pixel],Rojo,Verde,Azul);
End;

Cuando desees cambiar el valor de rojo, verde y azul a un pixel en particular llama a "EstablecerPaleta" pasándole como parámetros el valor del píxel (De 0 a 15) seguido de las correspondientes tonalidades de rojo, verde y azul que le corresponderán a ese pixel.

1