2 * Copyright (C) Volition, Inc. 1999. All rights reserved.
4 * All source code herein is the property of Volition, Inc. You may not sell
5 * or otherwise commercially exploit the source or things you created based on
10 * $Logfile: /Freespace2/code/PcxUtils/pcxutils.cpp $
15 * code to deal with pcx files
18 * Revision 1.4 2004/06/11 02:05:16 tigital
19 * byte-swapping changes for bigendian systems
21 * Revision 1.3 2002/06/09 04:41:25 relnev
22 * added copyright header
24 * Revision 1.2 2002/05/07 03:16:48 theoddone33
25 * The Great Newline Fix
27 * Revision 1.1.1.1 2002/05/03 03:28:10 root
31 * 11 8/10/99 6:54p Dave
32 * Mad optimizations. Added paging to the nebula effect.
34 * 10 7/13/99 1:16p Dave
35 * 32 bit support. Whee!
37 * 9 2/05/99 12:52p Dave
38 * Fixed Glide nondarkening textures.
40 * 8 2/03/99 6:06p Dave
41 * Groundwork for FS2 PXO usertracker support. Gametracker support next.
43 * 7 2/03/99 11:44a Dave
44 * Fixed d3d transparent textures.
46 * 6 12/01/98 5:54p Dave
47 * Simplified the way pixel data is swizzled. Fixed tga bitmaps to work
48 * properly in D3D and Glide.
50 * 5 12/01/98 4:46p Dave
51 * Put in targa bitmap support (16 bit).
53 * 4 12/01/98 8:06a Dave
54 * Temporary checkin to fix some texture transparency problems in d3d.
56 * 3 11/30/98 1:07p Dave
57 * 16 bit conversion, first run.
59 * 2 10/07/98 10:53a Dave
62 * 1 10/07/98 10:50a Dave
64 * 16 1/19/98 11:37p Lawrance
65 * Fixing Optimization build warnings
67 * 15 9/09/97 3:39p Sandeep
68 * warning level 4 bugs
70 * 14 9/03/97 4:32p John
71 * changed bmpman to only accept ani and pcx's. made passing .pcx or .ani
72 * to bm_load functions not needed. Made bmpman keep track of palettes
73 * for bitmaps not mapped into game palettes.
75 * 13 7/20/97 6:59p Lawrance
76 * fixed bug that was writing out PCX files with an extra bogus line
78 * 12 6/05/97 6:07p John
81 * 11 6/05/97 3:59p John
82 * Fixed a bug in PCX reader
84 * 10 2/25/97 12:06p John
85 * fixed a pcx potential bug.
87 * 9 2/25/97 12:03p John
88 * fixed a pcx reading bug.
90 * 8 2/20/97 4:18p John
91 * fixed bug reading odd-width pcx files
93 * 7 11/26/96 9:28a Allender
94 * get palette info when getting pcx info
96 * 6 11/18/96 12:36p John
97 * Added code to dump screen to a PCX file.
99 * 5 11/13/96 4:51p Allender
100 * started overhaul of bitmap manager. bm_load no longer actually load
101 * the data, only the info for the bitmap. Locking the bitmap now forces
102 * load when no data present (or will if bpp changes)
108 #include "pcxutils.h"
112 /* PCX Header data type */
124 ubyte ColorMap[16][3];
131 // reads header information from the PCX file into the bitmap pointer
132 int pcx_read_header(char *real_filename, int *w, int *h, ubyte *pal )
136 char filename[MAX_FILENAME_LEN];
138 SDL_strlcpy( filename, real_filename, SDL_arraysize(filename) );
139 char *p = SDL_strchr( filename, '.' );
141 SDL_strlcat( filename, ".pcx", SDL_arraysize(filename) );
143 PCXfile = cfopen( filename , "rb" );
145 return PCX_ERROR_OPENING;
147 // read 128 char PCX header
148 if (cfread( &header, sizeof(PCXHeader), 1, PCXfile )!=1) {
150 return PCX_ERROR_NO_HEADER;
152 header.Xmin = INTEL_SHORT( header.Xmin );
153 header.Ymin = INTEL_SHORT( header.Ymin );
154 header.Xmax = INTEL_SHORT( header.Xmax );
155 header.Ymax = INTEL_SHORT( header.Ymax );
156 header.Hdpi = INTEL_SHORT( header.Hdpi );
157 header.Vdpi = INTEL_SHORT( header.Vdpi );
158 for ( int i=0; i<16; i++ ){
159 for ( int j=0; j<3; j++){
160 header.ColorMap[i][j] = INTEL_INT( header.ColorMap[i][j] );
163 header.BytesPerLine = INTEL_SHORT( header.BytesPerLine );
164 for ( int i=0; i<60; i++ ){
165 header.filler[i] = INTEL_INT( header.filler[i] );
168 // Is it a 256 color PCX file?
169 if ((header.Manufacturer != 10)||(header.Encoding != 1)||(header.Nplanes != 1)||(header.BitsPerPixel != 8)||(header.Version != 5)) {
171 return PCX_ERROR_WRONG_VERSION;
174 if (w) *w = header.Xmax - header.Xmin + 1;
175 if (h) *h = header.Ymax - header.Ymin + 1;
178 cfseek( PCXfile, -768, CF_SEEK_END );
179 cfread( pal, 3, 256, PCXfile );
183 return PCX_ERROR_NONE;
186 // static ubyte Pcx_load[1024*768 + 768 + sizeof(PCXHeader)];
187 // int Pcx_load_offset = 0;
188 // int Pcx_load_size = 0;
190 // #define GET_BUF() do { buffer = &Pcx_load[Pcx_load_offset]; if(Pcx_load_offset + buffer_size > Pcx_load_size) { buffer_size = Pcx_load_size - Pcx_load_offset; } } while(0);
191 int pcx_read_bitmap_8bpp( char * real_filename, ubyte *org_data, ubyte *palette )
195 int row, col, count, xsize, ysize;
197 int buffer_size, buffer_pos;
200 char filename[MAX_FILENAME_LEN];
202 SDL_strlcpy( filename, real_filename, SDL_arraysize(filename) );
203 char *p = SDL_strchr( filename, '.' );
205 SDL_strlcat( filename, ".pcx", SDL_arraysize(filename) );
207 PCXfile = cfopen( filename , "rb" );
209 return PCX_ERROR_OPENING;
211 // read 128 char PCX header
212 if (cfread( &header, sizeof(PCXHeader), 1, PCXfile )!=1) {
214 return PCX_ERROR_NO_HEADER;
216 header.Xmin = INTEL_SHORT( header.Xmin );
217 header.Ymin = INTEL_SHORT( header.Ymin );
218 header.Xmax = INTEL_SHORT( header.Xmax );
219 header.Ymax = INTEL_SHORT( header.Ymax );
220 header.Hdpi = INTEL_SHORT( header.Hdpi );
221 header.Vdpi = INTEL_SHORT( header.Vdpi );
223 header.BytesPerLine = INTEL_SHORT( header.BytesPerLine );
225 // Is it a 256 color PCX file?
226 if ((header.Manufacturer != 10)||(header.Encoding != 1)||(header.Nplanes != 1)||(header.BitsPerPixel != 8)||(header.Version != 5)) {
228 return PCX_ERROR_WRONG_VERSION;
231 // Find the size of the image
232 xsize = header.Xmax - header.Xmin + 1;
233 ysize = header.Ymax - header.Ymin + 1;
235 // Read the extended palette at the end of PCX file
236 // Read in a character which should be 12 to be extended palette file
238 cfseek( PCXfile, -768, CF_SEEK_END );
239 cfread( palette, 3, 256, PCXfile );
241 for ( int i=0; i<256; i++ ){ //tigital
242 palette[i] = INTEL_INT( palette[i] );
244 cfseek( PCXfile, sizeof(PCXHeader), CF_SEEK_SET );
249 // SDL_assert( buffer_size == 1024 ); // AL: removed to avoid optimized warning 'unreachable code'
250 buffer_size = cfread( buffer, 1, buffer_size, PCXfile );
254 for (row=0; row<ysize;row++) {
256 for (col=0; col<header.BytesPerLine;col++) {
258 data = buffer[buffer_pos++];
259 if ( buffer_pos == buffer_size ) {
260 buffer_size = cfread( buffer, 1, buffer_size, PCXfile );
261 SDL_assert( buffer_size > 0 );
264 if ((data & 0xC0) == 0xC0) {
266 data = buffer[buffer_pos++];
267 if ( buffer_pos == buffer_size ) {
268 buffer_size = cfread( buffer, 1, buffer_size, PCXfile );
269 SDL_assert( buffer_size > 0 );
283 return PCX_ERROR_NONE;
286 int pcx_read_bitmap_16bpp( char * real_filename, ubyte *org_data )
290 int row, col, count, xsize, ysize;
292 int buffer_size, buffer_pos;
295 char filename[MAX_FILENAME_LEN];
300 SDL_strlcpy( filename, real_filename, SDL_arraysize(filename) );
301 char *p = SDL_strchr( filename, '.' );
303 SDL_strlcat( filename, ".pcx", SDL_arraysize(filename) );
305 PCXfile = cfopen( filename , "rb" );
307 return PCX_ERROR_OPENING;
310 // read 128 char PCX header
311 if (cfread( &header, sizeof(PCXHeader), 1, PCXfile )!=1) {
313 return PCX_ERROR_NO_HEADER;
316 header.Xmin = INTEL_SHORT( header.Xmin );
317 header.Ymin = INTEL_SHORT( header.Ymin );
318 header.Xmax = INTEL_SHORT( header.Xmax );
319 header.Ymax = INTEL_SHORT( header.Ymax );
320 header.Hdpi = INTEL_SHORT( header.Hdpi );
321 header.Vdpi = INTEL_SHORT( header.Vdpi );
323 header.BytesPerLine = INTEL_SHORT( header.BytesPerLine );
325 // Is it a 256 color PCX file?
326 if ((header.Manufacturer != 10)||(header.Encoding != 1)||(header.Nplanes != 1)||(header.BitsPerPixel != 8)||(header.Version != 5)) {
328 return PCX_ERROR_WRONG_VERSION;
331 // Find the size of the image
332 xsize = header.Xmax - header.Xmin + 1;
333 ysize = header.Ymax - header.Ymin + 1;
335 // Read the extended palette at the end of PCX file
336 // Read in a character which should be 12 to be extended palette file
338 cfseek( PCXfile, -768, CF_SEEK_END );
339 cfread( palette, 3, 256, PCXfile );
340 cfseek( PCXfile, sizeof(PCXHeader), CF_SEEK_SET );
345 // SDL_assert( buffer_size == 1024 ); // AL: removed to avoid optimized warning 'unreachable code'
346 buffer_size = cfread( buffer, 1, buffer_size, PCXfile );
350 for (row=0; row<ysize;row++) {
352 for (col=0; col<header.BytesPerLine;col++) {
354 data = buffer[buffer_pos++];
355 if ( buffer_pos == buffer_size ) {
356 buffer_size = cfread( buffer, 1, buffer_size, PCXfile );
357 SDL_assert( buffer_size > 0 );
360 if ((data & 0xC0) == 0xC0) {
362 data = buffer[buffer_pos++];
363 if ( buffer_pos == buffer_size ) {
364 buffer_size = cfread( buffer, 1, buffer_size, PCXfile );
365 SDL_assert( buffer_size > 0 );
374 // stuff the 24 bit value
376 g = palette[data*3 + 1];
377 b = palette[data*3 + 2];
382 // if the color matches the transparent color, make it so
384 if((0 == (int)palette[data*3]) && (255 == (int)palette[data*3+1]) && (0 == (int)palette[data*3+2])){
391 bm_set_components((ubyte*)&bit_16, &r, &g, &b, &al);
394 *((ushort*)pixdata) = bit_16;
400 org_data += (xsize * 2);
403 return PCX_ERROR_NONE;
406 int pcx_read_bitmap_16bpp_aabitmap( char * real_filename, ubyte *org_data )
410 int row, col, count, xsize, ysize;
412 int buffer_size, buffer_pos;
415 char filename[MAX_FILENAME_LEN];
418 SDL_strlcpy( filename, real_filename, SDL_arraysize(filename) );
419 char *p = SDL_strchr( filename, '.' );
422 SDL_strlcat( filename, ".pcx", SDL_arraysize(filename) );
424 PCXfile = cfopen( filename , "rb" );
426 return PCX_ERROR_OPENING;
429 // read 128 char PCX header
430 if (cfread( &header, sizeof(PCXHeader), 1, PCXfile )!=1) {
432 return PCX_ERROR_NO_HEADER;
435 header.Xmin = INTEL_SHORT( header.Xmin );
436 header.Ymin = INTEL_SHORT( header.Ymin );
437 header.Xmax = INTEL_SHORT( header.Xmax );
438 header.Ymax = INTEL_SHORT( header.Ymax );
439 header.Hdpi = INTEL_SHORT( header.Hdpi );
440 header.Vdpi = INTEL_SHORT( header.Vdpi );
442 header.BytesPerLine = INTEL_SHORT( header.BytesPerLine );
444 // Is it a 256 color PCX file?
445 if ((header.Manufacturer != 10)||(header.Encoding != 1)||(header.Nplanes != 1)||(header.BitsPerPixel != 8)||(header.Version != 5)) {
447 return PCX_ERROR_WRONG_VERSION;
450 // Find the size of the image
451 xsize = header.Xmax - header.Xmin + 1;
452 ysize = header.Ymax - header.Ymin + 1;
454 // Read the extended palette at the end of PCX file
455 // Read in a character which should be 12 to be extended palette file
457 cfseek( PCXfile, -768, CF_SEEK_END );
458 cfread( palette, 3, 256, PCXfile );
459 cfseek( PCXfile, sizeof(PCXHeader), CF_SEEK_SET );
464 // SDL_assert( buffer_size == 1024 ); // AL: removed to avoid optimized warning 'unreachable code'
465 buffer_size = cfread( buffer, 1, buffer_size, PCXfile );
469 for (row=0; row<ysize;row++) {
471 for (col=0; col<header.BytesPerLine;col++) {
473 data = buffer[buffer_pos++];
474 if ( buffer_pos == buffer_size ) {
475 buffer_size = cfread( buffer, 1, buffer_size, PCXfile );
476 SDL_assert( buffer_size > 0 );
479 if ((data & 0xC0) == 0xC0) {
481 data = buffer[buffer_pos++];
482 if ( buffer_pos == buffer_size ) {
483 buffer_size = cfread( buffer, 1, buffer_size, PCXfile );
484 SDL_assert( buffer_size > 0 );
494 // memcpy(pixdata, &data, 2);
495 *((ushort*)pixdata) = (ushort)data;
502 org_data += (xsize * 2);
505 return PCX_ERROR_NONE;
508 int pcx_read_bitmap_16bpp_nondark( char * real_filename, ubyte *org_data )
512 int row, col, count, xsize, ysize;
514 int buffer_size, buffer_pos;
517 char filename[MAX_FILENAME_LEN];
522 SDL_strlcpy( filename, real_filename, SDL_arraysize(filename) );
523 char *p = SDL_strchr( filename, '.' );
525 SDL_strlcat( filename, ".pcx", SDL_arraysize(filename) );
527 PCXfile = cfopen( filename , "rb" );
529 return PCX_ERROR_OPENING;
532 // read 128 char PCX header
533 if (cfread( &header, sizeof(PCXHeader), 1, PCXfile )!=1) {
535 return PCX_ERROR_NO_HEADER;
537 header.Xmin = INTEL_SHORT( header.Xmin );
538 header.Ymin = INTEL_SHORT( header.Ymin );
539 header.Xmax = INTEL_SHORT( header.Xmax );
540 header.Ymax = INTEL_SHORT( header.Ymax );
541 header.Hdpi = INTEL_SHORT( header.Hdpi );
542 header.Vdpi = INTEL_SHORT( header.Vdpi );
543 header.BytesPerLine = INTEL_SHORT( header.BytesPerLine );
545 // Is it a 256 color PCX file?
546 if ((header.Manufacturer != 10)||(header.Encoding != 1)||(header.Nplanes != 1)||(header.BitsPerPixel != 8)||(header.Version != 5)) {
548 return PCX_ERROR_WRONG_VERSION;
551 // Find the size of the image
552 xsize = header.Xmax - header.Xmin + 1;
553 ysize = header.Ymax - header.Ymin + 1;
555 // Read the extended palette at the end of PCX file
556 // Read in a character which should be 12 to be extended palette file
558 cfseek( PCXfile, -768, CF_SEEK_END );
559 cfread( palette, 3, 256, PCXfile );
560 cfseek( PCXfile, sizeof(PCXHeader), CF_SEEK_SET );
565 // SDL_assert( buffer_size == 1024 ); // AL: removed to avoid optimized warning 'unreachable code'
566 buffer_size = cfread( buffer, 1, buffer_size, PCXfile );
570 for (row=0; row<ysize;row++) {
572 for (col=0; col<header.BytesPerLine;col++) {
574 data = buffer[buffer_pos++];
575 if ( buffer_pos == buffer_size ) {
576 buffer_size = cfread( buffer, 1, buffer_size, PCXfile );
577 SDL_assert( buffer_size > 0 );
580 if ((data & 0xC0) == 0xC0) {
582 data = buffer[buffer_pos++];
583 if ( buffer_pos == buffer_size ) {
584 buffer_size = cfread( buffer, 1, buffer_size, PCXfile );
585 SDL_assert( buffer_size > 0 );
594 // stuff the 24 bit value
596 g = palette[data*3 + 1];
597 b = palette[data*3 + 2];
599 // if this is a nondarkening texture
600 // if this color matches a nondarkening pixel color, set the alpha to high
602 if(palman_is_nondarkening(r, g, b)){
608 bm_set_components((ubyte*)&bit_16, &r, &g, &b, &al);
611 *((ushort*)pixdata) = bit_16;
617 org_data += (xsize * 2);
620 return PCX_ERROR_NONE;
623 // subroutine for writing an encoded byte pair
624 // returns count of bytes written, 0 if error
625 int pcx_encode_byte(ubyte byt, ubyte cnt, FILE * fid)
628 if ( (cnt==1) && (0xc0 != (0xc0 & byt)) ) {
629 if(EOF == putc((int)byt, fid))
630 return 0; // disk write error (probably full)
633 if(EOF == putc((int)0xC0 | cnt, fid))
634 return 0; // disk write error
635 if(EOF == putc((int)byt, fid))
636 return 0; // disk write error
643 // returns number of bytes written into outBuff, 0 if failed
644 int pcx_encode_line(ubyte *inBuff, int inLen, FILE * fp)
646 ubyte this_ptr, last;
650 register ubyte runCount; // max single runlength is 63
655 for (srcIndex = 1; srcIndex < inLen; srcIndex++) {
656 this_ptr = *(++inBuff);
657 if (this_ptr == last) {
658 runCount++; // it encodes
659 if (runCount == 63) {
660 i = pcx_encode_byte(last, runCount, fp);
667 } else { // this_ptr != last
669 i = pcx_encode_byte(last, runCount, fp);
680 if (runCount) { // finish up
681 i = pcx_encode_byte(last, runCount, fp);
691 int pcx_write_bitmap( char * real_filename, int w, int h, ubyte ** row_ptrs, ubyte * palette )
698 char filename[MAX_FILENAME_LEN];
700 SDL_strlcpy( filename, real_filename, SDL_arraysize(filename) );
701 char *p = SDL_strchr( filename, '.' );
703 SDL_strlcat( filename, ".pcx", SDL_arraysize(filename) );
705 memset( &header, 0, sizeof( PCXHeader ) );
707 header.Manufacturer = 10;
710 header.BitsPerPixel = 8;
712 header.Xmax = (short)(w-1);
713 header.Ymax = (short)(h-1);
716 header.BytesPerLine =(short)(w);
718 PCXfile = fopen( filename , "wb" );
720 return PCX_ERROR_OPENING;
722 if ( fwrite( &header, sizeof( PCXHeader ), 1, PCXfile ) != 1 ) {
724 return PCX_ERROR_WRITING;
727 for (i=0; i<h; i++ ) {
728 if (!pcx_encode_line( row_ptrs[i], w, PCXfile )) {
730 return PCX_ERROR_WRITING;
734 // Mark an extended palette
736 if (fwrite( &data, 1, 1, PCXfile )!=1) {
738 return PCX_ERROR_WRITING;
741 // Write the extended palette
742 // for (i=0; i<768; i++ )
745 retval = fwrite( palette, 768, 1, PCXfile );
747 // for (i=0; i<768; i++ )
752 return PCX_ERROR_WRITING;
756 return PCX_ERROR_NONE;
760 //text for error messges
761 char pcx_error_messages[] = {
763 "Error opening file.\0"
764 "Couldn't read PCX header.\0"
765 "Unsupported PCX version.\0"
766 "Error reading data.\0"
767 "Couldn't find palette information.\0"
768 "Error writing data.\0"
772 //function to return pointer to error message
773 char *pcx_errormsg(int error_number)
775 char *p = pcx_error_messages;
777 while (error_number--) {