rnd-20030803-2-src
[rocksndiamonds.git] / src / libgame / pcx.c
1 /***********************************************************
2 * Artsoft Retro-Game Library                               *
3 *----------------------------------------------------------*
4 * (c) 1994-2002 Artsoft Entertainment                      *
5 *               Holger Schemel                             *
6 *               Detmolder Strasse 189                      *
7 *               33604 Bielefeld                            *
8 *               Germany                                    *
9 *               e-mail: info@artsoft.org                   *
10 *----------------------------------------------------------*
11 * pcx.c                                                    *
12 ***********************************************************/
13
14 #ifndef TARGET_SDL
15
16 #include <stdio.h>
17
18 #include "pcx.h"
19 #include "misc.h"
20
21
22 #define PCX_DEBUG               0
23
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          */
29
30 #define PCX_HEADER_SIZE         128
31 #define PCX_COLORMAP_SIZE       (3 * PCX_MAXCOLORS)
32
33 struct PCX_Header
34 {
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          */
51 };
52
53 /* global PCX error value */
54 int errno_pcx = PCX_Success;
55
56 #if 0
57 static byte *PCX_ReadBitmap(Image *image, byte *buffer_ptr, byte *buffer_last)
58 {
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.
63    */
64
65   unsigned int bytes_per_pixel = (image->depth + 7) / 8;
66   register byte *bitmap_ptr, *bitmap_last;
67   register byte value, count;
68
69   bitmap_ptr = image->data;
70   bitmap_last = bitmap_ptr + (image->width * image->height * bytes_per_pixel);
71
72   while (bitmap_ptr < bitmap_last && buffer_ptr < buffer_last)
73   {
74     value = *buffer_ptr++;
75
76     if ((value & 0xc0) == 0xc0)         /* this is a repeat count byte */
77     {
78       count = value & 0x3f;             /* extract repeat count from byte */
79       value = *buffer_ptr++;            /* next byte is value to repeat */
80
81       for (; count && bitmap_ptr < bitmap_last; count--)
82         *bitmap_ptr++ = value;
83
84       if (count)                        /* repeat count spans end of bitmap */
85         return NULL;
86     }
87     else
88       *bitmap_ptr++ = value;
89
90     image->rgb.color_used[value] = TRUE;
91   }
92
93   /* check if end of buffer was reached before end of bitmap */
94   if (bitmap_ptr < bitmap_last)
95     return NULL;
96
97   /* return current buffer position for next decoding function */
98   return buffer_ptr;
99 }
100 #endif
101
102 static boolean PCX_ReadBitmap(FILE *file, struct PCX_Header *pcx, Image *image)
103 {
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;
110   int y;
111
112   for (y = 0; y < height; y++)
113   {
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;
117     int value_int;
118     int i;
119
120     for (i = 0; i < bytes_per_row; i++)
121     {
122       if (count == 0)
123       {
124         if ((value_int = fgetc(file)) == EOF)
125         {
126           free(row_buffer);
127           return FALSE;
128         }
129
130         value = (byte)value_int;
131
132         if ((value & 0xc0) == 0xc0)     /* this is a repeat count byte */
133         {
134           count = value & 0x3f;         /* extract repeat count from byte */
135
136           if ((value_int = fgetc(file)) == EOF)
137           {
138             free(row_buffer);
139             return FALSE;
140           }
141
142           value = (byte)value_int;
143         }
144         else
145           count = 1;
146       }
147
148       dst_ptr[i] = value;
149       count--;
150
151       if (pcx_depth == 8)
152         image->rgb.color_used[value] = TRUE;
153     }
154
155     if (pcx_depth <= 4)                 /* expand planes to 1 byte/pixel */
156     {
157       byte *src_ptr = row_buffer;
158       int plane;
159
160       for (plane = 0; plane < pcx->color_planes; plane++)
161       {
162         int i, j, x = 0;
163
164         for (i = 0; i < pcx->bytes_per_line; i++)
165         {
166           byte value = *src_ptr++;
167
168           for (j = 7; j >= 0; j--)
169           {
170             byte bit;
171
172             if (i * 8 + j >= width)     /* skip padding bits */
173               continue;
174
175             bit = (value >> j) & 1;
176             bitmap_ptr[x++] |= bit << plane;
177           }
178         }
179       }
180     }
181     else if (pcx_depth == 24)           /* de-interlace planes */
182     {
183       byte *src_ptr = row_buffer;
184       int plane;
185
186       for (plane = 0; plane < pcx->color_planes; plane++)
187       {
188         int x;
189
190         dst_ptr = bitmap_ptr + plane;
191         for (x = 0; x < width; x++)
192         {
193           *dst_ptr = *src_ptr++;
194           dst_ptr += pcx->color_planes;
195         }
196       }
197     }
198
199     bitmap_ptr += image->bytes_per_row;
200   }
201
202   free(row_buffer);
203
204   return TRUE;
205 }
206
207 #if 0
208 static byte *PCX_ReadColormap(Image *image,byte *buffer_ptr, byte *buffer_last)
209 {
210   int i, magic;
211
212   /* read colormap magic byte */
213   magic = *buffer_ptr++;
214
215   /* check magic colormap header byte */
216   if (magic != PCX_256COLORS_MAGIC)
217     return NULL;
218
219   /* check if enough bytes left for a complete colormap */
220   if (buffer_ptr + PCX_COLORMAP_SIZE > buffer_last)
221     return NULL;
222
223   /* read 256 colors from PCX colormap */
224   for (i=0; i<PCX_MAXCOLORS; i++)
225   {
226     image->rgb.red[i]   = *buffer_ptr++ << 8;
227     image->rgb.green[i] = *buffer_ptr++ << 8;
228     image->rgb.blue[i]  = *buffer_ptr++ << 8;
229   }
230
231   /* return current buffer position for next decoding function */
232   return buffer_ptr;
233 }
234 #endif
235
236 static boolean PCX_ReadColormap(FILE *file,struct PCX_Header *pcx,Image *image)
237 {
238   int pcx_depth = pcx->bits_per_pixel * pcx->color_planes;
239   int num_colors = (1 << pcx_depth);
240   int i;
241
242   if (image->depth != 8)
243     return TRUE;
244
245   if (pcx_depth == 8)
246   {
247     byte value;
248     int value_int;
249
250     /* look for a 256-colour palette */
251     do
252     {
253       if ((value_int = fgetc(file)) == EOF)
254         return FALSE;
255       value = (byte)value_int;
256     }
257     while (value != PCX_256COLORS_MAGIC);
258
259     /* read 256 colors from PCX colormap */
260     for(i = 0; i < PCX_MAXCOLORS; i++)
261     {
262       image->rgb.red[i]   = (byte)fgetc(file) << 8;
263       image->rgb.green[i] = (byte)fgetc(file) << 8;
264       image->rgb.blue[i]  = (byte)fgetc(file) << 8;
265     }
266   }
267   else
268   {
269     for(i = 0; i < num_colors; i++)
270     {
271       image->rgb.red[i]   = pcx->palette[i][0] << 8;
272       image->rgb.green[i] = pcx->palette[i][1] << 8;
273       image->rgb.blue[i]  = pcx->palette[i][2] << 8;
274     }
275   }
276
277   return TRUE;
278 }
279
280 Image *Read_PCX_to_Image(char *filename)
281 {
282   FILE *file;
283   byte header_buffer[PCX_HEADER_SIZE];
284   struct PCX_Header pcx;
285   Image *image;
286   int width, height, depth, pcx_depth;
287   int i;
288
289   errno_pcx = PCX_Success;
290
291   if (!(file = fopen(filename, MODE_READ)))
292   {
293     errno_pcx = PCX_OpenFailed;
294     return NULL;
295   }
296
297   if (fread(header_buffer, 1, PCX_HEADER_SIZE, file) != PCX_HEADER_SIZE)
298   {
299     fclose(file);
300
301     errno_pcx = PCX_ReadFailed;
302     return NULL;
303   }
304
305   pcx.signature      = header_buffer[0];
306   pcx.version        = header_buffer[1];
307   pcx.encoding       = header_buffer[2];
308   pcx.bits_per_pixel = header_buffer[3];
309   pcx.xmin           = (header_buffer[5]  << 8) | header_buffer[4];
310   pcx.ymin           = (header_buffer[7]  << 8) | header_buffer[6];
311   pcx.xmax           = (header_buffer[9]  << 8) | header_buffer[8];
312   pcx.ymax           = (header_buffer[11] << 8) | header_buffer[10];
313   pcx.color_planes   = header_buffer[65];
314   pcx.bytes_per_line = (header_buffer[67] << 8) | header_buffer[66];
315   pcx.palette_type   = (header_buffer[69] << 8) | header_buffer[68];
316
317   for (i = 0; i < 48; i++)
318     pcx.palette[i / 3][i % 3] = header_buffer[16 + i];
319
320   width  = pcx.xmax - pcx.xmin + 1;
321   height = pcx.ymax - pcx.ymin + 1;
322   pcx_depth = pcx.bits_per_pixel * pcx.color_planes;
323   depth = ((pcx_depth + 7) / 8) * 8;
324
325   if (pcx.signature != PCX_MAGIC ||
326       pcx.version != PCX_SUPPORTED_VERSION ||
327       pcx.encoding != PCX_ENCODING ||
328       width < 0 || height < 0)
329   {
330     fclose(file);
331
332     errno_pcx = PCX_FileInvalid;
333     return NULL;
334   }
335
336 #if PCX_DEBUG
337   if (options.verbose)
338   {
339     printf("\n");
340     printf("%s is a %dx%d PC Paintbrush image\n", filename, width, height);
341     printf("depth: %d\n", depth);
342     printf("bits_per_pixel: %d\n", pcx.bits_per_pixel);
343     printf("color_planes: %d\n", pcx.color_planes);
344     printf("bytes_per_line: %d\n", pcx.bytes_per_line);
345     printf("palette type: %s\n",
346            (pcx.palette_type == 1 ? "color" :
347             pcx.palette_type == 2 ? "grayscale" : "undefined"));
348   }
349 #endif
350
351   /* allocate new image structure */
352   image = newImage(width, height, depth);
353
354   /* read compressed bitmap data */
355   if (!PCX_ReadBitmap(file, &pcx, image))
356   {
357     fclose(file);
358     freeImage(image);
359
360     errno_pcx = PCX_FileInvalid;
361     return NULL;
362   }
363
364   /* read colormap data */
365   if (!PCX_ReadColormap(file, &pcx, image))
366   {
367     fclose(file);
368     freeImage(image);
369
370     errno_pcx = PCX_ColorFailed;
371     return NULL;
372   }
373
374   fclose(file);
375
376   if (pcx_depth == 8)
377   {
378     /* determine number of used colormap entries for 8-bit PCX images */
379     for (i=0; i<PCX_MAXCOLORS; i++)
380       if (image->rgb.color_used[i])
381         image->rgb.used++;
382   }
383
384 #if PCX_DEBUG
385   if (options.verbose)
386     printf("Read_PCX_to_Image: %d colors in colormap\n", image->rgb.used);
387 #endif
388
389   return image;
390 }
391
392 #endif /* !TARGET_SDL */