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/TgaUtils/TgaUtils.cpp $
17 * Revision 1.3 2002/06/09 04:41:27 relnev
18 * added copyright header
20 * Revision 1.2 2002/05/07 03:16:52 theoddone33
21 * The Great Newline Fix
23 * Revision 1.1.1.1 2002/05/03 03:28:11 root
27 * 7 7/13/99 1:16p Dave
28 * 32 bit support. Whee!
30 * 6 3/20/99 3:46p Dave
31 * Added support for model-based background nebulae. Added 3 new
34 * 5 12/03/98 9:39a Dave
35 * Removed bogus tga code assert?
37 * 4 12/02/98 5:47p Dave
38 * Put in interface xstr code. Converted barracks screen to new format.
40 * 3 12/01/98 5:54p Dave
41 * Simplified the way pixel data is swizzled. Fixed tga bitmaps to work
42 * properly in D3D and Glide.
44 * 2 12/01/98 4:46p Dave
45 * Put in targa bitmap support (16 bit).
64 #define MAX_TARGA_RUN_LENGTH_PACKET 128
65 #define TARGA_HEADER_LENGTH 18
66 #define ULORIGIN (header.image_descriptor & 0x20)
74 typedef struct targa_header {
86 ubyte image_descriptor;
96 // copy from one pixel buffer to another
98 // to - pointer to dest. buffet
99 // from - pointer to source buffer
100 // pixels - number of pixels to copy
101 // fromsize - source pixel size
102 // tosize - dest. pixel size
104 // returns - number of pixels copied to destination
106 static int targa_copy_data(char *to, char *from, int pixels, int fromsize, int tosize)
108 if ( (fromsize == 2) && (tosize==2) ) {
109 // Flip the alpha bit on 1555 format
112 src = (ushort *)from;
113 dst = (ushort *)from;
114 for (int i=0; i<pixels; i++ ) {
115 *dst++ = (ushort)((*src++) ^ 0x8000); // Flip the transparency bit
117 return tosize*pixels;
118 } else if ( (fromsize == 2) && (tosize == 3) ) {
121 src = (ushort *)from;
122 for (int i=0; i<pixels; i++ ) {
123 ushort pixel = *src++;
125 *to++ = (ubyte)((pixel & 0x1f) * 8);
126 *to++ = (ubyte)(((pixel >> 5) & 63) * 4);
127 *to++ = (ubyte)(((pixel >> 11) & 0x1f) * 8);
129 return tosize*pixels;
131 SDL_assert(fromsize == tosize);
132 memcpy(to, from, pixels * fromsize);
133 return tosize*pixels;
137 // targa_pixels_equal -- Test if two pixels are identical
139 // pix1 - first pixel data
140 // pix2 - second pixel data
141 // pixbytes - number of bytes per pixel
143 // returns - 0 if No Match, else 1 if Match
144 static int targa_pixels_equal(char *pix1, char *pix2, int pixbytes)
147 if ( *pix1++ != *pix2++ ) {
150 } while ( --pixbytes > 0 );
155 // Perform targa RLE on the input data
157 // out - Buffer to write it out to
158 // in - Buffer to compress
159 // outsize - Number of bytes in output buffer
160 // pixsize - Number of bytes in input pixel
161 // bytecount - Number of bytes input
163 // returns - size of compressed data
165 int targa_compress(char *out, char *in, int outsize, int pixsize, int bytecount)
167 int pixcount; // number of pixels in the current packet
168 char *inputpixel=NULL; // current input pixel position
169 char *matchpixel=NULL; // pixel value to match for a run
170 char *flagbyte=NULL; // location of last flag byte to set
171 int rlcount; // current count in r.l. string
172 int rlthresh; // minimum valid run length
173 char *copyloc; // location to begin copying at
175 // set the threshold -- the minimum valid run length
178 rlthresh = 2; // for 8bpp, require a 2 pixel span before rle'ing
183 // set the first pixel up
185 flagbyte = out; // place to put next flag if run
191 // loop till data processing complete
194 // if we have accumulated a 128-byte packet, process it
195 if ( pixcount == 129 ) {
198 // set the run flag if this is a run
200 if ( rlcount >= rlthresh ) {
205 // copy the data into place
207 flagbyte += targa_copy_data(flagbyte, copyloc, pixcount-1, pixsize, outsize);
210 // set up for next packet
214 // if zeroth byte, handle as special case
215 if ( pixcount == 1 ) {
217 copyloc = inputpixel; /* point to 1st guy in packet */
218 matchpixel = inputpixel; /* set pointer to pix to match */
220 inputpixel += pixsize;
224 // assembling a packet -- look at next pixel
226 // current pixel == match pixel?
227 if ( targa_pixels_equal(inputpixel, matchpixel, outsize) ) {
229 // establishing a run of enough length to
230 // save space by doing it
231 // -- write the non-run length packet
232 // -- start run-length packet
234 if ( ++rlcount == rlthresh ) {
236 // close a non-run packet
238 if ( pixcount > (rlcount+1) ) {
239 // write out length and do not set run flag
241 *flagbyte++ = (char)(pixcount - 2 - rlthresh);
243 flagbyte += targa_copy_data(flagbyte, copyloc, (pixcount-1-rlcount), pixsize, outsize);
245 copyloc = inputpixel;
246 pixcount = rlcount + 1;
251 // no match -- either break a run or continue without one
252 // if a run exists break it:
253 // write the bytes in the string (outsize+1)
254 // start the next string
256 if ( rlcount >= rlthresh ) {
258 *flagbyte++ = (char)(0x80 | rlcount);
259 flagbyte += targa_copy_data(flagbyte, copyloc, 1, pixsize, outsize);
264 // not a match and currently not a run
265 // - save the current pixel
266 // - reset the run-length flag
268 matchpixel = inputpixel;
272 inputpixel += pixsize;
273 } while ( inputpixel < (in + bytecount));
275 // quit this buffer without loosing any data
277 if ( --pixcount >= 1 ) {
278 *flagbyte = (char)(pixcount - 1);
279 if ( rlcount >= rlthresh ) {
284 // copy the data into place
286 flagbyte += targa_copy_data(flagbyte, copyloc, pixcount, pixsize, outsize);
288 return(flagbyte-out);
291 // Reads a pixel of the specified bytes_per_pixel into memory and
292 // returns the number of bytes read into memory.
293 // NOTE : for Freespace2, this also swizzles data into the proper screen (NOT texture) format - just like
296 // dst - A pointer to the destination. Must be at least 4 bytes long.
297 // targa_file - The file to read from.
298 // bytes_per_pixel - The bytes per pixel of the file.
299 // dest_size - bytes per pixel in destination (1 or 2. 1 == 8 bit paletted)
301 // returns - Number of byte read into memory
303 static void targa_read_pixel( int num_pixels, ubyte **dst, ubyte **src, int bytes_per_pixel, int dest_size )
311 for(idx=0; idx<num_pixels; idx++){
312 // stuff the 16 bit pixel
313 memcpy(&pixel, *src, bytes_per_pixel);
315 // if the pixel is transparent, make it so...
316 if(((pixel & 0x7c00) == 0) && ((pixel & 0x03e0) == 0x03e0) && ((pixel & 0x001f) == 0)){
320 bm_set_components((ubyte*)&pixel, &r, &g, &b, &al);
322 // get the 8 bit r, g, and b values
323 r = (ubyte)(((pixel & 0x7c00) >> 10) * 8);
324 g = (ubyte)(((pixel & 0x03e0) >> 5) * 8);
325 b = (ubyte)((pixel & 0x001f) * 8);
328 // now stuff these back, swizzling properly
330 bm_set_components((ubyte*)&pixel, &r, &g, &b, &al);
333 // 16 bit destination
335 // stuff the final pixel
336 memcpy( *dst, &pixel, bytes_per_pixel );
340 pal_index = (ubyte)palette_find((int)r, (int)g, (int)b);
346 (*src) += bytes_per_pixel;
352 // External Functions
356 // Reads header information from the targa file into the bitmap pointer
358 // filename - name of the targa bitmap file
359 // w - (output) width of the bitmap
360 // h - (output) height of the bitmap
361 // bpp - (output) bits per pixel of the bitmap
363 // returns - TARGA_ERROR_NONE if successful, otherwise error code
365 int targa_read_header(char *real_filename, int *w, int *h, int *bpp, ubyte *palette )
369 char filename[MAX_FILENAME_LEN];
371 SDL_strlcpy( filename, real_filename, sizeof(filename) );
372 char *p = SDL_strchr( filename, '.' );
374 SDL_strlcat( filename, ".tga", sizeof(filename) );
376 targa_file = cfopen( filename , "rb" );
378 return TARGA_ERROR_READING;
381 header.id_length = cfread_ubyte(targa_file);
382 // header.id_length=targa_file.read_char();
384 header.color_map_type = cfread_ubyte(targa_file);
385 // header.color_map_type=targa_file.read_char();
387 header.image_type = cfread_ubyte(targa_file);
388 // header.image_type=targa_file.read_char();
390 header.cmap_start = cfread_short(targa_file);
391 // header.cmap_start=targa_file.read_short();
393 header.cmap_length = cfread_short(targa_file);
394 // header.cmap_length=targa_file.read_short();
396 header.cmap_depth = cfread_ubyte(targa_file);
397 // header.cmap_depth=targa_file.read_char();
399 header.xoffset = cfread_short(targa_file);
400 // header.xoffset=targa_file.read_short();
402 header.yoffset = cfread_short(targa_file);
403 // header.yoffset=targa_file.read_short();
405 header.width = cfread_short(targa_file);
406 // header.width=targa_file.read_short();
408 header.height = cfread_short(targa_file);
409 // header.height=targa_file.read_short();
411 header.pixel_depth = cfread_ubyte(targa_file);
412 // header.pixel_depth=targa_file.read_char();
414 header.image_descriptor = cfread_ubyte(targa_file);
415 // header.image_descriptor=targa_file.read_char();
422 *bpp = header.pixel_depth;
424 // only support 16 bit pixels
425 SDL_assert(*bpp == 16);
427 return TARGA_ERROR_READING;
430 return TARGA_ERROR_NONE;
433 // Uncompresses some RLE'd TGA data
435 // dst: pointer uncompressed destination.
436 // src: pointer to source rle'd data.
437 // bitmap_width: how many pixels to uncompress.
438 // bytes_per_pixel: bytes per pixel of the data.
440 // returns: number of input bytes processed.
442 int targa_uncompress( ubyte *dst, ubyte *src, int bitmap_width, int bytes_per_pixel, int dest_size )
444 ubyte *pixdata = dst;
445 ubyte *src_pixels = src;
447 int pixel_count = 0; // Initialize pixel counter
449 // Main decoding loop
450 while (pixel_count < bitmap_width ) {
452 // Get the pixel count
453 int run_count = *src_pixels++;
455 // Make sure writing this next run will not overflow the buffer
456 SDL_assert(pixel_count + (run_count & 0x7f) + 1 <= bitmap_width );
458 // If the run is encoded...
459 if ( run_count & 0x80 ) {
460 run_count &= ~0x80; // Mask off the upper bit
462 // Update total pixel count
463 pixel_count += (run_count + 1);
465 ubyte pixel_value[4]; // temporary
466 ubyte *tmp = pixel_value;
467 targa_read_pixel( 1, &tmp, &src_pixels, bytes_per_pixel, dest_size );
469 // Write remainder of pixel run to buffer 'run_count' times
471 memcpy( pixdata, pixel_value, dest_size );
472 pixdata += dest_size;
473 } while (run_count--);
475 } else { // ...the run is unencoded (raw)
476 // Update total pixel count
477 pixel_count += (run_count + 1);
479 // Read run_count pixels
480 targa_read_pixel(run_count+1, &pixdata, &src_pixels, bytes_per_pixel, dest_size );
484 SDL_assert( pixel_count == bitmap_width );
486 return src_pixels - src;
490 // Loads a Targa bitmap
492 // filename - name of the targa file to load
493 // image_data - allocated storage for the bitmap
495 // returns - true if succesful, false otherwise
497 int targa_read_bitmap(char *real_filename, ubyte *image_data, ubyte *palette, int dest_size)
499 SDL_assert(real_filename);
502 char filename[MAX_FILENAME_LEN];
506 SDL_strlcpy( filename, real_filename, sizeof(filename) );
507 char *p = SDL_strchr( filename, '.' );
509 SDL_strlcat( filename, ".tga", sizeof(filename) );
511 targa_file = cfopen( filename , "rb" );
513 return TARGA_ERROR_READING;
516 header.id_length = cfread_ubyte(targa_file);
517 // header.id_length=targa_file.read_char();
519 header.color_map_type = cfread_ubyte(targa_file);
520 // header.color_map_type=targa_file.read_char();
522 header.image_type = cfread_ubyte(targa_file);
523 // header.image_type=targa_file.read_char();
525 header.cmap_start = cfread_short(targa_file);
526 // header.cmap_start=targa_file.read_short();
528 header.cmap_length = cfread_short(targa_file);
529 // header.cmap_length=targa_file.read_short();
531 header.cmap_depth = cfread_ubyte(targa_file);
532 // header.cmap_depth=targa_file.read_char();
534 header.xoffset = cfread_short(targa_file);
535 // header.xoffset=targa_file.read_short();
537 header.yoffset = cfread_short(targa_file);
538 // header.yoffset=targa_file.read_short();
540 header.width = cfread_short(targa_file);
541 // header.width=targa_file.read_short();
543 header.height = cfread_short(targa_file);
544 // header.height=targa_file.read_short();
546 header.pixel_depth = cfread_ubyte(targa_file);
547 // header.pixel_depth=targa_file.read_char();
549 header.image_descriptor = cfread_ubyte(targa_file);
550 // header.image_descriptor=targa_file.read_char();
552 int bytes_per_pixel = (header.pixel_depth>>3);
553 // we're only allowing 2 bytes per pixel (16 bit compressed)
554 SDL_assert(bytes_per_pixel == 2);
555 if(bytes_per_pixel != 2){
557 return TARGA_ERROR_READING;
562 if ( header.image_descriptor & 0x10 ) {
568 if ( header.image_descriptor & 0x20 ) {
575 // only accept 16 bit, compressed
576 if(header.pixel_depth!=16) {
578 return TARGA_ERROR_READING;
582 char test=char(header.image_descriptor&0xF);
583 if((test!=8)&&(test!=0)) {
585 return TARGA_ERROR_READING;
589 if((header.image_type!=1)&&(header.image_type!=2)&&(header.image_type!=9)&&(header.image_type!=10)) {
591 return TARGA_ERROR_READING;
594 // skip the Image ID field -- should not be needed
595 if(header.id_length>0) {
596 if(cfseek(targa_file, header.id_length, CF_SEEK_SET)) {
598 return TARGA_ERROR_READING;
602 // read palette if one present.
604 if (header.color_map_type) { // non-zero indicates palette in the file
607 // Determine the size of the color map
608 SDL_assert(header.cmap_depth==24);
609 SDL_assert(header.cmap_length<=256);
612 // Read the color map data
614 for (i = 0; i < header.cmap_length; i++) {
615 r = cfread_ubyte(targa_file);
616 g = cfread_ubyte(targa_file);
617 b = cfread_ubyte(targa_file);
625 // Fill out with black.
627 for (; i < 256; i++) {
635 int bytes_remaining = cfilelength(targa_file)-cftell(targa_file);
637 SDL_assert(bytes_remaining>0);
639 ubyte *fileptr = (ubyte*)malloc(bytes_remaining);
642 return TARGA_ERROR_READING;
645 ubyte *src_pixels = fileptr;
647 cfread(fileptr, bytes_remaining, 1, targa_file);
649 int rowsize = header.width * bytes_per_pixel;
651 if ( (header.image_type == 1) || (header.image_type == 2) || (header.image_type == 3) ) {
654 for (int i = 0; i < header.height; i++) {
658 pixptr = image_data + i * rowsize;
660 pixptr = image_data + ((header.height - i - 1) * rowsize);
663 targa_read_pixel(header.width, &pixptr, &src_pixels, bytes_per_pixel, dest_size );
666 } else if (header.image_type == 9 || header.image_type == 10 || header.image_type == 11) {
668 // the following handles RLE'ed targa data.
670 // targas encoded by the scanline -- loop on the height
671 for (int i = 0; i < header.height; i++) {
675 pixdata = image_data + i * rowsize;
677 pixdata = image_data + ((header.height - i - 1) * rowsize);
680 src_pixels += targa_uncompress( pixdata, src_pixels, header.width, bytes_per_pixel, dest_size );
689 return TARGA_ERROR_NONE;
692 // Write out a Targa format bitmap. Always writes out a top-up bitmap.
693 // JAS: DOESN'T WORK WITH 8-BPP PALETTES
695 // filename: name of the Targa file, .tga extension added if not passed in
696 // data: raw image data
697 // w: width of the bitmap in pixels
698 // h: height of the bitmap in pixels
699 // bpp: bits per pixel of the bitmap
701 // returns: 0 if successful, otherwise -1
703 int targa_write_bitmap(char *real_filename, ubyte *data, ubyte *palette, int w, int h, int bpp)
705 SDL_assert(bpp == 24);
706 char filename[MAX_FILENAME_LEN];
708 int bytes_per_pixel = BYTES_PER_PIXEL(bpp);
711 SDL_strlcpy( filename, real_filename, sizeof(filename) );
712 char *p = SDL_strchr( filename, '.' );
714 SDL_strlcat( filename, ".tga", sizeof(filename) );
716 f = cfopen( filename , "wb" );
718 return TARGA_ERROR_READING;
721 // Write the TGA header
723 // f.write_ubyte(0); // IDLength
726 //f.write_ubyte(0); // ColorMapType
728 cfwrite_ubyte(10, f);
729 // f.write_ubyte(10); // image_type: 2 = 24bpp, uncompressed, 10=24bpp rle compressed
731 cfwrite_ushort(0, f);
732 // f.write_ushort(0); // CMapStart
734 cfwrite_ushort(0, f);
735 // f.write_ushort(0); // CMapLength
738 // f.write_ubyte(0); // CMapDepth
740 cfwrite_ushort(0, f);
741 // f.write_ushort(0); // XOffset
743 cfwrite_ushort(0, f);
744 // f.write_ushort(0); // YOffset
746 cfwrite_ushort((ushort)w, f);
747 // f.write_ushort((ushort)w); // Width
749 cfwrite_ushort((ushort)h, f);
750 // f.write_ushort((ushort)h); // Height
752 cfwrite_ubyte(24, f);
753 // f.write_ubyte(24); // pixel_depth
755 cfwrite_ubyte(0x20, f);
756 // f.write_ubyte(0x20); // ImageDesc ( 0x20 = Origin at upper left )
758 ubyte *compressed_data;
759 compressed_data = (ubyte*)malloc(w * h * bytes_per_pixel);
760 SDL_assert(compressed_data);
761 if(compressed_data == NULL){
766 int compressed_data_len;
767 compressed_data_len = targa_compress((char*)compressed_data, (char*)data, 3, bytes_per_pixel, w * h * bytes_per_pixel);
768 if (compressed_data_len < 0) {
769 free(compressed_data);
774 cfwrite(compressed_data, compressed_data_len, 1, f);