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.
352 lines
8.6 KiB
352 lines
8.6 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
// Purpose: |
|
// |
|
//===========================================================================// |
|
|
|
#include <stdlib.h> |
|
#include <stdio.h> |
|
#include "tier0/dbg.h" |
|
#include "filesystem.h" |
|
#include "bitmap/tgawriter.h" |
|
#include "tier1/utlbuffer.h" |
|
#include "bitmap/imageformat.h" |
|
#include "tier2/tier2.h" |
|
#include "tier2/fileutils.h" |
|
|
|
// memdbgon must be the last include file in a .cpp file!!! |
|
#include "tier0/memdbgon.h" |
|
|
|
namespace TGAWriter |
|
{ |
|
|
|
#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; |
|
}; |
|
#pragma pack() |
|
|
|
#define fputc myfputc |
|
#define fwrite myfwrite |
|
|
|
static void fputLittleShort( unsigned short s, CUtlBuffer &buffer ) |
|
{ |
|
buffer.PutChar( s & 0xff ); |
|
buffer.PutChar( s >> 8 ); |
|
} |
|
|
|
static inline void myfputc( unsigned char c, FileHandle_t fileHandle ) |
|
{ |
|
g_pFullFileSystem->Write( &c, 1, fileHandle ); |
|
} |
|
|
|
static inline void myfwrite( void const *data, int size1, int size2, FileHandle_t fileHandle ) |
|
{ |
|
g_pFullFileSystem->Write( data, size1 * size2, fileHandle ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// FIXME: assumes that we don't need to do gamma correction. |
|
//----------------------------------------------------------------------------- |
|
bool WriteToBuffer( unsigned char *pImageData, CUtlBuffer &buffer, int width, int height, |
|
ImageFormat srcFormat, ImageFormat dstFormat ) |
|
{ |
|
TGAHeader_t header; |
|
|
|
// Fix the dstFormat to match what actually is going to go into the file |
|
switch( dstFormat ) |
|
{ |
|
case IMAGE_FORMAT_RGB888: |
|
dstFormat = IMAGE_FORMAT_BGR888; |
|
break; |
|
#if defined( _X360 ) |
|
case IMAGE_FORMAT_LINEAR_RGB888: |
|
dstFormat = IMAGE_FORMAT_LINEAR_BGR888; |
|
break; |
|
#endif |
|
case IMAGE_FORMAT_RGBA8888: |
|
dstFormat = IMAGE_FORMAT_BGRA8888; |
|
break; |
|
} |
|
|
|
header.id_length = 0; // comment length |
|
header.colormap_type = 0; // ??? |
|
|
|
switch( dstFormat ) |
|
{ |
|
case IMAGE_FORMAT_BGR888: |
|
#if defined( _X360 ) |
|
case IMAGE_FORMAT_LINEAR_BGR888: |
|
#endif |
|
header.image_type = 2; // 24/32 bit uncompressed TGA |
|
header.pixel_size = 24; |
|
break; |
|
case IMAGE_FORMAT_BGRA8888: |
|
header.image_type = 2; // 24/32 bit uncompressed TGA |
|
header.pixel_size = 32; |
|
break; |
|
case IMAGE_FORMAT_I8: |
|
header.image_type = 1; // 8 bit uncompressed TGA |
|
header.pixel_size = 8; |
|
break; |
|
default: |
|
return false; |
|
break; |
|
} |
|
|
|
header.colormap_index = 0; |
|
header.colormap_length = 0; |
|
header.colormap_size = 0; |
|
header.x_origin = 0; |
|
header.y_origin = 0; |
|
header.width = ( unsigned short )width; |
|
header.height = ( unsigned short )height; |
|
header.attributes = 0x20; // Makes it so we don't have to vertically flip the image |
|
|
|
buffer.PutChar( header.id_length ); |
|
buffer.PutChar( header.colormap_type ); |
|
buffer.PutChar( header.image_type ); |
|
fputLittleShort( header.colormap_index, buffer ); |
|
fputLittleShort( header.colormap_length, buffer ); |
|
buffer.PutChar( header.colormap_size ); |
|
fputLittleShort( header.x_origin, buffer ); |
|
fputLittleShort( header.y_origin, buffer ); |
|
fputLittleShort( header.width, buffer ); |
|
fputLittleShort( header.height, buffer ); |
|
buffer.PutChar( header.pixel_size ); |
|
buffer.PutChar( header.attributes ); |
|
|
|
int nSizeInBytes = width * height * ImageLoader::SizeInBytes( dstFormat ); |
|
buffer.EnsureCapacity( buffer.TellPut() + nSizeInBytes ); |
|
unsigned char *pDst = (unsigned char*)buffer.PeekPut(); |
|
|
|
if ( !ImageLoader::ConvertImageFormat( pImageData, srcFormat, pDst, dstFormat, width, height ) ) |
|
return false; |
|
|
|
buffer.SeekPut( CUtlBuffer::SEEK_CURRENT, nSizeInBytes ); |
|
return true; |
|
} |
|
|
|
bool WriteDummyFileNoAlloc( const char *fileName, int width, int height, enum ImageFormat dstFormat ) |
|
{ |
|
TGAHeader_t tgaHeader; |
|
|
|
Assert( g_pFullFileSystem ); |
|
if( !g_pFullFileSystem ) |
|
{ |
|
return false; |
|
} |
|
COutputFile fp( fileName ); |
|
|
|
int nBytesPerPixel, nImageType, nPixelSize; |
|
|
|
switch( dstFormat ) |
|
{ |
|
case IMAGE_FORMAT_BGR888: |
|
#if defined( _X360 ) |
|
case IMAGE_FORMAT_LINEAR_BGR888: |
|
#endif |
|
nBytesPerPixel = 3; // 24/32 bit uncompressed TGA |
|
nPixelSize = 24; |
|
nImageType = 2; |
|
break; |
|
case IMAGE_FORMAT_BGRA8888: |
|
nBytesPerPixel = 4; // 24/32 bit uncompressed TGA |
|
nPixelSize = 32; |
|
nImageType = 2; |
|
break; |
|
case IMAGE_FORMAT_I8: |
|
nBytesPerPixel = 1; // 8 bit uncompressed TGA |
|
nPixelSize = 8; |
|
nImageType = 1; |
|
break; |
|
default: |
|
return false; |
|
break; |
|
} |
|
|
|
memset( &tgaHeader, 0, sizeof(tgaHeader) ); |
|
tgaHeader.id_length = 0; |
|
tgaHeader.image_type = (unsigned char) nImageType; |
|
tgaHeader.width = (unsigned short) width; |
|
tgaHeader.height = (unsigned short) height; |
|
tgaHeader.pixel_size = (unsigned char) nPixelSize; |
|
tgaHeader.attributes = 0x20; |
|
|
|
// Write the Targa header |
|
fp.Write( &tgaHeader, sizeof(TGAHeader_t) ); |
|
|
|
// Write out width * height black pixels |
|
unsigned char black[4] = { 0x1E, 0x9A, 0xFF, 0x00 }; |
|
for (int i = 0; i < width * height; i++) |
|
{ |
|
fp.Write( black, nBytesPerPixel ); |
|
} |
|
|
|
return true; |
|
} |
|
|
|
bool WriteTGAFile( const char *fileName, int width, int height, enum ImageFormat srcFormat, uint8 const *srcData, int nStride ) |
|
{ |
|
TGAHeader_t tgaHeader; |
|
|
|
COutputFile fp( fileName ); |
|
|
|
int nBytesPerPixel, nImageType, nPixelSize; |
|
|
|
bool bMustConvert = false; |
|
ImageFormat dstFormat = srcFormat; |
|
|
|
switch( srcFormat ) |
|
{ |
|
case IMAGE_FORMAT_BGR888: |
|
#if defined( _X360 ) |
|
case IMAGE_FORMAT_LINEAR_BGR888: |
|
#endif |
|
nBytesPerPixel = 3; // 24/32 bit uncompressed TGA |
|
nPixelSize = 24; |
|
nImageType = 2; |
|
break; |
|
case IMAGE_FORMAT_BGRA8888: |
|
nBytesPerPixel = 4; // 24/32 bit uncompressed TGA |
|
nPixelSize = 32; |
|
nImageType = 2; |
|
break; |
|
case IMAGE_FORMAT_RGBA8888: |
|
bMustConvert = true; |
|
dstFormat = IMAGE_FORMAT_BGRA8888; |
|
nBytesPerPixel = 4; // 24/32 bit uncompressed TGA |
|
nPixelSize = 32; |
|
nImageType = 2; |
|
break; |
|
case IMAGE_FORMAT_I8: |
|
nBytesPerPixel = 1; // 8 bit uncompressed TGA |
|
nPixelSize = 8; |
|
nImageType = 1; |
|
break; |
|
default: |
|
return false; |
|
break; |
|
} |
|
|
|
memset( &tgaHeader, 0, sizeof(tgaHeader) ); |
|
tgaHeader.id_length = 0; |
|
tgaHeader.image_type = (unsigned char) nImageType; |
|
tgaHeader.width = (unsigned short) width; |
|
tgaHeader.height = (unsigned short) height; |
|
tgaHeader.pixel_size = (unsigned char) nPixelSize; |
|
tgaHeader.attributes = 0x20; |
|
|
|
// Write the Targa header |
|
fp.Write( &tgaHeader, sizeof(TGAHeader_t) ); |
|
|
|
// Write out image data |
|
if ( bMustConvert ) |
|
{ |
|
uint8 *pLineBuf = new uint8[ nBytesPerPixel * width ]; |
|
while( height-- ) |
|
{ |
|
ImageLoader::ConvertImageFormat( srcData, srcFormat, pLineBuf, dstFormat, width, 1 ); |
|
fp.Write( pLineBuf, nBytesPerPixel * width ); |
|
srcData += nStride; |
|
} |
|
delete[] pLineBuf; |
|
} |
|
else |
|
{ |
|
while( height-- ) |
|
{ |
|
fp.Write( srcData, nBytesPerPixel * width ); |
|
srcData += nStride; |
|
} |
|
|
|
} |
|
return true; |
|
} |
|
|
|
|
|
bool WriteRectNoAlloc( unsigned char *pImageData, const char *fileName, int nXOrigin, int nYOrigin, int width, int height, int nStride, enum ImageFormat srcFormat ) |
|
{ |
|
Assert( g_pFullFileSystem ); |
|
if( !g_pFullFileSystem ) |
|
{ |
|
return false; |
|
} |
|
FileHandle_t fp; |
|
fp = g_pFullFileSystem->Open( fileName, "r+b" ); |
|
|
|
// |
|
// Read in the targa header |
|
// |
|
TGAHeader_t tgaHeader; |
|
g_pFullFileSystem->Read( &tgaHeader, sizeof(tgaHeader), fp ); |
|
|
|
|
|
int nBytesPerPixel, nPixelSize; |
|
|
|
switch( srcFormat ) |
|
{ |
|
case IMAGE_FORMAT_BGR888: |
|
#if defined( _X360 ) |
|
case IMAGE_FORMAT_LINEAR_BGR888: |
|
#endif |
|
nBytesPerPixel = 3; // 24/32 bit uncompressed TGA |
|
nPixelSize = 24; |
|
break; |
|
case IMAGE_FORMAT_BGRA8888: |
|
nBytesPerPixel = 4; // 24/32 bit uncompressed TGA |
|
nPixelSize = 32; |
|
break; |
|
case IMAGE_FORMAT_I8: |
|
nBytesPerPixel = 1; // 8 bit uncompressed TGA |
|
nPixelSize = 8; |
|
break; |
|
default: |
|
return false; |
|
break; |
|
} |
|
|
|
// Verify src data matches the targa we're going to write into |
|
if ( nPixelSize != tgaHeader.pixel_size ) |
|
{ |
|
Warning( "TGA doesn't match source data.\n" ); |
|
return false; |
|
} |
|
|
|
|
|
// Seek to the origin of the target subrect from the beginning of the file |
|
g_pFullFileSystem->Seek( fp, nBytesPerPixel * (tgaHeader.width * nYOrigin + nXOrigin), FILESYSTEM_SEEK_CURRENT ); |
|
|
|
unsigned char *pSrc = pImageData; |
|
|
|
// Run through each scanline of the incoming rect |
|
for (int row=0; row < height; row++ ) |
|
{ |
|
g_pFullFileSystem->Write( pSrc, nBytesPerPixel * width, fp ); |
|
|
|
// Advance src pointer to next scanline |
|
pSrc += nBytesPerPixel * nStride; |
|
|
|
// Seek ahead in the file |
|
g_pFullFileSystem->Seek( fp, nBytesPerPixel * ( tgaHeader.width - width ), FILESYSTEM_SEEK_CURRENT ); |
|
} |
|
|
|
g_pFullFileSystem->Close( fp ); |
|
return true; |
|
} |
|
|
|
} // end namespace TGAWriter |
|
|
|
|