|
|
Artículo realizado por
El formato gráfico PCX es aquel que utiliza la compresión
creada por Zsoft. Este tipo de fichero no alcanza las compresiones realizadas
por otros ficheros gráficos como los GIF o los JPG,
pero el tiempo que se tarda en descomprimir estos últimos es demasiado
elevado. EL motivo por el que el PCX ha sido escogido es porque
su compresión es bastante aceptable, acompañado de una elevada
velocidad de descompresión. Si quisiéramos usar por ejemplo
un JPG de la misma forma en que en se usan los PCX, el retardo sería
inaceptable.
El formato PCX se encuentra en casi todos los programas gráficos
que existen. En concreto, los usados para este proyecto: DPaint Enhanced,
Autodesk 3D Studio y Paint Shop Pro, Photoshop. Otros programas
como Corel Draw, Photo Paint o el mismo AutoCad y
otros más, soportan este formato, incluido el Paintbrush
de Windows, que lo incorpora como estándar junto al BMP.
Un fichero PCX comienza con una cabecera de 128 bytes. En general, esta
cabecera no se usa, puesto que el control de los distintos formatos gráficos
lo realiza el propio programa, y no el usuario (que podría equivocarse).
Esto tiene un motivo: de nuevo la velocidad. Se perdería mucho tiempo
si tuviésemos que chequear los valores de la cabecera para comprobar
que en efecto es un fichero con la resolución adecuada, el número
de bytes correcto...
Tras los 128 bytes de cabecera siguen los bytes de la imagen gráfica.
El método usado es la técnica Run-Length-Enconding
(RLE) orientada a byte.
La siguiente tabla presenta el contenido de la cabeza de un fichero
PCX.
Para decodificar un fichero PCX se realiza continuamente un proceso
determinado:
Tras el último byte de la codificación, se encuentra un
byte de separación con el valor 12 en decimal y tras él,
768 bytes indican ternas consecutivas de valores RGB.
Pero estos valores no se pueden tomar directamente, ya que son bytes
(0-255) y los valores RGB van de 0 a 63, por lo que tendremos que dividir
el valor leído por 4.
El siguiente procedimiento se encarga de leer un fichero y guardarlo
en una zona de memoria de 64000 bytes, asignando finalmente la paleta al
DAC de vídeo
void LeePcxMcga(char nombre[12],BYTE *pantalla)
Aparte de este procedimiento se puede hacer el siguiente:
void LeePcxMcgaR(char [12],unsigned char *,PALETA);
Es el mismo que el anterior con la salvedad de que no asigna la paleta
al DAC de vídeo, sino que la almacena en la variable de tipo
PALETA para su posterior uso. Esto es útil para los volcados
entre pantallas que no tienen la misma paleta, puesto que si se usase el
anterior procedimiento, la primera pantalla vería su paleta trastocada
y el efecto sería desastroso al cambiar los colores a otros que
puede que no tengan nada que ver.
2.1.11.- Lectura de Ficheros FLI
Un fichero FLI es un tipo de archivo en el que se guarda la información
referente a una animación, estructurada de forma óptima para
su posterior reproducción.
Este tipo de ficheros es soportado por programas como Autodesk Animator
Pro y Autodesk 3D Studio 4.0. Es el más conocido de los
ficheros de animación (junto a su hermano mayor, el FLC).
Los detalles de un fichero FLI son moderadamente complejos, pero
la idea en la que se basa es simple: no hay que preocuparse en guardar
las partes de un frame (pantalla hablando en términos de
animación) que son las mismas que las del anterior frame. No solamente
esto ahorra espacio, sino que es mucho más rápido dejar un
píxel que escribirlo de nuevo.
Un fichero FLI, como todos los ficheros que almacenan algún tipo
de formato que se va a usar posteriormente, tiene una cabecera. Su tamaño
es de 128 bytes. Tras esta cabecera están almacenados un conjunto
de frames. Utilizando la idea anterior, los siguientes frames son almacenados
como la diferencia con el anterior frame. Imprescindiblemente, el primer
frame almacenado debe ser guardado completamente, ya que no hay uno anterior
con el que calcular la diferencia. Para comprobaciones y chequeos (no lo
usamos por el motivo de la velocidad a cambio de una pequeña pérdida
de posible seguridad) hay un último frame que contiene la diferencia
entre el último frame anterior a éste, y el primero.
La cabecera de un fichero FLI es la siguiente:
José Antonio Suárez.
Capítulo 6.
Programación de tarjetas de vídeo (IV).
2.1.10.- Lectura de Ficheros PCX
Byte
Ítem
Tamaño
Descripción
0
Creador
1
Constante, 10 = ZSoft .pcx
1
Versión
1
Información de Versión:
0 = Versión 2.5 de PC Paintbrush.
2 = Versión 2.8 w/palette: información.
3 = Versión 2.8 w/o palette: información.
4 = PC Paintbrush para Windows (además Windows usa la versión
5).
5 = Versión 3.0 y > de PC Paintbrush y PC Paintbrush +, incluye
Publisher´s Paintbrush y ficheros PCX de 24 bits.
2
Codificación
1
1 = .PCX run length encoding.
3
Bits por Píxel
1
Número de bits para representar un píxel
(por plano) - 1, 2, 4, o 8.
4
Ventana
8
Dimensiones de la imagen: Xmin,Ymin,Xmax,Ymax.
12
HDpi
2
Resolución horizontal de la imagen en
DPI .
14
VDpi
2
Resolución vertical de la imagen en DPI.
16
Mapa de color
48
Paleta de colores.
64
Reservado
1
Constante = 0
65
Nº Planos
1
Número de planos de color.
66
Bytes por línea
2
Número de bytes necesarios para guardar
una plano de línea. Tiene que ser un número impar.
68
Paleta
2
Indica cómo interpretar la paleta.
1 = Color/BW
2 = Escala de grises
70
HScreenSize
2
Tamaño horizontal de la pantalla en píxels.
72
VScreenSize
2
Tamaño vertical de la pantalla en píxels.
74
Relleno
54
Bytes con valor 0 hasta rellenar los 128 de
la cabecera.
{
register WORD cont,cont2;
BYTE *direscrt,byte,r,g,b,filas;
FILE *fichero;
fichero=fopen(nombre,"rb"); fseek(fichero,128,0);
direscrt=pantalla; cont=64000;
while (cont>0)
{
byte=getc(fichero);
if (byte<=192)
{ *direscrt++=byte; cont--; }
else
{
cont2=byte&63; byte=getc(fichero);
for(;cont2>0;cont2--)
{ *direscrt++=byte; cont--; }
}
}
getc(fichero);
for (cont2=0;cont2<256;cont2++)
{
r=getc(fichero)>>2; g=getc(fichero)>>2;
b=getc(fichero)>>2;
Establecer1ColorPaleta(cont2,r,g,b);
}
fclose(fichero);
}
|
|
|
|
|
|
|
Longitud del fichero. |
|
|
|
Constante = AF11 |
|
|
|
Número de Frames. Máximo 4000 frames. |
|
|
|
Anchura de la resolución de pantalla. |
|
|
|
Altura de la resolución de pantalla. |
|
|
|
Profundidad de un píxel. Normalmente 8. |
|
|
|
Constante = 0 |
|
|
|
Número de ticks entre cada frame. |
|
|
|
Constante = 0 |
|
|
|
Constante = 0 |
|
|
|
Relleno de ceros hasta completar los 128 bytes. |
Como se indicó antes, tras la cabecera vienen los frames.
Éstos a su vez tienen cada uno su propia cabecera de 16 bytes:
|
|
|
|
|
|
|
Bytes en el frame. Como máximo 64 Kbytes. |
|
|
|
Constante = F1FA |
|
|
|
Número de Chunks en el frame. |
|
|
|
Relleno de ceros hasta completar los 16 bytes. |
Después de la cabecera del frame, vienen los chunks que componen el frame.
Primero viene un chunk de color si la paleta de colores ha cambiado desde el anterior frame, luego viene un chunk de píxel si los píxel han cambiado con respecto al anterior frame. Aclaremos ahora en qué consiste un chunk.
Un chunk es como una estructura determinada que contiene un tipo de
información. De nuevo, cada chunk tiene una cabecera de 6 bytes:
|
|
|
|
|
|
|
Número de bytes en el chunk. |
|
|
|
Tipo del chunk. |
Hay cinco tipos de chunk:
|
|
|
|
|
Paleta comprimida. |
|
|
Línea comprimida. |
|
|
Establece toda la pantalla al color 0. |
|
|
Compresión del primer frame con RLE. |
|
|
64000 bytes sin comprimir. |
Analicemos ahora en detalle lo que significa cada tipo de chunk, sabiendo que el primero es el chunk de color que antes apuntamos, y los cuatro restantes son chunks de píxel.
Además hay que decir que los esquemas de compresión son todos orientados a byte como en los PCX. Para permitir DMA (no es el caso nuestro), el FLI observa que si los datos comprimidos terminan con una longitud par, un byte de relleno es insertado para que el chunk FLI_COPY comience siempre en una dirección impar.
Análisis de los chunks:
FLI_COLOR
El primer byte de una línea comprimida indica el número de paquetes en esa línea. Si la línea no cambia con respecto al anterior frame, este valor es 0. El formato individual de cada paquete es el siguiente:
En el peor de los casos un frame comprimido mediante chunks FLI_LC, puede llegar a ocupar 70 Kbytes. Para prevenir esto, si en la compresión se detecta que se ha pasado de los 60000 bytes o más, no se continúa con la compresión, se deshecha y se utiliza FLI_COPY
Consiste en un conjunto de 64000 bytes de datos sin comprimir.
FLI_BLACK
void VerFLIMcga(char fichero[12],int retardo,BYTE
*pant)
{
register WORD pixel,vc;
int segmento,desplazamiento;
FILE *fli;
CABECERA_FLI hdr;
CABECERA_FRAME frm;
unsigned long size;
WORD type,pakets,cnt,lines,change,i,c,f,cont,cont2;
BYTE skip,v,*aux,rojo,verde,azul;
signed char s;
clock_t tiks;
PALETA paleta;
// Apertura del fichero
fli=fopen(fichero,"rb");
if (!fli)
{
ModoTexto();
printf("\nNo se encuentra
el fichero %s",fichero);
exit (0);
}
// Máximo buffer para dar velocidad
en la carga del fichero
setvbuf(fli,VBuf,_IOFBF,BUFSIZE);
// Lectura de la cabeza
fread(&hdr,sizeof(CABECERA_FLI),1,fli);
// Para cada frame...
for (f=0;f<hdr.frames;f++)
{
tiks=clock();
// Lee una cabecera
de frame
fread(&frm,sizeof(CABECERA_FRAME),1,fli);
if (frm.size!=0L)
for (i=0;i<frm.chunks;i++)
{
// Cabecera del chunk
fread(&size,sizeof(long),1,fli);
type=getw(fli);
// Dependiendo del tipo del frame
switch (type)
{
// Línea comprimida
case FLI_LC:
// Número de líneas que son iguales al frame anterior
lines=getw(fli);
// Número de líneas que han cambiado
change=getw(fli);
// Pasado a ensamblador: cont=(256*lines+64*lines) == 320*lines
asm{
mov vc,4
mov ax,lines
mov bx,ax
shl ax,8
shl bx,6
add ax,bx
mov cont,ax
}
cont2=cont;
for(c=0;c<change;c++)
{
pakets=getc(fli);
asm{
inc vc
}
while (pakets>0)
{
skip=getc(fli);
asm{
dec pakets
inc vc
xor ah,ah
mov al,skip
add cont,ax
inc vc
}
s=getc(fli);
if (s>0)
{
asm{
xor ah,ah
mov al,s
add vc,ax
}
salto:
rojo=getc(fli);
asm{
mov ax,0xA000
mov es,ax
mov di,cont
mov al,rojo
stosb
inc cont
dec s
jnz salto
}
}
else
{
s=-s;
v=getc(fli);
asm{
inc vc
xor ch,ch
mov cl,s
mov ax,0xA000
mov es,ax
mov di,cont
mov al,v
rep stosb
xor ah,ah
mov al,s
add cont,ax
}
}
}
asm{
add cont2,320
}
cont=cont2;
}
// Si ha habido un número impar de bytes, se lee el de relleno
if (vc&1) getc(fli);
break;
case FLI_BRUN:
// Para contar el número de bytes descomprimidos
asm{
mov vc,0
mov cont,0
}
// Compresión línea por línea
for (skip=0;skip<200;skip++)
{
// Número de paquetes en la linea
pakets=getc(fli);
asm{
inc vc
}
while (pakets>0)
{
// Número de veces que se repite un byte
s=getc(fli);
asm{
inc vc
dec pakets
}
// Si s es negativo, el número de bytes es -s y est n en
// los siguientes s bytes: no está comprimido
if (s<0)
{
s=-s;
asm{
xor ah,ah
mov al,s
add vc,ax
}
salto1:
rojo=getc(fli);
asm{
mov ax,0xA000
mov es,ax
mov di,cont
mov al,rojo
stosb
inc cont
dec s
jnz salto1
}
}
// Si es positivo, se repite el siguiente byte s veces:
// está comprimido
else
{
v=getc(fli);
asm{
inc vc
mov ax,0xA000
mov es,ax
mov ax,cont
mov di,ax
mov al,v
mov cl,s
xor ch,ch
rep stosb
xor ah,ah
mov al,s
add cont,ax
}
}
}
}
// Si se han obtenido un número impar de bytes, entonces falta
// otro byte que es de relleno
if (vc&1) getc(fli);
break;
// Cambiar un número determinado de colores
case FLI_COLOR:
// Número de paquetes comprimidos
pakets=getw(fli);
asm{
mov vc,2
mov c,0
}
while (pakets--)
{
// Cuántos colores se saltan
skip=getc(fli);
asm{
inc vc
// Cada 3 bytes representa 1 color: rojo, verde y azul, por lo
// que se saltar n 3*número de colores bytes
mov al,skip
xor ah,ah
mov cl,3
mul cl
add c,ax
inc vc
}
// Cuántos colores se cambian
cnt=getc(fli);
// Si es 0, se interpreta como 256, de 0 a 255 colores a cambiar
if (cnt==0)
asm{
mov cnt,256
}
while (cnt--)
{
// Se asignan los valores RGB de cada color a la paleta
paleta[c]=getc(fli);
asm{
inc c
}
paleta[c]=getc(fli);
asm{
inc c
}
paleta[c]=getc(fli);
asm{
inc c
add vc,3
}
}
}
// Se hace efectivo el cambio de paleta
for (lines=0,change=0;lines<256;change+=3,lines++)
{
rojo=paleta[change];
verde=paleta[change+1];
azul=paleta[change+2];
asm{
mov ax,lines
mov dx,0x3c8
out dx,al
mov al,rojo
mov dx,0x3c9
out dx,al
mov al,verde
out dx,al
mov al,azul
out dx,al
}
}
// Si ha habido un
número impar de bytes, se lee el de relleno
if (vc&1) getc(fli);
break;
// 64000 bytes sin
comprimir
case FLI_COPY:
aux=pant;
// Se leen los 64000
píxeles de la pantalla
for (pixel=0;pixel<64000;pixel++)
*aux++=getc(fli);
// Se vuelcan de una
vez
segmento=FP_SEG(pant);
desplazamiento=FP_OFF(pant);
asm{
push ds
mov ax,0xA000
mov es,ax
mov ds,segmento
mov si,desplazamiento
xor di,di
mov cx,32000
rep movsw
pop ds
}
// Aquí no
hay comprobación del número de vc, 64000 es par
break;
// Cls a negro
case FLI_BLACK:
asm{
mov ax,0xA000
mov es,ax
xor di,di
xor ax,ax
mov cx,32000
rep stosw
}
break;
// Si no se
reconoce, se sale
default:
ModoTexto();
printf("\nVersión
incorrecta de archivo FLI");
exit (0);
}
}
// Espera hasta el número especificado
de ticks del reloj
while (clock()-tiks<retardo);
}
fclose(fli);
}
Este procedimiento nos permite indicar la velocidad de su ejecución,
siendo la más adecuada el valor 1 para los ordenadores rápidos
(486-Pentium), y 0 para los más lentos. Si por las características
globales de un ordenador determinado, con el valor 1 la animación
fluye demasiado deprisa, o se quiere que avance lentamente, los valores
2 y 3 se pueden llegar a utilizar sin perder demasiada fluidez.
|
|