rnd-19981111-1
[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 #include "image.h"
15
16 #define PCX_MAGIC               0x0a    /* first byte in a PCX image file */
17 #define PCX_LAST_VERSION        5       /* last acceptable version number */
18 #define PCX_ENCODING            1       /* PCX encoding method */
19 #define PCX_256COLORS_MAGIC     0x0c    /* first byte of a PCX 256 color map */
20 #define PCX_MAXDEPTH            8       /* supports up to 8 bits per pixel */
21 #define PCX_MAXCOLORS           256     /* maximum number of colors */
22 #define PCX_HEADER_SIZE         128
23 #define PCX_PALETTE_SIZE        (3 * PCX_MAXCOLORS)
24
25 struct PCX_Header
26 {
27   unsigned char signature;      /* PCX file identifier                 */
28   unsigned char version;        /* version compatibility level         */
29   unsigned char encoding;       /* encoding method                     */
30   unsigned char bits_per_pixel; /* bits per pixel, or depth            */
31   unsigned short xmin;          /* X position of left edge             */
32   unsigned short ymin;          /* Y position of top edge              */
33   unsigned short xmax;          /* X position of right edge            */
34   unsigned short ymax;          /* Y position of bottom edge           */
35   unsigned short hres;          /* X screen resolution of source image */
36   unsigned short vres;          /* Y screen resolution of source image */
37   unsigned char palette[16][3]; /* PCX color map                       */
38   unsigned char reserved;       /* should be 0, 1 if std res fax       */
39   unsigned char color_planes;   /* bit planes in image                 */
40   unsigned short bytes_per_line;/* byte delta between scanlines        */
41   unsigned short palette_type;  /* 0 = undef, 1 = color, 2 = grayscale */
42   unsigned char filler[58];     /* fill to struct size of 128          */
43 };
44
45 static boolean PCX_LoadImage();         /* Routine to load PCX bitmap */
46 static boolean PCX_LoadColormap();      /* Routine to load PCX colormap */
47
48 Image *Read_PCX_to_Image(char *filename)
49 {
50   FILE *file;
51   unsigned char buffer[PCX_HEADER_SIZE];
52   struct PCX_Header pcx;
53   Image *image;
54   int width, height, depth;
55
56   if (!(file = fopen(filename, "r")))
57     return NULL;
58
59   /* read PCX header */
60   if (fread(buffer, 1, PCX_HEADER_SIZE, file) != PCX_HEADER_SIZE)
61   {
62     fclose(file);
63     return NULL;
64   }
65
66   pcx.signature = buffer[0];
67   pcx.version = buffer[1];
68   pcx.encoding = buffer[2];
69   pcx.bits_per_pixel = buffer[3];
70   pcx.xmin = buffer[4]  + 256 * buffer[5];
71   pcx.ymin = buffer[6]  + 256 * buffer[7];
72   pcx.xmax = buffer[8]  + 256 * buffer[9];
73   pcx.ymax = buffer[10] + 256 * buffer[11];
74   pcx.color_planes = buffer[65];
75   pcx.bytes_per_line = buffer[66] + 256 * buffer[67];
76   pcx.palette_type = buffer[68] + 256 * buffer[69];
77
78   width = pcx.xmax - pcx.xmin + 1;
79   height = pcx.ymax - pcx.ymin + 1;
80   depth = pcx.bits_per_pixel;
81
82   if (pcx.signature != PCX_MAGIC || pcx.version > PCX_LAST_VERSION ||
83       pcx.encoding != PCX_ENCODING || pcx.color_planes > PCX_MAXDEPTH ||
84       width < 0 || height < 0)
85   {
86     fclose(file);
87     return NULL;
88   }
89
90   if (options.verbose)
91   {
92     printf("%s is a %dx%d PC Paintbrush image with %d bitplanes\n",
93            filename, pcx.xmax, pcx.ymax,
94            pcx.color_planes);
95     printf("depth: %d\n", pcx.bits_per_pixel);
96     printf("bytes_per_line: %d\n", pcx.bytes_per_line);
97     printf("palette type: %s\n",
98            (pcx.palette_type == 1 ? "color" :
99             pcx.palette_type == 2 ? "grayscale" : "undefined"));
100   }
101
102   /* allocate new image structure */
103   image = newRGBImage(width, height, depth);
104
105   /* read compressed bitmap data */
106   if (!PCX_LoadImage(file, image))
107   {
108     fclose(file);
109     return image;
110   }
111
112   /* read colormap data */
113   if (!PCX_LoadColormap(file, image))
114   {
115     fclose(file);
116     return image;
117   }
118
119   fclose(file);
120   return(image);
121 }
122
123 static boolean PCX_LoadImage(FILE *file, Image *image)
124 {
125   /* Run Length Encoding: If the two high bits are set,
126    * then the low 6 bits contain a repeat count, and the byte to
127    * repeat is the next byte in the file.  If the two high bits are
128    * not set, then this is the byte to write.
129    */
130
131   register unsigned char *ptr, *ptr_last;
132   int value, count;
133
134   ptr = image->data;
135   ptr_last = ptr + (image->width * image->height * image->pixlen);
136
137   while (ptr < ptr_last)
138   {
139     if ((value = fgetc(file)) == EOF)
140       return FALSE;
141
142     if ((value & 0xc0) == 0xc0)
143     {
144       count = value & 0x3f;
145
146       if ((value = fgetc(file)) == EOF)
147         return FALSE;
148
149       for ( ; count && (ptr < ptr_last); count--)
150         *ptr++ = (unsigned char)value;
151
152       if (count)
153         printf("Repeat count spans end of image!\n");
154     }
155     else
156       *ptr++ = (unsigned char)value;
157   }
158
159   return TRUE;
160 }
161
162 static boolean PCX_LoadColormap(FILE *file, Image *image)
163 {
164   unsigned char buffer[PCX_PALETTE_SIZE];
165   int i, result, magic;
166
167   /* read colormap magic byte */
168   if ((magic = fgetc(file)) == EOF)
169     return FALSE;
170
171   if (magic != PCX_256COLORS_MAGIC)
172     return FALSE;
173
174   /* read PCX 256 colors colormap */
175   if ((result = fread(buffer, 1, PCX_PALETTE_SIZE, file)) != PCX_PALETTE_SIZE)
176     return FALSE;
177
178   for (i=0; i<PCX_MAXCOLORS; i++)
179   {
180     image->rgb.red[i]   = buffer[i*3 + 0] << 8;
181     image->rgb.green[i] = buffer[i*3 + 1] << 8;
182     image->rgb.blue[i]  = buffer[i*3 + 2] << 8;
183   }
184   image->rgb.used = PCX_MAXCOLORS;
185
186   return TRUE;
187 }