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