openmohaa/code/gamespy/gt2/gt2action/TGAFile.cpp
2023-02-04 21:00:01 +01:00

1574 lines
No EOL
35 KiB
C++

/*
Modified by Chris Losinger for Smaller Animals Software's ImgLib/ImgDLL.
Esp. RGB->BGR and row order switches. 9/98
*/
#include <stdio.h>
#include <io.h>
/*
TGADefs.h - Tye and Constant declaration file
This File defines the Types, and the Constant Values used by the
TGAFile Class.
Created By: Timothy A. Bish
Created On: 08/18/98
*/
#ifndef TGADEFSH
#define TGADEFSH
#include <windows.h>
#pragma pack(1) // Align the structure on byte boundries...
// Possible Image Types
#define TGA_NOIMAGETYPE 0 // No Image Data Included in Image
#define TGA_MAPRGBTYPE 1 // Colormapped Image Data - No Compression
#define TGA_RAWRGBTYPE 2 // Truecolor Image Data - No Compression
#define TGA_RAWMONOTYPE 3 // Monochrome Image Data - No Compression
#define TGA_MAPENCODETYPE 9 // Colormapped Image Data - Compressed RLE
#define TGA_RAWENCODETYPE 10 // Truecolor Image Data - Compressed RLE
#define TGA_MONOENCODETYPE 11 // Monochrome Image Data - Compressed RLE
// Version Macro
#define TGA_VERSIONONE 1 // Version 1 File Format
#define TGA_VERSIONTWO 2 // Version 2 File Format
// File Read Write Modes
const int GREYSC = 0; // Image is Greyscale
const int COLOUR = 1; // Image is Color
const int MAPPED = 2; // Image has a Color Map
const int RLENCO = 4; // Image is RLE Encoded
// 18 Byte Sturcture representin the basic definitions of
// the image
typedef struct _aTGAHEADER
{
BYTE IDLength; // 00h Size of ID Field
BYTE ColorMapType; // 01h Color Map Type
BYTE ImageType; // 02h Image Type Code
WORD CMapStart; // 03h Color Map Origin
WORD CMapLength; // 05h Color Map Length
BYTE CMapDepth; // 07h Color Map Depth
WORD XOffset; // 08h X origin of Image
WORD YOffset; // 0Ah Y origin of Image
WORD Width; // 0Ch Width of Image
WORD Height; // 0Eh Height of Image
BYTE PixelDepth; // 10h Image Pixel Size
BYTE ImageDescriptor; // 11h Image Description Byte
} TGAHEADER;
// The footer is 26 Bytes in length and is always at the end of a
// TGA v2.0 file.
typedef struct _aTGAFOOTER
{
DWORD ExtensionOffset; // Extension Area Offset
DWORD DeveloperOffset; // Developer Directory Offset
CHAR Signature[18]; // TGA Signature
} TGAFOOTER;
typedef struct _aTGATAG
{
WORD TagNumber; // ID Number of the Tag
DWORD DataOffset; // Offset Location of the Tag
DWORD DataSize; // Size of the Tag Data in Bytes
} TGATAG;
// The extension area is basically the second header in the TGA v2.0
// file format.
typedef struct _aTGAEXTENSION
{
WORD Size; // Extension Size
CHAR AuthorName[41]; // Author Name
CHAR AuthorComment[324]; // Author Comment
WORD StampMonth; // Date/Time Stamp: Month
WORD StampDay; // Date/Time Stamp: Day
WORD StampYear; // Date/Time Stamp: Year
WORD StampHour; // Date/Time Stamp: Hour
WORD StampMinute; // Date/Time Stamp: Minute
WORD StampSecond; // Date/Time Stamp: Second
CHAR JobName[41]; // Job Name/ID
WORD JobHour; // Job Time: Hours
WORD JobMinute; // Job Time: Minutes
WORD JobSecond; // Job Time: Seconds
CHAR SoftwareId[41]; // Software ID
WORD VersionNumber; // Version Number of Software
BYTE VersionLetter; // Version Letter of Software
DWORD KeyColor; // Key Color
WORD PixelNumerator; // Pixel Aspect Ratio
WORD PixelDenominator; // Pixel Aspect Ratio
WORD GammaNumerator; // Gamma Value
WORD GammaDenominator; // Gamma Value
DWORD ColorOffset; // Color Correction Offset
DWORD StampOffset; // Postage Stamp Offset
DWORD ScanOffset; // Scanline Table Offset
BYTE AttributesType; // Attributes Type
} TGAEXTENSION;
// The Color Correction Table is an array of 2048 Bytes in length, which
// contians 256 entries used to store the values used for color remapping.
typedef struct _aTGACOLORCORRECTIONTABLE
{
SHORT Alpha; // Alpha Channel Seldom Used
SHORT Red; // Red Value of Correction
SHORT Green; // Green Value of Correction
SHORT Blue; // Green Value of Correction
} TGACOLORCORRECTIONTABLE;
#define TRIALVERSION -1 // LIB was not initialized with a registered key
#define IMGOK 0 // no err
#define MEMERR 1 // out of mem
#define FILEOPENERR 2 // error on file open
#define FILEREADERR 3 // error on file read
#define FILEWRITEERR 4 // error on file write
#define BADPARAMERR 5 // bad user param
#define INVALIDBMPERR 6 // bad BMP file
#define BMPRLEERR 7 // we don't do compressed (RLE) BMP files
#define INVALIDGIFERR 8 // bad GIF file
#define INVALIDJPGERR 9 // bad JPG file
#define IMGDCERR 10 // error with device context
#define IMGDIBERR 11 // problem with a GetDIBits call
#define NOGIFERR 12 // GIF support disabled
#define IMGNORESOURCE 13 // resource not found
#define CALLBACKCANCEL 14 // callback returned FALSE - operation aborted
#define INVALIDPNGERR 15 // bad PNG file
#define PNGCREATEERR 16 // internal PNG lib behavior - contact smaller animals s.w.
#define IMGDLLINTERNAL 17 // misc unexpected behavior error - contact smaller animals s.w.
#define IMGFONTERR 18 // trouble creating a font object
#define INTTIFFERR 19 // misc internal TIFF error
#define INVALIDTIFFERR 20 // invalid TIFF file
#define TIFFLZWNOTSUPPORTED 21 // this will not read TIFF-LZW iamges
#define INVALIDPCXERR 22 // invalid PCX image
#define CREATEBMPERR 23 // a call to the fn CreateCompatibleBitmap failed
#define IMGNOLINES 24 // end of an image while using single-line de/compression
#define GETDIBERR 25 // error during a call to GetDIBits
#define DEVOPNOSUPPORT 26 // device does not support an operation required by this function
#define INVALIDWMF 27 // invalid windows metafile
#define DEPTHMISMATCHERR 28 // the file was not of the requested bit-depth
#define INVALIDTGAERR 35 // Invalid TGA File
#define NOTGATHUMBNAIL 36 // No TGA Thumbnail in the file
#pragma pack()
#endif
class TGAFile
{
public:
// parameters
__int32 m_error;
public:
// operations
TGAFile();
BOOL IsFileTGA(const char * fileName);
LPVOID LoadTGA( const char *fileName, // Name of file
UINT32 *width, // Width in Pixel
UINT32 *height); // Height
HGLOBAL LoadTGA8Bit(const char *fileName, // Name of File
UINT32 *width, // Width in pixels
UINT32 *height, // Height
RGBQUAD *pal); // Palette of RGBQUADS
BOOL GetTGADims(const char *fileName,
UINT32 *width,
UINT32 *height);
BOOL SaveTGA32(const char * fileName, // output path
BYTE *inBuf, // RGB buffer
UINT32 width, // size
UINT32 height);
BOOL Save8BitTGA(const char * fileName, // output path
BYTE *colormappedbuffer, // one BYTE per pixel colomapped image
UINT32 width, // Width of image
UINT32 height, // Height of image
__int32 colors, // number of colors (number of RGBQUADs)
RGBQUAD *colormap); // array of RGBQUADs
HGLOBAL LoadTGAThumbnail(const char *fileName, // Name of file
UINT32 *width, // Width in Pixel
UINT32 *height); // Height
private:
// Parameters
char TGA_ImageIDFeild[256];// Text in file
BYTE TGA_Attribute; // Number of attribute bytes per pixel
// i.e. 1 for T16 and 8 for T32
UINT32 mode; // Mode of current Read or Write
// RLE Decompression Variables
BYTE Red, // Stores pixel value for
Grn, // RLE series of oixels
Blu,
Alpha;
UINT32 l; // Used when 8 bit files use RLE
__int32 RLECount, RLEFlag; // Indicates whether the RLE series
// is still going or is finished
private:
// Operations
__int32 TGA_GetFileVersion(FILE *fp); // Determines whether this is a V1.0
// or V2.0 TGA File
BOOL TGA_GetMapEntry(BYTE *Red, // Get the Color Values out of the
BYTE *Green, // Color map in the TGA File
BYTE *Blue, // Return TRUE on Success
BYTE *Alpha,
FILE *fp,
UINT32 Depth);
// version that takes a file ptr
BOOL TGA_GetPixelValue(BYTE *Red, // Get and parse a single pixel value
BYTE *Grn, // from the TGA file. Handles Unencoding
BYTE *Blu, // of RLE encoded files.
BYTE *Alp, // plus Alpha (08jan00/bgw)
FILE *fp,
UINT32 PixelDepth,
RGBQUAD *CMap);
// version that takes a buffer ptr
BOOL TGA_GetPixelValue(BYTE *Red, // Get and parse a single pixel value
BYTE *Grn, // from the TGA file. Handles Unencoding
BYTE *Blu, // of RLE encoded files.
BYTE *Alp, // plus Alpha (08jan00/bgw)
BYTE ** ppTGAData,
UINT32 PixelDepth,
RGBQUAD *CMap);
};
//#define MAX_IMAGEREAD_BUFFER 65535
//BYTE * gpImageReadBuffer = NULL;
//long glImageReadBufferSize = 0;
//BYTE * gpImageReadBufPos = NULL;
//
//
//typedef struct tagTGAColorComponents
// {
// BYTE red;
// BYTE green;
// BYTE blue;
// BYTE alpha;
// } TGAColorComponents;
/* TGA File REader Classs Implementation File
This Implementation Allows the reading of TGA (Targa) Files
into an RGB buffer. Also the class allows an RGB Buffer to be
written to a TGA File. There is also a function to determine
the dimensions of a TGA file.
Created By: Timothy A. Bish
Created On: 08/17/98
*/
////////////////////////////////////////////////////////////////////////////
// No Much going on here
TGAFile::TGAFile()
{
m_error = IMGOK;
}
////////////////////////////////////////////////////////////////////////////
// GetTGADimns
// Find dims of the image in a TGA file
// Returns - TRUE on success
BOOL
TGAFile::GetTGADims(const char * fileName, UINT32 * width, UINT32 * height)
{
// for safety
*width = 0;
*height = 0;
FILE * fp;
TGAHEADER tgahd;
// Init the file Header to all zeros
ZeroMemory(&tgahd, sizeof(tgahd));
// init
m_error = IMGOK;
fp = fopen(fileName, "rb");
if(fp == NULL)
{
m_error = FILEOPENERR;
return FALSE;
}
// Get the Header
if(fread(&tgahd, 1, sizeof(TGAHEADER), fp) != sizeof(TGAHEADER))
{
m_error = FILEREADERR;
fclose(fp);
return FALSE;
}
// Check fo valid data in structure
if(tgahd.PixelDepth > 32)
{
// I don't do Pixel Depths Bigger than 32
m_error = INVALIDTGAERR;
fclose(fp);
return NULL;
}
// Anything other than the standard TGA types
// and I quit
switch(tgahd.ImageType)
{
case TGA_MAPRGBTYPE:
case TGA_RAWRGBTYPE:
case TGA_RAWMONOTYPE:
case TGA_MAPENCODETYPE:
case TGA_RAWENCODETYPE:
case TGA_MONOENCODETYPE:
break;
default:
m_error = INVALIDTGAERR;
fclose(fp);
return NULL;
}
// Grab the Image dimensions
*width = tgahd.Width;
*height = tgahd.Height;
fclose(fp);
return TRUE;
}
/*****************************************************************************
* NAME:
* TGAFile::IsFileTGA
*
* DESCRIPTION:
* Description goes here...
*
*******************************************************************************/
BOOL
TGAFile::IsFileTGA(const char * fileName)
{
TGAHEADER tgahd;
FILE * fp;
ZeroMemory(&tgahd, sizeof(tgahd));
fp = fopen(fileName, "rb");
long rc = fread(&tgahd, 1, sizeof(TGAHEADER), fp);
fclose(fp);
if( (rc == sizeof(TGAHEADER)) && // must be big enough for a header...
(tgahd.PixelDepth == 32) && // 32-bit TGAs only
(tgahd.ImageType == TGA_RAWRGBTYPE) ) // Raw RGBA format only
return(TRUE);
return(FALSE);
}
////////////////////////////////////////////////////////////////////////////
// LoadTGA
// load a .TGA file - 1,4,8,24,32 bit
// allocates and returns an RGB buffer containing the image.
// modifies width and height accordingly - NULL, 0, 0 on error
LPVOID
TGAFile::LoadTGA(const char * fileName, UINT32 * width, UINT32 * height)
{
LPVOID pNew = NULL;
BYTE * pRGB = NULL;
BYTE Alpha;
TGAHEADER tgahd;
BYTE TGA_Origin = 0;
RGBQUAD CColMap[256];
// for safety
*width = 0;
*height = 0;
// init
m_error = IMGOK;
// Init the file Header to all zeros
ZeroMemory(&tgahd, sizeof(tgahd));
FILE * fp;
fp = fopen(fileName, "rb");
if(fp == NULL)
{
m_error = FILEOPENERR;
return FALSE;
}
// Read the TGA Header
long rc = fread(&tgahd, 1, sizeof(TGAHEADER), fp);
if(rc != sizeof(TGAHEADER))
{
m_error = FILEREADERR;
fclose(fp);
return NULL;
}
// Check fo valid data in structure
if((tgahd.PixelDepth> 32) || (tgahd.PixelDepth<8))
{
// I don't do Pixel Depths Bigger than 32
m_error = INVALIDTGAERR;
fclose(fp);
return NULL;
}
// Anything other than the standard TGA types
// and I quit
switch(tgahd.ImageType)
{
case TGA_MAPRGBTYPE:
case TGA_RAWRGBTYPE:
case TGA_RAWMONOTYPE:
case TGA_MAPENCODETYPE:
case TGA_RAWENCODETYPE:
case TGA_MONOENCODETYPE:
break;
default:
m_error = INVALIDTGAERR;
fclose(fp);
return NULL;
}
// Set the number of Color Planes
if(tgahd.ImageType == TGA_RAWMONOTYPE)
{
mode = GREYSC;
}
else
{
mode = COLOUR;
}
// Read the ID Descriptor if present
if(tgahd.IDLength != 0)
{
// Read the TGA Comments
long rc = fread(&TGA_ImageIDFeild, 1, tgahd.IDLength, fp);
if(rc != tgahd.IDLength)
{
m_error = FILEREADERR;
fclose(fp);
return NULL;
}
}
// Parse the Image Descriptor
TGA_Attribute = (BYTE)(tgahd.ImageDescriptor & 0x0f);
TGA_Origin = (BYTE)((tgahd.ImageDescriptor & 0x20) / 32);
// If present read the color map
if(tgahd.ColorMapType != 0)
{
// Get the color map
for(__int32 i = 0; i < (tgahd.CMapStart + tgahd.CMapLength); i++)
{
TGA_GetMapEntry(&CColMap[i].rgbRed, &CColMap[i].rgbGreen, &CColMap[i].rgbBlue, &Alpha, fp, tgahd.CMapDepth);
}
// If the TGA file actually needs the color map
// Set the mode to show this
if((tgahd.ImageType != TGA_RAWRGBTYPE) && (tgahd.ImageType != TGA_RAWMONOTYPE) && (tgahd.ImageType != TGA_RAWENCODETYPE))
mode = mode | MAPPED;
}
// Check Run Length Encoding
if((tgahd.ImageType == TGA_MAPENCODETYPE) || (tgahd.ImageType == TGA_RAWENCODETYPE) || (tgahd.ImageType == TGA_MONOENCODETYPE))
mode = mode | RLENCO;
long lImgDataSize = (tgahd.Height * tgahd.Width) * (tgahd.PixelDepth / 8);
// Allocate the memory buffers
pNew = malloc(lImgDataSize);
// pNew = (LPVOID) theApp.m_TGABuffer.GetBuffer((size_t)(tgahd.Width * tgahd.Height * 4));
if(pNew == NULL)
{
m_error = MEMERR;
fclose(fp);
return NULL;
}
else
pRGB = (BYTE *)pNew;
// RGB from image Data
//DWORD destOffset = 0;
RLECount = 0;
RLEFlag = 0;
// //
// // (re-)alocate a local image buffer to read the TGA formatted
// // data into. this avoids the huge critical-section delays in
// // every call to getc().
// //
// long lBufSize = lImgDataSize + 16; // slop
// if(lBufSize < MAX_IMAGEREAD_BUFFER)
// lBufSize = MAX_IMAGEREAD_BUFFER;
// if(glImageReadBufferSize < lBufSize)
// {
// if(gpImageReadBuffer)
// {
// free(gpImageReadBuffer);
// gpImageReadBuffer = NULL; // tidy
// gpImageReadBufPos = NULL;
// }
//
// glImageReadBufferSize = lBufSize;
// gpImageReadBuffer = (BYTE *) malloc(glImageReadBufferSize);
//
// if(!gpImageReadBuffer)
// {
// glImageReadBufferSize = 0;
// return NULL; // v. bad news.
// }
// TRACE("* (Re-)Allocated local TGA data buffer: %d bytes\n", glImageReadBufferSize);
// }
//
// Read the TGA format data into the local buffer
//
long lTGABytesRead = fread(pRGB, 1, lImgDataSize, fp);
if(lTGABytesRead != lImgDataSize)
{
// ASSERT(0);
//free(pNew);
return NULL;
}
// Grab the image dimensions
*width = tgahd.Width;
*height = tgahd.Height;
// Clean Up
fclose(fp);
m_error = IMGOK;
return pNew;
///////////////
// all out...dorks didn't realize that TGA's memory format == DIBSections!
//////////////
#if 0
//
// Read the TGA format data into the local buffer
//
long lTGABytesRead = fread(gpImageReadBuffer, 1, lImgDataSize, fp);
if(lTGABytesRead != lImgDataSize)
{
ASSERT(0);
return NULL;
}
gpImageReadBufPos = gpImageReadBuffer;
// copy DWORDs instead of bytes...
UINT32 * pPixel = (UINT32 *) pRGB;
UINT32 * pReadBufPixel = (UINT32 *) gpImageReadBufPos;
for(UINT32 row = 0; row < tgahd.Height; row++)
{
for(UINT32 col = 0; col < tgahd.Width; col++)
{
// Reset RLE Counters
BYTE Red, Grn, Blu, Alp;
TGA_GetPixelValue(&Red, &Grn, &Blu, &Alp, &gpImageReadBufPos, tgahd.PixelDepth, CColMap);
// Invert if the image origin is in Bottom left
if(TGA_Origin != 0)
{ // Bottom Left Origin
destOffset = ((tgahd.Height -1) -row) * tgahd.Width * 4 + col * 4;
*(pRGB + destOffset + 0) = Red;
*(pRGB + destOffset + 1) = Grn;
*(pRGB + destOffset + 2) = Blu;
*(pRGB + destOffset + 3) = Alp;
}
else
{ // Top Left Origin
*(pRGB + destOffset + 0) = Red;
*(pRGB + destOffset + 1) = Grn;
*(pRGB + destOffset + 2) = Blu;
*(pRGB + destOffset + 3) = Alp;
destOffset += 4;
}
#if 0 // even faster!
// Red = *(gpImageReadBufPos++);
// Grn = *(gpImageReadBufPos++);
// Blu = *(gpImageReadBufPos++);
// Alp = *(gpImageReadBufPos++);
//
// *(pRGB + destOffset + 0) = Red;
// *(pRGB + destOffset + 1) = Grn;
// *(pRGB + destOffset + 2) = Blu;
// *(pRGB + destOffset + 3) = Alp;
// destOffset += 4;
// TGAColorComponents rgbaPixel;
*(pPixel++) = *(pReadBufPixel++);
// BYTE * p = pRGB + destOffset;
// *(p++) = *(gpImageReadBufPos++);
// *(p++) = *(gpImageReadBufPos++);
// *(p++) = *(gpImageReadBufPos++);
// *(p++) = *(gpImageReadBufPos++);
// destOffset += 4;
#endif
} // loop col
} // loop row
// Grab the image dimensions
*width = tgahd.Width;
*height = tgahd.Height;
// Clean Up
fclose(fp);
m_error = IMGOK;
return pNew;
#endif
}
///////////////////////////////////////////////////////////////////////////////////
// LoadTGA8Bit
// Loads in an 8 Bit buffer and the color palette that is
// associated with that buffer, if the image is 8 Bit, else
// it sets global error to DEPTHMISMATCHERR
HGLOBAL
TGAFile::LoadTGA8Bit(const char * fileName, // Name of File
UINT32 * width, // Width in pixels
UINT32 * height, // Height
RGBQUAD * pal) // Palette of RGBQUADS
{
HGLOBAL hNew = NULL;
BYTE * pRGB = NULL;
BYTE Alpha;
TGAHEADER tgahd;
BYTE TGA_Origin = 0;
// for safety
*width = 0;
*height = 0;
// init
m_error = IMGOK;
// Init the file Header to all zeros
ZeroMemory(&tgahd, sizeof(tgahd));
// Init the Palette to all zeros
ZeroMemory(pal, sizeof(pal));
FILE * fp;
fp = fopen(fileName, "rb");
if(fp == NULL)
{
m_error = FILEOPENERR;
return FALSE;
}
else
{
// Read the TGA Header
long rc = fread(&tgahd, 1, sizeof(TGAHEADER), fp);
if(rc != sizeof(TGAHEADER))
{
m_error = FILEREADERR;
fclose(fp);
return NULL;
}
// Check fo valid data in structure
if(tgahd.PixelDepth> 8)
{
// Not an 8bit Image
m_error = DEPTHMISMATCHERR;
fclose(fp);
return NULL;
}
// Anything other than the standard TGA types
// and I quit
switch(tgahd.ImageType)
{
case TGA_MAPRGBTYPE:
case TGA_MAPENCODETYPE:
break;
default:
m_error = DEPTHMISMATCHERR;
fclose(fp);
return NULL;
}
// Set the Color Mode
if(tgahd.ImageType == TGA_RAWMONOTYPE)
{
mode = GREYSC;
}
else
{
mode = COLOUR;
}
// Read the ID Descriptor if present
if(tgahd.IDLength != 0)
{
// Read the TGA Comments
long rc = fread(&TGA_ImageIDFeild, 1, tgahd.IDLength, fp);
if(rc != tgahd.IDLength)
{
m_error = FILEREADERR;
fclose(fp);
return NULL;
}
}
// Parse the Image Descriptor
TGA_Attribute = (BYTE)(tgahd.ImageDescriptor & 0x0f);
TGA_Origin = (BYTE)((tgahd.ImageDescriptor & 0x20) / 32);
// If present read the color map
if(tgahd.ColorMapType != 0)
{
// Get the color map
for(__int32 i = 0; i < (tgahd.CMapStart + tgahd.CMapLength); i++)
{
TGA_GetMapEntry(&pal[i].rgbRed, &pal[i].rgbGreen, &pal[i].rgbBlue, &Alpha, fp, tgahd.CMapDepth);
}
}
else
{
m_error = INVALIDTGAERR;
fclose(fp);
return NULL;
}
// Check Run Length Encoding
if((tgahd.ImageType == TGA_MAPENCODETYPE) || (tgahd.ImageType == TGA_RAWENCODETYPE) || (tgahd.ImageType == TGA_MONOENCODETYPE))
mode = mode | RLENCO;
// Allocate the memory buffers
hNew = GlobalAlloc(GHND, tgahd.Width * tgahd.Height);
if(hNew == NULL)
{
m_error = MEMERR;
fclose(fp);
return NULL;
}
else
{
pRGB = (BYTE *)GlobalLock(hNew);
if(pRGB == NULL)
{
m_error = MEMERR;
fclose(fp);
return NULL;
}
}
// RGB from image Data
DWORD destOffset = 0;
RLECount = 0;
RLEFlag = 0;
for(UINT32 row = 0; row < tgahd.Height; row++)
{
for(UINT32 col = 0; col < tgahd.Width; col++)
{
BYTE Red, Grn, Blu, Alp;
// Reset RLE Counters
TGA_GetPixelValue(&Red, &Grn, &Blu, &Alp, fp, tgahd.PixelDepth, pal);
// Invert if the image origin is in Bottom left
if(TGA_Origin == 0)
{ // Bottom Left Origin
*(pRGB + destOffset) = Red;
destOffset = ((tgahd.Height -1) -row) * tgahd.Width + col;
}
else
{ // Top Left Origin
*(pRGB + destOffset) = Red;
destOffset++;
}
}
}
}
// Grab the image dimensions
*width = tgahd.Width;
*height = tgahd.Height;
// Clean Up
GlobalUnlock(hNew);
fclose(fp);
m_error = IMGOK;
return hNew;
}
///////////////////////////////////////////////////////////////////////////////////
// LoadTGAThumbNail
// Checks the TGA file for the existance of a thumbnail image
// Reads and returns it in a 24 Bit RGB Buffer if a thumbnail
// exists. Returns NULL if there isn't one sets TGANOTHUMBNAIL
HGLOBAL
TGAFile::LoadTGAThumbnail
(
const char * fileName, // Name of file
UINT32 * width, // Width in Pixel
UINT32 * height // Height
)
{
HGLOBAL hNew = NULL;
TGAHEADER tgahd;
TGAFOOTER tgaft;
TGAEXTENSION tgaext;
BYTE stampWidth = 0, stampHeight = 0;
long lResult;
// for safety
*width = 0;
*height = 0;
// init
m_error = IMGOK;
// Init the file structs
ZeroMemory(&tgahd, sizeof(tgahd));
ZeroMemory(&tgaft, sizeof(tgaft));
ZeroMemory(&tgaext, sizeof(tgaext));
FILE * fp;
fp = fopen(fileName, "rb");
if(fp == NULL)
{
m_error = FILEOPENERR;
return FALSE;
}
else
{
// Read the TGA Header
long rc = fread(&tgahd, 1, sizeof(TGAHEADER), fp);
if(rc != sizeof(TGAHEADER))
{
m_error = FILEREADERR;
fclose(fp);
return NULL;
}
// Check fo valid data in structure
if((tgahd.PixelDepth> 32) || (tgahd.PixelDepth<8))
{
// I don't do Pixel Depths Bigger than 32
m_error = INVALIDTGAERR;
fclose(fp);
return NULL;
}
// Anything other than the standard TGA types
// and I quit
switch(tgahd.ImageType)
{
case TGA_MAPRGBTYPE:
case TGA_RAWRGBTYPE:
case TGA_RAWMONOTYPE:
case TGA_MAPENCODETYPE:
case TGA_RAWENCODETYPE:
case TGA_MONOENCODETYPE:
break;
default:
m_error = INVALIDTGAERR;
fclose(fp);
return NULL;
}
// Set the number of Color Planes
if(tgahd.ImageType == TGA_RAWMONOTYPE)
{
mode = GREYSC;
}
else
{
mode = COLOUR;
}
// Check for file Version
// Seek the last 26 bytes of the file
if(fseek(fp, -26, SEEK_END))
{
// Error Quit
fclose(fp);
return NULL;
}
// Read in the last 26 Bytes of the File
lResult = fread(&tgaft, 1, sizeof(TGAFOOTER), fp);
if(lResult != sizeof(TGAFOOTER))
{
m_error = FILEREADERR;
fclose(fp);
return NULL;
}
// Check for the Marker at the end of the file
lResult = strcmp(tgaft.Signature, "TRUEVISION-XFILE.");
if(lResult != 0)
{
// Not V2.0 File no Thumbnail
m_error = NOTGATHUMBNAIL;
fclose(fp);
return NULL;
}
// Check for the existance of an extension area
if(tgaft.ExtensionOffset == 0)
{
// No Thumbnail in this file
m_error = NOTGATHUMBNAIL;
fclose(fp);
return NULL;
}
// Seek the extension area
if(fseek(fp, tgaft.ExtensionOffset, SEEK_SET))
{
// Error Quit
m_error = FILEREADERR;
fclose(fp);
return NULL;
}
// Read in the last 26 Bytes of the File
lResult = fread(&tgaext, 1, sizeof(TGAEXTENSION), fp);
if(lResult != sizeof(TGAEXTENSION))
{
m_error = FILEREADERR;
fclose(fp);
return NULL;
}
// Seek the thumbnail image
if(fseek(fp, tgaext.StampOffset, SEEK_SET))
{
// Error Quit
m_error = FILEREADERR;
fclose(fp);
return NULL;
}
// Read the Width and Height from the first two bytes
// of the postage stamp data.
fread(&stampWidth, 1, 1, fp);
fread(&stampHeight, 1, 1, fp);
if((stampWidth <= 0) || (stampWidth> 64) || (stampHeight <= 0) || (stampHeight> 64))
{
m_error = INVALIDTGAERR;
fclose(fp);
return NULL;
}
}
// Clean Up
*width = stampWidth;
*height = stampHeight;
fclose(fp);
// Return Okay Image
return hNew;
}
///////////////////////////////////////////////////////////////////////////////////
// SaveTGA32
// Saves the buffer as a 32 bit True Color Image in TGA format
BOOL
TGAFile::SaveTGA32
(
const char * fileName, // output path
BYTE * inBuf, // BGR buffer
UINT32 width, // size in pixels
UINT32 height
)
{
long lResult = 0;
TGAHEADER tgahd;
m_error = IMGOK;
// Init the file Header to all zeros
ZeroMemory(&tgahd, sizeof(tgahd));
if(inBuf == NULL)
{
m_error = BADPARAMERR;
return FALSE;
}
if((width == 0) || (height == 0))
{
m_error = BADPARAMERR;
return FALSE;
}
// Initialize the Header for the File
tgahd.IDLength = 0;
tgahd.ColorMapType = 0;
tgahd.ImageType = TGA_RAWRGBTYPE;
tgahd.CMapStart = 0;
tgahd.CMapLength = 0;
tgahd.CMapDepth = 0;
tgahd.XOffset = 0;
tgahd.YOffset = 0;
tgahd.Width = (WORD)width;
tgahd.Height = (WORD)height;
tgahd.PixelDepth = 32;
tgahd.ImageDescriptor = 0;
// Open a file to write
FILE * fp = fopen(fileName, "wb");
if(fp == NULL)
{
m_error = FILEOPENERR;
return FALSE;
}
// Write the Header to File.
if((lResult = fwrite(&tgahd, 1, sizeof(TGAHEADER), fp)) != 18)
{
fclose(fp);
m_error = FILEWRITEERR;
return FALSE;
}
// Wrte the Bytes to file
DWORD destOffset = 0;
BYTE temp = 0;
DWORD rowStride = tgahd.Width * 4;
for(UINT32 row = 0; row < tgahd.Height; row++)
{
//DWORD rowOffset = rowStride * row;
DWORD rowOffset = rowStride *((tgahd.Height -1) -row);
for(UINT32 col = 0; col < tgahd.Width; col++)
{
destOffset = rowOffset + 4 * col;
temp = *(inBuf + destOffset + 2);
if(fwrite(&temp, 1, 1, fp) != 1)
{
m_error = FILEWRITEERR;
fclose(fp);
return FALSE;
}
temp = *(inBuf + destOffset + 1);
if(fwrite(&temp, 1, 1, fp) != 1)
{
m_error = FILEWRITEERR;
fclose(fp);
return FALSE;
}
temp = *(inBuf + destOffset + 0);
if(fwrite(&temp, 1, 1, fp) != 1)
{
m_error = FILEWRITEERR;
fclose(fp);
return FALSE;
}
if(fwrite(&temp, 1, 1, fp) != 1)
{
m_error = FILEWRITEERR;
fclose(fp);
return FALSE;
}
}
}
// Cleanup
fclose(fp);
m_error = IMGOK;
return TRUE;
}
///////////////////////////////////////////////////////////////////////////////////
// Save8BitTGA
// Save's to an 8 Bit Color mapped file using the Palette
// passed in to the function.
BOOL
TGAFile::Save8BitTGA(const char * fileName, // output path
BYTE * inBuf, // one BYTE per pixel colomapped image
UINT32 width, // Width of Image
UINT32 height, // Height of Image
__int32 colors, // number of colors (number of RGBQUADs)
RGBQUAD * colormap) // array of RGBQUADs
{
long lResult = 0;
TGAHEADER tgahd;
// Init
m_error = IMGOK;
// Init the file Header to all zeros
ZeroMemory(&tgahd, sizeof(tgahd));
if(inBuf == NULL)
{
m_error = BADPARAMERR;
return FALSE;
}
if((width == 0) || (height == 0))
{
m_error = BADPARAMERR;
return FALSE;
}
if(colormap == NULL)
{
m_error = BADPARAMERR;
return FALSE;
}
// Initialize the Header for the File
tgahd.IDLength = 0;
tgahd.ColorMapType = 1;
tgahd.ImageType = TGA_MAPRGBTYPE;
tgahd.CMapStart = 0;
tgahd.CMapLength = (SHORT)colors;
tgahd.CMapDepth = 24;
tgahd.XOffset = 0;
tgahd.YOffset = 0;
tgahd.Width = (WORD)width;
tgahd.Height = (WORD)height;
tgahd.PixelDepth = 8;
tgahd.ImageDescriptor = 0x28;
// Open a file to write
FILE * fp = fopen(fileName, "wb");
if(fp == NULL)
{
m_error = FILEOPENERR;
return FALSE;
}
// Write the Header to File.
if((lResult = fwrite(&tgahd, 1, sizeof(TGAHEADER), fp)) != 18)
{
m_error = FILEWRITEERR;
fclose(fp);
return FALSE;
}
// Write out the Colormap
for(__int32 i = 0; i < colors; i++)
{
putc(colormap[i].rgbBlue, fp);
putc(colormap[i].rgbGreen, fp);
putc(colormap[i].rgbRed, fp);
}
// Wrte the Bytes to file
DWORD destOffset = 0;
BYTE temp = 0;
for(UINT32 row = 0; row < tgahd.Height; row++)
{
for(UINT32 col = 0; col < tgahd.Width; col++)
{
temp = *(inBuf + destOffset + 0);
if(fwrite(&temp, 1, 1, fp) != 1)
{
m_error = FILEWRITEERR;
fclose(fp);
return FALSE;
}
destOffset += 1;
}
}
// Cleanup
fclose(fp);
m_error = IMGOK;
return TRUE;
}
////////////////////////////////////////////////////////////////////////////////////
// TGA_GetMapEntry
// Get the Color Values out of the
// Color map in the TGA File
// Return 0 on Success
BOOL
TGAFile::TGA_GetMapEntry(BYTE * Red, BYTE * Green, BYTE * Blue, BYTE * Alpha, FILE * fp, UINT32 Depth)
{
UINT32 j, k, l;
BYTE i, r, g, b, a = 0;
long lResult;
switch(Depth)
{
case 8: // Greyscale Read and Triplicate
lResult = fread(&i, 1, 1, fp);
// Check for error
if(lResult != 1)
{
m_error = FILEREADERR;
return FALSE;
}
// Set RGB Values
r = i;
g = i;
b = i;
break;
case 16: // 5 bits each of Red, Green, and Blue
case 15: // Watch for the Byte order
lResult = fread(&j, 1, 1, fp);
lResult = lResult + fread(&k, 1, 1, fp);
// Check for error
if(lResult != 2)
{
m_error = FILEREADERR;
return FALSE;
}
l = j + k * 256;
b = (BYTE)(((l >> 10) & 31) << 3);
g = (BYTE)(((l >> 5) & 31) << 3);
r = (BYTE)((l & 31) << 3);
break;
case 32: // Read the Alpha bit a Throw it away
case 24: // Eight bits for each Red, Green and Blue
lResult = fread(&i, 1, 1, fp);
r = i;
lResult = lResult + fread(&i, 1, 1, fp);
g = i;
lResult = lResult + fread(&i, 1, 1, fp);
b = i;
// Check for error
if(lResult != 3)
{
m_error = FILEREADERR;
return FALSE;
}
if(Depth == 32)
{
lResult = fread(&i, 1, 1, fp);
if(lResult != 1)
{
m_error = FILEREADERR;
return FALSE;
}
// Stroe Alpha bit
a = i;
}
break;
default:
// Some Other Pixel Depth Which I don't support
return FALSE;
}
*Red = r;
*Green = g;
*Blue = b;
*Alpha = a;
// Reutrn No Error
return TRUE;
}
////////////////////////////////////////////////////////////////////////////////////
// TGA_GetFileVersion
// Retrieves the Version of the TGA File
// BYTES 8-23 of a Version 2.0 Footer will be equal
// to "TRUEVISION-XFILE" as ASCII
// Returns - Version number 1 or 2
__int32
TGAFile::TGA_GetFileVersion(FILE * fp)
{
long result;
fpos_t pos;
TGAFOOTER tgaft;
// Save the Current position of the File Stream
if(fgetpos(fp, &pos))
{
// Error Quit
return FILEREADERR;
}
// Seek the last 26 bytes of the file
if(fseek(fp, -26, SEEK_END))
{
// Error Quit
return FILEREADERR;
}
// Read in the last 26 Bytes of the File
result = fread(&tgaft, 1, sizeof(TGAFOOTER), fp);
if(result != sizeof(TGAFOOTER))
{
m_error = FILEREADERR;
return FILEREADERR;
}
// Return the File Stream to its initial position
if(fsetpos(fp, &pos))
{
// Error Quit
return FILEREADERR;
}
// Check for the Marker at the end of the file
if(!strcspn(tgaft.Signature, "TRUEVISION-XFILE"))
{
// Marker found its V2.0 TGA
return TGA_VERSIONTWO;
}
// No Marker was found Assume V1.0 TGA
return TGA_VERSIONONE;
}
////////////////////////////////////////////////////////////////////////////////////
// TGA_getPixelValue
// Retreve a pixel value from the buffer and parse
// the value if its RLE encoded. Returns the RGB
// value of the pixel.
// Retruns - TRUE on success
BOOL
TGAFile::TGA_GetPixelValue
(
BYTE * rRed,
BYTE * rGrn,
BYTE * rBlu,
BYTE * rAlp,
BYTE ** ppTGAData,
UINT32 PixelDepth,
RGBQUAD * CColMap
)
{
//
// Buffered TGAs are always 32-bit,
// so go direct from file to RGBA
//
*rRed = *((*ppTGAData)++);
*rGrn = *((*ppTGAData)++);
*rBlu = *((*ppTGAData)++);
*rAlp = *((*ppTGAData)++);
return TRUE;
}
////////////////////////////////////////////////////////////////////////////////////
// TGA_getPixelValue
// Retreve a pixel value from the file and parse
// the value if its RLE encoded. Returns the RGB
// value of the pixel.
// Retruns - TRUE on success
BOOL
TGAFile::TGA_GetPixelValue(BYTE * rRed, BYTE * rGrn, BYTE * rBlu, BYTE * rAlp, FILE * fp, UINT32 PixelDepth, RGBQUAD * CColMap)
{
BYTE i, j, k;
long lResult;
// Check for Run Length Encoding
if((mode & RLENCO) != 0)
{
if(RLECount == 0)
{ // Restrat the rum
lResult = fread(&i, 1, 1, fp);
if(lResult != 1)
{
m_error = FILEREADERR;
return FALSE;
}
RLEFlag = (i & 0x80) >> 7;
if(RLEFlag == 0)
{ // Stream of unencoded pixels
RLECount = i + 1;
}
else
{ // Single Pixel Replicated
RLECount = i-127;
}
RLECount--; // ecrement count and get pixel
}
else
{
// I have already read the count and at least the first pixel
RLECount--;
if(RLEFlag != 0)
{
// Replicated Pixels
goto PixelEncode;
}
}
}
// Rea the appropiate number of BYTES and break into RGB
switch(PixelDepth)
{
case 8: // Greyscale Read 1 Byte and Triplicate
lResult = fread(&i, 1, 1, fp);
if(lResult != 1)
{
m_error = FILEREADERR;
return FALSE;
}
Red = i;
Grn = i;
Blu = i;
l = i;
break;
case 16: // 1 Bit alpha not used
case 15: // Five bits each for RGB watch byte ordering
lResult = fread(&j, 1, 1, fp);
lResult = lResult + fread(&k, 1, 1, fp);
// Check for error
if(lResult != 2)
{
m_error = FILEREADERR;
return FALSE;
}
l = j + k * 256;
Blu = (BYTE)(((l >> 10) & 31) << 3);
Grn = (BYTE)(((l >> 5) & 31) << 3);
Red = (BYTE)((l & 31) << 3);
break;
case 24: // Eight bits each for RGB
lResult = fread(&i, 1, 1, fp);
Red = i;
lResult = lResult + fread(&i, 1, 1, fp);
Grn = i;
lResult = lResult + fread(&i, 1, 1, fp);
Blu = i;
// opaque alpha (08jan00/bgw)
Alpha = 0xFF;
// Check for error
if(lResult != 3)
{
m_error = FILEREADERR;
return FALSE;
}
break;
case 32: // With alpha (08jan00/bgw)
lResult = fread(&i, 1, 1, fp);
Red = i;
lResult = lResult + fread(&i, 1, 1, fp);
Grn = i;
lResult = lResult + fread(&i, 1, 1, fp);
Blu = i;
lResult = lResult + fread(&i, 1, 1, fp);
Alpha = i;
// Check for error
if(lResult != 4)
{
m_error = FILEREADERR;
return FALSE;
}
break;
default: // Unknown number of bis per pixel
m_error = INVALIDTGAERR;
return NULL;
}
PixelEncode: // Set the actual pixel values
if((mode & MAPPED) == MAPPED)
{
// Remap Color Mapped Pixels
*rRed = CColMap[l].rgbRed;
*rGrn = CColMap[l].rgbGreen;
*rBlu = CColMap[l].rgbBlue;
*rAlp = 0xFF; // opaque always (08jan00/bgw)
}
else
{
// Set Unmapped Values
*rRed = Red;
*rGrn = Grn;
*rBlu = Blu;
*rAlp = Alpha;
}
return TRUE;
}
extern "C" unsigned char * LoadTGAFile
(
const char * filename,
int * width,
int * height
)
{
TGAFile tgaFile;
return (unsigned char *)tgaFile.LoadTGA(filename, (UINT32 *)width, (UINT32 *)height);
}