/*---------------------------------------------------------------------* Project: TexConv File: tga.cpp Copyright 1998-2001 Nintendo. All rights reserved. These coded instructions, statements and computer programs contain proprietary information of Nintendo of America Inc. and/or Nintendo Company Ltd., and are protected by Federal copyright law. They may not be disclosed to third parties or copied or duplicated in any form, in whole or in part, without the prior written consent of Nintendo. Change History: $Log: tga.cpp,v $ Revision 1.1 2006/02/17 09:03:29 mitu 1st version 2 4/11/01 3:19p John Updated header copyrights and pathname. 1 12/03/99 3:48p Ryan 4 10/13/99 12:39p Mikepc remove unused local variables. 3 10/13/99 11:14a Mikepc bug fix- had to change 'free' argument from mFree( (void**)&ptr ) to free( ptr ) after switching from mFree to free function. 2 10/12/99 7:57p Mikepc for less confusing portability, changed to use calloc and free instead of internal tplConv.lib memory calls. 1 10/08/99 3:06p Mikepc code for user-defined ( .tga ) file reading function. Moved here from tplConv.lib for portability. Developer is free to modify/ add code to read other file types. 1 10/08/99 3:02p Mikepc code for user-defined ( .tga ) file-reading function. File reading code has been moved to tc from tplConv.lib for portability. Developer is free to modify/add to this code to create other file-reading functions. 10 9/16/99 8:47p Mikepc updated code for auto-palette generation 9 9/02/99 11:12a Mikepc some code re-organization between files. added code (verify.cpp) to invoke s3tc.exe from within tc program. changed some routines to accommodate the new texture creation path. 8 8/26/99 4:59p Mikepc renamed file extensions from .c to .cpp. .cpp extension allows addition of namespace protection to remove potential name collisions with tool code. Exceptions are CreateTplFile and QuickConvert. These are extern "C" linked. 7 8/26/99 11:38a Mikepc 6 8/26/99 11:03a Mikepc tplCon rewrite for efficient memory usage, batch file processing ability. $NoKeywords: $ -----------------------------------------------------------------------*/ #include #include #include "tga.h" // simplified form of a .tga file ( header and data ) // used as an intermediate for .tga file to layer conversion typedef struct SimpleTga { u32 imageType; u32 width; u32 height; u32 pixelDepth; u32 abpp; u32 colorMapType; u32 cMapLength; u32 cMapDepth; u32 imageDataSize; u8* imageData; u32 paletteDataSize; u8* paletteData; }SimpleTga; // forward references static void UncompressTgaImage( u8* rawImage, SimpleTga* tga ); static void UnencodeTgaImage( u8* riPtr, u8* dstPtr, u32 width, u32 height, u32 pixelDepth ); static void OrientUncompressedTgaData( u8* srcBuff, u32 width, u32 height, u32 pixelDepth, u32 imageDesc ); static void AdjustTgaForPaletteOffset( u8* dfData, SimpleTga* tga, u16 cMapStart ); static void CreateTgaColorLayer( TCFilePtr dfPtr, SimpleTga* tga ); static void CreateTgaAlphaLayer( TCFilePtr dfPtr, SimpleTga* tga ); static void CreateTgaPalTable( TCFilePtr dfPtr, SimpleTga* tga ); //---------------------------------------------------------------------------------- // decode a tga file into a 'DecodedFile' structure u32 ReadTgaFile( u32 rawSize, u8* rawBits, TCFile* dfPtr ) { u32 i; u8 *rawImage, *rawPalette; u32 rawHeaderSize, rawPaletteSize; u32 tgaImageSize; u8 imageDesc; u16 cMapStart; SimpleTga tga; assert( rawSize > 0 ); assert( rawBits != NULL ); assert( dfPtr != NULL ); // zero out the tga structure for( i=0; i< sizeof(SimpleTga); i++ ) { *(u8*)( (u8*)(&tga) + i ) = 0; } // fill 'tga' using .tga header byte offsets tga.imageType = (u32)( *((u8* )( rawBits + 0x02 )) ); tga.width = (u32)( *((u16*)( rawBits + 0x0C )) ); tga.height = (u32)( *((u16*)( rawBits + 0x0E )) ); tga.pixelDepth = (u32)( *((u8* )( rawBits + 0x10 )) ); // bits tga.abpp = (u32)( *((u8* )( rawBits + 0x11 )) & (u8)(0x0F) ); // bits 0 to 3 tga.colorMapType = (u32)( *((u8* )( rawBits + 0x01 )) ); tga.cMapLength = (u32)( *((u16*)( rawBits + 0x05 )) ); tga.cMapDepth = (u32)( *((u8* )( rawBits + 0x07 )) ); // bits cMapStart = *((u16*)( rawBits + 0x03 )); imageDesc = *((u8* )( rawBits + 0x11 )); // quick error check- if image type == 0, there is neither image nor palette data present if( tga.imageType == 0 ) { TCErrorMsg( "DecodeTgaFile(): file %s contains no image or palette data\n", dfPtr->name ); return 0; } // convert cMapDepth and pixelDepths from bits to bytes // can mostly just right-shift 3 bits but must check for 15-bit format // don't accept other odd bit-depths. // palette entry sizes switch( tga.cMapDepth ) { case 0x00: // no palette tga.cMapDepth = 0; break; case 0x10: // 16-bits fall through case 0x18: // 24 bits fall through case 0x20: // 32 bits fall through tga.cMapDepth >>= 3; break; case 0x0F: // 15 bit entries = 5551 format- will be treated the same as 16 bits tga.cMapDepth = 2; break; default: // unsupported palette entry size TCErrorMsg( "DecodeTgaFile(): unsupported palette entry size in file %s\n", dfPtr->name ); return 0; break; } // pixel depths switch(tga.pixelDepth) { case 0x08: // 8-bits fall through case 0x10: // 16-bits fall through case 0x18: // 24 bits fall through case 0x20: // 32 bits fall through tga.pixelDepth >>= 3; break; case 0x0F: // 15 bit entries = 5551 format- will be treated the same as 16 bits tga.pixelDepth = 2; break; default: // unsupported pixel depth entry size TCErrorMsg( "DecodeTgaFile(): unsupported palette bit depth in file %s\n", dfPtr->name ); return 0; break; } // .tga header size is 18 bytes + IDLength bytes (byte 0 of .tga header) rawHeaderSize = (u32)( rawBits[0] ) + 18; // palette (if present) follows the header if( tga.colorMapType == 0 ) { rawPalette = 0; rawPaletteSize = 0; } else { // a palette may be present even if not used by the image (e.g., TIPS prog. p. 867) // in this case, account for the palette size but don't save the palette data rawPalette = 0; if( (tga.imageType == 1) || (tga.imageType == 9) ) // palette is used by image { rawPalette = ( rawBits + rawHeaderSize ); } rawPaletteSize = (u32)tga.cMapDepth * (u32)tga.cMapLength; } // image (if present) follows the palette if( (tga.width == 0) || (tga.height == 0) || (tga.pixelDepth == 0) ) { rawImage = 0; tgaImageSize = 0; } else { rawImage = ( rawBits + rawHeaderSize + rawPaletteSize ); tgaImageSize = tga.width * tga.height * tga.pixelDepth; } // data buffers must be allocated before decompression is performed if( rawPalette != NULL ) { tga.paletteDataSize = rawPaletteSize; tga.paletteData = (u8*)calloc( 1, rawPaletteSize ); // copy the complete palette as is to 'tga->paletteData' for(i=0; iimageData UncompressTgaImage( rawImage, &tga ); // if the tga file contains a palette and a cMapStart offset, adjust the image data values // to include this offset AdjustTgaForPaletteOffset( tga.imageData, &tga, cMapStart ); // orient the now un-encoded image data so that its origin is the upper left // corner of the screen. OrientUncompressedTgaData( tga.imageData, tga.width, tga.height, tga.pixelDepth, imageDesc ); } // create layer components of dfPtr from tga raw image and palette CreateTgaColorLayer( dfPtr, &tga ); CreateTgaAlphaLayer( dfPtr, &tga ); CreateTgaPalTable( dfPtr, &tga ); // free tga memory when done if( tga.imageData != NULL ) { free( tga.imageData ); tga.imageData = NULL; } if( tga.paletteData != NULL ) { free( tga.paletteData ); tga.paletteData = NULL; } return 1; } //----------------------------------------------------------------------- // the simplified .tga file resides in 'tga' // separate out the color information and place it in the color layer's buffer static void CreateTgaColorLayer( TCFilePtr dfPtr, SimpleTga* tga ) { u8* pixelPtr; u8 g, b; u16 r, u16Tmp; u32 row, col; u32 type; TCLayer* newLayer; // allocate a color layer dfPtr->lyColor = TCCreateLayer(); newLayer = dfPtr->lyColor; // determine the data format for the layer buffer // (make 'best fit' from .tga to layer color formats) // only 3 image types possible for a simplified tga image // ( color-index, monochrome, rgb (rgba) ). switch( tga->imageType ) { case 1: type = LY_IMAGE_COLOR_CI16; break; // color-index case 2: type = LY_IMAGE_COLOR_RGB24; break; // truecolor case 3: type = LY_IMAGE_COLOR_RGB24; break; // monochrome } // 1 LOD only from a tga file TCSetLayerAttributes( newLayer, type, tga->width, tga->height ); // allocate a data buffer for this layer if( (newLayer->data = TCSetLayerBuffer(newLayer)) == 0 ) { TCErrorMsg( "CreateImageColorLayerFromTga(): couldn't allocate layer buffer\n" ); return; } // get ria, g, b values from 'tga' structure; set layer pixel values individually pixelPtr = tga->imageData; for(row=0; rowheight; row++) { for(col=0; colwidth; col++) { switch(tga->imageType) { case 1: // colormapped image data switch(tga->pixelDepth) { case 1: r = (u16)(*pixelPtr); break; case 2: r = (u16)( *(u16*)pixelPtr ); break; default: TCErrorMsg( "CreateImageColorLayerFromTga(): unknown pixel depth for file %s colormapped data\n", dfPtr->name ); return; break; } break; case 2: // truecolor image data switch(tga->pixelDepth) { case 2: u16Tmp = *(u16*)pixelPtr; // duplicate msbs to maintain full range r = (u16)( ((u16Tmp & 0x7C00) >> 7) | ((u16Tmp & 0x7000) >> 12) ); g = (u8)( ((u16Tmp & 0x03E0) >> 2) | ((u16Tmp & 0x0380) >> 7) ); b = (u8)( ((u16Tmp & 0x001F) << 3) | ((u16Tmp & 0x001C) >> 2) ); break; case 3: // fall through case 4: // for 4, ignore alpha value (pixelPtr + 3) r = (u16)( *(pixelPtr + 2) ); g = *(pixelPtr + 1); b = *pixelPtr; break; default: TCErrorMsg( "CreateImageColorLayerFromTga(): unknown pixel depth for file %s data\n", dfPtr->name ); return; break; } break; case 3: // monochrome image data- set to rgb // if pixel depth > 1, use the average of three color values switch(tga->pixelDepth) { case 1: r = (u16)(*pixelPtr); g = (u8)r; b = (u8)r; break; case 2: u16Tmp = *(u16*)pixelPtr; // duplicate msbs to maintain full range r = (u16)( ((u16Tmp & 0x7C00) >> 7) | ((u16Tmp & 0x7000) >> 12) ); r += (u16)( ((u16Tmp & 0x03E0) >> 2) | ((u16Tmp & 0x0380) >> 7) ); r += (u16)( ((u16Tmp & 0x001F) << 3) | ((u16Tmp & 0x001C) >> 2) ); r /= 3; g = (u8)r; b = (u8)r; break; case 3: // fall through case 4: // for 4, ignore alpha value (pixelPtr + 3) r = (u16)(*(pixelPtr + 2)); // red r += (u16)(*(pixelPtr + 1)); // green r += (u16)(*pixelPtr); // blue r /= 3; // average the 3 color values g = (u8)r; b = (u8)r; break; default: TCErrorMsg( "CreateImageColorLayerFromTga(): unknown pixel depth for file %s intensity data\n", dfPtr->name ); return; break; } break; } // end switch( tga->imagetype ) TCSetLayerValue( newLayer, col, row, r, g, b ); pixelPtr += tga->pixelDepth; } // end 'col' for loop } // end 'row' for loop } //------------------------------------------------------------------------------------ // alpha layer can come from either alpha component of rgba (2 or 4 bytes/pixel) image // or from monochrome image static void CreateTgaAlphaLayer( TCFilePtr dfPtr, SimpleTga* tga ) { u8* pixelPtr; u32 row, col; u16 a, u16Tmp; TCLayer* newLayer; // alpha layer can only be created from the following image types: rgba, monochrome if( tga->imageType == 1 ) // color-indexed { return; } if( tga->imageType == 2 ) // true color { // only rgba formats are acceptable if( (tga->pixelDepth != 2) && (tga->pixelDepth != 4) ) { return; } } dfPtr->lyAlpha = TCCreateLayer(); newLayer = dfPtr->lyAlpha; TCSetLayerAttributes( newLayer, LY_IMAGE_ALPHA_A8, tga->width, tga->height ); newLayer->data = TCSetLayerBuffer(newLayer); // set alpha layer pixel values individually; // source could be either the alpha channel of a 4 byte/pixel image, the alpha bit of a 16-bit image, // or color data from a monochrome image pixelPtr = (u8*)(tga->imageData); for(row=0; rowheight; row++) { for(col=0; col< tga->width; col++) { // fetch, convert and set a pixel switch(tga->imageType) { case 2: // truecolor image data // use alpha bits if present switch(tga->pixelDepth) { case 2: // 1-bit alpha u16Tmp = *(u16*)pixelPtr; a = 0; if( (u16Tmp & 0x8000) == 0x8000 ) // check if alpha bit is set { a = 0x00FF; // (will be converted to u8 value when set) } break; case 4: // use the 8-bit alpha channel a = (u16)(*(pixelPtr + 3)); break; } break; case 3: // monochrome image data // if an alpha channel is available, use it. // otherwise, average the three color values to obtain alpha. switch(tga->pixelDepth) { case 1: // 8-bit monochrome a = (u16)(*pixelPtr); break; case 2: u16Tmp = *(u16*)pixelPtr; // 16-bit monochrome- use single alpha bit a = 0; if( (u16Tmp & 0x8000) == 0x8000 ) { a = 0x00FF; } break; case 3: // 24-bit monochrome- average the three color values a = (u16)(*(pixelPtr + 2)); // red a += (u16)(*(pixelPtr + 1)); // green a += (u16)(*pixelPtr); // blue a /= 3; break; case 4: // 32-bit monochrome- use alpha channel a = (u16)(*(pixelPtr + 3)); break; default: TCErrorMsg( "CreateImageAlphaLayerFromTga(): intensity file %s has unknown pixel depth\n", dfPtr->name ); return; break; } break; } // end switch( tga->imageType ) TCSetLayerValue( newLayer, col, row, a, 0, 0 ); pixelPtr += tga->pixelDepth; } // end 'col' for loop } // end 'row' for loop } //------------------------------------------------------------------------------------ static void CreateTgaPalTable( TCFilePtr dfPtr, SimpleTga* tga ) { u8 r, g, b, a; u32 i; u8* rawPtr; u16 u16Tmp; if( tga->paletteData == NULL ) { return; } dfPtr->palPtr = (TCPalTable*)TCCreatePalTable( tga->cMapLength ); rawPtr = tga->paletteData; for(i=0; i< tga->cMapLength; i++ ) { switch( tga->cMapDepth ) { case 2: // 15 or 16-bit case u16Tmp = *(u16*)rawPtr; // duplicate msbs to maintain full range r = (u8)( ((u16Tmp & 0x7C00) >> 7) | ((u16Tmp & 0x7000) >> 12) ); g = (u8)( ((u16Tmp & 0x03E0) >> 2) | ((u16Tmp & 0x0380) >> 7 ) ); b = (u8)( ((u16Tmp & 0x001F) << 3) | ((u16Tmp & 0x001C) >> 2 ) ); a = 0; if( (u16Tmp & 0x8000) == 0x0000 ) { a = 0xFF; } rawPtr += 2; break; case 3: // rgb24 r = *( rawPtr + 2 ); g = *( rawPtr + 1 ); b = *( rawPtr ); a = 0xFF; rawPtr += 3; break; case 4: // rgba r = *( rawPtr + 2 ); g = *( rawPtr + 1 ); b = *( rawPtr ); a = *( rawPtr + 3 ); rawPtr += 4; break; } // set this entry value TCSetPalTableValue( dfPtr->palPtr, i, r, g, b, a ); } } //------------------------------------------------------------------------------------ // .TGA SIMPLIFICATION SECTION //------------------------------------------------------------------------------------ // if required, decompress an RLE encoded image into pixel data. // place the uncompressed pixel data into dfData static void UncompressTgaImage( u8* rawImage, SimpleTga* tga ) { u32 i, size; u8* srcPtr, *dstPtr; u8* tmpBuff; // uncompress the rawImage if needed into a temporary buffer tmpBuff = NULL; switch(tga->imageType) { // uncompressed raw image types; use 'rawImage' as is case 0: // fall through case 1: // fall through case 2: // fall through case 3: // fall through srcPtr = rawImage; break; // RLE compressed raw image types case 9: // fall through case 10: // fall through case 11: // fall through // compute the size of the uncompressed image size = tga->width * tga->height * tga->pixelDepth; tmpBuff = (u8*)calloc( 1, size ); UnencodeTgaImage( rawImage, tmpBuff, tga->width, tga->height, tga->pixelDepth ); srcPtr = tmpBuff; break; } // copy the uncompressed pixels from 'srcPtr' ('tmpBuff' or 'rawImage') into 'tgaBuffer->imageData' // note: 'tgaBuffer->imageData' must already be allocated size = tga->width * tga->height * tga->pixelDepth; dstPtr = tga->imageData; for( i=0; i < size; i++ ) { *dstPtr++ = *srcPtr++; } // remap encoded imageType(s) to un-encoded equivalents // (makes for shorter switch statements in later functions) switch(tga->imageType) { case 9: tga->imageType = 1; break; case 10: tga->imageType = 2; break; case 11: tga->imageType = 3; break; } // free uncompressed raw image buffer if( tmpBuff != NULL ) { free( tmpBuff ); tmpBuff = NULL; } } //------------------------------------------------------------------------------------ // convert an RLE tga image into an un-encoded image // remove 'count' bytes from image- write data only // // riPtr: pointer to the raw compressed image data // dstPtr: pointer to a buffer large enough to hold the uncompressed image data // width: pixel width of the image // height: pixel height of the image // pixelDepth: pixel depth in bytes // static void UnencodeTgaImage( u8* riPtr, u8* dstPtr, u32 width, u32 height, u32 pixelDepth ) { u32 i,j; u32 totalCount, maxCount; u32 runLength; u32 riOffset, dstOffset; u8 countByte; riOffset = 0; dstOffset = 0; totalCount = 0; maxCount = width * height; // total # of pixels in the uncompressed image // perform read/write until all pixels have been decompressed while(totalCount < maxCount) { // read the count byte countByte = *((u8*)(riPtr + riOffset)); runLength = (u32)(countByte & 0x7F) + 1; // run length is lower 7 bits + 1 (1 to 128) riOffset += 1; // moves riPtr past countByte to pixel data // check high bit for RLE or un-encoded pixel run if( (countByte & 0x80) == 0x80 ) // RLE { // read the pixel data out as a repeating run of pixels for(i=0; i> 4); // bits 4 & 5 give source screen origin // destination image will have its origin in the upper left corner // source image could have its origin in any of the 4 screen corners // note: for origins with x on the right, must be careful not to copy // multi-byte pixels out backwards!! // pixels will always be written 'forwards' (dstPtr++), so dstXstep is the number of // bytes needed to move to the start of the next pixel in x, and dstYstep is the // number of bytes needed to relocate dstPtr from its final write position // to the start of the first pixel of the next successive row. switch(origin) { case 0x00: // lower left dstPtr = (u8*)(dstBuff + (width * pixelDepth * (height - 1))); dstXstep = pixelDepth; dstYstep = -1 * (width * pixelDepth) * 2; break; case 0x01: // lower right dstPtr = (u8*)(dstBuff + (width * pixelDepth * height) - pixelDepth); dstXstep = -(s32)pixelDepth; dstYstep = 0; break; case 0x02: // upper left // srcBuff data is already oriented correctly; no processing required if( dstBuff != NULL ) { free( dstBuff ); dstBuff = NULL; } return; break; case 0x03: // upper right dstPtr = (u8*)(dstBuff + (width * pixelDepth) - pixelDepth); dstXstep = -(s32)pixelDepth; dstYstep = ( width * pixelDepth ) * 2; break; } //----------------------------------- // copy and re-orient the srcBuff data srcPtr = (u8*)srcBuff; for(i=0; iimageType != 1) && (tga->imageType != 9) ) { return; } // accept only 8 or 16 bit indices if( (tga->pixelDepth != 1) && (tga->pixelDepth != 2) ) { TCErrorMsg( "AdjustTgaForPaletteOffset(): unsupported pixel depth\n" ); return; } vPtr = (void*)( tga->imageData ); for(i=0; i< tga->height; i++) { for(j=0; j< tga->width; j++) { if(tga->pixelDepth == 1) { *(u8*)vPtr = (u8)( *(u8*)(vPtr) + cMapStart); vPtr = (u8*)vPtr + 1; } else if(tga->pixelDepth == 2) { *(u16*)(vPtr) = (u16)( *(u16*)(vPtr) + cMapStart); vPtr = (u8*)vPtr + 2; } } } } //-----------------------------------------------------------------------------------------------