You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1001 lines
27 KiB
1001 lines
27 KiB
//========= Copyright 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"
|
|
|
|
// 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;
|
|
|
|
static unsigned char g_SrcGammaTable[256];
|
|
static unsigned char g_DstGammaTable[256];
|
|
|
|
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) );
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// 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 );
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
#if 0
|
|
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;
|
|
}
|
|
|
|
#if 0
|
|
//-----------------------------------------------------------------------------
|
|
// 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
|
|
|