2 * $Logfile: /Freespace2/code/TgaUtils/TgaUtils.cpp $
9 * Revision 1.1 2002/05/03 03:28:11 root
13 * 7 7/13/99 1:16p Dave
14 * 32 bit support. Whee!
16 * 6 3/20/99 3:46p Dave
17 * Added support for model-based background nebulae. Added 3 new
20 * 5 12/03/98 9:39a Dave
21 * Removed bogus tga code assert?
23 * 4 12/02/98 5:47p Dave
24 * Put in interface xstr code. Converted barracks screen to new format.
26 * 3 12/01/98 5:54p Dave
27 * Simplified the way pixel data is swizzled. Fixed tga bitmaps to work
28 * properly in D3D and Glide.
30 * 2 12/01/98 4:46p Dave
31 * Put in targa bitmap support (16 bit).
50 #define MAX_TARGA_RUN_LENGTH_PACKET 128
51 #define TARGA_HEADER_LENGTH 18
52 #define ULORIGIN (header.image_descriptor & 0x20)
60 typedef struct targa_header {
72 ubyte image_descriptor;
82 // copy from one pixel buffer to another
84 // to - pointer to dest. buffet
85 // from - pointer to source buffer
86 // pixels - number of pixels to copy
87 // fromsize - source pixel size
88 // tosize - dest. pixel size
90 // returns - number of pixels copied to destination
92 static int targa_copy_data(char *to, char *from, int pixels, int fromsize, int tosize)
94 if ( (fromsize == 2) && (tosize==2) ) {
95 // Flip the alpha bit on 1555 format
100 for (int i=0; i<pixels; i++ ) {
101 *dst++ = (ushort)((*src++) ^ 0x8000); // Flip the transparency bit
103 return tosize*pixels;
104 } else if ( (fromsize == 2) && (tosize == 3) ) {
107 src = (ushort *)from;
108 for (int i=0; i<pixels; i++ ) {
109 ushort pixel = *src++;
111 *to++ = (ubyte)((pixel & 0x1f) * 8);
112 *to++ = (ubyte)(((pixel >> 5) & 63) * 4);
113 *to++ = (ubyte)(((pixel >> 11) & 0x1f) * 8);
115 return tosize*pixels;
117 Assert(fromsize == tosize);
118 memcpy(to, from, pixels * fromsize);
119 return tosize*pixels;
123 // targa_pixels_equal -- Test if two pixels are identical
125 // pix1 - first pixel data
126 // pix2 - second pixel data
127 // pixbytes - number of bytes per pixel
129 // returns - 0 if No Match, else 1 if Match
130 static int targa_pixels_equal(char *pix1, char *pix2, int pixbytes)
133 if ( *pix1++ != *pix2++ ) {
136 } while ( --pixbytes > 0 );
141 // Perform targa RLE on the input data
143 // out - Buffer to write it out to
144 // in - Buffer to compress
145 // outsize - Number of bytes in output buffer
146 // pixsize - Number of bytes in input pixel
147 // bytecount - Number of bytes input
149 // returns - size of compressed data
151 int targa_compress(char *out, char *in, int outsize, int pixsize, int bytecount)
153 int pixcount; // number of pixels in the current packet
154 char *inputpixel=NULL; // current input pixel position
155 char *matchpixel=NULL; // pixel value to match for a run
156 char *flagbyte=NULL; // location of last flag byte to set
157 int rlcount; // current count in r.l. string
158 int rlthresh; // minimum valid run length
159 char *copyloc; // location to begin copying at
161 // set the threshold -- the minimum valid run length
164 rlthresh = 2; // for 8bpp, require a 2 pixel span before rle'ing
169 // set the first pixel up
171 flagbyte = out; // place to put next flag if run
177 // loop till data processing complete
180 // if we have accumulated a 128-byte packet, process it
181 if ( pixcount == 129 ) {
184 // set the run flag if this is a run
186 if ( rlcount >= rlthresh ) {
191 // copy the data into place
193 flagbyte += targa_copy_data(flagbyte, copyloc, pixcount-1, pixsize, outsize);
196 // set up for next packet
200 // if zeroth byte, handle as special case
201 if ( pixcount == 1 ) {
203 copyloc = inputpixel; /* point to 1st guy in packet */
204 matchpixel = inputpixel; /* set pointer to pix to match */
206 inputpixel += pixsize;
210 // assembling a packet -- look at next pixel
212 // current pixel == match pixel?
213 if ( targa_pixels_equal(inputpixel, matchpixel, outsize) ) {
215 // establishing a run of enough length to
216 // save space by doing it
217 // -- write the non-run length packet
218 // -- start run-length packet
220 if ( ++rlcount == rlthresh ) {
222 // close a non-run packet
224 if ( pixcount > (rlcount+1) ) {
225 // write out length and do not set run flag
227 *flagbyte++ = (char)(pixcount - 2 - rlthresh);
229 flagbyte += targa_copy_data(flagbyte, copyloc, (pixcount-1-rlcount), pixsize, outsize);
231 copyloc = inputpixel;
232 pixcount = rlcount + 1;
237 // no match -- either break a run or continue without one
238 // if a run exists break it:
239 // write the bytes in the string (outsize+1)
240 // start the next string
242 if ( rlcount >= rlthresh ) {
244 *flagbyte++ = (char)(0x80 | rlcount);
245 flagbyte += targa_copy_data(flagbyte, copyloc, 1, pixsize, outsize);
250 // not a match and currently not a run
251 // - save the current pixel
252 // - reset the run-length flag
254 matchpixel = inputpixel;
258 inputpixel += pixsize;
259 } while ( inputpixel < (in + bytecount));
261 // quit this buffer without loosing any data
263 if ( --pixcount >= 1 ) {
264 *flagbyte = (char)(pixcount - 1);
265 if ( rlcount >= rlthresh ) {
270 // copy the data into place
272 flagbyte += targa_copy_data(flagbyte, copyloc, pixcount, pixsize, outsize);
274 return(flagbyte-out);
277 // Reads a pixel of the specified bytes_per_pixel into memory and
278 // returns the number of bytes read into memory.
279 // NOTE : for Freespace2, this also swizzles data into the proper screen (NOT texture) format - just like
282 // dst - A pointer to the destination. Must be at least 4 bytes long.
283 // targa_file - The file to read from.
284 // bytes_per_pixel - The bytes per pixel of the file.
285 // dest_size - bytes per pixel in destination (1 or 2. 1 == 8 bit paletted)
287 // returns - Number of byte read into memory
289 static void targa_read_pixel( int num_pixels, ubyte **dst, ubyte **src, int bytes_per_pixel, int dest_size )
297 for(idx=0; idx<num_pixels; idx++){
298 // stuff the 16 bit pixel
299 memcpy(&pixel, *src, bytes_per_pixel);
301 // if the pixel is transparent, make it so...
302 if(((pixel & 0x7c00) == 0) && ((pixel & 0x03e0) == 0x03e0) && ((pixel & 0x001f) == 0)){
306 bm_set_components((ubyte*)&pixel, &r, &g, &b, &al);
308 // get the 8 bit r, g, and b values
309 r = (ubyte)(((pixel & 0x7c00) >> 10) * 8);
310 g = (ubyte)(((pixel & 0x03e0) >> 5) * 8);
311 b = (ubyte)((pixel & 0x001f) * 8);
314 // now stuff these back, swizzling properly
316 bm_set_components((ubyte*)&pixel, &r, &g, &b, &al);
319 // 16 bit destination
321 // stuff the final pixel
322 memcpy( *dst, &pixel, bytes_per_pixel );
326 pal_index = (ubyte)palette_find((int)r, (int)g, (int)b);
332 (*src) += bytes_per_pixel;
338 // External Functions
342 // Reads header information from the targa file into the bitmap pointer
344 // filename - name of the targa bitmap file
345 // w - (output) width of the bitmap
346 // h - (output) height of the bitmap
347 // bpp - (output) bits per pixel of the bitmap
349 // returns - TARGA_ERROR_NONE if successful, otherwise error code
351 int targa_read_header(char *real_filename, int *w, int *h, int *bpp, ubyte *palette )
355 char filename[MAX_FILENAME_LEN];
357 strcpy( filename, real_filename );
358 char *p = strchr( filename, '.' );
360 strcat( filename, ".tga" );
362 targa_file = cfopen( filename , "rb" );
364 return TARGA_ERROR_READING;
367 header.id_length = cfread_ubyte(targa_file);
368 // header.id_length=targa_file.read_char();
370 header.color_map_type = cfread_ubyte(targa_file);
371 // header.color_map_type=targa_file.read_char();
373 header.image_type = cfread_ubyte(targa_file);
374 // header.image_type=targa_file.read_char();
376 header.cmap_start = cfread_short(targa_file);
377 // header.cmap_start=targa_file.read_short();
379 header.cmap_length = cfread_short(targa_file);
380 // header.cmap_length=targa_file.read_short();
382 header.cmap_depth = cfread_ubyte(targa_file);
383 // header.cmap_depth=targa_file.read_char();
385 header.xoffset = cfread_short(targa_file);
386 // header.xoffset=targa_file.read_short();
388 header.yoffset = cfread_short(targa_file);
389 // header.yoffset=targa_file.read_short();
391 header.width = cfread_short(targa_file);
392 // header.width=targa_file.read_short();
394 header.height = cfread_short(targa_file);
395 // header.height=targa_file.read_short();
397 header.pixel_depth = cfread_ubyte(targa_file);
398 // header.pixel_depth=targa_file.read_char();
400 header.image_descriptor = cfread_ubyte(targa_file);
401 // header.image_descriptor=targa_file.read_char();
408 *bpp = header.pixel_depth;
410 // only support 16 bit pixels
413 return TARGA_ERROR_READING;
416 return TARGA_ERROR_NONE;
419 // Uncompresses some RLE'd TGA data
421 // dst: pointer uncompressed destination.
422 // src: pointer to source rle'd data.
423 // bitmap_width: how many pixels to uncompress.
424 // bytes_per_pixel: bytes per pixel of the data.
426 // returns: number of input bytes processed.
428 int targa_uncompress( ubyte *dst, ubyte *src, int bitmap_width, int bytes_per_pixel, int dest_size )
430 ubyte *pixdata = dst;
431 ubyte *src_pixels = src;
433 int pixel_count = 0; // Initialize pixel counter
435 // Main decoding loop
436 while (pixel_count < bitmap_width ) {
438 // Get the pixel count
439 int run_count = *src_pixels++;
441 // Make sure writing this next run will not overflow the buffer
442 Assert(pixel_count + (run_count & 0x7f) + 1 <= bitmap_width );
444 // If the run is encoded...
445 if ( run_count & 0x80 ) {
446 run_count &= ~0x80; // Mask off the upper bit
448 // Update total pixel count
449 pixel_count += (run_count + 1);
451 ubyte pixel_value[4]; // temporary
452 ubyte *tmp = pixel_value;
453 targa_read_pixel( 1, &tmp, &src_pixels, bytes_per_pixel, dest_size );
455 // Write remainder of pixel run to buffer 'run_count' times
457 memcpy( pixdata, pixel_value, dest_size );
458 pixdata += dest_size;
459 } while (run_count--);
461 } else { // ...the run is unencoded (raw)
462 // Update total pixel count
463 pixel_count += (run_count + 1);
465 // Read run_count pixels
466 targa_read_pixel(run_count+1, &pixdata, &src_pixels, bytes_per_pixel, dest_size );
470 Assert( pixel_count == bitmap_width );
472 return src_pixels - src;
476 // Loads a Targa bitmap
478 // filename - name of the targa file to load
479 // image_data - allocated storage for the bitmap
481 // returns - true if succesful, false otherwise
483 int targa_read_bitmap(char *real_filename, ubyte *image_data, ubyte *palette, int dest_size)
485 Assert(real_filename);
488 char filename[MAX_FILENAME_LEN];
492 strcpy( filename, real_filename );
493 char *p = strchr( filename, '.' );
495 strcat( filename, ".tga" );
497 targa_file = cfopen( filename , "rb" );
499 return TARGA_ERROR_READING;
502 header.id_length = cfread_ubyte(targa_file);
503 // header.id_length=targa_file.read_char();
505 header.color_map_type = cfread_ubyte(targa_file);
506 // header.color_map_type=targa_file.read_char();
508 header.image_type = cfread_ubyte(targa_file);
509 // header.image_type=targa_file.read_char();
511 header.cmap_start = cfread_short(targa_file);
512 // header.cmap_start=targa_file.read_short();
514 header.cmap_length = cfread_short(targa_file);
515 // header.cmap_length=targa_file.read_short();
517 header.cmap_depth = cfread_ubyte(targa_file);
518 // header.cmap_depth=targa_file.read_char();
520 header.xoffset = cfread_short(targa_file);
521 // header.xoffset=targa_file.read_short();
523 header.yoffset = cfread_short(targa_file);
524 // header.yoffset=targa_file.read_short();
526 header.width = cfread_short(targa_file);
527 // header.width=targa_file.read_short();
529 header.height = cfread_short(targa_file);
530 // header.height=targa_file.read_short();
532 header.pixel_depth = cfread_ubyte(targa_file);
533 // header.pixel_depth=targa_file.read_char();
535 header.image_descriptor = cfread_ubyte(targa_file);
536 // header.image_descriptor=targa_file.read_char();
538 int bytes_per_pixel = (header.pixel_depth>>3);
539 // we're only allowing 2 bytes per pixel (16 bit compressed)
540 Assert(bytes_per_pixel == 2);
541 if(bytes_per_pixel != 2){
543 return TARGA_ERROR_READING;
547 if ( header.image_descriptor & 0x10 ) {
553 if ( header.image_descriptor & 0x20 ) {
559 // only accept 16 bit, compressed
560 if(header.pixel_depth!=16) {
562 return TARGA_ERROR_READING;
566 char test=char(header.image_descriptor&0xF);
567 if((test!=8)&&(test!=0)) {
569 return TARGA_ERROR_READING;
573 if((header.image_type!=1)&&(header.image_type!=2)&&(header.image_type!=9)&&(header.image_type!=10)) {
575 return TARGA_ERROR_READING;
578 // skip the Image ID field -- should not be needed
579 if(header.id_length>0) {
580 if(cfseek(targa_file, header.id_length, CF_SEEK_SET)) {
582 return TARGA_ERROR_READING;
586 // read palette if one present.
588 if (header.color_map_type) { // non-zero indicates palette in the file
591 // Determine the size of the color map
592 Assert(header.cmap_depth==24);
593 Assert(header.cmap_length<=256);
596 // Read the color map data
598 for (i = 0; i < header.cmap_length; i++) {
599 r = cfread_ubyte(targa_file);
600 g = cfread_ubyte(targa_file);
601 b = cfread_ubyte(targa_file);
609 // Fill out with black.
611 for (; i < 256; i++) {
619 int bytes_remaining = cfilelength(targa_file)-cftell(targa_file);
621 Assert(bytes_remaining>0);
623 ubyte *fileptr = (ubyte*)malloc(bytes_remaining);
626 return TARGA_ERROR_READING;
629 ubyte *src_pixels = fileptr;
631 cfread(fileptr, bytes_remaining, 1, targa_file);
633 int rowsize = header.width * bytes_per_pixel;
635 if ( (header.image_type == 1) || (header.image_type == 2) || (header.image_type == 3) ) {
638 for (int i = 0; i < header.height; i++) {
642 pixptr = image_data + i * rowsize;
644 pixptr = image_data + ((header.height - i - 1) * rowsize);
647 targa_read_pixel(header.width, &pixptr, &src_pixels, bytes_per_pixel, dest_size );
650 } else if (header.image_type == 9 || header.image_type == 10 || header.image_type == 11) {
652 // the following handles RLE'ed targa data.
654 // targas encoded by the scanline -- loop on the height
655 for (int i = 0; i < header.height; i++) {
659 pixdata = image_data + i * rowsize;
661 pixdata = image_data + ((header.height - i - 1) * rowsize);
664 src_pixels += targa_uncompress( pixdata, src_pixels, header.width, bytes_per_pixel, dest_size );
673 return TARGA_ERROR_NONE;
676 // Write out a Targa format bitmap. Always writes out a top-up bitmap.
677 // JAS: DOESN'T WORK WITH 8-BPP PALETTES
679 // filename: name of the Targa file, .tga extension added if not passed in
680 // data: raw image data
681 // w: width of the bitmap in pixels
682 // h: height of the bitmap in pixels
683 // bpp: bits per pixel of the bitmap
685 // returns: 0 if successful, otherwise -1
687 int targa_write_bitmap(char *real_filename, ubyte *data, ubyte *palette, int w, int h, int bpp)
690 char filename[MAX_FILENAME_LEN];
692 int bytes_per_pixel = BYTES_PER_PIXEL(bpp);
695 strcpy( filename, real_filename );
696 char *p = strchr( filename, '.' );
698 strcat( filename, ".tga" );
700 f = cfopen( filename , "wb" );
702 return TARGA_ERROR_READING;
705 // Write the TGA header
707 // f.write_ubyte(0); // IDLength
710 //f.write_ubyte(0); // ColorMapType
712 cfwrite_ubyte(10, f);
713 // f.write_ubyte(10); // image_type: 2 = 24bpp, uncompressed, 10=24bpp rle compressed
715 cfwrite_ushort(0, f);
716 // f.write_ushort(0); // CMapStart
718 cfwrite_ushort(0, f);
719 // f.write_ushort(0); // CMapLength
722 // f.write_ubyte(0); // CMapDepth
724 cfwrite_ushort(0, f);
725 // f.write_ushort(0); // XOffset
727 cfwrite_ushort(0, f);
728 // f.write_ushort(0); // YOffset
730 cfwrite_ushort((ushort)w, f);
731 // f.write_ushort((ushort)w); // Width
733 cfwrite_ushort((ushort)h, f);
734 // f.write_ushort((ushort)h); // Height
736 cfwrite_ubyte(24, f);
737 // f.write_ubyte(24); // pixel_depth
739 cfwrite_ubyte(0x20, f);
740 // f.write_ubyte(0x20); // ImageDesc ( 0x20 = Origin at upper left )
742 ubyte *compressed_data;
743 compressed_data = (ubyte*)malloc(w * h * bytes_per_pixel);
744 Assert(compressed_data);
745 if(compressed_data == NULL){
750 int compressed_data_len;
751 compressed_data_len = targa_compress((char*)compressed_data, (char*)data, 3, bytes_per_pixel, w * h * bytes_per_pixel);
752 if (compressed_data_len < 0) {
753 free(compressed_data);
758 cfwrite(compressed_data, compressed_data_len, 1, f);