Budowa pliku PCX


Strona główna | Autor | Borland C++ 5.5 | GUI | Książki | OpenGL | Programy | Projekty | Teksty


Opis

Plik PCX został opracowany w 1982 roku przez firmę ZSoft Corporation - twórcę programu graficznego Paintbrush. Format pliku był wielokrotnie ulepszany i wzbogacany o nowe możliwości. Zaletą formatu PCX jest bardzo duża popularność, prosta budowa i prosty algorytm kompresji RLE. Algorytm ten wystarczająco skuteczny dla wielu klas obrazów.

Plik PCX posiada 128 bajtowy nagłówek o następującej budowie:

Nazwa pola Długość w bajtach Opis
Manufacturer 1 wartość stała równa 10
Version 1 pole zawiera informację o wersji pliku PCX:
  1. plik obsługiwany przez wersję 2.5 programu PC Paintbrush,
  1. plik obsługiwany przez wersję 2.8 programu PC Paintbrush, zawierający informacje o palecie,
  1. plik obsługiwany przez wersję 2.8 programu PC Paintbrush, nie zawierający informacji o palecie,
  1. plik obsługiwany przez program PC Paintbrush for Windows,
  1. plik obsługiwany przez wersję 3.0 i nowsze programu PC Paintbrush i PC Paintbrush+, zawiera obsługę rysunków 24-bitowych.
Encoding 1 wartość 1 oznacza kodowanie rysunku metodą RLE, dowolna inna brak kodowania
BitsPerPixel 1 ilość bitów reprezentujących jeden piksel dla każdej płaszczyzny (ang. Plane) rysunku: 1, 2, 4 lub 8
Window 4*2 wymiary rysunku: Xmin, Ymin, Xmax, Ymax, szerokość i wysokość rysunku oblicza się następująco: Xmax - Xmin + 1 i Ymax - Ymin + 1
HDpi 2 pozioma rozdzielczość rysunku w DPI
VDpi 2 pionowa rozdzielczość rysunku w DPI
Colormap 48 informacja o 16-kolorowej palecie EGA/VGA, 16 kolorów opisanych po 3 bajty: czerwony (Red), zielony (Green) i niebieski (Blue)
Reserved 1 pole zarezerwowane, wartość 0
NPlanes 1 ilość płaszczyzn (ang. planes) w rysunku, maksymalnie mogą być 4 płaszczyzny: R, G, B, I (Intensity), aby obliczyć ilość bajtów potrzebnych do dekompresji jednego wiersza rysunku trzeba pomnożyć NPlanes * BytesPerLine
BytesPerLine 2 ilość bajtów potrzebnych do dekompresji pojedynczej płaszczyzny wiersza rysunku, uwaga! ta wartość może być większa niż wyliczona z różnicy: Xmax - Xmin
PaletteInfo 2 sposób interpretacji palety: 1 - kolorowa lub czarno biała, 2 - odcienie szarości, pole ignorowane przez program PC Paintbrush IV/IV Plus
HscreenSize 2 poziomy rozmiar rysunku w pikselach, pole zapisywane przez program PC Paintbrush IV/IV Plus
VscreenSize 2 pionowy rozmiar rysunku w pikselach, pole zapisywane przez program PC Paintbrush IV/IV Plus
Filler 54 pole wypełnione zerami do końca nagłówka

Przy odczycie pliku PCCX trzeba określić wymiary rysunku w pikselach. Wymiary te określa się w następujący sposób:
szerokość = Xmax - Xmin + 1
wysokość = Ymax - Ymin + 1
Następnie trzeba ustalić jaka ilość bajtów jest potrzebna do odczytu jednej linii rysunku, tj: NPlanes * BytesPerLine.

Jeżeli rysunek ma 16 kolorów, lub mniej wówczas informację o palecie zawiera pole Colormap. W przypadku rysunku 256-kolorowego (pole Version = 5), paleta umieszczona jest na końcu pliku. Dane opisujące punkty obrazu oddziela od 768 - bitowej palety kolorów jeden bajt o wartości 12. Paleta zawiera opis 256 kolorów w układzie RGB, każdy kolor opisuje jeden bajt.

Rysunek w formacie 24-bitowym, to pole Version = 5, BitsPerPixel = 8 i NPlanes = 3. Poszczególne płaszczyzny rysunku zapisane są w następującej kolejności: RRR..., GGG..., BBB..., III... (jeżeli występuje), czyli najpierw opis składowych czerwonych dla poszczególnych pikseli, potem zielonych itd.

W pliku PCX zastosowane prostą metodę kompresji RLE (ang. Run Length Encoding). Oto funkcja w języku C dekodująca pojedynczą linię obrazu (z dokumentacji technicznej pliku PCX):

/* This procedure reads one encoded block from the image file and stores
a count and data byte.
Return result:  0 = valid data stored, EOF = out of data in file */

encget(pbyt, pcnt, fid)
int *pbyt;        /* where to place data */
int *pcnt;        /* where to place count */
FILE *fid;        /* image file handle */
{
int i;
        *pcnt = 1;        /* assume a "run" length of one */
        if (EOF == (i = getc(fid)))
                return (EOF);
        if (0xC0 == (0xC0 & i))
                {
                *pcnt = 0x3F & i;
                if (EOF == (i = getc(fid)))
                        return (EOF);
                }
        *pbyt = i;
        return (0);
}

/* Here's a program fragment using encget.  This reads an entire file and
stores it in a (large) buffer, pointed to by the variable "bufr". "fp" is
the file pointer for the image */

int i;
long l, lsize;
     lsize = (long )hdr.BytesPerLine * hdr.Nplanes * (1 + hdr.Ymax - hdr.Ymin);
     for (l = 0; l < lsize; )             /* increment by cnt below */
                {
                if (EOF == encget(&chr, &cnt, fp))
                        break;
                for (i = 0; i < cnt; i++)
                        *bufr++ = chr;
                l += cnt;
                }

Kolejny przykład (źródło jak wyżej) pokazuje jak zapisać dane do pliku PCX:
/* Subroutine for writing an encoded byte pair (or single byte if it
doesn't encode) to a file. It returns the count of bytes written, 0 if error */

encput(byt, cnt, fid)
unsigned char byt, cnt;
FILE *fid;
{
  if (cnt) {
        if ((cnt == 1) && (0xC0 != (0xC0 & byt)))
                {
                if (EOF == putc((int )byt, fid))
                        return(0);     /* disk write error (probably full) */
                return(1);
                }
        else
                {
                if (EOF == putc((int )0xC0 | cnt, fid))
                        return (0);      /* disk write error */
                if (EOF == putc((int )byt, fid))
                        return (0);      /* disk write error */
                return (2);
                }
        }
   return (0);
}

This subroutine encodes one scanline and writes it to a file.
It returns number of bytes written into outBuff, 0 if failed. */

encLine(inBuff, inLen, fp)
unsigned char *inBuff;    /* pointer to scanline data */
int inLen;                        /* length of raw scanline in bytes */
FILE *fp;                        /* file to be written to */
{
unsigned char this, last;
int srcIndex, i;
register int total;
register unsigned char runCount;     /* max single runlength is 63 */
  total = 0;
  runCount = 1;
  last = *(inBuff);

/* Find the pixel dimensions of the image by calculating
[XSIZE = Xmax - Xmin + 1] and [YSIZE = Ymax - Ymin + 1].
Then calculate how many bytes are in a "run" */

  for (srcIndex = 1; srcIndex < inLen; srcIndex++)
        {
        this = *(++inBuff);
        if (this == last)     /* There is a "run" in the data, encode it */
                {
                runCount++;
                if (runCount == 63)
                        {
                        if (! (i = encput(last, runCount, fp)))
                                return (0);
                        total += i;
                        runCount = 0;
                        }
                }
        else                /* No "run"  -  this != last */
                {
                if (runCount)
                        {
                        if (! (i = encput(last, runCount, fp)))
                                return(0);
                        total += i;
                        }
                last = this;
                runCount = 1;
                }
        }        /* endloop */
  if (runCount)        /* finish up */
        {
        if (! (i = encput(last, runCount, fp)))
                return (0);
        return (total + i);
        }
  return (total);
}


Do pobrania


© Janusz Ganczarski
JanuszG@enter.net.pl