//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// // // Purpose: // //=============================================================================// #include #include "bitmap/tgaloader.h" #include "tier0/dbg.h" #include "basetypes.h" #include #include "tier1/utlvector.h" #include "tier1/utlbuffer.h" #include "filesystem.h" #include "tier2/tier2.h" #include "byteswap.h" // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" namespace TGALoader { //----------------------------------------------------------------------------- // Format of the TGA header on disk //----------------------------------------------------------------------------- #pragma pack (1) struct TGAHeader_t { unsigned char id_length; unsigned char colormap_type; unsigned char image_type; unsigned short colormap_index; unsigned short colormap_length; unsigned char colormap_size; unsigned short x_origin; unsigned short y_origin; unsigned short width; unsigned short height; unsigned char pixel_size; unsigned char attributes; }; //----------------------------------------------------------------------------- // read a row into an RGBA8888 array. //----------------------------------------------------------------------------- typedef void (*ReadRowFunc_t)( CUtlBuffer& buf, TGAHeader_t const& header, unsigned char* pDstMemory ); //----------------------------------------------------------------------------- // output a RGBA8888 row into the destination format. //----------------------------------------------------------------------------- typedef void (*OutputRowFunc_t)( CUtlBuffer& buf, TGAHeader_t const& header, unsigned char* pDstMemory ); //----------------------------------------------------------------------------- // Important constants //----------------------------------------------------------------------------- #define TGA_MAX_COLORMAP_SIZE ( 256 * 4 ) #define TGA_MAX_ROW_LENGTH_IN_PIXELS IMAGE_MAX_DIM //----------------------------------------------------------------------------- // Globals... blech //----------------------------------------------------------------------------- static unsigned char g_ColorMap[TGA_MAX_COLORMAP_SIZE]; // run-length state from row to row for RLE images static bool g_IsRunLengthPacket; static int g_PixelsLeftInPacket; #ifndef _PS3 static unsigned char g_SrcGammaTable[256]; static unsigned char g_DstGammaTable[256]; #endif typedef CUtlMemory CTempImage; //----------------------------------------------------------------------------- // Reads in a file, sticks it into a UtlVector //----------------------------------------------------------------------------- static bool ReadFile( char const* pFileName, CTempImage& image, int maxbytes = -1 ) { Assert( pFileName ); Assert( g_pFullFileSystem ); if( !g_pFullFileSystem ) { return false; } FileHandle_t fileHandle; fileHandle = g_pFullFileSystem->Open( pFileName, "rb" ); if( !fileHandle ) return false; // How big is the file? long pos; if (maxbytes < 0) { pos = g_pFullFileSystem->Size( fileHandle ); } else { pos = maxbytes; } // Allocate enough space image.EnsureCapacity( pos ); // Back to the start of the file g_pFullFileSystem->Seek( fileHandle, 0, FILESYSTEM_SEEK_HEAD ); // Read the file into the vector memory int len = g_pFullFileSystem->Read( image.Base(), pos, fileHandle ); // Close the file g_pFullFileSystem->Close( fileHandle ); // It's an error if we didn't read in enough goodies return len == pos; } //----------------------------------------------------------------------------- // Reads in the TGA Header //----------------------------------------------------------------------------- static void ReadHeader( CUtlBuffer& buf, TGAHeader_t& header ) { buf.Get( &header, sizeof(TGAHeader_t) ); if ( CByteswap::IsMachineBigEndian() ) { CByteswap bs; bs.ActivateByteSwapping( true ); // Assume that TGAs are Win32-little-endian #pragma warning( push ) #pragma warning( disable : 4366 ) // warning C4366: The result of the unary '&' operator may be unaligned bs.SwapBuffer( &header.colormap_index ); bs.SwapBuffer( &header.colormap_length ); bs.SwapBuffer( &header.x_origin ); bs.SwapBuffer( &header.y_origin ); bs.SwapBuffer( &header.width ); bs.SwapBuffer( &header.height ); #pragma warning( pop ) } } //----------------------------------------------------------------------------- // Figures out TGA information //----------------------------------------------------------------------------- bool GetInfo( CUtlBuffer& buf, int *width, int *height, ImageFormat *imageFormat, float *sourceGamma ) { TGAHeader_t header; ReadHeader( buf, header ); switch( header.image_type ) { case 1: // 8 bit uncompressed TGA image case 3: // 8 bit monochrome uncompressed TGA image case 9: // 8 bit compressed TGA image *imageFormat = IMAGE_FORMAT_I8; break; case 2: // 24/32 bit uncompressed TGA image case 10: // 24/32 bit compressed TGA image if( header.pixel_size == 32 ) { *imageFormat = IMAGE_FORMAT_ABGR8888; } else if( header.pixel_size == 24 ) { *imageFormat = IMAGE_FORMAT_BGR888; } else { return false; } break; default: return false; break; } *width = header.width; *height = header.height; *sourceGamma = ARTWORK_GAMMA; return true; } //----------------------------------------------------------------------------- // Returns the minimum amount you have to load to get information about the TGA file //----------------------------------------------------------------------------- int TGAHeaderSize() { return sizeof( TGAHeader_t ); } //----------------------------------------------------------------------------- // Gets info about a TGA file //----------------------------------------------------------------------------- bool GetInfo( char const* pFileName, int *width, int *height, ImageFormat *imageFormat, float *sourceGamma ) { // temporary memory CTempImage image; // try to read in the file if (!ReadFile( pFileName, image, sizeof(TGAHeader_t) )) return false; // Serialization buffer CUtlBuffer buf( image.Base(), image.NumAllocated(), CUtlBuffer::READ_ONLY ); return GetInfo( buf, width, height, imageFormat, sourceGamma ); } //----------------------------------------------------------------------------- // Various output methods //----------------------------------------------------------------------------- void OutputRowRGBA8888( CUtlBuffer& buf, TGAHeader_t const& header, unsigned char* pDst ) { for( int i = 0; i < header.width; ++i, pDst += 4 ) { unsigned char* pSrc = (unsigned char*)buf.PeekGet(); pDst[0] = pSrc[0]; pDst[1] = pSrc[1]; pDst[2] = pSrc[2]; pDst[3] = pSrc[3]; buf.SeekGet( CUtlBuffer::SEEK_CURRENT, 4 ); } } void OutputRowABGR8888( CUtlBuffer& buf, TGAHeader_t const& header, unsigned char* pDst ) { for( int i = 0; i < header.width; ++i, pDst += 4 ) { unsigned char* pSrc = (unsigned char*)buf.PeekGet(); pDst[3] = pSrc[0]; pDst[2] = pSrc[1]; pDst[1] = pSrc[2]; pDst[0] = pSrc[3]; buf.SeekGet( CUtlBuffer::SEEK_CURRENT, 4 ); } } void OutputRowRGB888( CUtlBuffer& buf, TGAHeader_t const& header, unsigned char* pDst ) { for( int i = 0; i < header.width; ++i, pDst += 3 ) { unsigned char* pSrc = (unsigned char*)buf.PeekGet(); pDst[0] = pSrc[0]; pDst[1] = pSrc[1]; pDst[2] = pSrc[2]; buf.SeekGet( CUtlBuffer::SEEK_CURRENT, 4 ); } } void OutputRowBGR888( CUtlBuffer& buf, TGAHeader_t const& header, unsigned char* pDst ) { for( int i = 0; i < header.width; ++i, pDst += 3 ) { unsigned char* pSrc = (unsigned char*)buf.PeekGet(); pDst[2] = pSrc[0]; pDst[1] = pSrc[1]; pDst[0] = pSrc[2]; buf.SeekGet( CUtlBuffer::SEEK_CURRENT, 4 ); } } void OutputRowRGB565( CUtlBuffer& buf, TGAHeader_t const& header, unsigned char* pDst ) { Assert( 0 ); } void OutputRowI8( CUtlBuffer& buf, TGAHeader_t const& header, unsigned char* pDst ) { for( int i = 0; i < header.width; ++i, ++pDst ) { unsigned char* pSrc = (unsigned char*)buf.PeekGet(); if( ( pSrc[0] == pSrc[1] ) && ( pSrc[1] == pSrc[2] ) ) { pDst[0] = pSrc[0]; } else { pDst[0] = ( unsigned char )( 0.299f * pSrc[0] + 0.587f * pSrc[1] + 0.114f * pSrc[2] ); } buf.SeekGet( CUtlBuffer::SEEK_CURRENT, 4 ); } } void OutputRowIA88( CUtlBuffer& buf, TGAHeader_t const& header, unsigned char* pDst ) { for( int i = 0; i < header.width; ++i, pDst += 2 ) { unsigned char* pSrc = (unsigned char*)buf.PeekGet(); if( ( pSrc[0] == pSrc[1] ) && ( pSrc[1] == pSrc[2] ) ) { pDst[0] = pSrc[0]; } else { pDst[0] = ( unsigned char )( 0.299f * pSrc[0] + 0.587f * pSrc[1] + 0.114f * pSrc[2] ); } pDst[1] = pSrc[3]; buf.SeekGet( CUtlBuffer::SEEK_CURRENT, 4 ); } } void OutputRowA8( CUtlBuffer& buf, TGAHeader_t const& header, unsigned char* pDst ) { for( int i = 0; i < header.width; ++i, ++pDst ) { unsigned char* pSrc = (unsigned char*)buf.PeekGet(); pDst[0] = pSrc[3]; buf.SeekGet( CUtlBuffer::SEEK_CURRENT, 4 ); } } void OutputRowRGB888BlueScreen( CUtlBuffer& buf, TGAHeader_t const& header, unsigned char* pDst ) { for( int i = 0; i < header.width; ++i, pDst += 3 ) { unsigned char* pSrc = (unsigned char*)buf.PeekGet(); pDst[0] = (unsigned char)(( ( int )pSrc[0] * ( int )pSrc[3] ) >> 8); pDst[1] = (unsigned char)(( ( int )pSrc[1] * ( int )pSrc[3] ) >> 8); pDst[2] = (( ( ( ( int )pSrc[2] * ( int )pSrc[3] ) ) >> 8 ) + ( 255 - pSrc[3] )); buf.SeekGet( CUtlBuffer::SEEK_CURRENT, 4 ); } } void OutputRowBGR888BlueScreen( CUtlBuffer& buf, TGAHeader_t const& header, unsigned char* pDst ) { for( int i = 0; i < header.width; ++i, pDst += 3 ) { unsigned char* pSrc = (unsigned char*)buf.PeekGet(); pDst[2] = (unsigned char)(( ( int )pSrc[0] * ( int )pSrc[3] ) >> 8); pDst[1] = (unsigned char)(( ( int )pSrc[1] * ( int )pSrc[3] ) >> 8); pDst[0] = (unsigned char)(( ( ( ( int )pSrc[2] * ( int )pSrc[3] ) ) >> 8 ) + ( 255 - pSrc[3] )); buf.SeekGet( CUtlBuffer::SEEK_CURRENT, 4 ); } } void OutputRowARGB8888( CUtlBuffer& buf, TGAHeader_t const& header, unsigned char* pDst ) { for( int i = 0; i < header.width; ++i, pDst += 4 ) { unsigned char* pSrc = (unsigned char*)buf.PeekGet(); pDst[0] = pSrc[3]; pDst[1] = pSrc[0]; pDst[2] = pSrc[1]; pDst[3] = pSrc[2]; buf.SeekGet( CUtlBuffer::SEEK_CURRENT, 4 ); } } void OutputRowBGRA8888( CUtlBuffer& buf, TGAHeader_t const& header, unsigned char* pDst ) { for( int i = 0; i < header.width; ++i, pDst += 4 ) { unsigned char* pSrc = (unsigned char*)buf.PeekGet(); pDst[0] = pSrc[2]; pDst[1] = pSrc[1]; pDst[2] = pSrc[0]; pDst[3] = pSrc[3]; buf.SeekGet( CUtlBuffer::SEEK_CURRENT, 4 ); } } void OutputRowBGRX8888( CUtlBuffer& buf, TGAHeader_t const& header, unsigned char* pDst ) { for( int i = 0; i < header.width; ++i, pDst += 4 ) { unsigned char* pSrc = (unsigned char*)buf.PeekGet(); pDst[0] = pSrc[2]; pDst[1] = pSrc[1]; pDst[2] = pSrc[0]; pDst[3] = 255; buf.SeekGet( CUtlBuffer::SEEK_CURRENT, 4 ); } } void OutputRowBGR565( CUtlBuffer& buf, TGAHeader_t const& header, unsigned char* pDst ) { for( int i = 0; i < header.width; ++i, pDst += 2 ) { unsigned char* pSrc = (unsigned char*)buf.PeekGet(); unsigned short rgba = (pSrc[2] & 0x1F) | ((pSrc[1] & 0x3F) << 5) | ((pSrc[0] & 0x1F) << 11); pDst[0] = rgba & 0xFF; pDst[1] = rgba >> 8; buf.SeekGet( CUtlBuffer::SEEK_CURRENT, 4 ); } } void OutputRowBGRX5551( CUtlBuffer& buf, TGAHeader_t const& header, unsigned char* pDst ) { for( int i = 0; i < header.width; ++i, pDst += 2 ) { unsigned char* pSrc = (unsigned char*)buf.PeekGet(); unsigned short rgba = (pSrc[2] & 0x1F) | ((pSrc[1] & 0x1F) << 5) | ((pSrc[0] & 0x1F) << 10) | 0x8000; pDst[0] = rgba & 0xFF; pDst[1] = rgba >> 8; buf.SeekGet( CUtlBuffer::SEEK_CURRENT, 4 ); } } #ifndef _PS3 static OutputRowFunc_t GetOutputRowFunc( ImageFormat imageFormat ) { switch( imageFormat ) { case IMAGE_FORMAT_RGBA8888: return &OutputRowRGBA8888; case IMAGE_FORMAT_ABGR8888: return &OutputRowABGR8888; case IMAGE_FORMAT_RGB888: return &OutputRowRGB888; case IMAGE_FORMAT_BGR888: return &OutputRowBGR888; case IMAGE_FORMAT_RGB565: return &OutputRowRGB565; case IMAGE_FORMAT_I8: return &OutputRowI8; case IMAGE_FORMAT_IA88: return &OutputRowIA88; case IMAGE_FORMAT_A8: return &OutputRowA8; case IMAGE_FORMAT_RGB888_BLUESCREEN: return &OutputRowRGB888BlueScreen; case IMAGE_FORMAT_BGR888_BLUESCREEN: return &OutputRowBGR888BlueScreen; case IMAGE_FORMAT_ARGB8888: return &OutputRowARGB8888; case IMAGE_FORMAT_BGRA8888: return &OutputRowBGRA8888; case IMAGE_FORMAT_BGRX8888: return &OutputRowBGRX8888; case IMAGE_FORMAT_BGR565: return &OutputRowBGR565; case IMAGE_FORMAT_BGRX5551: return &OutputRowBGRX5551; #ifdef _X360 case IMAGE_FORMAT_LINEAR_RGB888: return &OutputRowRGB888; case IMAGE_FORMAT_LINEAR_BGR888: return &OutputRowBGR888; #endif default: return NULL; break; } } static void InitSourceGammaConversionTable( float srcGamma ) { static float lastSrcGamma = -1; if (lastSrcGamma == srcGamma) return; lastSrcGamma = srcGamma; ImageLoader::ConstructGammaTable( g_SrcGammaTable, srcGamma, 1.0f ); } static void InitDestGammaConversionTable( float dstGamma ) { static float lastDstGamma = -1; if (lastDstGamma == dstGamma) return; lastDstGamma = dstGamma; ImageLoader::ConstructGammaTable( g_DstGammaTable, 1.0f, dstGamma ); } #endif //----------------------------------------------------------------------------- // Reads an 8-bit palettized TGA image //----------------------------------------------------------------------------- void ReadRow8BitUncompressedWithColormap( CUtlBuffer& buf, TGAHeader_t const& header, unsigned char* pDst ) { int i; unsigned char* colormapEntry; switch( header.colormap_size ) { case 8: for( i = 0; i < header.width; ++i, pDst += 4 ) { int pal = buf.GetUnsignedChar(); colormapEntry = &g_ColorMap[pal]; pDst[0] = colormapEntry[0]; pDst[1] = colormapEntry[0]; pDst[2] = colormapEntry[0]; pDst[3] = 255; } break; case 24: for( i = 0; i < header.width; ++i, pDst += 4 ) { int pal = buf.GetUnsignedChar(); colormapEntry = &g_ColorMap[pal * 3]; pDst[0] = colormapEntry[2]; pDst[1] = colormapEntry[1]; pDst[2] = colormapEntry[0]; pDst[3] = 255; } break; case 32: for( i = 0; i < header.width; ++i, pDst += 4 ) { int pal = buf.GetUnsignedChar(); colormapEntry = &g_ColorMap[pal * 4]; pDst[0] = colormapEntry[3]; pDst[1] = colormapEntry[2]; pDst[2] = colormapEntry[1]; pDst[3] = colormapEntry[0]; } break; default: Assert( 0 ); break; } } //----------------------------------------------------------------------------- // Reads an 8-bit greyscale TGA image //----------------------------------------------------------------------------- void ReadRow8BitUncompressedWithoutColormap( CUtlBuffer& buf, TGAHeader_t const& header, unsigned char* pDst ) { for( int i = 0; i < header.width; ++i, pDst += 4 ) { pDst[0] = pDst[1] = pDst[2] = buf.GetUnsignedChar(); pDst[3] = 255; } } //----------------------------------------------------------------------------- // Reads a 24-bit TGA image //----------------------------------------------------------------------------- void ReadRow24BitUncompressedWithoutColormap( CUtlBuffer& buf, TGAHeader_t const& header, unsigned char* pDst ) { for( int i = 0; i < header.width; ++i, pDst += 4 ) { pDst[2] = buf.GetUnsignedChar(); pDst[1] = buf.GetUnsignedChar(); pDst[0] = buf.GetUnsignedChar(); pDst[3] = 255; } } //----------------------------------------------------------------------------- // Reads a 32-bit TGA image //----------------------------------------------------------------------------- void ReadRow32BitUncompressedWithoutColormap( CUtlBuffer& buf, TGAHeader_t const& header, unsigned char* pDst ) { for( int i = 0; i < header.width; ++i, pDst += 4 ) { pDst[2] = buf.GetUnsignedChar(); pDst[1] = buf.GetUnsignedChar(); pDst[0] = buf.GetUnsignedChar(); pDst[3] = buf.GetUnsignedChar(); } } //----------------------------------------------------------------------------- // Decompresses a run-length encoded row of bytes //----------------------------------------------------------------------------- static void DecompressRow( CUtlBuffer& buf, TGAHeader_t const& header, unsigned char* pDst ) { int bytesPerPixel = header.pixel_size >> 3; int pixelsLeftInRow = header.width; int numPixelsToProcess; #ifdef DBGFLAG_ASSERT unsigned char *pLast = pDst + header.width * bytesPerPixel; #endif unsigned char repeat[4] = {}; do { if( !g_PixelsLeftInPacket ) { // start a new packet. unsigned char packetHeader = buf.GetUnsignedChar(); g_PixelsLeftInPacket = 1 + ( packetHeader & 0x7f ); if( packetHeader & 0x80 ) { g_IsRunLengthPacket = true; // Read what I'm supposed to repeat for (int i = 0; i < bytesPerPixel; ++i) { repeat[i] = buf.GetUnsignedChar(); } } else { g_IsRunLengthPacket = false; } } // already in the middle of a packet of data. numPixelsToProcess = g_PixelsLeftInPacket; if( numPixelsToProcess > pixelsLeftInRow ) { numPixelsToProcess = pixelsLeftInRow; } if( g_IsRunLengthPacket ) { for( int i = numPixelsToProcess; --i >= 0; pDst += bytesPerPixel ) { for (int j = 0; j < bytesPerPixel; ++j ) { pDst[j] = repeat[j]; } } } else { buf.Get( pDst, numPixelsToProcess * bytesPerPixel ); pDst += numPixelsToProcess * bytesPerPixel; } g_PixelsLeftInPacket -= numPixelsToProcess; pixelsLeftInRow -= numPixelsToProcess; } while( pixelsLeftInRow ); Assert( pDst == pLast ); } //----------------------------------------------------------------------------- // Reads a compressed 8-bit palettized TGA image //----------------------------------------------------------------------------- void ReadRow8BitCompressedWithColormap( CUtlBuffer& buf, TGAHeader_t const& header, unsigned char* pDst ) { unsigned char rowI_8[TGA_MAX_ROW_LENGTH_IN_PIXELS]; DecompressRow( buf, header, rowI_8 ); CUtlBuffer uncompressedBuf( rowI_8, TGA_MAX_ROW_LENGTH_IN_PIXELS, CUtlBuffer::READ_ONLY ); ReadRow8BitUncompressedWithColormap( uncompressedBuf, header, pDst ); } //----------------------------------------------------------------------------- // Reads a compressed 8-bit greyscale TGA image //----------------------------------------------------------------------------- void ReadRow8BitCompressedWithoutColormap( CUtlBuffer& buf, TGAHeader_t const& header, unsigned char* pDst ) { unsigned char rowI_8[TGA_MAX_ROW_LENGTH_IN_PIXELS]; DecompressRow( buf, header, rowI_8 ); CUtlBuffer uncompressedBuf( rowI_8, TGA_MAX_ROW_LENGTH_IN_PIXELS, CUtlBuffer::READ_ONLY ); ReadRow8BitUncompressedWithoutColormap( uncompressedBuf, header, pDst ); } //----------------------------------------------------------------------------- // Reads a compressed 24-bit TGA image //----------------------------------------------------------------------------- void ReadRow24BitCompressedWithoutColormap( CUtlBuffer& buf, TGAHeader_t const& header, unsigned char* pDst ) { unsigned char rowBGR_888[TGA_MAX_ROW_LENGTH_IN_PIXELS * 3]; DecompressRow( buf, header, rowBGR_888 ); CUtlBuffer uncompressedBuf( rowBGR_888, TGA_MAX_ROW_LENGTH_IN_PIXELS * 3, CUtlBuffer::READ_ONLY ); ReadRow24BitUncompressedWithoutColormap( uncompressedBuf, header, pDst ); } //----------------------------------------------------------------------------- // Reads a compressed 32-bit TGA image //----------------------------------------------------------------------------- void ReadRow32BitCompressedWithoutColormap( CUtlBuffer& buf, TGAHeader_t const& header, unsigned char* pDst ) { unsigned char rowBGRA_8888[TGA_MAX_ROW_LENGTH_IN_PIXELS << 2]; DecompressRow( buf, header, rowBGRA_8888 ); CUtlBuffer uncompressedBuf( rowBGRA_8888, TGA_MAX_ROW_LENGTH_IN_PIXELS << 2, CUtlBuffer::READ_ONLY ); ReadRow32BitUncompressedWithoutColormap( uncompressedBuf, header, pDst ); } //----------------------------------------------------------------------------- // Method used to read the TGA //----------------------------------------------------------------------------- static ReadRowFunc_t GetReadRowFunc( TGAHeader_t const& header ) { switch( header.image_type ) { case 1: // 8 bit uncompressed TGA image case 3: // 8 bit monochrome uncompressed TGA image if( header.colormap_length ) { return &ReadRow8BitUncompressedWithColormap; } else { return &ReadRow8BitUncompressedWithoutColormap; } case 9: // 8 bit compressed TGA image if( header.colormap_length ) { return &ReadRow8BitCompressedWithColormap; } else { return &ReadRow8BitCompressedWithoutColormap; } case 2: // 24/32 bit uncompressed TGA image switch( header.pixel_size ) { case 24: return &ReadRow24BitUncompressedWithoutColormap; break; case 32: return &ReadRow32BitUncompressedWithoutColormap; break; default: //Error( "unsupported tga colordepth: %d", TGAHeader_t.pixel_size" ); return 0; break; } case 10: // 24/32 bit compressed TGA image if( header.colormap_length ) { // Error( "colormaps not support with 24/32 bit TGAs." ); return 0; } else { switch( header.pixel_size ) { case 24: return &ReadRow24BitCompressedWithoutColormap; break; case 32: return &ReadRow32BitCompressedWithoutColormap; break; default: //Error( "unsupported tga colordepth: %d", TGAHeader_t.pixel_size" ); return NULL; break; } } default: // Error( "unsupported tga pixel format" ); return 0; break; } } //----------------------------------------------------------------------------- // Reads the color map //----------------------------------------------------------------------------- static bool ReadColormap( CUtlBuffer& buf, TGAHeader_t const& header ) { int numColormapBytes = header.colormap_length * ( header.colormap_size >> 3 ); if( numColormapBytes > TGA_MAX_COLORMAP_SIZE ) { // Error( "colormap bigger than TGA_MAX_COLORMAP_SIZE" ); return false; } // read colormap buf.Get( g_ColorMap, numColormapBytes ); return true; } //----------------------------------------------------------------------------- // Reads the source image //----------------------------------------------------------------------------- static bool ReadSourceImage( CUtlBuffer& buf, TGAHeader_t& header, CTempImage& image ) { // Figure out our reading and riting ReadRowFunc_t ReadRowFunc = GetReadRowFunc( header ); if( !ReadRowFunc ) return false; // HACK: Fixme: We really shouldn't be using globals here // Init RLE vars g_PixelsLeftInPacket = 0; // Only allocate the memory once int memRequired = ImageLoader::GetMemRequired( header.width, header.height, 1, IMAGE_FORMAT_RGBA8888, false ); image.EnsureCapacity( memRequired ); // read each row and process it. Note the image is upside-down from // the way we want it. unsigned char* pDstBits; // flip the image vertically if necessary. if (header.attributes & 0x20) { for( int row = 0; row < header.height; ++row ) { pDstBits = image.Base() + row * header.width * ImageLoader::SizeInBytes(IMAGE_FORMAT_RGBA8888); ReadRowFunc( buf, header, pDstBits ); } } else { for( int row = header.height; --row >= 0; ) { pDstBits = image.Base() + row * header.width * ImageLoader::SizeInBytes(IMAGE_FORMAT_RGBA8888); ReadRowFunc( buf, header, pDstBits ); } } return true; } #ifndef _PS3 //----------------------------------------------------------------------------- // Outputs the final image //----------------------------------------------------------------------------- static bool OutputImage( CTempImage& image, TGAHeader_t& header, ImageFormat imageFormat, unsigned char* pDst ) { // How do we write? OutputRowFunc_t OutputRowFunc = GetOutputRowFunc( imageFormat ); if( !OutputRowFunc ) return false; CUtlBuffer buf( image.Base(), image.NumAllocated(), CUtlBuffer::READ_ONLY ); unsigned char* pDstBits; for( int row = 0; row < header.height; ++row ) { pDstBits = pDst + row * header.width * ImageLoader::SizeInBytes(imageFormat); OutputRowFunc( buf, header, pDstBits ); } return true; } #endif //----------------------------------------------------------------------------- // Parses the lovely bits previously read from disk //----------------------------------------------------------------------------- bool Load( unsigned char *pOutputImage, CUtlBuffer& buf, int width, int height, ImageFormat imageFormat, float targetGamma, bool mipmap ) { TGAHeader_t header; // Read the TGA header ReadHeader( buf, header ); // skip TARGA image comment if( header.id_length != 0 ) { buf.SeekGet( CUtlBuffer::SEEK_CURRENT, header.id_length ); } // Read the color map for palettized images if( header.colormap_length != 0 ) { if (!ReadColormap( buf, header )) return false; } // Stores the RGBA8888 temp version of the image which we'll use to // to do mipmapping... CTempImage tmpImage; if (!ReadSourceImage( buf, header, tmpImage )) return false; // Erg... what if header.width * header.height > width * height? // Then don't do anything, this is an error condition... if ((width * height) < (header.width * header.height)) return false; // Now that we've got the source image, generate the mip-map levels ImageLoader::GenerateMipmapLevels( tmpImage.Base(), pOutputImage, header.width, header.height, 1, imageFormat, ARTWORK_GAMMA, targetGamma, mipmap ? 0 : 1 ); return true; } //----------------------------------------------------------------------------- // Reads a TGA image from a file //----------------------------------------------------------------------------- bool Load( unsigned char *pOutputImage, const char *pFileName, int width, int height, ImageFormat imageFormat, float targetGamma, bool mipmap ) { Assert( pOutputImage && pFileName ); // memory for the file CTempImage vec; // Read that puppy in! if (!ReadFile( pFileName, vec )) return false; // Make an unserialization buffer CUtlBuffer buf( vec.Base(), vec.NumAllocated(), CUtlBuffer::READ_ONLY ); // Do the dirty deed return Load( pOutputImage, buf, width, height, imageFormat, targetGamma, mipmap ); } //----------------------------------------------------------------------------- // Creates a map in linear space //----------------------------------------------------------------------------- bool LoadRGBA8888( CUtlBuffer& buf, CUtlMemory &outputData, int &outWidth, int &outHeight ) { TGAHeader_t header; // Read the TGA header ReadHeader( buf, header ); // skip TARGA image comment if( header.id_length != 0 ) { buf.SeekGet( CUtlBuffer::SEEK_CURRENT, header.id_length ); } // Read the color map for palettized images if( header.colormap_length != 0 ) { if (!ReadColormap( buf, header )) return false; } // Stores the RGBA8888 temp version of the image which we'll use to // to do mipmapping... int memSize = ImageLoader::GetMemRequired( header.width, header.height, 1, IMAGE_FORMAT_RGBA8888, false ); outputData.EnsureCapacity( memSize ); if (!ReadSourceImage( buf, header, outputData )) return false; outWidth = header.width; outHeight = header.height; return true; } //----------------------------------------------------------------------------- // Reads a TGA, keeps it in RGBA8888 //----------------------------------------------------------------------------- bool LoadRGBA8888( const char *pFileName, CUtlMemory &outputData, int &outWidth, int &outHeight ) { Assert( pFileName ); // memory for the file CTempImage vec; // Read that puppy in! if (!ReadFile( pFileName, vec )) return false; // Make an unserialization buffer CUtlBuffer buf( vec.Base(), vec.NumAllocated(), CUtlBuffer::READ_ONLY ); // Do the dirty deed return LoadRGBA8888( buf, outputData, outWidth, outHeight ); } } // end namespace TGALoader