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.
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
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.
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.
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:
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:
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.
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.
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.
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."
Santiago Romero
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).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.
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.
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.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.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.
Tabla 2: "Algunas funciones de GRAPHICS.LIB."