/*<<<***************************************************************************
The MIT License (MIT)

Copyright(C) 2013 - 2015 Marc Mirti Mancinelli, Christophe Lardau and Philippe Pamart (xdcube.org)

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
////////////////////////////////////////////////////////////////////////////////
#Title: gdiImage
#Project: xdcGdi
////////////////////////////////////////////////////////////////////////////////
#Author: Marc MIRTI MANCINELLI
#History:
<15/09/2013: creation.
12/02/2014: this notice inserted.
***************************************************************************>>>*/
#include "stdafx.h"
#include "gdiImage.h"
#include "gdiDC.h"

void gdiImage::setParams(LPCSTR pType,LPCSTR pLabel)
{
	if (pType)
		m_type = pType;
	else
		m_type.Empty();
	if (pLabel)
		m_label = pLabel;
	else
		m_label.Empty();
}

void gdiImage::LoadFromStream(LPCSTR pFormat,IxbInputStream *pStm)
{
	xbThrow gdiEcompress(E_NOTIMPL,"gdiImage("+m_type+"/"+m_label+")::LoadFromStream() : not implemented (xdcGdi)");
}

void gdiImage::SaveToStream(LPCSTR pFormat,int compressLevel,IxbOutputStream *pStm) const
{
	xbThrow gdiEcompress(E_NOTIMPL,"gdiImage("+m_type+"/"+m_label+")::SaveToStream() : not implemented (xdcGdi)");
}

/*bool gdiImage::GetDC(int bmpId,LPCSTR pDCtype,xbRefPtr<IgdiDC> *pprDC,xbOpMode reqMode)
{
	if (reqMode == xbOp_Required)
		xbThrow gdiEcompress(E_NOTIMPL,"gdiImage("+m_type+"/"+m_label+")::GetDC() : not implemented (xdcGdi)");
	return false;
}
*/
bool gdiImage::GetBitmap(int bmpId,xbRefPtr<IgdiBitmap> *pprBmp,xbOpMode reqMode)
{
	if (bmpId == -1)
		bmpId=1;

	xbSectLock lck(&m_sectPtr);
	if (bmpId < 1 || bmpId > m_arrBmp.Length)
	{
		if (reqMode == xbOp_Required)
			xbThrow gdiEcompress("gdiImage("+m_type+"/"+m_label+")::GetBitmap() : bmpId="+xbString(bmpId)+" doesn't exist");
		return false;
	}
	if (m_arrBmp.unsafeGetAt(bmpId-1).m_prBmp.IsEmpty())
	{
		if (reqMode == xbOp_Required)
			xbThrow gdiEcompress("gdiImage("+m_type+"/"+m_label+")::GetBitmap() : bmpId="+xbString(bmpId)+" doesn't exist");
		return false;
	}
	if (pprBmp)
		*pprBmp = m_arrBmp.unsafeGetAt(bmpId-1).m_prBmp;
	return true;
}

gdiSizeLow gdiImage::GetPixelSize(int bmpId) const
{
	xbSectLock lck(&m_sectPtr);
	if (bmpId == -1)
	{
		if (m_arrBmp.IsEmpty())
			return gdiSizeLow();

		if (m_arrBmp.First().m_info.imgSz.cx)
			return m_arrBmp.First().m_info.imgSz;

		bmpId=1;
	}
	if (bmpId < 1 || bmpId > m_arrBmp.Length)
		xbThrow gdiEcompress("gdiImage("+m_type+"/"+m_label+")::GetPixelSize() : bmpId="+xbString(bmpId)+" doesn't exist");
	return m_arrBmp.unsafeGetAt(bmpId-1).m_info.bmpSz;
}

HBITMAP gdiImage::getHandle(xbLock *pLck,int bmpId)
{
	if (bmpId == -1)
		bmpId=1;
	xbRefPtr<IgdiBitmap> prBmp;
	{
		xbSectLock lck(&m_sectPtr);
		if (bmpId < 1 || bmpId > m_arrBmp.Length)
			xbThrow gdiEcompress("gdiImage("+m_type+"/"+m_label+")::getHandle() : bmpId="+xbString(bmpId)+" doesn't exist");
		prBmp = m_arrBmp.unsafeGetAt(bmpId-1).m_prBmp;
		if (prBmp.IsEmpty())
			xbThrow gdiEcompress("gdiImage("+m_type+"/"+m_label+")::getHandle() : bmpId="+xbString(bmpId)+" has no bitmap data");
	}
	return prBmp->getHandle(pLck);
}

void gdiImage::msAlphaUndo(int bmpId)
{
	if (bmpId == -1)
		bmpId=1;
	xbRefPtr<IgdiBitmap> prBmp;
	{
		xbSectLock lck(&m_sectPtr);
		if (bmpId < 1 || bmpId > m_arrBmp.Length)
			xbThrow gdiEcompress("gdiImage("+m_type+"/"+m_label+")::msAlphaUndo() : bmpId="+xbString(bmpId)+" doesn't exist");
		prBmp = m_arrBmp.unsafeGetAt(bmpId-1).m_prBmp;
		if (prBmp.IsEmpty())
			 xbThrow gdiEcompress("gdiImage("+m_type+"/"+m_label+")::msAlphaUndo() : bmpId="+xbString(bmpId)+" has no bitmap data");
	}

	prBmp->msAlphaUndo();
}

void gdiImage::msAlphaCorrect(int bmpId)
{
	if (bmpId == -1)
		bmpId=1;
	xbRefPtr<IgdiBitmap> prBmp;
	{
		xbSectLock lck(&m_sectPtr);
		if (bmpId < 1 || bmpId > m_arrBmp.Length)
			xbThrow gdiEcompress("gdiImage("+m_type+"/"+m_label+")::msAlphaCorrect() : bmpId="+xbString(bmpId)+" doesn't exist");
		prBmp = m_arrBmp.unsafeGetAt(bmpId-1).m_prBmp;
		if (prBmp.IsEmpty())
			 xbThrow gdiEcompress("gdiImage("+m_type+"/"+m_label+")::msAlphaCorrect() : bmpId="+xbString(bmpId)+" has no bitmap data");
	}

	prBmp->msAlphaCorrect();
}

gdiSizeU gdiImage::GetMetricSize(int bmpId) const
{
	if (bmpId == -1)
		bmpId=1;
	xbSectLock lck(&m_sectPtr);
	if (bmpId < 1 || bmpId > m_arrBmp.Length)
		xbThrow gdiEcompress("gdiImage("+m_type+"/"+m_label+")::GetMetricSize() : bmpId="+xbString(bmpId)+" doesn't exist");

	return m_arrBmp.unsafeGetAt(bmpId-1).m_info.bmpMetricSz;
}

bool gdiImage::IsAnimated() const
{
	xbSectLock lck(&m_sectPtr);
	return m_arrBmp.Length > 1 && m_arrBmp.First().m_info.bmpLoopNext != 0;
}

bool gdiImage::IsPaginated() const
{
	xbSectLock lck(&m_sectPtr);
	return m_arrBmp.Length > 0 && m_arrBmp.First().m_info.bmpPage >= 0;
}

bool gdiImage::HasMultipleResolution() const
{
	return false;
}


int gdiImage::GetAnimCount() const
{
	xbSectLock lck(&m_sectPtr);
	return m_arrBmp.Length > 1 && m_arrBmp.First().m_info.bmpLoopNext != 0 ? m_arrBmp.Length : 0;
}

int gdiImage::GetAnimDelay(int bmpId) const
{
	xbSectLock lck(&m_sectPtr);
	if (bmpId < 1 || bmpId > m_arrBmp.Length)
		xbThrow gdiEcompress("gdiImage("+m_type+"/"+m_label+")::GetAnimDelay() : bmpId="+xbString(bmpId)+" doesn't exist");
	return m_arrBmp.unsafeGetAt(bmpId-1).m_info.bmpLoopTime;
}

int gdiImage::GetAnimNext(int bmpId) const
{
	xbSectLock lck(&m_sectPtr);
	if (bmpId < 1 || bmpId > m_arrBmp.Length)
		xbThrow gdiEcompress("gdiImage("+m_type+"/"+m_label+")::GetAnimNext() : bmpId="+xbString(bmpId)+" doesn't exist");
	int loopNext = m_arrBmp.unsafeGetAt(bmpId-1).m_info.bmpLoopNext;
	if (loopNext==-1)
	{
		if (bmpId+1>m_arrBmp.Length)
			return 1;

		return bmpId+1;
	}
	return loopNext;
}

int gdiImage::GetPageCount() const
{
	return 0;
}

int gdiImage::GetBitmapCount() const
{
	xbSectLock lck(&m_sectPtr);
	return m_arrBmp.Length;
}

int gdiImage::GetBitmapInfo(xbArray<gdiBmpInfo> *pArray) const
{
	xbSectLock lck(&m_sectPtr);
	int cnt = m_arrBmp.Length;
	pArray->Length = cnt;
	for (int i = 0; i < cnt; i++)
	{
		pArray->SetAt(i,m_arrBmp.unsafeGetAt(i).m_info);
		pArray->GetAt(i).OwnData();
	}
	return cnt;
}

int gdiImage::GetBitmapInfo(int bmpId,gdiBmpInfo *pInfo) const
{
	if (bmpId == -1)
		bmpId=1;
	xbSectLock lck(&m_sectPtr);
	if (bmpId < 1 || bmpId > m_arrBmp.Length)
		xbThrow gdiEcompress("gdiImage("+m_type+"/"+m_label+")::GetBitmapInfo() : bmpId="+xbString(bmpId)+" doesn't exist");
	*pInfo = m_arrBmp.unsafeGetAt(bmpId-1).m_info;
	pInfo->OwnData();

	return bmpId;
}

int gdiImage::GetBitmapId(int bmpAnimNo,int bmpPage,gdiSizeU *pSz) const
{
	return -1;
}

int gdiImage::SetBitmapInfo(xbArray<gdiBmpInfo> *pArray)
{
	xbThrow gdiEcompress(E_NOTIMPL,"gdiImage("+m_type+"/"+m_label+")::SetBitmapInfo() : not implemented (xdcGdi)");
	return 0;
}

int gdiImage::SetBitmapInfo(int bmpId,gdiBmpInfo *pInfo,IgdiBitmap *pBmp)
{
	if (bmpId == -1)
		bmpId=1;
	xbSectLock lck(&m_sectPtr);
	if (bmpId < 1 || bmpId > m_arrBmp.Length)
		xbThrow gdiEcompress("gdiImage("+m_type+"/"+m_label+")::GetBitmapInfo() : bmpId="+xbString(bmpId)+" doesn't exist");

	if (pInfo)
	{
		m_arrBmp.unsafeGetAt(bmpId-1).m_info = *pInfo;
		m_arrBmp.unsafeGetAt(bmpId-1).m_info.OwnData();
	}

	if (pBmp)
	{
		m_arrBmp.unsafeGetAt(bmpId-1).m_prBmp = pBmp;
	}

	return bmpId;
}

int gdiImage::CreateBitmap(int bmpId,gdiBmpInfo *pInfo,xbRefPtr<IgdiBitmap> *pprBmp)
{
	if (bmpId == -1)
		bmpId=1;
	if (bmpId < 1)
		xbThrow gdiEcompress("gdiImage("+m_type+"/"+m_label+")::GetBitmapInfo() : bmpId="+xbString(bmpId)+" doesn't exist");
	xbSectLock lck(&m_sectPtr);
	if (bmpId > m_arrBmp.Length)
		m_arrBmp.Length = bmpId;
	if (pInfo)
	{
		m_arrBmp.unsafeGetAt(bmpId-1).m_info = *pInfo;
		m_arrBmp.unsafeGetAt(bmpId-1).m_info.OwnData();
	}
	else
		pInfo = &m_arrBmp.unsafeGetAt(bmpId-1).m_info;

	gdiCreateBitmap(&m_arrBmp.unsafeGetAt(bmpId-1).m_prBmp,pInfo->bmpSz,pInfo->bmpType,pInfo->bmpBitsPerPixel);
	if (pprBmp)
		*pprBmp = m_arrBmp.unsafeGetAt(bmpId-1).m_prBmp;

	return bmpId;
}


void gdiImage::Empty()
{
	xbSectLock lck(&m_sectPtr);
	m_arrBmp.Empty();
}

bool gdiImage::IsEmpty() const
{
	xbSectLock lck(&m_sectPtr);
	return m_arrBmp.IsEmpty();
}

bool gdiImage::IsValid() const
{
	xbSectLock lck(&m_sectPtr);
	return m_arrBmp.IsValid();
}

void gdiImage::SetBitmapCount(int bmpCnt)
{
	xbSectLock lck(&m_sectPtr);
	m_arrBmp.Length = bmpCnt;
}


void gdiImage::SetPixelSize(const gdiSizeLow &szWanted) const
{
	xbThrow gdiEcompress(E_NOTIMPL,"gdiImage("+m_type+"/"+m_label+")::SetPixelSize() : not implemented (xdcGdi)");
}

void gdiImage::SetMetricSize(const gdiSizeU &szWanted) const
{
	xbThrow gdiEcompress(E_NOTIMPL,"gdiImage("+m_type+"/"+m_label+")::SetMetricSize() : not implemented (xdcGdi)");
}


void gdiImage::askClose(LPCSTR pReason)
{
	{
		xbSectLock lck(&m_sectPtr);
		if (pReason && m_closeReason.IsEmpty())
			m_closeReason = pReason;
	}
	Destroy();
}

xbString gdiImage::getCloseReason() const
{
	xbSectLock lck(&m_sectPtr);
	xbString ret(m_closeReason);
	ret.OwnData();
	return ret;
}

void gdiImage::onDestruction(bool /*bInDestroy*/)
{
	xbSectLock lck(&m_sectPtr);
	eProtect(m_arrBmp.Empty());
}






/***********************************************************************************************************************
gdiBitmap
************************************************************************************************************************/
void gdiBitmap::askClose(LPCSTR pReason)
{
	{
		xbSectLock lck(&m_sectPtr);
		if (pReason && m_closeReason.IsEmpty())
			m_closeReason = pReason;
	}
	Destroy();
}

xbString gdiBitmap::getCloseReason() const
{
	xbSectLock lck(&m_sectPtr);
	xbString ret(m_closeReason);
	ret.OwnData();
	return ret;
}

void gdiBitmap::onDestruction(bool /*bInDestroy*/)
{
	xbSectLock lck(&m_sectPtr);
	m_bmpBitsPerPixel = 0;
	m_bmpRowByteLength = 0;
	m_bmpType = gdiBmpColor_Error;
	m_bmpSz.Empty();
	if (m_hBitmap)
	{
		::DeleteObject(m_hBitmap);
		m_hBitmap = NULL;
	}
	m_bmpDataPtr = NULL;
	//m_bmpData.Empty();
}

/*bool gdiBitmap::GetDC(LPCSTR pDCtype,xbRefPtr<IgdiDC> *pprDC,xbOpMode reqMode)
{
	if (reqMode == xbOp_Required)
		xbThrow gdiEcompress(E_NOTIMPL,"gdiBitmap::GetDC() : not implemented (xdcGdi)");
	return false;
}
*/
void gdiBitmap::GetBitmapInfo(gdiSizeLow *pSz,gdiBmpColorType *pBmpType,int *pBmpBitsPerPixel) const
{
	xbSectLock lck(&m_sectPtr);
	if (pSz)
		*pSz = m_bmpSz;
	if (pBmpType)
		*pBmpType = m_bmpType;
	if (pBmpBitsPerPixel)
		*pBmpBitsPerPixel = m_bmpBitsPerPixel;
}

bool gdiBitmap::SetBitmapInfo(const gdiSizeLow *pSz,gdiBmpColorType bmpType,int bmpBitsPerPixel,bool bConvert,xbOpMode reqMode)
{
	xbSectLock lck(&m_sectPtr);

	if ((!pSz || *pSz == m_bmpSz) && (bmpType == gdiBmpColor_Error || bmpType == m_bmpType)
		&& (bmpBitsPerPixel == 0 || bmpBitsPerPixel == m_bmpBitsPerPixel))
	{
		return true;
	}

	if (!pSz)
		pSz = &m_bmpSz;
	if (bmpType == gdiBmpColor_Error)
		bmpType = m_bmpType;
	if (bmpBitsPerPixel == 0)
		bmpBitsPerPixel = m_bmpBitsPerPixel;

	if (bConvert && m_bmpDataPtr)
		return convert(*pSz,bmpType,bmpBitsPerPixel,reqMode);

	return create(*pSz,bmpType,bmpBitsPerPixel,reqMode);
}

bool gdiBitmap::Convert(const gdiSizeLow *pSz,gdiBmpColorType bmpType,int bmpBitsPerPixel,xbRefPtr<IgdiBitmap> *pprBmpDest,xbOpMode reqMode)
{
	xbSectLock lck(&m_sectPtr);
	if (!pSz)
		pSz = &m_bmpSz;
	if (bmpType == gdiBmpColor_Error)
		bmpType = m_bmpType;
	if (bmpBitsPerPixel == 0)
		bmpBitsPerPixel = m_bmpBitsPerPixel;

	if (!gdiCreateBitmap(pprBmpDest,*pSz,bmpType,bmpBitsPerPixel,reqMode))
		return false;

	return CopyTo(*pprBmpDest,reqMode);
}

gdiSizeLow gdiBitmap::GetPixelSize() const
{
	xbSectLock lck(&m_sectPtr);
	return m_bmpSz;
}

gdiBmpColorType gdiBitmap::GetColorType() const
{
	xbSectLock lck(&m_sectPtr);
	return m_bmpType;
}

int gdiBitmap::GetBitsPerPixel() const
{
	xbSectLock lck(&m_sectPtr);
	return m_bmpBitsPerPixel;
}

int gdiBitmap::GetRowByteLength() const
{
	xbSectLock lck(&m_sectPtr);
	return m_bmpRowByteLength;
}

void gdiBitmap::SetPixelSize(const gdiSizeLow &sz,bool bConvert)
{
	xbSectLock lck(&m_sectPtr);
	if (sz == m_bmpSz)
		return;
	if (bConvert && m_bmpDataPtr)
		convert(sz,m_bmpType,m_bmpBitsPerPixel,xbOp_Required);
	else
		create(sz,m_bmpType,m_bmpBitsPerPixel,xbOp_Required);
}

void gdiBitmap::SetColorType(gdiBmpColorType colorType,bool bConvert)
{
	xbSectLock lck(&m_sectPtr);
	if (colorType == m_bmpType)
		return;
	if (bConvert && m_bmpDataPtr)
		convert(m_bmpSz,colorType,m_bmpBitsPerPixel,xbOp_Required);
	else
		create(m_bmpSz,colorType,m_bmpBitsPerPixel,xbOp_Required);
}

void gdiBitmap::SetBitsPerPixel(int bmpBitsPerPixel,bool bConvert)
{
	xbSectLock lck(&m_sectPtr);
	if (bmpBitsPerPixel == m_bmpBitsPerPixel)
		return;
	if (bConvert && m_bmpDataPtr)
		convert(m_bmpSz,m_bmpType,m_bmpBitsPerPixel,xbOp_Required);
	else
		create(m_bmpSz,m_bmpType,m_bmpBitsPerPixel,xbOp_Required);
}

bool gdiBitmap::setParams(const gdiSizeLow &sz, gdiBmpColorType bmpType, int bitsPerPixel, xbOpMode reqMode)
{
	return create(sz, bmpType,bitsPerPixel,reqMode);
}

void gdiBitmap::Empty()
{
	xbSectLock lck(&m_sectPtr);
	m_prGrayed.Empty();
	m_bmpBitsPerPixel = 0;
	m_bmpRowByteLength = 0;
	m_bmpType = gdiBmpColor_Error;
	m_bmpSz.Empty();

	if (m_hBitmap)
	{
		::DeleteObject(m_hBitmap);
		m_hBitmap = NULL;
	}
	m_bmpDataPtr = NULL;
//	m_bmpData.Empty();
}

bool gdiBitmap::IsEmpty() const
{
	xbSectLock lck(&m_sectPtr);
	return m_bmpDataPtr == NULL;
}

bool gdiBitmap::IsValid() const
{
	xbSectLock lck(&m_sectPtr);
	return m_bmpDataPtr != NULL;
}

bool gdiBitmap::create(const gdiSizeLow &sz, gdiBmpColorType bmpType, int bitsPerPixel, xbOpMode reqMode)
{
	switch(bmpType)
	{
	case gdiBmpColor_Gray:
		if (bitsPerPixel == 0)
			bitsPerPixel = 8;
		if (bitsPerPixel != 1 && bitsPerPixel != 8 && bitsPerPixel != 24 && bitsPerPixel != 32)
			break;
		goto createDta;
	case gdiBmpColor_RGB:
		if (bitsPerPixel == 0)
			bitsPerPixel = 24;
		if (bitsPerPixel != 24 /*&& bitsPerPixel != 32*/)
			break;
		goto createDta;
/*	case gdiBmpColor_CMYK:
		if (bitsPerPixel == 0)
			bitsPerPixel = 32;
		if (bitsPerPixel != 32)
			break;
		goto createDta;
	case gdiBmpColor_YUV:
		if (bitsPerPixel == 0)
			bitsPerPixel = 24;
		if (bitsPerPixel != 24 && bitsPerPixel != 32)
			break;
		goto createDta;
	case gdiBmpColor_YUVK:
		if (bitsPerPixel == 0)
			bitsPerPixel = 32;
		if (bitsPerPixel != 32)
			break;
		goto createDta;*/
	case gdiBmpColor_RGBA:
		if (bitsPerPixel == 0)
			bitsPerPixel = 32;
		if (bitsPerPixel != 32)
			break;
		goto createDta;
/*	case gdiBmpColor_YUVA:
		if (bitsPerPixel == 0)
			bitsPerPixel = 32;
		if (bitsPerPixel != 32)
			break;
		goto createDta;*/
	};
	if (reqMode = xbOp_Required)
		xbThrow gdiEcompress(E_NOTIMPL,"Bitmap format not supported","gdiBitmap::create(bmpType="+xbString(int(bmpType))+", bitsPerPixel="+xbString(bitsPerPixel)+") (xdcGdi)");
	return false;

createDta:
	m_bmpRowByteLength = (((bitsPerPixel * sz.cx) / 8 + sizeof(ulong) - 1) / sizeof(ulong)) * sizeof(ulong);
//	m_bmpData.Length = m_bmpRowByteLength * sz.cy;
	m_bmpBitsPerPixel = bitsPerPixel;
	m_bmpSz = sz;
	m_bmpType = bmpType;

	if (m_hBitmap)
	{
		m_bmpDataPtr = NULL;
		::DeleteObject(m_hBitmap);
	}

	m_bMSalphaCorrect = false;
	gdiScreenDC refdc;
	BITMAPINFOHEADER	info;
	info.biSize			= sizeof(info);
	info.biWidth		= m_bmpSz.cx;
	info.biHeight		= -m_bmpSz.cy;
	info.biPlanes		= 1;
	info.biBitCount		= ushort(m_bmpBitsPerPixel);
	info.biCompression	= BI_RGB;
	info.biSizeImage	= 0;
	info.biXPelsPerMeter = 0;
	info.biYPelsPerMeter = 0;
	info.biClrUsed		= 0;
	info.biClrImportant = 0;

	m_bmpDataPtr = NULL;
	m_hBitmap = CreateDIBSection(refdc.GetHandle(),(BITMAPINFO*)&info,DIB_RGB_COLORS,(void**)&m_bmpDataPtr,NULL,0 );
	if (!m_hBitmap)
		xbThrow gdiEsys(xbHR_LAST,"Bitmap format not supported by OS");

	return true;
}

bool gdiBitmap::convert(const gdiSizeLow &sz, gdiBmpColorType bmpType, int bitsPerPixel, xbOpMode reqMode)
{
	if (reqMode = xbOp_Required)
		xbThrow gdiEsys(E_NOTIMPL,"Bitmap format not supported","gdiBitmap::convert(bmpType="+xbString(int(bmpType))+", bitsPerPixel="+xbString(bitsPerPixel)+") (xdcGdi)");

	return false;
}

bool gdiBitmap::CopyTo(IgdiBitmap *pBmpDest,xbOpMode reqMode)
{
//	if (pBmpDest->IsEmpty())
	{
		xbSectLock lck(&m_sectPtr);
		if (!pBmpDest->SetBitmapInfo(&m_bmpSz,m_bmpType,m_bmpBitsPerPixel,false,reqMode))
			return false;

		pBmpDest->CopyDataFrom(m_bmpDataPtr,m_bmpRowByteLength * m_bmpSz.cy);

		return true;
	}

	if (reqMode = xbOp_Required)
		xbThrow gdiEsys(E_NOTIMPL,"Copy not supported for bitmap format asked","gdiBitmap::CopyTo(bmpTypeSrc="+xbString(int(m_bmpType))+", bitsPerPixelSrc="+xbString(m_bmpBitsPerPixel)+", bmpTypeDst="+xbString(int(pBmpDest->GetColorType()))+", bitsPerPixelSrc="+xbString(pBmpDest->GetBitsPerPixel())+") (xdcGdi)");
	return false;
}

void gdiBitmap::CopyDataFrom(const void *pData, int cByteData)
{
	xbSectLock lck(&m_sectPtr);
	m_bMSalphaCorrect = false;
	if (m_bmpRowByteLength * m_bmpSz.cy < cByteData)
		xbThrow gdiEsys(E_NOTIMPL,"Data to copy too big for current bitmap format","gdiBitmap::CopyTo(bmpTypeDst="+xbString(int(m_bmpType))+", bitsPerPixelDst="+xbString(m_bmpBitsPerPixel)+") (xdcGdi)");

	memcpy(m_bmpDataPtr,pData,cByteData);
}

uchar *gdiBitmap::getBitmapData(xbLock	*pLck,int *pRowByteLength)
{
	if (pLck)
		pLck->Lock(this);
	if (pRowByteLength)
		*pRowByteLength = m_bmpRowByteLength;

	return m_bmpDataPtr;
}

int gdiBitmap::GetBitmapRow(xbLock *pLck,int rowStart, uchar** rowPointers, int rowCnt, int rowRequiredByteLength)
{
	xbSectLock lck;
	if (pLck)
		pLck->Lock(this);
	else
		lck.Lock(&m_sectPtr);

	if (rowRequiredByteLength != 0
		&& rowRequiredByteLength > m_bmpRowByteLength)
		xbThrow gdiEsys(E_NOTIMPL,"Byte length for rows too big for current bitmap format","gdiBitmap::GetBitmapRow(bmpType="+xbString(int(m_bmpType))+", bitsPerPixel="+xbString(m_bmpBitsPerPixel)+") (xdcGdi)");

	if (rowStart<0||rowStart >= m_bmpSz.cy) 
		return 0;

	if (rowCnt + rowStart > m_bmpSz.cy)
		rowCnt = m_bmpSz.cy - rowStart;

	uchar *ptr = m_bmpDataPtr + (rowStart * m_bmpRowByteLength);

	for(int i = 0; i < rowCnt; i++, ptr += m_bmpRowByteLength)
		rowPointers[i] = ptr;

	return rowCnt;
}

void gdiBitmap::Clear(const gdiColor &clr)
{
	xbThrow gdiEsys(E_NOTIMPL,"Clear not supported for current bitmap format","gdiBitmap::Clear(bmpType="+xbString(int(m_bmpType))+", bitsPerPixel="+xbString(m_bmpBitsPerPixel)+") (xdcGdi)");
}

HBITMAP gdiBitmap::getHandle(xbLock *pLck)
{
	xbSectLock lck;
	if (pLck)
		pLck->Lock(this);
	else
		lck.Lock(&m_sectPtr);

	m_prGrayed.Empty();
	return m_hBitmap;
}

void gdiBitmap::Draw(
	IgdiDC &dcDest,					// handle to destination DC
	int x,						// x-coord of upper-left corner
	int y,						// y-coord of upper-left corner
	int width,					// destination width
	int height,					// destination height
	int xSrc,						// x-coord of upper-left corner
	int ySrc,						// y-coord of upper-left corner
	int widthSrc,					// source width
	int heightSrc,				// source height
	int alpha
	)
{
	if (!width || !height || !widthSrc || !heightSrc)
		return;
	xbRefPtr<IgdiDC> prSrcDC = dcDest.CreateCompatibleDC();

	prSrcDC->Select(this);

	if (widthSrc == -1) 
		widthSrc = m_bmpSz.cx;
	if (heightSrc == -1) 
		heightSrc = m_bmpSz.cy;
	if (width == -1) 
		width = m_bmpSz.cx;
	if (height == -1) 
		height = m_bmpSz.cy;
	
	if (HasAlpha() || alpha!=-1 || dcDest.HasAlpha())
	{
		msAlphaCorrect();

		dcDest.AlphaDraw(x,y,width,height,*prSrcDC,xSrc,ySrc,widthSrc,heightSrc,alpha);
	}
	else
	{
		dcDest.StdDraw(x,y,width,height,*prSrcDC,xSrc,ySrc,widthSrc,heightSrc);
	}
}

void gdiBitmap::DrawGrayed(
	IgdiDC &dcDest,					// handle to destination DC
	  int x,						// x-coord of upper-left corner
	  int y,						// y-coord of upper-left corner
	  int width,					// destination width
	  int height,					// destination height
	  int xSrc,						// x-coord of upper-left corner
	  int ySrc,						// y-coord of upper-left corner
	  int widthSrc,					// source width
	  int heightSrc,				// source height
	  int alpha
  )
{
	if (!width || !height || !widthSrc || !heightSrc)
		return;
	xbRefPtr<IgdiBitmap> prBmp;
	if (Grayed(&prBmp,xbOp_Optional))
		prBmp->Draw(dcDest,x,y,width,height,xSrc,ySrc,widthSrc,heightSrc,alpha);
	else
		Draw(dcDest,x,y,width,height,xSrc,ySrc,widthSrc,heightSrc,alpha);
}

bool gdiBitmap::HasAlpha()
{
	return m_bmpType == gdiBmpColor_RGBA;
}

void gdiImage::Draw(int bmpId,IgdiDC &dcDest,int x,	int y, int width, int height, int xSrc, int ySrc, 
		int widthSrc, int heightSrc, int alpha)
{
	if (!width || !height || !widthSrc || !heightSrc)
		return;

	if (bmpId == -1)
		bmpId=1;
	xbSectLock lck(&m_sectPtr);

	if (bmpId < 1 || bmpId > m_arrBmp.Length)
		xbThrow gdiEcompress("gdiImage("+m_type+"/"+m_label+")::Draw() : bmpId="+xbString(bmpId)+" doesn't exist");
	const BmpItem &bmpItem = m_arrBmp.unsafeGetAt(bmpId-1);
	xbRefPtr<IgdiBitmap> prBmp(bmpItem.m_prBmp);
	if (prBmp.IsEmpty())
		xbThrow gdiEcompress("gdiImage("+m_type+"/"+m_label+")::Draw() : bmpId="+xbString(bmpId)+" doesn't exist");
	if ((bmpItem.m_info.bmpLoopFlags & gdiLoopFlag_LeaveImage) && bmpId > 1)
	{
		lck.Unlock();

		Draw(bmpId-1,dcDest,x,y,width,height,xSrc,ySrc,widthSrc,heightSrc,alpha);

		lck.Lock(&m_sectPtr);
	}
	if (bmpItem.m_info.imgSz.cx && bmpItem.m_info.bmpSz != bmpItem.m_info.imgSz)
	{
		if (widthSrc == -1) 
			widthSrc = bmpItem.m_info.imgSz.cx;
		if (heightSrc == -1) 
			heightSrc = bmpItem.m_info.imgSz.cy;
		if (width == -1) 
			width = bmpItem.m_info.imgSz.cx;
		if (height == -1) 
			height = bmpItem.m_info.imgSz.cy;

		int xDec = 0;
		int yDec = 0;
		int newWidthSrc = widthSrc;
		int newHeightSrc = heightSrc;

		if (xSrc < bmpItem.m_info.bmpOffset.x)
		{
			xDec = (width*(bmpItem.m_info.bmpOffset.x - xSrc))/widthSrc;
			newWidthSrc -= bmpItem.m_info.bmpOffset.x - xSrc;
			xSrc = 0; //bmpItem.m_info.bmpOffset.x;
		}
		if (ySrc < bmpItem.m_info.bmpOffset.y)
		{
			yDec = (height*(bmpItem.m_info.bmpOffset.y - ySrc))/heightSrc;
			newHeightSrc -= bmpItem.m_info.bmpOffset.y - ySrc;
			ySrc = 0; //bmpItem.m_info.bmpOffset.y;
		}

		int wDec = 0,hDec = 0;
		if (newWidthSrc > bmpItem.m_info.bmpSz.cx)
		{
			wDec = (width*(newWidthSrc - bmpItem.m_info.bmpSz.cx))/widthSrc;
			newWidthSrc = bmpItem.m_info.bmpSz.cx;
		}
		if (newHeightSrc > bmpItem.m_info.bmpSz.cy)
		{
			hDec = (height*(newHeightSrc - bmpItem.m_info.bmpSz.cy))/heightSrc;
			newHeightSrc = bmpItem.m_info.bmpSz.cy;
		}


		widthSrc = newWidthSrc;
		heightSrc = newHeightSrc;
		x += xDec;
		width -= xDec+wDec;
		y += yDec;
		height -= yDec+hDec;
		if (widthSrc <= 0 || heightSrc <= 0 || width <= 0 || height <= 0)
			return;
	}

	lck.Unlock();

	prBmp->Draw(dcDest,x,y,width,height,xSrc,ySrc,widthSrc,heightSrc,alpha);
}

void gdiImage::DrawGrayed(int bmpId,IgdiDC &dcDest,int x,	int y, int width, int height, int xSrc, int ySrc, 
		int widthSrc, int heightSrc, int alpha)
{
	if (bmpId == -1)
		bmpId=1;
	xbSectLock lck(&m_sectPtr);
	if (bmpId < 1 || bmpId > m_arrBmp.Length)
		xbThrow gdiEcompress("gdiImage("+m_type+"/"+m_label+")::Draw() : bmpId="+xbString(bmpId)+" doesn't exist");
	xbRefPtr<IgdiBitmap> prBmp(m_arrBmp.unsafeGetAt(bmpId-1).m_prBmp);
	if (prBmp.IsEmpty())
		xbThrow gdiEcompress("gdiImage("+m_type+"/"+m_label+")::Draw() : bmpId="+xbString(bmpId)+" doesn't exist");
	lck.Unlock();

	prBmp->DrawGrayed(dcDest,x,y,width,height,xSrc,ySrc,widthSrc,heightSrc,alpha);
}

bool gdiImage::HasAlpha(int bmpId)
{
	if (bmpId == -1)
		bmpId=1;
	xbSectLock lck(&m_sectPtr);
	if (bmpId < 1 || bmpId > m_arrBmp.Length)
		xbThrow gdiEcompress("gdiImage("+m_type+"/"+m_label+")::HasAlpha() : bmpId="+xbString(bmpId)+" doesn't exist");
	return m_arrBmp.unsafeGetAt(bmpId-1).m_info.bmpType == gdiBmpColor_RGBA;
}

bool gdiBitmap::Grayed(xbRefPtr<IgdiBitmap> *pprBmpDest,xbOpMode reqMode)
{
	xbSectLock lck(&m_sectPtr);
	if (m_prGrayed.IsValid())
	{
		if (pprBmpDest)
			*pprBmpDest = m_prGrayed;
		return true;
	}

	xbRefPtr<IgdiBitmap> prBmpDest;
	if (!gdiCreateBitmap(&prBmpDest,m_bmpSz,m_bmpType,m_bmpBitsPerPixel,reqMode))
		return false;

	if (!CopyTo(prBmpDest,reqMode))
		return false;

	effectGray(prBmpDest,m_bmpSz,m_bmpBitsPerPixel/8);

	m_prGrayed = prBmpDest;

	if (pprBmpDest)
		*pprBmpDest = m_prGrayed;
	return true;
}

void gdiBitmap::msAlphaCorrect()
{
	if (m_bMSalphaCorrect)
		return;
	if (m_bmpBitsPerPixel != 32)
		return;
	m_bMSalphaCorrect = true;

	xbArray<uchar*> rows;
	int height = rows.Length = m_bmpSz.cy;

	xbLock lck;
	GetBitmapRow(&lck,0,rows.Begin(),height,m_bmpSz.cx*4);

	uchar *ptr;
	uchar *ptrend;
	for(int y = 0,alpha; y < height;y++)
	{
		ptr = (uchar*)rows.GetAt(y);
		ptrend = ptr+m_bmpSz.cx*4;

		while(ptr < ptrend)
		{
//			if (*ptr == 0xFFFFFF)
//				*ptr = 0;
			alpha = *(ptr+3);
			*(ptr) = (ulong(*(ptr)) * alpha) / 255;	
			*(ptr+1) = (ulong(*(ptr+1)) * alpha) / 255;	
			*(ptr+2) = (ulong(*(ptr+2)) * alpha) / 255;	

			ptr+=4;
		}
	}
}

void gdiBitmap::msAlphaUndo()
{
	if (!m_bMSalphaCorrect)
		return;
	if (m_bmpBitsPerPixel != 32)
	{
		m_bMSalphaCorrect = false;
		return;
	}

	xbArray<uchar*> rows;
	int height = rows.Length = m_bmpSz.cy;

	xbLock lck;
	GetBitmapRow(&lck,0,rows.Begin(),height,m_bmpSz.cx*4);

	uchar *ptr;
	uchar *ptrend;
	for(int y = 0,alpha; y < height;y++)
	{
		ptr = (uchar*)rows.GetAt(y);
		ptrend = ptr+m_bmpSz.cx*4;

		while(ptr < ptrend)
		{
//			if (*ptr == 0xFFFFFF)
//				*ptr = 0;
			alpha = *(ptr+3);
			*(ptr) = (ulong(*(ptr)) * 255) / alpha;
			*(ptr+1) = (ulong(*(ptr+1)) * 255) / alpha;
			*(ptr+2) = (ulong(*(ptr+2)) * 255) / alpha;

			ptr+=4;
		}
	}

	m_bMSalphaCorrect = false;
}

void gdiBitmap::effectGray(IgdiBitmap *pBmp,gdiSizeLow &sz,int bytePerPixel)
{
	xbArray<uchar*> rows;
	int height = rows.Length = sz.cy;

	xbLock lck;
	pBmp->GetBitmapRow(&lck,0,rows.Begin(),height,sz.cx*bytePerPixel);

	uchar *ptr;
	uchar *ptrend;
	int val;
	for(int y = 0; y < height;y++)
	{
		ptr = rows.GetAt(y);
		ptrend = ptr+(sz.cx*bytePerPixel);

		while(ptr < ptrend)
		{
			val = *ptr + *(ptr+1) + *(ptr+2);
			val /= 3;
			*(ptr++)=val;
			*(ptr++)=val;
			*(ptr++)=val;
			if (bytePerPixel>3)
				ptr++;
		}
	}
}
