//------------------------------------------------------------------------------ // File : tga.cpp //------------------------------------------------------------------------------ // GLVU : Copyright 1997 - 2002 // The University of North Carolina at Chapel Hill //------------------------------------------------------------------------------ // Permission to use, copy, modify, distribute and sell this software and its // documentation for any purpose is hereby granted without fee, provided that // the above copyright notice appear in all copies and that both that copyright // notice and this permission notice appear in supporting documentation. // Binaries may be compiled with this software without any royalties or // restrictions. // // The University of North Carolina at Chapel Hill makes no representations // about the suitability of this software for any purpose. It is provided // "as is" without express or implied warranty. //============================================================================ // tga.hpp : Targa image format module //============================================================================ // Modified by Vincent Scheib, Aug 2000 // from a modified version from David K. McAllister, Aug. 2000. #include #include #include #include #include #ifdef _WIN32 // MSVC doesn't define 'ios::nocreate' in , only in . // but SGI doesn't define 'ios::binary' in only ! // DOH! // Follow up: 10/5/01 Apparently ios::nocreate is dead. The latest standard // doesn't include it because opening a file that doesn't exist never really // should have been implemented to create the file. #define NOCREATE 0x20 #else #define NOCREATE ((ios::openmode)0x0) #endif #define TGA_VERBOSE 0 using namespace std; //============================================================================ //============================================================================ // Definitions //============================================================================ // Header definition. typedef struct TGA_Header_ { unsigned char ImageIDLength; // length of Identifier String. unsigned char CoMapType; // 0 = no map unsigned char ImgType; // image type (see below for values) unsigned char Index_lo, Index_hi; // index of first color map entry unsigned char Length_lo, Length_hi; // number of entries in color map unsigned char CoSize; // size of color map entry (15,16,24,32) unsigned char X_org_lo, X_org_hi; // x origin of image unsigned char Y_org_lo, Y_org_hi; // y origin of image unsigned char Width_lo, Width_hi; // width of image unsigned char Height_lo, Height_hi; // height of image unsigned char PixelSize; // pixel size (8,16,24,32) unsigned char Desc; // 4 bits are number of attribute bits per pixel } TGA_Header; // Definitions for image types. #define TGA_NULL 0 #define TGA_MAP 1 #define TGA_RGB 2 #define TGA_MONO 3 #define TGA_RLEMAP 9 #define TGA_RLERGB 10 #define TGA_RLEMONO 11 #define TGA_DESC_ALPHA_MASK ((unsigned char)0xF) // number of alpha channel bits #define TGA_DESC_ORG_MASK ((unsigned char)0x30) // origin mask #define TGA_ORG_BOTTOM_LEFT 0x00 // origin mask #define TGA_ORG_BOTTOM_RIGHT 0x10 #define TGA_ORG_TOP_LEFT 0x20 #define TGA_ORG_TOP_RIGHT 0x30 //============================================================================ //============================================================================ // Helper functions for LOAD //============================================================================ static int decode_map8(const unsigned char *src0, unsigned char *dest0, const int size, const int chan, const unsigned char *color_map) { const unsigned char *src = src0; unsigned char *dest = dest0; for(int i=0; i dest0 + width*height*chan) count = (dest0 + width*height*chan - dest) / chan; for(int j=0; j> 2)) & 0xf8; dest[2] = (src[0] << 3) & 0xf8; src += 2; dest += 3; } return(0); } static int decode_rgb24(const unsigned char *src0, unsigned char *dest0, const int width, const int height) { const unsigned char *src = src0; unsigned char *dest = dest0; for(int i=0; i dest0 + width*height*chan) // prevent from writing out of dest range count = (dest0 + width*height*chan - dest) / chan; for(int j=0; j> 2)) & 0xf8; dest[2] = (src[0] << 3) & 0xf8; if(raw) // In raw mode, keep advancing "src" to subsequent values src += 2; // In RLE mode, just repeat the packet[1] RGB color dest += chan; } if(!raw) // After outputting count RGBA values, advance "src" beyond color if rle src += 2; } assert(dest <= dest0 + width*height*chan); return(0); } // Decode run-length encoded Targa into 32-bit pixels // Stores 24-bit source data into a 32-bit dest. // Sets alpha to 0. static int decode_rgb_rle24(const unsigned char *src0, unsigned char *dest0, const int width, const int height) { int chan = 3; const unsigned char *src = src0; unsigned char *dest = dest0; while(dest < dest0 + width*height*chan) { // Pixels encoded in "packets" // First byte is raw/rle flag(upper bit) and count(1-128 as 0-127 in lower 7 bits) // If raw, the next count chan-byte color values in the file are taken verbatim // If rle, the next single chan-byte color value speaks for the next count pixels int raw = (*src & 0x80) == 0; // Is this packet raw pixels or a repeating color int count = (*src & 0x7f) + 1; // How many raw pixels or color repeats src++; // Advance src beyond first byte to 32-bit color if(dest + count*chan > dest0 + width*height*chan) // prevent from writing out of dest range count = (dest0 + width*height*chan - dest) / chan; for(int j=0; j dest0 + width*height*chan) // prevent from writing out of dest range count = (dest0 + width*height*chan - dest) / chan; for(int j=0; jImageIDLength; int cmapsize = header->CoMapType ? (header->CoSize / 8) * ((header->Length_hi << 8) | header->Length_lo) : 0; unsigned char *encoded_pixels = color_map + cmapsize; char itype_names[16][16] = {"NULL", "MAP", "RGB", "MONO", "4", "5", "6", "7", "8", "RLE-MAP", "RLE-RGB", "RLE-MONO", "12", "13", "14", "15"}; #if TGA_VERBOSE fprintf(stderr, "TGA Image type %c bpp = %d\n", itype_names[0xf & header->ImgType], int(header->PixelSize)); #endif if((header->Desc & TGA_DESC_ORG_MASK) != TGA_ORG_TOP_LEFT && (header->Desc & TGA_DESC_ORG_MASK) != TGA_ORG_BOTTOM_LEFT) { fprintf(stderr, "TGA: Not top/bottom left origin: image desc %c\n", header->Desc ); delete [] fdata; Width = Height = 0; return; } Width = ((header->Width_hi) << 8) | header->Width_lo; Height = ((header->Height_hi) << 8) | header->Height_lo; int size = Width * Height; int chan = header->PixelSize / 8; // 1, 3 or 4 if(chan == 2) chan = 3; // 16-bit means R5G6B5. if(header->ImgType == TGA_MAP || header->ImgType == TGA_RLEMAP) chan = header->CoSize / 8; int dsize = size * chan; if (Color==NULL) Color = new unsigned char[dsize]; if (Color==NULL) { fprintf(stderr, "TGA: Could not allocate internal memory for TGA file read %s\n", FileName); delete [] fdata; Width = Height = 0; return; } switch(header->ImgType) { case TGA_MAP: if(header->PixelSize == 8) decode_map8(encoded_pixels, Color, size, chan, color_map); else { fprintf(stderr, "TGA: Bad color mapped index size: %d bits/pixel.\n", (int)(header->PixelSize)); delete [] fdata; Width = Height = 0; return; } break; case TGA_RLEMAP: if(header->PixelSize == 8) decode_map_rle8(encoded_pixels, Color, Width, Height, chan, color_map); else { fprintf(stderr, "TGA: Bad color mapped rle index size: %d bits/pixel.\n", (int)(header->PixelSize)); delete [] fdata; Width = Height = 0; return; } break; case TGA_MONO: if(header->PixelSize == 8) memcpy(Color, encoded_pixels, dsize); else { fprintf(stderr, "TGA: Bad pixel size: %d bits/pixel\n", (int)(header->PixelSize)); delete [] fdata; Width = Height = 0; return; } break; case TGA_RGB: switch(header->PixelSize) { case 16: decode_rgb16(encoded_pixels, Color, Width, Height); break; case 24: decode_rgb24(encoded_pixels, Color, Width, Height); break; case 32: decode_rgb32(encoded_pixels, Color, Width, Height); break; default: fprintf(stderr, "TGA: Bad pixel size: %d bits/pixel\n", (int)(header->PixelSize)); delete [] fdata; Width = Height = 0; return; } break; case TGA_RLERGB: switch(header->PixelSize) { case 16: decode_rgb_rle16(encoded_pixels, Color, Width, Height); break; case 24: decode_rgb_rle24(encoded_pixels, Color, Width, Height); break; case 32: decode_rgb_rle32(encoded_pixels, Color, Width, Height); break; default: fprintf(stderr, "TGA: Bad pixel size: %d bits/pixel\n", (int)(header->PixelSize)); delete [] fdata; Width = Height = 0; return; } break; default: fprintf(stderr, "TGA: Image type %c bpp = %d.\n", itype_names[0xf & header->ImgType], int(header->PixelSize)); delete [] fdata; Width = Height = 0; return; } // Flip it so that origin is always top left. // With Open GL this is backwards logic for some reason. if((header->Desc & TGA_DESC_ORG_MASK) == TGA_ORG_BOTTOM_LEFT) VFlip(Color, Width, Height, chan); delete [] fdata; Channels = chan; return; } //============================================================================ //============================================================================ // Helper functions for WRITE //============================================================================ inline bool colors_equal(const unsigned char *a, const unsigned char *b, const int chan) { bool same = true; for(int i=0; i 128) count = 128; if(raw) { dest[dp++] = count - 1; if(chan == 4) { for(int i=0; i 65535 || Width <= 0 || Height > 65535 || Height <= 0) { cerr << "TGA: Can not write TGA file of dimentions" << Width << "x" << Height << " too big or empty.\n"; return; } int size = Width*Height; int chan = channels; int dsize = size*chan; FILE *ft = fopen(FileName, "wb"); if(ft == NULL) { cerr << "TGA: Failed to open file `" << FileName << "' for writing.\n"; return; } TGA_Header header; header.ImageIDLength = 0; header.CoMapType = 0; // no colormap header.Index_lo = header.Index_hi = 0; // no colormap header.Length_lo = header.Length_hi = header.CoSize = 0; // no colormap header.X_org_lo = header.X_org_hi = header.Y_org_lo = header.Y_org_hi = 0; // 0,0 origin header.Width_lo = Width; header.Width_hi = Width >> 8; header.Height_lo = Height; header.Height_hi = Height >> 8; header.Desc = TGA_ORG_TOP_LEFT; switch(chan) { case 1: header.ImgType = TGA_MONO; header.PixelSize = 8; break; case 3: header.ImgType = TGA_RLERGB; header.PixelSize = 24; break; case 4: header.ImgType = TGA_RLERGB; header.PixelSize = 32; header.Desc |= 8; // This many alpha bits. break; default: cerr << "TGA: Cannot save file of " << chan << " channels.\n"; return; } fwrite(&header, sizeof(header), 1, ft); unsigned char *out_data = new unsigned char[dsize + size / 128 + 1]; assert(out_data); int rle_size = encode_rle(Color, out_data, size, chan); fwrite(out_data, rle_size, 1, ft); fclose(ft); return; }