// This is a part of the Microsoft Foundation Classes C++ library.
// Copyright (C) 1992-1998 Microsoft Corporation
// All rights reserved.
//
// This source code is only intended as a supplement to the
// Microsoft Foundation Classes Reference and related
// electronic documentation provided with the library.
// See these sources for detailed information regarding the
// Microsoft Foundation Classes product.

#include "stdafx.h"

#ifdef AFX_CORE2_SEG
#pragma code_seg(AFX_CORE2_SEG)
#endif

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// Diagnostic Output
#ifdef _DEBUG
CDumpContext& AFXAPI operator<<(CDumpContext& dc, SIZE size)
{
	return dc << "(" << size.cx << " x " << size.cy << ")";
}

CDumpContext& AFXAPI operator<<(CDumpContext& dc, POINT point)
{
	return dc << "(" << point.x << ", " << point.y << ")";
}

CDumpContext& AFXAPI operator<<(CDumpContext& dc, const RECT& rect)
{
	return dc << "(L " << rect.left << ", T " << rect.top << ", R " <<
		rect.right << ", B " << rect.bottom << ")";
}
#endif //_DEBUG

/////////////////////////////////////////////////////////////////////////////
// CDC

CDC::CDC()
{
	m_hDC = NULL;
	m_hAttribDC = NULL;
	m_bPrinting = FALSE;
}

#ifdef _DEBUG
void CDC::AssertValid() const
{
	CObject::AssertValid();
}

void CDC::Dump(CDumpContext& dc) const
{
	CObject::Dump(dc);

	dc << "m_hDC = " << (UINT)m_hDC;
	dc << "\nm_hAttribDC = " << (UINT)m_hAttribDC;
	dc << "\nm_bPrinting = " << m_bPrinting;

	dc << "\n";
}
#endif //_DEBUG

#include "fixalloc.h"

class CTempDC : public CDC
{
	DECLARE_DYNCREATE(CTempDC)
	DECLARE_FIXED_ALLOC(CTempDC);
};

CHandleMap* PASCAL afxMapHDC(BOOL bCreate)
{
	AFX_MODULE_THREAD_STATE* pState = AfxGetModuleThreadState();
	if (pState->m_pmapHDC == NULL && bCreate)
	{
		BOOL bEnable = AfxEnableMemoryTracking(FALSE);
#ifndef _AFX_PORTABLE
		_PNH pnhOldHandler = AfxSetNewHandler(&AfxCriticalNewHandler);
#endif
		pState->m_pmapHDC = new CHandleMap(RUNTIME_CLASS(CTempDC),
			offsetof(CDC, m_hDC), 2);

#ifndef _AFX_PORTABLE
		AfxSetNewHandler(pnhOldHandler);
#endif
		AfxEnableMemoryTracking(bEnable);
	}
	return pState->m_pmapHDC;
}

CDC* PASCAL CDC::FromHandle(HDC hDC)
{
	CHandleMap* pMap = afxMapHDC(TRUE); //create map if not exist
	ASSERT(pMap != NULL);
	CDC* pDC = (CDC*)pMap->FromHandle(hDC);
	ASSERT(pDC == NULL || pDC->m_hDC == hDC);
	return pDC;
}

BOOL CDC::Attach(HDC hDC)
{
	ASSERT(m_hDC == NULL);      // only attach once, detach on destroy
	ASSERT(m_hAttribDC == NULL);    // only attach to an empty DC

	if (hDC == NULL)
		return FALSE;
	CHandleMap* pMap = afxMapHDC(TRUE); // create map if not exist
	ASSERT(pMap != NULL);
	pMap->SetPermanent(m_hDC = hDC, this);

	SetAttribDC(m_hDC);     // Default to same as output
	return TRUE;
}

HDC CDC::Detach()
{
	HDC hDC = m_hDC;
	if (hDC != NULL)
	{
		CHandleMap* pMap = afxMapHDC(); // don't create if not exist
		if (pMap != NULL)
			pMap->RemoveHandle(m_hDC);
	}

	ReleaseAttribDC();
	m_hDC = NULL;
	return hDC;
}

BOOL CDC::DeleteDC()
{
	if (m_hDC == NULL)
		return FALSE;

	return ::DeleteDC(Detach());
}

CDC::~CDC()
{
	if (m_hDC != NULL)
		::DeleteDC(Detach());
}


void CDC::SetAttribDC(HDC hDC)  // Set the Attribute DC
{
	m_hAttribDC = hDC;
}

void CDC::SetOutputDC(HDC hDC)  // Set the Output DC
{
#ifdef _DEBUG
	CHandleMap* pMap = afxMapHDC();
	if (pMap != NULL && pMap->LookupPermanent(m_hDC) == this)
	{
		TRACE0("Cannot Set Output hDC on Attached CDC.\n");
		ASSERT(FALSE);
	}
#endif
	m_hDC = hDC;
}

void CDC::ReleaseAttribDC()     // Release the Attribute DC
{
	m_hAttribDC = NULL;
}

void CDC::ReleaseOutputDC()     // Release the Output DC
{
#ifdef _DEBUG
	CHandleMap* pMap = afxMapHDC();
	if (pMap != NULL && pMap->LookupPermanent(m_hDC) == this)
	{
		TRACE0("Cannot Release Output hDC on Attached CDC.\n");
		ASSERT(FALSE);
	}
#endif
	m_hDC = NULL;
}

/////////////////////////////////////////////////////////////////////////////
// Out-of-line routines

int CDC::StartDoc(LPCTSTR lpszDocName)
{
	DOCINFO di;
	memset(&di, 0, sizeof(DOCINFO));
	di.cbSize = sizeof(DOCINFO);
	di.lpszDocName = lpszDocName;
	return StartDoc(&di);
}

int CDC::SaveDC()
{
	ASSERT(m_hDC != NULL);
	int nRetVal = 0;
	if (m_hAttribDC != NULL)
		nRetVal = ::SaveDC(m_hAttribDC);
	if (m_hDC != m_hAttribDC && ::SaveDC(m_hDC) != 0)
		nRetVal = -1;   // -1 is the only valid restore value for complex DCs
	return nRetVal;
}

BOOL CDC::RestoreDC(int nSavedDC)
{
	// if two distinct DCs, nSavedDC can only be -1
	ASSERT(m_hDC != NULL);
	ASSERT(m_hDC == m_hAttribDC || nSavedDC == -1);

	BOOL bRetVal = TRUE;
	if (m_hDC != m_hAttribDC)
		bRetVal = ::RestoreDC(m_hDC, nSavedDC);
	if (m_hAttribDC != NULL)
		bRetVal = (bRetVal && ::RestoreDC(m_hAttribDC, nSavedDC));
	return bRetVal;
}

CGdiObject* PASCAL CDC::SelectGdiObject(HDC hDC, HGDIOBJ h)
{
	return CGdiObject::FromHandle(::SelectObject(hDC, h));
}

CGdiObject* CDC::SelectStockObject(int nIndex)
{
	ASSERT(m_hDC != NULL);

	HGDIOBJ hObject = ::GetStockObject(nIndex);
	HGDIOBJ hOldObj = NULL;

	ASSERT(hObject != NULL);
	if (m_hDC != m_hAttribDC)
		hOldObj = ::SelectObject(m_hDC, hObject);
	if (m_hAttribDC != NULL)
		hOldObj = ::SelectObject(m_hAttribDC, hObject);
	return CGdiObject::FromHandle(hOldObj);
}

CPen* CDC::SelectObject(CPen* pPen)
{
	ASSERT(m_hDC != NULL);
	HGDIOBJ hOldObj = NULL;

	if (m_hDC != m_hAttribDC)
		hOldObj = ::SelectObject(m_hDC, pPen->GetSafeHandle());
	if (m_hAttribDC != NULL)
		hOldObj = ::SelectObject(m_hAttribDC, pPen->GetSafeHandle());
	return (CPen*)CGdiObject::FromHandle(hOldObj);
}

CBrush* CDC::SelectObject(CBrush* pBrush)
{
	ASSERT(m_hDC != NULL);
	HGDIOBJ hOldObj = NULL;

	if (m_hDC != m_hAttribDC)
		hOldObj = ::SelectObject(m_hDC, pBrush->GetSafeHandle());
	if (m_hAttribDC != NULL)
		hOldObj = ::SelectObject(m_hAttribDC, pBrush->GetSafeHandle());
	return (CBrush*)CGdiObject::FromHandle(hOldObj);
}

CFont* CDC::SelectObject(CFont* pFont)
{
	ASSERT(m_hDC != NULL);
	HGDIOBJ hOldObj = NULL;

	if (m_hDC != m_hAttribDC)
		hOldObj = ::SelectObject(m_hDC, pFont->GetSafeHandle());
	if (m_hAttribDC != NULL)
		hOldObj = ::SelectObject(m_hAttribDC, pFont->GetSafeHandle());
	return (CFont*)CGdiObject::FromHandle(hOldObj);
}

int CDC::SelectObject(CRgn* pRgn)
{
	ASSERT(m_hDC != NULL);
	int nRetVal = GDI_ERROR;

	if (m_hDC != m_hAttribDC)
		nRetVal = (int)::SelectObject(m_hDC, pRgn->GetSafeHandle());
	if (m_hAttribDC != NULL)
		nRetVal = (int)::SelectObject(m_hAttribDC, pRgn->GetSafeHandle());
	return nRetVal;
}

CPalette* CDC::SelectPalette(CPalette* pPalette, BOOL bForceBackground)
{
	ASSERT(m_hDC != NULL);

	return (CPalette*) CGdiObject::FromHandle(::SelectPalette(m_hDC,
		(HPALETTE)pPalette->GetSafeHandle(), bForceBackground));
}

COLORREF CDC::SetBkColor(COLORREF crColor)
{
	ASSERT(m_hDC != NULL);
	COLORREF crRetVal = CLR_INVALID;

	if (m_hDC != m_hAttribDC)
		crRetVal = ::SetBkColor(m_hDC, crColor);
	if (m_hAttribDC != NULL)
		crRetVal = ::SetBkColor(m_hAttribDC, crColor);
	return crRetVal;
}

int CDC::SetBkMode(int nBkMode)
{
	ASSERT(m_hDC != NULL);
	int nRetVal = 0;

	if (m_hDC != m_hAttribDC)
		nRetVal = ::SetBkMode(m_hDC, nBkMode);
	if (m_hAttribDC != NULL)
		nRetVal = ::SetBkMode(m_hAttribDC, nBkMode);
	return nRetVal;
}

int CDC::SetPolyFillMode(int nPolyFillMode)
{
	ASSERT(m_hDC != NULL);
	int nRetVal = 0;

	if (m_hDC != m_hAttribDC)
		nRetVal = ::SetPolyFillMode(m_hDC, nPolyFillMode);
	if (m_hAttribDC != NULL)
		nRetVal = ::SetPolyFillMode(m_hAttribDC, nPolyFillMode);
	return nRetVal;
}

int CDC::SetROP2(int nDrawMode)
{
	ASSERT(m_hDC != NULL);
	int nRetVal = 0;

	if (m_hDC != m_hAttribDC)
		nRetVal = ::SetROP2(m_hDC, nDrawMode);
	if (m_hAttribDC != NULL)
		nRetVal = ::SetROP2(m_hAttribDC, nDrawMode);
	return nRetVal;
}

int CDC::SetStretchBltMode(int nStretchMode)
{
	ASSERT(m_hDC != NULL);
	int nRetVal = 0;

	if (m_hDC != m_hAttribDC)
		nRetVal = ::SetStretchBltMode(m_hDC, nStretchMode);
	if (m_hAttribDC != NULL)
		nRetVal = ::SetStretchBltMode(m_hAttribDC, nStretchMode);
	return nRetVal;
}

COLORREF CDC::SetTextColor(COLORREF crColor)
{
	ASSERT(m_hDC != NULL);
	COLORREF crRetVal = CLR_INVALID;

	if (m_hDC != m_hAttribDC)
		crRetVal = ::SetTextColor(m_hDC, crColor);
	if (m_hAttribDC != NULL)
		crRetVal = ::SetTextColor(m_hAttribDC, crColor);
	return crRetVal;
}

int CDC::SetMapMode(int nMapMode)
{
	ASSERT(m_hDC != NULL);
	int nRetVal = 0;

	if (m_hDC != m_hAttribDC)
		nRetVal = ::SetMapMode(m_hDC, nMapMode);
	if (m_hAttribDC != NULL)
		nRetVal = ::SetMapMode(m_hAttribDC, nMapMode);
	return nRetVal;
}

CPoint CDC::SetViewportOrg(int x, int y)
{
	ASSERT(m_hDC != NULL);
	CPoint point;

	if (m_hDC != m_hAttribDC)
		VERIFY(::SetViewportOrgEx(m_hDC, x, y, &point));
	if (m_hAttribDC != NULL)
		VERIFY(::SetViewportOrgEx(m_hAttribDC, x, y, &point));
	return point;
}

CPoint CDC::OffsetViewportOrg(int nWidth, int nHeight)
{
	ASSERT(m_hDC != NULL);
	CPoint point;

	if (m_hDC != m_hAttribDC)
		VERIFY(::OffsetViewportOrgEx(m_hDC, nWidth, nHeight, &point));
	if (m_hAttribDC != NULL)
		VERIFY(::OffsetViewportOrgEx(m_hAttribDC, nWidth, nHeight, &point));
	return point;
}

CSize CDC::SetViewportExt(int x, int y)
{
	ASSERT(m_hDC != NULL);
	CSize size;

	if (m_hDC != m_hAttribDC)
		VERIFY(::SetViewportExtEx(m_hDC, x, y, &size));
	if (m_hAttribDC != NULL)
		VERIFY(::SetViewportExtEx(m_hAttribDC, x, y, &size));
	return size;
}

CSize CDC::ScaleViewportExt(int xNum, int xDenom, int yNum, int yDenom)
{
	ASSERT(m_hDC != NULL);
	CSize size;

	if (m_hDC != m_hAttribDC)
		VERIFY(::ScaleViewportExtEx(m_hDC, xNum, xDenom, yNum, yDenom, &size));
	if (m_hAttribDC != NULL)
		VERIFY(::ScaleViewportExtEx(m_hAttribDC, xNum, xDenom, yNum, yDenom, &size));
	return size;
}

CPoint CDC::SetWindowOrg(int x, int y)
{
	ASSERT(m_hDC != NULL);
	CPoint point;

	if (m_hDC != m_hAttribDC)
		VERIFY(::SetWindowOrgEx(m_hDC, x, y, &point));
	if (m_hAttribDC != NULL)
		VERIFY(::SetWindowOrgEx(m_hAttribDC, x, y, &point));
	return point;
}

CPoint CDC::OffsetWindowOrg(int nWidth, int nHeight)
{
	ASSERT(m_hDC != NULL);
	CPoint point;

	if (m_hDC != m_hAttribDC)
		VERIFY(::OffsetWindowOrgEx(m_hDC, nWidth, nHeight, &point));
	if (m_hAttribDC != NULL)
		VERIFY(::OffsetWindowOrgEx(m_hAttribDC, nWidth, nHeight, &point));
	return point;
}

CSize CDC::SetWindowExt(int x, int y)
{
	ASSERT(m_hDC != NULL);
	CSize size;

	if (m_hDC != m_hAttribDC)
		VERIFY(::SetWindowExtEx(m_hDC, x, y, &size));
	if (m_hAttribDC != NULL)
		VERIFY(::SetWindowExtEx(m_hAttribDC, x, y, &size));
	return size;
}

CSize CDC::ScaleWindowExt(int xNum, int xDenom, int yNum, int yDenom)
{
	ASSERT(m_hDC != NULL);
	CSize size;

	if (m_hDC != m_hAttribDC)
		VERIFY(::ScaleWindowExtEx(m_hDC, xNum, xDenom, yNum, yDenom, &size));
	if (m_hAttribDC != NULL)
		VERIFY(::ScaleWindowExtEx(m_hAttribDC, xNum, xDenom, yNum, yDenom, &size));
	return size;
}

int CDC::GetClipBox(LPRECT lpRect) const
{
	ASSERT(m_hDC != NULL);
	return ::GetClipBox(m_hDC, lpRect);
}

int CDC::SelectClipRgn(CRgn* pRgn)
{
	ASSERT(m_hDC != NULL);
	int nRetVal = ERROR;

	if (m_hDC != m_hAttribDC)
		nRetVal = ::SelectClipRgn(m_hDC, (HRGN)pRgn->GetSafeHandle());
	if (m_hAttribDC != NULL)
		nRetVal = ::SelectClipRgn(m_hAttribDC, (HRGN)pRgn->GetSafeHandle());
	return nRetVal;
}

int CDC::ExcludeClipRect(int x1, int y1, int x2, int y2)
{
	ASSERT(m_hDC != NULL);
	int nRetVal = ERROR;

	if (m_hDC != m_hAttribDC)
		nRetVal = ::ExcludeClipRect(m_hDC, x1, y1, x2, y2);
	if (m_hAttribDC != NULL)
		nRetVal = ::ExcludeClipRect(m_hAttribDC, x1, y1, x2, y2);
	return nRetVal;
}

int CDC::ExcludeClipRect(LPCRECT lpRect)
{
	ASSERT(m_hDC != NULL);
	int nRetVal = ERROR;

	if (m_hDC != m_hAttribDC)
		nRetVal = ::ExcludeClipRect(m_hDC, lpRect->left, lpRect->top,
			lpRect->right, lpRect->bottom);
	if (m_hAttribDC != NULL)
		nRetVal = ::ExcludeClipRect(m_hAttribDC, lpRect->left, lpRect->top,
			lpRect->right, lpRect->bottom);
	return nRetVal;
}

int CDC::IntersectClipRect(int x1, int y1, int x2, int y2)
{
	ASSERT(m_hDC != NULL);
	int nRetVal = ERROR;

	if (m_hDC != m_hAttribDC)
		nRetVal = ::IntersectClipRect(m_hDC, x1, y1, x2, y2);
	if (m_hAttribDC != NULL)
		nRetVal = ::IntersectClipRect(m_hAttribDC, x1, y1, x2, y2);
	return nRetVal;
}

int CDC::IntersectClipRect(LPCRECT lpRect)
{
	ASSERT(m_hDC != NULL);
	int nRetVal = ERROR;

	if (m_hDC != m_hAttribDC)
		nRetVal = ::IntersectClipRect(m_hDC, lpRect->left, lpRect->top,
			lpRect->right, lpRect->bottom);
	if (m_hAttribDC != NULL)
		nRetVal = ::IntersectClipRect(m_hAttribDC, lpRect->left, lpRect->top,
			lpRect->right, lpRect->bottom);
	return nRetVal;
}

int CDC::OffsetClipRgn(int x, int y)
{
	ASSERT(m_hDC != NULL);
	int nRetVal = ERROR;

	if (m_hDC != m_hAttribDC)
		nRetVal = ::OffsetClipRgn(m_hDC, x, y);
	if (m_hAttribDC != NULL)
		nRetVal = ::OffsetClipRgn(m_hAttribDC, x, y);
	return nRetVal;
}

int CDC::OffsetClipRgn(SIZE size)
{
	ASSERT(m_hDC != NULL);
	int nRetVal = ERROR;

	if (m_hDC != m_hAttribDC)
		nRetVal = ::OffsetClipRgn(m_hDC, size.cx, size.cy);
	if (m_hAttribDC != NULL)
		nRetVal = ::OffsetClipRgn(m_hAttribDC, size.cx, size.cy);
	return nRetVal;
}

CPoint CDC::MoveTo(int x, int y)
{
	ASSERT(m_hDC != NULL);
	CPoint point;

	if (m_hDC != m_hAttribDC)
		VERIFY(::MoveToEx(m_hDC, x, y, &point));
	if (m_hAttribDC != NULL)
		VERIFY(::MoveToEx(m_hAttribDC, x, y, &point));
	return point;
}

BOOL CDC::LineTo(int x, int y)
{
	ASSERT(m_hDC != NULL);
	if (m_hAttribDC != NULL && m_hDC != m_hAttribDC)
		::MoveToEx(m_hAttribDC, x, y, NULL);
	return ::LineTo(m_hDC, x, y);
}

UINT CDC::SetTextAlign(UINT nFlags)
{
	ASSERT(m_hDC != NULL);
	UINT nRetVal = GDI_ERROR;

	if (m_hDC != m_hAttribDC)
		::SetTextAlign(m_hDC, nFlags);
	if (m_hAttribDC != NULL)
		nRetVal = ::SetTextAlign(m_hAttribDC, nFlags);
	return nRetVal;
}

int CDC::SetTextJustification(int nBreakExtra, int nBreakCount)
{
	ASSERT(m_hDC != NULL);
	int nRetVal = 0;

	if (m_hDC != m_hAttribDC)
		nRetVal = ::SetTextJustification(m_hDC, nBreakExtra, nBreakCount);
	if (m_hAttribDC != NULL)
		nRetVal = ::SetTextJustification(m_hAttribDC, nBreakExtra, nBreakCount);
	return nRetVal;
}

int CDC::SetTextCharacterExtra(int nCharExtra)
{
	ASSERT(m_hDC != NULL);
	int nRetVal = 0x8000000;
	if (m_hDC != m_hAttribDC)
		nRetVal = ::SetTextCharacterExtra(m_hDC, nCharExtra);
	if (m_hAttribDC != NULL)
		nRetVal = ::SetTextCharacterExtra(m_hAttribDC, nCharExtra);
	return nRetVal;
}

DWORD CDC::SetMapperFlags(DWORD dwFlag)
{
	ASSERT(m_hDC != NULL);
	DWORD dwRetVal = GDI_ERROR;
	if (m_hDC != m_hAttribDC)
		dwRetVal = ::SetMapperFlags(m_hDC, dwFlag);
	if (m_hAttribDC != NULL)
		dwRetVal = ::SetMapperFlags(m_hAttribDC, dwFlag);
	return dwRetVal;
}

typedef DWORD (CALLBACK* AFX_GDIGETLAYOUTPROC)(HDC);
typedef DWORD (CALLBACK* AFX_GDISETLAYOUTPROC)(HDC, DWORD);

DWORD CDC::GetLayout() const
{
	ASSERT(m_hDC != NULL);

	HINSTANCE hInst = ::GetModuleHandleA("GDI32.DLL");
	ASSERT(hInst != NULL);

	DWORD dwGetLayout = LAYOUT_LTR;

	AFX_GDIGETLAYOUTPROC pfn;
	pfn = (AFX_GDIGETLAYOUTPROC) GetProcAddress(hInst, "GetLayout");

	// if they API is available, just call it. If it is not
	// available, indicate an error.

	if (pfn != NULL)
		dwGetLayout = (*pfn)(m_hDC);
	else
	{
		dwGetLayout = GDI_ERROR;
		SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
	}

	return dwGetLayout;
}

DWORD CDC::SetLayout(DWORD dwSetLayout)
{
	ASSERT(m_hDC != NULL);

	HINSTANCE hInst = ::GetModuleHandleA("GDI32.DLL");
	ASSERT(hInst != NULL);

	DWORD dwGetLayout = LAYOUT_LTR;

	AFX_GDISETLAYOUTPROC pfn;
	pfn = (AFX_GDISETLAYOUTPROC) GetProcAddress(hInst, "SetLayout");

	// If the API is availalbe, just call it. If it's not available,
	// setting anything other than LAYOUT_LTR is an error.

	if (pfn != NULL)
		dwGetLayout = (*pfn)(m_hDC, dwSetLayout);
	else if (dwSetLayout != LAYOUT_LTR)
	{
		dwGetLayout = GDI_ERROR;
		SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
	}

	return dwGetLayout;
}

void CWnd::ScreenToClient(LPRECT lpRect) const
{
	ASSERT(::IsWindow(m_hWnd));
	::ScreenToClient(m_hWnd, (LPPOINT)lpRect);
	::ScreenToClient(m_hWnd, ((LPPOINT)lpRect)+1);
	if (GetExStyle() & WS_EX_LAYOUTRTL)
		CRect::SwapLeftRight(lpRect);
}

void CWnd::ClientToScreen(LPRECT lpRect) const
{
	ASSERT(::IsWindow(m_hWnd));
	::ClientToScreen(m_hWnd, (LPPOINT)lpRect);
	::ClientToScreen(m_hWnd, ((LPPOINT)lpRect)+1);
	if (GetExStyle() & WS_EX_LAYOUTRTL)
		CRect::SwapLeftRight(lpRect);
}

/////////////////////////////////////////////////////////////////////////////
// Advanced Win32 GDI functions

BOOL CDC::ArcTo(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4)
{
	ASSERT(m_hDC != NULL);
	BOOL bResult = ::ArcTo(m_hDC, x1, y1, x2, y2, x3, y3, x4, y4);
	if (m_hDC != m_hAttribDC)
	{
		CPoint pt;
		VERIFY(::GetCurrentPositionEx(m_hDC, &pt));
		VERIFY(::MoveToEx(m_hAttribDC, pt.x, pt.y, NULL));
	}
	return bResult;
}

int CDC::SetArcDirection(int nArcDirection)
{
	ASSERT(m_hDC != NULL);
	int nResult = 0;
	if (m_hDC != m_hAttribDC)
		nResult = ::SetArcDirection(m_hDC, nArcDirection);
	if (m_hAttribDC != NULL)
		nResult = ::SetArcDirection(m_hAttribDC, nArcDirection);
	return nResult;
}

BOOL CDC::PolyDraw(const POINT* lpPoints, const BYTE* lpTypes, int nCount)
{
	ASSERT(m_hDC != NULL);
	BOOL bResult = ::PolyDraw(m_hDC, lpPoints, lpTypes, nCount);
	if (m_hDC != m_hAttribDC)
	{
		CPoint pt;
		VERIFY(::GetCurrentPositionEx(m_hDC, &pt));
		VERIFY(::MoveToEx(m_hAttribDC, pt.x, pt.y, NULL));
	}
	return bResult;
}

BOOL CDC::PolylineTo(const POINT* lpPoints, int nCount)
{
	ASSERT(m_hDC != NULL);
	BOOL bResult = ::PolylineTo(m_hDC, lpPoints, nCount);
	if (m_hDC != m_hAttribDC)
	{
		CPoint pt;
		VERIFY(::GetCurrentPositionEx(m_hDC, &pt));
		VERIFY(::MoveToEx(m_hAttribDC, pt.x, pt.y, NULL));
	}
	return bResult;
}

BOOL CDC::SetColorAdjustment(const COLORADJUSTMENT* lpColorAdjust)
{
	ASSERT(m_hDC != NULL);
	BOOL bResult = FALSE;
	if (m_hDC != m_hAttribDC)
		bResult = ::SetColorAdjustment(m_hDC, lpColorAdjust);
	if (m_hAttribDC != NULL)
		bResult = ::SetColorAdjustment(m_hAttribDC, lpColorAdjust);
	return bResult;
}

BOOL CDC::PolyBezierTo(const POINT* lpPoints, int nCount)
{
	ASSERT(m_hDC != NULL);
	BOOL bResult = ::PolyBezierTo(m_hDC, lpPoints, nCount);
	if (m_hDC != m_hAttribDC)
	{
		CPoint pt;
		VERIFY(::GetCurrentPositionEx(m_hDC, &pt));
		VERIFY(::MoveToEx(m_hAttribDC, pt.x, pt.y, NULL));
	}
	return bResult;
}

BOOL CDC::SelectClipPath(int nMode)
{
	ASSERT(m_hDC != NULL);

	// output DC always holds the current path
	if (!::SelectClipPath(m_hDC, nMode))
		return FALSE;

	// transfer clipping region into the attribute DC
	BOOL bResult = TRUE;
	if (m_hDC != m_hAttribDC)
	{
		HRGN hRgn = ::CreateRectRgn(0, 0, 0, 0);
		if (::GetClipRgn(m_hDC, hRgn) < 0 || !::SelectClipRgn(m_hAttribDC, hRgn))
		{
			TRACE0("Error: unable to transfer clip region in CDC::SelectClipPath!\n");
			bResult = FALSE;
		}
		DeleteObject(hRgn);
	}
	return bResult;
}

int CDC::SelectClipRgn(CRgn* pRgn, int nMode)
{
	ASSERT(m_hDC != NULL);
	int nRetVal = ERROR;
	if (m_hDC != m_hAttribDC)
		nRetVal = ::ExtSelectClipRgn(m_hDC, (HRGN)pRgn->GetSafeHandle(), nMode);
	if (m_hAttribDC != NULL)
		nRetVal = ::ExtSelectClipRgn(m_hAttribDC, (HRGN)pRgn->GetSafeHandle(), nMode);
	return nRetVal;
}

/////////////////////////////////////////////////////////////////////////////
// Special handling for metafile playback

int CALLBACK AfxEnumMetaFileProc(HDC hDC,
	HANDLETABLE* pHandleTable, METARECORD* pMetaRec, int nHandles, LPARAM lParam)
{
	CDC* pDC = (CDC*)lParam;
	ASSERT_VALID(pDC);

	switch (pMetaRec->rdFunction)
	{
	// these records have effects different for each CDC derived class
	case META_SETMAPMODE:
		pDC->SetMapMode((int)(short)pMetaRec->rdParm[0]);
		break;
	case META_SETWINDOWEXT:
		pDC->SetWindowExt(
			(int)(short)pMetaRec->rdParm[1], (int)(short)pMetaRec->rdParm[0]);
		break;
	case META_SETWINDOWORG:
		pDC->SetWindowOrg(
			(int)(short)pMetaRec->rdParm[1], (int)(short)pMetaRec->rdParm[0]);
		break;
	case META_SETVIEWPORTEXT:
		pDC->SetViewportExt(
			(int)(short)pMetaRec->rdParm[1], (int)(short)pMetaRec->rdParm[0]);
		break;
	case META_SETVIEWPORTORG:
		pDC->SetViewportOrg(
			(int)(short)pMetaRec->rdParm[1], (int)(short)pMetaRec->rdParm[0]);
		break;
	case META_SCALEWINDOWEXT:
		pDC->ScaleWindowExt(
			(int)(short)pMetaRec->rdParm[3], (int)(short)pMetaRec->rdParm[2],
			(int)(short)pMetaRec->rdParm[1], (int)(short)pMetaRec->rdParm[0]);
		break;
	case META_SCALEVIEWPORTEXT:
		pDC->ScaleViewportExt(
			(int)(short)pMetaRec->rdParm[3], (int)(short)pMetaRec->rdParm[2],
			(int)(short)pMetaRec->rdParm[1], (int)(short)pMetaRec->rdParm[0]);
		break;
	case META_OFFSETVIEWPORTORG:
		pDC->OffsetViewportOrg(
			(int)(short)pMetaRec->rdParm[1], (int)(short)pMetaRec->rdParm[0]);
		break;
	case META_SAVEDC:
		pDC->SaveDC();
		break;
	case META_RESTOREDC:
		pDC->RestoreDC((int)(short)pMetaRec->rdParm[0]);
		break;
	case META_SETBKCOLOR:
		pDC->SetBkColor(*(UNALIGNED COLORREF*)&pMetaRec->rdParm[0]);
		break;
	case META_SETTEXTCOLOR:
		pDC->SetTextColor(*(UNALIGNED COLORREF*)&pMetaRec->rdParm[0]);
		break;

	// need to watch out for SelectObject(HFONT), for custom font mapping
	case META_SELECTOBJECT:
		{
			HGDIOBJ hObject = pHandleTable->objectHandle[pMetaRec->rdParm[0]];
			UINT nObjType = GetObjectType(hObject);
			if (nObjType == 0)
			{
				// object type is unknown, determine if it is a font
				HFONT hStockFont = (HFONT)::GetStockObject(SYSTEM_FONT);
				HFONT hFontOld = (HFONT)::SelectObject(pDC->m_hDC, hStockFont);
				HGDIOBJ hObjOld = ::SelectObject(pDC->m_hDC, hObject);
				if (hObjOld == hStockFont)
				{
					// got the stock object back, so must be selecting a font
					pDC->SelectObject(CFont::FromHandle((HFONT)hObject));
					break;  // don't play the default record
				}
				else
				{
					// didn't get the stock object back, so restore everything
					::SelectObject(pDC->m_hDC, hFontOld);
					::SelectObject(pDC->m_hDC, hObjOld);
				}
				// and fall through to PlayMetaFileRecord...
			}
			else if (nObjType == OBJ_FONT)
			{
				// play back as CDC::SelectObject(CFont*)
				pDC->SelectObject(CFont::FromHandle((HFONT)hObject));
				break;  // don't play the default record
			}
		}
		// fall through...

	default:
		::PlayMetaFileRecord(hDC, pHandleTable, pMetaRec, nHandles);
		break;
	}

	return 1;
}

BOOL CDC::PlayMetaFile(HMETAFILE hMF)
{
	if (::GetDeviceCaps(m_hDC, TECHNOLOGY) == DT_METAFILE)
	{
		// playing metafile in metafile, just use core windows API
		return ::PlayMetaFile(m_hDC, hMF);
	}

	// for special playback, lParam == pDC
	return ::EnumMetaFile(m_hDC, hMF, AfxEnumMetaFileProc, (LPARAM)this);
}

/////////////////////////////////////////////////////////////////////////////
// Coordinate transforms

void CDC::LPtoDP(LPSIZE lpSize) const
{
	ASSERT(AfxIsValidAddress(lpSize, sizeof(SIZE)));

	CSize sizeWinExt = GetWindowExt();
	CSize sizeVpExt = GetViewportExt();
	lpSize->cx = MulDiv(lpSize->cx, abs(sizeVpExt.cx), abs(sizeWinExt.cx));
	lpSize->cy = MulDiv(lpSize->cy, abs(sizeVpExt.cy), abs(sizeWinExt.cy));
}

void CDC::DPtoLP(LPSIZE lpSize) const
{
	ASSERT(AfxIsValidAddress(lpSize, sizeof(SIZE)));

	CSize sizeWinExt = GetWindowExt();
	CSize sizeVpExt = GetViewportExt();
	lpSize->cx = MulDiv(lpSize->cx, abs(sizeWinExt.cx), abs(sizeVpExt.cx));
	lpSize->cy = MulDiv(lpSize->cy, abs(sizeWinExt.cy), abs(sizeVpExt.cy));
}

/////////////////////////////////////////////////////////////////////////////
// Helper DCs

#ifdef _DEBUG
void CClientDC::AssertValid() const
{
	CDC::AssertValid();
	ASSERT(m_hWnd == NULL || ::IsWindow(m_hWnd));
}

void CClientDC::Dump(CDumpContext& dc) const
{
	CDC::Dump(dc);

	dc << "m_hWnd = " << (UINT)m_hWnd;
	dc << "\n";
}
#endif

CClientDC::CClientDC(CWnd* pWnd)
{
	ASSERT(pWnd == NULL || ::IsWindow(pWnd->m_hWnd));

	if (!Attach(::GetDC(m_hWnd = pWnd->GetSafeHwnd())))
		AfxThrowResourceException();
}

CClientDC::~CClientDC()
{
	ASSERT(m_hDC != NULL);
	::ReleaseDC(m_hWnd, Detach());
}

#ifdef _DEBUG
void CWindowDC::AssertValid() const
{
	CDC::AssertValid();
	ASSERT(m_hWnd == NULL || ::IsWindow(m_hWnd));
}

void CWindowDC::Dump(CDumpContext& dc) const
{
	CDC::Dump(dc);

	dc << "m_hWnd = " << (UINT)m_hWnd;
	dc << "\n";
}
#endif

CWindowDC::CWindowDC(CWnd* pWnd)
{
	ASSERT(pWnd == NULL || ::IsWindow(pWnd->m_hWnd));

	if (!Attach(::GetWindowDC(m_hWnd = pWnd->GetSafeHwnd())))
		AfxThrowResourceException();
}

CWindowDC::~CWindowDC()
{
	ASSERT(m_hDC != NULL);
	::ReleaseDC(m_hWnd, Detach());
}

#ifdef _DEBUG
void CPaintDC::AssertValid() const
{
	CDC::AssertValid();
	ASSERT(::IsWindow(m_hWnd));
}

void CPaintDC::Dump(CDumpContext& dc) const
{
	CDC::Dump(dc);

	dc << "m_hWnd = " << (UINT)m_hWnd;
	dc << "\nm_ps.hdc = " << (UINT)m_ps.hdc;
	dc << "\nm_ps.fErase = " << m_ps.fErase;
	dc << "\nm_ps.rcPaint = " << (CRect)m_ps.rcPaint;

	dc << "\n";
}
#endif

CPaintDC::CPaintDC(CWnd* pWnd)
{
	ASSERT_VALID(pWnd);
	ASSERT(::IsWindow(pWnd->m_hWnd));

	if (!Attach(::BeginPaint(m_hWnd = pWnd->m_hWnd, &m_ps)))
		AfxThrowResourceException();
}

CPaintDC::~CPaintDC()
{
	ASSERT(m_hDC != NULL);
	ASSERT(::IsWindow(m_hWnd));

	::EndPaint(m_hWnd, &m_ps);
	Detach();
}

/////////////////////////////////////////////////////////////////////////////
// CGdiObject

#ifdef _DEBUG
void CGdiObject::Dump(CDumpContext& dc) const
{
	CObject::Dump(dc);

	dc << "m_hObject = " << (UINT)m_hObject;
	dc << "\n";
}

void CGdiObject::AssertValid() const
{
	CObject::AssertValid();
	ASSERT(m_hObject == NULL ||
		(afxData.bWin95 || ::GetObjectType(m_hObject) != 0));
}
#endif

#include "fixalloc.h"

class CTempGdiObject : public CGdiObject
{
	DECLARE_DYNCREATE(CTempGdiObject)
	DECLARE_FIXED_ALLOC(CTempGdiObject);
};

CHandleMap* PASCAL afxMapHGDIOBJ(BOOL bCreate)
{
	AFX_MODULE_THREAD_STATE* pState = AfxGetModuleThreadState();
	if (pState->m_pmapHGDIOBJ == NULL && bCreate)
	{
		BOOL bEnable = AfxEnableMemoryTracking(FALSE);
#ifndef _AFX_PORTABLE
		_PNH pnhOldHandler = AfxSetNewHandler(&AfxCriticalNewHandler);
#endif
		pState->m_pmapHGDIOBJ = new CHandleMap(RUNTIME_CLASS(CTempGdiObject),
			offsetof(CGdiObject, m_hObject));

#ifndef _AFX_PORTABLE
		AfxSetNewHandler(pnhOldHandler);
#endif
		AfxEnableMemoryTracking(bEnable);
	}
	return pState->m_pmapHGDIOBJ;
}

CGdiObject* PASCAL CGdiObject::FromHandle(HGDIOBJ h)
{
	CHandleMap* pMap = afxMapHGDIOBJ(TRUE); //create map if not exist
	ASSERT(pMap != NULL);
	CGdiObject* pObject = (CGdiObject*)pMap->FromHandle(h);
	ASSERT(pObject == NULL || pObject->m_hObject == h);
	return pObject;
}

BOOL CGdiObject::Attach(HGDIOBJ hObject)
{
	ASSERT(m_hObject == NULL);      // only attach once, detach on destroy
	if (hObject == NULL)
		return FALSE;
	CHandleMap* pMap = afxMapHGDIOBJ(TRUE); // create map if not exist
	ASSERT(pMap != NULL);
	pMap->SetPermanent(m_hObject = hObject, this);
	return TRUE;
}

HGDIOBJ CGdiObject::Detach()
{
	HGDIOBJ hObject = m_hObject;
	if (hObject != NULL)
	{
		CHandleMap* pMap = afxMapHGDIOBJ(); // don't create if not exist
		if (pMap != NULL)
			pMap->RemoveHandle(m_hObject);
	}

	m_hObject = NULL;
	return hObject;
}

BOOL CGdiObject::DeleteObject()
{
	if (m_hObject == NULL)
		return FALSE;
	return ::DeleteObject(Detach());
}

/////////////////////////////////////////////////////////////////////////////
// Standard GDI objects

/////////////////////////////////////////////////////////////////////////////
// CPen

CPen::CPen(int nPenStyle, int nWidth, COLORREF crColor)
{
	if (!Attach(::CreatePen(nPenStyle, nWidth, crColor)))
		AfxThrowResourceException();
}

CPen::CPen(int nPenStyle, int nWidth, const LOGBRUSH* pLogBrush,
	int nStyleCount, const DWORD* lpStyle)
{
	if (!Attach(::ExtCreatePen(nPenStyle, nWidth, pLogBrush, nStyleCount,
			lpStyle)))
		AfxThrowResourceException();
}

/////////////////////////////////////////////////////////////////////////////

#ifdef _DEBUG
void CPen::Dump(CDumpContext& dc) const
{
	CGdiObject::Dump(dc);

	if (m_hObject == NULL)
		return;

	if (!afxData.bWin95 && ::GetObjectType(m_hObject) != OBJ_PEN)
	{
		// not a valid object
		dc << "has ILLEGAL HPEN!";
		return;
	}

	LOGPEN lp;
	VERIFY(GetObject(sizeof(lp), &lp));
	dc << "lgpn.lopnStyle = " << lp.lopnStyle;
	dc << "\nlgpn.lopnWidth.x (width) = " << lp.lopnWidth.x;
	dc << "\nlgpn.lopnColor = " << (void*)lp.lopnColor;

	dc << "\n";
}
#endif

/////////////////////////////////////////////////////////////////////////////
// CBrush

CBrush::CBrush(COLORREF crColor)
{
	if (!Attach(::CreateSolidBrush(crColor)))
		AfxThrowResourceException();
}

CBrush::CBrush(int nIndex, COLORREF crColor)
{
	if (!Attach(::CreateHatchBrush(nIndex, crColor)))
		AfxThrowResourceException();
}

CBrush::CBrush(CBitmap* pBitmap)
{
	ASSERT_VALID(pBitmap);

	if (!Attach(::CreatePatternBrush((HBITMAP)pBitmap->m_hObject)))
		AfxThrowResourceException();
}

BOOL CBrush::CreateDIBPatternBrush(HGLOBAL hPackedDIB, UINT nUsage)
{
	ASSERT(hPackedDIB != NULL);
	const void* lpPackedDIB = ::GlobalLock(hPackedDIB);
	ASSERT(lpPackedDIB != NULL);
	BOOL bResult = Attach(::CreateDIBPatternBrushPt(lpPackedDIB, nUsage));
	::GlobalUnlock(hPackedDIB);
	return bResult;
}

#ifdef _DEBUG
void CBrush::Dump(CDumpContext& dc) const
{
	CGdiObject::Dump(dc);

	if (m_hObject == NULL)
		return;

	if (!afxData.bWin95 && ::GetObjectType(m_hObject) != OBJ_BRUSH)
	{
		// not a valid window
		dc << "has ILLEGAL HBRUSH!";
		return;
	}

	LOGBRUSH lb;
	VERIFY(GetObject(sizeof(lb), &lb));
	dc << "lb.lbStyle = " << lb.lbStyle;
	dc << "\nlb.lbHatch = " << lb.lbHatch;
	dc << "\nlb.lbColor = " << (void*)lb.lbColor;

	dc << "\n";
}
#endif

/////////////////////////////////////////////////////////////////////////////

#ifdef _DEBUG
void CFont::Dump(CDumpContext& dc) const
{
	CGdiObject::Dump(dc);

	if (m_hObject == NULL)
		return;

	if (!afxData.bWin95 && ::GetObjectType(m_hObject) != OBJ_FONT)
	{
		// not a valid GDI object
		dc << "has ILLEGAL HFONT!";
		return;
	}

	LOGFONT lf;
	VERIFY(GetObject(sizeof(lf), &lf));
	dc << "lf.lfHeight = " << lf.lfHeight;
	dc << "\nlf.lfWidth = " << lf.lfWidth;
	dc << "\nlf.lfEscapement = " << lf.lfEscapement;
	dc << "\nlf.lfOrientation = " << lf.lfOrientation;
	dc << "\nlf.lfWeight = " << lf.lfWeight;
	dc << "\nlf.lfItalic = " << (int)lf.lfItalic;
	dc << "\nlf.lfUnderline = " << (int)lf.lfUnderline;
	dc << "\nlf.lfStrikeOut = " << (int)lf.lfStrikeOut;
	dc << "\nlf.lfCharSet = " << (int)lf.lfCharSet;
	dc << "\nlf.lfOutPrecision = " << (int)lf.lfOutPrecision;
	dc << "\nlf.lfClipPrecision = " << (int)lf.lfClipPrecision;
	dc << "\nlf.lfQuality = " << (int)lf.lfQuality;
	dc << "\nlf.lfPitchAndFamily = " << (int)lf.lfPitchAndFamily;
	dc << "\nlf.lfFaceName = " << (LPCTSTR)lf.lfFaceName;

	dc << "\n";
}
#endif

/////////////////////////////////////////////////////////////////////////////

#ifdef _DEBUG
void CBitmap::Dump(CDumpContext& dc) const
{
	CGdiObject::Dump(dc);

	if (m_hObject == NULL)
		return;

	if (!afxData.bWin95 && ::GetObjectType(m_hObject) != OBJ_BITMAP)
	{
		// not a valid object
		dc << "has ILLEGAL HBITMAP!";
		return;
	}

	BITMAP bm;
	VERIFY(GetObject(sizeof(bm), &bm));
	dc << "bm.bmType = " << bm.bmType;
	dc << "\nbm.bmHeight = " << bm.bmHeight;
	dc << "\nbm.bmWidth = " << bm.bmWidth;
	dc << "\nbm.bmWidthBytes = " << bm.bmWidthBytes;
	dc << "\nbm.bmPlanes = " << bm.bmPlanes;
	dc << "\nbm.bmBitsPixel = " << bm.bmBitsPixel;

	dc << "\n";
}
#endif

#ifdef AFX_INIT_SEG
#pragma code_seg(AFX_INIT_SEG)
#endif

IMPLEMENT_DYNAMIC(CResourceException, CException)
CResourceException _simpleResourceException(FALSE, AFX_IDS_RESOURCE_EXCEPTION);

IMPLEMENT_DYNAMIC(CUserException, CException)
CUserException _simpleUserException(FALSE, AFX_IDS_USER_EXCEPTION);

IMPLEMENT_DYNCREATE(CDC, CObject)
IMPLEMENT_DYNAMIC(CClientDC, CDC)
IMPLEMENT_DYNAMIC(CWindowDC, CDC)
IMPLEMENT_DYNAMIC(CPaintDC, CDC)
IMPLEMENT_DYNCREATE(CGdiObject, CObject)

IMPLEMENT_DYNAMIC(CPen, CGdiObject)
IMPLEMENT_DYNAMIC(CBrush, CGdiObject)
IMPLEMENT_DYNAMIC(CFont, CGdiObject)
IMPLEMENT_DYNAMIC(CBitmap, CGdiObject)
IMPLEMENT_DYNAMIC(CPalette, CGdiObject)
IMPLEMENT_DYNAMIC(CRgn, CGdiObject)

IMPLEMENT_DYNCREATE(CTempDC, CDC);

IMPLEMENT_DYNCREATE(CTempGdiObject, CGdiObject);

/////////////////////////////////////////////////////////////////////////////
// Standard exception processing

#ifdef AFX_CORE2_SEG
#pragma code_seg(AFX_CORE2_SEG)
#endif

// resource failure
void AFXAPI AfxThrowResourceException()
{
	THROW((CResourceException*)&_simpleResourceException);
}

// user alert
void AFXAPI AfxThrowUserException()
{
	THROW((CUserException*)&_simpleUserException);
}

#ifdef AFX_INIT_SEG
#pragma code_seg(AFX_INIT_SEG)
#endif

#pragma warning(disable: 4074)
#pragma init_seg(compiler)
IMPLEMENT_FIXED_ALLOC(CTempDC, 64);
IMPLEMENT_FIXED_ALLOC(CTempGdiObject, 64);

/////////////////////////////////////////////////////////////////////////////
