Formato PCX

 
Hardware
Modo 13h
Retrazado Vertical
Primitivas
La Paleta
Pantallas Virtuales
Sprites
Tablas Prefedifinidas
Efectos
Texto
Formatos gráficos
Periféricos
Optimizaciones
Herramientas
Cursos
Links
Foro
Correo Electrónico
Este formato gráfico fue creado por ZSoft y difundido por la famosa familia de programas de dibujo Paintbrush. Pero es su facilidad de codificación y su rápida descompresión lo que lo han hecho existir entre los formatos gráficos que hoy existen, a pesar de tener uno de los peores ratios de compresión.

Nosotros nos centraremos en este formato para representar imágenes en el modo 13h, es decir trataremos imágenes de hasta 320x200 pixeles.

El formato PCX esta dividido básicamente en tres partes:

1. La cabecera
2. Los datos o imagen
3. La paleta
 

1. La cabecera: la conforman los 128 primeros bytes, y nos da información sobre la imagen. Es lo primero que debemos leer de un archivo para saber una serie de datos de la imagen.

Las características presentadas en la cabecera son las siguientes:

Byte 0: Identificación (El valor 10 significa ZSoft)
Byte 1: Versión
Byte 2: Tipo de compresión
Byte 3: Numero de bits por pixel (8)
Byte 4: X mínima (2 bytes)
Byte 6: Y mínima (2 bytes)
Byte 8: X máxima (2 bytes)
Byte 10: Y máxima (2 bytes)
Byte 12: Resolución horizontal en DPI (2 bytes)
Byte 14: Resolución vertical en DPI  (2 bytes)
Byte 16: Datos para la paleta de 16 colores (48 bytes)
Byte 64:  Reservado
Byte 65: Número de planos de color
Byte 66: Bytes por línea (2 bytes)
Byte 68: Si es 1 = Color, 2 = Escala de grises (2 bytes)
Byte 70: Reservados (58 bytes)
 

2. Los datos o imagen:  Aquí se encuentran los datos con una codificación algo especial, denominada RLE (Run Length Encoded), es decir una codificación por longitud de serie. Es una técnica de compresión algo antigua. Se basa en no repetir datos de igual valor si son consecutivos, mediante un algoritmo algo técnico. Para que quede mas claro como funciona este algoritmo, supongamos que tenemos los siguientes bytes de una imagen:

62 62 62 62 62 62 62

Si codificamos esto con RLE nos quedaría como: 07 62
Es decir, primero guardamos la longitud de la cadena, seguido por el valor que se repite.

Para ir decodificando cada byte de la imagen, debemos empezar después de la cabecera de 128 bytes. Primero leemos un byte. Si los dos bits mas significativos valen cero, entonces el byte leído es un byte de datos y simplemente lo almacenamos en la memoria. Si los dos bits mas significativos valen uno, entonces el byte es un contador que indica el número de veces que se repite el siguiente byte.
Para comprobar los dos bits más significativos, hacemos lo siguiente:

if((byte & 192)==192)  // Los dos bits están activados, por lo tanto es un contador
else                                 // es un byte de datos

En el caso de que los dos bits superiores estén activos, entonces usamos ese byte como contador, pero desactivando los dos bits superiores, ya que estos solo sirven para indicar que el byte que leímos es un contador. 

Para desactivar los dos bits superiores, solamente hacemos la operación lógica & del byte que leímos con el valor 63. Es decir hacemos esto:

contador=byte & 63;

3. La paleta: Al final se haya la paleta que tiene un primer byte que debe ser 12, o en hexadecimal 0Ch. Esto es para indicar que la paleta contiene 256 colores. Como cada color se guarda como una estructura RGB, es decir, por combinación de los colores primarios rojo, verde y azul, tendremos 768 datos para contener la paleta de 256 colores.

Antes de comenzar a crear la rutina para leer este tipo de archivos. Deberemos crearnos una estructura para guardar los datos de la cabecera:
 

typedef struct
{
    char Fabricante;                  // Fabricante (0ah = ZSoft)
    char Version;                       // Número de versión del pcx
    char Compresion;                // Hay compresión? 1=Si RLE
    char BPP;                            // Bits por pixel
    int  xmin;                             // Dimensiones del pcx
    int  ymin;
    int  xmax;
    int  ymax;
    int  Hres;                              // Resoluciones de la imagen (DPI)
    int  Vres;
    char Palette16[48];               // Paleta de color (no la usamos)
    char Reservado;                  // No se utiliza, tiene que valer 0
    char Numero_planos;          // Indica el número. de planos de color en la imagen
    int  Bytes_por_linea;           // Numero de bytes por línea de la imagen
    int  Palette_type;                // Como interpretamos la paleta
    char Reservados[58];          // Reservados para futuras ampliaciones
} PcxHead;

De esta forma tenemos un nuevo tipo de dato, que contiene la información necesaria para manejar la cabecera de un archivo PCX.
Ahora veremos como cargar una imagen y guardarla en una pantalla virtual.
 

int LoadPcx(char *filename, int off_x, int off_y, t_paleta paleta, unsigned char *where)
{
    enum{ARCHIVO_NO_ENCONTRADO=0, NO_ES_ZSOFT=-1, MUY_GRANDE=-2, OK=1};

    unsigned int cont, ancho, alto, x=0, y=0;
    unsigned char byte, listo=0;
    PcxHead head;
    FILE *f;

    if((f=fopen(filename, "rb"))==NULL)  return ARCHIVO_NO_ENCONTRADO;

    fread(&head, sizeof(char), 128, f);

    if(head.Fabricante!=0x0A)
    {
        fclose(f);
        return NO_ES_ZSOFT;
    }

    ancho=(head.xmax-head.xmin)+1;
    alto=(head.ymax-head.ymin)+1;

    if((ancho>320) || (alto>200))
    {
        fclose(f);
        return MUY_GRANDE;
    }

    while(!listo)
    {
        byte=fgetc(f);
        // Si los 2 bits mas significativos estan activados, entonces el byte es un contador
        if((byte&192)==192) // 192=11000000b
        {
            cont=byte & 63;  // 63=00111111b
            byte=fgetc(f);
            for(; cont>0; cont--)
            {
                where[((off_y+y)<<8)+((off_y+y)<<6)+(off_x+x)]=byte;
                x++;
                if(x==ancho)  // Si ya terminamos la línea
                {
                    x=0;           // Volvemos a empezar en la línea siguiente
                    y++;
                }
                // Si ya terminamos todas las líneas de la imagen
                if(y==alto) listo=1;
            }
        }
         // Si los 2 bits mas significativos no están activados, entonces es un byte de datos
        else
        {
            where[((off_y+y)<<8)+((off_y+y)<<6)+(off_x+x)]=byte;
            x++;
            if(x==ancho)      // Si ya terminamos la línea
            {
                x=0;               // Volvemos a empezar en la línea siguiente
                y++;
            }
            // Si ya terminamos todas las líneas de la imagen
            if(y==alto) listo=1;
        }
    }

    fgetc(f); // Se lee el byte de separación de valor 12d

    for(cont=0; cont<768; cont++)
        paleta[cont]=fgetc(f)>>2; // dividimos por 4
    fclose(f);

    return OK;
}

Como ven la función necesita los siguientes parámetros: el nombre del archivo PCX, las coordenadas donde comenzara a dibujarse la imagen (esquina superior izquierda), comúnmente en (0,0), una variable para guardar la paleta y por último la pantalla virtual donde guardar la imagen.

Ahora veremos una modificación de la funcion anterior. La ventaja de esta función, es que ahora no desperdiciaremos memoria, si la imagen por ejemplo es de 15x15 pixeles. La otra ventaja es que se guarda el ancho y el alto de la imagen, así podremos usar esta función para cargar sprites y luego para colocarlos en pantalla utilizaremos la funcion PutSprite().
 

int CargaPcx(char *filename, t_paleta paleta, unsigned char **where)
{
    enum{ARCHIVO_NO_ENCONTRADO=0, NO_ES_ZSOFT=-1, MUY_GRANDE=-2,
              NO_MEMORIA=-3, OK=1};

    unsigned int i, cont, total, ancho, alto;
    unsigned char byte;
    PcxHead head;
    FILE *f;

    if((f=fopen(filename, "rb"))==NULL)  return ARCHIVO_NO_ENCONTRADO;

    fread(&head, sizeof(char), 128, f);

    if(head.Fabricante!=0x0A)
    {
        fclose(f);
        return NO_ES_ZSOFT;
    }

    ancho=(head.xmax-head.xmin)+1;
    alto=(head.ymax-head.ymin)+1;

    if((ancho>320) || (alto>200))
    {
        fclose(f);
        return MUY_GRANDE;
    }

    if((*where=(BYTE*)(malloc((ancho*alto)+4)))==NULL)
        return NO_MEMORIA;

    memcpy(*where, &ancho, 2);
    memcpy(*where+2, &alto, 2);

    total=ancho*alto;

    for(i=0; i<total;)
    {
        byte=fgetc(f);
        if((byte&192)==192)
        {
            cont=byte & 63;
            byte=fgetc(f);
            for(; cont>0; cont--)
            {
                *(*where+i+4)=byte;
                i++;
            }
        }
        else
        {
            *(*where+i+4)=byte;
            i++;
        }
    }

    fgetc(f);

    for(cont=0; cont<768; cont++)
    paleta[cont]=fgetc(f)>>2;
    fclose(f);

   return OK;
}



Ejemplos
 

Librería gráfica: vgalib.h
 
 


 
 
Bajar todos los ejemplos Bajar todos los ejemplos (161 Kb)


Volver a formatos gráficos
1
Hosted by www.Geocities.ws

1