|
|
//===== Copyright � 1996-2005, Valve Corporation, All rights reserved. ======//
//
// Purpose:
//
//=============================================================================//
#include <stdio.h>
#include "bitmap/tgaloader.h"
#include "tier0/dbg.h"
#include "basetypes.h"
#include <math.h>
#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<unsigned char> 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<unsigned char> &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<unsigned char> &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
|