CURSO DE PROGRAMACIÓN GRÁFICA

Artículo 1: INICIACIÓN A LOS MODOS GRÁFICOS

Autor: (c) Santiago Romero.
Revista: Programación Actual (Prensa Técnica) nº 1, Abril-1997


Actualmente, las aplicaciones gráficas copan el mercado informático, mientras nuestros programas de texto se debaten entre las 80 columnas de la pantalla. Con este curso vamos a dejar atrás los modos de texto para aprender a dominar nuestras tarjetas gráficas.


OBJETIVO DE ESTE CURSO

En el mercado del software actual, el mercado de los entornos gráficos, de los juegos espectaculares y de unas demos con efectos imposibles de realizar hasta la fecha, y gracias a la gran potencia de proceso que nos ofrecen los nuevos ordenadores, nos damos cuenta de que los modos de texto (tan usados hasta ahora), no cubren las expectativas de este nuevo y complejo grupo de usuarios que lo mantienen.

Nuestros programas en modo texto (aunque consideremos que ese modo es el más apropiado para nuestra aplicación) no son lo suficientemente valorados sin una buena Interface Gráfica, y eso sin comentar la imposibilidad del programador para realizar un juego o demo en estos entornos. Hay que tener en cuenta que una buena interface gráfica actualmente es como mínimo el 70% del programa, puesto que es la mayor referencia que el usuario tiene de la aplicación/demo/juego.

Con este curso, los programadores que deseen "dar el salto" a la programación en modos gráficos lo podrán hacer con el apoyo que supone tener documentación, librerías y ejemplos como los que se proporcionarán aquí, mientras que los ya iniciados en la programación gráfica aprenderán aquí técnicas y algoritmos de dibujo de gran utilidad.

Entre los diversos temas que se tratarán hay asuntos tan importantes como:

 - Fundamentos de la programación gráfica,
 - Sprites, Fuentes ( monocolor, multicolor ), Paleta,
 - Modos planares y modos unchained ( X, Y, Q ... ),
 - Formatos gráficos,
 - Programación SVGA,
 - Sistemas de mapeados por bloques,
 - Algoritmos de dibujo de primitivas,
 - etc...

El objetivo es que el lector sea capaz de desarrollar sus propios programas, juegos y demos e incluso iniciarse en temas mucho más complejos como programación 3D y otros aspectos que antes quedaban más por encima de sus posibilidades.

Aparte de los conocimientos que se puedan adquirir de programación gráfica (que comenzarán desde un nivel cero o casi cero), aquí se irán comentando aspectos de ensamblador que nos ayudarán a optimizar nuestras rutinas gráficas para conseguir un mejor rendimiento del lenguaje que utilicemos, pero en principio durante todo el curso se utilizará el lenguaje ANSI C (C estándar) como base de los programas, de manera que no será necesario saber programar en assembler, porque a lo largo del curso, y dependiendo de las necesidades, se irán introduciendo y explicando instrucciones ASM con lo que los programadores de lenguajes de alto nivel (C/PASCAL) pueden aprovechar para aprender y dominar el lenguaje ensamblador.

Por supuesto, siempre será más fácil el aprendizaje si se poseen conocimientos extra de la estructura general de un PC, la memoria, y algo de assembler, de manera que el lector se beneficie de todas las nuevas técnicas que conlleva la programación gráfica actual.


ALGO DE TERMINOLOGIA

No podemos comenzar el curso sin un mínimo de "jerga gráfica" que nos ayudará a comprender los primeros aspectos de la programación de los modos gráficos. Son conceptos muy usados y que van a repetirse mucho a lo largo de los artículos, resultando por ello unos conceptos muy intuitivos y de fácil comprensión:

Modos alfanuméricos: En los modos alfanuméricos (modos de texto), la pantalla está dividida en una serie de celdillas donde sólo cabe un carácter (tanto un carácter alfabético como un número -de ahí el término alfanumérico-), proporcionándonos resoluciones generalmente reducidas, como por ejemplo 80x25, que quiere decir que tenemos la pantalla limitada a 80 caracteres por línea horizontal y con 25 líneas en el total visualizable. Este tipo de limitaciones es el que nos queremos saltar pasando a programar en los modos gráficos, en contraposición a los de texto. Cualquier forma gráfica en estos modos tiene que ser creada a base de combinaciones de símbolos ASCII. Para un mayor detalle de estos modos, consultar la sección Programación de la VGA de la revista Sólo Programadores número 9.

Pixel: Cuando ejecutamos un programa en modo gráfico, mirando a la pantalla podemos darnos cuenta de que el dibujo ahi representado está formado por una serie de unidades mínimas (unos pequeños puntos cuadrados), cada una de ellas con un color, y con la acumulación de las cuales se forman las figuras gráficas que vemos representadas en el monitor.

La palabra pixel viene de la abreviación del término inglés PEL -picture element- (elemento del dibujo), y representa la unidad mínima capaz de ser representada por el monitor en esa resolución.

Resolución: Acabamos de nombrar la palabra resolución en la definición de pixel, y es que en la pantalla caben un número determinado de pixels (o puntos) a lo ancho y a lo alto de ella. Así, podemos ver que, en un determinado juego, en la pantalla caben 320 pixels a lo ancho y 200 a lo alto mientras en Windows pueden caben por ejemplo 640x480 pixels en la misma pantalla.

La resolución de un modo de vídeo, especificado en el formato (ej: 640x480) nos indica la cantidad de pixels que caben en la pantalla tanto a lo ancho (640 en este caso) como a lo alto (480), lo que quiere decir que en la pantalla hay un total de 640x480 pixels = 307.200 pixels (en nuestro ejemplo). Queda claro pués que a mayor resolución, como el tamaño del monitor es siempre el mismo, los puntos serán más pequeños y conseguiremos una mayor calidad gráfica, mientras que a resoluciones menores (ej: 320x200) es más fácil notar el tamaño de los puntos y la calidad representada será menor.

Coordenadas de pantalla: cuando necesitemos localizar en pantalla una posición específica (por ejemplo: un punto), lo haremos a partir de sus coordenadas, que consisten en dos números que indican una posición respecto a las coordenadas <0,0> (parte superior izquierda de la pantalla).

Supongamos que en el modo 320x200 queremos localizar el centro de la pantalla. Este será, obviamente, el punto (160,100), puesto que ambos números nos indican la posición horizontal primero y luego vertical considerando la pantalla como un eje XY. El último punto de la pantalla tendría en este caso las coordenadas (319,199).

Modos gráficos: Cada modo gráfico se caracteriza por su resolución y su número de colores. Así, tenemos desde el modo gráfico 320x200x256 (256 colores) hasta, por ejemplo, el 1024x768x16M (con 16 Millones de colores). Nuestra tarjeta gráfica es capaz de inicializar todos estos distintos modos, y podremos elegir el más conveniente a nuestros propósitos para usarlo en nuestro programa. Queda claro entonces que, cuanto más nueva y potente sea nuestra tarjeta gráfica (que va instalada dentro del ordenador), de más modos gráficos dispone, de tal manera que sería imposible inicializar el modo 800x600x256 colores en una tarjeta gráfica que no sea SVGA (Super VGA). Cuando nos dispongamos a crear un programa, tendremos que decidir primero en que modo gráfico vamos a realizarlo, debido a que cada modo se programa de una manera y, como es obvio, a menor resolución y menor número de colores, el programa resultante será más rápido. Con respecto al término modo gráfico, que no sea un modo de texto no quiere decir que no podamos representar información textual, pués construiremos los carácteres a base de pixels, ya que cualquier carácter es en sí mismo un símbolo gráfico.

Paleta: Llamaremos paleta (por ahora) al conjunto de colores que posee un modo gráfico (2, 16, 256, 32.000 ó 16 millones, por ejemplo) y ya entraremos más adelante en cómo modificar este juego de colores de que disponemos para adaptar los colores existentes a las tonalidades deseadas.


BREVE HISTORIA EVOLUTIVA

Hasta llegar al nivel gráfico actual (tarjetas SVGA PCI con resoluciones como 1024x768 a 16 millones de colores), el PC ha pasado por períodos gráficos casi nulos desde que se creó (en otoño de 1981, IBM incluía en sus equipos la tarjeta MDA, Monochrome Display Adapter), con su típico interface de texto y resoluciones que pronto descubrieron las limitaciones iniciales que implicaba a la hora de presentar información gráfica (40x25, 80x25).

En ese mismo momento (1981) apareció la tarjeta CGA, (Color Graphics Array, o matriz de gráficos color). Esta tarjeta, pese a disponer de pocos modos gráficos en baja resolución (como 320x200x4 o 640x200x2) y con tan sólo 4 colores, supuso el cambio del PC hacia su estado actual. Pronto esta tarjeta se quedó corta e IBM incluyó en sus modelos PS/2 la tarjeta MCGA (MultiColor Graphics Array), que mejoraba la anterior incluyendo los modos de video de la CGA y 640x480 con 2 colores.

La tarjeta EGA (Enhaced Graphics Adapter, adaptador de gráficos mejorado) supuso en 1984 la adición de modos de video de 16 colores (320x200x16, 640x200x16 y 640x350x16) incorporando como diferencia principal con la CGA un sistema de programación por planos de bits (que veremos más adelante). Pero la verdadera revolución llegó con la aparición de la VGA (Video Graphics Array), que ha supuesto hasta ahora el mayor estándar en tarjetas gráficas, ya que incorporaba todos los modos de video soportados por las tarjetas anteriores (CGA, MCGA y EGA), añadiendo el modo 640x480 a 16 colores, y funcionando con cualquier tipo de monitor (analógicos, multifrecuencia, estándar, etc...), cosa que no hacían las anteriores tarjetas.

Nótese por lo tanto el gran soporte de compatibilidad descendente que lleva incluida la tarjeta VGA, incluyendo los modos gráficos de las tarjetas EGA, MCGA y CGA.

De la llegada de las SVGA (Super VGA) se tratará más adelante, así como su programación mediante el estandar VESA en su última versión, la VBE 2.0.


COMENZANDO EN MODO GRÁFICO

En esta primera entrega se comentará la programación en modo gráfico usando el soporte que para ello integra Borland en sus compiladores: hablamos del sistema BGI -Borland Graphics Interface- (Interface gráfico que usan tanto C como Pascal), que permite, mediante órdenes concretas del lenguaje que estemos utilizando, acceder a las funciones de dibujo más corrientes a partir de las cuales podemos empezar nuestro periplo por los modos gráficos. Por supuesto, a partir del siguiente artículo ya usaremos directamente los recursos de nuestra tarjeta sin el uso de los ficheros BGI, que, como veremos en un posterior apartado, tienen serias desventajas.

Para empezar, hay que tener claro que los ficheros .BGI incluidos con los compiladores de Borland (en el subdirectorio \BGI) son ficheros de código ejecutable con funciones como putpixel(), circle(), line(), etc... que el programador puede usar para crear cualquier forma gráfica.

Para poder usar estas funciones en nuestros programas, y tomando como ejemplo el compilador Borland C (o Turbo C), para ello tan sólo hemos de cumplir 3 requisitos:

 1. Incluir el fichero de cabecera graphics.h
 2. Incluir la librería graphics.lib en el momento de la compilación.
    Esto es muy sencillo de hacer desde dentro del entorno C (abriendo
    un fichero proyecto desde las opciones del compilador) o desde
    fuera, mediante:

	 bcc fichero.c graphics.lib

 3. Que cuando ejecutemos nuestro programa, esté presente en el directorio
    actual ( u otro definido ) el fichero BGI correspondiente al modo
    gráfico que estemos inicializando (ej: CGA = CGA.BGI, VGA = EGAVGA.BGI,
    etc...).

La mejor manera de aprender algo tan intuitivo como las órdenes BGI de Borland C es mediante un ejemplo. Veamos el listado 1 y analicémos las líneas que deben resultar nuevas.


/*    Listado1.c
      Ejemplo de inicialización del modo gráfico 640x200
      a 16 colores usando el fichero EGAVGA.BGI .   
*/

#include <graphics.h>           /* header de gráficos */
#include <stdlib.h>
#include <stdio.h>
#include <conio.h>              /* otras funciones */

int main(void)
{
   /* variables necesarias cuando iniciamos modo gráfico */ 
 int gdriver=EGA, gmode=EGALO, errorcode;

   /* inicializar modo gráfico y gestión de errores */
 initgraph(&gdriver, &gmode, "C:\\BC\\BGI");      
 errorcode = graphresult();

 if (errorcode != grOk)   
 {
   printf("Error inicializando gráficos: %s\n",
            grapherrormsg(errorcode));
   printf("Pulse cualquier tecla para salir...");
   getch();
   exit(1);
 }

  /* dibujar una línea desde (0,0) hasta (MaxX, MaxY) */
 line(0, 0, getmaxx(), getmaxy());

  /* esperar una tecla y volver al modo de texto */
  getch();
  closegraph();             /* "cerrar" los gráficos */
  return(0);                /* al modo anterior... */
}


ANÁLISIS DEL LISTADO 1

#include <graphics.h>
Este es el paso 1 nombrado anteriormente. Incluye las definiciones de las funciones para la librería graphics.lib.

int gdriver=EGA, gmode=EGALO, errorcode;
Estas variables son necesarias para usar con la función initgraph(), gdriver indica el tipo de tarjeta a usar (y, por lo tanto, el fichero BGI que va a utilizar el ejecutable), mientras que gmode indica el modo gráfico a inicializar. En este ejemplo se usa la EGA (fichero EGAVGA.BGI) poniendo gdriver=EGA, y se inicia el modo 640x200 a 16 colores (gmode=EGALO). Los posibles valores que podemos poner en gdriver y gmode están contemplados en la tabla 1.
Tabla de modos de video

Si queremos que el ordenador use el modo gráfico de mayor resolución de que disponga nuestro PC, bastará con seleccionar gdriver=DETECT y no dar ningún valor a gmode. Sigamos ahora con nuestro listado:

initgraph(&gdriver, &gmode, "C:\\BC\\BGI");
La función initgraph( driver, modo, directorio ) inicializa el modo gráfico usando el driver driver, situado en el path directorio, y entrando en el modo gráfico especificado en modo. En este caso, buscará en el directorio C:\BC\BGI cuando se ejecute nuestro programa. Si sabemos que el driver va a estar en el directorio actual cuando se ejecute nuestro programa, bastará con poner comillas vacías (""). Por supuesto, las barras dobles se introducen para que C no tome la barra simple como carácteres de escape (\n, \r, etc...). Tras llamar a esta función, y si no ha ocurrido ningún error, habremos inicializado el modo gráfico deseado. El control de errores viene a continuación:


errorcode = graphresult();
   if (errorcode != grOk)   
    {
      printf("Error inicializando gráficos: %s\n",
               grapherrormsg(errorcode));
      printf("Pulse cualquier tecla para salir...");
      getch();
      exit(1);
    }

La función graphresult() nos devuelve un código indicándonos el resultado de la inicialización gráfica. Si devuelve grOK, no habrá habido ningún error y ya podremos utilizar las funciones gráficas del BGI para dibujar en pantalla, como hace line(x1,y1,x2,y2);:

line(0, 0, getmaxx(), getmaxy());
Al igual que line(), getmaxx(), getmaxy() proceden de graphics.lib. Estas últimas devuelven, respectivamente, el ancho y alto (x máxima e y máxima) de la pantalla. La primera de ellas, line(x1, y1, x2, y2), dibuja una linea desde (x1,y1) hasta (x2,y2) usando el color actual, que puede ser cambiado usando la función setcolor( color ); También podemos borrar la pantalla usando la función clearviewport();.

closegraph();
La función closegraph() cierra el modo gráfico inicializado con initgraph() y restaura el modo que había antes de llamar a esta última.


DIBUJANDO EN UN MODO GRÁFICO YA INICIALIZADO

Una vez inicializado el modo gráfico deseado, podemos usar cualquiera de las funciones C para dibujar en la pantalla, como puede verse en los ejemplos proporcionados en el CD que acompaña a la revista. A partir de las primitivas que suponen el punto, la línea, el rellenado, etc... podemos crear buenas composiciones gráficas para ir adentrándonos en la programación gráfica.

Podemos ver en la tabla 2 algunas de las funciones gráficas de C y que podemos usar tras entrar en alguno de sus modos gráficos. En ella están todos los parámetros que requieren, y aunque no están todas las funciones BGI, en cualquier manual de referencia C o PASCAL encontraremos la lista completa.

Referencia funciones BGI

Tenemos también el programa TEST.EXE, que testea el número de figuras gráficas (pixels, líneas, etc...) por segundo que es capaz de dibujar el driver BGI en distintas resoluciones, y el fichero BGIFUNC.DOC, como ampliación a estas funciones gráficas.


DESVENTAJAS DE LOS FICHEROS BGI

Son muchas y graves las desventajas de estos ficheros. Para empezar, al incluir la librería graphics.lib, el tamaño del programa ejecutable aumenta sobre los 18.000 bytes (en el caso de Borland C/C++ v3.1) debido a la inclusión de todas las funciones, aunque sólo usemos algunas de ellas.

Además, el programa al ser ejecutado deberá tener disponible en el directorio actual (u otro establecido mediante initgraph) el fichero BGI correspondiente al modo que se inicialice, y aunque se puede linkar junto al ejecutable usando la utilidad BGIOBJ.EXE, el sistema BGI sigue pecando en el sentido que más puede quejarse un programa: su velocidad.

Si un programador desarrolla una librería para, por ejemplo, 320x200x256 colores, creará las funciones específicas para ese modo, aprovechando las características que la tarjeta nos brinde para él, y obteniendo una "librería especializada" que funcionará exclusivamente en dicho modo. Por contra, los BGI tienen que funcionar para distintos modos de video, y todo eso dentro de la misma función detectando en cada momento la resolución que se está usando y de una manera "estándar" para todos ellos, por lo que el código ejecutable resultante sea demasiado lento para intentar hacer con él un programa de carácter profesional.

Esto no quiere decir que no sea recomendable el aprendizaje del sistema BGI, sino que debe ser tomado como base porque es la mejor manera de iniciarse en los modos gráficos.


EN LA PRÓXIMA ENTREGA

Este primer artículo ha sido tan sólo introductorio, ya que su objetivo era la integración del lector en los modos gráficos de una manera sencilla (en este caso órdenes del propio compilador), pero a partir del próximo número entraremos realmente en la programación específica de los diversos modos, usando para ello tanto la BIOS de la tarjeta (uso de la interrupción 10h) como el hardware de la VGA (programación directa de la video memoria), dejando atrás el BGI que, por otra parte, sería aconsejable dominar debido a su facilidad de uso, sobre todo en aquellos programadores que abandonan ahora los modos de texto.

De cualquier manera, proponemos al lector que realice programas y ejemplos con los BGI para que pueda valorar las funciones que proporciona y la velocidad que desarrolla, y poder así darse cuenta de las limitaciones que encierra y la diferencia entre estos ejemplos y los que crearemos a partir de la próxima entrega. Será a partir de entonces cuando entraremos de lleno en la programación de los distintos modos gráficos.

Pulse aquí para bajarse los ejemplos y listados del artículo (65 Kb).

Tabla 1: "Drivers BGI y sus respectivos modos gráficos."
Tabla 2: "Algunas funciones de GRAPHICS.LIB."

Santiago Romero


Volver a la tabla de contenidos.