//-----------------------------------------------------------------------------
// File: Cbitmap.cpp
//
// Desc: CBitmap class is an object that wraps around a Windows bitmap.
//
// Copyright (C) 1999-2000 Microsoft Corporation. All Rights Reserved.
//-----------------------------------------------------------------------------

#include "common.hpp"
#include "id3dsurf.h"

BOOL DI_AlphaBlend(
  HDC hdcDest,                 // handle to destination DC
  int nXOriginDest,            // x-coord of upper-left corner
  int nYOriginDest,            // y-coord of upper-left corner
  int nWidthDest,              // destination width
  int nHeightDest,             // destination height
  HDC hdcSrc,                  // handle to source DC
  int nXOriginSrc,             // x-coord of upper-left corner
  int nYOriginSrc,             // y-coord of upper-left corner
  int nWidthSrc,               // source width
  int nHeightSrc              // source height
)
{
	LPBYTE pbDestBits = NULL;
	HBITMAP hTempDestDib = NULL;
	int nXOriginDestLogical = nXOriginDest, nYOriginDestLogical = nYOriginDest;

	// Convert nXOriginDest and nYOriginDest from logical coord to device coord
	POINT pt = {nXOriginDest, nYOriginDest};
	LPtoDP(hdcDest, &pt, 1);
	nXOriginDest = pt.x;
	nYOriginDest = pt.y;
	// Convert nXOriginSrc and nYOriginSrc from logical coord to device coord
	pt.x = nXOriginSrc;
	pt.y = nYOriginSrc;
	LPtoDP(hdcSrc, &pt, 1);
	nXOriginSrc = pt.x;
	nYOriginSrc = pt.y;

	// Get the bits for both source and destination first
	// Every BITMAP used in the UI is created with CreateDIBSection, so we know we can get the bits.
	HBITMAP hSrcBmp, hDestBmp;
	DIBSECTION SrcDibSec, DestDibSec;
	hSrcBmp = (HBITMAP)GetCurrentObject(hdcSrc, OBJ_BITMAP);
	GetObject(hSrcBmp, sizeof(DIBSECTION), &SrcDibSec);
	hDestBmp = (HBITMAP)GetCurrentObject(hdcDest, OBJ_BITMAP);
	GetObject(hDestBmp, sizeof(DIBSECTION), &DestDibSec);
	if (!SrcDibSec.dsBm.bmBits) return FALSE;  // Not necessary, but to be absolutely safe.

	// Calculate the rectangle to perform the operation.
	if (nXOriginSrc + nWidthSrc > SrcDibSec.dsBm.bmWidth) nWidthSrc = SrcDibSec.dsBm.bmWidth - nXOriginSrc;
	if (nYOriginSrc + nHeightSrc > SrcDibSec.dsBm.bmHeight) nHeightSrc = SrcDibSec.dsBm.bmHeight - nYOriginSrc;
	if (nXOriginDest + nWidthDest > DestDibSec.dsBm.bmWidth) nWidthDest = DestDibSec.dsBm.bmWidth - nXOriginDest;
	if (nYOriginDest + nHeightDest > DestDibSec.dsBm.bmHeight) nHeightDest = DestDibSec.dsBm.bmHeight - nYOriginDest;

	if (nWidthDest > nWidthSrc) nWidthDest = nWidthSrc;
	if (nHeightDest > nHeightSrc) nHeightDest = nHeightSrc;
	if (nWidthSrc > nWidthDest) nWidthSrc = nWidthDest;
	if (nHeightSrc > nHeightDest) nHeightSrc = nHeightDest;

	BITMAPINFO bmi;
	ZeroMemory(&bmi.bmiHeader, sizeof(BITMAPINFOHEADER));
	bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
	bmi.bmiHeader.biWidth = nWidthDest;
	bmi.bmiHeader.biHeight = nHeightDest;
	bmi.bmiHeader.biPlanes = 1;
	bmi.bmiHeader.biBitCount = 32;
	bmi.bmiHeader.biCompression = BI_RGB;

	// Bitmap will have the same width as the dest, but only lines covered in the subrect.
	hTempDestDib = CreateDIBSection(hdcDest, &bmi, DIB_RGB_COLORS, (LPVOID*)&pbDestBits, NULL, NULL);
	if (!hTempDestDib)
		return FALSE;

	HDC hTempDC = CreateCompatibleDC(hdcDest);
	if (!hTempDC)
	{
		DeleteObject(hTempDestDib);
		return FALSE;
	}
	HBITMAP hOldTempBmp = (HBITMAP)SelectObject(hTempDC, hTempDestDib);
	BOOL res = BitBlt(hTempDC, 0, 0, nWidthDest, nHeightDest, hdcDest, nXOriginDestLogical, nYOriginDestLogical, SRCCOPY);
	SelectObject(hTempDC, hOldTempBmp);
	DeleteDC(hTempDC);
	if (!res)
	{
		DeleteObject(hTempDestDib);
		return FALSE;
	}

	// We have the bits. Now do the blend.
	for (int j = 0; j < nHeightSrc; ++j)
	{
		assert(j >= 0 &&
		       j < nHeightDest);
		LPBYTE pbDestRGB = (LPBYTE)&((DWORD*)pbDestBits)[j * nWidthDest];

		assert(nYOriginSrc+SrcDibSec.dsBm.bmHeight-nHeightSrc >= 0 &&
		       nYOriginSrc+SrcDibSec.dsBm.bmHeight-nHeightSrc < SrcDibSec.dsBm.bmHeight);
		LPBYTE pbSrcRGBA = (LPBYTE)&((DWORD*)SrcDibSec.dsBm.bmBits)[(j+nYOriginSrc+SrcDibSec.dsBm.bmHeight-nHeightSrc)
		                                                            * SrcDibSec.dsBm.bmWidth + nXOriginSrc];

		for (int i = 0; i < nWidthSrc; ++i)
		{
			// Blend
			if (pbSrcRGBA[3] == 255)
			{
				// Alpha is 255. straight copy.
				*(LPDWORD)pbDestRGB = *(LPDWORD)pbSrcRGBA;
			} else
			if (pbSrcRGBA[3])
			{
				// Alpha is non-zero
				pbDestRGB[0] = pbSrcRGBA[0] + (((255-pbSrcRGBA[3]) * pbDestRGB[0]) >> 8);
				pbDestRGB[1] = pbSrcRGBA[1] + (((255-pbSrcRGBA[3]) * pbDestRGB[1]) >> 8);
				pbDestRGB[2] = pbSrcRGBA[2] + (((255-pbSrcRGBA[3]) * pbDestRGB[2]) >> 8);
			}
			pbDestRGB += sizeof(DWORD);
			pbSrcRGBA += sizeof(DWORD);
		}  // for
	} // for

	HDC hdcTempDest = CreateCompatibleDC(hdcDest);
	if (hdcTempDest)
	{
		HBITMAP hOldTempBmp = (HBITMAP)SelectObject(hdcTempDest, hTempDestDib);  // Select the temp dib for blitting
		// Get logical coord for device coord of dest origin
		POINT pt = {nXOriginDest, nYOriginDest};
		DPtoLP(hdcDest, &pt, 1);
		BitBlt(hdcDest, pt.x, pt.y, nWidthDest, nHeightDest,
		       hdcTempDest, 0, 0, SRCCOPY);
		SelectObject(hdcTempDest, hOldTempBmp);
		DeleteDC(hdcTempDest);
	}

	DeleteObject(hTempDestDib);
	return TRUE;
}

CBitmap::~CBitmap()
{
	if (m_hbm != NULL)
		DeleteObject(m_hbm);
	m_hbm = NULL;
	m_bSizeKnown = FALSE;
}

HDC CreateAppropDC(HDC hDC)
{
	return CreateCompatibleDC(hDC);
}

HBITMAP CreateAppropBitmap(HDC hDC, int cx, int cy)
{
	if (hDC != NULL)
		return CreateCompatibleBitmap(hDC, cx, cy);
	
	HWND hWnd = GetDesktopWindow();
	HDC hWDC = GetWindowDC(hWnd);
	HBITMAP hbm = NULL;
	if (hWDC != NULL)
	{
		hbm = CreateCompatibleBitmap(hWDC, cx, cy);
		ReleaseDC(hWnd, hWDC);
	}

	return hbm;
}

CBitmap *CBitmap::CreateFromResource(HINSTANCE hInst, LPCTSTR tszName)
{
	return CreateViaLoadImage(hInst, tszName, IMAGE_BITMAP, 0, 0,
		LR_CREATEDIBSECTION | LR_DEFAULTSIZE);
}

CBitmap *CBitmap::CreateFromFile(LPCTSTR tszFileName)
{
	return CreateViaD3DX(tszFileName);
}

// Use D3DX API to load our surface with image content.
CBitmap *CBitmap::CreateViaD3DX(LPCTSTR tszFileName, LPDIRECT3DSURFACE8 pUISurf)
{
	HRESULT hr;
	LPDIRECT3D8 pD3D = NULL;
	LPDIRECT3DDEVICE8 pD3DDev = NULL;
	LPDIRECT3DTEXTURE8 pTex = NULL;
	LPDIRECT3DSURFACE8 pSurf = NULL;
	HBITMAP hDIB = NULL;

	__try
	{

		// If the UI surface is NULL, create a new device.  Otherwise, use existing device.
		if (!pUISurf)
		{
			pD3D = Direct3DCreate8(D3D_SDK_VERSION);
			if (!pD3D)
				return NULL;
			OutputDebugString(_T("D3D created\n"));
			D3DDISPLAYMODE Mode;
			pD3D->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &Mode);
			D3DPRESENT_PARAMETERS d3dpp;
			d3dpp.BackBufferWidth = 1;
			d3dpp.BackBufferHeight = 1;
			d3dpp.BackBufferFormat = Mode.Format;
			d3dpp.BackBufferCount = 1;
			d3dpp.MultiSampleType = D3DMULTISAMPLE_NONE;
			d3dpp.SwapEffect = D3DSWAPEFFECT_COPY;
			d3dpp.hDeviceWindow = NULL;
			d3dpp.Windowed = TRUE;
			d3dpp.EnableAutoDepthStencil = FALSE;
			d3dpp.FullScreen_RefreshRateInHz = 0;
			d3dpp.FullScreen_PresentationInterval = 0;
			d3dpp.Flags = 0;
			hr = pD3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_REF, GetActiveWindow(), D3DCREATE_SOFTWARE_VERTEXPROCESSING, &d3dpp, &pD3DDev);
			if (FAILED(hr))
			{
            	TCHAR tszMsg[MAX_PATH];

				_stprintf(tszMsg, _T("CreateDevice returned 0x%X\n"), hr);
				OutputDebugString(tszMsg);
				return NULL;
			}
		} else
		{
			hr = pUISurf->GetDevice(&pD3DDev);
			if (FAILED(hr))
				return NULL;
		}

		OutputDebugString(_T("D3D device createed\n"));
		hr = pD3DDev->CreateTexture(512, 512, 0, 0, D3DFMT_A8R8G8B8, D3DPOOL_SYSTEMMEM, &pTex);
		if (FAILED(hr))
			return NULL;
		OutputDebugString(_T("D3D texture createed\n"));
		hr = pTex->GetSurfaceLevel(0, &pSurf);
		if (FAILED(hr))
			return NULL;
		OutputDebugString(_T("Surface interface obtained\n"));

		D3DXIMAGE_INFO d3dii;
		if (FAILED(D3DXLoadSurfaceFromFile(pSurf, NULL, NULL, tszFileName, NULL, D3DX_FILTER_NONE, 0, &d3dii)))
			return NULL;

		// Create a bitmap and copy the texture content onto it.
		int iDibWidth = d3dii.Width, iDibHeight = d3dii.Height;
		if (iDibWidth > 430) iDibWidth = 430;
		if (iDibHeight > 310) iDibHeight = 310;
		LPBYTE pDIBBits;
		BITMAPINFO bmi;
		bmi.bmiHeader.biSize = sizeof(bmi.bmiHeader);
		bmi.bmiHeader.biWidth = iDibWidth;
		bmi.bmiHeader.biHeight = iDibHeight;
		bmi.bmiHeader.biPlanes = 1;
		bmi.bmiHeader.biBitCount = 32;
		bmi.bmiHeader.biCompression = BI_RGB;
		bmi.bmiHeader.biSizeImage = 0;
		bmi.bmiHeader.biXPelsPerMeter = 0;
		bmi.bmiHeader.biYPelsPerMeter = 0;
		bmi.bmiHeader.biClrUsed = 0;
		bmi.bmiHeader.biClrImportant = 0;
		hDIB = CreateDIBSection(NULL, &bmi, DIB_RGB_COLORS, (LPVOID*)&pDIBBits, NULL, 0);
		if (!hDIB)
			return NULL;

		// Pre-process the pixel data based on alpha for AlphaBlend()
		D3DLOCKED_RECT lrc;
		pSurf->LockRect(&lrc, NULL, NULL);
		BYTE *pbData = (LPBYTE)lrc.pBits;
		{
			for (DWORD i = 0; i < 512 * 512; ++i)
			{
				BYTE bAlpha = pbData[i * 4 + 3];
				pbData[i * 4] = pbData[i * 4] * bAlpha / 255;
				pbData[i * 4 + 1] = pbData[i * 4 + 1] * bAlpha / 255;
				pbData[i * 4 + 2] = pbData[i * 4 + 2] * bAlpha / 255;
			}
		}
		pSurf->UnlockRect();

		// Lock the surface
		D3DLOCKED_RECT D3DRect;
		hr = pSurf->LockRect(&D3DRect, NULL, 0);
		if (FAILED(hr))
			return NULL;

		// Copy the bits
		// Note that the image is reversed in Y direction, so we need to re-reverse it.
		for (int y = 0; y < iDibHeight; ++y)
			CopyMemory(pDIBBits + ((iDibHeight-1-y) * iDibWidth * 4), (LPBYTE)D3DRect.pBits + (y * D3DRect.Pitch), iDibWidth * 4);

		// Unlock
		pSurf->UnlockRect();

		CBitmap *pbm = new CBitmap;
		if (!pbm) return NULL;
		pbm->m_hbm = hDIB;
		hDIB = NULL;
		pbm->FigureSize();

		return pbm;
	}
	__finally
	{
		if (hDIB) DeleteObject(hDIB);
		if (pSurf) pSurf->Release();
		if (pTex) pTex->Release();
		if (pD3DDev) pD3DDev->Release();
		if (pD3D) pD3D->Release();
	}
	return NULL;
}

CBitmap *CBitmap::CreateViaLoadImage(HINSTANCE hInst, LPCTSTR tszName, UINT uType, int cx, int cy, UINT fuLoad)
{
	if (fuLoad & LR_SHARED)
	{
		assert(0);
		return NULL;
	}

	CBitmap *pbm = new CBitmap;
	if (pbm == NULL)
		return NULL;
	
	HANDLE handle = ::LoadImage(hInst, tszName, uType, cx, cy, fuLoad);
	
	if (handle == NULL)
	{
		delete pbm;
		return NULL;
	}

	pbm->m_hbm = (HBITMAP)handle;

	pbm->FigureSize();

	return pbm;
}

BOOL CBitmap::FigureSize()
{
	BITMAP bm;

	if (0 == GetObject((HGDIOBJ)m_hbm, sizeof(BITMAP), (LPVOID)&bm))
		return FALSE;

	m_size.cx = abs(bm.bmWidth);
	m_size.cy = abs(bm.bmHeight);

	return m_bSizeKnown = TRUE;
}

CBitmap *CBitmap::StealToCreate(HBITMAP &refbm)
{
	if (refbm == NULL)
		return NULL;

	CBitmap *pbm = new CBitmap;
	if (pbm == NULL)
		return NULL;
	
	pbm->m_hbm = refbm;
	refbm = NULL;

	pbm->FigureSize();

	return pbm;
}

BOOL CBitmap::GetSize(SIZE *psize)
{
	if (m_hbm == NULL || !m_bSizeKnown || psize == NULL)
		return FALSE;

	*psize = m_size;
	return TRUE;
}

void CBitmap::AssumeSize(SIZE size)
{
	m_size = size;
	m_bSizeKnown = TRUE;  //m_hbm != NULL;
}

CBitmap *CBitmap::CreateResizedTo(SIZE size, HDC hDC, int iStretchMode, BOOL bStretch)
{
	CBitmap *pbm = new CBitmap;
	HDC hSrcDC = NULL;
	HDC hDestDC = NULL;
	HBITMAP hBitmap = NULL;
	HGDIOBJ hOldSrcBitmap = NULL, hOldDestBitmap = NULL;
	BOOL bRet = FALSE;
	int oldsm = 0;
	POINT brushorg;

	if (pbm == NULL || size.cx < 1 || size.cy < 1 || m_hbm == NULL || !m_bSizeKnown)
		goto error;

	hSrcDC = CreateAppropDC(hDC);
	hDestDC = CreateAppropDC(hDC);
	if (hSrcDC == NULL || hDestDC == NULL)
		goto error;

	hBitmap = CreateAppropBitmap(hDC, size.cx, size.cy);
	if (hBitmap == NULL)
		goto error;

	if (bStretch)
	{
		if (GetStretchBltMode(hDestDC) != iStretchMode)
		{
			if (iStretchMode == HALFTONE)
				GetBrushOrgEx(hDestDC, &brushorg);
			oldsm = SetStretchBltMode(hDestDC, iStretchMode);
			if (iStretchMode == HALFTONE)
				SetBrushOrgEx(hDestDC, brushorg.x, brushorg.y, NULL);
		}
	}

	hOldSrcBitmap = SelectObject(hSrcDC, m_hbm);
	hOldDestBitmap = SelectObject(hDestDC, hBitmap);
	if (bStretch)
		bRet = StretchBlt(hDestDC, 0, 0, size.cx, size.cy, hSrcDC, 0, 0, m_size.cx, m_size.cy, SRCCOPY);
	else
		bRet = BitBlt(hDestDC, 0, 0, size.cx, size.cy, hSrcDC, 0, 0, SRCCOPY);
	SelectObject(hDestDC, hOldDestBitmap);
	SelectObject(hSrcDC, hOldSrcBitmap);

	if (bStretch)
	{
		if (oldsm != 0)
		{
			if (oldsm == HALFTONE)
				GetBrushOrgEx(hDestDC, &brushorg);
			SetStretchBltMode(hDestDC, oldsm);
			if (oldsm == HALFTONE)
				SetBrushOrgEx(hDestDC, brushorg.x, brushorg.y, NULL);
		}
	}

	if (!bRet)
		goto error;

	pbm->m_hbm = hBitmap;
	hBitmap = NULL;
	pbm->AssumeSize(size);

	goto cleanup;
error:
	if (pbm != NULL)
		delete pbm;
	pbm = NULL;
cleanup:
	if (hBitmap != NULL)
		DeleteObject(hBitmap);
	if (hSrcDC != NULL)
		DeleteDC(hSrcDC);
	if (hDestDC != NULL)
		DeleteDC(hDestDC);

	return pbm;
}

HDC CBitmap::BeginPaintInto(HDC hCDC)
{
	if (m_hDCInto != NULL)
	{
		assert(0);
		return NULL;
	}

	m_hDCInto = CreateAppropDC(hCDC);
	if (m_hDCInto == NULL)
		return NULL;

	m_hOldBitmap = SelectObject(m_hDCInto, m_hbm);

	return m_hDCInto;
}

void CBitmap::EndPaintInto(HDC &hDC)
{
	if (hDC == NULL || hDC != m_hDCInto)
	{
		assert(0);
		return;
	}

	SelectObject(m_hDCInto, m_hOldBitmap);
	DeleteDC(m_hDCInto);
	m_hDCInto = NULL;
	hDC = NULL;
}

void CBitmap::PopOut()
{
	if (m_hDCInto == NULL)
	{
		assert(0);
		return;
	}

	SelectObject(m_hDCInto, m_hOldBitmap);
}

void CBitmap::PopIn()
{
	if (m_hDCInto == NULL)
	{
		assert(0);
		return;
	}

	m_hOldBitmap = SelectObject(m_hDCInto, m_hbm);
}

BOOL CBitmap::Draw(HDC hDC, POINT origin, SIZE crop, BOOL bAll)
{
	if (hDC == NULL || m_hbm == NULL)
		return FALSE;

	if (bAll && !m_bSizeKnown)
		return FALSE;

	if (bAll)
		crop = m_size;

	HDC hDCbm = CreateAppropDC(hDC);
	if (hDCbm == NULL)
		return FALSE;

	BOOL bPop = m_hDCInto != NULL;

	if (bPop)
		PopOut();

	HGDIOBJ hOldBitmap = SelectObject(hDCbm, m_hbm);
	BOOL bRet = BitBlt(hDC, origin.x, origin.y, crop.cx, crop.cy, hDCbm, 0, 0, SRCCOPY);
	SelectObject(hDCbm, hOldBitmap);
	DeleteDC(hDCbm);

	if (bPop)
		PopIn();

	return bRet;
}

BOOL CBitmap::Blend(HDC hDC, POINT origin, SIZE crop, BOOL bAll)
{
	if (hDC == NULL || m_hbm == NULL)
		return FALSE;

	if (bAll && !m_bSizeKnown)
		return FALSE;

	if (bAll)
		crop = m_size;

	HDC hDCbm = CreateAppropDC(hDC);
	if (hDCbm == NULL)
		return FALSE;

	BOOL bPop = m_hDCInto != NULL;

	if (bPop)
		PopOut();

#ifndef AC_SRC_ALPHA
#define AC_SRC_ALPHA AC_SRC_NO_PREMULT_ALPHA
#endif

	HGDIOBJ hOldBitmap = SelectObject(hDCbm, m_hbm);
	BOOL bRet;

	bRet = DI_AlphaBlend(hDC, origin.x, origin.y, crop.cx, crop.cy, hDCbm, 0, 0, m_size.cx, m_size.cy);
	SelectObject(hDCbm, hOldBitmap);
	DeleteDC(hDCbm);

	if (bPop)
		PopIn();

	return bRet;
}

CBitmap *CBitmap::Dup()
{	
	SIZE t;
	if (!GetSize(&t))
		return NULL;
	return CreateResizedTo(t, NULL, COLORONCOLOR, FALSE);
}

CBitmap *CBitmap::Create(SIZE size, HDC hCDC)
{
	CBitmap *pbm = new CBitmap;
	if (pbm == NULL)
		return NULL;

	pbm->m_hbm = CreateAppropBitmap(hCDC, size.cx, size.cy);
	if (pbm->m_hbm == NULL)
	{
		delete pbm;
		return NULL;
	}

	pbm->AssumeSize(size);

	return pbm;
}

CBitmap *CBitmap::Create(SIZE size, COLORREF color, HDC hCDC)
{
	CBitmap *pbm = Create(size, hCDC);
	if (pbm == NULL)
		return NULL;

	HDC hDC = pbm->BeginPaintInto();
	if (hDC == NULL)
	{
		delete pbm;
		return NULL;
	}
	
	HGDIOBJ hBrush = (HGDIOBJ)CreateSolidBrush(color),
		hOldBrush = SelectObject(hDC, hBrush);
	Rectangle(hDC, -1, -1, size.cx + 1, size.cy + 1);
	SelectObject(hDC, hOldBrush);
	DeleteObject(hBrush);

	pbm->EndPaintInto(hDC);

	return pbm;
}

BOOL CBitmap::Get(HDC hDC, POINT point)
{
	if (!m_bSizeKnown)
		return FALSE;
	return Get(hDC, point, m_size);
}

BOOL CBitmap::Get(HDC hDC, POINT point, SIZE size)
{
	if (m_hDCInto != NULL || hDC == NULL)
		return FALSE;

	HDC hDCInto = BeginPaintInto(hDC);
	if (hDCInto == NULL)
		return FALSE;

	BOOL bRet = BitBlt(hDCInto, 0, 0, size.cx, size.cy, hDC, point.x, point.y, SRCCOPY);
	
	EndPaintInto(hDCInto);

	return bRet;
}

CBitmap *CBitmap::CreateHorzGradient(const RECT &rect, COLORREF rgbLeft, COLORREF rgbRight)
{
	SIZE size = GetRectSize(rect);
	COLORREF rgbMid = RGB(
		(int(GetRValue(rgbLeft)) + int(GetRValue(rgbRight))) / 2,
		(int(GetGValue(rgbLeft)) + int(GetGValue(rgbRight))) / 2,
		(int(GetBValue(rgbLeft)) + int(GetBValue(rgbRight))) / 2);
	return Create(size, rgbMid);
}

BOOL CBitmap::MapToDC(HDC hDCTo, HDC hDCMapFrom)
{
	if (hDCTo == NULL || !m_bSizeKnown || m_hDCInto != NULL)
		return FALSE;

	HBITMAP hbm = CreateAppropBitmap(hDCTo, m_size.cx, m_size.cy);
	if (hbm == NULL)
		return FALSE;

	HDC hDCFrom = NULL;
	HDC hDCInto = NULL;
	HGDIOBJ hOld = NULL;
	BOOL bRet = FALSE;

	hDCFrom = BeginPaintInto(hDCMapFrom);
	if (!hDCFrom)
		goto cleanup;

	hDCInto = CreateCompatibleDC(hDCTo);
	if (!hDCInto)
		goto cleanup;

	hOld = SelectObject(hDCInto, (HGDIOBJ)hbm);
	bRet = BitBlt(hDCInto, 0, 0, m_size.cx, m_size.cy, hDCFrom, 0, 0, SRCCOPY);
	SelectObject(hDCInto, hOld);

cleanup:
	if (hDCFrom)
		EndPaintInto(hDCFrom);
	if (hDCInto)
		DeleteDC(hDCInto);
	if (bRet)
	{
		if (m_hbm)
			DeleteObject((HGDIOBJ)m_hbm);
		m_hbm = hbm;
		hbm = NULL;
	}
	if (hbm)
		DeleteObject((HGDIOBJ)hbm);

	return bRet;
}
