1 /***********************************************************
2 * Artsoft Retro-Game Library *
3 *----------------------------------------------------------*
4 * (c) 1994-2002 Artsoft Entertainment *
6 * Detmolder Strasse 189 *
9 * e-mail: info@artsoft.org *
10 *----------------------------------------------------------*
12 ***********************************************************/
24 #define PCX_MAGIC 0x0a /* first byte in a PCX image file */
25 #define PCX_SUPPORTED_VERSION 5 /* last acceptable version number */
26 #define PCX_ENCODING 1 /* PCX encoding method */
27 #define PCX_256COLORS_MAGIC 0x0c /* first byte of a PCX 256 color map */
28 #define PCX_MAXCOLORS 256 /* maximum number of colors */
30 #define PCX_HEADER_SIZE 128
31 #define PCX_COLORMAP_SIZE (3 * PCX_MAXCOLORS)
35 unsigned char signature; /* PCX file identifier */
36 unsigned char version; /* version compatibility level */
37 unsigned char encoding; /* encoding method */
38 unsigned char bits_per_pixel; /* bits per pixel (not depth!) */
39 unsigned short xmin; /* X position of left edge */
40 unsigned short ymin; /* Y position of top edge */
41 unsigned short xmax; /* X position of right edge */
42 unsigned short ymax; /* Y position of bottom edge */
43 unsigned short hres; /* X screen resolution of source image */
44 unsigned short vres; /* Y screen resolution of source image */
45 unsigned char palette[16][3]; /* PCX color map */
46 unsigned char reserved; /* should be 0, 1 if std res fax */
47 unsigned char color_planes; /* "color planes" in image */
48 unsigned short bytes_per_line;/* byte delta between scanlines */
49 unsigned short palette_type; /* 0 = undef, 1 = color, 2 = grayscale */
50 unsigned char filler[58]; /* fill to struct size of 128 */
53 /* global PCX error value */
54 int errno_pcx = PCX_Success;
57 static byte *PCX_ReadBitmap(Image *image, byte *buffer_ptr, byte *buffer_last)
59 /* Run Length Encoding: If the two high bits are set,
60 * then the low 6 bits contain a repeat count, and the byte to
61 * repeat is the next byte in the file. If the two high bits are
62 * not set, then this is the byte to write.
65 unsigned int bytes_per_pixel = (image->depth + 7) / 8;
66 register byte *bitmap_ptr, *bitmap_last;
67 register byte value, count;
69 bitmap_ptr = image->data;
70 bitmap_last = bitmap_ptr + (image->width * image->height * bytes_per_pixel);
72 while (bitmap_ptr < bitmap_last && buffer_ptr < buffer_last)
74 value = *buffer_ptr++;
76 if ((value & 0xc0) == 0xc0) /* this is a repeat count byte */
78 count = value & 0x3f; /* extract repeat count from byte */
79 value = *buffer_ptr++; /* next byte is value to repeat */
81 for (; count && bitmap_ptr < bitmap_last; count--)
82 *bitmap_ptr++ = value;
84 if (count) /* repeat count spans end of bitmap */
88 *bitmap_ptr++ = value;
90 image->rgb.color_used[value] = TRUE;
93 /* check if end of buffer was reached before end of bitmap */
94 if (bitmap_ptr < bitmap_last)
97 /* return current buffer position for next decoding function */
102 static boolean PCX_ReadBitmap(FILE *file, struct PCX_Header *pcx, Image *image)
104 int width = image->width;
105 int height = image->height;
106 int pcx_depth = pcx->bits_per_pixel * pcx->color_planes;
107 int bytes_per_row = pcx->color_planes * pcx->bytes_per_line;
108 byte *row_buffer = checked_malloc(bytes_per_row);
109 byte *bitmap_ptr = image->data;
112 for (y = 0; y < height; y++)
114 /* decode a scan line into a temporary buffer first */
115 byte *dst_ptr = (pcx_depth == 8 ? bitmap_ptr : row_buffer);
116 byte value = 0, count = 0;
120 for (i = 0; i < bytes_per_row; i++)
124 if ((value_int = fgetc(file)) == EOF)
130 value = (byte)value_int;
132 if ((value & 0xc0) == 0xc0) /* this is a repeat count byte */
134 count = value & 0x3f; /* extract repeat count from byte */
136 if ((value_int = fgetc(file)) == EOF)
142 value = (byte)value_int;
152 image->rgb.color_used[value] = TRUE;
155 if (pcx_depth <= 4) /* expand planes to 1 byte/pixel */
157 byte *src_ptr = row_buffer;
160 for (plane = 0; plane < pcx->color_planes; plane++)
164 for (i = 0; i < pcx->bytes_per_line; i++)
166 byte value = *src_ptr++;
168 for (j = 7; j >= 0; j--)
170 byte bit = (value >> j) & 1;
172 if (i * 8 + j >= width) /* skip padding bits */
175 bitmap_ptr[x++] |= bit << plane;
180 else if (pcx_depth == 24) /* de-interlace planes */
182 byte *src_ptr = row_buffer;
185 for (plane = 0; plane < pcx->color_planes; plane++)
189 dst_ptr = bitmap_ptr + plane;
190 for (x = 0; x < width; x++)
192 *dst_ptr = *src_ptr++;
193 dst_ptr += pcx->color_planes;
198 bitmap_ptr += image->bytes_per_row;
207 static byte *PCX_ReadColormap(Image *image,byte *buffer_ptr, byte *buffer_last)
211 /* read colormap magic byte */
212 magic = *buffer_ptr++;
214 /* check magic colormap header byte */
215 if (magic != PCX_256COLORS_MAGIC)
218 /* check if enough bytes left for a complete colormap */
219 if (buffer_ptr + PCX_COLORMAP_SIZE > buffer_last)
222 /* read 256 colors from PCX colormap */
223 for (i = 0; i < PCX_MAXCOLORS; i++)
225 image->rgb.red[i] = *buffer_ptr++ << 8;
226 image->rgb.green[i] = *buffer_ptr++ << 8;
227 image->rgb.blue[i] = *buffer_ptr++ << 8;
230 /* return current buffer position for next decoding function */
235 static boolean PCX_ReadColormap(FILE *file,struct PCX_Header *pcx,Image *image)
237 int pcx_depth = pcx->bits_per_pixel * pcx->color_planes;
238 int num_colors = (1 << pcx_depth);
241 if (image->depth != 8)
249 /* look for a 256-colour palette */
252 if ((value_int = fgetc(file)) == EOF)
254 value = (byte)value_int;
256 while (value != PCX_256COLORS_MAGIC);
258 /* read 256 colors from PCX colormap */
259 for (i = 0; i < PCX_MAXCOLORS; i++)
261 image->rgb.red[i] = (byte)fgetc(file) << 8;
262 image->rgb.green[i] = (byte)fgetc(file) << 8;
263 image->rgb.blue[i] = (byte)fgetc(file) << 8;
268 for (i = 0; i < num_colors; i++)
270 image->rgb.red[i] = pcx->palette[i][0] << 8;
271 image->rgb.green[i] = pcx->palette[i][1] << 8;
272 image->rgb.blue[i] = pcx->palette[i][2] << 8;
279 Image *Read_PCX_to_Image(char *filename)
282 byte header_buffer[PCX_HEADER_SIZE];
283 struct PCX_Header pcx;
285 int width, height, depth, pcx_depth;
288 errno_pcx = PCX_Success;
290 if (!(file = fopen(filename, MODE_READ)))
292 errno_pcx = PCX_OpenFailed;
296 if (fread(header_buffer, 1, PCX_HEADER_SIZE, file) != PCX_HEADER_SIZE)
300 errno_pcx = PCX_ReadFailed;
304 pcx.signature = header_buffer[0];
305 pcx.version = header_buffer[1];
306 pcx.encoding = header_buffer[2];
307 pcx.bits_per_pixel = header_buffer[3];
308 pcx.xmin = (header_buffer[5] << 8) | header_buffer[4];
309 pcx.ymin = (header_buffer[7] << 8) | header_buffer[6];
310 pcx.xmax = (header_buffer[9] << 8) | header_buffer[8];
311 pcx.ymax = (header_buffer[11] << 8) | header_buffer[10];
312 pcx.color_planes = header_buffer[65];
313 pcx.bytes_per_line = (header_buffer[67] << 8) | header_buffer[66];
314 pcx.palette_type = (header_buffer[69] << 8) | header_buffer[68];
316 for (i = 0; i < 48; i++)
317 pcx.palette[i / 3][i % 3] = header_buffer[16 + i];
319 width = pcx.xmax - pcx.xmin + 1;
320 height = pcx.ymax - pcx.ymin + 1;
321 pcx_depth = pcx.bits_per_pixel * pcx.color_planes;
322 depth = ((pcx_depth + 7) / 8) * 8;
324 if (pcx.signature != PCX_MAGIC ||
325 pcx.version != PCX_SUPPORTED_VERSION ||
326 pcx.encoding != PCX_ENCODING ||
327 width < 0 || height < 0)
331 errno_pcx = PCX_FileInvalid;
339 printf("%s is a %dx%d PC Paintbrush image\n", filename, width, height);
340 printf("depth: %d\n", depth);
341 printf("bits_per_pixel: %d\n", pcx.bits_per_pixel);
342 printf("color_planes: %d\n", pcx.color_planes);
343 printf("bytes_per_line: %d\n", pcx.bytes_per_line);
344 printf("palette type: %s\n",
345 (pcx.palette_type == 1 ? "color" :
346 pcx.palette_type == 2 ? "grayscale" : "undefined"));
350 /* allocate new image structure */
351 image = newImage(width, height, depth);
353 /* read compressed bitmap data */
354 if (!PCX_ReadBitmap(file, &pcx, image))
359 errno_pcx = PCX_FileInvalid;
363 /* read colormap data */
364 if (!PCX_ReadColormap(file, &pcx, image))
369 errno_pcx = PCX_ColorFailed;
377 /* determine number of used colormap entries for 8-bit PCX images */
378 for (i = 0; i < PCX_MAXCOLORS; i++)
379 if (image->rgb.color_used[i])
385 printf("Read_PCX_to_Image: %d colors in colormap\n", image->rgb.used);
391 #endif /* !TARGET_SDL */