1 /*****************************************************************************/
9 /* (C) 2012, Ullrich von Bassewitz */
10 /* Roemerstrasse 52 */
11 /* D-70794 Filderstadt */
12 /* EMail: uz@cc65.org */
15 /* This software is provided 'as-is', without any expressed or implied */
16 /* warranty. In no event will the authors be held liable for any damages */
17 /* arising from the use of this software. */
19 /* Permission is granted to anyone to use this software for any purpose, */
20 /* including commercial applications, and to alter it and redistribute it */
21 /* freely, subject to the following restrictions: */
23 /* 1. The origin of this software must not be misrepresented; you must not */
24 /* claim that you wrote the original software. If you use this software */
25 /* in a product, an acknowledgment in the product documentation would be */
26 /* appreciated but is not required. */
27 /* 2. Altered source versions must be plainly marked as such, and must not */
28 /* be misrepresented as being the original software. */
29 /* 3. This notice may not be removed or altered from any source */
32 /*****************************************************************************/
51 /*****************************************************************************/
53 /*****************************************************************************/
57 /* Some PCX constants */
58 #define PCX_MAGIC_ID 0x0A
59 #define PCX_MAX_PLANES 4
61 /* A raw PCX header is just a block of bytes */
62 typedef unsigned char RawPCXHeader[128];
64 /* Structured PCX header */
65 typedef struct PCXHeader PCXHeader;
78 unsigned BytesPerPlane;
81 unsigned ScreenHeight;
88 /* Read a little endian word from a byte array at offset O */
89 #define WORD(H, O) ((H)[O] | ((H)[O+1] << 8))
93 /*****************************************************************************/
95 /*****************************************************************************/
99 static PCXHeader* NewPCXHeader (void)
100 /* Allocate a new PCX header and return it */
102 /* No initialization here */
103 return xmalloc (sizeof (PCXHeader));
108 static void FreePCXHeader (PCXHeader* H)
109 /* Free a PCX header structure */
116 static PCXHeader* ReadPCXHeader (FILE* F, const char* Name)
117 /* Read a structured PCX header from the given file and return it */
121 /* Allocate a new PCXHeader structure */
122 PCXHeader* P = NewPCXHeader ();
124 /* Read the raw header */
125 ReadData (F, H, sizeof (H));
127 /* Convert the data into structured form */
129 P->FileVersion = H[1];
130 P->Compressed = H[2];
132 P->XMin = WORD (H, 4);
133 P->YMin = WORD (H, 6);
134 P->XMax = WORD (H, 8);
135 P->YMax = WORD (H, 10);
136 P->XDPI = WORD (H, 12);
137 P->YDPI = WORD (H, 14);
139 P->BytesPerPlane = WORD (H, 66);
140 P->PalInfo = WORD (H, 68);
141 P->ScreenWidth = WORD (H, 70);
142 P->ScreenHeight = WORD (H, 72);
143 P->Width = P->XMax - P->XMin + 1;
144 P->Height = P->YMax - P->YMin + 1;
146 /* Check the header data */
147 if (P->Id != PCX_MAGIC_ID || P->FileVersion == 1 || P->FileVersion > 5) {
148 Error ("`%s' is not a PCX file", Name);
150 if (P->Compressed > 1) {
151 Error ("Unsupported compression (%d) in PCX file `%s'",
152 P->Compressed, Name);
155 * - one plane with either 1 or 8 bits per pixel
156 * - three planes with 8 bits per pixel
157 * - four planes with 8 bits per pixel (does this exist?)
159 if (!((P->BPP == 1 && P->Planes == 1) ||
160 (P->BPP == 8 && (P->Planes == 1 || P->Planes == 3 || P->Planes == 4)))) {
161 /* We could support others, but currently we don't */
162 Error ("Unsupported PCX format: %u planes, %u bpp in PCX file `%s'",
163 P->Planes, P->BPP, Name);
165 if (P->PalInfo != 1 && P->PalInfo != 2) {
166 Error ("Unsupported palette info (%u) in PCX file `%s'",
169 if (!ValidBitmapSize (P->Width, P->Height)) {
170 Error ("PCX file `%s' has an unsupported size (w=%u, h=%d)",
171 Name, P->Width, P->Height);
174 /* Return the structured header */
180 static void DumpPCXHeader (const PCXHeader* P, const char* Name)
181 /* Dump the header of the PCX file in readable form to stdout */
183 printf ("File name: %s\n", Name);
184 printf ("PCX Version: ");
185 switch (P->FileVersion) {
186 case 0: puts ("2.5"); break;
187 case 2: puts ("2.8 with palette"); break;
188 case 3: puts ("2.8 without palette"); break;
189 case 4: puts ("PCX for Windows without palette"); break;
190 case 5: puts ("3.0"); break;
192 printf ("Image type: %s\n", P->PalInfo? "color" : "grayscale");
193 printf ("Compression: %s\n", P->Compressed? "RLE" : "None");
194 printf ("Structure: %u planes of %u bits\n", P->Planes, P->BPP);
195 printf ("Bounding box: [%u/%u - %u/%u]\n", P->XMin, P->YMin, P->XMax, P->YMax);
196 printf ("Resolution: %u/%u DPI\n", P->XDPI, P->YDPI);
197 printf ("Screen size: %u/%u\n", P->ScreenWidth, P->ScreenHeight);
198 printf ("Bytes per plane: %u\n", P->BytesPerPlane);
203 static void ReadPlane (FILE* F, PCXHeader* P, unsigned char* L)
204 /* Read one (possibly compressed) plane from the file */
208 /* Uncompress RLE data */
209 unsigned Remaining = P->Width;
214 /* Read the next byte */
215 unsigned char B = Read8 (F);
217 /* Check for a run length */
218 if ((B & 0xC0) == 0xC0) {
219 C = (B & 0x3F); /* Count */
220 B = Read8 (F); /* Value */
225 /* Write the data to the buffer */
238 /* Just read one line */
239 ReadData (F, L, P->Width);
246 Bitmap* ReadPCXFile (const Collection* A)
247 /* Read a bitmap from a PCX file */
257 /* Get the file name */
258 const char* Name = NeedAttrVal (A, "name", "read pcx file");
261 FILE* F = fopen (Name, "rb");
263 Error ("Cannot open PCX file `%s': %s", Name, strerror (errno));
266 /* Read the PCX header */
267 P = ReadPCXHeader (F, Name);
269 /* Dump the header if requested */
271 DumpPCXHeader (P, Name);
274 /* Create the bitmap */
275 B = NewBitmap (P->Width, P->Height);
277 /* Determine the type of the bitmap */
279 case 1: B->Type = (P->PalInfo? bmIndexed : bmMonochrome); break;
280 case 3: B->Type = bmRGB; break;
281 case 4: B->Type = bmRGBA; break;
282 default:Internal ("Unexpected number of planes");
286 SB_CopyStr (&B->Name, Name);
288 /* Allocate memory for the scan line */
289 L = xmalloc (P->Width);
291 /* Read the pixel data */
293 if (P->Planes == 1) {
295 /* This is either monochrome or indexed */
298 for (Y = 0, Px = B->Data; Y < P->Height; ++Y) {
307 for (X = 0, I = 0, Mask = 0x01; X < P->Width; ++Px) {
308 Px->Index = (L[I] & Mask) != 0;
319 /* One plane with 8bpp is indexed */
320 for (Y = 0, Px = B->Data; Y < P->Height; ++Y) {
326 for (X = 0; X < P->Width; ++X, ++Px) {
335 /* 3 or 4 planes are RGB or RGBA (don't know if this exists) */
336 for (Y = 0, Px = B->Data; Y < P->Height; ++Y) {
338 /* Read the R plane and move the data */
340 for (X = 0; X < P->Width; ++X, ++Px) {
344 /* Read the G plane and move the data */
346 for (X = 0; X < P->Width; ++X, ++Px) {
350 /* Read the B plane and move the data */
352 for (X = 0; X < P->Width; ++X, ++Px) {
356 /* Either read the A plane or clear it */
357 if (P->Planes == 4) {
359 for (X = 0; X < P->Width; ++X, ++Px) {
363 for (X = 0; X < P->Width; ++X, ++Px) {
370 /* One plane means we have a palette which is either part of the header
373 if (B->Type == bmMonochrome) {
375 /* Create the monochrome palette */
376 B->Pal = NewMonochromePalette ();
378 } else if (B->Type == bmIndexed) {
382 unsigned char Palette[256][3];
383 unsigned long EndPos;
385 /* Determine the current file position */
386 unsigned long CurPos = FileGetPos (F);
388 /* Seek to the end of the file */
389 (void) fseek (F, 0, SEEK_END);
391 /* Get this position */
392 EndPos = FileGetPos (F);
394 /* There's a palette if the old location is 769 bytes from the end */
395 if (EndPos - CurPos == sizeof (Palette) + 1) {
398 FileSetPos (F, CurPos);
400 /* Check for palette marker */
401 if (Read8 (F) != 0x0C) {
402 Error ("Invalid palette marker in PCX file `%s'", Name);
405 } else if (EndPos == CurPos) {
407 /* The palette is in the header */
410 /* Check the maximum index for safety */
412 Error ("PCX file `%s' contains more than 16 indexed colors "
413 "but no extra palette", Name);
417 Error ("Error in PCX file `%s': %lu bytes at end of pixel data",
418 Name, EndPos - CurPos);
421 /* Read the palette. We will just read what we need. */
423 ReadData (F, Palette, Count * sizeof (Palette[0]));
425 /* Create the palette from the data */
426 B->Pal = NewPalette (Count);
427 for (I = 0; I < Count; ++I) {
428 B->Pal->Entries[I].R = Palette[I][0];
429 B->Pal->Entries[I].G = Palette[I][1];
430 B->Pal->Entries[I].B = Palette[I][2];
431 B->Pal->Entries[I].A = 0;
439 /* Free the PCX header */
442 /* Return the bitmap */