rnd-20070207-2-src
[rocksndiamonds.git] / src / libgame / pcx.c
1 /***********************************************************
2 * Artsoft Retro-Game Library                               *
3 *----------------------------------------------------------*
4 * (c) 1994-2006 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 static boolean PCX_ReadBitmap(FILE *file, struct PCX_Header *pcx, Image *image)
57 {
58   int width = image->width;
59   int height = image->height;
60   int pcx_depth = pcx->bits_per_pixel * pcx->color_planes;
61   int bytes_per_row = pcx->color_planes * pcx->bytes_per_line;
62   byte *row_buffer = checked_malloc(bytes_per_row);
63   byte *bitmap_ptr = image->data;
64   int y;
65
66   for (y = 0; y < height; y++)
67   {
68     /* decode a scan line into a temporary buffer first */
69     byte *dst_ptr = (pcx_depth == 8 ? bitmap_ptr : row_buffer);
70     byte value = 0, count = 0;
71     int value_int;
72     int i;
73
74     for (i = 0; i < bytes_per_row; i++)
75     {
76       if (count == 0)
77       {
78         if ((value_int = fgetc(file)) == EOF)
79         {
80           free(row_buffer);
81           return FALSE;
82         }
83
84         value = (byte)value_int;
85
86         if ((value & 0xc0) == 0xc0)     /* this is a repeat count byte */
87         {
88           count = value & 0x3f;         /* extract repeat count from byte */
89
90           if ((value_int = fgetc(file)) == EOF)
91           {
92             free(row_buffer);
93             return FALSE;
94           }
95
96           value = (byte)value_int;
97         }
98         else
99           count = 1;
100       }
101
102       dst_ptr[i] = value;
103       count--;
104
105       if (pcx_depth == 8)
106         image->rgb.color_used[value] = TRUE;
107     }
108
109     if (pcx_depth <= 4)                 /* expand planes to 1 byte/pixel */
110     {
111       byte *src_ptr = row_buffer;
112       int plane;
113
114       for (plane = 0; plane < pcx->color_planes; plane++)
115       {
116         int i, j, x = 0;
117
118         for (i = 0; i < pcx->bytes_per_line; i++)
119         {
120           byte value = *src_ptr++;
121
122           for (j = 7; j >= 0; j--)
123           {
124             byte bit = (value >> j) & 1;
125
126             if (i * 8 + j >= width)     /* skip padding bits */
127               continue;
128
129             bitmap_ptr[x++] |= bit << plane;
130           }
131         }
132       }
133     }
134     else if (pcx_depth == 24)           /* de-interlace planes */
135     {
136       byte *src_ptr = row_buffer;
137       int plane;
138
139       for (plane = 0; plane < pcx->color_planes; plane++)
140       {
141         int x;
142
143         dst_ptr = bitmap_ptr + plane;
144         for (x = 0; x < width; x++)
145         {
146           *dst_ptr = *src_ptr++;
147           dst_ptr += pcx->color_planes;
148         }
149       }
150     }
151
152     bitmap_ptr += image->bytes_per_row;
153   }
154
155   free(row_buffer);
156
157   return TRUE;
158 }
159
160 static boolean PCX_ReadColormap(FILE *file,struct PCX_Header *pcx,Image *image)
161 {
162   int pcx_depth = pcx->bits_per_pixel * pcx->color_planes;
163   int num_colors = (1 << pcx_depth);
164   int i;
165
166   if (image->depth != 8)
167     return TRUE;
168
169   if (pcx_depth == 8)
170   {
171     byte value;
172     int value_int;
173
174     /* look for a 256-colour palette */
175     do
176     {
177       if ((value_int = fgetc(file)) == EOF)
178         return FALSE;
179       value = (byte)value_int;
180     }
181     while (value != PCX_256COLORS_MAGIC);
182
183     /* read 256 colors from PCX colormap */
184     for (i = 0; i < PCX_MAXCOLORS; i++)
185     {
186       image->rgb.red[i]   = (byte)fgetc(file) << 8;
187       image->rgb.green[i] = (byte)fgetc(file) << 8;
188       image->rgb.blue[i]  = (byte)fgetc(file) << 8;
189     }
190   }
191   else
192   {
193     for (i = 0; i < num_colors; i++)
194     {
195       image->rgb.red[i]   = pcx->palette[i][0] << 8;
196       image->rgb.green[i] = pcx->palette[i][1] << 8;
197       image->rgb.blue[i]  = pcx->palette[i][2] << 8;
198     }
199   }
200
201   return TRUE;
202 }
203
204 Image *Read_PCX_to_Image(char *filename)
205 {
206   FILE *file;
207   byte header_buffer[PCX_HEADER_SIZE];
208   struct PCX_Header pcx;
209   Image *image;
210   int width, height, depth, pcx_depth;
211   int i;
212
213   errno_pcx = PCX_Success;
214
215   if (!(file = fopen(filename, MODE_READ)))
216   {
217     errno_pcx = PCX_OpenFailed;
218     return NULL;
219   }
220
221   if (fread(header_buffer, 1, PCX_HEADER_SIZE, file) != PCX_HEADER_SIZE)
222   {
223     fclose(file);
224
225     errno_pcx = PCX_ReadFailed;
226     return NULL;
227   }
228
229   pcx.signature      = header_buffer[0];
230   pcx.version        = header_buffer[1];
231   pcx.encoding       = header_buffer[2];
232   pcx.bits_per_pixel = header_buffer[3];
233   pcx.xmin           = (header_buffer[5]  << 8) | header_buffer[4];
234   pcx.ymin           = (header_buffer[7]  << 8) | header_buffer[6];
235   pcx.xmax           = (header_buffer[9]  << 8) | header_buffer[8];
236   pcx.ymax           = (header_buffer[11] << 8) | header_buffer[10];
237   pcx.color_planes   = header_buffer[65];
238   pcx.bytes_per_line = (header_buffer[67] << 8) | header_buffer[66];
239   pcx.palette_type   = (header_buffer[69] << 8) | header_buffer[68];
240
241   for (i = 0; i < 48; i++)
242     pcx.palette[i / 3][i % 3] = header_buffer[16 + i];
243
244   width  = pcx.xmax - pcx.xmin + 1;
245   height = pcx.ymax - pcx.ymin + 1;
246   pcx_depth = pcx.bits_per_pixel * pcx.color_planes;
247   depth = ((pcx_depth + 7) / 8) * 8;
248
249   if (pcx.signature != PCX_MAGIC ||
250       pcx.version != PCX_SUPPORTED_VERSION ||
251       pcx.encoding != PCX_ENCODING ||
252       width < 0 || height < 0)
253   {
254     fclose(file);
255
256     errno_pcx = PCX_FileInvalid;
257     return NULL;
258   }
259
260 #if PCX_DEBUG
261   if (options.verbose)
262   {
263     printf("\n");
264     printf("%s is a %dx%d PC Paintbrush image\n", filename, width, height);
265     printf("depth: %d\n", depth);
266     printf("bits_per_pixel: %d\n", pcx.bits_per_pixel);
267     printf("color_planes: %d\n", pcx.color_planes);
268     printf("bytes_per_line: %d\n", pcx.bytes_per_line);
269     printf("palette type: %s\n",
270            (pcx.palette_type == 1 ? "color" :
271             pcx.palette_type == 2 ? "grayscale" : "undefined"));
272   }
273 #endif
274
275   /* allocate new image structure */
276   image = newImage(width, height, depth);
277
278   /* read compressed bitmap data */
279   if (!PCX_ReadBitmap(file, &pcx, image))
280   {
281     fclose(file);
282     freeImage(image);
283
284     errno_pcx = PCX_FileInvalid;
285     return NULL;
286   }
287
288   /* read colormap data */
289   if (!PCX_ReadColormap(file, &pcx, image))
290   {
291     fclose(file);
292     freeImage(image);
293
294     errno_pcx = PCX_ColorFailed;
295     return NULL;
296   }
297
298   fclose(file);
299
300   if (pcx_depth == 8)
301   {
302     /* determine number of used colormap entries for 8-bit PCX images */
303     for (i = 0; i < PCX_MAXCOLORS; i++)
304       if (image->rgb.color_used[i])
305         image->rgb.used++;
306   }
307
308 #if PCX_DEBUG
309   if (options.verbose)
310     printf("Read_PCX_to_Image: %d colors in colormap\n", image->rgb.used);
311 #endif
312
313   return image;
314 }
315
316 #endif /* !TARGET_SDL */