////////////////////////////////////////////////////////////////////
// Image.cpp
//
// Copyright 2007 cDc@seacave
// Distributed under the Boost Software License, Version 1.0
// (See http://www.boost.org/LICENSE_1_0.txt)

#include "Common.h"
#include "Image.h"

using namespace SEACAVE;


// D E F I N E S ///////////////////////////////////////////////////


// S T R U C T S ///////////////////////////////////////////////////

DEFINE_LOG(CImage, _T("IO      "));

// Set the image details;
// if image's data is not NULL, but its size is too small,
// data's buffer is not allocated and _BUFFERSIZE is returned.
HRESULT CImage::Reset(Size width, Size height, PIXELFORMAT pixFormat, Size levels, bool bAllocate)
{
	// reinitialize image with given params
	const size_t oldDataSize = GetDataSize();
	m_dataWidth	= m_width	= width;
	m_dataHeight= m_height	= height;
	m_format	= pixFormat;
	m_stride	= GetStride(pixFormat);
	m_lineWidth	= m_width * m_stride;
	m_numLevels	= levels;
	m_level		= 0;
	if (bAllocate) {
		if (m_data != NULL) {
			if (oldDataSize < GetDataSize())
				return _BUFFERSIZE;
		} else {
			m_data	= new uint8_t[GetDataSize()];
		}
	}
	return _OK;
} // Reset
/*----------------------------------------------------------------*/

HRESULT CImage::Reset(LPCTSTR szFileName, IMCREATE mode)
{
	// open the new image stream
	m_fileName = szFileName;
	// try to directly access the file
	File* f;
	if (mode == READ) {
		f = new File(szFileName, File::READ, File::OPEN);
	} else {
		Util::ensureFolder(szFileName);
		f = new File(szFileName, File::WRITE, File::CREATE | File::TRUNCATE);
	}
	if (!f->isOpen()) {
		delete f;
		return _INVALIDFILE;
	}
	if ((m_pStream = f) == NULL) {
		LOG(LT_IMAGE, _T("error: failed opening image '%s'"), szFileName);
		return _INVALIDFILE;
	}
	return _OK;
} // Reset
/*----------------------------------------------------------------*/

HRESULT CImage::Reset(IOSTREAMPTR& pStream)
{
	// use the already opened image stream
	m_fileName.clear();
	m_pStream = pStream;
	return _OK;
} // Reset
/*----------------------------------------------------------------*/

void CImage::Close()
{
	// close the image stream
	m_pStream.Release();
	m_data.Release();
} // Close
/*----------------------------------------------------------------*/


HRESULT CImage::ReadHeader()
{
	return _OK;
} // ReadHeader
/*----------------------------------------------------------------*/

HRESULT CImage::ReadData(void* pData, PIXELFORMAT dataFormat, Size nStride, Size lineWidth)
{
	// read data
	if (dataFormat == m_format && nStride == m_stride) {
		// read image directly to the data buffer
		if (lineWidth == m_lineWidth) {
			const size_t nSize = m_dataHeight*m_lineWidth;
			if (nSize != m_pStream->read(pData, nSize))
				return _INVALIDFILE;
		} else {
			for (Size j=0; j<m_dataHeight; ++j, (uint8_t*&)pData+=lineWidth)
				if (m_lineWidth != m_pStream->read(pData, m_lineWidth))
					return _INVALIDFILE;
		}
	} else {
		// read image to a buffer and convert it
		CAutoPtrArr<uint8_t> const buffer(new uint8_t[m_lineWidth]);
		for (Size j=0; j<m_dataHeight; ++j, (uint8_t*&)pData+=lineWidth) {
			if (m_lineWidth != m_pStream->read(buffer, m_lineWidth))
				return _INVALIDFILE;
			if (!FilterFormat(pData, dataFormat, nStride, buffer, m_format, m_stride, m_dataWidth))
				return _FAIL;
		}
	}
	// prepare next level
	if (m_level+1 < m_numLevels)
		m_lineWidth = GetDataSizes(++m_level, m_dataWidth, m_dataHeight);
	return _OK;
} // ReadData
/*----------------------------------------------------------------*/


HRESULT CImage::WriteHeader(PIXELFORMAT imageFormat, Size width, Size height, BYTE numLevels)
{
	// write header
	m_numLevels = numLevels;
	m_level = 0;
	m_format = imageFormat;
	m_stride = GetStride(m_format);
	m_width = width;
	m_height = height;
	m_lineWidth = GetDataSizes(0, m_dataWidth, m_dataHeight);
	return _OK;
} // WriteHeader
/*----------------------------------------------------------------*/

HRESULT CImage::WriteData(void* pData, PIXELFORMAT dataFormat, Size nStride, Size lineWidth)
{
	// write data
	if (dataFormat == m_format && nStride == m_stride) {
		// write data buffer directly to the image
		if (lineWidth == m_lineWidth) {
			const size_t nSize = m_dataHeight*m_lineWidth;
			if (nSize != m_pStream->write(pData, nSize))
				return _INVALIDFILE;
		} else {
			for (Size j=0; j<m_dataHeight; ++j, (uint8_t*&)pData+=lineWidth)
				if (m_lineWidth != m_pStream->write(pData, m_lineWidth))
					return _INVALIDFILE;
		}
	} else {
		// convert data to a buffer and write it
		CAutoPtrArr<uint8_t> const buffer(new uint8_t[m_lineWidth]);
		for (Size j=0; j<m_dataHeight; ++j, (uint8_t*&)pData+=lineWidth) {
			if (!FilterFormat(buffer, m_format, m_stride, pData, dataFormat, nStride, m_dataWidth))
				return _FAIL;
			if (m_lineWidth != m_pStream->write(buffer, m_lineWidth))
				return _INVALIDFILE;
		}
	}
	// prepare next level
	if (m_level+1 < m_numLevels)
		m_lineWidth = GetDataSizes(++m_level, m_dataWidth, m_dataHeight);
	return _OK;
} // WriteData
/*----------------------------------------------------------------*/


CImage::Size CImage::GetDataSizes(Size mipLevel, Size& width, Size& height) const
{
	// Bump by the mip level.
	height = MAXF((Size)1, m_height >> mipLevel);
	width  = MAXF((Size)1, m_width >> mipLevel);

	// if compressed, divide dims by 4
	if (m_format >= PF_DXT1)
	{
		width = (width+3)>>2;
		height = (height+3)>>2;
	}

	return width * m_stride;
} // GetDataSizes
/*----------------------------------------------------------------*/


CImage::Size CImage::GetStride(PIXELFORMAT pixFormat)
{
	switch (pixFormat)
	{
	case PF_A8:
	case PF_GRAY8:
		return 1;
	case PF_R5G6B5:
		return 2;
	case PF_B8G8R8:
	case PF_R8G8B8:
		return 3;
	case PF_R8G8B8A8:
	case PF_A8R8G8B8:
	case PF_B8G8R8A8:
	case PF_A8B8G8R8:
		return 4;
	case PF_DXT1:
		return 8;
	case PF_DXT2:
	case PF_DXT3:
	case PF_DXT4:
	case PF_DXT5:
	case PF_3DC:
		return 16;
	default:
		LOG(LT_IMAGE, "error: unsupported SCI pixel format");
	}
	return 0;
} // GetStride
/*----------------------------------------------------------------*/


// If the given image format has alpha channel, returns true.
bool CImage::FormatHasAlpha(PIXELFORMAT format)
{
	switch (format)
	{
	case PF_A8:
	case PF_R8G8B8A8:
	case PF_A8R8G8B8:
	case PF_B8G8R8A8:
	case PF_A8B8G8R8:
	case PF_DXT2:
	case PF_DXT3:
	case PF_DXT4:
	case PF_DXT5:
		return true;
	case PF_GRAY8:
	case PF_R5G6B5:
	case PF_B8G8R8:
	case PF_R8G8B8:
	case PF_DXT1:
	case PF_3DC:
		return false;
	default:
		ASSERT("Unknown format" == NULL);
	}
	return false;
} // FormatHasAlpha
/*----------------------------------------------------------------*/


// Convert from one format to another.
// nSzize is the number of pixels to process.
bool CImage::FilterFormat(void* pDst, PIXELFORMAT formatDst, Size strideDst, const void* pSrc, PIXELFORMAT formatSrc, Size strideSrc, Size nSzize)
{
	//ASSERT(formatDst != formatSrc || strideDst != strideSrc)
	switch (formatDst)
	{
	case PF_A8:
	case PF_GRAY8:
		switch (formatSrc)
		{
		case PF_R8G8B8A8:
		case PF_A8R8G8B8:
		case PF_B8G8R8A8:
		case PF_A8B8G8R8:
			// from PF_R8G8B8A8 to PF_A8 (just copy the alpha channel)
			(uint8_t*&)pSrc += 3; //skip the first RGB values
		case PF_A8:
		case PF_GRAY8:
			// from PF_A8 to PF_A8 (just copy)
			ASSERT(strideDst != strideSrc);
			for (Size i=0; i<nSzize; ++i,(uint8_t*&)pDst+=strideDst,(uint8_t*&)pSrc+=strideSrc)
				((uint8_t*)pDst)[0] = ((uint8_t*)pSrc)[0];
			return true;

		case PF_R5G6B5:
			// from PF_R5G6B5 to PF_A8 (16bits to gray)
			for (Size i=0; i<nSzize; ++i,(uint8_t*&)pDst+=strideDst,(uint8_t*&)pSrc+=strideSrc) {
				const uint16_t clr = *((uint16_t*)pSrc);
				((uint8_t*)pDst)[0] = RGB24TO8(RGB16TOR(clr), RGB16TOG(clr), RGB16TOB(clr));
			}
			return true;

		case PF_B8G8R8:
		case PF_R8G8B8:
			// from PF_R8G8B8 to PF_A8 (24bits to gray)
			for (Size i=0; i<nSzize; ++i,(uint8_t*&)pDst+=strideDst,(uint8_t*&)pSrc+=strideSrc)
				((uint8_t*)pDst)[0] = RGB24TO8(((uint8_t*)pSrc)[2], ((uint8_t*)pSrc)[1], ((uint8_t*)pSrc)[0]);
			return true;
		}
		break;

	case PF_R5G6B5:
		switch (formatSrc)
		{
		case PF_A8:
		case PF_GRAY8:
			// from PF_A8 to PF_R5G6B5 (doesn't make sense)
			ASSERT(pDst != pSrc);
			for (Size i=0; i<nSzize; ++i,(uint8_t*&)pDst+=strideDst,(uint8_t*&)pSrc+=strideSrc)
				*((uint16_t*)pDst) = RGB24TO16(((uint8_t*)pSrc)[0], ((uint8_t*)pSrc)[0], ((uint8_t*)pSrc)[0]);
			return true;

		case PF_R5G6B5:
			// from PF_R5G6B5 to PF_R5G6B5 (just copy)
			ASSERT(strideDst != strideSrc);
			for (Size i=0; i<nSzize; ++i,(uint8_t*&)pDst+=strideDst,(uint8_t*&)pSrc+=strideSrc)
				*((uint16_t*)pDst) = *((uint16_t*)pSrc);
			return true;

		case PF_R8G8B8:
			// from PF_R8G8B8 to PF_R5G6B5 (24bits to 16bits)
		case PF_R8G8B8A8:
			// from PF_R8G8B8A8 to PF_R5G6B5 (24bits to 16bits)
			for (Size i=0; i<nSzize; ++i,(uint8_t*&)pDst+=strideDst,(uint8_t*&)pSrc+=strideSrc)
				*((uint16_t*)pDst) = RGB24TO16(((uint8_t*)pSrc)[2], ((uint8_t*)pSrc)[1], ((uint8_t*)pSrc)[0]);
			return true;

		case PF_A8R8G8B8:
			// from PF_A8R8G8B8 to PF_R5G6B5 (24bits to 16bits)
			for (Size i=0; i<nSzize; ++i,(uint8_t*&)pDst+=strideDst,(uint8_t*&)pSrc+=strideSrc)
				*((uint16_t*)pDst) = RGB24TO16(((uint8_t*)pSrc)[3], ((uint8_t*)pSrc)[2], ((uint8_t*)pSrc)[1]);
			return true;

		case PF_B8G8R8:
			// from PF_B8G8R8 to PF_R5G6B5 (24bits to 16bits)
			for (Size i=0; i<nSzize; ++i,(uint8_t*&)pDst+=strideDst,(uint8_t*&)pSrc+=strideSrc)
				*((uint16_t*)pDst) = RGB24TO16(((uint8_t*)pSrc)[0], ((uint8_t*)pSrc)[1], ((uint8_t*)pSrc)[2]);
			return true;

		case PF_B8G8R8A8:
			// from PF_B8G8R8A8 to PF_R5G6B5 (24bits to 16bits)
			for (Size i=0; i<nSzize; ++i,(uint8_t*&)pDst+=strideDst,(uint8_t*&)pSrc+=strideSrc)
				*((uint16_t*)pDst) = RGB24TO16(((uint8_t*)pSrc)[0], ((uint8_t*)pSrc)[1], ((uint8_t*)pSrc)[2]);
			return true;

		case PF_A8B8G8R8:
			// from PF_A8B8G8R8 to PF_R5G6B5 (24bits to 16bits)
			for (Size i=0; i<nSzize; ++i,(uint8_t*&)pDst+=strideDst,(uint8_t*&)pSrc+=strideSrc)
				*((uint16_t*)pDst) = RGB24TO16(((uint8_t*)pSrc)[1], ((uint8_t*)pSrc)[2], ((uint8_t*)pSrc)[3]);
			return true;
		}
		break;

	case PF_R8G8B8:
		switch (formatSrc)
		{
		case PF_A8:
		case PF_GRAY8:
			// from PF_A8 to PF_R8G8B8 (doesn't make sense)
			ASSERT(pDst != pSrc);
			for (Size i=0; i<nSzize; ++i,(uint8_t*&)pDst+=strideDst,(uint8_t*&)pSrc+=strideSrc)
				((uint8_t*)pDst)[0] = ((uint8_t*)pDst)[1] = ((uint8_t*)pDst)[2] = *((uint8_t*)pSrc);
			return true;

		case PF_R5G6B5:
			// from PF_R5G6B5 to PF_R8G8B8 (16bits to 24bits))
			ASSERT(pDst != pSrc);
			for (Size i=0; i<nSzize; ++i,(uint8_t*&)pDst+=strideDst,(uint8_t*&)pSrc+=strideSrc) {
				const uint16_t clr = *((uint16_t*)pSrc);
				((uint8_t*)pDst)[0] = RGB16TOB(clr);
				((uint8_t*)pDst)[1] = RGB16TOG(clr);
				((uint8_t*)pDst)[2] = RGB16TOR(clr);
			}
			return true;

		case PF_R8G8B8:
			// from PF_R8G8B8 to PF_R8G8B8 (just copy)
			ASSERT(strideDst != strideSrc);
		case PF_R8G8B8A8:
			// from PF_R8G8B8A8 to PF_R8G8B8 (just copy)
			for (Size i=0; i<nSzize; ++i,(uint8_t*&)pDst+=strideDst,(uint8_t*&)pSrc+=strideSrc) {
				((uint16_t*)pDst)[0] = ((uint16_t*)pSrc)[0];
				((uint8_t*)pDst)[2] = ((uint8_t*)pSrc)[2];
			}
			return true;

		case PF_A8R8G8B8:
			// from PF_A8R8G8B8 to PF_R8G8B8 (just copy)
			ASSERT(pDst != pSrc);
			for (Size i=0; i<nSzize; ++i,(uint8_t*&)pDst+=strideDst,(uint8_t*&)pSrc+=strideSrc) {
				((uint8_t*)pDst)[0] = ((uint8_t*)pSrc)[1];
				((uint8_t*)pDst)[1] = ((uint8_t*)pSrc)[2];
				((uint8_t*)pDst)[2] = ((uint8_t*)pSrc)[3];
			}
			return true;

		case PF_B8G8R8:
			// from PF_B8G8R8 to PF_R8G8B8 (flip)
		case PF_B8G8R8A8:
			// from PF_B8G8R8A8 to PF_R8G8B8 (flip)
			ASSERT(pDst != pSrc);
			for (Size i=0; i<nSzize; ++i,(uint8_t*&)pDst+=strideDst,(uint8_t*&)pSrc+=strideSrc) {
				((uint8_t*)pDst)[0] = ((uint8_t*)pSrc)[2];
				((uint8_t*)pDst)[1] = ((uint8_t*)pSrc)[1];
				((uint8_t*)pDst)[2] = ((uint8_t*)pSrc)[0];
			}
			return true;

		case PF_A8B8G8R8:
			// from PF_A8B8G8R8 to PF_R8G8B8 (flip)
			ASSERT(pDst != pSrc);
			for (Size i=0; i<nSzize; ++i,(uint8_t*&)pDst+=strideDst,(uint8_t*&)pSrc+=strideSrc) {
				((uint8_t*)pDst)[0] = ((uint8_t*)pSrc)[3];
				((uint8_t*)pDst)[1] = ((uint8_t*)pSrc)[2];
				((uint8_t*)pDst)[2] = ((uint8_t*)pSrc)[1];
			}
			return true;
		}
		break;

	case PF_R8G8B8A8:
		switch (formatSrc)
		{
		case PF_A8:
		case PF_GRAY8:
			// from PF_A8 to PF_R8G8B8A8 (doesn't make sense)
			ASSERT(pDst != pSrc);
			for (Size i=0; i<nSzize; ++i,(uint8_t*&)pDst+=strideDst,(uint8_t*&)pSrc+=strideSrc) {
				((uint8_t*)pDst)[0] = ((uint8_t*)pDst)[1] = ((uint8_t*)pDst)[2] = *((uint8_t*)pSrc);
				((uint8_t*)pDst)[3] = 0xFF;
			}
			return true;

		case PF_R5G6B5:
			// from PF_R5G6B5 to PF_R8G8B8A8 (16bits to 24bits))
			ASSERT(pDst != pSrc);
			for (Size i=0; i<nSzize; ++i,(uint8_t*&)pDst+=strideDst,(uint8_t*&)pSrc+=strideSrc) {
				const uint16_t clr = *((uint16_t*)pSrc);
				((uint8_t*)pDst)[0] = RGB16TOB(clr);
				((uint8_t*)pDst)[1] = RGB16TOG(clr);
				((uint8_t*)pDst)[2] = RGB16TOR(clr);
				((uint8_t*)pDst)[3] = 0xFF;
			}
			return true;

		case PF_R8G8B8:
			// from PF_R8G8B8 to PF_R8G8B8A8 (just copy the RGB and set A to 255)
			ASSERT(pDst != pSrc);
			for (Size i=0; i<nSzize; ++i,(uint8_t*&)pDst+=strideDst,(uint8_t*&)pSrc+=strideSrc) {
				((uint16_t*)pDst)[0] = ((uint16_t*)pSrc)[0];
				((uint8_t*)pDst)[2] = ((uint8_t*)pSrc)[2];
				((uint8_t*)pDst)[3] = 0xFF;
			}
			return true;

		case PF_R8G8B8A8:
			// from PF_R8G8B8A8 to PF_R8G8B8A8 (just copy)
			ASSERT(strideDst != strideSrc);
			for (Size i=0; i<nSzize; ++i,(uint8_t*&)pDst+=strideDst,(uint8_t*&)pSrc+=strideSrc)
				((uint32_t*)pDst)[0] = ((uint32_t*)pSrc)[0];
			return true;

		case PF_A8R8G8B8:
			// from PF_A8R8G8B8 to PF_R8G8B8A8 (copy and flip)
			ASSERT(pDst != pSrc);
			for (Size i=0; i<nSzize; ++i,(uint8_t*&)pDst+=strideDst,(uint8_t*&)pSrc+=strideSrc) {
				((uint8_t*)pDst)[0] = ((uint8_t*)pSrc)[1];
				((uint8_t*)pDst)[1] = ((uint8_t*)pSrc)[2];
				((uint8_t*)pDst)[2] = ((uint8_t*)pSrc)[3];
				((uint8_t*)pDst)[3] = ((uint8_t*)pSrc)[0];
			}
			return true;

		case PF_B8G8R8:
			// from PF_B8G8R8 to PF_R8G8B8A8 (copy and flip the RGB and set A to 255)
			ASSERT(pDst != pSrc);
			for (Size i=0; i<nSzize; ++i,(uint8_t*&)pDst+=strideDst,(uint8_t*&)pSrc+=strideSrc) {
				((uint8_t*)pDst)[0] = ((uint8_t*)pSrc)[2];
				((uint8_t*)pDst)[1] = ((uint8_t*)pSrc)[1];
				((uint8_t*)pDst)[2] = ((uint8_t*)pSrc)[0];
				((uint8_t*)pDst)[3] = 0xFF;
			}
			return true;

		case PF_B8G8R8A8:
			// from PF_B8G8R8A8 to PF_R8G8B8A8 (copy and flip)
			ASSERT(pDst != pSrc);
			for (Size i=0; i<nSzize; ++i,(uint8_t*&)pDst+=strideDst,(uint8_t*&)pSrc+=strideSrc) {
				((uint8_t*)pDst)[0] = ((uint8_t*)pSrc)[2];
				((uint8_t*)pDst)[1] = ((uint8_t*)pSrc)[1];
				((uint8_t*)pDst)[2] = ((uint8_t*)pSrc)[0];
				((uint8_t*)pDst)[3] = ((uint8_t*)pSrc)[3];
			}
			return true;

		case PF_A8B8G8R8:
			// from PF_A8B8G8R8 to PF_R8G8B8A8 (flip)
			ASSERT(pDst != pSrc);
			for (Size i=0; i<nSzize; ++i,(uint8_t*&)pDst+=strideDst,(uint8_t*&)pSrc+=strideSrc) {
				((uint8_t*)pDst)[0] = ((uint8_t*)pSrc)[3];
				((uint8_t*)pDst)[1] = ((uint8_t*)pSrc)[2];
				((uint8_t*)pDst)[2] = ((uint8_t*)pSrc)[1];
				((uint8_t*)pDst)[3] = ((uint8_t*)pSrc)[0];
			}
			return true;
		}
		break;

	case PF_A8R8G8B8:
		switch (formatSrc)
		{
		case PF_A8:
		case PF_GRAY8:
			// from PF_A8 to PF_A8R8G8B8 (doesn't make sense)
			ASSERT(pDst != pSrc);
			for (Size i=0; i<nSzize; ++i,(uint8_t*&)pDst+=strideDst,(uint8_t*&)pSrc+=strideSrc) {
				((uint8_t*)pDst)[0] = 0xFF;
				((uint8_t*)pDst)[1] = ((uint8_t*)pDst)[2] = ((uint8_t*)pDst)[3] = *((uint8_t*)pSrc);
			}
			return true;

		case PF_R5G6B5:
			// from PF_R5G6B5 to PF_A8R8G8B8 (16bits to 24bits))
			ASSERT(pDst != pSrc);
			for (Size i=0; i<nSzize; ++i,(uint8_t*&)pDst+=strideDst,(uint8_t*&)pSrc+=strideSrc) {
				const uint16_t clr = *((uint16_t*)pSrc);
				((uint8_t*)pDst)[0] = 0xFF;
				((uint8_t*)pDst)[1] = RGB16TOB(clr);
				((uint8_t*)pDst)[2] = RGB16TOG(clr);
				((uint8_t*)pDst)[3] = RGB16TOR(clr);
			}
			return true;

		case PF_R8G8B8:
			// from PF_R8G8B8 to PF_A8R8G8B8 (just copy the RGB and set A to 255)
			ASSERT(pDst != pSrc);
			for (Size i=0; i<nSzize; ++i,(uint8_t*&)pDst+=strideDst,(uint8_t*&)pSrc+=strideSrc) {
				((uint8_t*)pDst)[0] = 0xFF;
				((uint8_t*)pDst)[1] = ((uint8_t*)pSrc)[0];
				((uint8_t*)pDst)[2] = ((uint8_t*)pSrc)[1];
				((uint8_t*)pDst)[3] = ((uint8_t*)pSrc)[2];
			}
			return true;

		case PF_R8G8B8A8:
			// from PF_R8G8B8A8 to PF_A8R8G8B8 (copy and flip)
			ASSERT(pDst != pSrc);
			for (Size i=0; i<nSzize; ++i,(uint8_t*&)pDst+=strideDst,(uint8_t*&)pSrc+=strideSrc) {
				((uint8_t*)pDst)[0] = ((uint8_t*)pSrc)[3];
				((uint8_t*)pDst)[1] = ((uint8_t*)pSrc)[0];
				((uint8_t*)pDst)[2] = ((uint8_t*)pSrc)[1];
				((uint8_t*)pDst)[3] = ((uint8_t*)pSrc)[2];
			}
			return true;

		case PF_A8R8G8B8:
			// from PF_A8R8G8B8 to PF_A8R8G8B8 (just copy)
			ASSERT(strideDst != strideSrc);
			for (Size i=0; i<nSzize; ++i,(uint8_t*&)pDst+=strideDst,(uint8_t*&)pSrc+=strideSrc)
				((uint32_t*)pDst)[0] = ((uint32_t*)pSrc)[0];
			return true;

		case PF_B8G8R8:
			// from PF_B8G8R8 to PF_A8R8G8B8 (copy and flip the RGB and set A to 255)
			ASSERT(pDst != pSrc);
			for (Size i=0; i<nSzize; ++i,(uint8_t*&)pDst+=strideDst,(uint8_t*&)pSrc+=strideSrc) {
				((uint8_t*)pDst)[0] = 0xFF;
				((uint8_t*)pDst)[1] = ((uint8_t*)pSrc)[2];
				((uint8_t*)pDst)[2] = ((uint8_t*)pSrc)[1];
				((uint8_t*)pDst)[3] = ((uint8_t*)pSrc)[0];
			}
			return true;

		case PF_B8G8R8A8:
			// from PF_B8G8R8A8 to PF_A8R8G8B8 (flip)
			ASSERT(pDst != pSrc);
			for (Size i=0; i<nSzize; ++i,(uint8_t*&)pDst+=strideDst,(uint8_t*&)pSrc+=strideSrc) {
				((uint8_t*)pDst)[0] = ((uint8_t*)pSrc)[3];
				((uint8_t*)pDst)[1] = ((uint8_t*)pSrc)[2];
				((uint8_t*)pDst)[2] = ((uint8_t*)pSrc)[1];
				((uint8_t*)pDst)[3] = ((uint8_t*)pSrc)[0];
			}
			return true;

		case PF_A8B8G8R8:
			// from PF_A8B8G8R8 to PF_A8R8G8B8 (copy and flip)
			ASSERT(pDst != pSrc);
			for (Size i=0; i<nSzize; ++i,(uint8_t*&)pDst+=strideDst,(uint8_t*&)pSrc+=strideSrc) {
				((uint8_t*)pDst)[0] = ((uint8_t*)pSrc)[0];
				((uint8_t*)pDst)[1] = ((uint8_t*)pSrc)[3];
				((uint8_t*)pDst)[2] = ((uint8_t*)pSrc)[2];
				((uint8_t*)pDst)[3] = ((uint8_t*)pSrc)[1];
			}
			return true;
		}
		break;

	case PF_B8G8R8:
		switch (formatSrc)
		{
		case PF_A8:
		case PF_GRAY8:
			// from PF_A8 to PF_B8G8R8 (doesn't make sense)
			ASSERT(pDst != pSrc);
			for (Size i=0; i<nSzize; ++i,(uint8_t*&)pDst+=strideDst,(uint8_t*&)pSrc+=strideSrc)
				((uint8_t*)pDst)[0] = ((uint8_t*)pDst)[1] = ((uint8_t*)pDst)[2] = *((uint8_t*)pSrc);
			return true;

		case PF_R5G6B5:
			// from PF_R5G6B5 to PF_B8G8R8 (16bits to 24bits))
			ASSERT(pDst != pSrc);
			for (Size i=0; i<nSzize; ++i,(uint8_t*&)pDst+=strideDst,(uint8_t*&)pSrc+=strideSrc) {
				const uint16_t clr = *((uint16_t*)pSrc);
				((uint8_t*)pDst)[0] = RGB16TOR(clr);
				((uint8_t*)pDst)[1] = RGB16TOG(clr);
				((uint8_t*)pDst)[2] = RGB16TOB(clr);
			}
			return true;

		case PF_R8G8B8:
			// from PF_R8G8B8 to PF_B8G8R8 (flip)
		case PF_R8G8B8A8:
			// from PF_R8G8B8A8 to PF_B8G8R8 (flip)
			ASSERT(pDst != pSrc);
			for (Size i=0; i<nSzize; ++i,(uint8_t*&)pDst+=strideDst,(uint8_t*&)pSrc+=strideSrc) {
				((uint8_t*)pDst)[0] = ((uint8_t*)pSrc)[2];
				((uint8_t*)pDst)[1] = ((uint8_t*)pSrc)[1];
				((uint8_t*)pDst)[2] = ((uint8_t*)pSrc)[0];
			}
			return true;

		case PF_A8R8G8B8:
			// from PF_A8R8G8B8 to PF_B8G8R8 (flip)
			ASSERT(pDst != pSrc);
			for (Size i=0; i<nSzize; ++i,(uint8_t*&)pDst+=strideDst,(uint8_t*&)pSrc+=strideSrc) {
				((uint8_t*)pDst)[0] = ((uint8_t*)pSrc)[3];
				((uint8_t*)pDst)[1] = ((uint8_t*)pSrc)[2];
				((uint8_t*)pDst)[2] = ((uint8_t*)pSrc)[1];
			}
			return true;

		case PF_B8G8R8:
			// from PF_B8G8R8 to PF_B8G8R8 (just copy)
			ASSERT(strideDst != strideSrc);
		case PF_B8G8R8A8:
			// from PF_B8G8R8A8 to PF_B8G8R8 (just copy)
			for (Size i=0; i<nSzize; ++i,(uint8_t*&)pDst+=strideDst,(uint8_t*&)pSrc+=strideSrc) {
				((uint16_t*)pDst)[0] = ((uint16_t*)pSrc)[0];
				((uint8_t*)pDst)[2] = ((uint8_t*)pSrc)[2];
			}
			return true;

		case PF_A8B8G8R8:
			// from PF_A8B8G8R8 to PF_B8G8R8 (just copy)
			for (Size i=0; i<nSzize; ++i,(uint8_t*&)pDst+=strideDst,(uint8_t*&)pSrc+=strideSrc) {
				((uint8_t*)pDst)[0] = ((uint8_t*)pSrc)[1];
				((uint8_t*)pDst)[1] = ((uint8_t*)pSrc)[2];
				((uint8_t*)pDst)[2] = ((uint8_t*)pSrc)[3];
			}
			return true;
		}
		break;

	case PF_B8G8R8A8:
		switch (formatSrc)
		{
		case PF_A8:
		case PF_GRAY8:
			// from PF_A8 to PF_B8G8R8A8 (doesn't make sense)
			ASSERT(pDst != pSrc);
			for (Size i=0; i<nSzize; ++i,(uint8_t*&)pDst+=strideDst,(uint8_t*&)pSrc+=strideSrc) {
				((uint8_t*)pDst)[0] = ((uint8_t*)pDst)[1] = ((uint8_t*)pDst)[2] = *((uint8_t*)pSrc);
				((uint8_t*)pDst)[3] = 0xFF;
			}
			return true;

		case PF_R5G6B5:
			// from PF_R5G6B5 to PF_B8G8R8A8 (16bits to 24bits))
			ASSERT(pDst != pSrc);
			for (Size i=0; i<nSzize; ++i,(uint8_t*&)pDst+=strideDst,(uint8_t*&)pSrc+=strideSrc) {
				const uint16_t clr = *((uint16_t*)pSrc);
				((uint8_t*)pDst)[0] = RGB16TOR(clr);
				((uint8_t*)pDst)[1] = RGB16TOG(clr);
				((uint8_t*)pDst)[2] = RGB16TOB(clr);
				((uint8_t*)pDst)[3] = 0xFF;
			}
			return true;

		case PF_R8G8B8:
			// from PF_R8G8B8 to PF_B8G8R8A8 (copy and flip the RGB and set A to 255)
			ASSERT(pDst != pSrc);
			for (Size i=0; i<nSzize; ++i,(uint8_t*&)pDst+=strideDst,(uint8_t*&)pSrc+=strideSrc) {
				((uint8_t*)pDst)[0] = ((uint8_t*)pSrc)[2];
				((uint8_t*)pDst)[1] = ((uint8_t*)pSrc)[1];
				((uint8_t*)pDst)[2] = ((uint8_t*)pSrc)[0];
				((uint8_t*)pDst)[3] = 0xFF;
			}
			return true;

		case PF_R8G8B8A8:
			// from PF_R8G8B8A8 to PF_B8G8R8A8 (copy and flip)
			ASSERT(pDst != pSrc);
			for (Size i=0; i<nSzize; ++i,(uint8_t*&)pDst+=strideDst,(uint8_t*&)pSrc+=strideSrc) {
				((uint8_t*)pDst)[0] = ((uint8_t*)pSrc)[2];
				((uint8_t*)pDst)[1] = ((uint8_t*)pSrc)[1];
				((uint8_t*)pDst)[2] = ((uint8_t*)pSrc)[0];
				((uint8_t*)pDst)[3] = ((uint8_t*)pSrc)[3];
			}
			return true;

		case PF_A8R8G8B8:
			// from PF_A8R8G8B8 to PF_B8G8R8A8 (copy and flip)
			ASSERT(pDst != pSrc);
			for (Size i=0; i<nSzize; ++i,(uint8_t*&)pDst+=strideDst,(uint8_t*&)pSrc+=strideSrc) {
				((uint8_t*)pDst)[0] = ((uint8_t*)pSrc)[3];
				((uint8_t*)pDst)[1] = ((uint8_t*)pSrc)[2];
				((uint8_t*)pDst)[2] = ((uint8_t*)pSrc)[1];
				((uint8_t*)pDst)[3] = ((uint8_t*)pSrc)[0];
			}
			return true;

		case PF_B8G8R8:
			// from PF_B8G8R8 to PF_B8G8R8A8 (just copy the RGB and set A to 255)
			ASSERT(pDst != pSrc);
			for (Size i=0; i<nSzize; ++i,(uint8_t*&)pDst+=strideDst,(uint8_t*&)pSrc+=strideSrc) {
				((uint16_t*)pDst)[0] = ((uint16_t*)pSrc)[0];
				((uint8_t*)pDst)[2] = ((uint8_t*)pSrc)[2];
				((uint8_t*)pDst)[3] = 0xFF;
			}
			return true;

		case PF_B8G8R8A8:
			// from PF_B8G8R8A8 to PF_B8G8R8A8 (just copy)
			ASSERT(strideDst != strideSrc);
			for (Size i=0; i<nSzize; ++i,(uint8_t*&)pDst+=strideDst,(uint8_t*&)pSrc+=strideSrc)
				((uint32_t*)pDst)[0] = ((uint32_t*)pSrc)[0];
			return true;

		case PF_A8B8G8R8:
			// from PF_A8B8G8R8 to PF_B8G8R8A8 (copy and flip)
			ASSERT(pDst != pSrc);
			for (Size i=0; i<nSzize; ++i,(uint8_t*&)pDst+=strideDst,(uint8_t*&)pSrc+=strideSrc) {
				((uint8_t*)pDst)[0] = ((uint8_t*)pSrc)[1];
				((uint8_t*)pDst)[1] = ((uint8_t*)pSrc)[2];
				((uint8_t*)pDst)[2] = ((uint8_t*)pSrc)[3];
				((uint8_t*)pDst)[3] = ((uint8_t*)pSrc)[0];
			}
			return true;
		}
		break;

	case PF_A8B8G8R8:
		switch (formatSrc)
		{
		case PF_A8:
		case PF_GRAY8:
			// from PF_A8 to PF_A8B8G8R8 (doesn't make sense)
			ASSERT(pDst != pSrc);
			for (Size i=0; i<nSzize; ++i,(uint8_t*&)pDst+=strideDst,(uint8_t*&)pSrc+=strideSrc) {
				((uint8_t*)pDst)[0] = 0xFF;
				((uint8_t*)pDst)[1] = ((uint8_t*)pDst)[2] = ((uint8_t*)pDst)[3] = *((uint8_t*)pSrc);
			}
			return true;

		case PF_R5G6B5:
			// from PF_R5G6B5 to PF_A8B8G8R8 (16bits to 24bits))
			ASSERT(pDst != pSrc);
			for (Size i=0; i<nSzize; ++i,(uint8_t*&)pDst+=strideDst,(uint8_t*&)pSrc+=strideSrc) {
				const uint16_t clr = *((uint16_t*)pSrc);
				((uint8_t*)pDst)[0] = 0xFF;
				((uint8_t*)pDst)[1] = RGB16TOR(clr);
				((uint8_t*)pDst)[2] = RGB16TOG(clr);
				((uint8_t*)pDst)[3] = RGB16TOB(clr);
			}
			return true;

		case PF_R8G8B8:
			// from PF_R8G8B8 to PF_A8B8G8R8 (copy and flip the RGB and set A to 255)
			ASSERT(pDst != pSrc);
			for (Size i=0; i<nSzize; ++i,(uint8_t*&)pDst+=strideDst,(uint8_t*&)pSrc+=strideSrc) {
				((uint8_t*)pDst)[0] = 0xFF;
				((uint8_t*)pDst)[1] = ((uint8_t*)pSrc)[2];
				((uint8_t*)pDst)[2] = ((uint8_t*)pSrc)[1];
				((uint8_t*)pDst)[3] = ((uint8_t*)pSrc)[0];
			}
			return true;

		case PF_R8G8B8A8:
			// from PF_R8G8B8A8 to PF_A8B8G8R8 (copy and flip)
			ASSERT(pDst != pSrc);
			for (Size i=0; i<nSzize; ++i,(uint8_t*&)pDst+=strideDst,(uint8_t*&)pSrc+=strideSrc) {
				((uint8_t*)pDst)[0] = ((uint8_t*)pSrc)[3];
				((uint8_t*)pDst)[1] = ((uint8_t*)pSrc)[2];
				((uint8_t*)pDst)[2] = ((uint8_t*)pSrc)[1];
				((uint8_t*)pDst)[3] = ((uint8_t*)pSrc)[0];
			}
			return true;

		case PF_A8R8G8B8:
			// from PF_A8R8G8B8 to PF_A8B8G8R8 (copy and flip)
			ASSERT(pDst != pSrc);
			for (Size i=0; i<nSzize; ++i,(uint8_t*&)pDst+=strideDst,(uint8_t*&)pSrc+=strideSrc) {
				((uint8_t*)pDst)[0] = ((uint8_t*)pSrc)[0];
				((uint8_t*)pDst)[1] = ((uint8_t*)pSrc)[2];
				((uint8_t*)pDst)[2] = ((uint8_t*)pSrc)[1];
				((uint8_t*)pDst)[3] = ((uint8_t*)pSrc)[0];
			}
			return true;

		case PF_B8G8R8:
			// from PF_B8G8R8 to PF_A8B8G8R8 (just copy the RGB and set A to 255)
			ASSERT(pDst != pSrc);
			for (Size i=0; i<nSzize; ++i,(uint8_t*&)pDst+=strideDst,(uint8_t*&)pSrc+=strideSrc) {
				((uint8_t*)pDst)[0] = 0xFF;
				((uint8_t*)pDst)[1] = ((uint8_t*)pSrc)[0];
				((uint8_t*)pDst)[2] = ((uint8_t*)pSrc)[1];
				((uint8_t*)pDst)[3] = ((uint8_t*)pSrc)[2];
			}
			return true;

		case PF_B8G8R8A8:
			// from PF_B8G8R8A8 to PF_A8B8G8R8 (copy and flip)
			ASSERT(pDst != pSrc);
			for (Size i=0; i<nSzize; ++i, (uint8_t*&)pDst+=strideDst, (uint8_t*&)pSrc+=strideSrc) {
				((uint8_t*)pDst)[0] = ((uint8_t*)pSrc)[3];
				((uint8_t*)pDst)[1] = ((uint8_t*)pSrc)[0];
				((uint8_t*)pDst)[2] = ((uint8_t*)pSrc)[1];
				((uint8_t*)pDst)[3] = ((uint8_t*)pSrc)[2];
			}
			return true;

		case PF_A8B8G8R8:
			// from PF_A8B8G8R8 to PF_A8B8G8R8 (just copy)
			ASSERT(strideDst != strideSrc);
			for (Size i=0; i<nSzize; ++i,(uint8_t*&)pDst+=strideDst,(uint8_t*&)pSrc+=strideSrc)
				((uint32_t*)pDst)[0] = ((uint32_t*)pSrc)[0];
			return true;
		}
		break;
	}
	return false;
} // FilterFormat
/*----------------------------------------------------------------*/


// Flip R and B in a 24bit pixel color.
// size is the number of pixels to process.
// stride is the number of bytes used for one pixel.
void CImage::FlipRB24(uint8_t* data, Size size, Size stride)
{
	for (Size i=0; i<size; ++i, data+=stride)
	{
		const uint8_t tmp = data[0];
		data[0] = data[2];
		data[2] = tmp;
	}
} // FlipRB24
/*----------------------------------------------------------------*/


// Copy and flip R and B in a 24bit pixel color.
// size is the number of pixels to process.
// stride is the number of bytes used for one pixel for dest and source respectively.
void CImage::CopyFlipRB24(uint8_t* pDst, const uint8_t* pSrc, Size size, Size strideDst, Size strideSrc)
{
	for (Size i=0; i<size; ++i, pDst+=strideDst, pSrc+=strideSrc)
	{
		pDst[0] = pSrc[2];
		pDst[1] = pSrc[1];
		pDst[2] = pSrc[0];
	}
} // CopyFlipRB24
/*----------------------------------------------------------------*/


CImage* CImage::Create(LPCTSTR szName, IMCREATE mode)
{
	// check image file type
	CImage* pImage;
	LPCTSTR const fext = _tcsrchr(szName, '.');
	if (fext == NULL)
		goto UNKNOWN_FORMAT;
	if (_tcsncicmp(fext, _T(".sci"), 4) == 0)
		pImage = new CImageSCI();
	#ifdef _IMAGE_BMP
	else if (_tcsncicmp(fext, _T(".bmp"), 4) == 0)
		pImage = new CImageBMP();
	#endif
	#ifdef _IMAGE_TGA
	else if (_tcsncicmp(fext, _T(".tga"), 4) == 0)
		pImage = new CImageTGA();
	#endif
	#ifdef _IMAGE_DDS
	else if (_tcsncicmp(fext, _T(".dds"), 4) == 0)
		pImage = new CImageDDS();
	#endif
	#ifdef _IMAGE_PNG
	else if (_tcsncicmp(fext, _T(".png"), 4) == 0)
		pImage = new CImagePNG();
	#endif
	#ifdef _IMAGE_JPG
	else if (_tcsncicmp(fext, _T(".jpg"), 4) == 0 || _tcsncicmp(fext, _T(".jpeg"), 5) == 0)
		pImage = new CImageJPG();
	#endif
	#ifdef _IMAGE_TIFF
	else if (_tcsncicmp(fext, _T(".tif"), 4) == 0 || _tcsncicmp(fext, _T(".tiff"), 5) == 0)
		pImage = new CImageTIFF();
	#endif
	else
		goto UNKNOWN_FORMAT;

	// open the image stream
	if (FAILED(pImage->Reset(szName, mode))) {
		delete pImage;
		return NULL;
	}

	return pImage;

UNKNOWN_FORMAT:
	// Unknown format
	LOG(LT_IMAGE, "error: unknown image format '%s'", szName);
	return NULL;
} // Create
/*----------------------------------------------------------------*/


#ifndef _RELEASE

// Save image as raw data.
void CImage::Dump(LPCTSTR szFileName)
{
	const String strName = Util::getFileName(szFileName)+String::ToString(m_width)+_T("x")+String::ToString(m_height)+_T("x")+String::ToString(m_stride)+Util::getFileExt(szFileName);
	File f(strName, File::WRITE, File::CREATE | File::TRUNCATE);
	if (!f.isOpen())
		return;
	f.write(GetData(), GetDataSize());
} // FlipRB24
/*----------------------------------------------------------------*/

#endif
