]> icculus.org git repositories - btb/d2x.git/blob - 2d/pcx.c
portability
[btb/d2x.git] / 2d / pcx.c
1 /* $Id: pcx.c,v 1.4 2002-07-26 21:10:53 btb Exp $ */
2 /*
3 THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
4 SOFTWARE CORPORATION ("PARALLAX").  PARALLAX, IN DISTRIBUTING THE CODE TO
5 END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
6 ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
7 IN USING, DISPLAYING,  AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
8 SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
9 FREE PURPOSES.  IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
10 CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES.  THE END-USER UNDERSTANDS
11 AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.
12 COPYRIGHT 1993-1998 PARALLAX SOFTWARE CORPORATION.  ALL RIGHTS RESERVED.
13 */
14 /*
15  *
16  * Routines to read/write pcx images.
17  *
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #include <conf.h>
22 #endif
23
24 #include <stdlib.h>
25 #include <stdio.h>
26 #include <string.h>
27
28 #include "gr.h"
29 #include "grdef.h"
30 #include "u_mem.h"
31 #include "pcx.h"
32 #include "cfile.h"
33
34 int pcx_encode_byte(ubyte byt, ubyte cnt, FILE * fid);
35 int pcx_encode_line(ubyte *inBuff, int inLen, FILE * fp);
36
37 /* PCX Header data type */
38 typedef struct  {
39         ubyte           Manufacturer;
40         ubyte           Version;
41         ubyte           Encoding;
42         ubyte           BitsPerPixel;
43         short           Xmin;
44         short           Ymin;
45         short           Xmax;
46         short           Ymax;
47         short           Hdpi;
48         short           Vdpi;
49         ubyte           ColorMap[16][3];
50         ubyte           Reserved;
51         ubyte           Nplanes;
52         short           BytesPerLine;
53         ubyte           filler[60];
54 } PCXHeader;
55
56 /*
57  * reads a PCXHeader structure from a CFILE
58  */
59 static void PCXHeader_read(PCXHeader *ph, CFILE *fp)
60 {
61         ph->Manufacturer = cfile_read_byte(fp);
62         ph->Version = cfile_read_byte(fp);
63         ph->Encoding = cfile_read_byte(fp);
64         ph->BitsPerPixel = cfile_read_byte(fp);
65         ph->Xmin = cfile_read_short(fp);
66         ph->Ymin = cfile_read_short(fp);
67         ph->Xmax = cfile_read_short(fp);
68         ph->Ymax = cfile_read_short(fp);
69         ph->Hdpi = cfile_read_short(fp);
70         ph->Vdpi = cfile_read_short(fp);
71         cfread(&ph->ColorMap, 16*3, 1, fp);
72         ph->Reserved = cfile_read_byte(fp);
73         ph->Nplanes = cfile_read_byte(fp);
74         ph->BytesPerLine = cfile_read_short(fp);
75         cfread(&ph->filler, 60, 1, fp);
76 }
77
78 int pcx_read_bitmap( char * filename, grs_bitmap * bmp,int bitmap_type ,ubyte * palette )
79 {
80         PCXHeader header;
81         CFILE * PCXfile;
82         int i, row, col, count, xsize, ysize;
83         ubyte data, *pixdata;
84
85         PCXfile = cfopen( filename , "rb" );
86         if ( !PCXfile )
87                 return PCX_ERROR_OPENING;
88
89         // read 128 char PCX header
90         PCXHeader_read(&header, PCXfile);
91 #if 0
92         if (cfread( &header, sizeof(PCXHeader), 1, PCXfile )!=1)        {
93                 cfclose( PCXfile );
94                 return PCX_ERROR_NO_HEADER;
95         }
96 #endif
97
98         // Is it a 256 color PCX file?
99         if ((header.Manufacturer != 10)||(header.Encoding != 1)||(header.Nplanes != 1)||(header.BitsPerPixel != 8)||(header.Version != 5))      {
100                 cfclose( PCXfile );
101                 return PCX_ERROR_WRONG_VERSION;
102         }
103
104         // Find the size of the image
105         xsize = header.Xmax - header.Xmin + 1;
106         ysize = header.Ymax - header.Ymin + 1;
107
108         if ( bitmap_type == BM_LINEAR ) {
109                 if ( bmp->bm_data == NULL )     {
110                         gr_init_bitmap_alloc (bmp, bitmap_type, 0, 0, xsize, ysize, xsize);
111                 }
112         }
113
114         if ( bmp->bm_type == BM_LINEAR )        {
115                 for (row=0; row< ysize ; row++)      {
116                         pixdata = &bmp->bm_data[bmp->bm_rowsize*row];
117                         for (col=0; col< xsize ; )      {
118                                 if (cfread( &data, 1, 1, PCXfile )!=1 ) {
119                                         cfclose( PCXfile );     
120                                         return PCX_ERROR_READING;
121                                 }
122                                 if ((data & 0xC0) == 0xC0)     {
123                                         count =  data & 0x3F;
124                                         if (cfread( &data, 1, 1, PCXfile )!=1 ) {
125                                                 cfclose( PCXfile );     
126                                                 return PCX_ERROR_READING;
127                                         }
128                                         memset( pixdata, data, count );
129                                         pixdata += count;
130                                         col += count;
131                                 } else {
132                                         *pixdata++ = data;
133                                         col++;
134                                 }
135                         }
136                 }
137         } else {
138                 for (row=0; row< ysize ; row++)      {
139                         for (col=0; col< xsize ; )      {
140                                 if (cfread( &data, 1, 1, PCXfile )!=1 ) {
141                                         cfclose( PCXfile );     
142                                         return PCX_ERROR_READING;
143                                 }
144                                 if ((data & 0xC0) == 0xC0)     {
145                                         count =  data & 0x3F;
146                                         if (cfread( &data, 1, 1, PCXfile )!=1 ) {
147                                                 cfclose( PCXfile );     
148                                                 return PCX_ERROR_READING;
149                                         }
150                                         for (i=0;i<count;i++)
151                                                 gr_bm_pixel( bmp, col+i, row, data );
152                                         col += count;
153                                 } else {
154                                         gr_bm_pixel( bmp, col, row, data );
155                                         col++;
156                                 }
157                         }
158                 }
159         }
160
161         // Read the extended palette at the end of PCX file
162         if ( palette != NULL )  {
163                 // Read in a character which should be 12 to be extended palette file
164                 if (cfread( &data, 1, 1, PCXfile )==1)  {
165                         if ( data == 12 )       {
166                                 if (cfread(palette,768, 1, PCXfile)!=1) {
167                                         cfclose( PCXfile );
168                                         return PCX_ERROR_READING;
169                                 }
170                                 for (i=0; i<768; i++ )
171                                         palette[i] >>= 2;
172                         }
173                 } else {
174                         cfclose( PCXfile );     
175                         return PCX_ERROR_NO_PALETTE;
176                 }
177         }
178         cfclose(PCXfile);
179         return PCX_ERROR_NONE;
180 }
181
182 int pcx_write_bitmap( char * filename, grs_bitmap * bmp, ubyte * palette )
183 {
184         int retval;
185         int i;
186         ubyte data;
187         PCXHeader header;
188         FILE * PCXfile;
189
190         memset( &header, 0, sizeof( PCXHeader ) );
191
192         header.Manufacturer = 10;
193         header.Encoding = 1;
194         header.Nplanes = 1;
195         header.BitsPerPixel = 8;
196         header.Version = 5;
197         header.Xmax = bmp->bm_w-1;
198         header.Ymax = bmp->bm_h-1;
199         header.BytesPerLine = bmp->bm_w;
200
201         PCXfile = fopen( filename , "wb" );
202         if ( !PCXfile )
203                 return PCX_ERROR_OPENING;
204
205         if ( fwrite( &header, sizeof( PCXHeader ), 1, PCXfile ) != 1 )  {
206                 fclose( PCXfile );
207                 return PCX_ERROR_WRITING;
208         }
209
210         for (i=0; i<bmp->bm_h; i++ )    {
211                 if (!pcx_encode_line( &bmp->bm_data[bmp->bm_rowsize*i], bmp->bm_w, PCXfile ))   {
212                         fclose( PCXfile );
213                         return PCX_ERROR_WRITING;
214                 }
215         }
216
217         // Mark an extended palette     
218         data = 12;
219         if (fwrite( &data, 1, 1, PCXfile )!=1)  {
220                 fclose( PCXfile );
221                 return PCX_ERROR_WRITING;
222         }
223
224         // Write the extended palette
225         for (i=0; i<768; i++ )  
226                 palette[i] <<= 2;
227         
228         retval = fwrite( palette, 768, 1, PCXfile );
229
230         for (i=0; i<768; i++ )  
231                 palette[i] >>= 2;
232
233         if (retval !=1) {
234                 fclose( PCXfile );
235                 return PCX_ERROR_WRITING;
236         }
237
238         fclose( PCXfile );
239         return PCX_ERROR_NONE;
240
241 }
242
243 // returns number of bytes written into outBuff, 0 if failed 
244 int pcx_encode_line(ubyte *inBuff, int inLen, FILE * fp)
245 {  
246         ubyte this, last;
247         int srcIndex, i;
248         register int total;
249         register ubyte runCount;        // max single runlength is 63
250         total = 0;
251         last = *(inBuff);               
252         runCount = 1;
253
254         for (srcIndex = 1; srcIndex < inLen; srcIndex++) {
255                 this = *(++inBuff);
256                 if (this == last)       {
257                         runCount++;                     // it encodes
258                         if (runCount == 63)     {
259                                 if (!(i=pcx_encode_byte(last, runCount, fp)))
260                                         return(0);
261                                 total += i;
262                                 runCount = 0;
263                         }
264                 } else {        // this != last
265                         if (runCount)   {
266                                 if (!(i=pcx_encode_byte(last, runCount, fp)))
267                                         return(0);
268                                 total += i;
269                         }
270                         last = this;
271                         runCount = 1;
272                 }
273         }       
274
275         if (runCount)   {               // finish up
276                 if (!(i=pcx_encode_byte(last, runCount, fp)))
277                         return 0;
278                 return total + i;
279         }
280         return total;
281 }
282
283 // subroutine for writing an encoded byte pair 
284 // returns count of bytes written, 0 if error
285 int pcx_encode_byte(ubyte byt, ubyte cnt, FILE * fid) 
286 {
287         if (cnt) {
288                 if ( (cnt==1) && (0xc0 != (0xc0 & byt)) )       {
289                         if(EOF == putc((int)byt, fid))
290                                 return 0;       // disk write error (probably full)
291                         return 1;
292                 } else {
293                         if(EOF == putc((int)0xC0 | cnt, fid))
294                                 return 0;       // disk write error
295                         if(EOF == putc((int)byt, fid))
296                                 return 0;       // disk write error
297                         return 2;
298                 }
299         }
300         return 0;
301 }
302
303 //text for error messges
304 char pcx_error_messages[] = {
305         "No error.\0"
306         "Error opening file.\0"
307         "Couldn't read PCX header.\0"
308         "Unsupported PCX version.\0"
309         "Error reading data.\0"
310         "Couldn't find palette information.\0"
311         "Error writing data.\0"
312 };
313
314
315 //function to return pointer to error message
316 char *pcx_errormsg(int error_number)
317 {
318         char *p = pcx_error_messages;
319
320         while (error_number--) {
321
322                 if (!p) return NULL;
323
324                 p += strlen(p)+1;
325
326         }
327
328         return p;
329 }
330
331 // fullscreen loading, 10/14/99 Jan Bobrowski
332
333 int pcx_read_fullscr(char * filename, ubyte * palette)
334 {
335         int pcx_error;
336         grs_bitmap bm;
337         gr_init_bitmap_data(&bm);
338         pcx_error = pcx_read_bitmap(filename, &bm, BM_LINEAR, palette);
339         if (pcx_error == PCX_ERROR_NONE)
340                 show_fullscr(&bm);
341         gr_free_bitmap_data(&bm);
342         return pcx_error;
343 }