#include "StdAfx.h"
#include <assert.h>													// assert()
#include <Dbghelp.h>												// GetTimestampForLoadedLibrary

#include "IRCLog.h"													// IRCLog
#include "IConfig.h"												// IConfig
#include "PathUtil.h"												// ReplaceExtension

#include "ImageCompiler.h"									// CImageCompiler
#include "ImageUserDialog.h"								// CImageUserDialog
#include "ICfgFile.h"												// ICfgFile

#define _TIFF_DATA_TYPEDEFS_								// because we defined uint32,... already
#include "TiffLib/include/tiffio.h"					// TIFF library

#include "PixelFormats.h"										// g_pixelformats

#include "neuquant.h"

#include "ImageObject.h"
#include <commctrl.h>												// TCITEM

#include "WeightFilterSet.h"								// CWeightFilterSet
#include "Cry_Math.h"												// Vec3
#include "Cry_Color.h"											// ColorF
#include "IResCompiler.h"										// Vec3
#include "tools.h"													// GetBoolParam()
#include "ATICompressorLib\ATI_Compress.h"	// ATI_TC_ConvertTexture()
#include "NVidiaLib\dxtlib.h"								// NVidia DXT processing
#include "SuffixUtil.h"											// CSuffixUtil
#include "TextureFallbackSystem.h"					// CTextureFallbackSystem
#include "ImageExtensionHelper.h"						// CImageExtensionHelper
#include "SimpleDXTCompressor.h"						// CSimpleDXTCompressor

//#ifdef CRY_STRING
//int sEmptyStringBuffer[] = { -1, 0, 0, 0 };
//string::StrHeader* string::m_emptyStringData = (string::StrHeader*)&sEmptyStringBuffer;
//wstring::StrHeader* wstring::m_emptyStringData = (wstring::StrHeader*)&sEmptyStringBuffer;
//#endif //CRY_STRING


const char* stristr(const char* szString, const char* szSubstring)
{
  int nSuperstringLength = (int)strlen(szString);
  int nSubstringLength = (int)strlen(szSubstring);

  for(int nSubstringPos = 0; nSubstringPos <= nSuperstringLength - nSubstringLength; ++nSubstringPos)
  {
		if (strnicmp(szString+nSubstringPos, szSubstring, nSubstringLength) == 0)
			return szString+nSubstringPos;
  }

	return NULL;
}



// constructor
CImageCompiler::CImageCompiler(CTextureFallbackSystem* pTextureFallbackSystem)
:	m_pTextureFallbackSystem(pTextureFallbackSystem)
{
	m_pd3ddev=0;
	m_pd3d=0;
	m_pInputImage=0;
	m_pFinalImage=0;
	m_pCC=0;
	m_iOrigPixelFormatNo=0;
	m_hWnd=0;
	m_bInitialized=false;
	m_fTimeProfiled=0;

	ClearInputImageFlags();
}


// destructor
CImageCompiler::~CImageCompiler()
{
	DeInit();

	assert(!m_pd3ddev);
	assert(!m_pd3d);
	assert(!m_pInputImage);
	assert(!m_pFinalImage);
}


void CImageCompiler::FreeTexture()
{
	delete m_pInputImage; m_pInputImage=0;
	delete m_pFinalImage; m_pFinalImage=0;
}



ImageObject *CImageCompiler::GetOutputAndRelease()
{
	ImageObject *pRet=m_pFinalImage;

	m_pFinalImage=0;

	return pRet;
}


char *CImageCompiler::GetNameFromD3DFormat( D3DFORMAT inFormat )
{
#define FMT(fmt) case fmt: return #fmt;

	switch(inFormat)
	{
		FMT(D3DFMT_UNKNOWN);
		FMT(D3DFMT_R8G8B8);
		FMT(D3DFMT_A8R8G8B8);
		FMT(D3DFMT_X8R8G8B8);
		FMT(D3DFMT_R5G6B5);
		FMT(D3DFMT_X1R5G5B5);
		FMT(D3DFMT_A1R5G5B5);
		FMT(D3DFMT_A4R4G4B4);
		FMT(D3DFMT_R3G3B2);
		FMT(D3DFMT_A8);
		FMT(D3DFMT_A8R3G3B2);
		FMT(D3DFMT_X4R4G4B4);
		FMT(D3DFMT_A2B10G10R10);
		FMT(D3DFMT_A8B8G8R8);
		FMT(D3DFMT_X8B8G8R8);
		FMT(D3DFMT_G16R16);
		FMT(D3DFMT_A2R10G10B10);
		FMT(D3DFMT_A16B16G16R16);
		FMT(D3DFMT_A8P8);
		FMT(D3DFMT_P8);
		FMT(D3DFMT_L8);
		FMT(D3DFMT_A8L8);
		FMT(D3DFMT_A4L4);
		FMT(D3DFMT_V8U8);
		FMT(D3DFMT_L6V5U5);
		FMT(D3DFMT_X8L8V8U8);
		FMT(D3DFMT_Q8W8V8U8);
		FMT(D3DFMT_V16U16);
		FMT(D3DFMT_A2W10V10U10);
		FMT(D3DFMT_UYVY);
		FMT(D3DFMT_R8G8_B8G8);
		FMT(D3DFMT_YUY2);
		FMT(D3DFMT_G8R8_G8B8);
		FMT(D3DFMT_DXT1);
		FMT(D3DFMT_DXT2);
		FMT(D3DFMT_DXT3);
		FMT(D3DFMT_DXT4);
		FMT(D3DFMT_DXT5);
		FMT(D3DFMT_D16_LOCKABLE);
		FMT(D3DFMT_D32);
		FMT(D3DFMT_D15S1);
		FMT(D3DFMT_D24S8);
		FMT(D3DFMT_D24X8);
		FMT(D3DFMT_D24X4S4);
		FMT(D3DFMT_D16);
		FMT(D3DFMT_D32F_LOCKABLE);
		FMT(D3DFMT_D24FS8);
		FMT(D3DFMT_L16);
		FMT(D3DFMT_VERTEXDATA);
		FMT(D3DFMT_INDEX16);
		FMT(D3DFMT_INDEX32);
		FMT(D3DFMT_Q16W16V16U16);
		FMT(D3DFMT_MULTI2_ARGB8);
		FMT(D3DFMT_R16F);
		FMT(D3DFMT_G16R16F);
		FMT(D3DFMT_A16B16G16R16F);
		FMT(D3DFMT_R32F);
		FMT(D3DFMT_G32R32F);
		FMT(D3DFMT_A32B32G32R32F);
		FMT(D3DFMT_CxV8U8);
	}
#undef FMT

	return "D3DFMT_UNKNOWN";
}



void CImageCompiler::GetInputFromMemory( ImageObject *pInput )
{
	FreeTexture();

	m_pInputImage=pInput;

	ClearInputImageFlags();

	if(m_pInputImage)
		m_bInputUsesAlpha=m_pInputImage->HasAlpha();
}

void CImageCompiler::DeInit()
{
	// no logging allowed - m_pCC is already 0

	if(m_bInitialized)
	{
		m_bInitialized = false;

		FreeTexture();

		ReleasePpo(&m_pd3ddev);
		ReleasePpo(&m_pd3d);

		UnregisterClass("skeleton", g_hInst);
	}
}


bool CImageCompiler::SaveOutput( ImageObject *pOutput, const char *szType, const char *lpszPathName )
{
	assert(lpszPathName);
	assert(pOutput);

	if(!pOutput)
	{
		CCLOG->LogError("CImageCompiler::Save() failed (conversion failed?)");
		return false;
	}

	// Force remove of the read only flag.
	SetFileAttributes( lpszPathName,FILE_ATTRIBUTE_ARCHIVE );

	if(m_Props.m_eGlobalCompressor==CImageProperties::eCGTIF)
	{
		if(!SaveAsTIFF(pOutput,lpszPathName))
		{
			CCLOG->LogError("CImageCompiler::SaveAsTIFF() failed");
			return false;
		}
	}
	else
	{
		if(!pOutput->SaveImage(lpszPathName))
		{
			CCLOG->LogError("CImageCompiler::Save failed (file IO?)");
			return false;
		}
	}

	uint32 dwAttached=0;

	if(m_pFinalImage->GetAttachedImage())
		dwAttached=CalcTextureMemory(m_pFinalImage->GetAttachedImage());

	if(dwAttached)
		CCLOG->Log("   %s: %d bytes (including %d bytes attached)",szType,CalcTextureMemory(m_pFinalImage),dwAttached);
	 else
		CCLOG->Log("   %s: %d bytes",szType,CalcTextureMemory(m_pFinalImage));

	return true;
}


LRESULT CALLBACK MainWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
	return DefWindowProc(hWnd,msg,wParam,lParam);
}

void CImageCompiler::UserMessage( const char *szMessage )
{
	assert(szMessage);
	MessageBox(0,szMessage,"ResourcCompileImage",MB_OK);
}


bool CImageCompiler::Init( HWND inhWnd )
{
	m_bInitialized = true;

	WNDCLASSEX wcex;

	wcex.cbSize = sizeof(WNDCLASSEX); 

	wcex.style			= CS_HREDRAW | CS_VREDRAW;
	wcex.lpfnWndProc	= (WNDPROC)MainWndProc;
	wcex.cbClsExtra		= 0;
	wcex.cbWndExtra		= 0;
	wcex.hInstance		= g_hInst;
	wcex.hIcon			= 0;
	wcex.hCursor		= 0;
	wcex.hbrBackground	= 0;
	wcex.lpszMenuName	= 0;
	wcex.lpszClassName	= "skeleton";
	wcex.hIconSm		= 0;

	RegisterClassEx(&wcex);

  m_hWnd = CreateWindow(
      "skeleton", "the title",
      WS_OVERLAPPEDWINDOW,
      0, 0, 400, 400,
      NULL, NULL, g_hInst, NULL);

   // return if error on CreateWindow
  if(m_hWnd == NULL)
		return false;

	InitCommonControls();

/* - not need to log this - userdialog shows available formats
	CCLOG->Log("");
	CCLOG->Log("ResourceCompilerImage supported output pixel formats:");

	int iCount=CPixelFormats::GetPixelFormatCount();

	for(int i=0;i<iCount;i++)
	{
		if(g_pixelformats[i].bDestinationFormat)
		{
			CCLOG->Log(" * %s",g_pixelformats[i].szName);
			CCLOG->Log("   %s",g_pixelformats[i].szDescription);
		}
	}
	CCLOG->Log("");
*/

	return true;
}


bool CImageCompiler::InitDirectX()
{
	assert(m_pCC);

	if(m_pd3d!=0 && m_pd3ddev!=0)
		return true;									// was already called
	
	CCLOG->Log("");
	CCLOG->Log("Init DirectX ...");
	CCLOG->Log("");

	// Initialize DirectDraw
	m_pd3d = Direct3DCreate9(D3D_SDK_VERSION);
	if (m_pd3d == NULL)
	{
		// m_pCC is 0 so we cannot use log 
		UserMessage("Direct3DCreate9() failed (wrong DirectX version?)");
		return false;
	}

	HRESULT hr;
	D3DDEVTYPE devType;

	ZeroMemory(&m_presentParams, sizeof(m_presentParams));
	m_presentParams.Windowed = TRUE;
	m_presentParams.SwapEffect = D3DSWAPEFFECT_COPY;
	m_presentParams.BackBufferWidth = 256;
	m_presentParams.BackBufferHeight = 256;
	m_presentParams.BackBufferFormat = D3DFMT_UNKNOWN;

	//devType = D3DDEVTYPE_REF; 

	IConfig *pConfig = m_pCC->config;

	bool bUserDialog=false;

	m_pCC->config->Get("userdialog",bUserDialog);

	if(bUserDialog)
	{
		devType = D3DDEVTYPE_HAL;
		CCLOG->Log("    ... HAL");
	}
	else
	{
		devType = D3DDEVTYPE_NULLREF;		// Mike Borrows hinted to us this can be used
		CCLOG->Log("    ... NULLREF");
	}

	hr = m_pd3d->CreateDevice(D3DADAPTER_DEFAULT, devType,m_hWnd, 
		D3DCREATE_SOFTWARE_VERTEXPROCESSING, &m_presentParams, &m_pd3ddev);
	if(FAILED(hr))
	{
		m_pd3d->Release();m_pd3d=0;
		// maybe we should the log here as well
		UserMessage("CreateDevice() failed (wrong DirectX version? - at minimum you should have DirectX9.0c)");
		return false;
	}

	if(devType!=D3DDEVTYPE_NULLREF)
	{
		D3DCAPS9 Caps;
		m_pd3ddev->GetDeviceCaps(&Caps);
		if(Caps.PrimitiveMiscCaps & D3DPMISCCAPS_NULLREFERENCE)
		{
			m_pd3ddev->Release();m_pd3ddev=0;
			m_pd3d->Release();m_pd3d=0;

			// maybe we should the log here as well
			UserMessage("CreateDevice() D3DPMISCCAPS_NULLREFERENCE (Is you default adapter a 3D gfx card?)");
			return false;
		}
	}

	return true;
}


ImageObject *CImageCompiler::LoadImage( const char *lpszPathName, const char *lpszExtension, ICfgFile *pFileConfig )
{
	if(stricmp(lpszExtension,"tif")==0)
		return LoadUsingTIFLoader(lpszPathName,pFileConfig);		// we handle the loading ourselfes

	CCLOG->LogError("unknown Extension:'%s'",lpszExtension );
	return 0;			// unknown extension
}

/*
bool CImageCompiler::IsDDNDIFProcessing( ImageObject *pCurrentImage ) const
{
	assert(m_pCC);

	if(GetBoolParam(m_pCC->config,"ddndifprocessing",false))
	if(CSuffixUtil::CheckForSuffix(m_pCC->sourceFileFinal,"ddndif"))
	if(!IsPerfectGreyscale(pCurrentImage))
		return true;

	return false;
}
*/


bool CImageCompiler::LoadInput( const char *lpszPathName, const char *lpszExtension )
{
	FreeTexture();

	// testcases for CSuffixUtil::CheckForSuffix()
	assert(!CSuffixUtil::CheckForSuffix("dirt_ddn.dds","ddndif"));
	assert(CSuffixUtil::CheckForSuffix("dirt_ddndif.dds","ddndif"));
	assert(!CSuffixUtil::CheckForSuffix("dirt.dds","ddndif"));
	assert(CSuffixUtil::CheckForSuffix("dirt_ddn_ddndif.dds","ddndif"));
	assert(!CSuffixUtil::CheckForSuffix("dirt_ddndif_ddn.dds","ddndif"));
	assert(CSuffixUtil::CheckForSuffix("dirt_ddndif","ddndif"));
	assert(!CSuffixUtil::CheckForSuffix("dirt_ddndif2","ddndif"));
	assert(!CSuffixUtil::CheckForSuffix("dirt_ddndif2_","ddndif"));

	assert(!m_pInputImage);

	m_pInputImage = LoadImage(lpszPathName,lpszExtension,m_pCC->pFileSpecificConfig);

	if(!m_pInputImage)
		return false;

	if(CSuffixUtil::CheckForSuffix(m_pCC->sourceFileFinal,"ddndif"))
	{
		m_bInputUsesDenormalizedNormals=true;
	}

	return true;
}


CString CImageCompiler::GetSpecialInstructionsFromTIFF( TIFF *tif )
{
	// get image metadata
	{
		unsigned char *pPtr;
		unsigned int wc;

		if(TIFFGetField(tif,TIFFTAG_PHOTOSHOP,&wc,&pPtr))		// 34377 IPTC TAG
		{
			unsigned char *pPtrEnd = pPtr + wc;

			while(pPtr!=pPtrEnd)
			{
				if(pPtr[0]=='8' && pPtr[1]=='B' && pPtr[2]=='I' && pPtr[3]=='M')
				{
					pPtr+=4;

					unsigned short Id = ((unsigned short)(pPtr[0])<<8) | (unsigned short)pPtr[1];

					pPtr+=2;

					// get pascal string 
					unsigned int dwNameSize = (unsigned int)pPtr[0];

					pPtr++;

					char szName[256];

					strncpy(szName,(char *)pPtr,dwNameSize);
					szName[dwNameSize]=0;
/*
					OutputDebugString("Tag: '");
					OutputDebugString(szName);
					OutputDebugString("'\n");
*/
					pPtr+=dwNameSize;

					// align 2 bytes
					if((dwNameSize%2)==0)
						pPtr++;

					unsigned int dwSize = ((unsigned int)(pPtr[0])<<24) | ((unsigned int)(pPtr[1])<<16) | ((unsigned int)(pPtr[2])<<8) |(unsigned int)pPtr[3];
					pPtr+=4;

					//if(strcmp(szName,"Caption")==0)
					//	return GetSpecialInstructionsFromPhotoshopCaption((unsigned char *)pPtr,dwSize);

					if (Id == 0x0404)
					{
						m_iptcHeader.Parse(pPtr, dwSize);
						std::vector<unsigned char> buffer;
						m_iptcHeader.GetCombinedFields(CIPTCHeader::FieldSpecialInstructions, buffer, " ");
						return CString(&buffer[0]);
					}

					pPtr+=dwSize;

					// align 2 bytes
					if((dwSize%2)!=0)
						pPtr++;
				}
				else
				{
					assert(0);
				}
			}
		}
	}

	return CString("");
}

bool CImageCompiler::IsFinalPixelFormatValidForNormalmaps() const
{
	const SPixelFormats *pInfo = CPixelFormats::GetPixelFormatInfo(m_Props.GetDestPixelFormat(m_bInputUsesAlpha));

	if(!pInfo)
		return false;

	return pInfo->DxtNo==D3DFMT_3DC;		// can be adjusted for other platforms
}

bool CImageCompiler::IsFinalPixelFormatCompressed() const
{
	const SPixelFormats *pInfo = CPixelFormats::GetPixelFormatInfo(m_Props.GetDestPixelFormat(m_bInputUsesAlpha));

	if(!pInfo)
		return false;

	return pInfo->bCompressed;
}


ImageObject *CImageCompiler::LoadUsingTIFLoader( const char *lpszPathName, ICfgFile *pFileConfig )
{
	TIFF* tif = TIFFOpen(lpszPathName,"r");

	ImageObject *pRet=0;

	if(tif) 
	{
		uint32 dwWidth, dwHeight, tifExtra=0;
		size_t npixels;
		uint32* raster;

		TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &dwWidth);
		TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &dwHeight);

		uint32 dwChannels=3;
		TIFFGetField(tif, TIFFTAG_SAMPLESPERPIXEL,&dwChannels);
//		TIFFGetField(tif, TIFFTAG_BITSPERSAMPLE,&dwBitsPerChannel);					// usually 8 bits

		bool bSetAlphaToWhite = (dwChannels==3);			// 3 channel textures should be treated as 4 channel ones with white alpha

		CCLOG->Log("CImageCompiler::LoadUsingTIFLoader dwChannels=%d",dwChannels);

		npixels = dwWidth * dwHeight;

		tsize_t sizeRaster = (tsize_t)(npixels * sizeof (uint32));

		raster = (uint32*) _TIFFmalloc(sizeRaster);

		if(raster) 
		{
			if(TIFFReadRGBAImage(tif, dwWidth, dwHeight, raster, 0)) 
			{
				pRet = new CRAWImage(dwWidth,dwHeight,1,CPixelFormats::GetNoFromD3DFormat(D3DFMT_A8R8G8B8),IsFinalPixelFormatCompressed());

				char *dest;
				uint32 dwPitch;

				if(pRet->Lock(0,0,dest,dwPitch))
				{
					for(uint32 dwY=0;dwY<dwHeight;++dwY)
					{
						char *src2 = (char *)&raster[(dwHeight-1-dwY)*dwWidth];
						char *dest2 = &dest[dwPitch*dwY];

						if(bSetAlphaToWhite)
						{
							for(uint32 dwX=0;dwX<dwWidth;++dwX)
							{
								// change byte order
								dest2[0] = src2[2];			// blue
								dest2[1] = src2[1];
								dest2[2] = src2[0];
								dest2[3] = (char)0xff;				// alpha

								dest2+=4;src2+=4;
							}
						}
						else
						{
							for(uint32 dwX=0;dwX<dwWidth;++dwX)
							{
								// change byte order
								dest2[0] = src2[2];			// blue
								dest2[1] = src2[1];
								dest2[2] = src2[0];
								dest2[3] = src2[3];			// alpha

								dest2+=4;src2+=4;
							}
						}
					}
					
					// m_pCC->pFileSpecificConfig
					if(pFileConfig)
					{
						CString sSpecialInstructions = GetSpecialInstructionsFromTIFF(tif);

						UpdateOrCreateEntryFromString(pFileConfig,sSpecialInstructions.GetBuffer());
					}

					pRet->Unlock(0);
				}
				else
				{
					CCLOG->LogError("CImageCompiler::LoadUsingTIFLoader Lock failed");
					delete pRet; pRet=0;
				}
			}
			else CCLOG->LogError("CImageCompiler::LoadUsingTIFLoader failed (RGA/RGBA image?)");

			_TIFFfree(raster);
		}
		else CCLOG->LogError("LoadUsingTIFLoader out of memory %dBytes",sizeRaster);

		TIFFClose(tif);
	}
	else CCLOG->LogError("CImageCompiler::LoadUsingTIFLoader TIFFOpen failed");


	m_pCC->pFileSpecificConfig->SetConfig( eCP_PriorityFile,COMMON_SECTION,m_pCC->config );
	m_pCC->pFileSpecificConfig->SetConfig( eCP_PriorityFile,m_pCC->plattformName,m_pCC->config );

	ClearInputImageFlags();

	if(pRet)
		m_bInputUsesAlpha=pRet->HasAlpha();

	return pRet;
}

// needed for UpdateAndSaveConfigToTIF 
class CConfigString :public IConfigSink
{
public:
	// constructor
	CConfigString() :m_pMemBlock(0)
	{
	}

	// destructor
	~CConfigString()
	{
		delete m_pMemBlock;
	}

	// interface IConfigSink -------------------------------------------

	virtual void Set( const EConfigPriority ePri, const char *key, const char* value )
	{
		assert(ePri==eCP_PriorityFile);

		m_sValue += CString("/") + key + "=" + value + " ";
	}

	// --------------------------------

	// call only once, memory is released by this class
	unsigned char *GetResult( uint32 &outdwSize )
	{
		//assert(!m_pMemBlock);

		//unsigned short wSize = m_sValue.GetLength();
		//unsigned char *pwSize = (unsigned char *)&wSize;

		//unsigned int dwSize = wSize+5;
		//unsigned char *pdwSize = (unsigned char *)&dwSize;

		////const char* szCaptionString = "Caption";
		//const char* szCaptionString = "";
		//outdwSize=4+2+1+(uint32)strlen(szCaptionString)+4+dwSize;

		//m_pMemBlock = new unsigned char[outdwSize];

		//int nMemBlockIndex = 0;
		//m_pMemBlock[nMemBlockIndex++]='8';	m_pMemBlock[nMemBlockIndex++]='B';	m_pMemBlock[nMemBlockIndex++]='I';	m_pMemBlock[nMemBlockIndex++]='M';
		//m_pMemBlock[nMemBlockIndex++]=0x04; m_pMemBlock[nMemBlockIndex++]=0x04;
		//m_pMemBlock[nMemBlockIndex++]=(unsigned char)strlen(szCaptionString); strncpy((char *)&m_pMemBlock[nMemBlockIndex],szCaptionString,strlen(szCaptionString)); nMemBlockIndex += strlen(szCaptionString);
		//m_pMemBlock[nMemBlockIndex++]=pdwSize[3];	m_pMemBlock[nMemBlockIndex++]=pdwSize[2];	m_pMemBlock[nMemBlockIndex++]=pdwSize[1];	m_pMemBlock[nMemBlockIndex++]=pdwSize[0];
		//m_pMemBlock[nMemBlockIndex++]=28;	m_pMemBlock[nMemBlockIndex++]=2;
		//m_pMemBlock[nMemBlockIndex++]=40;		// Special Instructions
		//m_pMemBlock[nMemBlockIndex++]=pwSize[1];	m_pMemBlock[nMemBlockIndex++]=pwSize[0];

		//memcpy(&m_pMemBlock[nMemBlockIndex],m_sValue,m_sValue.GetLength()); nMemBlockIndex += m_sValue.GetLength();

		//// debug
		//OutputDebugString("\nWrite SpecialInstructions in TIFF: '");
		//OutputDebugString(m_sValue);		
		//OutputDebugString("'\n");

		//return m_pMemBlock;
#define ADD(str) std::copy(str, str + sizeof(str) - 1, std::back_inserter(irb))
		std::vector<char> irb;
		ADD("8BIM");
		ADD("\x04\x04");
		ADD("\0\0");
		int irbSizePosition = int(irb.size());
		ADD("\0\0\0\0");
		int irbDataStart = int(irb.size());
		ADD("\x1C\x02");
		ADD("\0");
		ADD("\x00\x02");
		ADD("\x00\x02");
		ADD("\x1C\x02");
		ADD("\x28");
		int captionSizePosition = int(irb.size());
		ADD("\0\0");
		int captionStartPosition = int(irb.size());
		//ADD(m_sValue.GetString());
		std::copy(m_sValue.GetString(), m_sValue.GetString() + m_sValue.GetLength(), std::back_inserter(irb));
#undef ADD

		int captionSize = int(irb.size()) - captionStartPosition;
		char* captionSizePointer = &irb[0] + captionSizePosition;
		captionSizePointer[0] = ((captionSize >> 8) & 0xFF); captionSizePointer[1] = (captionSize & 0xFF);

		int irbSize = int(irb.size()) - irbDataStart;
		char* irbSizePointer = &irb[0] + irbSizePosition;
		irbSizePointer[0] = ((irbSize >> 24) & 0xFF); irbSizePointer[1] = ((irbSize >> 16) & 0xFF); irbSizePointer[2] = ((irbSize >> 8) & 0xFF); irbSizePointer[3] = (irbSize & 0xFF);

		m_pMemBlock = new unsigned char[int(irb.size())];
		memcpy(m_pMemBlock, &irb[0], int(irb.size()));

		outdwSize = int(irb.size());
		return m_pMemBlock;
	}

	// Arguments:
	//   szName - must not be 0
	void SetSection( const char *szName )
	{
		assert(szName);
	
//		m_sValue += CString("[") + szName + "]";
	}

private: // -----------------------------------------------------

	CString						m_sValue;					//
	unsigned char *		m_pMemBlock;			//
};




// Arguments:
//   lpszPathName must not be 0
//   pCfgFile must not be 0
//   m_pCC name is misleading but this way we can use CCLOG
static bool UpdateAndSaveConfigToTIF( const char *lpszPathName, ICfgFile *pCfgFile, ConvertContext *m_pCC )
{
	assert(lpszPathName);
	assert(pCfgFile);

	bool bRet=false;

	TIFF* tif_in = TIFFOpen(lpszPathName,"r");	

	if(tif_in) 
	{			 
		uint32 dwWidth( 0 ), dwHeight( 0 ), dwPlanes ( 0 ), dwBitsPerSample( 0 );
		uint32 dwPhotometric( 0 ), dwOrientation( 0 ), dwPlanarConfig( 0 );
		uint32 dwRowsPerStrip( 0 ), dwCompression( 0 );

		// get properties
		TIFFGetField(tif_in, TIFFTAG_IMAGEWIDTH, &dwWidth);
		TIFFGetField(tif_in, TIFFTAG_IMAGELENGTH, &dwHeight);
		TIFFGetField(tif_in, TIFFTAG_SAMPLESPERPIXEL, &dwPlanes);
		TIFFGetField(tif_in, TIFFTAG_BITSPERSAMPLE, &dwBitsPerSample ); 
		TIFFGetField(tif_in, TIFFTAG_ROWSPERSTRIP, &dwRowsPerStrip ); 
		TIFFGetField(tif_in, TIFFTAG_COMPRESSION, &dwCompression);
		TIFFGetField(tif_in, TIFFTAG_PHOTOMETRIC, &dwPhotometric );
		TIFFGetField(tif_in, TIFFTAG_PLANARCONFIG, &dwPlanarConfig );
		TIFFGetField(tif_in, TIFFTAG_ORIENTATION, &dwOrientation );

		size_t npixels( dwWidth * dwHeight );

		char* raster = (char*) _TIFFmalloc((tsize_t)(npixels * dwPlanes));

		if(!raster)
			CCLOG->LogError("UpdateAndSaveConfigToTIF out of memory");

		if(raster
		&& (dwPlanes==1 || dwPlanes==3 || dwPlanes==4)
		&& dwBitsPerSample==8) 
		{
			// load image as we overwrite the file
			bool loadingWasSuccessful( true );
			for( uint32 row( 0 ); row < dwHeight; ++row )
			{
				if( 0 > TIFFReadScanline(tif_in, &raster[dwWidth*row*dwPlanes], row, 0) )					
				{
					loadingWasSuccessful = false;
					break;
				}
			}
			TIFFClose(tif_in);tif_in=0;

			// write image with resource compiler meta data
			if( false != loadingWasSuccessful )
			{				
				TIFF* tif_out = TIFFOpen(lpszPathName,"w");

				if(tif_out)
				{
					// set typical properties
					TIFFSetField(tif_out, TIFFTAG_IMAGEWIDTH, dwWidth);
					TIFFSetField(tif_out, TIFFTAG_IMAGELENGTH, dwHeight);
					TIFFSetField(tif_out, TIFFTAG_SAMPLESPERPIXEL, dwPlanes);
					TIFFSetField(tif_out, TIFFTAG_BITSPERSAMPLE, dwBitsPerSample ); 
					TIFFSetField(tif_out, TIFFTAG_ROWSPERSTRIP, dwRowsPerStrip); 
					TIFFSetField(tif_out, TIFFTAG_COMPRESSION, dwCompression);
					TIFFSetField(tif_out, TIFFTAG_PHOTOMETRIC, dwPhotometric );
					TIFFSetField(tif_out, TIFFTAG_PLANARCONFIG, dwPlanarConfig );
					TIFFSetField(tif_out, TIFFTAG_ORIENTATION, dwOrientation );

					// set meta data
					{
						CConfigString configString;

						for(unsigned int i=0;;++i)
						{
							const char *szSectionName = pCfgFile->GetSectionName(i);

							if(!szSectionName)
								break;

							configString.SetSection(szSectionName);

							pCfgFile->SetConfig(eCP_PriorityFile,szSectionName,&configString);
						}

						uint32 dwSize; 
						unsigned char *pMemBlock = configString.GetResult(dwSize);

						TIFFSetField(tif_out,TIFFTAG_PHOTOSHOP,dwSize,pMemBlock);		// 34377 IPTC TAG
					}

					// write image
					bool writingWasSucessful( true );
					for( uint32 row( 0 ); row < dwHeight; ++row )
					{
						if( 0 > TIFFWriteScanline(tif_out, &raster[dwWidth*row*dwPlanes], row, 0) )
						{
							writingWasSucessful = false;
							break;
						}						
					}

					_TIFFfree(raster);raster=0;
					
					TIFFClose(tif_out);
					
					bRet = writingWasSucessful;
				}
			}
		}
		if(raster)_TIFFfree(raster);
		if(tif_in)TIFFClose(tif_in);
	}

	return bRet;
}




/*
bool CImageCompiler::LoadUsingDirectX( const char *lpszPathName )
{
	if(!InitDirectX())
		return false;

	assert(m_pCC);		// need this pointer set

	D3DXIMAGE_INFO imageinfo;
	D3DXIMAGE_INFO imageinfo2;

	assert(!m_pInputImage);

	if( FAILED( D3DXGetImageInfoFromFile( lpszPathName, &imageinfo ) ) )
	{
		CCLOG->LogError("D3DXGetImageInfoFromFile failed");
		return FALSE;
	}

	HRESULT hRes;

	switch( imageinfo.ResourceType )
	{
	case D3DRTYPE_TEXTURE:
		{
			LPDIRECT3DTEXTURE9 pNewDXImage=0;

			hRes=D3DXCreateTextureFromFileEx( m_pd3ddev, lpszPathName, 
				imageinfo.Width, imageinfo.Height, imageinfo.MipLevels, 0,
				imageinfo.Format, D3DPOOL_DEFAULT, D3DX_FILTER_NONE, D3DX_FILTER_NONE, 0, 
				&imageinfo2, NULL, (LPDIRECT3DTEXTURE9*)&pNewDXImage );

			if(FAILED(hRes))
			{
				CCLOG->LogError("D3DXCreateTextureFromFileEx failed (power of two?) '%s'",DXGetErrorString9(hRes));
				return false;
			}

			m_pInputImage = new DXImage(pNewDXImage);
		}
		break;

	case D3DRTYPE_VOLUMETEXTURE:
		CCLOG->LogError("ERROR_COULDNTLOADFILE Volumetextures are not supported");
		return false;

	case D3DRTYPE_CUBETEXTURE:
		CCLOG->LogError("ERROR_COULDNTLOADFILE Cubemaps are not supported");
		return false;	

	default:
		CCLOG->LogError("ERROR_COULDNTLOADFILE");
		return false;
	}

	ClearInputImageFlags();

	if(m_pInputImage)
		m_bInputUsesAlpha=m_pInputImage->HasAlpha();

	return true;
}
*/



inline uint32 VEC4ToUINT32( Vec4 &inVec)
{
	int ia = (int)(inVec.w*255.0f + 0.499f);
	int ir = (int)(inVec.x*255.0f + 0.499f);
	int ig = (int)(inVec.y*255.0f + 0.499f);
	int ib = (int)(inVec.z*255.0f + 0.499f);

	if (ir < 0)ir = 0;
	if (ir > 255)ir = 255;
	if (ig < 0)ig = 0;
	if (ig > 255)ig = 255;
	if (ib < 0)ib = 0;
	if (ib > 255)ib = 255;
	if (ia < 0)ia = 0;
	if (ia > 255)ia = 255;

	return (((uint32)ia)<<24) | (((uint32)ir)<<16) | (((uint32)ig)<<8) | (uint32)ib;
}
/*
// Arguments:
//   fStrength - 0=off..1=full
void Sharpen( CSimpleBitmap<Vec4> &InOut, const uint32 dwWidth, const uint32 dwHeight, const float fStrength )
{
	CSimpleBitmap<Vec4> In;
	
	In.Alloc(dwWidth,dwHeight);
	
	for(uint32 dwY=0;dwY<dwHeight;++dwY)
	for(uint32 dwX=0;dwX<dwWidth;++dwX)
	{
		Vec4 vValue;
		InOut.Get(dwX,dwY,vValue);
		In.Set(dwX,dwY,vValue);
	}

	CWeightFilterSet filterset_sharpen;

	filterset_sharpen.CreateSharpen(fStrength);

	for(uint32 dwY=0;dwY<dwHeight;++dwY)
	for(uint32 dwX=0;dwX<dwWidth;++dwX)
	{
		Vec4 vResult(0,0,0,0);

		filterset_sharpen.GetBlockWithFilter(In,dwX,dwY,vResult);

		// needed when using sharpening filter in mip map generation
		vResult.x = clamp_tpl(vResult.x,0.0f,1.0f);
		vResult.y = clamp_tpl(vResult.y,0.0f,1.0f);
		vResult.z = clamp_tpl(vResult.z,0.0f,1.0f);
		vResult.w = clamp_tpl(vResult.w,0.0f,1.0f);

		InOut.Set(dwX,dwY,vResult);
	}
}
*/






ImageObject *CImageCompiler::CreateMipMaps( const ImageObject *pInputImage, const uint32 indwReduceResolution,
	const bool inbRemoveMips, const bool bRenormalize, const float fGamma ) 
{
	assert(fGamma>0);
	assert(pInputImage);

	bool bMaintainAlphaCoverage = m_Props.m_bMaintainAlphaCoverage;
//	float fReferenceAlphaCoverage=-1;			// means not set yet

	float fInvGamma = 1.0f/fGamma;
	uint32 dwWidth,dwHeight,dwDepth,dwSides,dwMips;

	pInputImage->GetExtend(dwWidth,dwHeight,dwDepth,dwSides,dwMips);

	uint32 dwNumNewMips = (uint32)max((int)_CalcMipCount(dwWidth,dwHeight)-(int)indwReduceResolution,1);

	if(inbRemoveMips)
		dwNumNewMips=1;

	uint32 dwW,dwH;

	dwW=max(dwWidth>>indwReduceResolution,(uint32)1);
	dwH=max(dwHeight>>indwReduceResolution,(uint32)1);

	CRAWImage *pRet = new CRAWImage(dwW,dwH,dwNumNewMips,CPixelFormats::GetNoFromD3DFormat(D3DFMT_A8R8G8B8),IsFinalPixelFormatCompressed());

	pRet->CopyPropertiesFrom(*pInputImage);

	CSimpleBitmap<Vec4> Bmp[2];			// double buffering
//	CSimpleBitmap<Vec4> Bmp[3];			// triple buffering to get sharp but correct (always the upper left top area is used)

	// Sometimes this allocation fails, so if we are using multiple threads we may fail. In this case the calling code
	// may try again later.
	pInputImage->CopyToSimpleBitmap(Bmp[0]);

	if(Bmp[0].CalcBitmapSize()==0)
	{
		CCLOG->LogError("CImageCompiler::CreateMipMaps failed, input image cannot be processed (input has to be a TIF file)");
		return 0;
	}

	if(m_Props.m_iColorModel==1)
	{
		Vec4 *pMem = Bmp[0].GetPointer();
		for(uint32 dwY=0;dwY<dwHeight;++dwY)
		for(uint32 dwX=0;dwX<dwWidth;++dwX)
		{
			Vec4 &ref = *pMem++;

//			ref.z += ((rand()%1000)*0.001f-0.5f)/64.0f; 
//			ref.y += ((rand()%1000)*0.001f-0.5f)/64.0f; 
//			ref.z += ((rand()%1000)*0.001f-0.5f)/64.0f; 

			ColorF col = ColorF(ref.x,ref.y,ref.z).RGB2mCIE();

//			col.g=0.5f;
//			ColorF testback = ColorF((int)(col.r*255.0f)/255.0f,(int)(col.g*255.0f)/255.0f,(int)(col.b*255.0f)/255.0f).mCIE2RGB();
			ref.x = col.r;
			ref.y = col.g;
			ref.z = col.b;
		}
	}

	// gamma->linear - could be optimized by table lookup
	if(fGamma!=1.0f)
	{
		Vec4 *pMem = Bmp[0].GetPointer();
		for(uint32 dwY=0;dwY<dwHeight;++dwY)
		for(uint32 dwX=0;dwX<dwWidth;++dwX)
		{
			Vec4 &ref = *pMem++;

			ref.x=powf(ref.x,fGamma);
			ref.y=powf(ref.y,fGamma);
			ref.z=powf(ref.z,fGamma);
		}
	}

	if(dwWidth/2>0 && dwHeight/2>0)
		Bmp[1].Alloc(dwWidth/2,dwHeight/2);

//	if(dwWidth/4>0 && dwHeight/4>0)
//		Bmp[2].Alloc(dwWidth/4,dwHeight/4);

	static CWeightFilterSet filterset_1;		// one mip down
//	static CWeightFilterSet filterset_2;		// two mips down

	static float fCurrentFilter=-1;		// to detect if we need to compute the filterset
	float fRequestedFilter = (float)(m_Props.m_iMipSharpen*0.01f);		// to detect if we need to compute the filterset

	// create downsampling filter
	if(fCurrentFilter!=fRequestedFilter)									// do avoid doing this for every call
	{
		fCurrentFilter = fRequestedFilter;

		CSummedAreaFilterKernel kernel;
	
		//		kernel.CreateFromGauss(256,0.5f,0.23f);		// with sharpening
		//		kernel.CreateFromGauss();									// without sharpening
		float fSqrtCurrentFilter=powf(fCurrentFilter,1.0/8.0);		// to make slider more or less linear
		kernel.CreateFromGauss(256,1.0f-0.5f*fSqrtCurrentFilter,0.23f*fSqrtCurrentFilter);		// with defined sharpening

//		assert(kernel.GetHeight()==256 && kernel.GetWeight()==256);			// some code rely on that

		float fKernelRadius=2.0f;
		m_pCC->config->Get("globalmipblur",fKernelRadius);		// <=2.0f -> 4x4 kernel

		filterset_1.Create(kernel,1.0f*fKernelRadius,false);		// kernel size can/should be adjusted (smallest: 4->1, so bCenter is false)
//		filterset_2.Create(kernel,2.0f*fKernelRadius,false);		// kernel size can/should be adjusted (smallest: 4->1, so bCenter is false)
	}

	uint32 colAverageARGB=0xffffffff;
	bool bAverageColorSet=false;

	int iIteration=0;
	for(int iMip=-(int)indwReduceResolution;iMip<(int)dwNumNewMips;++iMip,++iIteration)
	{
		CSimpleBitmap<Vec4> &rCurrentBmp =  Bmp[iIteration%2];

		if(bMaintainAlphaCoverage)
			MaintainAlphaCoverage(rCurrentBmp,dwWidth,dwHeight);

		float fAlphaOffset = m_Props.ComputeMIPAlphaOffset(iMip+indwReduceResolution,dwNumNewMips+indwReduceResolution);

//		if(m_Props.m_iMipSharpen)
//			Sharpen(rCurrentBmp,dwWidth,dwHeight,(float)(m_Props.m_iMipSharpen*0.01f));

		if(iMip>=0)
		{
			char *pDestMem;
			uint32 dwDestPitch;

			if(pRet->Lock(0,(uint32)iMip,pDestMem,dwDestPitch))
			{
				for(uint32 dwY=0;dwY<dwHeight;++dwY)
				{
					uint32 *pDestLine=(uint32 *)(pDestMem+dwDestPitch*dwY);

					for(uint32 dwX=0;dwX<dwWidth;++dwX)
					{
						Vec4 vValue;

						bool bOk = rCurrentBmp.Get(dwX,dwY,vValue);			assert(bOk);

						// linear->gamma
						if(fGamma!=1.0f)
						{
							vValue.x=powf(vValue.x,fInvGamma);
							vValue.y=powf(vValue.y,fInvGamma);
							vValue.z=powf(vValue.z,fInvGamma);
						}

						if(bRenormalize)
						{
							Vec3 vNormal(vValue.x*2.0f-1.0f,vValue.y*2.0f-1.0f,vValue.z*2.0f-1.0f);

							vNormal.NormalizeSafe();

							vValue.x=vNormal.x*0.5f+0.5f;
							vValue.y=vNormal.y*0.5f+0.5f;
							vValue.z=vNormal.z*0.5f+0.5f;
						}
	
						vValue.w+=fAlphaOffset;

						*pDestLine++=VEC4ToUINT32(vValue);
					}
				}
				
				if(dwHeight==1 && dwWidth==1)
				{
					colAverageARGB=*((uint32 *)pDestMem);
//					CCLOG->Log("CImageCompiler::CreateMipMaps AvgColor(ARGB) = %x",colAverageARGB);
					bAverageColorSet=true;
				}

				pRet->Unlock((uint32)iMip);
			}
			else
			{
				CCLOG->LogError("CImageCompiler::CreateMipMaps Lock failed");
				delete pRet;
				return 0;
			}
		}

		dwWidth/=2;				if(!dwWidth)dwWidth=1;
		dwHeight/=2;			if(!dwHeight)dwHeight=1;

		// calculate down sampled version
//		if(iIteration==0)
		{
			// lookup one mip up
			CSimpleBitmap<Vec4> &rInBmp =  Bmp[iIteration%2];
			CSimpleBitmap<Vec4> &rOutBmp =  Bmp[(iIteration+1)%2];

			for(uint32 dwY=0;dwY<dwHeight;++dwY)
			for(uint32 dwX=0;dwX<dwWidth;++dwX)
			{
				Vec4 vResult(0,0,0,0);

				filterset_1.GetBlockWithFilter(rInBmp,dwX*2+1,dwY*2+1,vResult);

				// needed when using sharpening filter in mip map generation
				vResult.x = clamp_tpl(vResult.x,0.0f,1.0f);
				vResult.y = clamp_tpl(vResult.y,0.0f,1.0f);
				vResult.z = clamp_tpl(vResult.z,0.0f,1.0f);
				vResult.w = clamp_tpl(vResult.w,0.0f,1.0f);

				rOutBmp.Set(dwX,dwY,vResult);
			}
		}
/*		else
		{
			// lookup two mips up
			CSimpleBitmap<Vec4> &rInBmp =  Bmp[(iIteration-1)%3];
			CSimpleBitmap<Vec4> &rOutBmp =  Bmp[(iIteration+1)%3];

			for(uint32 dwY=0;dwY<dwHeight;++dwY)
				for(uint32 dwX=0;dwX<dwWidth;++dwX)
				{
					Vec4 vResult(0,0,0,0);

					filterset_2.GetBlockWithFilter(rInBmp,dwX*4+2,dwY*4+2,vResult);

					// needed when using sharpening filter in mip map generation
					vResult.x = clamp_tpl(vResult.x,0.0f,1.0f);
					vResult.y = clamp_tpl(vResult.y,0.0f,1.0f);
					vResult.z = clamp_tpl(vResult.z,0.0f,1.0f);
					vResult.w = clamp_tpl(vResult.w,0.0f,1.0f);

					rOutBmp.Set(dwX,dwY,vResult);
				}
		}
*/
	}

	if(bAverageColorSet)
		pRet->SetAverageColor(colAverageARGB);

	return pRet;
}


void CImageCompiler::MaintainAlphaCoverage( CSimpleBitmap<Vec4> &inoutBitmap, const uint32 dwWidth, const uint32 dwHeight )
{
	static CWeightFilterSet filterset;
	static bool bValidFilterset=false;

	float fMaintainAlphaCoverage_before = ComputeAlphaCoverage(inoutBitmap,dwWidth,dwHeight,0.5f);

	float fSharpenFactor = 0.170f;	// from experiements - this is a good value
//	for(float fSharpenFactor=0.13f;fSharpenFactor<0.2f;fSharpenFactor+=0.005f)		// test
//	{
//		CSimpleBitmap<Vec4> cpy=inoutBitmap;			// test

	// create downsampling filter
		// remark next line for the test
	if(!bValidFilterset)									// do avoid doing this for every call
	{
		CSummedAreaFilterKernel kernel;
	
//		kernel.CreateFromGauss(256,0.5f,0.23f);		// with sharpening
//		kernel.CreateFromGauss(256,0.5f,0.19f);		// with less sharpening
		kernel.CreateFromGauss(256,0.5f,fSharpenFactor);
//		kernel.CreateFromGauss();									// without sharpening

		filterset.Create(kernel,1.0f,true);		// kernel size can/should be adjusted (smallest: 4->1, so bCenter is false)

//		filterset.Debug("MaintainAlphaCoverage");

		bValidFilterset=true;
	}

	CSimpleBitmap<float> NewAlpha;

	NewAlpha.Alloc(dwWidth,dwHeight);
	
	for(uint32 dwY=0;dwY<dwHeight;++dwY)
	for(uint32 dwX=0;dwX<dwWidth;++dwX)
	{
		Vec4 vResult(0,0,0,0);

		filterset.GetBlockWithFilter(inoutBitmap,dwX,dwY,vResult);

		NewAlpha.Set(dwX,dwY,vResult.w);
	}

	for(uint32 dwY=0;dwY<dwHeight;++dwY)
	for(uint32 dwX=0;dwX<dwWidth;++dwX)
	{
		const uint32 dwSteps=4;											// Sample count is dwSteps*dwSteps
		const float fStep = 1.0f/(float)dwSteps;
		const float fHalfStep = 0.5f*fStep;

		float fSumValue=0.0f;

		for(float fY=-0.5f+fHalfStep;fY<0.5f;fY+=fStep)
		for(float fX=-0.5f+fHalfStep;fX<0.5f;fX+=fStep)
		{
			float fValue=0.0f;

			NewAlpha.GetFiltered((float)dwX+fX,(float)dwY+fY,fValue);

			if(fValue>0.5f)
				fSumValue += 1.0f/(float)(dwSteps*dwSteps);
		}

		Vec4 vResult;

		inoutBitmap.Get(dwX,dwY,vResult);

		vResult.w=fSumValue;

		inoutBitmap.Set(dwX,dwY,vResult);
	}
	

	{
		char str[256];

		sprintf(str,"AlphaCoverage %dx%d fSharpenFactor=%.4f   %.4f -> %.4f\n",dwWidth,dwHeight,
			fSharpenFactor,fMaintainAlphaCoverage_before,ComputeAlphaCoverage(inoutBitmap,dwWidth,dwHeight,0.5f));
		OutputDebugString(str);
	}

//	inoutBitmap = cpy;			// test
//	}			// test

	OutputDebugString("\n");
}

/*
void CImageCompiler::MaintainAlphaCoverage( CSimpleBitmap<Vec4> &inoutBitmap, float &fReferenceAlphaCoverage )
{
	if(fReferenceAlphaCoverage==-1)
	{
//		fReferenceAlphaCoverage = ComputeAlphaCoverage(inoutBitmap,0.5f);		return;
		fReferenceAlphaCoverage = ComputeWeightedAlphaCoverage(inoutBitmap);
	}

	float fMin=0.0f, fMax=1.0f;

	float fCurrentAlphaCoverage=-1;

	for(uint32 dwIter=0;dwIter<8;++dwIter)
	{
		float fMid = 0.5f*(fMin+fMax);
		float fTestAlphaCoverage = ComputeAlphaCoverage(inoutBitmap,fMid);

		if(fCurrentAlphaCoverage==-1)
			fCurrentAlphaCoverage=fTestAlphaCoverage;

		if(fTestAlphaCoverage<fReferenceAlphaCoverage)
			fMax=fMid;
		 else
			fMin=fMid;
	}

	uint32 dwWidth=inoutBitmap.GetWidth(), dwHeight=inoutBitmap.GetHeight();

//	float fMul = fCurrentAlphaCoverage (0.5f / (0.5f*(fMin+fMax)));
	float fMul = 1.0f / (fMin+fMax);

	for(uint32 dwY=0;dwY<dwHeight;++dwY)
	for(uint32 dwX=0;dwX<dwWidth;++dwX)
	{
		Vec4 &Value = inoutBitmap.GetRef(dwX,dwY);
		
		Value.w = min(1.0f,Value.w*fMul);
	}

	// test
	float fDebug = ComputeAlphaCoverage(inoutBitmap,0.5f);
}
*/

float CImageCompiler::ComputeAlphaCoverage( const CSimpleBitmap<Vec4> &inBitmap, const uint32 dwWidth, const uint32 dwHeight, const float fRefAlpha )
{
	uint32 dwTrueCount=0;

	float fMul = 0.5f/fRefAlpha;

	const uint32 dwSteps=4;											// Sample count is dwSteps*dwSteps

	for(uint32 dwY=0;dwY<dwHeight;++dwY)
	for(uint32 dwX=0;dwX<dwWidth;++dwX)
	{
		const float fStep = 1.0f/(float)dwSteps;
		const float fHalfStep = 0.5f*fStep;

//		float fSumValue=0.0f;

		for(float fY=-0.5f+fHalfStep;fY<0.5f;fY+=fStep)
		for(float fX=-0.5f+fHalfStep;fX<0.5f;fX+=fStep)
		{
			Vec4 Value;
			
			inBitmap.GetFiltered(dwX+fX,dwY+fY,Value);

//			fSumValue+=Value.w;
			if(Value.w>fRefAlpha)
				++dwTrueCount;
		}

//		if(min(fSumValue*fMul,1.0f)>0.5f)
//			dwTrueCount++;
	}

	return (float)dwTrueCount/(dwSteps*dwSteps)/(float)(dwWidth*dwHeight);
}





uint32 CImageCompiler::_CalcMipCount( const uint32 indwWidth, const uint32 indwHeight, const bool bDXT )
{
	LONG lwTempH,lwTempW,lwPowsW,lwPowsH;

	lwTempW = indwWidth;
	lwTempH = indwHeight;

	if(bDXT)
	{
		lwTempW/=4;
		lwTempH/=4;
	}

	lwPowsW = 0;
	lwPowsH = 0;
	while (lwTempW > 0)
	{
		lwPowsW++;
		lwTempW = lwTempW / 2;
	}
	while (lwTempH > 0)
	{
		lwPowsH++;
		lwTempH = lwTempH / 2;
	}
	return lwPowsW > lwPowsH ? lwPowsW : lwPowsH;
}



// needed for the NVidia library
void WriteDTXnFile( DWORD count, void * buffer, void * userData )
{
	assert(0);
}


static char *g_DDSMemoryFilePtr=0;			// needed because NVidia lib ReadDTXnFile userData cannot be passed

// needed for the NVidia library
void ReadDTXnFile( DWORD count, void * buffer, void * userData )
{
	memcpy(buffer,g_DDSMemoryFilePtr,count);

	g_DDSMemoryFilePtr+=count;
}


struct SLoadAllMipSurfaces
{
	ImageObject *		m_pThis;
	uint32					m_dwMip;
};


// callback for the NVidia library
HRESULT LoadAllMipSurfaces( void * data, int iLevel, DWORD size, int Width, int Height, void * user )
{
	SLoadAllMipSurfaces *pUser = (SLoadAllMipSurfaces *)user;

	char *pMem;
	uint32 dwPitch;

	pUser->m_pThis->Lock(0,pUser->m_dwMip,pMem,dwPitch);

	assert(size = dwPitch*Height);

	memcpy(pMem,data,size);

	pUser->m_pThis->Unlock(pUser->m_dwMip);

	return 0;	// OK
}


static TextureFormats FindCorespondingNVidiaFormat( D3DFORMAT fmtTo )
{
	switch(fmtTo)
	{
//		case D3DFMT_DXT1: return kDXT1a;	// DXT1 with one bit alpha
		case D3DFMT_DXT1: return kDXT1;	// DXT1 with one bit alpha
    case D3DFMT_DXT3: return kDXT3;   // explicit alpha
    case D3DFMT_DXT5: return kDXT5;   // interpolated alpha
    case D3DFMT_A4R4G4B4: return k4444;   // a4 r4 g4 b4
    case D3DFMT_A1R5G5B5: return k1555;   // a1 r5 g5 b5
    case D3DFMT_R5G6B5: return k565;    // a0 r5 g6 b5
    case D3DFMT_A8R8G8B8: return k8888;   // a8 r8 g8 b8
    case D3DFMT_X8R8G8B8: return k8888;   // a8 r8 g8 b8
    case D3DFMT_R8G8B8: return k888;    // a0 r8 g8 b8
    case D3DFMT_X1R5G5B5: return k555;    // a0 r5 g5 b5
    case D3DFMT_P8: return k8;   // paletted
    case D3DFMT_V8U8: return kV8U8;   // DuDv 
//    case D3DFMT_: return kCxV8U8;   // normal map
    case D3DFMT_A8: return kA8;            // alpha only
//    case D3DFMT_: return k4;            // 16 bit color      
//    case D3DFMT_: return kQ8W8V8U8;
    case D3DFMT_A8L8: return kA8L8;
    case D3DFMT_R32F: return kR32F;
    case D3DFMT_A32B32G32R32F: return kA32B32G32R32F;
    case D3DFMT_A16B16G16R16F: return kA16B16G16R16F;
    case D3DFMT_L8: return kL8;       // luminance
	}

	return kTextureFormatLast;			// output pixel format not supported yet
}


ImageObject *CImageCompiler::ConvertFormat( ImageObject *inSrc, D3DFORMAT fmtTo )
{
	assert(inSrc);

	ImageObject *pRet = ConvertFormatWithSpecifiedCompressor(inSrc,fmtTo);

	if(!pRet)
		return pRet;

	if(pRet->GetPixelFormat()==CPixelFormats::GetNoFromD3DFormat(D3DFMT_L8))
		pRet->SetImageFlags( pRet->GetImageFlags() | CImageExtensionHelper::EIF_Greyscale );

	return pRet;
}


CRAWImage *CImageCompiler::CopyToNewCRAWImage( ImageObject *inSrc )
{
	assert(inSrc);

	uint32 dwWidth,dwHeight,dwDepth,dwSides,dwMips;

	inSrc->GetExtend(dwWidth,dwHeight,dwDepth,dwSides,dwMips);

	if(dwSides!=1)
		return 0;				// currently we don't support cubemaps

	if(dwDepth!=1)
		return 0;				// currently we don't support volume textures
	
	uint32 dwInPlanes=0;

	TPixelFormatID srcPixelformat=inSrc->GetPixelFormat();

	D3DFORMAT d3dSrcFormat = CPixelFormats::GetPixelFormatInfo(srcPixelformat)->DxtNo;

	CRAWImage *pRet = new CRAWImage(dwWidth,dwHeight,dwMips,CPixelFormats::GetNoFromD3DFormat(d3dSrcFormat),IsFinalPixelFormatCompressed());

	pRet->CopyPropertiesFrom(*inSrc);

	for(uint32 dwMip=0;dwMip<dwMips;++dwMip)
	{
		uint32 dwLocalWidth = inSrc->GetWidth(dwMip);			// we get error on NVidia with this (assumes input is 4x4 as well)
		uint32 dwLocalHeight = inSrc->GetHeight(dwMip);

		uint32 dwLines = dwLocalHeight;

		if(CPixelFormats::GetPixelFormatInfo(srcPixelformat)->bCompressed)
			dwLines = (dwLocalHeight+3)/4;

		char *pMem=0;
		uint32 dwPitch=0;

		if(inSrc->Lock(0,dwMip,pMem,dwPitch))
		{
			char *pDstMem;
			uint32 dwDstPitch;

			if(pRet->Lock(0,dwMip,pDstMem,dwDstPitch))
			{
				for(uint32 dwY=0;dwY<dwLines;++dwY)
					memcpy(&pDstMem[dwDstPitch*dwY],&pMem[dwPitch*dwY],min(dwPitch,dwDstPitch));

				pRet->Unlock(dwMip);
			}
			else assert(0);

			inSrc->Unlock(dwMip);
		}
		else assert(0);
	}
	
	// recursive for all attached images
	if(ImageObject *pAttached = inSrc->GetAttachedImage())
	{
		CRAWImage *pAttachedRAW = CopyToNewCRAWImage(pAttached);

		pRet->SetAttachedImage(pAttachedRAW);
	}


	return pRet;
}


ImageObject *CImageCompiler::ConvertFormatWithSpecifiedCompressor( ImageObject *inSrc, D3DFORMAT fmtTo )
{
	assert(inSrc);

	uint32 dwWidth,dwHeight,dwDepth,dwSides,dwMips;

	inSrc->GetExtend(dwWidth,dwHeight,dwDepth,dwSides,dwMips);

	if(dwSides!=1)
		return 0;				// currently we don't support cubemaps

	if(dwDepth!=1)
		return 0;				// currently we don't support volume textures

	if(fmtTo==D3DFMT_3DC)
	if(InputUsesDenormalizedNormals())			// 3DC only if we have normalized normals
		fmtTo=D3DFMT_X8R8G8B8;

	// Simple can only handle DXT5
	if(m_Props.m_eGlobalCompressor==CImageProperties::eCGSimple)
	if(fmtTo==D3DFMT_DXT5)
		return ConvertFormatWithSimpleCompressor(inSrc,fmtTo);

	// 3DC can only be processed by ATI library
	if(inSrc->GetPixelFormat()==CPixelFormats::GetNoFromD3DFormat(D3DFMT_3DC) || fmtTo==D3DFMT_3DC)
		return ConvertFormatWithATI(inSrc,fmtTo);
/*
	// try ATI
	if(m_Props.m_eGlobalCompressor>=CImageProperties::eCGATI)
	{
		ImageObject *pRet=ConvertFormatWithATI(inSrc,fmtTo);

		if(pRet)
			return pRet;
	}
*/
	// try NVidia
	if(m_Props.m_eGlobalCompressor>=CImageProperties::eCGNVDXT)
	{
//		assert(!inSrc->GetDXTex());

		uint32 dwInPlanes=0;
		bool bCompressOrDecompress=true;		// true=Compress, false=Decompress

		TPixelFormatID srcPixelformat=inSrc->GetPixelFormat();

		D3DFORMAT d3dSrcFormat = CPixelFormats::GetPixelFormatInfo(srcPixelformat)->DxtNo;

		switch(d3dSrcFormat)
		{
			case D3DFMT_R8G8B8:
				dwInPlanes=3;break;

			case D3DFMT_A8R8G8B8:
			case D3DFMT_X8R8G8B8:
				{
					if(fmtTo==D3DFMT_A8R8G8B8 || fmtTo==D3DFMT_X8R8G8B8)
						return inSrc;		// no need to do anything

					dwInPlanes=4;
				}
				break;

			default:
				bCompressOrDecompress=false;
		}

		CompressionOptions options;

		options.TextureFormat = FindCorespondingNVidiaFormat(fmtTo);

		if(options.TextureFormat==kTextureFormatLast)
			return 0;			// output pixel format not supported yet

		ImageObject *pRet = new CRAWImage(dwWidth,dwHeight,dwMips,CPixelFormats::GetNoFromD3DFormat(fmtTo),IsFinalPixelFormatCompressed());

		pRet->CopyPropertiesFrom(*inSrc);

		if(bCompressOrDecompress)
		{
			SLoadAllMipSurfaces UserData;

			UserData.m_pThis=pRet;

			options.user_data=&UserData;
			options.MipMapType=dNoMipMaps;
//			options.bQuickCompress=true;		// 20sec -> 5sec (similar to DirectX compressor speed) but very ugly results

			for(uint32 dwMip=0;dwMip<dwMips;++dwMip)
			{
				uint32 dwLocalWidth = inSrc->GetWidth(dwMip);			// we get error on NVidia with this (assumes input is 4x4 as well)
				uint32 dwLocalHeight = inSrc->GetHeight(dwMip);

				char *pMem=0;
				uint32 dwPitch=0;

				HRESULT hRes=-1;		// failed

				if(inSrc->Lock(0,dwMip,pMem,dwPitch))
				{
					UserData.m_dwMip=dwMip;

					hRes = nvDXTcompressBGRA((unsigned char *)pMem,dwLocalWidth,dwLocalHeight,dwPitch,&options,dwInPlanes,LoadAllMipSurfaces);

					inSrc->Unlock(dwMip);
				}

				if(FAILED(hRes))		// Lock or NVidia compression failed
				{
					delete pRet;
					return 0;
				}
			}
		}
		else
		{
			uint32 dwWishOutPlanes=0;

			switch(fmtTo)
			{
				case D3DFMT_R8G8B8:
					dwWishOutPlanes=3;break;

				case D3DFMT_A8R8G8B8:
				case D3DFMT_X8R8G8B8:
					dwWishOutPlanes=4;break;
			}

			for(uint32 dwMip=0;dwMip<dwMips;++dwMip)
			{
				uint32 dwLocalWidth = pRet->GetWidth(dwMip);
				uint32 dwLocalHeight = pRet->GetHeight(dwMip);

				uint32 dwLines = dwLocalHeight;

				if(CPixelFormats::GetPixelFormatInfo(srcPixelformat)->bCompressed)
					dwLines = (dwLocalHeight+3)/4;

				char *pSrcMem;
				uint32 dwSrcPitch;

				inSrc->Lock(0,dwMip,pSrcMem,dwSrcPitch);

				int src_format=FindCorespondingNVidiaFormat(CPixelFormats::GetPixelFormatInfo(srcPixelformat)->DxtNo);

				char *pDDSMemoryFile = new char[sizeof(uint32)+sizeof(DDS_HEADER)+dwSrcPitch*dwLines];	// magic,header,data
				
				// magic
				*(DWORD *)pDDSMemoryFile = MAKEFOURCC('D','D','S',' ');
				// header
				DDS_HEADER *ddsh = (DDS_HEADER *)&pDDSMemoryFile[sizeof(uint32)];
				memset(ddsh,0,sizeof(DDS_HEADER));
				ddsh->dwSize = sizeof(DDS_HEADER);
				ddsh->dwWidth = dwLocalWidth;
				ddsh->dwHeight = dwLocalHeight;
				ddsh->dwHeaderFlags = DDSD_CAPS | DDSD_PIXELFORMAT | DDSD_WIDTH | DDSD_HEIGHT | DDSD_LINEARSIZE;
				ddsh->dwPitchOrLinearSize = dwSrcPitch*dwLines;
				if(CPixelFormats::GetPixelFormatInfo(srcPixelformat)->bCompressed)
				{
					ddsh->ddspf.dwFlags = DDS_FOURCC;
					ddsh->ddspf.dwFourCC = CPixelFormats::GetPixelFormatInfo(srcPixelformat)->DxtNo;
				}
				else ddsh->ddspf.dwFlags = DDPF_RGB;

				if(strcmp(CPixelFormats::GetPixelFormatInfo(srcPixelformat)->szAlpha,"0")!=0)		// has alpha
					ddsh->ddspf.dwFlags |= DDPF_ALPHAPIXELS;

				ddsh->ddspf.dwSize = sizeof(ddsh->ddspf);
				ddsh->dwSurfaceFlags = DDS_SURFACE_FLAGS_TEXTURE;
				// data
				memcpy(&pDDSMemoryFile[sizeof(uint32)+sizeof(DDS_HEADER)],pSrcMem,dwSrcPitch*dwLines);

				inSrc->Unlock(dwMip);

				g_DDSMemoryFilePtr = pDDSMemoryFile;		// to pass the ptr to ReadDTXnFile()

				uint32 dwOutPlanes = dwWishOutPlanes;

				// nvDXTdecompress calls ReadDTXnFile to get the data
				unsigned char *pUncompressed = nvDXTdecompress((int &)dwLocalWidth,(int &)dwLocalHeight,(int &)dwOutPlanes,(int &)dwLocalWidth,(int &)dwSrcPitch,src_format);

				delete [] pDDSMemoryFile;pDDSMemoryFile=0;

				char *pDstMem;
				uint32 dwDstPitch;

				pRet->Lock(0,dwMip,pDstMem,dwDstPitch);

				if(pUncompressed)
				{
					if(dwOutPlanes==dwWishOutPlanes)
					{
						assert(dwWishOutPlanes==4);
						assert(dwOutPlanes==4);

						unsigned char *Dst=(unsigned char *)pDstMem, *Src=pUncompressed;

						for(uint32 dwY=0;dwY<dwLocalHeight;++dwY)
						for(uint32 dwX=0;dwX<dwLocalWidth;++dwX)
						{
							*Dst++=Src[2];
							*Dst++=Src[1];
							*Dst++=Src[0];
							*Dst++=Src[3];
							Src+=4;
						}
					}
					else
					{
						assert(dwWishOutPlanes==4);
						assert(dwOutPlanes==3);

						unsigned char *Dst=(unsigned char *)pDstMem, *Src=pUncompressed;

						for(uint32 dwY=0;dwY<dwLocalHeight;++dwY)
						for(uint32 dwX=0;dwX<dwLocalWidth;++dwX)
						{
							*Dst++=*Src++;
							*Dst++=*Src++;
							*Dst++=*Src++;
							*Dst++=0;
						}
					}
				}

				pRet->Unlock(dwMip);

				if(!pUncompressed)		// NVidia compression failed
				{
					delete pRet;
					pRet=0;
					CCLOG->LogError("Error NVidia nvDXTdecompress failed");
					break;
//					return 0;
				}

				delete [] pUncompressed;
			}
		}

		if(pRet)
			return pRet;
	}

	// try directX
	{
//		assert(inSrc->GetDXTex());

		ImageObject *pRet=0;

		if(ChangeFormatWithDirectX(inSrc,fmtTo,pRet,0)==S_OK)
			return pRet;
	}

	return 0;
}



class CAutoReleaseIUnknown
{
public:
	CAutoReleaseIUnknown( IUnknown *pUnk=0 ) :m_pUnk(pUnk)
	{
	}

	~CAutoReleaseIUnknown()
	{
		if(m_pUnk)
			m_pUnk->Release();
	}

	void Attach( IUnknown *pUnk )
	{
		m_pUnk=pUnk;
	}

private: // -------------------------------------------

	IUnknown *				m_pUnk;			//
};


HRESULT CImageCompiler::ChangeFormatWithDirectX( ImageObject *pInputImage, D3DFORMAT fmtTo, ImageObject *&pNewImage,
	const uint32 indwReduceResolution )
{
	assert(!pNewImage);
	assert(pInputImage);
	assert(pNewImage!=pInputImage);

	if(!InitDirectX())
		return false;

	HRESULT hr;
	LPDIRECT3DTEXTURE9 pmiptexNew;

	LPDIRECT3DBASETEXTURE9 ptexCur=pInputImage->GetDXTex();

	uint32 dwWidth,dwHeight,dwDepth,dwSides,dwMips;

	pInputImage->GetExtend(dwWidth,dwHeight,dwDepth,dwSides,dwMips);

	CAutoReleaseIUnknown AutoReleaseUnk;		// calls Release() on it's attached pointer when if goes out of focus

	D3DFORMAT fmtFrom=CPixelFormats::GetPixelFormatInfo(pInputImage->GetPixelFormat())->DxtNo;

	if(!ptexCur)
	{
		LPDIRECT3DTEXTURE9 p2DTexture=0;

		hr = m_pd3ddev->CreateTexture(dwWidth,dwHeight, dwMips, 0, fmtFrom, D3DPOOL_MANAGED, &p2DTexture, NULL);
		if (FAILED(hr))
		{
			char *szFmtName = GetNameFromD3DFormat(fmtTo);
			CCLOG->LogError("CreateTexture failed fmt=%s (texture format not supported by driver?)",szFmtName);
			return hr;
		}

		ptexCur = p2DTexture;

		AutoReleaseUnk.Attach(ptexCur);		// calls Release() on it's attached pointer when if goes out of focus
		
		for(uint32 dwMip=0;dwMip<dwMips;++dwMip)
		{
			IDirect3DSurface9 *pSurface=0;

			hr = p2DTexture->GetSurfaceLevel(dwMip,&pSurface);
			if (FAILED(hr))
			{
				char *szFmtName = GetNameFromD3DFormat(fmtTo);
				CCLOG->LogError("GetSurfaceLevel failed fmt=%s (texture format not supported by driver?)",szFmtName);
				return hr;
			}

			char *pMem;
			uint32 dwPitch;

			if(pInputImage->Lock(0,dwMip,pMem,dwPitch))
			{
				RECT rectSrc;

				rectSrc.left=0;
				rectSrc.top=0;
				rectSrc.right=pInputImage->GetWidth(dwMip);
				rectSrc.bottom=pInputImage->GetHeight(dwMip);
				hr = D3DXLoadSurfaceFromMemory(pSurface,0,0,pMem,fmtFrom,dwPitch,0,&rectSrc,D3DX_FILTER_NONE,0);

				if (FAILED(hr))
				{
					CCLOG->LogError("'%s' DX ERROR: %s", "2D Texure, D3DXLoadSurfaceFromMemory()",DXGetErrorString9A(hr));
					return hr;
				}

				pInputImage->Unlock(0);
			}
			else
			{
				pSurface->Release();
				CCLOG->LogError("pInputImage->Lock failed");
				return S_FALSE;
			}

			pSurface->Release();
		}
	}



	if (fmtFrom == D3DFMT_DXT2 || fmtFrom == D3DFMT_DXT4)
	{
		if (fmtTo == D3DFMT_DXT1)
		{
			CCLOG->LogError("ERROR_PREMULTTODXT1");
		}
		else if (fmtTo != D3DFMT_DXT2 && fmtTo != D3DFMT_DXT4)
		{
			CCLOG->LogError("ERROR_PREMULTALPHA");
			return E_FAIL;
		}
	}


/*		if(fmtTo==D3DFMT_P8)
	{
		pNewImage = new P8Image(dwWidth, dwHeight, dwMips);
	}
	else
*/
	{
		if ((fmtTo == D3DFMT_DXT1 || fmtTo == D3DFMT_DXT2 ||
			fmtTo == D3DFMT_DXT3 || fmtTo == D3DFMT_DXT4 ||
			fmtTo == D3DFMT_DXT5) && (dwWidth % 4 != 0 || dwHeight % 4 != 0))
		{
			CCLOG->LogError("ERROR_NEEDMULTOF4 = for DXT compression we need width and height to be multiple of 4");
			return E_FAIL;
		}

		hr = m_pd3ddev->CreateTexture(dwWidth,dwHeight, dwMips, 0, fmtTo, D3DPOOL_MANAGED, &pmiptexNew, NULL);
		if (FAILED(hr))
		{
			char *szFmtName = GetNameFromD3DFormat(fmtTo);
			CCLOG->LogError("GetNameFromD3DFormat failed fmt=%s (texture format not supported by driver?)",szFmtName);
			return hr;
		}
		pNewImage = new DXImage(pmiptexNew);

		pNewImage->CopyPropertiesFrom(*pInputImage);
	}
	if (FAILED(BltAllLevels(pInputImage,D3DCUBEMAP_FACE_FORCE_DWORD, ptexCur, pNewImage,indwReduceResolution, fmtTo)))
		return hr;

	return S_OK;
}



HRESULT CImageCompiler::BltAllLevels( ImageObject *pInputImage, D3DCUBEMAP_FACES FaceType, LPDIRECT3DBASETEXTURE9 ptexSrc,
	ImageObject *pNewImage, const uint32 indwReduceResolution, D3DFORMAT fmtTo )
{
	uint32 dwReduceRes=indwReduceResolution;
//	uint32 dwMips=_CalcMipCount(m_dwOrigWidth,m_dwOrigHeight)-dwReduceRes;
	uint32 dwMips=ptexSrc->GetLevelCount();
	
	for (uint32 iLevel = 0; iLevel < dwMips; iLevel++)
	{
		LPDIRECT3DTEXTURE9 pmiptexSrc = (LPDIRECT3DTEXTURE9)ptexSrc;
		LPDIRECT3DSURFACE9 psurfSrc = NULL;
		DXERRORCHECK("GetSurfaceLevel",pmiptexSrc->GetSurfaceLevel(iLevel+indwReduceResolution, &psurfSrc));

		{ 
			HRESULT _hr = pNewImage->UpdateSurface(psurfSrc, iLevel,D3DX_FILTER_NONE, FaceType, m_pd3ddev); 
			if(FAILED(_hr))
				CCLOG->LogError("'%s' DX ERROR: %s", "2D Texure, GetSurfaceLevel()",DXGetErrorString9A(_hr));
		}

		ReleasePpo(&psurfSrc);
	};
	
	return S_OK;
}

void CImageCompiler::Release()
{
	delete this;
}





void CImageCompiler::SetCC( ConvertContext *pCC )
{
	m_pCC=pCC;
}


size_t GetFileSize( const char *szFilePath )
{
	FILE *in=fopen(szFilePath,"rb");

	if(!in)
		return 0;

	//measure the file size
	fseek(in,0,SEEK_END);

	size_t RetSize=ftell(in);

	fseek(in,0,SEEK_SET);
	fclose(in);

	return RetSize;
}

//#include <crtdbg.h>		// to find memory leaks

// Process file. Return error code or 0 if successful
bool CImageCompiler::Process( ConvertContext &cc )
{
//	_CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );		// to find memory leaks

	m_Props.SetToDefault();

	SetCC(&cc);

	if (!m_bInitialized)
		Init( cc.pRC->GetEmptyWindow() );

	// highest to lowest priority:
	//
	// ranger_statue._rc  rc.ini  commandline
	CString sourceFile = cc.getSourcePath();

	bool bSuccess=true;

	if(LoadInput(sourceFile,cc.sourceFileFinalExtension))
	{
		if(!LoadConfigFile(eCP_PriorityHighest))
			return false;			// error
		
		SetPresetSettings();

		if(m_Props.m_bUserDialog)
		{
			CImageUserDialog dialog;
			
			dialog.DoModal(this);
		}
		else bSuccess=RunWithProperties(true);			// run and save

		if(bSuccess)
			m_pCC->pRC->AddFileStats(m_pCC, sourceFile,GetFileSize(sourceFile),GetFileSize(m_pCC->getOutputPath()),m_Props.m_sPreset,GetDestInfoString());
	}
	else
	{
		CCLOG->LogError("CImageCompiler::Process LoadInput '%s' Ext:'%s' failed",(const char*)sourceFile,(const char*)cc.sourceFileFinalExtension );
		bSuccess=false;
	}

	SetCC(0);
	FreeTexture();

/*	static int iCounter=0;			++iCounter;
	if(iCounter==16)
	{
		_CrtDumpMemoryLeaks();		// to find memory leaks
		iCounter=0;
	}
*/
	return bSuccess;		// success
}


bool CImageCompiler::GetOutputFile( ConvertContext& cc )
{
	//specify output path
	cc.outputFile = Path::ReplaceExtension( cc.sourceFileFinal,"dds" );
	return true;
}


bool CImageCompiler::ProceedToNextPass( ImageObject * &pCurrent, ImageObject *pNew )
{
	if(pCurrent!=m_pInputImage && pCurrent!=pNew)			// never release the input image, don't delete the same
		delete pCurrent;

	pCurrent=pNew;

	return pCurrent!=0;
}


// access to an float array
class CInputProxy_Bump2Normal
{
public:
	// constructor
	CInputProxy_Bump2Normal( float *pMem, const size_t nPitchInBytes, const uint32 dwWidth, const uint32 dwHeight )
		:m_pMem(pMem), m_nPitchInBytes(nPitchInBytes), m_dwWidth(dwWidth), m_dwHeight(dwHeight)
	{
		assert(pMem);
		assert(dwWidth);
		assert(dwHeight);
		assert(m_nPitchInBytes);
	}

	// needed to be a valid proxy
	inline bool Get( const uint32 dwX, const uint32 dwY, float &out ) const
	{
		uint32 x = dwX%m_dwWidth, y = dwY%m_dwHeight;

		char *pMem = (char *)m_pMem;

		out = ((float*)(pMem+y*m_nPitchInBytes)) [x];

		return true;
	}

	// needed to be a valid proxy
	uint32 GetWidth() const
	{
		return m_dwWidth;
	}
	
	// needed to be a valid proxy
	uint32 GetHeight() const
	{
		return m_dwHeight;
	}

private: // ----------------------------------------------------------

	float *			m_pMem;								//
	size_t			m_nPitchInBytes;			//
	uint32			m_dwWidth;						//
	uint32			m_dwHeight;						//
};


/*
bool CImageCompiler::ConvertToFloat4( ImageObject * &pInput )
{
	if(pInput->GetSimpleBitmap())
		return true;									// was already the requested type

	if(pInput->GetPixelFormat()!=CPixelFormats::GetNoFromD3DFormat(D3DFMT_A32B32G32R32F))
	{
		ImageObject *pRes=ConvertFormat(pInput,D3DFMT_A32B32G32R32F);

		if(!pRes)
			return false;

		if(!ProceedToNextPass(pInput,pRes))
			return false;
	}

	uint32 dwWidth,dwHeight,dwDepth,dwSides,dwMips;

	pInput->GetExtend(dwWidth,dwHeight,dwDepth,dwSides,dwMips);

	if(dwDepth!=1)		// volume textures are not handled
	{
		CCLOG->LogError("CImageCompiler::ConvertToFloat4 failed (volume textures are not supported)");
		return false;
	}

	if(dwSides!=1)		// cubemaps are not handled
	{
		CCLOG->LogError("CImageCompiler::ConvertToFloat4 failed (Cubemaps are not supported)");
		return false;
	}
	
	{
		ImageObject *pRes=new CFloat4Image(dwWidth,dwHeight);

		pRes->CopyPropertiesFrom(*pInput);

		char *pInputMem;
		uint32 dwInputPitch;

		if(!pInput->Lock(0,0,pInputMem,dwInputPitch))
		{
			CCLOG->LogError("CImageCompiler::Bump2Normal LockRect1 failed");
			return false;
		}

		CSimpleBitmap<Vec4> *pOutputBitmap = pRes->GetSimpleBitmap();			assert(pOutputBitmap);

		// convert to float4
		for(uint32 dwY=0;dwY<dwHeight;++dwY)
		{
			float *src = (float *)&pInputMem[dwY*dwInputPitch];

			for(uint32 dwX=0;dwX<dwWidth;++dwX,src+=4)
				pOutputBitmap->Set(dwX,dwY,Vec4(src[0],src[1],src[2],src[3]));
		}

		pInput->Unlock(0);

		if(!ProceedToNextPass(pInput,pRes))
			return false;
	}

	return true;
}
*/


ImageObject *CImageCompiler::MinAlpha( ImageObject &rFlatARGB, const uint32 dwMinAlpha  ) const
{
	assert(dwMinAlpha>0);
	assert(dwMinAlpha<=255);

	uint32 dwWidth,dwHeight,dwDepth,dwSides,dwMips;

	rFlatARGB.GetExtend(dwWidth,dwHeight,dwDepth,dwSides,dwMips);

	assert(dwMips==1);

	ImageObject *pRet = new CRAWImage(dwWidth,dwHeight,dwMips,CPixelFormats::GetNoFromD3DFormat(D3DFMT_A8R8G8B8),IsFinalPixelFormatCompressed());

	pRet->CopyPropertiesFrom(rFlatARGB);

	char *pInputMem;
	uint32 dwInputPitch;

	if(!rFlatARGB.Lock(0,0,pInputMem,dwInputPitch))
	{
		CCLOG->LogError("CImageCompiler::MinAlpha Lock1 failed");
		assert(0);
		return 0;
	}

	char *pOutputMem;
	uint32 dwOutputPitch;

	if(!pRet->Lock(0,0,pOutputMem,dwOutputPitch))
	{
		rFlatARGB.Unlock(0);
		CCLOG->LogError("CImageCompiler::MinAlpha Lock2 failed");
		assert(0);
		return 0;
	}


	for(uint32 dwY=0;dwY<dwHeight;++dwY)
	{
		uint32 *src = (uint32 *)&pInputMem[dwY*dwInputPitch];
		uint32 *dst = (uint32 *)&pOutputMem[dwY*dwOutputPitch];

		for(uint32 dwX=0;dwX<dwWidth;++dwX)
		{
			uint32 dwARGB = *src++;
			uint8 dwA = dwARGB >> 24;

			if(dwA<dwMinAlpha)
				dwA=dwMinAlpha;
		
			*dst++ = (dwARGB&0xffffff) | (dwA << 24);
		}
	}

	pRet->Unlock(0);
	rFlatARGB.Unlock(0);

	return pRet;
}


ImageObject *CImageCompiler::Bump2Normal( const CBumpProperties &rProperties, ImageObject *pFlatFloatARGB,
	const bool bApplyRangeAdjustment, const bool bValueFromAlpha )
{
//	const bool bSetAlphaChannel = rProperties.m_iBumpToNormalFilter==2;

	uint32 dwWidth,dwHeight,dwDepth,dwSides,dwMips;

	pFlatFloatARGB->GetExtend(dwWidth,dwHeight,dwDepth,dwSides,dwMips);

	if(dwDepth!=1)		// volume textures are not handled
	{
		CCLOG->LogError("CImageCompiler::Bump2Normal failed (volume textures are not supported)");
		return 0;
	}

	if(dwSides!=1)		// cubemaps are not handled
	{
		CCLOG->LogError("CImageCompiler::Bump2Normal failed (Cubemaps are not supported)");
		return 0;
	}

	CSimpleBitmap<float>	IntermediateImage[2];		// 0=input, 1=blurred input

	if(!IntermediateImage[0].Alloc(dwWidth,dwHeight) || !IntermediateImage[1].Alloc(dwWidth,dwHeight))
	{
		CCLOG->LogError("CImageCompiler::Bump2Normal failed (not enough memory for IntermediateImage)");		
		return 0;
	}


	ImageObject *pRet=new CFloat4Image(dwWidth,dwHeight);

	if(!pRet)
	{
		CCLOG->LogError("CImageCompiler::Bump2Normal failed (not enough memory for CFloat4Image)");
		return 0;
	}

	pRet->CopyPropertiesFrom(*pFlatFloatARGB);

	char *pInputMem;
	uint32 dwInputPitch;

	if(!pFlatFloatARGB->Lock(0,0,pInputMem,dwInputPitch))
	{
		CCLOG->LogError("CImageCompiler::Bump2Normal LockRect1 failed");
//		p2DTexture->Release();
		return 0;
	}

	CSimpleBitmap<Vec4> *pOutputBitmap = pRet->GetSimpleBitmap();			assert(pOutputBitmap);


	// store input in IntermediateImage[0]
	if(bValueFromAlpha)
	{
		for(uint32 dwY=0;dwY<dwHeight;++dwY)
		{
			float *src2 = (float *)&pInputMem[dwY*dwInputPitch];

			for(uint32 dwX=0;dwX<dwWidth;++dwX,src2+=4)
				IntermediateImage[0].Set(dwX,dwY,src2[3]);
		}
	}
	else
	{
		for(uint32 dwY=0;dwY<dwHeight;++dwY)
		{
			float *src2 = (float *)&pInputMem[dwY*dwInputPitch];

			for(uint32 dwX=0;dwX<dwWidth;++dwX,src2+=4)
				IntermediateImage[0].Set(dwX,dwY,(src2[0]+src2[1]+src2[2])*(1.0f/3.0f));
		}
	}

	// calculate blurred/filtered version from the IntermediateImage[0] and put to IntermediateImage[1]
	{
		CSummedAreaFilterKernel kernel;
		static CWeightFilterSet filterset;

		if(!kernel.IsValid())
			kernel.CreateFromGauss();

		filterset.Create(kernel,1.0f * (1.0f+rProperties.m_fBumpBlurAmount),true);		// bCenter=true : kernel center is in the middle of a pixel

		// get input from the red channel
		CInputProxy_Bump2Normal InputProxy1(IntermediateImage[0].GetPointer(),dwWidth*sizeof(float),dwWidth,dwHeight);

		for(uint32 dwY=0;dwY<dwHeight;++dwY)
		for(uint32 dwX=0;dwX<dwWidth;++dwX)
		{
			float fResult=0;

			filterset.GetBlockWithFilter(InputProxy1,dwX,dwY,fResult);

			IntermediateImage[1].Set(dwX,dwY,fResult);
		}
	}

	// convert displace to normalmap
	{
		// get input from the alpha channel
		CInputProxy_Bump2Normal InputProxy2(IntermediateImage[1].GetPointer(),dwWidth*sizeof(float),dwWidth,dwHeight);

		float fInvStrength=2.0f;
		
		if(rProperties.m_fBumpStrength!=0.0f)
			fInvStrength/=rProperties.m_fBumpStrength;		// samples are 2 pixel apart  + small number to avoid divide by 0

		for(int iY=0;iY<(int)dwHeight;++iY)
		{
			float *dest = (float *)pOutputBitmap->GetPointer(0,iY);

			for(int iX=0;iX<(int)dwWidth;++iX)
			{
				float fHorizSamples[2];

				InputProxy2.Get(iX-1,iY,fHorizSamples[0]);
				InputProxy2.Get(iX+1,iY,fHorizSamples[1]);

				Vec3 vHoriz=Vec3(fInvStrength,0,fHorizSamples[1]-fHorizSamples[0]);

				float fVertSamples[2];

				InputProxy2.Get(iX,iY-1,fVertSamples[0]);
				InputProxy2.Get(iX,iY+1,fVertSamples[1]);

				Vec3 vVert=Vec3(0,fInvStrength,fVertSamples[1]-fVertSamples[0]);	

				Vec3 vNormal = vHoriz.Cross(vVert);

				vNormal.NormalizeSafe();

				dest[0] = vNormal.x;
				dest[1] = vNormal.y;
				dest[2] = vNormal.z;

				dest+=4;
			}
		}
	}

	// convert from -1..1 to 0..1
	if(bApplyRangeAdjustment)
	{
		for(int iY=0;iY<(int)dwHeight;++iY)
		for(int iX=0;iX<(int)dwWidth;++iX)
		{
			Vec4 &v = pOutputBitmap->GetRef(iX,iY);

			// convert from -1..1 to 0..1
			v.x = v.x*0.5f+0.5f;
			v.y = v.y*0.5f+0.5f;
			v.z = v.z*0.5f+0.5f;
		}
	}

	// restore alpha channel
//	if(!bSetAlphaChannel)
	for(uint32 dwY=0;dwY<dwHeight;++dwY)
	{
		float *src2 = (float *)&pInputMem[dwY*dwInputPitch];
		float *dst2 = (float *)pOutputBitmap->GetPointer(0,dwY);

		for(uint32 dwX=0;dwX<dwWidth;++dwX,src2+=4,dst2+=4)
			dst2[3]=src2[3];
	}

	pFlatFloatARGB->Unlock(0);

	return pRet;
}

ImageObject *CImageCompiler::CombineNormalMaps( ImageObject *pInOut, ImageObject *pBump ) const
{
	assert(pInOut);
	assert(pBump);

	uint32 dwWidth,dwHeight,dwDepth,dwSides,dwMips;
	uint32 dwBumpWidth,dwBumpHeight,dwBumpDepth,dwBumpSides,dwBumpMips;

	pBump->GetExtend(dwBumpWidth,dwBumpHeight,dwBumpDepth,dwBumpSides,dwBumpMips);
	pInOut->GetExtend(dwWidth,dwHeight,dwDepth,dwSides,dwMips);

	if(dwDepth!=1 || dwBumpDepth!=1)		// volume textures are not handled
	{
		CCLOG->LogError("CImageCompiler::CombineNormalMaps failed (volume textures are not supported)");
		return false;
	}

	if(dwSides!=1 || dwBumpSides!=1)		// cubemaps are not handled
	{
		CCLOG->LogError("CImageCompiler::CombineNormalMaps failed (Cubemaps are not supported)");
		return false;
	}
	
	char *pBumpMem,*pSrcMem;
	uint32 dwBumpPitch,dwSrcPitch;

	if(!pInOut->Lock(0,0,pSrcMem,dwSrcPitch))
	{
		CCLOG->LogError("CImageCompiler::CombineNormalMaps Lock1 failed (internal error)");
		return 0;
	}

	if(!pBump->Lock(0,0,pBumpMem,dwBumpPitch))
	{
		pInOut->Unlock(0);
		CCLOG->LogError("CImageCompiler::CombineNormalMaps Lock2 failed (internal error)");
		return 0;
	}

	CFloat4Image *pRet=new CFloat4Image(dwWidth,dwHeight);

	CSimpleBitmap<Vec4> *pOutBitmap = pRet->GetSimpleBitmap();

	pRet->CopyPropertiesFrom(*pInOut);

	Vec3 vHalf(128.0f/255.0f,128.0f/255.0f,128.0f/255.0f);

	for(uint32 dwY=0;dwY<dwHeight;++dwY)
	{				
		char *pSrcLine = (char *)&pSrcMem[dwY*dwSrcPitch];

		uint32 dwBumpY = dwY%dwBumpHeight;

		char *pBumpLine = (char *)&pBumpMem[dwBumpY*dwBumpPitch];

		float *pSrcPix = (float *)pSrcLine;

		for(uint32 dwX=0;dwX<dwWidth;++dwX)
		{
			uint32 dwBumpX = dwX%dwBumpWidth;

			float *pBumpPix = (float *)&pBumpLine[dwBumpX*sizeof(float)*4];

			Vec3 vBumpNormal(pBumpPix[0],pBumpPix[1],pBumpPix[2]);								// 0..1

//			vBumpNormal.x = 0.8f;
//			vBumpNormal.y = 0.8f;
//			vBumpNormal.z = 1.0f;
		
			Vec3 vNormal(pSrcPix[0],pSrcPix[1],pSrcPix[2]);												// 0..1

//			vNormal.x = 0.5f;
//			vNormal.y = 0.5f;
//			vNormal.z = 1.0f;

			vBumpNormal = vBumpNormal*2.0f-Vec3(1.0f,1.0f,1.0f);									// -1..0..1
			vNormal = vNormal*2.0f-Vec3(1.0f,1.0f,1.0f);													// -1..0..1

			Matrix33 mTransform;
			
			mTransform.SetRotationV0V1(Vec3(0,0,1),vBumpNormal.GetNormalized());

			vNormal = mTransform*vNormal;

			// convert from -1..1 to 0..1
//			if(m_Props.m_bApplyRangeAdjustment)
				vNormal = vNormal*0.5f+vHalf;

			pOutBitmap->Set(dwX,dwY,Vec4(vNormal.x,vNormal.y,vNormal.z,0));			// RGBA

			pSrcPix+=4;	// jump over RGBA
		}
	}

	pInOut->Unlock(0);
	pBump->Unlock(0);

	return pRet;
}


// code from CryEngine
static bool IsThereDDNDIF( const char *szSrcName )
{
	char szOutName[256];
	const char *str = stristr(szSrcName, "_ddn");

	if(!str)
		return false;

	int nSize = str - szSrcName;

	// make sure to add only 'ddndif', if texture filename hasn't got ddndif
	if(!stristr(szSrcName, "_ddndif")) 
	{
		memcpy(szOutName, szSrcName, nSize);
		memcpy(&szOutName[nSize], "_ddndif", 7);
		strcpy(&szOutName[nSize+7],".tif");
	}
	else
		strcpy(szOutName, szSrcName);  

	FILE *fp = fopen(szOutName,"rb");

	if(!fp)
	{
		strcpy(&szOutName[nSize+7],".dds");
		fp = fopen(szOutName, "rb");
	}

	if(fp)
	{
		fclose(fp);
		return true;
	}

	return false;
}



bool CImageCompiler::RunWithProperties( bool inbSave, const char *szExtendFileName )
{
	LARGE_INTEGER TimeStarted;
	QueryPerformanceCounter(&TimeStarted);

	//////////////////////////////////////////////////////////////////////////
	//////////////////////////////////////////////////////////////////////////
	//   autooptimize
	//////////////////////////////////////////////////////////////////////////
	//////////////////////////////////////////////////////////////////////////
	bool bAutoOptimize=true;
	
	m_pCC->config->Get("autooptimize",bAutoOptimize);

	if(bAutoOptimize && m_Props.m_bAutoOptimizeFile)
	{
//		CCLOG->Log("AutoOptimize activated (/autooptimize=1)");

		D3DFORMAT srcFormat = CPixelFormats::GetPixelFormatInfo(m_pInputImage->GetPixelFormat())->DxtNo;
		if (srcFormat == D3DFMT_DXT1 || srcFormat == D3DFMT_DXT2 || srcFormat == D3DFMT_DXT3 || srcFormat == D3DFMT_DXT5)
			return true;	// do not process
		if((m_pInputImage->GetImageFlags()&CImageExtensionHelper::EIF_Volumetexture) 
		|| (m_pInputImage->GetImageFlags()&CImageExtensionHelper::EIF_Cubemap))
			return true;	// do not process

		uint32 w,h,depth,sides,mips;
		m_pInputImage->GetExtend( w,h,depth,sides,mips );

		if( CSuffixUtil::CheckForSuffix(m_pCC->sourceFileFinal,"ddn") || CSuffixUtil::CheckForSuffix(m_pCC->sourceFileFinal,"bump") )
		{
			if( CSuffixUtil::CheckForSuffix(m_pCC->sourceFileFinal,"bump") )
			{
				CString sCurrentPreset;

				if(!m_pCC->config->Get("preset",sCurrentPreset) || sCurrentPreset=="")
				{
					// preset for _bump
					m_Props.m_sPreset="Bump2Normalmap_lowQ";
					SetPresetSettings();
				}
			}
			else
			{
				// normalmap preset for the _ddn 
				CString sCurrentPreset = m_Props.m_sPreset;

				if(sCurrentPreset=="")
				{
					m_Props.m_sPreset="Normalmap_lowQ";
					SetPresetSettings();
				}
			}
		}
		else
		{
			bool bIsPowerByTwo = m_pInputImage->IsPowerOfTwo();

			if (bIsPowerByTwo)
			{					
				if(m_Props.m_sPreset=="" && m_Props.m_Bump2Normal.m_iBumpToNormalFilter==0)
				{
					m_Props.m_sPreset="Diffuse_lowQ";
					SetPresetSettings();
				}
			}
			else
				CCLOG->LogError( "Texture %s is not power of two (%dx%d)",(const char*)m_pCC->sourceFileFinal,w,h );
		}
		
		if (w >= 1024 || h >= 1024)
		{
			int iReduce = m_Props.m_iReduceResolutionFile;

			if(iReduce<=0)
				m_Props.m_iReduceResolutionGlobal++;
		}
	}

	if(CSuffixUtil::CheckForSuffix(m_pCC->sourceFileFinal,"ddn") 
		|| CSuffixUtil::CheckForSuffix(m_pCC->sourceFileFinal,"bump") )
	{
		if(!IsFinalPixelFormatValidForNormalmaps())
			CCLOG->LogWarning("'%s' (ddn/bump) used no normalmap texture format (can be wrong on DX10)",(const char*)m_pCC->sourceFileFinal);
	}

	CString sOutputFileName=m_pCC->getOutputPath();

	// add a string to the filename (before the extension)
	if(szExtendFileName)
	{
		CString ext=Path::GetExt(sOutputFileName);

		sOutputFileName = Path::RemoveExtension(sOutputFileName);

		sOutputFileName += szExtendFileName+CString(".")+ext;
	}

	if(!m_bInitialized)
		Init( m_pCC->pRC->GetEmptyWindow() );

	if(m_pFinalImage) delete m_pFinalImage;	m_pFinalImage = NULL;

	if(inbSave)
	{
		bool bNoOutput = m_pCC->config->HasKey("nooutput");		// user requests /nooutput ?

		if(bNoOutput)
		{
			CCLOG->LogError("CImageCompiler::Process save Ext:'%s' /nooutput was specified",m_pCC->sourceFileFinalExtension);
			inbSave=false;
		}
	}

	if(m_Props.m_bPowOf2)
	if(!m_pInputImage->IsPowerOfTwo())
	{	
		// prevent non pow-of-two textures from being processed - engine doesn't like them
		CCLOG->LogError("CImageCompiler::Process image extend is not power-of-two (width and height needs to be out of ..,16,32,64,128,256,..)");
		return false;
	}

	ImageObject *pCurrentImage = m_pInputImage;

	// multi pass
	{
		// minalpha
		if(m_Props.m_iMinAlpha>0)	// 0..255
		{
			ImageObject *pIntermediateARGB = MinAlpha(*pCurrentImage,min(m_Props.m_iMinAlpha,255));

			if(!ProceedToNextPass(pCurrentImage,pIntermediateARGB))
			{
				delete pIntermediateARGB;
				return false;
			}
		}

		// Bump2Normal postprocess
		if(m_Props.m_Bump2Normal.m_iBumpToNormalFilter)			// Bump2Normal postprocess
		{
			if(ImageObject *pIntermediateFloatARGB=ConvertFormat(pCurrentImage,D3DFMT_A32B32G32R32F))
			{
				ImageObject *pRes=Bump2Normal(m_Props.m_Bump2Normal,pIntermediateFloatARGB,m_Props.m_bApplyRangeAdjustment,false);	// value from RGB luminance

				if(!ProceedToNextPass(pCurrentImage,pRes))
				{
					delete pIntermediateFloatARGB;
					return false;
				}
			}
			else CCLOG->LogError("CImageCompiler::Bump2Normal ConvertFormat1 failed");
		}

		if(m_Props.m_AlphaAsBump.m_iBumpToNormalFilter)			// Alpha As Bump
		{
			if(ImageObject *pIntermediateFloatARGB=ConvertFormat(pCurrentImage,D3DFMT_A32B32G32R32F))
			{
				ImageObject *pRes=Bump2Normal(m_Props.m_AlphaAsBump,pIntermediateFloatARGB,true,true);		// value from alpha
				if(!pRes)
				{
					delete pIntermediateFloatARGB;
					return false;
				}

				m_Props.m_bApplyRangeAdjustment=true;

				ImageObject *pCombined = CombineNormalMaps(pIntermediateFloatARGB,pRes);		// both input need to be in range 0..1

				delete pRes;pRes=0;
				delete pIntermediateFloatARGB;pIntermediateFloatARGB=0;

				if(!ProceedToNextPass(pCurrentImage,pCombined))
				{
					delete pCombined;pCombined=0;
					return false;
				}
			}
			else CCLOG->LogError("CImageCompiler::Bump2Normal ConvertFormat2 failed");
		}

		// generate mipmaps (needed for mipmaps, reduce resolution and average color computation)
 		if(m_Props.m_bMipmaps || m_Props.GetReduceResolution() || m_Props.m_bAverageColor)		
		{
			float fGamma = 1.0f;

			if(m_Props.m_bMipGammaCorrected)
			{
				fGamma = 2.2f; 
				
				m_pCC->config->Get("globalgamma",fGamma);
			}

			ImageObject *pRes=CreateMipMaps(pCurrentImage,m_Props.GetReduceResolution(),!m_Props.m_bMipmaps,m_Props.m_bMipRenormalize,fGamma);

			if(!ProceedToNextPass(pCurrentImage,pRes))
				return false;
		}

		int iDestPixelFormat = m_Props.GetDestPixelFormat(m_bInputUsesAlpha);
/*
		if(IsDDNDIFProcessing(pCurrentImage))		// DDNDIF special handling - greyscale images become accessibility, not greyscale become converted to accessibility
		{
			if(ImageObject *pFloatARGB=ConvertFormat(pCurrentImage,D3DFMT_A32B32G32R32F))
			{
				CCLOG->Log("CImageCompiler::Process DDNDIF not greyscale becomes converted to accessibility (shader requires data in that form)");
				DDNDIFProcessing(pFloatARGB);

				if(!ProceedToNextPass(pCurrentImage,pFloatARGB))
					return false;
			}
			else CCLOG->LogError("CImageCompiler::DDNDIF ConvertFormat3 failed");
		}
*/
		// compress result
		if(iDestPixelFormat!=-1)
		{
			D3DFORMAT format = g_pixelformats[iDestPixelFormat].DxtNo;

			// the following formats allow the conversion without Quality loss
			if(m_Props.m_bAutoDetectLuminanceOnly)
			if(format==D3DFMT_DXT1 || format==D3DFMT_X8R8G8B8 || format==D3DFMT_R8G8B8 || format==D3DFMT_R5G6B5 || format==D3DFMT_X1R5G5B5 || format==D3DFMT_X4R4G4B4 || format==D3DFMT_R3G3B2)
			if(IsPerfectGreyscale(pCurrentImage))
			{
//				CCLOG->Log("CImageCompiler::Process Luminance only detected - using L8");
				format=D3DFMT_L8;

				uint32 dwWidth,dwHeight,dwDepth,dwSides,dwMips;

				pCurrentImage->GetExtend(dwWidth,dwHeight,dwDepth,dwSides,dwMips);
				assert(dwMips>0);
			}

			assert(pCurrentImage);

			if(m_Props.m_eGlobalCompressor==CImageProperties::eCGDirectX)
			{
				// if it's not yet a DirectX object we need to convert it back again
				if(!pCurrentImage->GetDXTex())
				{
					ImageObject *pRes = ConvertFormat(pCurrentImage,D3DFMT_A32B32G32R32F);

					if(!ProceedToNextPass(pCurrentImage,pRes))
						return false;
				}
			}

			ImageObject *pRes=ConvertFormat(pCurrentImage,format);

			if(!ProceedToNextPass(pCurrentImage,pRes))
				return false;
		}

		// convert back to CRAWImage to save
		if(!pCurrentImage->GetCRAWImage())
		{
			CRAWImage *pRes=CopyToNewCRAWImage(pCurrentImage);
		
			assert(pRes);

			if(!ProceedToNextPass(pCurrentImage,pRes))
				return false;
		}

		// hard coded behavior for now - drop top mip for attached alpha channel (POM/offset bump doesn't require this resolution and we save memory)
		if(ImageObject *pAttachedImage = pCurrentImage->GetAttachedImage())
		{
			CRAWImage *pAttachedRAWImage = pAttachedImage->GetCRAWImage();			assert(pAttachedRAWImage);

			pAttachedRAWImage->DropTopMip();
		}
	}

	if(CSuffixUtil::CheckForSuffix(m_pCC->sourceFileFinal,"ddn"))
	{
		bool bFileSingle=false;

		m_pCC->config->Get("filesingle",bFileSingle);

		if(bFileSingle)
		if(!IsThereDDNDIF(m_pCC->getSourcePath()))
		{
			pCurrentImage->SetImageFlags(pCurrentImage->GetImageFlags()|CImageExtensionHelper::EIF_FileSingle);
			CCLOG->Log("tagged asset as FileSingle");
		}
	}


	m_pFinalImage=pCurrentImage;

	if(inbSave)
	{
//		CCLOG->Log("Saving ...");
		CCLOG->Log("");

		// destination
		if(!SaveOutput(m_pFinalImage,"    DDS",sOutputFileName))
		{
			CCLOG->LogError("CImageCompiler::Process save '%s' failed",(const char*)sOutputFileName );
			return false;
		}

		// fallback
		if(m_pTextureFallbackSystem && m_pTextureFallbackSystem->IsActivated() && m_pFinalImage)
		{
			if(!CreateTextureFallback(m_pFinalImage,2,7))
				CCLOG->LogError("CImageCompiler::CreateTextureFallback failed (internal error)");
		}

		// optional reduced destination
		if((m_pFinalImage->GetImageFlags() & CImageExtensionHelper::EIF_SupressEngineReduce)==0)
		{
			CString targetroot_low;

			if(m_pCC->config->Get("targetroot_low",targetroot_low))
			{
				targetroot_low = Path::AddBackslash(targetroot_low);

				CString sOutputFileName_low = CString(targetroot_low+m_pCC->m_sFileFolder+m_pCC->outputFile);

				m_pCC->pRC->EnsureDirectoriesPresent(Path::GetPath(sOutputFileName_low).GetString());

				CRAWImage *pRAWFinal = m_pFinalImage->GetCRAWImage();			assert(pRAWFinal);

				pRAWFinal->DropTopMip();

				if(!SaveOutput(pRAWFinal,"DDS_low",sOutputFileName_low))		// reduce=1
				{
					CCLOG->LogError("CImageCompiler::Process save low '%s' failed",sOutputFileName_low.GetBuffer() );
					return false;
				}
			}
		}
	}

	{
		LARGE_INTEGER TimeEnded,Frequency;
		
		QueryPerformanceCounter(&TimeEnded);

		m_fTimeProfiled=0;

		if(QueryPerformanceFrequency(&Frequency))
			m_fTimeProfiled = (float)((double)(TimeEnded.QuadPart-TimeStarted.QuadPart)/(double)Frequency.QuadPart);
	}
	return true;
}


void CImageCompiler::DDNDIFProcessing( ImageObject *pFloatARGB ) const
{
	// convert DDNDIF (unoccluded area direction to accessibility map)
	char *pInputMem;
	uint32 dwInputPitch;

	uint32 dwWidth,dwHeight,dwDepth,dwSides,dwMips;

	pFloatARGB->GetExtend(dwWidth,dwHeight,dwDepth,dwSides,dwMips);

	for(uint32 dwMip=0;dwMip<dwMips;++dwMip)
	{
		uint32 dwLocalWidth = pFloatARGB->GetWidth(dwMip);			// we get error on NVidia with this (assumes input is 4x4 as well)
		uint32 dwLocalHeight = pFloatARGB->GetHeight(dwMip);

		if(!pFloatARGB->Lock(0,dwMip,pInputMem,dwInputPitch))
		{
			CCLOG->LogError("CImageCompiler::DDNDIFProcessing LockRect mip=%d failed",dwMip);
			return;
		}

		const float fHalf=128.0f/255.0f;

		for(uint32 dwY=0;dwY<dwLocalHeight;++dwY)
		{
			float *src2 = (float *)&pInputMem[dwY*dwInputPitch];

			for(uint32 dwX=0;dwX<dwLocalWidth;++dwX,src2+=4)
			{
				float x = src2[0]-fHalf;
				float y = src2[1]-fHalf;
				float z = src2[2]-fHalf;

				float fLength = min(sqrtf(x*x+y*y+z*z)*2.0f,1.0f);

				src2[0]=fLength;
				src2[1]=fLength;
				src2[2]=fLength;
			}
		}

		pFloatARGB->Unlock(dwMip);
	}
}


bool CImageCompiler::CreateTextureFallback( ImageObject *pImage, const uint32 dwReduceMips, const uint32 dwMaxMips )
{
	assert(pImage);

	if(dwMaxMips>=8)
	{
		assert(0);			// we need this - to use small texture header
		return false;
	}

	std::vector<unsigned char> Buffer;

	Buffer.reserve(10*1024);


	uint32 dwWidth,dwHeight,dwDepth,dwSides,dwMips;

	pImage->GetExtend(dwWidth,dwHeight,dwDepth,dwSides,dwMips);

	if((dwWidth>1 || dwHeight>1)		// if there is an image
	&& dwMips==1)										// but only one mip
		return true;									// then there is no need to store a fallback - only mipped texture should have fallback

	uint32 dwFinalDestMips = (uint32)max(1,(int)dwMips-(int)dwReduceMips);

	dwFinalDestMips = min(dwFinalDestMips,dwMaxMips);

	int iGranularity = 1;
	
	if(CPixelFormats::GetPixelFormatInfo(pImage->GetPixelFormat())->bCompressed)
		iGranularity=4;

	uint32 dwFinalWidth = dwWidth>>(dwMips-dwFinalDestMips);							
	uint32 dwFinalHeight = dwHeight>>(dwMips-dwFinalDestMips);

	if(dwFinalWidth<1)dwFinalWidth=1;
	if(dwFinalHeight<1)dwFinalHeight=1;

	dwFinalWidth = ((dwFinalWidth+iGranularity-1)/iGranularity)*iGranularity;
	dwFinalHeight = ((dwFinalHeight+iGranularity-1)/iGranularity)*iGranularity;

	// ------------------------------------------------------------------------




	// DebugDump - only works if Entries contain DDSURFACEDESC2 header

	// small texture header 
	{
		uint32 dwBitsPerPixel;

		SPixelFormats *pPixelFormat = CPixelFormats::GetPixelFormatInfo(pImage->GetPixelFormat());

		uint32 dwDXPixelFormat = pPixelFormat->DxtNo;
		dwBitsPerPixel = (uint8)pPixelFormat->iBitsPerPixel;
		uint8 cFlags=0;

		if(pImage->HasAlpha())
			cFlags|=1;

		if(pImage->GetImageFlags()&CImageExtensionHelper::EIF_DontStream)
			cFlags|=2;

		if(dwFinalWidth>=256 || dwFinalHeight>=256)
			return false;

		Buffer.push_back((uint8)(dwWidth>>8));					// width high byte
    Buffer.push_back((uint8)(dwWidth>>0));					// width low byte
    Buffer.push_back((uint8)(dwHeight>>8));					// height high byte
    Buffer.push_back((uint8)(dwHeight>>0));					// height low byte
    Buffer.push_back((uint8)dwMips);							  // mips
		Buffer.push_back((uint8)dwFinalDestMips);				// mips
		Buffer.push_back((uint8)dwBitsPerPixel);				// bpp
		Buffer.push_back((uint8)(dwDXPixelFormat>>0));	// dxformat high byte
		Buffer.push_back((uint8)(dwDXPixelFormat>>8));	// dxformat
		Buffer.push_back((uint8)(dwDXPixelFormat>>16));	// dxformat
		Buffer.push_back((uint8)(dwDXPixelFormat>>24));	// dxformat low byte
		Buffer.push_back((uint8)cFlags);								// flags bit0:alpha, 1:don'tstream
	}

/*
	// DDS header 80 bytes
	{
		DDSURFACEDESC2 surf;

		CPixelFormats::BuildSurfaceDesc(pImage,surf);

		surf.dwWidth=dwFinalWidth;
		surf.dwHeight=dwFinalHeight;
		surf.dwMipMapCount=dwFinalDestMips;

		Buffer.resize(sizeof(DDSURFACEDESC2));
		memcpy(&Buffer[0],&surf,sizeof(DDSURFACEDESC2));
	}
*/



	// ------------------------------------------------------------------------

	for(uint32 dwMipLevel=dwMips-dwFinalDestMips;dwMipLevel<dwMips;++dwMipLevel)
	{
		char *pMem;
		uint32 dwPitch;

		if(!pImage->Lock(0,dwMipLevel,pMem,dwPitch))
		{
			assert(0);
			CCLOG->LogError("CTextureFallbackSystem: FATAL ERROR (lock failed)");
			break;
		}

//		uint32 dwMipWidth = pImage->GetWidth(dwMipLevel);
		uint32 dwMipHeight = pImage->GetHeight(dwMipLevel);

		if(CPixelFormats::GetPixelFormatInfo(pImage->GetPixelFormat())->bCompressed)
			dwMipHeight = max((uint32)1,((dwMipHeight+3)/4));

		uint32 dwOldBufferSize = (uint32)Buffer.size();
		uint32 dwBufferSize = dwPitch*dwMipHeight;

//		char str[256];

//		sprintf(str,"Fallback MipsSize: pitch:%d size:%d\n",dwPitch,dwBufferSize);
//		OutputDebugString(str);

		Buffer.resize(dwOldBufferSize+dwBufferSize);

		memcpy(&Buffer[dwOldBufferSize],pMem,dwBufferSize);

		pImage->Unlock(dwMipLevel);
	}

	uint32 dwBufferSize = (uint32)Buffer.size();

	if(dwBufferSize>128*128*4 * 4/3 + 100)		// 128x128 a 4 bytes with mips + extra
	{
		char str[256];

		sprintf_s(str,sizeof(str),"WARNING: the entry seems to be to big (%d bytes)\n",dwBufferSize);
		OutputDebugString(str);
		assert(0);
	}

//	if(!Buffer.empty())
	if(m_pTextureFallbackSystem && !m_pTextureFallbackSystem->AddEntry(m_pCC->getOutputPath(),&Buffer[0],dwBufferSize))
	{
		assert(0);
		CCLOG->LogError("CTextureFallbackSystem: FATAL ERROR (maybe duplicate name? - rename asset?) '%s'",m_pCC->getOutputPath());
	}
	else if (m_pTextureFallbackSystem)
	{
		uint64 crc = m_pTextureFallbackSystem->ComputeCRC(m_pCC->getOutputPath());

		CCLOG->Log("   fallback texture: CRC:%x%x %d bytes",(uint32)(crc>>32),(uint32)crc,(uint32)Buffer.size());
	}

	return true;
}


void CImageCompiler::ClearInputImageFlags()
{
	m_bInputUsesAlpha=false;
	m_bInputUsesDenormalizedNormals=false;
}


CString CImageCompiler::GetInfoStringUI( const bool inbOrig )
{
	int iNo = m_iOrigPixelFormatNo;

	if(!inbOrig)
	{
		if(m_pFinalImage)
			iNo = m_pFinalImage->GetPixelFormat();
		 else
			iNo = -1;

		if(iNo==-1)																										// format is recognized
			return "Format not recognized";
	}

	char str[256];

	uint32 dwMem=CalcTextureMemory(inbOrig?m_pInputImage:m_pFinalImage);

	uint32 dwWidth,dwHeight,dwDepth,dwSides,dwMips;

	m_pInputImage->GetExtend(dwWidth,dwHeight,dwDepth,dwSides,dwMips);

	const char *szAlpha = iNo==-1 ? "*" : g_pixelformats[iNo].szAlpha;

	if(inbOrig)
	{
		sprintf(str,"%dx%d Fmt:%s Alpha:%s\nMem:%.1fkB",dwWidth,dwHeight,CPixelFormats::GetPixelFormatName(iNo),szAlpha,
			dwMem/1024.0f);
	}
	else 
	{
		uint32 dwReduce = m_Props.GetReduceResolution();
		uint32 dwW=max(dwWidth>>dwReduce,(uint32)1);
		uint32 dwH=max(dwHeight>>dwReduce,(uint32)1);
		uint32 dwNumMips=max((int)_CalcMipCount(dwWidth,dwHeight)-(int)dwReduce,1);
		if(!m_Props.m_bMipmaps)dwNumMips=1;

		char *szAttachedStr="";

		if(m_pFinalImage->GetAttachedImage())
		{
			szAlpha = g_pixelformats[m_pFinalImage->GetAttachedImage()->GetPixelFormat()].szName;
			szAttachedStr="+";
		}

		uint32 dwImageFlags = m_pFinalImage->GetImageFlags();

		sprintf(str,"%dx%d Mips:%d Fmt:%s Alpha:%s%s\nMem:%.1fkB reduce:%d Flags:%p",dwW,dwH,
			dwNumMips,CPixelFormats::GetPixelFormatName(iNo),szAttachedStr,szAlpha,dwMem/1024.0f,dwReduce,dwImageFlags);
	}

	return str;
}




CString CImageCompiler::GetDestInfoString()
{
	int iNo = -1;

	if(m_pFinalImage)
		iNo = m_pFinalImage->GetPixelFormat();

	char str[1024];

	uint32 dwMem=CalcTextureMemory(m_pFinalImage);

	uint32 dwWidth,dwHeight,dwDepth,dwSides,dwMips;

	m_pInputImage->GetExtend(dwWidth,dwHeight,dwDepth,dwSides,dwMips);

	const char *szAlpha = iNo==-1 ? "*" : g_pixelformats[iNo].szAlpha;

	uint32 dwReduce = m_Props.GetReduceResolution();

	uint32 dwW=max(dwWidth>>dwReduce,(uint32)1);
	uint32 dwH=max(dwHeight>>dwReduce,(uint32)1);
	uint32 dwNumMips=max((int)_CalcMipCount(dwWidth,dwHeight)-(int)dwReduce,1);

	if(!m_Props.m_bMipmaps)
		dwNumMips=1;

	uint32 dwAttachedMem=0;
	
	if(m_pFinalImage)
	if(m_pFinalImage->GetAttachedImage())
		dwAttachedMem = CalcTextureMemory(m_pFinalImage->GetAttachedImage());
	
	sprintf(str,"%d"			// Width
							"\t%d"		// Height
							"\t%s"		// Alpha
							"\t%d"		// Mips
							"\t%.1f"	// MemInKBMem
							"\t%s"		// Format
							"\t%d"		// reduce
							"\t%.1f",	// AttachedMemInKB
		dwW,dwH,szAlpha,dwNumMips,dwMem/1024.0f,CPixelFormats::GetPixelFormatName(iNo),dwReduce,dwAttachedMem/1024.0f);

	return str;
}

/*
uint32 CImageCompiler::CalcTextureMemory( const bool inbOrig ) const
{
	int iSum=0;

	uint32 dwWidth,dwHeight,dwDepth,dwSides,dwMips;

	m_pInputImage->GetExtend(dwWidth,dwHeight,dwDepth,dwSides,dwMips);

	int iNo = -1;

	if(!inbOrig)
	{
		if(m_pFinalImage)
			iNo = m_pFinalImage->GetPixelFormat();
	}
	else iNo = m_iOrigPixelFormatNo;

	if(iNo==-1)																										// format is recognized
		return 0;

	int iBpp=g_pixelformats[iNo].iBitsPerPixel;
	int iMipmaps=1;
	
	if(!inbOrig && m_Props.m_bMipmaps)
		iMipmaps=_CalcMipCount(dwWidth,dwHeight);

	int iIgnoreTopMips=0;
	
	if(!inbOrig)
		iIgnoreTopMips=(int)m_Props.GetReduceResolution();

	for(int i=0;i<iMipmaps;i++)
	{
		if(iIgnoreTopMips)iIgnoreTopMips--;
		else
		{
			if(g_pixelformats[iNo].bCompressed)
			{
				iSum+=(max((uint32)1,dwWidth/4) * max((uint32)1,dwHeight/4) *4*4 * iBpp)/8;			// from DX SDK:  max(1,width ÷ 4)x max(1,height ÷ 4)x 8 (DXT1) or 16 (DXT2-5)
			}
			else
			{
				iSum+=(dwWidth*dwHeight*iBpp)/8;
			}
		}

		dwWidth=(dwWidth+1)/2; dwHeight=(dwHeight+1)/2;
	}

	return(iSum);
}
*/


uint32 CImageCompiler::CalcTextureMemory( const ImageObject *pImage )
{
	if(!pImage)
		return 0;

	uint32 dwRet=0;

	uint32 dwWidth,dwHeight,dwDepth,dwSides,dwMips;

	pImage->GetExtend(dwWidth,dwHeight,dwDepth,dwSides,dwMips);

	int iNo = pImage->GetPixelFormat();

	if(iNo==-1)																										// format is not recognized
		return 0;

	int iBpp=g_pixelformats[iNo].iBitsPerPixel;

	for(int dwMip=0;dwMip<dwMips;++dwMip)
	{
		if(g_pixelformats[iNo].bCompressed)
			dwRet+=(max((uint32)1,dwWidth/4) * max((uint32)1,dwHeight/4) *4*4 * iBpp)/8;			// from DX SDK:  max(1,width ÷ 4)x max(1,height ÷ 4)x 8 (DXT1) or 16 (DXT2-5)
		 else
			dwRet+=(dwWidth*dwHeight*iBpp)/8;

		dwWidth=(dwWidth+1)/2; dwHeight=(dwHeight+1)/2;
	}

	if(pImage->GetAttachedImage())
		dwRet += CalcTextureMemory(pImage->GetAttachedImage());

	return dwRet;
}


struct my_vertex
{
    FLOAT x, y, z,rhw;
    FLOAT u1, v1;
};

// Define corresponding FVF macro.
#define D3D8T_CUSTOMVERTEX ( D3DFVF_XYZRHW|D3DFVF_TEX1|D3DFVF_TEXCOORDSIZE2(0))


static void PrintInPreview( HWND inHWND, RECT &inRect, const char *inszTxt1, const char *inszTxt2=0 )
{
	HDC hdc=GetDC(inHWND);
	FillRect(hdc,&inRect,GetSysColorBrush(COLOR_3DFACE));
	SetBkMode(hdc,TRANSPARENT);
	SetTextAlign(hdc,TA_CENTER);
	ExtTextOut(hdc,(inRect.left+inRect.right)/2,(inRect.top+inRect.bottom)/2-8,ETO_CLIPPED,&inRect,inszTxt1,(int)strlen(inszTxt1),0);
	if(inszTxt2)ExtTextOut(hdc,(inRect.left+inRect.right)/2,(inRect.top+inRect.bottom)/2+8,ETO_CLIPPED,&inRect,inszTxt2,(int)strlen(inszTxt2),0);
	ReleaseDC(inHWND,hdc);
}



bool CImageCompiler::BlitTo( HWND inHWND, RECT &inRect, const float infOffsetX, const float infOffsetY, const int iniScale64, const bool inbOrig )
{
	if(!InitDirectX())
		return false;

	HRESULT hr;
	IDirect3DTexture9 *pTexture=0;

	uint32 dwWidth,dwHeight,dwDepth,dwSides,dwMips;

	m_pInputImage->GetExtend(dwWidth,dwHeight,dwDepth,dwSides,dwMips);

	if(m_Props.m_bPowOf2)
	if(!IsPowerOfTwo(dwWidth) || !IsPowerOfTwo(dwHeight))
	{
		char str[256];

		sprintf(str,"(%dx%d)",dwWidth,dwHeight);
		PrintInPreview(inHWND,inRect,"Image size is not power of two",str);
		return(true);
	}


	// error message is necessary
	if(inbOrig)
	{
		if(!m_pInputImage)
		{
			PrintInPreview(inHWND,inRect,"Load failed","(format not supported by driver?)");
			return(true);
		}
	}
	else
	{
		if(m_pFinalImage==0)
		{
			PrintInPreview(inHWND,inRect,"Conversion failed","(format not supported by driver?)");
			return true;
		}
	}
	// convert to blitable format
	ImageObject *pBaseTexture = inbOrig ? m_pInputImage : m_pFinalImage;
	
	assert(pBaseTexture);			// this was checked earlier

	ImageObject *pimgNew = ConvertFormat(pBaseTexture,D3DFMT_A8R8G8B8);

	if(!pimgNew)
		return false;

	pimgNew->CopyPropertiesFrom(*pBaseTexture);

	ImageObject *pRes = GenerateSpecialPreviewToARGB(pimgNew,m_Props.m_ePreviewMode);

	pRes->GetDXTexI(pTexture);

	if(pimgNew && pimgNew!=pBaseTexture)
	{
		delete pimgNew;
		pimgNew=0; 
	}

	if(!pTexture)
	{
		delete pRes;
		return false;
	}

	int iWidth=inRect.right-inRect.left,iHeight=inRect.bottom-inRect.top;

	int iW=((iWidth)*64)/iniScale64;	
	int iH=((iHeight)*64)/iniScale64;

	int iX=(int)(infOffsetX*dwWidth*1.0f);
	int iY=(int)(infOffsetY*dwHeight*1.0f);

	m_pd3ddev->BeginScene();

	m_pd3ddev->Clear(0,0,D3DCLEAR_TARGET,0xc0c0c0,0.0f,0);

	for(uint32 dwY=0;dwY<(uint32)iHeight/8;++dwY)
	for(uint32 dwX=0;dwX<(uint32)iWidth/8;++dwX)
	{
		if(((dwY+dwX)%2)==0)
		{
			D3DRECT rect;

			rect.x1=dwX*8;
			rect.y1=dwY*8;
			rect.x2=dwX*8+8;
			rect.y2=dwY*8+8;

			m_pd3ddev->Clear(1,&rect,D3DCLEAR_TARGET,0xf0f0f0,0.0f,0);
		}
	}

	hr=m_pd3ddev->SetTexture(0,pTexture);

	if(FAILED(hr))
	{
		CCLOG->LogError("SetTexture failed");
		delete pRes;
		return false;
	}

	if(m_Props.m_ePreviewMode==ePM_AlphaTest)
	{
		m_pd3ddev->SetRenderState(D3DRS_ALPHATESTENABLE,true);
		m_pd3ddev->SetRenderState(D3DRS_ALPHABLENDENABLE,false);
	}
	else
	{
		m_pd3ddev->SetRenderState(D3DRS_ALPHATESTENABLE,false);
		m_pd3ddev->SetRenderState(D3DRS_ALPHABLENDENABLE,true);
		m_pd3ddev->SetRenderState(D3DRS_SRCBLEND,D3DBLEND_SRCALPHA);
		m_pd3ddev->SetRenderState(D3DRS_DESTBLEND,D3DBLEND_INVSRCALPHA);
	}

	m_pd3ddev->SetTextureStageState(0,D3DTSS_ALPHAOP,D3DTOP_SELECTARG1);
	m_pd3ddev->SetTextureStageState(0,D3DTSS_ALPHAARG1,D3DTA_TEXTURE);
	m_pd3ddev->SetTextureStageState(0,D3DTSS_ALPHAARG2,D3DTA_CURRENT);

	m_pd3ddev->SetRenderState(D3DRS_ALPHAREF,128);
	m_pd3ddev->SetRenderState(D3DRS_ALPHAFUNC,D3DCMP_GREATEREQUAL);

	if(m_Props.m_bPreviewFiltered)
	{
		m_pd3ddev->SetSamplerState(0,D3DSAMP_MAGFILTER,D3DTEXF_LINEAR);
		m_pd3ddev->SetSamplerState(0,D3DSAMP_MINFILTER,D3DTEXF_LINEAR);
	}
	else
	{
		m_pd3ddev->SetSamplerState(0,D3DSAMP_MAGFILTER,D3DTEXF_POINT);
		m_pd3ddev->SetSamplerState(0,D3DSAMP_MINFILTER,D3DTEXF_POINT);
	}

	m_pd3ddev->SetSamplerState(0,D3DSAMP_MIPFILTER,D3DTEXF_POINT);

	if(m_Props.m_bPreviewTiled)
	{
		m_pd3ddev->SetSamplerState(0, D3DSAMP_ADDRESSU,	D3DTADDRESS_WRAP);
		m_pd3ddev->SetSamplerState(0, D3DSAMP_ADDRESSV,	D3DTADDRESS_WRAP);
	}
	else
	{
		m_pd3ddev->SetSamplerState(0, D3DSAMP_ADDRESSU,	D3DTADDRESS_BORDER);
		m_pd3ddev->SetSamplerState(0, D3DSAMP_ADDRESSV,	D3DTADDRESS_BORDER);
	}



	// Rendering of scene objects happens here.

	m_pd3ddev->SetVertexShader(NULL);
	m_pd3ddev->SetFVF( D3D8T_CUSTOMVERTEX );

	
	int iScaleX=(int)(iWidth*64.0f/iniScale64*0.5f);
	int iScaleY=(int)(iHeight*64.0f/iniScale64*0.5f);

	// Create vertex data with position and texture coordinates.
	my_vertex g_triangle_vertices[]=
	{
			//  x      y       z     rhw  u1  v1
			{ -0.5f,   -0.5f,   0.5f,  1,   (float)(-iScaleX+iX)/dwWidth,  (float)(-iScaleY+iY)/dwHeight, }, 
			{ 255.5f, -0.5f,   0.5f,  1,   (float)(iScaleX+iX)/dwWidth,   (float)(-iScaleY+iY)/dwHeight, }, 
			{ 255.5f, 255.5f, 0.5f,  1,   (float)(iScaleX+iX)/dwWidth,   (float)(iScaleY+iY)/dwHeight, }, 
			{ -0.5f,   255.5f, 0.5f,  1,   (float)(-iScaleX+iX)/dwWidth,  (float)(iScaleY+iY)/dwHeight, }, 
	};

	m_pd3ddev->DrawPrimitiveUP( D3DPT_TRIANGLEFAN, 2, g_triangle_vertices, sizeof(my_vertex));
	m_pd3ddev->SetTexture(0,0);

// End the scene.
	m_pd3ddev->EndScene();

	hr = m_pd3ddev->Present( NULL, &inRect, inHWND, 0 );

	if(FAILED(hr))
	{
		//check if device was lost
		if (hr == D3DERR_DEVICELOST)
		{
			CCLOG->LogError("Device Lost");

			m_pd3ddev->Reset(&m_presentParams);
		}
	}

	ReleasePpo(&pTexture);

	delete pRes;

	return true;
}



bool CImageCompiler::IsPerfectGreyscale( ImageObject * &pCurrentImage ) const
{
	CSimpleBitmap<Vec4> Bmp;

	if(!pCurrentImage->CopyToSimpleBitmap(Bmp))
		return false;

	// now we have to check if R=G=B
	uint32 dwWidth = Bmp.GetWidth(), dwHeight = Bmp.GetHeight();

	for(uint32 dwY=0;dwY<dwHeight;++dwY)
	for(uint32 dwX=0;dwX<dwWidth;++dwX)
	{
		Vec4 val;

		Bmp.Get(dwX,dwY,val);

		if(val.x!=val.y || val.y!=val.z)
		{
			// R=G=B is not the case
			return false;
		}
	}

	return true;
}


uint32 CImageCompiler::GetInputImageWidth() const
{
	if(m_pInputImage)
	{
		uint32 dwWidth,dwHeight,dwDepth,dwSides,dwMips;

		m_pInputImage->GetExtend(dwWidth,dwHeight,dwDepth,dwSides,dwMips);

		return dwWidth;
	}

	return 0;
}

//!
uint32 CImageCompiler::GetInputImageHeight() const
{
	if(m_pInputImage)
	{
		uint32 dwWidth,dwHeight,dwDepth,dwSides,dwMips;

		m_pInputImage->GetExtend(dwWidth,dwHeight,dwDepth,dwSides,dwMips);

		return dwHeight;
	}

	return 0;
}


bool CImageCompiler::ClampBlitOffset( const int iniWidth, const int iniHeight, float &inoutfX, float &inoutfY, const int iniScale64 ) const
{
	bool bRet=true;

	uint32 dwWidth,dwHeight,dwDepth,dwSides,dwMips;

	m_pInputImage->GetExtend(dwWidth,dwHeight,dwDepth,dwSides,dwMips);

	if( iniWidth >= (int)(dwWidth*iniScale64)/64 ) inoutfX=0.5f;
	else
	{
		float fXMin=(float)((iniWidth*64)/iniScale64)/(float)dwWidth*0.5f;
		float fXMax=1.0f-fXMin;

		if(inoutfX<fXMin)inoutfX=fXMin;
		if(inoutfX>fXMax)inoutfX=fXMax;

		bRet=false;
	}

	if( iniHeight >= (int)(dwHeight*iniScale64)/64 ) inoutfY=0.5f;
	else
	{
		float fYMin=(float)((iniHeight*64)/iniScale64)/(float)dwHeight*0.5f;
		float fYMax=1.0f-fYMin;

		if(inoutfY<fYMin)inoutfY=fYMin;
		if(inoutfY>fYMax)inoutfY=fYMax;

		bRet=false;
	}

	return bRet;
}







ImageObject *CImageCompiler::GenerateSpecialPreviewToARGB( ImageObject *pInputImage, const EPreviewMode ePreview ) const
{
	assert(m_pd3ddev);

	uint32 dwWidth,dwHeight,dwDepth,dwSides,dwInMips;

	pInputImage->GetExtend(dwWidth,dwHeight,dwDepth,dwSides,dwInMips);

	LPDIRECT3DTEXTURE9 p2DTexture;
	
	// maybe better to use D3DPOOL_SYSTEMMEM
	// D3DPOOL_MANAGED is needed to use LockRect()
	HRESULT hRes=m_pd3ddev->CreateTexture(dwWidth,dwHeight,dwInMips,0,D3DFMT_A8R8G8B8,D3DPOOL_MANAGED,&p2DTexture,0);

	ImageObject *pRet = new DXImage(p2DTexture);

	pRet->CopyPropertiesFrom(*pInputImage);

	for(uint32 dwMip=0;dwMip<dwInMips;++dwMip)
	{
		uint32 dwLocalWidth=dwWidth>>dwMip;						if(dwLocalWidth<1)dwLocalWidth=1;
		uint32 dwLocalHeight=dwHeight>>dwMip;					if(dwLocalHeight<1)dwLocalHeight=1;

		char *pInMem;
		uint32 dwInPitch;

		if(pInputImage->Lock(0,dwMip,pInMem,dwInPitch))
		{
			char *pOutMem;
			uint32 dwOutPitch;

			if(pRet->Lock(0,dwMip,pOutMem,dwOutPitch))
			{
				for(uint32 dwY=0;dwY<dwLocalHeight;++dwY)
				{
//					char *src2 = &pInMem[(dwHeight-1-dwY)*dwWidth];
					uint32 *src2 = (uint32 *)&pInMem[dwY*dwInPitch];
					uint32 *dest2 = (uint32 *)&pOutMem[dwOutPitch*dwY];

					for(uint32 dwX=0;dwX<dwLocalWidth;++dwX,++src2)
					{
						switch(ePreview)
						{
							case ePM_RGB:
								{
									uint32 ucR = ((*src2)>>16)&0xff;
									uint32 ucG = ((*src2)>>8)&0xff;
									uint32 ucB = (*src2)&0xff;

									*dest2++ = 0xff000000 | (ucR<<16) | (ucG<<8) | ucB;
								}
								break;

							case ePM_AAA:
								{
									uint32 dwAlpha= (*src2) >> 24;

									*dest2++ = 0xff000000 | (dwAlpha<<16) | (dwAlpha<<8) | dwAlpha;
								}
								break;

							case ePM_NormalLength:
								{
									uint8 ucR = (uint8)( ((*src2)>>16)&0xff );
									uint8 ucG = (uint8)( ((*src2)>>8)&0xff );
									uint8 ucB = (uint8)( (*src2)&0xff );

									float x = ((float)ucR-128.0f)/128.0f;
									float y = ((float)ucG-128.0f)/128.0f;
									float z = ((float)ucB-128.0f)/128.0f;

									uint32 dwGrey = (uint32)( sqrtf(x*x+y*y+z*z)*255.0f );

									if(dwGrey<2)		// to (0,0,0) normal = blue
									{
										*dest2++ = 0xff0000ff;
									}
									else if(dwGrey>255)		// to illegal normals = red
									{
										uint32 dwVal = dwGrey-255;

										if(dwVal>255)
											dwVal=255;

										uint32 dwR = 255;
										uint32 dwGB = max((int)(192-dwVal),(int)0);

										*dest2++ = 0xff000000 | (dwGB) | (dwGB<<8) | (dwR<<16);
									}
									else
									{
										*dest2++ = 0xff000000 | dwGrey | (dwGrey<<8) | (dwGrey<<16);
									}
								}
								break;

							case ePM_RGBA:
								{
									int A = (int)( ((*src2)>>24)&0xff );
									int R = (int)( ((*src2)>>16)&0xff );
									int G = (int)( ((*src2)>>8)&0xff );
									int B = (int)( (*src2)&0xff );

									*dest2++ = (A<<24) | (R<<16) | (G<<8) | B;
								}
								break;

							case ePM_RGBmulA:
							{
								int A = (int)( ((*src2)>>24)&0xff );
								int SrcR = (int)( ((*src2)>>16)&0xff );
								int SrcG = (int)( ((*src2)>>8)&0xff );
								int SrcB = (int)( (*src2)&0xff );

								int R = ( SrcR*A)/255;
								int G = ( SrcG*A)/255;
								int B = ( SrcB*A)/255;

								*dest2++ = 0xff000000 | (R<<16) | (G<<8) | B;
							}
							break;

							case ePM_AlphaTest:
								{
									uint32 ucR = ((*src2)>>16)&0xff;
									uint32 ucG = ((*src2)>>8)&0xff;
									uint32 ucB = (*src2)&0xff;

									uint32 dwAlpha= (*src2) >> 24;

									*dest2++ = (dwAlpha<<24) | (ucR<<16) | (ucG<<8) | ucB;
								}
								break;

							case ePM_mCIE2RGB:
								{
									uint32 ucR = ((*src2)>>16)&0xff;
									uint32 ucG = ((*src2)>>8)&0xff;
									uint32 ucB = (*src2)&0xff;


									uint32 dwAlpha= (*src2) >> 24;
									
									ColorF col = ColorF(ucR/255.0f,ucG/255.0f,ucB/255.0f).mCIE2RGB();
									ucR = (uint32)(col.r*255.0f+0.5f);
									ucG = (uint32)(col.g*255.0f+0.5f);
									ucB = (uint32)(col.b*255.0f+0.5f);

									assert(ucR<256);
									assert(ucG<256);
									assert(ucB<256);

									*dest2++ = (dwAlpha<<24) | (ucR<<16) | (ucG<<8) | ucB;
								}

								break;

							default:
								assert(0);
								break;
						}
					}
				}
				/*
				// test
				if(ePreview==ePM_mCIE2RGB)
				if(dwMip==0)
				{
					FILE *out = fopen("c:/temp/aa.raw","wb");
					
					for(uint32 dwY=0;dwY<dwLocalHeight;++dwY)
					for(uint32 dwX=0;dwX<dwLocalWidth;++dwX)
					{
						uint8 *dest2 = (uint8 *)&pOutMem[dwOutPitch*dwY+dwX*4];
						fwrite(&dest2[2],1,1,out);
						fwrite(&dest2[1],1,1,out);
						fwrite(&dest2[0],1,1,out);
					}
					fclose(out);
				}
				*/

				pRet->Unlock(dwMip);
			}
			else assert(0);

			pInputImage->Unlock(dwMip);
		}
		else assert(0);
	}

  return pRet;
}




bool CImageCompiler::IsPowerOfTwo( const uint32 indwValue )
{
	return((indwValue&(indwValue-1)) == 0);
}

// set the stored properties to the current file and save it
bool CImageCompiler::LoadConfigFile( const EConfigPriority ePriMask )
{
	assert(m_pCC);

	CString sPixelformat="UNKNOWN";					

	// pixelformat
	if(m_pCC->config->Get("pixelformat",sPixelformat,ePriMask))
	{
		int iNo=CPixelFormats::GetNoFromName(sPixelformat.GetBuffer());	

		if(iNo==-1)
		{ 
			assert(0);
			CCLOG->LogError("CImageCompiler::Process pixelformat '%s' not recognized",sPixelformat.GetBuffer());
			return false; 
		}

		m_Props.SetDestPixelFormat(iNo);
	}
	else m_Props.SetDestPixelFormat(0);

//	CCLOG->Log("CImageCompiler::pixelformat='%s'",g_pixelformats[m_Props.m_iDestPixelFormat].szName);

//	m_pCC->config->Merge(m_pCC->m_pcfgOverwrite);

	// ---------------------------

	m_pCC->config->Get("mipmaps",m_Props.m_bMipmaps,ePriMask);
	m_pCC->config->Get("mipgamma",m_Props.m_bMipGammaCorrected,ePriMask);
	m_pCC->config->Get("mc",m_Props.m_bMaintainAlphaCoverage,ePriMask);
	m_pCC->config->Get("detectL8",m_Props.m_bAutoDetectLuminanceOnly,ePriMask);
	m_pCC->config->Get("minalpha",m_Props.m_iMinAlpha,ePriMask);
	m_pCC->config->Get("colormodel",m_Props.m_iColorModel,ePriMask);
	m_pCC->config->Get("powof2",m_Props.m_bPowOf2,ePriMask);

	m_pCC->config->Get("mipnormalize",m_Props.m_bMipRenormalize,ePriMask);
	m_pCC->config->Get("globalcompressor",(int &)m_Props.m_eGlobalCompressor,ePriMask);

	m_pCC->config->Get("userdialog",m_Props.m_bUserDialog,ePriMask);
	m_pCC->config->Get("userdialogcustom",m_Props.m_bUserDialogCustom,ePriMask);
	m_pCC->config->Get("detectL8",m_Props.m_bAutoDetectLuminanceOnly,ePriMask);

	m_pCC->config->Get("bumptype",m_Props.m_Bump2Normal.m_iBumpToNormalFilter,ePriMask);

	m_Props.m_Bump2Normal.m_iBumpToNormalFilter = max(m_Props.m_Bump2Normal.m_iBumpToNormalFilter,0);

	m_pCC->config->Get("preset",m_Props.m_sPreset,ePriMask);

	if(!m_Props.m_bUserDialogCustom && !m_pCC->presets->Find(m_Props.m_sPreset))
	{
		// by default the first preset should be used - if there is one
		const char *szPreset = m_pCC->presets->GetSectionName(1);

		m_Props.m_sPreset = szPreset ? szPreset : "";
	}

	{
		for (int i = 0; i < CImageProperties::NUM_CONTROLLED_MIP_MAPS; ++i)
		{
			char szPropName[1024];
			sprintf(szPropName, "M%d", i);
			m_Props.m_iMIPAlpha[i]=50;
			m_pCC->config->Get(szPropName,m_Props.m_iMIPAlpha[i],ePriMask);
		}
		{
			CString text;
			m_pCC->config->Get("M", text);
			if (text.GetLength())
			{
				const char* ptr = text.GetBuffer();
				std::fill(m_Props.m_iMIPAlpha, m_Props.m_iMIPAlpha + CImageProperties::NUM_CONTROLLED_MIP_MAPS, 50);
				for (int i = 0; i < CImageProperties::NUM_CONTROLLED_MIP_MAPS; ++i)
				{
					ptr += strspn(ptr, " \t,");
					if (*ptr == 0)
						break;

					char* end;
					int val = strtol(ptr, &end, 10);
					if (ptr == end)
						break;
					ptr = end;

					m_Props.m_iMIPAlpha[i] = val;
				}
			}
		}

		m_pCC->config->Get("dns",m_Props.m_bDontStreamTexture,ePriMask);
		m_pCC->config->Get("ser",m_Props.m_bSupressEngineReduce,ePriMask);
		m_pCC->config->Get("autooptimizefile",m_Props.m_bAutoOptimizeFile,ePriMask);
		m_pCC->config->Get("bumpblur",m_Props.m_Bump2Normal.m_fBumpBlurAmount,ePriMask);
		m_pCC->config->Get("bumpstrength",m_Props.m_Bump2Normal.m_fBumpStrength,ePriMask);
		m_pCC->config->Get("reduce",m_Props.m_iReduceResolutionFile,ePriMask);
		m_pCC->config->Get("ms",m_Props.m_iMipSharpen,ePriMask);

		{
			m_pCC->config->Get("bumptype_a",m_Props.m_AlphaAsBump.m_iBumpToNormalFilter,ePriMask);
			m_pCC->config->Get("bumpblur_a",m_Props.m_AlphaAsBump.m_fBumpBlurAmount,ePriMask);
			m_pCC->config->Get("bumpstrength_a",m_Props.m_AlphaAsBump.m_fBumpStrength,ePriMask);
		}

		m_Props.m_Bump2Normal.m_fBumpBlurAmount=max(m_Props.m_Bump2Normal.m_fBumpBlurAmount,0.0f);
		m_Props.m_AlphaAsBump.m_iBumpToNormalFilter=max(m_Props.m_AlphaAsBump.m_iBumpToNormalFilter,0);
		m_Props.m_AlphaAsBump.m_fBumpBlurAmount=max(m_Props.m_AlphaAsBump.m_fBumpBlurAmount,0.0f);
	}

	m_Props.m_iReduceResolutionGlobal=m_pCC->config->GetSum("globalreduce");

	return true;
} 


bool CImageCompiler::SetPresetSettings()
{
	m_pCC->config->ClearPriorityUsage(eCP_PriorityPreset);

	bool bPreset = m_Props.m_sPreset!="";

	if(bPreset)
	{
		m_pCC->config->Set(eCP_PriorityFile,"preset", m_Props.m_sPreset);
		m_pCC->presets->SetConfig(eCP_PriorityPreset,m_Props.m_sPreset, m_pCC->config);
		LoadConfigFile(eCP_PriorityPreset);		// false = only preset properties
	}

	uint32 dwImageFlags = 0;
	
	m_pCC->config->Get("imgflags",(int &)dwImageFlags);

	if(m_Props.m_bSupressEngineReduce)
		dwImageFlags|=CImageExtensionHelper::EIF_SupressEngineReduce;

	if(m_Props.m_bDontStreamTexture)
		dwImageFlags|=CImageExtensionHelper::EIF_DontStream;

	m_pInputImage->SetImageFlags(dwImageFlags);

	return !bPreset;
}




// set the stored properties to the current file and save it
bool CImageCompiler::UpdateAndSaveConfig()
{
	m_pCC->pFileSpecificConfig->UpdateOrCreateEntry("","preset",m_Props.m_sPreset);
	// bools

	m_pCC->pFileSpecificConfig->UpdateOrCreateEntry("","pixelformat",CPixelFormats::GetPixelFormatName(m_Props.GetDestPixelFormat(InputHasAlpha() )));
	m_pCC->pFileSpecificConfig->UpdateOrCreateEntry("","mipmaps",m_Props.m_bMipmaps?"1":"0");
	//m_pCC->pFileSpecificConfig->UpdateOrCreateEntry("","mipmirror",m_Props.m_bMipMirror?"1":"0");
	m_pCC->pFileSpecificConfig->RemoveEntry("", "mipmirror");
	m_pCC->pFileSpecificConfig->UpdateOrCreateEntry("","mipgamma",m_Props.m_bMipGammaCorrected?"1":"0");
	m_pCC->pFileSpecificConfig->UpdateOrCreateEntry("","ser",m_Props.m_bSupressEngineReduce?"1":"0");
	m_pCC->pFileSpecificConfig->UpdateOrCreateEntry("","dns",m_Props.m_bDontStreamTexture?"1":"0");
	m_pCC->pFileSpecificConfig->UpdateOrCreateEntry("","autooptimizefile",m_Props.m_bAutoOptimizeFile?"1":"0");
	m_pCC->pFileSpecificConfig->UpdateOrCreateEntry("","mc",m_Props.m_bMaintainAlphaCoverage?"1":"0");
	m_pCC->pFileSpecificConfig->UpdateOrCreateEntry("","mipnormalize",m_Props.m_bMipRenormalize?"1":"0");
	m_pCC->pFileSpecificConfig->RemoveEntry("", "mipalphacoverage");
//	m_pCC->pFileSpecificConfig->UpdateOrCreateEntry("","userdialog",m_Props.m_bUserDialog?"1":"0");

	// DWORDs
	char str[256];

	sprintf(str,"%d",m_Props.m_iReduceResolutionFile);
	m_pCC->pFileSpecificConfig->UpdateOrCreateEntry("","reduce",str);

	sprintf(str,"%d",m_Props.m_iMipSharpen);
	m_pCC->pFileSpecificConfig->UpdateOrCreateEntry("","ms",str);

	std::string mipControlString;
	std::string separator = "";
	for (int i = 0; i < CImageProperties::NUM_CONTROLLED_MIP_MAPS; ++i)
	{
		char szPropName[1024];
		sprintf(szPropName, "M%d", i);
		sprintf(str,"%d",m_Props.m_iMIPAlpha[i]);
		//m_pCC->pFileSpecificConfig->UpdateOrCreateEntry("",szPropName,str);
		m_pCC->pFileSpecificConfig->RemoveEntry("",szPropName);
		mipControlString += separator;
		mipControlString += str;
		separator = ",";
	}
	m_pCC->pFileSpecificConfig->UpdateOrCreateEntry("","M",mipControlString.c_str());

	{
		sprintf(str,"%d",m_Props.m_Bump2Normal.m_iBumpToNormalFilter);
		m_pCC->pFileSpecificConfig->UpdateOrCreateEntry("","bumptype",str);

		sprintf(str,"%g",m_Props.m_Bump2Normal.m_fBumpStrength);
		m_pCC->pFileSpecificConfig->UpdateOrCreateEntry("","bumpstrength",str);

		sprintf(str,"%g",m_Props.m_Bump2Normal.m_fBumpBlurAmount);
		m_pCC->pFileSpecificConfig->UpdateOrCreateEntry("","bumpblur",str);
	}

	{
		sprintf(str,"%d",m_Props.m_AlphaAsBump.m_iBumpToNormalFilter);
		m_pCC->pFileSpecificConfig->UpdateOrCreateEntry("","bumptype_a",str);

		sprintf(str,"%g",m_Props.m_AlphaAsBump.m_fBumpStrength);
		m_pCC->pFileSpecificConfig->UpdateOrCreateEntry("","bumpstrength_a",str);

		sprintf(str,"%g",m_Props.m_AlphaAsBump.m_fBumpBlurAmount);
		m_pCC->pFileSpecificConfig->UpdateOrCreateEntry("","bumpblur_a",str);
	}

	if(m_pCC->sourceFileFinalExtension=="tif")
	{
		// TIFF, store inside the source file
		return UpdateAndSaveConfigToTIF(m_pCC->getSourcePath(),m_pCC->pFileSpecificConfig,m_pCC);
	}
	else 
	{
	 	// DirectX, store data in .ini nearby the source file
		return m_pCC->pFileSpecificConfig->Save();
	}
}



static ATI_TC_FORMAT FindCorespondingATIFormat( D3DFORMAT fmtTo )
{
	switch(fmtTo)
	{
		case D3DFMT_A8R8G8B8: return ATI_TC_FORMAT_ARGB_8888;
		case D3DFMT_X8R8G8B8: return ATI_TC_FORMAT_ARGB_8888;
    case D3DFMT_A32B32G32R32F: return ATI_TC_FORMAT_ARGB_32F;
    case D3DFMT_DXT1: return ATI_TC_FORMAT_DXT1;
    case D3DFMT_DXT3: return ATI_TC_FORMAT_DXT3;
    case D3DFMT_DXT5: return ATI_TC_FORMAT_DXT5;
    case D3DFMT_3DC: return ATI_TC_FORMAT_ATI2N;
//    case : return ATI_TC_FORMAT_ATI2N_XY;
 //   case : return ATI_TC_FORMAT_ATI2N_DXT5;
	}

	return (ATI_TC_FORMAT)(-1);			// output pixel format not supported yet
}

// e.g. ATI 3dc compression
ImageObject *CImageCompiler::ConvertFormatWithATI( ImageObject *inSrc, const D3DFORMAT format )
{
	assert(inSrc);

	// only standard 3d textures can be processed
	if((inSrc->GetImageFlags()&CImageExtensionHelper::EIF_Volumetexture) 
	|| (inSrc->GetImageFlags()&CImageExtensionHelper::EIF_Cubemap))
		return 0;

	ATI_TC_FORMAT fmtDest = FindCorespondingATIFormat(format);

	if(fmtDest==(ATI_TC_FORMAT)(-1))
		return 0;

	if(!inSrc->IsPowerOfTwo())
	{
		CCLOG->LogError("CImageCompiler::ConvertFormatWithATI failed (non power of two)");
		return 0;
	}

	// convert to a defined input format
//	ImageObject *pInputARGB = ConvertFormat(inSrc,D3DFMT_A8R8G8B8);
//	ATI_TC_FORMAT fmtSrc = ATI_TC_FORMAT_ARGB_8888;

	ATI_TC_FORMAT fmtSrc = FindCorespondingATIFormat(CPixelFormats::GetPixelFormatInfo(inSrc->GetPixelFormat())->DxtNo);

	if(fmtSrc==(ATI_TC_FORMAT)(-1))
		return 0;

	uint32 dwWidth,dwHeight,dwDepth,dwSides,dwInMips;

	inSrc->GetExtend(dwWidth,dwHeight,dwDepth,dwSides,dwInMips);

//	uint32 dwOutMips = _CalcMipCount(dwWidth,dwHeight,true);		// true = DXT

	ImageObject *pRet = new CRAWImage(dwWidth,dwHeight,dwInMips,CPixelFormats::GetNoFromD3DFormat(format),IsFinalPixelFormatCompressed());

	pRet->CopyPropertiesFrom(*inSrc);

	uint32 dwBadPixels=0,dwAllPixels=0;

	for(uint32 dwMip=0;dwMip<dwInMips;++dwMip)
	{
		uint32 dwLocalWidth=inSrc->GetWidth(dwMip);
		uint32 dwLocalHeight=inSrc->GetHeight(dwMip);

		char *pInMem;
		uint32 dwInPitch;

		if(inSrc->Lock(0,dwMip,pInMem,dwInPitch))
		{
			char *pOutMem;
			uint32 dwOutPitch;

			if(pRet->Lock(0,dwMip,pOutMem,dwOutPitch))
			{
				ATI_TC_Texture texInput;
				ATI_TC_Texture texOutput;

				texInput.dwSize=sizeof(ATI_TC_Texture);
				texInput.dwWidth=dwLocalWidth;
				texInput.dwHeight=dwLocalHeight;
				texInput.dwPitch=dwInPitch;							// ARGB = 4 bytes per pixel
				texInput.format=fmtSrc;
				texInput.dwDataSize=dwInPitch*dwLocalHeight;
				texInput.pData = (ATI_TC_BYTE *)pInMem;

				dwAllPixels+=dwLocalHeight*dwLocalWidth;

				bool bFixNormals = (fmtDest==ATI_TC_FORMAT_ATI2N);		// only this format stores normals

				if(bFixNormals)
				{
					if(fmtSrc == ATI_TC_FORMAT_ARGB_8888)		// fix illegal normals - caused problems when reconstructing on NVidia
					{
						assert(dwLocalWidth>=4 && dwLocalHeight>=4);		// required by new ATICompressorLib

						for(uint32 dwY=0;dwY<dwLocalHeight;++dwY)
						{
							uint32 *pMem = (uint32 *)&pInMem[dwY*dwInPitch];

							for(uint32 dwX=0;dwX<dwLocalWidth;++dwX,++pMem)
							{
								int x = (int)((*pMem>>16)&0xff) - 128;	// R
								int y = (int)((*pMem>>8)&0xff) - 128;		// G
								int l = x*x+y*y;

								if(l>128*128)								// fix required? limit normals in xy circle
								{
									float fX = ((x+128)*2.0f/255.0f)-1.0f;
									float fY = ((y+128)*2.0f/255.0f)-1.0f;
									float fL = fX*fX+fY*fY;
									float fInvLen = 1.0f/sqrt(fL);

									fX*=fInvLen; fY*=fInvLen;
		
									++dwBadPixels;

									int r = (int)((fX*0.5f+0.5f)*255.0f+0.4999f);
									int g = (int)((fY*0.5f+0.5f)*255.0f+0.4999f);

									*pMem = (*pMem&0xff000000) | (r<<16) | (g<<8);
								}
							}
						}
					}
					else if(fmtSrc == ATI_TC_FORMAT_ARGB_32F)
					{
						for(uint32 dwY=0;dwY<dwLocalHeight;++dwY)
						{
							float *pMem = (float *)&pInMem[dwY*dwInPitch];

							for(uint32 dwX=0;dwX<dwLocalWidth;++dwX,pMem+=4)
							{
								float x = (pMem[0]-0.5f)*2.0f;		// R
								float y = (pMem[1]-0.5f)*2.0f;		// G
								float z = (pMem[2]-0.5f)*2.0f;		// B
								float l = x*x+y*y;

								if(l>1.0f)								// fix required? limit normals in xy circle
								{
									float fInvLen = 1.0f/sqrt(l);

									x*=fInvLen; y*=fInvLen; z=0;

									++dwBadPixels;
									pMem[0] = x*0.5f+0.5f;			// R
									pMem[1] = y*0.5f+0.5f;			// G
									pMem[2] = z*0.5f+0.5f;			// B
								}
							}
						}
					}
				}

				texOutput.dwSize=sizeof(ATI_TC_Texture);
				texOutput.dwWidth=dwLocalWidth;
				texOutput.dwHeight=dwLocalHeight;
				texOutput.dwPitch=0;
				texOutput.format=fmtDest;
				texOutput.dwDataSize=ATI_TC_CalculateBufferSize(&texOutput);
				texOutput.pData = (ATI_TC_BYTE *)pOutMem;

//				assert(dwOutPitch*dwLocalHeight==texOutput.dwDataSize);

				ATI_TC_ERROR err = ATI_TC_ConvertTexture(&texInput,&texOutput,0,0,0,0);

				pRet->Unlock(dwMip);

				if(err != ATI_TC_OK)
				{
					CCLOG->LogError("CImageCompiler::ConvertFormatTo3DC failed (ATI_TC_ConvertTexture returned %d)",(int)err);
					delete pRet;
					return 0;
				}
			}

			inSrc->Unlock(dwMip);
		}
		else
		{
			CCLOG->LogError("CImageCompiler::ConvertFormatTo3DC failed (Lock)");
			delete pRet;
			return 0;
		}
	}

	if(dwAllPixels)
	{
		if((float)dwBadPixels/(float)dwAllPixels>0.03f)		// >3% bad pixels
		{
			// tell user
			CCLOG->LogError("CImageCompiler::ConvertFormatTo3DC %.1f%% bad pixels (denormalized normals)",100.0f*(float)dwBadPixels/(float)dwAllPixels);

			// tell coder
		//	assert(0);

			// do not compile
//			delete pRet;
//			return 0;
		}
	}

	if(format==D3DFMT_3DC)
	{
		// save alpha channel as attached image

		if(inSrc->HasAlpha())
		if(m_Props.m_AlphaAsBump.m_iBumpToNormalFilter==0)							// alpha2bump suppress the alpha channel export - this behavior saves memory
			pRet->SetAttachedImage(inSrc->ExtractAlphaAsA8Image());
	}
	else
	{
		// restore alpha channel from attached image
		pRet->SetAlphaFromA8Image(inSrc->GetAttachedImage());		
	}


	return pRet;
}






ImageObject *CImageCompiler::ConvertFormatWithSimpleCompressor( ImageObject *inSrc, const D3DFORMAT format )
{
	assert(inSrc);

	D3DFORMAT d3dSrcFormat = CPixelFormats::GetPixelFormatInfo(inSrc->GetPixelFormat())->DxtNo;

	if(d3dSrcFormat!=D3DFMT_A8R8G8B8 && d3dSrcFormat!=D3DFMT_X8R8G8B8)
	{
		assert(0);
		return 0;			// not supported yet
	}

	// only standard 3d textures can be processed
	if((inSrc->GetImageFlags()&CImageExtensionHelper::EIF_Volumetexture) 
		|| (inSrc->GetImageFlags()&CImageExtensionHelper::EIF_Cubemap))
		return 0;

	uint32 dwWidth,dwHeight,dwDepth,dwSides,dwInMips;

	inSrc->GetExtend(dwWidth,dwHeight,dwDepth,dwSides,dwInMips);

	ImageObject *pRet = new CRAWImage(dwWidth,dwHeight,dwInMips,CPixelFormats::GetNoFromD3DFormat(format),IsFinalPixelFormatCompressed());

	pRet->CopyPropertiesFrom(*inSrc);

	for(uint32 dwMip=0;dwMip<dwInMips;++dwMip)
	{
		uint32 dwLocalWidth=inSrc->GetWidth(dwMip);
		uint32 dwLocalHeight=inSrc->GetHeight(dwMip);

		char *pInMem;
		uint32 dwInPitch;

		if(inSrc->Lock(0,dwMip,pInMem,dwInPitch))
		{
			char *pOutMem;
			uint32 dwOutPitch;

			if(pRet->Lock(0,dwMip,pOutMem,dwOutPitch))
			{
				for(uint32 dwBlockY=0;dwBlockY<dwLocalHeight;dwBlockY+=4)
				for(uint32 dwBlockX=0;dwBlockX<dwLocalWidth;dwBlockX+=4)
				{
					uint8 colBlock[16*4];

					for(uint32 dwY=0;dwY<4;++dwY)
					{
						uint8 *pLineIn = (uint8 *)&pInMem[dwInPitch*(dwBlockY+dwY)+dwBlockX*4];		// in bytes: BGRA

						for(uint32 dwX=0;dwX<4;++dwX)
						{
							colBlock[dwY*16+dwX*4+0] = pLineIn[3];			// +0 = alpha
							colBlock[dwY*16+dwX*4+1] = pLineIn[0];			// +1 = blue
							colBlock[dwY*16+dwX*4+2] = pLineIn[1];			// +2 = green
							colBlock[dwY*16+dwX*4+3] = pLineIn[2];			// +3 = red

							pLineIn+=4;
						}
					}
/*
					memcpy(&dwCol[ 0],&pInMem[dwInPitch*(dwBlockY+0)+dwBlockX*4],4*4);
					memcpy(&dwCol[ 4],&pInMem[dwInPitch*(dwBlockY+1)+dwBlockX*4],4*4);
					memcpy(&dwCol[ 8],&pInMem[dwInPitch*(dwBlockY+2)+dwBlockX*4],4*4);
					memcpy(&dwCol[12],&pInMem[dwInPitch*(dwBlockY+3)+dwBlockX*4],4*4);
*/
					CSimpleDXTCompressor::CompressBlock4x4((uint32 *)colBlock,(uint8 *)&pOutMem[(dwBlockY/4)*dwOutPitch+dwBlockX*4]);
				}

//				memset(pOutMem,0,dwOutPitch*dwLocalHeight/4);

				pRet->Unlock(dwMip);	
/*
				if(err)
				{
					CCLOG->LogError("CImageCompiler::ConvertFormatWithSimpleCompressor failed");
					delete pRet;
					return 0;
				}
*/
			}

			inSrc->Unlock(dwMip);
		}
		else
		{
			CCLOG->LogError("CImageCompiler::ConvertFormatWithSimpleCompressor failed (Lock)");
			delete pRet;
			return 0;
		}
	}

	return pRet;
}



bool CImageCompiler::SaveAsTIFF( ImageObject * &pInput, const char *szFilename )
{
	assert(pInput);
	assert(szFilename);


	if(pInput->GetPixelFormat()!=CPixelFormats::GetNoFromD3DFormat(D3DFMT_A8R8G8B8))
	{
		ImageObject *pRes=ConvertFormat(pInput,D3DFMT_A8R8G8B8);

		if(!pRes)
			return false;

		if(!ProceedToNextPass(pInput,pRes))
			return false;
	}

	uint32 dwWidth,dwHeight,dwDepth,dwSides,dwMips;

	pInput->GetExtend(dwWidth,dwHeight,dwDepth,dwSides,dwMips);
/*
	if( gStuff->depth!=8
		 || (gStuff->imageMode!=plugInModeRGBColor && gStuff->imageMode!=plugInModeGrayScale)	//	Bitmap=0; Grayscale=1; Indexed=2; RGB=3 Multichannel=7; Duotone=8; Lab=9.
		 || (gStuff->planes!=1 && gStuff->planes!=3 && gStuff->planes!=4))		// grey,RGB,RGBA
	{
		Str255 errString = "*the input format is currently not supported";

		errString[0]=strlen((char *)&errString[1]);	// pascal string, could be done better

		gResult = PIReportError(errString);
		return;
	}
*/


	TIFF *pTiffFile = TIFFOpen(szFilename,"w");

	if(pTiffFile) 
	{
		char *pInputMem=0;
		uint32 dwInputPitch=0;
		
		if(!pInput->Lock(0,0,pInputMem,dwInputPitch))
		{
			TIFFClose(pTiffFile);
			return false;
		}

		uint32 dwPlanes = 4;
		uint32 dwBitsPerSample = 8;

		int32 done=0, total=dwHeight*dwPlanes;

		// We need to set some values for basic tags before we can add any data
		TIFFSetField(pTiffFile, TIFFTAG_IMAGEWIDTH, dwWidth);
		TIFFSetField(pTiffFile, TIFFTAG_IMAGELENGTH, dwHeight);	
		TIFFSetField(pTiffFile, TIFFTAG_SAMPLESPERPIXEL, dwPlanes);
		TIFFSetField(pTiffFile, TIFFTAG_BITSPERSAMPLE, 8); 
		TIFFSetField(pTiffFile, TIFFTAG_ROWSPERSTRIP, TIFFDefaultStripSize( pTiffFile, -1 ) ); 
		TIFFSetField(pTiffFile, TIFFTAG_COMPRESSION, COMPRESSION_NONE);
//		TIFFSetField(pTiffFile, TIFFTAG_PHOTOMETRIC, gStuff->planes==1?PHOTOMETRIC_MINISBLACK:PHOTOMETRIC_RGB);
		TIFFSetField(pTiffFile, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB);
		TIFFSetField(pTiffFile, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
		TIFFSetField(pTiffFile, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);

		size_t npixels = dwWidth * dwHeight;
		uint8 *raster = (uint8*) _TIFFmalloc((tsize_t)(npixels * sizeof (uint32)));

		if(!raster)
			CCLOG->LogError("SaveAsTIFF out of memory");

		for(uint32 row = 0; row < dwHeight; ++row)
		{
			uint32 *pDst = (uint32 *)&raster[dwWidth*row*dwPlanes];
			uint32 *pSrc = (uint32 *)&pInputMem[dwInputPitch*row];

			for(uint32 dwX=0;dwX<dwWidth;++dwX)
			{
				uint32 v = *pSrc++;

				*pDst++ = (v&0xff00ff00) | ((v&0xff)<<16) | ((v&0xff0000)>>16);			// swap red with blue
			}

			int err = TIFFWriteScanline(pTiffFile, &raster[dwWidth*row*dwPlanes], row, 0);
			assert( err >= 0 );

		}

		_TIFFfree(raster);raster=0;

		pInput->Unlock(0);
	}
	else return false;

	assert(pTiffFile);

	TIFFClose(pTiffFile);

	// update meta data
	UpdateAndSaveConfigToTIF(szFilename,m_pCC->pFileSpecificConfig,m_pCC);

	return true;
}

