// TGAImage.cpp: implementation of the CTGAImage class.
//
//////////////////////////////////////////////////////////////////////

#include "TGAImage.h"

#include "FileStream.h"

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

CTGAImage::CTGAImage()
{
	m_pszClassName = "CTGAImage";
}

CTGAImage::~CTGAImage()
{

}

typedef struct
{
    WORD red:5;
    WORD green:5;
	WORD blue:5;
    WORD n:1;
} RGB16;
typedef union
{
    RGB16 bit;
    WORD color;
} HICOLOR; 

bool CTGAImage::Load(LPSTR lpszFileName)
{
	CFileStream stream;
	if (!stream.Open(lpszFileName, CFileStream::READONLY))
		return false;

	TGAFILEHEADER tgaFileHeader;
	stream.Read(&tgaFileHeader, sizeof(TGAFILEHEADER), 1);

	if (tgaFileHeader.colorMapType != 0 && tgaFileHeader.colorMapType != 1)
    	return NULL;

	if (tgaFileHeader.bits != 8 && tgaFileHeader.bits != 16 && tgaFileHeader.bits != 24 && tgaFileHeader.bits != 32)
		return NULL;

	switch (tgaFileHeader.imageType)
	{
		case TGA_TYPE_MAPPED_RLE:
		case TGA_TYPE_MAPPED:
			m_format = FMT_COLOR_INDEX;
			break;

		case TGA_TYPE_GRAY_RLE:
		case TGA_TYPE_GRAY:
			m_format = FMT_LUMINANCE;
			break;

		case TGA_TYPE_COLOR_RLE:
		case TGA_TYPE_COLOR:
			switch (tgaFileHeader.bits)
			{
			case 16:
			case 24:
				m_format = FMT_RGB;
				break;
			case 32:
				m_format = FMT_RGBA;
				break;
			}
			break;

		default:
			return NULL;
	}

	// Skip the image ID field.
	if (tgaFileHeader.idLength != 0)
		stream.Seek(tgaFileHeader.idLength, SEEK_CUR);

	bool bHorzrev = (tgaFileHeader.descriptor & TGA_DESC_HORIZONTAL) ? true : false;
	bool bVertrev = !(tgaFileHeader.descriptor & TGA_DESC_VERTICAL)  ? true : false;

	// Read color map
	if (tgaFileHeader.colorMapType == 1)
	{
		if (tgaFileHeader.colorMapSize != 24)
			return NULL;

		// We need to read in the colormap.
		if (tgaFileHeader.colorMapLength == 0)
			return NULL;

		int nPalLength = tgaFileHeader.colorMapSize / 8;
		int nColors    = tgaFileHeader.colorMapLength * tgaFileHeader.colorMapIndex;

		m_pPalBuffer = new BYTE [nColors * nPalLength];
		if (!m_pPalBuffer)
			return NULL;

		stream.Read(m_pPalBuffer, nPalLength, tgaFileHeader.colorMapLength);

		if (nPalLength >= 3) 
		{
			// Rearrange the colors from BGR to RGB.
			for (int j = tgaFileHeader.colorMapIndex; j < tgaFileHeader.colorMapLength * nPalLength; j += nPalLength)
			{
				BYTE tmp = m_pPalBuffer[j];
				m_pPalBuffer[j] = m_pPalBuffer[j + 2];
				m_pPalBuffer[j + 2] = tmp;
			}
		}
	}

	int nPixelSize = 0;
	switch (m_format)
	{
	case FMT_COLOR_INDEX:
	case FMT_LUMINANCE:
		nPixelSize = 1;
		break;

	case FMT_RGB:
		nPixelSize = 3;
		break;
	case FMT_RGBA:
		nPixelSize = 4;
		break;
	}
	///////////////////////////////////////////////////////////////////////////
	m_dwWidth  = tgaFileHeader.width;
	m_dwHeight = tgaFileHeader.height;

	m_dwBufferLength = m_dwWidth * m_dwHeight * nPixelSize;
	m_pBuffer = new BYTE [m_dwBufferLength];
	if (!m_pBuffer)
		return false;

	///////////////////////////////////////////////////////////////////////////
	int nPixel, nLength;
	int nCount = m_dwWidth * m_dwHeight;

	LPBYTE pBuffer = m_pBuffer;
	switch (tgaFileHeader.imageType)
	{
	// Uncompressed, RGB images
	case TGA_TYPE_COLOR:
		for (nPixel = 0; nPixel < nCount; nPixel ++)
		{
			BYTE b1, b2, red, green, blue, alpha;

			switch (tgaFileHeader.bits)
			{
			case 16:
				b1 = (BYTE) stream.ReadChar();
				b2 = (BYTE) stream.ReadChar();

				HICOLOR hiColor;
				hiColor.color = (b2 << 8) | b1;

				red   = hiColor.bit.red   << 3;
				green = hiColor.bit.green << 3;
				blue  = hiColor.bit.blue  << 3;

				*pBuffer ++ = blue;
				*pBuffer ++ = green;
				*pBuffer ++ = red;
				break;

			case 24:
				blue  = (BYTE) stream.ReadChar();
				green = (BYTE) stream.ReadChar();
				red   = (BYTE) stream.ReadChar();

				*pBuffer ++ = red;
				*pBuffer ++ = green;
				*pBuffer ++ = blue;
				break;

			case 32:
				blue  = (BYTE) stream.ReadChar();
				green = (BYTE) stream.ReadChar();
				red   = (BYTE) stream.ReadChar();
				alpha = (BYTE) stream.ReadChar();
					
				*pBuffer ++ = red;
				*pBuffer ++ = green;
				*pBuffer ++ = blue;
				*pBuffer ++ = alpha;
				break;
			}
		}
		break;
	
	// Uncompressed, Alpha Images
	case TGA_TYPE_GRAY:
		stream.Read(pBuffer, sizeof(BYTE), m_dwBufferLength);
		break;

	// Runlength encoded RGB images
	case TGA_TYPE_COLOR_RLE:
		for (nPixel = 0; nPixel < nCount; nPixel ++)
		{
			BYTE b1, b2, red, green, blue, alpha, packetHeader, packetLength;

			packetHeader = (BYTE) stream.ReadChar();
			packetLength = 1 + (packetHeader & 0x7f);

			// run-length packet
			if (packetHeader & 0x80) 
			{
				switch (tgaFileHeader.bits)
				{
				case 16:
					b1 = (BYTE) stream.ReadChar();
					b2 = (BYTE) stream.ReadChar();

					HICOLOR hiColor;
					hiColor.color = (b2 << 8) | b1;

					red   = hiColor.bit.red   << 3;
					green = hiColor.bit.green << 3;
					blue  = hiColor.bit.blue  << 3;

					for (nLength = 0; nLength < (int) packetLength; nLength ++)
					{
						*pBuffer ++ = blue;
						*pBuffer ++ = green;
						*pBuffer ++ = red;
					}
					break;

				case 24:
					blue  = (BYTE) stream.ReadChar();
					green = (BYTE) stream.ReadChar();
					red   = (BYTE) stream.ReadChar();

					for (nLength = 0; nLength < (int) packetLength; nLength ++)
					{
						*pBuffer ++ = red;
						*pBuffer ++ = green;
						*pBuffer ++ = blue;
					}
					break;

				case 32:
					blue  = (BYTE) stream.ReadChar();
					green = (BYTE) stream.ReadChar();
					red   = (BYTE) stream.ReadChar();
					alpha = (BYTE) stream.ReadChar();

					for (nLength = 0; nLength < (int) packetLength; nLength ++)
					{
						*pBuffer ++ = red;
						*pBuffer ++ = green;
						*pBuffer ++ = blue;
						*pBuffer ++ = alpha;
					}
					break;
				}
			}
			// non run-length packet
			else
			{
				for (int j = 0; j < (int) packetLength; j ++)
				{
					switch (tgaFileHeader.bits)
					{
					case 16:
						b1 = (BYTE) stream.ReadChar();
						b2 = (BYTE) stream.ReadChar();

						HICOLOR hiColor;
						hiColor.color = (b2 << 8) | b1;

						red   = hiColor.bit.red   << 3;
						green = hiColor.bit.green << 3;
						blue  = hiColor.bit.blue  << 3;

						*pBuffer ++ = blue;
						*pBuffer ++ = green;
						*pBuffer ++ = red;
						break;

					case 24:
						blue  = (BYTE) stream.ReadChar();
						green = (BYTE) stream.ReadChar();
						red   = (BYTE) stream.ReadChar();

						*pBuffer ++ = red;
						*pBuffer ++ = green;
						*pBuffer ++ = blue;
						break;

					case 32:
						blue  = (BYTE) stream.ReadChar();
						green = (BYTE) stream.ReadChar();
						red   = (BYTE) stream.ReadChar();
						alpha = (BYTE) stream.ReadChar();
							
						*pBuffer ++ = red;
						*pBuffer ++ = green;
						*pBuffer ++ = blue;
						*pBuffer ++ = alpha;
						break;
					}
				}
			}

			nPixel += packetLength;
		}

		break;
	}

	if (bHorzrev)
		Flip();

	if (bVertrev)
		Mirror();

	return true;
}
