Das PCX-Format

Referat von Oliver Pola

Quelle: R. Steinbrecher: "Bildverarbeitung in der Praxis", Oldenbourg, Seite 273ff

Das PCX-Format wurde entwickelt von der Firma Zsoft-Corporation, USA. Es existieren die Versionen 0, 2, 3, 4 und 5. Version 0 war nur für zwei oder vier Farben geeignet, Version 2 unterstützte 16 Farben. Die aktuelle Version 5 verarbeitet auch 256 Farben aus 16,7 Millionen oder Echtfarben (24 Bit).
Die maximale Größe der Bilder im PCX-Format beträgt 65536 x 65536 Pixel. Es wird nur das RGB-Modell unterstützt und die Lauflängen-Kodierung angewendet.

Lauflängen-Kodierung (RLC):
Bei der Lauflängen-Kodierung werden aufeinander folgende gleiche Farbwerte in der Form kodiert, daß nur einmal der Farbwert und die Anzahl der Wiederholungen gespeichert wird.
Kennzeichen für die Lauflängen-Kodierung ist das setzen der beiden höchsten Bits. Die unteren 6 Bit geben dann die Anzahl (max. 63) der Wiederholungen, das folgende Byte den Farbwert an. Die Kodierung erfolgt für maximal eine Zeile.
Das bewirkt das Problem, daß Farbwerte >191 immer Lauflängen-kodiert werden, auch wenn der Farbwert nur einmal auftritt.
 

Header:

Byte   Anzahl    Bezeichner                 Wert / Bedeutung

0      1         Hersteller                 10 = Zsoft
1      1         Versionsnr.                0, 2, 3, 4 oder 5
2      1         Kodierung                  1 = RLC
3      1         Bits pro Pixel             1, 2, 4 oder 8
4      8         Bildgröße                  X_Min, Y_Min, X_Max, Y_Max jeweils 2 Byte
12     2         horiz. Aufl.               in dpi
14     2         vert. Aufl.                in dpi
16     48        Header-Palette             LUT für 16 oder weniger Farben
64     1         reserviert                 0
65     1         Farbebenen                 1-4
66     2         Bytes / Zeile / Farbebene    zur Ermittlung des benötigten Speichers
68     2         Interpretation             1 = Farbe oder s/w, 2 = Grauwerte
70     2         horiz. Bildschirmgröße
72     2         vert. Bildschirmgröße
74     54        Leer                       bis Byte 128

Angaben mit 2 Byte sind Integer-Werte.

Darauf folgen die Bildinformationen mit Bezug auf die Farbpalette oder zuerst Rot-Ebene dann Grün-Ebene dann Blau-Ebene.
Anschließend folgt, wenn benötigt, die 256-Farben-Palette mit 256 Farbtripeln (R,G,B), eingeleitet von einer dezimalen 12.

Die folgenden Algorithmen gehen davon aus, daß Version 5 mit Grauwerten, also 256 Farben, verwendet wird.
 

Algorithmus zur Speicherung

1     unsigned char *bild;          /* Adresse des Bildspeichers (Array) */
2     unsigned int bildzeilen;      /* Anzahl der Zeilen/Spalten des Bildes */
3     unsigned int bildspalten;
4
5     Save_PCX()
6     {
7          int Z_Index, S_Index;            /* PCX-Größenangaben */
8          unsigned char PCXHeader[128];
9          unsigned char LUT[768];          /* Farbpalette */
10         unsigned char Pixel;
11         unsigned char Anzahl;
12         int PCX_fd;                      /* File-Descriptor */
13         int i, j, z, s;
14         long Index;                      /* Adresse des Pixels im Bild */
15
16    ... Datei PCX_fd zum schreiben öffnen ...
17
18         /* PCX-Header erzeugen */
19         S_Index = bildspalten-1;
20         Z_Index = bildzeilen-1;
21
22         PCXHeader[0] = 10;                           /* Zsoft */
23         PCXHeader[1] = 5;                            /* Version 5 */
24         PCXHeader[2] = 1;                            /* RLC */
25         PCXHeader[3] = 8;                            /* Bits pro Pixel und Ebene */
26         PCXHeader[4] = 0;                            /* Xmin Low-Byte */
27         PCXHeader[5] = 0;                            /* Xmin High-Byte */
28         PCXHeader[6] = 0;                            /* Ymin Low-Byte */
29         PCXHeader[7] = 0;                            /* Ymin High-Byte */
30         PCXHeader[8] = S_Index-256*(S_Index/256);    /* Xmax Low-Byte */
31         PCXHeader[9] = S_Index/256;                  /* Xmax High-Byte */
32         PCXHeader[10] = Z_Index-256*(Z_Index/256);   /* Ymax Low-Byte */
33         PCXHeader[11] = Z_Index/256;                 /* Ymax High-Byte */
34         PCXHeader[12] = 0;                           /* X-Auflösung in dpi */
35         PCXHeader[13] = 0;
36         PCXHeader[14] = 0;                           /* Y-Auflösung in dpi */
37         PCXHeader[15] = 0;
38
39         /* Farbtabelle für 16 oder weniger Farben (nicht verwendet)*/
40         for(i=16; i<64; i++) PCXHeader[i] = 0;
41
42         PCXHeader[64] = 0;                           /* reserviert */
43         PCXHeader[65] = 1;                           /* Anzahl der Ebenen */
44         PCXHeader[66] = S_Index+1-256*((S_Index+1)/256);     /* Bytes/Zeile */
45         PCXHeader[67] = (S_Index+1)/256;
46         PCXHeader[68] = 2;                           /* Grauwerte */
47         PCXHeader[69] = 0;
48         PCXHeader[70] = 0;                           /* Auflösung des Bildschirms */
49         PCXHeader[71] = 0;
50         PCXHeader[72] = 0;
51         PCXHeader[73] = 0;
52
53         /* unbenutzte Bytes auf 0 Setzen */
54         for(i=74; i<128; i++) PCXHeader[i] = 0;
55
56         /* Farbtabelle (Graukeil) */
57         for(i=0; i<256; i++)
58             for(j=0; j<3; j++) LUT[i*3+j] = i;
59
60         write(PCX_fd, PCXHeader, 128);
61
62         /* RLC berechnen */
63         for(z=0; z<=Z_Index; z++)
64         {
65             Index = (long)z * (long)bildspalten;
66             s = 0;
67             while(s<=S_Index)
68             {
69                 Pixel = bild[Index+s];      /* erstes Vorkommen der Farbe */
70                 s++;
71                 Anzahl=1;
72                 while((s<=S_Index) && (Pixel==bild[Index+s]) && (Anzahl<63))
73                 {
74                     Anzahl++;
75                     s++;
76                 }
77                 /* Anzahl>1 oder Farbe>=192, dann RLC durchführen */
78                 if((Anzahl>1) || (Pixel>=192))
79                 {
80                     Anzahl = 0xC0 + Anzahl;     /* RLC-Kennung */
81                     write(PCX_fd, Anzahl, 1);
82                     write(PCX_fd, Pixel, 1);
83                 }
84                 else
85                 {
86                     write(PCX_fd, Pixel, 1);    /* einmaliges Pixel mit Grauwert<192 */
87
88                 }
89             }
90         }
91         /* LUT Farbpalette schreiben */
92         Pixel=12;                   /* Kennzeichen f. Bild-Ende */
93         write(PCX_fd, Pixel, 1);
94         write(PCX_fd, LUT, 768);    /* Farbpalette */
95         close(PCX_fd);
96     }
 
 
Algorithmus zum Laden
 
1      unsigned char *bild;              /* Adresse des Bildspeichers (Array) */
2      unsigned int bildzeilen;
3      unsigned int bildspalten;
4
5      Load_PCX()
6      {
7          unsigned char PCXHeader[128];
8          unsigned char Pixel;
9          unsigned char Anzahl;
10         int PCX_fd;                      /* File-Descriptor */
11         int i, j, z, s;
12         long Index;
13
14     ... Datei PCX_fd zum lesen öffnen ...
15
16         /* PCX-Header einlesen und auf Version 5 testen */
17         read(PCX_fd, PCXHeader, 128);
18         if((PCXHeader[0]!=10) || (PCXHeader[1]!=5) ||
19             (PCXHeader[2]!=1) || (PCXHeader[3]!=8))
20         {
21             ShowError("Falsches Datenformat.");
22             return();
23         }
24
25         /* Bildmaße berechnen */
26         bildspalten = 256*(unsigned int)PCXHeader[9]+(unsigned int)PCXHeader[8]+1;
27         bildzeilen = 256*(unsigned int)PCXHeader[11]+(unsigned int)PCXHeader[10]+1;
28
29         /* RLC einlesen */
30         Index = 0;
31         z = 0;
32         while(z<bildzeilen)
33         {
34             s = 0;
35             j = 0;
36             while(s<bildspalten)
37             {
38                 read(PCX_fd, Pixel, 1);
39                 if(Pixel>192)
40                 {
41                     Anzahl = Pixel-192;
42                     read(PCX_fd, Pixel, 1);
43                     for(i=0; i<Anzahl; i++)
44                     {
45                         bild[Index++] = Pixel;
46                         s++;
47                     }
48                 }
49                 else
50                 {
51                     bild[Index++] = Pixel;
52                     s++;
53                 }
54             }
55             z++;
56         }
57         close(PCX_fd);
58
59         /* Einlesen der Farbpalette entfällt, da Grauwerte vorausgesetzt */
60     }