rnd-20001125-3-src
[rocksndiamonds.git] / src / pcx.c
1 /***********************************************************
2 *  Rocks'n'Diamonds -- McDuffin Strikes Back!              *
3 *----------------------------------------------------------*
4 *  (c) 1995-98 Artsoft Entertainment                       *
5 *              Holger Schemel                              *
6 *              Oststrasse 11a                              *
7 *              33604 Bielefeld                             *
8 *              phone: ++49 +521 290471                     *
9 *              email: aeglos@valinor.owl.de                *
10 *----------------------------------------------------------*
11 *  pcx.c                                                   *
12 ***********************************************************/
13
14 #ifndef TARGET_SDL
15
16 #include "pcx.h"
17 #include "image.h"
18 #include "misc.h"
19
20 #define PCX_DEBUG               FALSE
21
22 #define PCX_MAGIC               0x0a    /* first byte in a PCX image file    */
23 #define PCX_LAST_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_MAXDEPTH            8       /* supports up to 8 bits per pixel   */
27 #define PCX_MAXCOLORS           256     /* maximum number of colors          */
28
29 #define PCX_HEADER_SIZE         128
30 #define PCX_COLORMAP_SIZE       (3 * PCX_MAXCOLORS)
31
32 struct PCX_Header
33 {
34   unsigned char signature;      /* PCX file identifier                 */
35   unsigned char version;        /* version compatibility level         */
36   unsigned char encoding;       /* encoding method                     */
37   unsigned char bits_per_pixel; /* bits per pixel, or depth            */
38   unsigned short xmin;          /* X position of left edge             */
39   unsigned short ymin;          /* Y position of top edge              */
40   unsigned short xmax;          /* X position of right edge            */
41   unsigned short ymax;          /* Y position of bottom edge           */
42   unsigned short hres;          /* X screen resolution of source image */
43   unsigned short vres;          /* Y screen resolution of source image */
44   unsigned char palette[16][3]; /* PCX color map                       */
45   unsigned char reserved;       /* should be 0, 1 if std res fax       */
46   unsigned char color_planes;   /* bit planes in image                 */
47   unsigned short bytes_per_line;/* byte delta between scanlines        */
48   unsigned short palette_type;  /* 0 = undef, 1 = color, 2 = grayscale */
49   unsigned char filler[58];     /* fill to struct size of 128          */
50 };
51
52 /* global PCX error value */
53 int errno_pcx = PCX_Success;
54
55 static byte *PCX_ReadBitmap(Image *image, byte *buffer_ptr, byte *buffer_last)
56 {
57   /* Run Length Encoding: If the two high bits are set,
58    * then the low 6 bits contain a repeat count, and the byte to
59    * repeat is the next byte in the file.  If the two high bits are
60    * not set, then this is the byte to write.
61    */
62
63   unsigned int bytes_per_pixel = (image->depth + 7) / 8;
64   register byte *bitmap_ptr, *bitmap_last;
65   register byte value, count;
66
67   bitmap_ptr = image->data;
68   bitmap_last = bitmap_ptr + (image->width * image->height * bytes_per_pixel);
69
70   while (bitmap_ptr < bitmap_last && buffer_ptr < buffer_last)
71   {
72     value = *buffer_ptr++;
73
74     if ((value & 0xc0) == 0xc0)         /* this is a repeat count byte */
75     {
76       count = value & 0x3f;             /* extract repeat count from byte */
77       value = *buffer_ptr++;            /* next byte is value to repeat */
78
79       for (; count && bitmap_ptr < bitmap_last; count--)
80         *bitmap_ptr++ = value;
81
82       if (count)                        /* repeat count spans end of bitmap */
83         return NULL;
84     }
85     else
86       *bitmap_ptr++ = value;
87
88     image->rgb.color_used[value] = TRUE;
89   }
90
91   /* check if end of buffer was reached before end of bitmap */
92   if (bitmap_ptr < bitmap_last)
93     return NULL;
94
95   /* return current buffer position for next decoding function */
96   return buffer_ptr;
97 }
98
99 static byte *PCX_ReadColormap(Image *image,byte *buffer_ptr, byte *buffer_last)
100 {
101   int i, magic;
102
103   /* read colormap magic byte */
104   magic = *buffer_ptr++;
105
106   /* check magic colormap header byte */
107   if (magic != PCX_256COLORS_MAGIC)
108     return NULL;
109
110   /* check if enough bytes left for a complete colormap */
111   if (buffer_ptr + PCX_COLORMAP_SIZE > buffer_last)
112     return NULL;
113
114   /* read 256 colors from PCX colormap */
115   for (i=0; i<PCX_MAXCOLORS; i++)
116   {
117     image->rgb.red[i]   = *buffer_ptr++ << 8;
118     image->rgb.green[i] = *buffer_ptr++ << 8;
119     image->rgb.blue[i]  = *buffer_ptr++ << 8;
120   }
121
122   /* return current buffer position for next decoding function */
123   return buffer_ptr;
124 }
125
126 Image *Read_PCX_to_Image(char *filename)
127 {
128   FILE *file;
129   byte *file_buffer;
130   byte *buffer_ptr, *buffer_last;
131   unsigned int file_length;
132   struct PCX_Header pcx;
133   Image *image;
134   int width, height, depth;
135   int i;
136
137   errno_pcx = PCX_Success;
138
139   if (!(file = fopen(filename, "r")))
140   {
141     errno_pcx = PCX_OpenFailed;
142     return NULL;
143   }
144
145   if (fseek(file, 0, SEEK_END) == -1)
146   {
147     fclose(file);
148     errno_pcx = PCX_ReadFailed;
149     return NULL;
150   }
151
152   file_length = ftell(file);
153   rewind(file);
154
155   if (file_length < PCX_HEADER_SIZE)
156   {
157     /* PCX file is too short to contain a valid PCX header */
158     fclose(file);
159
160     errno_pcx = PCX_FileInvalid;
161     return NULL;
162   }
163
164   file_buffer = checked_malloc(file_length);
165
166   if (fread(file_buffer, 1, file_length, file) != file_length)
167   {
168     fclose(file);
169     errno_pcx = PCX_ReadFailed;
170     return NULL;
171   }
172
173   fclose(file);
174
175   pcx.signature      = file_buffer[0];
176   pcx.version        = file_buffer[1];
177   pcx.encoding       = file_buffer[2];
178   pcx.bits_per_pixel = file_buffer[3];
179   pcx.xmin           = file_buffer[4]  + 256 * file_buffer[5];
180   pcx.ymin           = file_buffer[6]  + 256 * file_buffer[7];
181   pcx.xmax           = file_buffer[8]  + 256 * file_buffer[9];
182   pcx.ymax           = file_buffer[10] + 256 * file_buffer[11];
183   pcx.color_planes   = file_buffer[65];
184   pcx.bytes_per_line = file_buffer[66] + 256 * file_buffer[67];
185   pcx.palette_type   = file_buffer[68] + 256 * file_buffer[69];
186
187   width  = pcx.xmax - pcx.xmin + 1;
188   height = pcx.ymax - pcx.ymin + 1;
189   depth  = pcx.bits_per_pixel;
190
191   if (pcx.signature != PCX_MAGIC || pcx.version > PCX_LAST_VERSION ||
192       pcx.encoding != PCX_ENCODING || pcx.color_planes > PCX_MAXDEPTH ||
193       width < 0 || height < 0)
194   {
195     free(file_buffer);
196
197     errno_pcx = PCX_FileInvalid;
198     return NULL;
199   }
200
201 #if PCX_DEBUG
202   if (options.verbose)
203   {
204     printf("%s is a %dx%d PC Paintbrush image with %d bitplanes\n",
205            filename, width, height,
206            pcx.color_planes);
207     printf("depth: %d\n", pcx.bits_per_pixel);
208     printf("color_planes: %d\n", pcx.color_planes);
209     printf("bytes_per_line: %d\n", pcx.bytes_per_line);
210     printf("palette type: %s\n",
211            (pcx.palette_type == 1 ? "color" :
212             pcx.palette_type == 2 ? "grayscale" : "undefined"));
213   }
214 #endif
215
216   /* allocate new image structure */
217   image = newImage(width, height, depth);
218
219   buffer_ptr  = file_buffer + PCX_HEADER_SIZE;
220   buffer_last = file_buffer + file_length;
221
222   /* read compressed bitmap data */
223   if ((buffer_ptr = PCX_ReadBitmap(image, buffer_ptr, buffer_last)) == NULL)
224   {
225     free(file_buffer);
226     freeImage(image);
227
228     errno_pcx = PCX_FileInvalid;
229     return NULL;
230   }
231
232   if (file_length < PCX_HEADER_SIZE + PCX_COLORMAP_SIZE)
233   {
234     /* PCX file is too short to contain a valid 256 colors colormap */
235     fclose(file);
236     errno_pcx = PCX_ColorFailed;
237     return NULL;
238   }
239
240   /* read colormap data */
241   if (!PCX_ReadColormap(image, buffer_ptr, buffer_last))
242   {
243     free(file_buffer);
244     freeImage(image);
245     errno_pcx = PCX_ColorFailed;
246     return NULL;
247   }
248
249   free(file_buffer);
250
251   /* determine number of used colormap entries */
252   image->rgb.used = 0;
253   for (i=0; i<PCX_MAXCOLORS; i++)
254     if (image->rgb.color_used[i])
255       image->rgb.used++;
256
257 #if PCX_DEBUG
258   if (options.verbose)
259     printf("Read_PCX_to_Image: %d colors found\n", image->rgb.used);
260 #endif
261
262   return image;
263 }
264
265 #endif /* !TARGET_SDL */