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