2 * $Logfile: /Freespace2/code/TgaUtils/TgaUtils.cpp $
9 * Revision 1.2 2002/05/07 03:16:52 theoddone33
10 * The Great Newline Fix
12 * Revision 1.1.1.1 2002/05/03 03:28:11 root
16 * 7 7/13/99 1:16p Dave
17 * 32 bit support. Whee!
19 * 6 3/20/99 3:46p Dave
20 * Added support for model-based background nebulae. Added 3 new
23 * 5 12/03/98 9:39a Dave
24 * Removed bogus tga code assert?
26 * 4 12/02/98 5:47p Dave
27 * Put in interface xstr code. Converted barracks screen to new format.
29 * 3 12/01/98 5:54p Dave
30 * Simplified the way pixel data is swizzled. Fixed tga bitmaps to work
31 * properly in D3D and Glide.
33 * 2 12/01/98 4:46p Dave
34 * Put in targa bitmap support (16 bit).
53 #define MAX_TARGA_RUN_LENGTH_PACKET 128
54 #define TARGA_HEADER_LENGTH 18
55 #define ULORIGIN (header.image_descriptor & 0x20)
63 typedef struct targa_header {
75 ubyte image_descriptor;
85 // copy from one pixel buffer to another
87 // to - pointer to dest. buffet
88 // from - pointer to source buffer
89 // pixels - number of pixels to copy
90 // fromsize - source pixel size
91 // tosize - dest. pixel size
93 // returns - number of pixels copied to destination
95 static int targa_copy_data(char *to, char *from, int pixels, int fromsize, int tosize)
97 if ( (fromsize == 2) && (tosize==2) ) {
98 // Flip the alpha bit on 1555 format
101 src = (ushort *)from;
102 dst = (ushort *)from;
103 for (int i=0; i<pixels; i++ ) {
104 *dst++ = (ushort)((*src++) ^ 0x8000); // Flip the transparency bit
106 return tosize*pixels;
107 } else if ( (fromsize == 2) && (tosize == 3) ) {
110 src = (ushort *)from;
111 for (int i=0; i<pixels; i++ ) {
112 ushort pixel = *src++;
114 *to++ = (ubyte)((pixel & 0x1f) * 8);
115 *to++ = (ubyte)(((pixel >> 5) & 63) * 4);
116 *to++ = (ubyte)(((pixel >> 11) & 0x1f) * 8);
118 return tosize*pixels;
120 Assert(fromsize == tosize);
121 memcpy(to, from, pixels * fromsize);
122 return tosize*pixels;
126 // targa_pixels_equal -- Test if two pixels are identical
128 // pix1 - first pixel data
129 // pix2 - second pixel data
130 // pixbytes - number of bytes per pixel
132 // returns - 0 if No Match, else 1 if Match
133 static int targa_pixels_equal(char *pix1, char *pix2, int pixbytes)
136 if ( *pix1++ != *pix2++ ) {
139 } while ( --pixbytes > 0 );
144 // Perform targa RLE on the input data
146 // out - Buffer to write it out to
147 // in - Buffer to compress
148 // outsize - Number of bytes in output buffer
149 // pixsize - Number of bytes in input pixel
150 // bytecount - Number of bytes input
152 // returns - size of compressed data
154 int targa_compress(char *out, char *in, int outsize, int pixsize, int bytecount)
156 int pixcount; // number of pixels in the current packet
157 char *inputpixel=NULL; // current input pixel position
158 char *matchpixel=NULL; // pixel value to match for a run
159 char *flagbyte=NULL; // location of last flag byte to set
160 int rlcount; // current count in r.l. string
161 int rlthresh; // minimum valid run length
162 char *copyloc; // location to begin copying at
164 // set the threshold -- the minimum valid run length
167 rlthresh = 2; // for 8bpp, require a 2 pixel span before rle'ing
172 // set the first pixel up
174 flagbyte = out; // place to put next flag if run
180 // loop till data processing complete
183 // if we have accumulated a 128-byte packet, process it
184 if ( pixcount == 129 ) {
187 // set the run flag if this is a run
189 if ( rlcount >= rlthresh ) {
194 // copy the data into place
196 flagbyte += targa_copy_data(flagbyte, copyloc, pixcount-1, pixsize, outsize);
199 // set up for next packet
203 // if zeroth byte, handle as special case
204 if ( pixcount == 1 ) {
206 copyloc = inputpixel; /* point to 1st guy in packet */
207 matchpixel = inputpixel; /* set pointer to pix to match */
209 inputpixel += pixsize;
213 // assembling a packet -- look at next pixel
215 // current pixel == match pixel?
216 if ( targa_pixels_equal(inputpixel, matchpixel, outsize) ) {
218 // establishing a run of enough length to
219 // save space by doing it
220 // -- write the non-run length packet
221 // -- start run-length packet
223 if ( ++rlcount == rlthresh ) {
225 // close a non-run packet
227 if ( pixcount > (rlcount+1) ) {
228 // write out length and do not set run flag
230 *flagbyte++ = (char)(pixcount - 2 - rlthresh);
232 flagbyte += targa_copy_data(flagbyte, copyloc, (pixcount-1-rlcount), pixsize, outsize);
234 copyloc = inputpixel;
235 pixcount = rlcount + 1;
240 // no match -- either break a run or continue without one
241 // if a run exists break it:
242 // write the bytes in the string (outsize+1)
243 // start the next string
245 if ( rlcount >= rlthresh ) {
247 *flagbyte++ = (char)(0x80 | rlcount);
248 flagbyte += targa_copy_data(flagbyte, copyloc, 1, pixsize, outsize);
253 // not a match and currently not a run
254 // - save the current pixel
255 // - reset the run-length flag
257 matchpixel = inputpixel;
261 inputpixel += pixsize;
262 } while ( inputpixel < (in + bytecount));
264 // quit this buffer without loosing any data
266 if ( --pixcount >= 1 ) {
267 *flagbyte = (char)(pixcount - 1);
268 if ( rlcount >= rlthresh ) {
273 // copy the data into place
275 flagbyte += targa_copy_data(flagbyte, copyloc, pixcount, pixsize, outsize);
277 return(flagbyte-out);
280 // Reads a pixel of the specified bytes_per_pixel into memory and
281 // returns the number of bytes read into memory.
282 // NOTE : for Freespace2, this also swizzles data into the proper screen (NOT texture) format - just like
285 // dst - A pointer to the destination. Must be at least 4 bytes long.
286 // targa_file - The file to read from.
287 // bytes_per_pixel - The bytes per pixel of the file.
288 // dest_size - bytes per pixel in destination (1 or 2. 1 == 8 bit paletted)
290 // returns - Number of byte read into memory
292 static void targa_read_pixel( int num_pixels, ubyte **dst, ubyte **src, int bytes_per_pixel, int dest_size )
300 for(idx=0; idx<num_pixels; idx++){
301 // stuff the 16 bit pixel
302 memcpy(&pixel, *src, bytes_per_pixel);
304 // if the pixel is transparent, make it so...
305 if(((pixel & 0x7c00) == 0) && ((pixel & 0x03e0) == 0x03e0) && ((pixel & 0x001f) == 0)){
309 bm_set_components((ubyte*)&pixel, &r, &g, &b, &al);
311 // get the 8 bit r, g, and b values
312 r = (ubyte)(((pixel & 0x7c00) >> 10) * 8);
313 g = (ubyte)(((pixel & 0x03e0) >> 5) * 8);
314 b = (ubyte)((pixel & 0x001f) * 8);
317 // now stuff these back, swizzling properly
319 bm_set_components((ubyte*)&pixel, &r, &g, &b, &al);
322 // 16 bit destination
324 // stuff the final pixel
325 memcpy( *dst, &pixel, bytes_per_pixel );
329 pal_index = (ubyte)palette_find((int)r, (int)g, (int)b);
335 (*src) += bytes_per_pixel;
341 // External Functions
345 // Reads header information from the targa file into the bitmap pointer
347 // filename - name of the targa bitmap file
348 // w - (output) width of the bitmap
349 // h - (output) height of the bitmap
350 // bpp - (output) bits per pixel of the bitmap
352 // returns - TARGA_ERROR_NONE if successful, otherwise error code
354 int targa_read_header(char *real_filename, int *w, int *h, int *bpp, ubyte *palette )
358 char filename[MAX_FILENAME_LEN];
360 strcpy( filename, real_filename );
361 char *p = strchr( filename, '.' );
363 strcat( filename, ".tga" );
365 targa_file = cfopen( filename , "rb" );
367 return TARGA_ERROR_READING;
370 header.id_length = cfread_ubyte(targa_file);
371 // header.id_length=targa_file.read_char();
373 header.color_map_type = cfread_ubyte(targa_file);
374 // header.color_map_type=targa_file.read_char();
376 header.image_type = cfread_ubyte(targa_file);
377 // header.image_type=targa_file.read_char();
379 header.cmap_start = cfread_short(targa_file);
380 // header.cmap_start=targa_file.read_short();
382 header.cmap_length = cfread_short(targa_file);
383 // header.cmap_length=targa_file.read_short();
385 header.cmap_depth = cfread_ubyte(targa_file);
386 // header.cmap_depth=targa_file.read_char();
388 header.xoffset = cfread_short(targa_file);
389 // header.xoffset=targa_file.read_short();
391 header.yoffset = cfread_short(targa_file);
392 // header.yoffset=targa_file.read_short();
394 header.width = cfread_short(targa_file);
395 // header.width=targa_file.read_short();
397 header.height = cfread_short(targa_file);
398 // header.height=targa_file.read_short();
400 header.pixel_depth = cfread_ubyte(targa_file);
401 // header.pixel_depth=targa_file.read_char();
403 header.image_descriptor = cfread_ubyte(targa_file);
404 // header.image_descriptor=targa_file.read_char();
411 *bpp = header.pixel_depth;
413 // only support 16 bit pixels
416 return TARGA_ERROR_READING;
419 return TARGA_ERROR_NONE;
422 // Uncompresses some RLE'd TGA data
424 // dst: pointer uncompressed destination.
425 // src: pointer to source rle'd data.
426 // bitmap_width: how many pixels to uncompress.
427 // bytes_per_pixel: bytes per pixel of the data.
429 // returns: number of input bytes processed.
431 int targa_uncompress( ubyte *dst, ubyte *src, int bitmap_width, int bytes_per_pixel, int dest_size )
433 ubyte *pixdata = dst;
434 ubyte *src_pixels = src;
436 int pixel_count = 0; // Initialize pixel counter
438 // Main decoding loop
439 while (pixel_count < bitmap_width ) {
441 // Get the pixel count
442 int run_count = *src_pixels++;
444 // Make sure writing this next run will not overflow the buffer
445 Assert(pixel_count + (run_count & 0x7f) + 1 <= bitmap_width );
447 // If the run is encoded...
448 if ( run_count & 0x80 ) {
449 run_count &= ~0x80; // Mask off the upper bit
451 // Update total pixel count
452 pixel_count += (run_count + 1);
454 ubyte pixel_value[4]; // temporary
455 ubyte *tmp = pixel_value;
456 targa_read_pixel( 1, &tmp, &src_pixels, bytes_per_pixel, dest_size );
458 // Write remainder of pixel run to buffer 'run_count' times
460 memcpy( pixdata, pixel_value, dest_size );
461 pixdata += dest_size;
462 } while (run_count--);
464 } else { // ...the run is unencoded (raw)
465 // Update total pixel count
466 pixel_count += (run_count + 1);
468 // Read run_count pixels
469 targa_read_pixel(run_count+1, &pixdata, &src_pixels, bytes_per_pixel, dest_size );
473 Assert( pixel_count == bitmap_width );
475 return src_pixels - src;
479 // Loads a Targa bitmap
481 // filename - name of the targa file to load
482 // image_data - allocated storage for the bitmap
484 // returns - true if succesful, false otherwise
486 int targa_read_bitmap(char *real_filename, ubyte *image_data, ubyte *palette, int dest_size)
488 Assert(real_filename);
491 char filename[MAX_FILENAME_LEN];
495 strcpy( filename, real_filename );
496 char *p = strchr( filename, '.' );
498 strcat( filename, ".tga" );
500 targa_file = cfopen( filename , "rb" );
502 return TARGA_ERROR_READING;
505 header.id_length = cfread_ubyte(targa_file);
506 // header.id_length=targa_file.read_char();
508 header.color_map_type = cfread_ubyte(targa_file);
509 // header.color_map_type=targa_file.read_char();
511 header.image_type = cfread_ubyte(targa_file);
512 // header.image_type=targa_file.read_char();
514 header.cmap_start = cfread_short(targa_file);
515 // header.cmap_start=targa_file.read_short();
517 header.cmap_length = cfread_short(targa_file);
518 // header.cmap_length=targa_file.read_short();
520 header.cmap_depth = cfread_ubyte(targa_file);
521 // header.cmap_depth=targa_file.read_char();
523 header.xoffset = cfread_short(targa_file);
524 // header.xoffset=targa_file.read_short();
526 header.yoffset = cfread_short(targa_file);
527 // header.yoffset=targa_file.read_short();
529 header.width = cfread_short(targa_file);
530 // header.width=targa_file.read_short();
532 header.height = cfread_short(targa_file);
533 // header.height=targa_file.read_short();
535 header.pixel_depth = cfread_ubyte(targa_file);
536 // header.pixel_depth=targa_file.read_char();
538 header.image_descriptor = cfread_ubyte(targa_file);
539 // header.image_descriptor=targa_file.read_char();
541 int bytes_per_pixel = (header.pixel_depth>>3);
542 // we're only allowing 2 bytes per pixel (16 bit compressed)
543 Assert(bytes_per_pixel == 2);
544 if(bytes_per_pixel != 2){
546 return TARGA_ERROR_READING;
550 if ( header.image_descriptor & 0x10 ) {
556 if ( header.image_descriptor & 0x20 ) {
562 // only accept 16 bit, compressed
563 if(header.pixel_depth!=16) {
565 return TARGA_ERROR_READING;
569 char test=char(header.image_descriptor&0xF);
570 if((test!=8)&&(test!=0)) {
572 return TARGA_ERROR_READING;
576 if((header.image_type!=1)&&(header.image_type!=2)&&(header.image_type!=9)&&(header.image_type!=10)) {
578 return TARGA_ERROR_READING;
581 // skip the Image ID field -- should not be needed
582 if(header.id_length>0) {
583 if(cfseek(targa_file, header.id_length, CF_SEEK_SET)) {
585 return TARGA_ERROR_READING;
589 // read palette if one present.
591 if (header.color_map_type) { // non-zero indicates palette in the file
594 // Determine the size of the color map
595 Assert(header.cmap_depth==24);
596 Assert(header.cmap_length<=256);
599 // Read the color map data
601 for (i = 0; i < header.cmap_length; i++) {
602 r = cfread_ubyte(targa_file);
603 g = cfread_ubyte(targa_file);
604 b = cfread_ubyte(targa_file);
612 // Fill out with black.
614 for (; i < 256; i++) {
622 int bytes_remaining = cfilelength(targa_file)-cftell(targa_file);
624 Assert(bytes_remaining>0);
626 ubyte *fileptr = (ubyte*)malloc(bytes_remaining);
629 return TARGA_ERROR_READING;
632 ubyte *src_pixels = fileptr;
634 cfread(fileptr, bytes_remaining, 1, targa_file);
636 int rowsize = header.width * bytes_per_pixel;
638 if ( (header.image_type == 1) || (header.image_type == 2) || (header.image_type == 3) ) {
641 for (int i = 0; i < header.height; i++) {
645 pixptr = image_data + i * rowsize;
647 pixptr = image_data + ((header.height - i - 1) * rowsize);
650 targa_read_pixel(header.width, &pixptr, &src_pixels, bytes_per_pixel, dest_size );
653 } else if (header.image_type == 9 || header.image_type == 10 || header.image_type == 11) {
655 // the following handles RLE'ed targa data.
657 // targas encoded by the scanline -- loop on the height
658 for (int i = 0; i < header.height; i++) {
662 pixdata = image_data + i * rowsize;
664 pixdata = image_data + ((header.height - i - 1) * rowsize);
667 src_pixels += targa_uncompress( pixdata, src_pixels, header.width, bytes_per_pixel, dest_size );
676 return TARGA_ERROR_NONE;
679 // Write out a Targa format bitmap. Always writes out a top-up bitmap.
680 // JAS: DOESN'T WORK WITH 8-BPP PALETTES
682 // filename: name of the Targa file, .tga extension added if not passed in
683 // data: raw image data
684 // w: width of the bitmap in pixels
685 // h: height of the bitmap in pixels
686 // bpp: bits per pixel of the bitmap
688 // returns: 0 if successful, otherwise -1
690 int targa_write_bitmap(char *real_filename, ubyte *data, ubyte *palette, int w, int h, int bpp)
693 char filename[MAX_FILENAME_LEN];
695 int bytes_per_pixel = BYTES_PER_PIXEL(bpp);
698 strcpy( filename, real_filename );
699 char *p = strchr( filename, '.' );
701 strcat( filename, ".tga" );
703 f = cfopen( filename , "wb" );
705 return TARGA_ERROR_READING;
708 // Write the TGA header
710 // f.write_ubyte(0); // IDLength
713 //f.write_ubyte(0); // ColorMapType
715 cfwrite_ubyte(10, f);
716 // f.write_ubyte(10); // image_type: 2 = 24bpp, uncompressed, 10=24bpp rle compressed
718 cfwrite_ushort(0, f);
719 // f.write_ushort(0); // CMapStart
721 cfwrite_ushort(0, f);
722 // f.write_ushort(0); // CMapLength
725 // f.write_ubyte(0); // CMapDepth
727 cfwrite_ushort(0, f);
728 // f.write_ushort(0); // XOffset
730 cfwrite_ushort(0, f);
731 // f.write_ushort(0); // YOffset
733 cfwrite_ushort((ushort)w, f);
734 // f.write_ushort((ushort)w); // Width
736 cfwrite_ushort((ushort)h, f);
737 // f.write_ushort((ushort)h); // Height
739 cfwrite_ubyte(24, f);
740 // f.write_ubyte(24); // pixel_depth
742 cfwrite_ubyte(0x20, f);
743 // f.write_ubyte(0x20); // ImageDesc ( 0x20 = Origin at upper left )
745 ubyte *compressed_data;
746 compressed_data = (ubyte*)malloc(w * h * bytes_per_pixel);
747 Assert(compressed_data);
748 if(compressed_data == NULL){
753 int compressed_data_len;
754 compressed_data_len = targa_compress((char*)compressed_data, (char*)data, 3, bytes_per_pixel, w * h * bytes_per_pixel);
755 if (compressed_data_len < 0) {
756 free(compressed_data);
761 cfwrite(compressed_data, compressed_data_len, 1, f);