FORMATO PCX

O formato PCX foi uma das primeiras tentativas da indústria de PCs em produzir uma padrão para troca e armazenamento de imagens. Um formato padrão de imagens era necessário para atender às necessidades de troca de informação entre os aplicativos em desenvolvimento na época.

O formato PCX é um exemplo clássico de um padrão de fato criado a partir de uma iniciativa da indústria. Por ter sido criado a tanto tempo e ser ainda um padrão estável e de fato, a imensa maioria dos aplicativos gráficos atuais suporta a importação e exportação de arquivos neste formato, sendo provavelmente mais suportado do que qualquer outro formato de armazenamento atual.

O formato PCX é bastante flexível quanto ao tipo de informação que pode conter e esta talvez tenha sido o característica que lhe garantiu a sobrevida. Os arquivos PCX possuem um cabeçalho de tamanho fixo (128 bytes) , seguido de um número variável de bytes contendo dos valores dos pixels que compõem a imagem. Ao final do arquivo pode existir uma estrutura destinada a abrigar informações relativas à paleta em uso.

 

CABEÇALHOS DOS ARQUIVOS PCX

 

A melhor maneira de se entender o formato dos arquivos PCX é descrevendo o conteúdo de seu cabeçalho. Este cabeçalho contém todas as informações necessárias à correta interpretação dos dados da imagem contidos no arquivo. Sem as informações  do cabeçalho o conteúdo do arquivo dificilmente poderá ser decodificado. A declaração da struct PCX_File exemplifica e detalha todas as variáveis necessárias para possibilitar a leitura de um cabeçalho PCX. A struc PCX_File representa completamente o cabeçalho de um arquivo PCX, contendo 128 bytes.

struct PCX_File // Header completa de PCX=128 bytes
    {
    BYTE Header;
    BYTE Version; // 0 = Ver 2.5
    BYTE Encode; // Flag para compressão RLL presente (1=TRUE)
    BYTE BitPerPix; // Número de Bits por pixel
    WORD X1; // geralmente = 0)
    WORD Y1; // Posição Y do pixel [0,0] na tela  (geralmente = 0)
    WORD X2; // Tamanho horizontal da imagem
    WORD Y2; // Tamanho vertical da imagem
    WORD Hres; // Resolução Horizontal recomendável para display da imagem
    WORD Vres; // Resolução Vertical recomendável para display da imagem
    PCXCOLOR EGAPalette[16]; // Palette de cores de 4 bits (obsoleta)
    BYTE VideoMode; // Video Mode (obsoleto)
    BYTE ColorPlanes; // Planos de cores (8 bits = 1) (obsoleto)
    WORD BytesPerLine; // BytesPerLine (para expansão RLL)
    BYTE Unused[60]; // Espaço livre disponível para novas versões
    };

A struc PCXCOLOR representa a estrutura de arquivamento das cores da paleta EGA e VGA estendida, contidas no cabeçalho ou no final do arquivo PCX respectivamente. Cada um dos campos do cabeçalho do arquivo PCX contém algum tipo de informação relevante à decodificação de seu conteúdo. Atualmente alguns destes campos caíram em desuso em virtude dos novos ambientes operacionais ou pela evolução tecnológica do hardware da controladora de vídeo de um PC típico.  
struct PCXCOLOR
    {
    BYTE Red;
    BYTE Green;
    BYTE Blue;
    };

 

Sendo o PCX um padrão de fato e não regulamentado por nenhuma Instituição, alguns fabricantes optaram por utilizar alguns campos obsoletos ou até mesmo a área livre de 60 bytes ao final do cabeçalho para armazenar informações proprietárias e exclusivas de seus aplicativos. Isto significa que podem existir arquivos PCX que não poderão ser decodificados com as rotinas apresentadas neste capítulo, no entanto a prática tem demonstrado que o padrão atingiu um elevado grau de maturidade e que casos como este constituem a absoluta minoria.

O campo "BYTE Header" da struct PCX_File deve sempre conter o valor "10" (dez) decimal para identificar o arquivo como sendo um PCX válido. A maioria dos programas de decodificação de arquivos PCX irá rejeitar a abertura do arquivo caso este campo não contenha este valor.

O campo "BYTE Version" da struct PCX_File contém o número da versão utilizada para codificar o arquivo. O valor 0 (zero) indica que a versão é a 2.5 e o valor 2 indica versão 2.8 com paleta estendida, valor 3 indica versão 3.0 sem paleta e o valor 5 a versão 3.0 com paleta.

O campo "BYTE Encode" da struct PCX_File contém a informação sobre o tipo de compressão utilizada na área de imagem. Se a imagem estiver comprimida no esquema RLL o valor deste campo será sempre diferente de 0 (zero).

Os campos "BYTE BitPerPix" e "BYTE ColorPlanes" da struct PCX_File atuam em conjunto para determinar o tipo de codificação de cores utilizado no arquivo. As antigas placas EGA e VGA possuíam um intrincado esquema de formação de cores baseado em planos, assim uma placa EGA operando em 16 cores seria capaz de apresentar imagens codificadas em 1 bit por pixel em 4 planos. Com o advento das interfaces gráficas, e com o melhoramento do hardware da controladora de vídeo estes modos de baixa qualidade caíram em desuso e os arquivos atuais conterão quase sempre 8,16 ou 24 BitPerPixel e 1 plano de visualização.

  Os campos "WORD X1" e "WORD Y1" da struct PCX_File sempre serão de valor 0 (zero), a origem destes campos remonta ao formato PCC, ora em desuso, que permitia a localização de imagens parciais em áreas específicas da tela. Os arquivos PCX eram inicialmente destinados a preencher completamente a área disponível na tela. Seu propósito inicial no entanto foi modificado e estes campos hoje não contém mais nenhuma informação relevante para a decodificação ou apresentação da imagem.   Os campos "WORD X2" e "WORD Y2" da struct PCX_File contém o tamanho em pixels da imagem contida no arquivos.   Os campos "WORD Hres" e "WORD Vres" da struct PCX_File foram criados em função das diversas relações de altura x largura disponíveis nas controladoras de vídeo do início dos anos 80. As placas CGA, EGA e Hércules eram capazes de apresentar resoluções com pixeis NÃO quadrados, isto é, não havia uma relação direta entre o número de pixeis horizontais/verticais e a largura/altura do monitor de vídeo. Estas características do hardware podiam provocar distorções num dos eixos da imagem fazendo, por exemplo, que círculos fossem apresentados de forma oval e vice versa. O conteúdo destes campos hoje em dia é irrelevante, porém "por tradição" costuma-se preenchê-los com os valores 75 x 75 dpi, representando a densidade de pontos por polegada de uma placa VGA, operando no modo 640 x 480, num monitor típico de 14 polegadas.

Pelos mesmo motivos já expostos os campos "
BYTE VideoMode" e "WORD BytesPerLine" da struct PCX_File tornaram-se obsoletos e continham respectivamente o modo padrão de operação da placa de vídeo bem como o número de bytes por linha descomprimida. Atualmente o número de colunas da imagem é informado pelo campo "WORD X2", no entanto "por tradição" a maioria do aplicativos repete este valor no campo "BytesPerLine".

 

PALETTE DE CODIFICAÇÃO DE CORES

Finalmente existe ainda a "struct PCXCOLOR" destinada a conter informações  relativas à codificação de cores a ser utilizada para apresentar a imagem contida no arquivo. Esta estrutura deve estar presente e conter informações válidas para imagens de 16 cores (no cabeçalho) e para imagens de 256 cores (no final do arquivo). Para imagens de 16,777,216 (24 bits) cores as informações contidas nesta estrutura são irrelevantes.

O campo "PCXCOLOR EGAPalette[16]" da struct PCX_File contém as informações de cor para imagens de 16 cores. As cores são definidas pela combinação de valores RGB que podem variar de intensidade 0 (zero) a 63. As antiga placas de vídeo possuíam um conversor AD de apenas 6 bits, por isso a variação de intensidade e limitada a 63 ao invés do 255 níveis por cor atualmente em uso. A leitura dos 48 bytes do cabeçalho carrega a tabela de cores relativas aos pixels da imagens, assim sendo uma tabela que contenha os seguintes valores:

  struct PCXCOLOR
    {

BYTE Red;

BYTE Green; BYTE Blue;
63 0 0
0 63 0
0 0 63
28 32 14
... ... ...
    };

Indica ao aplicativo que os pixel de imagem de valor 0 (zero) devem ser apresentados na tela com a cor vermelha, os de valor 1 (um) deve ser apresentados em verde, os de valor 3 em azul, e assim sucessivamente para todos os 16 valores da tabela de cores EGA.

O mesmo raciocínio é válido para a paleta estendida, localizada ao final do arquivo e que contém o mesmo tipo de informação para imagens codificadas em 8 bits (256 cores). A paleta estendida inicia-se com uma tag de cheque, de valor decimal 12, indicando a presença de uma paleta estendida válida logo ao final da imagem. Seguidamente à esta tag encantam-se 768 bytes relativos às 256 cores (RGB) de cada um dos bytes da imagem. Diferentemente da paleta EGA, a paleta estendida é codificada em valores de 0 a 255 para intensidade de cores, compatíveis portanto com as capacidades dos atuais conversores DA das placas de vídeo atuais.

 

COMPRESSÃO RLE (RUN LENGTH ENCODED)

A necessidade de compressão se apresenta como óbvia quando tratamos com imagens.

O formato PCX incorpora um padrão simples de compressão (RLE) que ,tipicamente, é capaz de reduzir o tamanho dos arquivos de imagem em  50%. A compressão RLE é normalmente bastante eficiente, sua performance porém é altamente dependente do tipo da imagem podendo, em casos extremos, aumentar o tamanho teórico do arquivo. A prática  porém demonstra que isto raramente acontece. Esquemas mais complexos de compressão como o LZW foram incorporados a formatos de armazenamento mais recentes como o TIFF, porém com maior custo computacional e algoritmos de compressão e descompressão mais complexos.

Na compressão RLE cada linha da imagem é comprimida separadamente. O formato PCX acomoda a representação de imagens por planos de cor, isto é, codifica a imagem colorida em três imagens separadas e distintas para a componente R, G e B. Quando a imagem está armazenada em possui múltiplos planos a compressão RLE se dará para cada linha de cada plano separadamente. O formato PCX suporta até 4 planos, a saber: BGRI, respectivamente Blue, Green, Red e Intensity. Se a imagem possui apenas 1 plano então os dados da imagem referem-se ao índice de cores da paleta estendida ao final do arquivo pois só pode ser de 256 cores (8 bits).  

No esquema RLE o valor original de um pixel é substituído por um código de repetição, o valor a ser repetido, e o número de repetições. Assim se uma imagem possui grandes áreas contíguas, com a mesma tonalidade, o esquema de compressão RLE trará bons resultados. Por outro lado, se a imagem possui poucas áreas de tonalidade igual, o esquema RLE será pouco eficiente. O sinalizador de repetição e o contador de repetição são implementados num único byte. Os dois bits mais significativos são setados em 1 e os restantes 6 bit indicam o número de repetições no intervalo de 1 a 63. Se um bit de dado possui seus dois bit mais significativos setados (> 192) então obrigatoriamente ele será codificado como RLE mesmo que possua apenas uma repetição.  A seqüência abaixo ilustra este esquema de compressão e seus resultados.

 

Tabela - Exemplos de descompressão RLE

RLL

RLL binário Expandido
197 200 11000101 11001000 200 200 200 200 200
194 56 11000010 00111000 56 56
void 120 void 01111000 120
193 289 11000001 100100001 289
201 12 11001001 00001100 12 12 12 12 12 12 12 12 12

O algoritmo abaixo ilustra simplificadamente o esquema de descompressão RLE numa imagem de 8 bits e um plano de cor.

// Para cada linha de dados
for(y=0; y<PFile->TotY; y++)
    {
    InCount = 0; // Inicializa o contador de bytes por linha
    do
        {
        // Lê 1 byte do arquivo em disco

        fread(&CharReaded,1,1,TheFile);

        // Verifica existência da TAG RLL (192 = C0 hexa)
        if((CharReaded & 0xC0) == 0xC0)

            {
            RepCount = CharReaded & ~0xC0; // Extrai o valor de repetição
            fread(&CharReaded,1,1,TheFile); // Lê o byte a ser repetido

            // Carrega o buffer de linha com o número necessário de repetições
            while(RepCount--)
                Buf[InCount++] = CharReaded;
            }
        else // Se não encontrou a TAG RLL

            // Simplesmente copia o valor do pixel para o buffer de linha
            Buf[InCount++] = CharReaded;
        }while(InCount < BytesPerLine);

        // Repete o loop até carregar toda a linha

    // Copia para Buffer de linha para o BitMap de memória do Windows
    SetBitmapBits(hBuf,PFile->Horz,Buf+PFile->OrgX);
    BitBlt(hdcBmp,0,YDst,PFile->Horz,1,hdcBuf,0,0,SRCCOPY);
    YDst++;
    } // Fim da carga dos bits

//Carga da palette estendida
// Lê o TAG de palette estendida
fread(&Color256Palette,sizeof(Color256Palette),1,TheFile);

 

// Valida o TAG de palette estendida

if(Color256Palette.ExtendedPalette!=PCX256COLORTAG)
    GetApplication()->ExecDialog(new TMesDlg(this,"MES_EXC","","Marcador de Paleta Extendida","inválido"));

// Carrega a palette extendida
for(i=0; i<256; i++)

    {
    PFile->Pe[i].peRed =Color256Palette.Palette[i].Red;
    PFile->Pe[i].peGreen=Color256Palette.Palette[i].Green;
    PFile->Pe[i].peBlue =Color256Palette.Palette[i].Blue;
    }
fclose(TheFile); // Fecha o arquivo de leitura
}