]> icculus.org git repositories - btb/d2x.git/blob - 2d/pcx.c
move old per-file change logs into new file ChangeLog-old
[btb/d2x.git] / 2d / pcx.c
1 /* $Id: pcx.c,v 1.9 2004-08-28 23:17:45 schaffner 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 #ifdef OGL
35 #include "palette.h"
36 #endif
37
38 #if defined(POLY_ACC)
39 #include "poly_acc.h"
40 #endif
41
42 int pcx_encode_byte(ubyte byt, ubyte cnt, CFILE *fid);
43 int pcx_encode_line(ubyte *inBuff, int inLen, CFILE *fp);
44
45 /* PCX Header data type */
46 typedef struct {
47         ubyte   Manufacturer;
48         ubyte   Version;
49         ubyte   Encoding;
50         ubyte   BitsPerPixel;
51         short   Xmin;
52         short   Ymin;
53         short   Xmax;
54         short   Ymax;
55         short   Hdpi;
56         short   Vdpi;
57         ubyte   ColorMap[16][3];
58         ubyte   Reserved;
59         ubyte   Nplanes;
60         short   BytesPerLine;
61         ubyte   filler[60];
62 } __pack__ PCXHeader;
63
64 #define PCXHEADER_SIZE 128
65
66 #ifdef FAST_FILE_IO
67 #define PCXHeader_read_n(ph, n, fp) cfread(ph, sizeof(PCXHeader), n, fp)
68 #else
69 /*
70  * reads n PCXHeader structs from a CFILE
71  */
72 int PCXHeader_read_n(PCXHeader *ph, int n, CFILE *fp)
73 {
74         int i;
75
76         for (i = 0; i < n; i++) {
77                 ph->Manufacturer = cfile_read_byte(fp);
78                 ph->Version = cfile_read_byte(fp);
79                 ph->Encoding = cfile_read_byte(fp);
80                 ph->BitsPerPixel = cfile_read_byte(fp);
81                 ph->Xmin = cfile_read_short(fp);
82                 ph->Ymin = cfile_read_short(fp);
83                 ph->Xmax = cfile_read_short(fp);
84                 ph->Ymax = cfile_read_short(fp);
85                 ph->Hdpi = cfile_read_short(fp);
86                 ph->Vdpi = cfile_read_short(fp);
87                 cfread(&ph->ColorMap, 16*3, 1, fp);
88                 ph->Reserved = cfile_read_byte(fp);
89                 ph->Nplanes = cfile_read_byte(fp);
90                 ph->BytesPerLine = cfile_read_short(fp);
91                 cfread(&ph->filler, 60, 1, fp);
92         }
93         return i;
94 }
95 #endif
96
97 int pcx_get_dimensions( char *filename, int *width, int *height)
98 {
99         CFILE *PCXfile;
100         PCXHeader header;
101
102         PCXfile = cfopen(filename, "rb");
103         if (!PCXfile) return PCX_ERROR_OPENING;
104
105         if (PCXHeader_read_n(&header, 1, PCXfile) != 1) {
106                 cfclose(PCXfile);
107                 return PCX_ERROR_NO_HEADER;
108         }
109         cfclose(PCXfile);
110
111         *width = header.Xmax - header.Xmin+1;
112         *height = header.Ymax - header.Ymin+1;
113
114         return PCX_ERROR_NONE;
115 }
116
117 #ifdef MACINTOSH
118 int pcx_read_bitmap_palette( char *filename, ubyte *palette)
119 {
120         PCXHeader header;
121         CFILE * PCXfile;
122         ubyte data;
123         int i;
124
125         PCXfile = cfopen( filename , "rb" );
126         if ( !PCXfile )
127                 return PCX_ERROR_OPENING;
128
129         // read 128 char PCX header
130         if (PCXHeader_read_n( &header, 1, PCXfile )!=1) {
131                 cfclose( PCXfile );
132                 return PCX_ERROR_NO_HEADER;
133         }
134         // Is it a 256 color PCX file?
135         if ((header.Manufacturer != 10)||(header.Encoding != 1)||(header.Nplanes != 1)||(header.BitsPerPixel != 8)||(header.Version != 5))      {
136                 cfclose( PCXfile );
137                 return PCX_ERROR_WRONG_VERSION;
138         }
139
140         // Read the extended palette at the end of PCX file
141         // Read in a character which should be 12 to be extended palette file
142
143         if (palette != NULL) {
144                 cfseek( PCXfile, -768, SEEK_END );
145                 cfread( palette, 3, 256, PCXfile );
146                 cfseek( PCXfile, PCXHEADER_SIZE, SEEK_SET );
147                 for (i=0; i<768; i++ )
148                         palette[i] >>= 2;
149 #ifdef MACINTOSH
150                 for (i = 0; i < 3; i++) {
151                         data = palette[i];
152                         palette[i] = palette[765+i];
153                         palette[765+i] = data;
154                 }
155 #endif
156         }
157 }
158 #endif
159
160 int pcx_read_bitmap( char * filename, grs_bitmap * bmp,int bitmap_type ,ubyte * palette )
161 {
162         PCXHeader header;
163         CFILE * PCXfile;
164         int i, row, col, count, xsize, ysize;
165         ubyte data, *pixdata;
166 #if defined(POLY_ACC)
167     unsigned char local_pal[768];
168
169     pa_flush();
170 #endif
171
172         PCXfile = cfopen( filename , "rb" );
173         if ( !PCXfile )
174                 return PCX_ERROR_OPENING;
175
176         // read 128 char PCX header
177         if (PCXHeader_read_n( &header, 1, PCXfile )!=1) {
178                 cfclose( PCXfile );
179                 return PCX_ERROR_NO_HEADER;
180         }
181
182         // Is it a 256 color PCX file?
183         if ((header.Manufacturer != 10)||(header.Encoding != 1)||(header.Nplanes != 1)||(header.BitsPerPixel != 8)||(header.Version != 5))      {
184                 cfclose( PCXfile );
185                 return PCX_ERROR_WRONG_VERSION;
186         }
187
188         // Find the size of the image
189         xsize = header.Xmax - header.Xmin + 1;
190         ysize = header.Ymax - header.Ymin + 1;
191
192 #if defined(POLY_ACC)
193     // Read the extended palette at the end of PCX file
194     if(bitmap_type == BM_LINEAR15)      // need palette for conversion from 8bit pcx to 15bit.
195     {
196         cfseek( PCXfile, -768, SEEK_END );
197         cfread( local_pal, 3, 256, PCXfile );
198         cfseek( PCXfile, PCXHEADER_SIZE, SEEK_SET );
199         for (i=0; i<768; i++ )
200             local_pal[i] >>= 2;
201         pa_save_clut();
202         pa_update_clut(local_pal, 0, 256, 0);
203     }
204 #endif
205
206         if ( bitmap_type == BM_LINEAR ) {
207                 if ( bmp->bm_data == NULL )     {
208                         gr_init_bitmap_alloc (bmp, bitmap_type, 0, 0, xsize, ysize, xsize);
209                 }
210         }
211
212         if ( bmp->bm_type == BM_LINEAR )        {
213                 for (row=0; row< ysize ; row++)      {
214                         pixdata = &bmp->bm_data[bmp->bm_rowsize*row];
215                         for (col=0; col< xsize ; )      {
216                                 if (cfread( &data, 1, 1, PCXfile )!=1 ) {
217                                         cfclose( PCXfile );
218                                         return PCX_ERROR_READING;
219                                 }
220                                 if ((data & 0xC0) == 0xC0)     {
221                                         count =  data & 0x3F;
222                                         if (cfread( &data, 1, 1, PCXfile )!=1 ) {
223                                                 cfclose( PCXfile );
224                                                 return PCX_ERROR_READING;
225                                         }
226 #ifdef MACINTOSH
227                                         if (data == 0)
228                                                 data = 255;
229                                         else if (data == 255)
230                                                 data = 0;
231 #endif
232                                         memset( pixdata, data, count );
233                                         pixdata += count;
234                                         col += count;
235                                 } else {
236 #ifdef MACINTOSH
237                                         if (data == 0)
238                                                 data = 255;
239                                         else if (data == 255)
240                                                 data = 0;
241 #endif
242                                         *pixdata++ = data;
243                                         col++;
244                                 }
245                         }
246                 }
247 #if defined(POLY_ACC)
248     } else if( bmp->bm_type == BM_LINEAR15 )    {
249         ushort *pixdata2, pix15;
250         PA_DFX (pa_set_backbuffer_current());
251                   PA_DFX (pa_set_write_mode(0));
252                 for (row=0; row< ysize ; row++)      {
253             pixdata2 = (ushort *)&bmp->bm_data[bmp->bm_rowsize*row];
254                         for (col=0; col< xsize ; )      {
255                                 if (cfread( &data, 1, 1, PCXfile )!=1 ) {
256                                         cfclose( PCXfile );
257                                         return PCX_ERROR_READING;
258                                 }
259                                 if ((data & 0xC0) == 0xC0)     {
260                                         count =  data & 0x3F;
261                                         if (cfread( &data, 1, 1, PCXfile )!=1 ) {
262                                                 cfclose( PCXfile );
263                                                 return PCX_ERROR_READING;
264                                         }
265                     pix15 = pa_clut[data];
266                     for(i = 0; i != count; ++i) pixdata2[i] = pix15;
267                     pixdata2 += count;
268                                         col += count;
269                                 } else {
270                     *pixdata2++ = pa_clut[data];
271                                         col++;
272                                 }
273                         }
274         }
275         pa_restore_clut();
276                   PA_DFX (pa_swap_buffer());
277         PA_DFX (pa_set_frontbuffer_current());
278
279 #endif
280         } else {
281                 for (row=0; row< ysize ; row++)      {
282                         for (col=0; col< xsize ; )      {
283                                 if (cfread( &data, 1, 1, PCXfile )!=1 ) {
284                                         cfclose( PCXfile );
285                                         return PCX_ERROR_READING;
286                                 }
287                                 if ((data & 0xC0) == 0xC0)     {
288                                         count =  data & 0x3F;
289                                         if (cfread( &data, 1, 1, PCXfile )!=1 ) {
290                                                 cfclose( PCXfile );
291                                                 return PCX_ERROR_READING;
292                                         }
293                                         for (i=0;i<count;i++)
294                                                 gr_bm_pixel( bmp, col+i, row, data );
295                                         col += count;
296                                 } else {
297                                         gr_bm_pixel( bmp, col, row, data );
298                                         col++;
299                                 }
300                         }
301                 }
302         }
303
304         // Read the extended palette at the end of PCX file
305         if ( palette != NULL )  {
306                 // Read in a character which should be 12 to be extended palette file
307                 if (cfread( &data, 1, 1, PCXfile )==1)  {
308                         if ( data == 12 )       {
309                                 if (cfread(palette,768, 1, PCXfile)!=1) {
310                                         cfclose( PCXfile );
311                                         return PCX_ERROR_READING;
312                                 }
313                                 for (i=0; i<768; i++ )
314                                         palette[i] >>= 2;
315 #ifdef MACINTOSH
316                                 for (i = 0; i < 3; i++) {
317                                         data = palette[i];
318                                         palette[i] = palette[765+i];
319                                         palette[765+i] = data;
320                                 }
321 #endif
322                         }
323                 } else {
324                         cfclose( PCXfile );
325                         return PCX_ERROR_NO_PALETTE;
326                 }
327         }
328         cfclose(PCXfile);
329         return PCX_ERROR_NONE;
330 }
331
332 int pcx_write_bitmap( char * filename, grs_bitmap * bmp, ubyte * palette )
333 {
334         int retval;
335         int i;
336         ubyte data;
337         PCXHeader header;
338         CFILE *PCXfile;
339
340         memset( &header, 0, PCXHEADER_SIZE );
341
342         header.Manufacturer = 10;
343         header.Encoding = 1;
344         header.Nplanes = 1;
345         header.BitsPerPixel = 8;
346         header.Version = 5;
347         header.Xmax = bmp->bm_w-1;
348         header.Ymax = bmp->bm_h-1;
349         header.BytesPerLine = bmp->bm_w;
350
351         PCXfile = cfopen(filename, "wb");
352         if ( !PCXfile )
353                 return PCX_ERROR_OPENING;
354
355         if (cfwrite(&header, PCXHEADER_SIZE, 1, PCXfile) != 1)
356         {
357                 cfclose(PCXfile);
358                 return PCX_ERROR_WRITING;
359         }
360
361         for (i=0; i<bmp->bm_h; i++ )    {
362                 if (!pcx_encode_line( &bmp->bm_data[bmp->bm_rowsize*i], bmp->bm_w, PCXfile ))   {
363                         cfclose(PCXfile);
364                         return PCX_ERROR_WRITING;
365                 }
366         }
367
368         // Mark an extended palette
369         data = 12;
370         if (cfwrite(&data, 1, 1, PCXfile) != 1)
371         {
372                 cfclose(PCXfile);
373                 return PCX_ERROR_WRITING;
374         }
375
376         // Write the extended palette
377         for (i=0; i<768; i++ )
378                 palette[i] <<= 2;
379
380         retval = cfwrite(palette, 768, 1, PCXfile);
381
382         for (i=0; i<768; i++ )
383                 palette[i] >>= 2;
384
385         if (retval !=1) {
386                 cfclose(PCXfile);
387                 return PCX_ERROR_WRITING;
388         }
389
390         cfclose(PCXfile);
391         return PCX_ERROR_NONE;
392
393 }
394
395 // returns number of bytes written into outBuff, 0 if failed
396 int pcx_encode_line(ubyte *inBuff, int inLen, CFILE *fp)
397 {
398         ubyte this, last;
399         int srcIndex, i;
400         register int total;
401         register ubyte runCount;        // max single runlength is 63
402         total = 0;
403         last = *(inBuff);
404         runCount = 1;
405
406         for (srcIndex = 1; srcIndex < inLen; srcIndex++) {
407                 this = *(++inBuff);
408                 if (this == last)       {
409                         runCount++;                     // it encodes
410                         if (runCount == 63)     {
411                                 if (!(i=pcx_encode_byte(last, runCount, fp)))
412                                         return(0);
413                                 total += i;
414                                 runCount = 0;
415                         }
416                 } else {        // this != last
417                         if (runCount)   {
418                                 if (!(i=pcx_encode_byte(last, runCount, fp)))
419                                         return(0);
420                                 total += i;
421                         }
422                         last = this;
423                         runCount = 1;
424                 }
425         }
426
427         if (runCount)   {               // finish up
428                 if (!(i=pcx_encode_byte(last, runCount, fp)))
429                         return 0;
430                 return total + i;
431         }
432         return total;
433 }
434
435 // subroutine for writing an encoded byte pair
436 // returns count of bytes written, 0 if error
437 int pcx_encode_byte(ubyte byt, ubyte cnt, CFILE *fid)
438 {
439         if (cnt) {
440                 if ( (cnt==1) && (0xc0 != (0xc0 & byt)) )       {
441                         if(EOF == cfputc((int)byt, fid))
442                                 return 0;       // disk write error (probably full)
443                         return 1;
444                 } else {
445                         if(EOF == cfputc((int)0xC0 | cnt, fid))
446                                 return 0;       // disk write error
447                         if(EOF == cfputc((int)byt, fid))
448                                 return 0;       // disk write error
449                         return 2;
450                 }
451         }
452         return 0;
453 }
454
455 //text for error messges
456 char pcx_error_messages[] = {
457         "No error.\0"
458         "Error opening file.\0"
459         "Couldn't read PCX header.\0"
460         "Unsupported PCX version.\0"
461         "Error reading data.\0"
462         "Couldn't find palette information.\0"
463         "Error writing data.\0"
464 };
465
466
467 //function to return pointer to error message
468 char *pcx_errormsg(int error_number)
469 {
470         char *p = pcx_error_messages;
471
472         while (error_number--) {
473
474                 if (!p) return NULL;
475
476                 p += strlen(p)+1;
477
478         }
479
480         return p;
481
482 }
483
484 // fullscreen loading, 10/14/99 Jan Bobrowski
485
486 int pcx_read_fullscr(char * filename, ubyte * palette)
487 {
488         int pcx_error;
489         grs_bitmap bm;
490         gr_init_bitmap_data(&bm);
491         pcx_error = pcx_read_bitmap(filename, &bm, BM_LINEAR, palette);
492         if (pcx_error == PCX_ERROR_NONE) {
493 #ifdef OGL
494                 gr_palette_load(palette);
495 #endif
496                 show_fullscr(&bm);
497         }
498         gr_free_bitmap_data(&bm);
499         return pcx_error;
500 }