Click here to Skip to main content
15,891,375 members
Articles / Desktop Programming / MFC

SkinControls 1.1 - A journey in automating the skinning of Windows controls

Rate me:
Please Sign up or sign in to vote.
4.97/5 (80 votes)
11 Oct 200314 min read 359.8K   19.8K   273  
A self-contained, user-extensible, application-wide skinning architecture for Windows controls.
// Roundcnr.cpp: implementation of the CRoundCorner class.
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "Roundcorner.h"
#include "syscolors.h"

#include <math.h>

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

const double Pi = 3.141592654;

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

enum { COLD, DOWN , HOT };

struct Corner
{
	Corner() { bInfillBetween = FALSE; }

	CPoint ptQuadrant;
	CPoint ptOriginOffset;
	COLORREF crFrom, crTo;
	int nAngleFrom, nAngleTo;
	BOOL bInfillBetween;
};

// precalculated color factors
float fHiliteFactor[] = { 0.2f, 0.35f, 0.29f, 0.24f, 0.18f, 0.12f, 0.06f };
float fShadowFactor[] = { 0.7f, 0.75f, 0.81f, 0.85f, 0.88f, 0.91f, 0.95f };

const int NUMFACTORS = sizeof(fHiliteFactor) / sizeof(float);

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

CRoundCorner::CRoundCorner(CWnd* pWnd, int nRadius, COLORREF crBkgnd)
{
	ASSERT (pWnd);
	m_pWnd = pWnd;
	m_nRadius = max(0, nRadius);
	m_crBkgnd = crBkgnd;
}

CRoundCorner::~CRoundCorner()
{

}

void CRoundCorner::Draw(int nCorners, DWORD dwStyle)
{
	ASSERT (m_pWnd);
	
	if (!m_pWnd)
		return;

	CRect rWnd;
	m_pWnd->GetWindowRect(rWnd);

	CWindowDC dc(m_pWnd);

	Draw(&dc, rWnd, m_nRadius, nCorners, dwStyle, m_crBkgnd);
}

// static version
void CRoundCorner::Draw(CDC* pDC, LPRECT pRect, int nRadius, int nCorners, DWORD dwStyle, COLORREF crBkgnd)
{
	// special case: if cold and the backgnd color 
	// has not been specified then nothing to draw
	int nState = (dwStyle & RC_DOWN) ? DOWN : (dwStyle & RC_UP) ? HOT : COLD;

	if (nState == COLD && crBkgnd == -1)
		return;

	// edge style
	if (dwStyle & RC_RENDER)
	{
		ASSERT (dwStyle & RC_EDGES);

		if (!(dwStyle & RC_EDGES))
			return;

		if (crBkgnd == (COLORREF)-1)
			crBkgnd = GetColor(COLOR_3DFACE, -1);
	}

	CRect rect(pRect);

//	BOOL bDefBtn = (dwStyle & RC_DEFBTN) && (dwStyle & RC_EDGES); 
	
//	if (bDefBtn)
//		rect.DeflateRect(1, 1);

	rect.right--;
	rect.bottom--;

	nRadius = max(0, nRadius);

	// the maximum allowable radius depends one which Corners are to drawn
	int nMaxRadius = 0;
	BOOL bTopLeft = (nCorners & RC_TOPLEFT);
	BOOL bTopRight = (nCorners & RC_TOPRIGHT);
	BOOL bBottomLeft = (nCorners & RC_BOTTOMLEFT);
	BOOL bBottomRight = (nCorners & RC_BOTTOMRIGHT);

	if (bTopLeft && bTopRight && !bBottomLeft && !bBottomRight)
		nMaxRadius = min(rect.Height(), rect.Width() / 2);

	else if (!bTopLeft && !bTopRight && bBottomLeft && bBottomRight)
		nMaxRadius = min(rect.Height(), rect.Width() / 2); // same

	else if (bTopLeft && bBottomLeft && !bTopRight && !bBottomRight)
		nMaxRadius = min(rect.Width(), rect.Height() / 2);

	else if (!bTopLeft && !bBottomLeft && bTopRight && bBottomRight)
		nMaxRadius = min(rect.Width(), rect.Height() / 2); // same

	else 
		nMaxRadius = min(rect.Width(), rect.Height()) / 2;

	nRadius = min(nRadius, nMaxRadius);

	if (nRadius)
	{
		// draw the Corners first and then the connecting edges
		if (bTopLeft)
			DrawTopLeft(pDC, rect, nRadius, dwStyle, crBkgnd);

		if (bTopRight)
			DrawTopRight(pDC, rect, nRadius, dwStyle, crBkgnd);

		if (bBottomRight)
			DrawBottomRight(pDC, rect, nRadius, dwStyle, crBkgnd);

		if (bBottomLeft)
			DrawBottomLeft(pDC, rect, nRadius, dwStyle, crBkgnd);
	}

	// now the edges
	DrawEdges(pDC, rect, nRadius, nCorners, dwStyle, crBkgnd);

	// now the def button border
/*	if (bDefBtn)
	{
		rect.InflateRect(1, 1);
		DrawDefBtnBorder(pDC, rect, nRadius, nCorners);
	}
*/
}

void CRoundCorner::DrawDefBtnBorder(CDC* pDC, LPRECT pRect, int nRadius, int nCorners)
{
	if (!nRadius || !nCorners)
	{
		pDC->Draw3dRect(pRect, 0, 0);
		return;
	}

	BOOL bTopLeft = (nCorners & RC_TOPLEFT);
	BOOL bTopRight = (nCorners & RC_TOPRIGHT);
	BOOL bBottomLeft = (nCorners & RC_BOTTOMLEFT);
	BOOL bBottomRight = (nCorners & RC_BOTTOMRIGHT);

	Corner cnr;
	CRect rect(pRect);
	nRadius = max(0, nRadius);
	nRadius = min(nRadius, min(rect.Width(), rect.Height()) / 2);

	cnr.crFrom = cnr.crTo = 0;
	cnr.bInfillBetween = TRUE;

	// top left
	if (bTopLeft)
	{
		cnr.ptQuadrant = CPoint(-1, 1);
		cnr.ptOriginOffset = CPoint(rect.left + nRadius, rect.top + nRadius);
		cnr.nAngleFrom = 270;
		cnr.nAngleTo = 360;

		DrawCorner(pDC, nRadius, &cnr);
	}

	// top right
	if (bTopRight)
	{
		cnr.ptQuadrant = CPoint(1, 1);
		cnr.ptOriginOffset = CPoint(rect.right - nRadius, rect.top + nRadius);
		cnr.nAngleFrom = 0;
		cnr.nAngleTo = 90;

		DrawCorner(pDC, nRadius, &cnr);
	}

	// bottom left
	if (bBottomLeft)
	{
		cnr.ptQuadrant = CPoint(-1, -1);
		cnr.ptOriginOffset = CPoint(rect.left + nRadius, rect.bottom - nRadius);
		cnr.nAngleFrom = 180;
		cnr.nAngleTo = 270;

		DrawCorner(pDC, nRadius, &cnr);
	}

	// bottom right
	if (bBottomRight)
	{
		cnr.ptQuadrant = CPoint(1, -1);
		cnr.ptOriginOffset = CPoint(rect.right - nRadius, rect.bottom - nRadius);
		cnr.nAngleFrom = 90;
		cnr.nAngleTo = 180;

		DrawCorner(pDC, nRadius, &cnr);
	}

	// edges

	// top
	CRect rHLine(rect.left + (bTopLeft ? nRadius : 0), rect.top, 
				rect.right - (bTopRight ? nRadius: 0), rect.top + 1);

	// left
	CRect rVLine(rect.left, rect.top + (bTopLeft ? nRadius : 0), 
				rect.left + 1, rect.bottom - (bBottomLeft ? nRadius : 0));

	pDC->FillSolidRect(rHLine, 0);
	pDC->FillSolidRect(rVLine, 0);
	
	// bottom
	rHLine.SetRect(rect.left + (bBottomLeft ? nRadius : 0), rect.bottom, 
					rect.right - ((bBottomRight && nRadius) ? nRadius : -1), rect.bottom + 1);
	
	// right
	rVLine.SetRect(rect.right, rect.top + (bTopRight ? nRadius : 0), 
					rect.right + 1, rect.bottom - ((bBottomRight && nRadius) ? nRadius : -1));

	pDC->FillSolidRect(rHLine, 0);
	pDC->FillSolidRect(rVLine, 0);
}

void CRoundCorner::DrawEdges(CDC* pDC, LPRECT pRect, int nRadius, int nCorners, DWORD dwStyle, COLORREF crBkgnd)
{
	if (!(dwStyle & RC_EDGES)) // sanity check
		return;

	BOOL bRender = (dwStyle & RC_RENDER);
	BOOL bThin = !bRender && (dwStyle & RC_THIN);

	int nState = (dwStyle & RC_DOWN) ? DOWN : (dwStyle & RC_UP) ? HOT : COLD;

	// if cold and the backgnd color has not been set then nothing to draw
	if (nState == COLD && crBkgnd == -1)
		return;

	BOOL bTopLeft = (nCorners & RC_TOPLEFT);
	BOOL bTopRight = (nCorners & RC_TOPRIGHT);
	BOOL bBottomLeft = (nCorners & RC_BOTTOMLEFT);
	BOOL bBottomRight = (nCorners & RC_BOTTOMRIGHT);

	CRect rect(pRect);
	COLORREF color;

	nRadius = max(0, nRadius);
	nRadius = min(nRadius, min(rect.Width(), rect.Height()) / 2);

	// top
	CRect rHLine(rect.left + (bTopLeft ? nRadius : 0), rect.top, 
				rect.right - (bTopRight ? nRadius: 0), rect.top + 1);

	// left
	CRect rVLine(rect.left, rect.top + (bTopLeft ? nRadius : 0), 
				rect.left + 1, rect.bottom - (bBottomLeft ? nRadius : 0));

	if (bRender && crBkgnd != -1)
	{
		int nLine = 0;
		int nMaxLine = nRadius ? min(NUMFACTORS, nRadius) : 
								min(NUMFACTORS, min(rect.Width(), rect.Height()) / 2);

		do
		{
			int nFactor = (nLine * NUMFACTORS) / nMaxLine;

			COLORREF color = (nState == DOWN) ? Darker(crBkgnd, fShadowFactor[nFactor]) : 
							(nState == HOT) ? Lighter(crBkgnd, fHiliteFactor[nFactor]) : crBkgnd;

			pDC->FillSolidRect(rHLine, color);
			pDC->FillSolidRect(rVLine, color);
			rHLine.OffsetRect(0, 1);
			rVLine.OffsetRect(1, 0);

			if (!nRadius)
			{
				// shorten by one pixel
				rHLine.DeflateRect(1, 0);
				rVLine.DeflateRect(0, 1);
			}

			nLine++;
		}
		while (nLine < nMaxLine);
	}
	else
	{
		if (!bThin)
		{
			color = (nState == DOWN) ? GetColor(COLOR_3DSHADOW, crBkgnd) : 
					(nState == HOT) ? GetColor(COLOR_3DFACE, crBkgnd) : crBkgnd;

			pDC->FillSolidRect(rHLine, color);
			pDC->FillSolidRect(rVLine, color);
			rHLine.OffsetRect(0, 1);
			rVLine.OffsetRect(1, 0);

			if (!nRadius)
			{
				// shorten by one pixel
				rHLine.DeflateRect(1, 0);
				rVLine.DeflateRect(0, 1);
			}
			
			color = (nState == DOWN) ? GetColor(COLOR_3DDKSHADOW, crBkgnd) : 
					(nState == HOT) ? GetColor(COLOR_3DHILIGHT, crBkgnd) : crBkgnd;
		}
		else
		{
			color = (nState == DOWN) ? GetColor(COLOR_3DSHADOW, crBkgnd) : 
					(nState == HOT) ? GetColor(COLOR_3DHILIGHT, crBkgnd) : crBkgnd;
		}

		pDC->FillSolidRect(rHLine, color);
		pDC->FillSolidRect(rVLine, color);
	}
	
	// bottom
	rHLine.SetRect(rect.left + (bBottomLeft ? nRadius : 0), rect.bottom, 
					rect.right - ((bBottomRight && nRadius) ? nRadius : -1), rect.bottom + 1);
	
	// right
	rVLine.SetRect(rect.right, rect.top + (bTopRight ? nRadius : 0), 
					rect.right + 1, rect.bottom - ((bBottomRight && nRadius) ? nRadius : -1));
	
	if (bRender && crBkgnd != -1)
	{
		int nLine = 0;
		int nMaxLine = nRadius ? min(NUMFACTORS, nRadius) : 
								min(NUMFACTORS, min(rect.Width(), rect.Height()) / 2);

		do
		{
			int nFactor = (nLine * NUMFACTORS) / nMaxLine;

			COLORREF color = (nState == DOWN) ? Lighter(crBkgnd, fHiliteFactor[nFactor]) : 
							(nState == HOT) ? Darker(crBkgnd, fShadowFactor[nFactor]) : crBkgnd;

			pDC->FillSolidRect(rHLine, color);
			pDC->FillSolidRect(rVLine, color);
			rHLine.OffsetRect(0, -1);
			rVLine.OffsetRect(-1, 0);

			if (!nRadius)
			{
				// shorten by one pixel
				rHLine.DeflateRect(1, 0);
				rVLine.DeflateRect(0, 1);
			}

			nLine++;
		}
		while (nLine < nMaxLine);
	}
	else
	{
		if (!bThin)
		{
			color = (nState == DOWN) ? GetColor(COLOR_3DHILIGHT, crBkgnd) : 
					(nState == HOT) ? GetColor(COLOR_3DDKSHADOW, crBkgnd) : crBkgnd;

			pDC->FillSolidRect(rHLine, color);
			pDC->FillSolidRect(rVLine, color);
			rHLine.OffsetRect(0, -1);
			rVLine.OffsetRect(-1, 0);

			if (!nRadius)
			{
				// shorten by one pixel
				rHLine.DeflateRect(1, 0); 
				rVLine.DeflateRect(0, 1);
			}
			
			color = (nState == DOWN) ? GetColor(COLOR_3DFACE, crBkgnd) : 
					(nState == HOT) ? GetColor(COLOR_3DSHADOW, crBkgnd) : crBkgnd;
		}
		else
		{
			color = (nState == DOWN) ? GetColor(COLOR_3DHIGHLIGHT, crBkgnd) : 
					(nState == HOT) ? GetColor(COLOR_3DSHADOW, crBkgnd) : crBkgnd;
		}

		pDC->FillSolidRect(rHLine, color);
		pDC->FillSolidRect(rVLine, color);
	}
}

COLORREF CRoundCorner::GetColor(int nAngle, int nDegreesFrom, int nDegreesTo, COLORREF crFrom, COLORREF crTo)
{
	if (crFrom == crTo)
		return crFrom;

	COLORREF crResult = RGB(0, 0, 0);

	if (nDegreesFrom > nDegreesTo)
		nDegreesTo += 360;

	ASSERT (nAngle >= nDegreesFrom && nAngle <= nDegreesTo);

	if (nAngle >= nDegreesFrom && nAngle <= nDegreesTo)
	{
		// all this does is to approximate the averageof the two colors
		// at the point being queried
		BYTE byRedFrom = GetRValue(crFrom);
		BYTE byGreenFrom = GetGValue(crFrom);
		BYTE byBlueFrom = GetBValue(crFrom);
		BYTE byRedTo = GetRValue(crTo);
		BYTE byGreenTo = GetGValue(crTo);
		BYTE byBlueTo = GetBValue(crTo);

		int nOffset = nAngle - nDegreesFrom;
		int nDiff = nDegreesTo - nDegreesFrom;

		BYTE byRedResult = (byRedFrom * (nDiff - nOffset) + byRedTo * nOffset) / nDiff;
		BYTE byGreenResult = (byGreenFrom * (nDiff - nOffset) + byGreenTo * nOffset) / nDiff;
		BYTE byBlueResult = (byBlueFrom * (nDiff - nOffset) + byBlueTo * nOffset) / nDiff;

		crResult = RGB(byRedResult, byGreenResult, byBlueResult);
	}

	return crResult;
}

void CRoundCorner::DrawTopLeft(CDC* pDC, LPRECT pRect, int nRadius, DWORD dwStyle, COLORREF crBkgnd)
{
	if (!nRadius)
		return;

	BOOL bRender = (dwStyle & RC_RENDER) && (crBkgnd != -1);
	BOOL bThin = !bRender && (dwStyle & RC_THIN);

	int nState = (dwStyle & RC_DOWN) ? DOWN : (dwStyle & RC_UP) ? HOT : COLD;

	Corner cnr;

	cnr.ptQuadrant = CPoint(-1, 1);
	cnr.ptOriginOffset = CPoint(pRect->left + nRadius, pRect->top + nRadius);
	cnr.nAngleFrom = 270;
	cnr.nAngleTo = 360;

	if (bRender)
	{
		int nLine = 0;
		int nMaxLine = min(NUMFACTORS, nRadius);

		cnr.bInfillBetween = TRUE;

		do
		{
			int nFactor = (nLine * NUMFACTORS) / nMaxLine;

			cnr.crFrom = (nState == DOWN) ? Darker(crBkgnd, fShadowFactor[nFactor]) : 
							(nState == HOT) ? Lighter(crBkgnd, fHiliteFactor[nFactor]) : crBkgnd;

			cnr.crTo = cnr.crFrom;

			DrawCorner(pDC, nRadius, &cnr);
			nRadius--;
			nLine++;
		}
		while (nLine < nMaxLine);
	}
	else
	{
		if (!bThin)
		{
			cnr.crFrom = (nState == DOWN) ? GetColor(COLOR_3DSHADOW, crBkgnd) : 
							(nState == HOT) ? GetColor(COLOR_3DFACE, crBkgnd) : crBkgnd;

			cnr.crTo = cnr.crFrom;

			cnr.bInfillBetween = TRUE;
			DrawCorner(pDC, nRadius, &cnr);
			nRadius--;

			cnr.crFrom = (nState == DOWN) ? GetColor(COLOR_3DDKSHADOW, crBkgnd) : 
							(nState == HOT) ? GetColor(COLOR_3DHILIGHT, crBkgnd) : crBkgnd;
		}
		else
		{
			cnr.crFrom = (nState == DOWN) ? GetColor(COLOR_3DSHADOW, crBkgnd) : 
							(nState == HOT) ? GetColor(COLOR_3DHILIGHT, crBkgnd) : crBkgnd;
		}

		cnr.crTo = cnr.crFrom;

		cnr.bInfillBetween = FALSE;
		DrawCorner(pDC, nRadius, &cnr);
	}
}

void CRoundCorner::DrawTopRight(CDC* pDC, LPRECT pRect, int nRadius, DWORD dwStyle, COLORREF crBkgnd)
{
	if (!nRadius)
		return;

	BOOL bRender = (dwStyle & RC_RENDER) && (crBkgnd != -1);
	BOOL bThin = !bRender && (dwStyle & RC_THIN);

	int nState = (dwStyle & RC_DOWN) ? DOWN : (dwStyle & RC_UP) ? HOT : COLD;

	Corner cnr;

	cnr.ptQuadrant = CPoint(1, 1);
	cnr.ptOriginOffset = CPoint(pRect->right - nRadius, pRect->top + nRadius);
	cnr.nAngleFrom = 0;
	cnr.nAngleTo = 90;

	if (bRender)
	{
		int nLine = 0;
		int nMaxLine = min(NUMFACTORS, nRadius);

		cnr.bInfillBetween = TRUE;

		do
		{
			int nFactor = (nLine * NUMFACTORS) / nMaxLine;

			cnr.crFrom = (nState == DOWN) ? Darker(crBkgnd, fShadowFactor[nFactor]) : 
							(nState == HOT) ? Lighter(crBkgnd, fHiliteFactor[nFactor]) : crBkgnd;

			cnr.crTo = (nState == DOWN) ? Lighter(crBkgnd, fHiliteFactor[nFactor]) : 
							(nState == HOT) ? Darker(crBkgnd, fShadowFactor[nFactor]) : crBkgnd;

			DrawCorner(pDC, nRadius, &cnr);
			nRadius--;
			nLine++;
		}
		while (nLine < nMaxLine);
	}
	else
	{
		if (!bThin)
		{
			cnr.crFrom = (nState == DOWN) ? GetColor(COLOR_3DSHADOW, crBkgnd) : 
							(nState == HOT) ? GetColor(COLOR_3DFACE, crBkgnd) : crBkgnd;

			cnr.crTo = (nState == DOWN) ? GetColor(COLOR_3DFACE, crBkgnd) : 
							(nState == HOT) ? GetColor(COLOR_3DSHADOW, crBkgnd) : crBkgnd;

			cnr.bInfillBetween = TRUE;
			DrawCorner(pDC, nRadius, &cnr);
			nRadius--;

			cnr.crFrom = (nState == DOWN) ? GetColor(COLOR_3DDKSHADOW, crBkgnd) : 
							(nState == HOT) ? GetColor(COLOR_3DHILIGHT, crBkgnd) : crBkgnd;

			cnr.crTo = (nState == DOWN) ? GetColor(COLOR_3DHILIGHT, crBkgnd) : 
							(nState == HOT) ? GetColor(COLOR_3DDKSHADOW, crBkgnd) : crBkgnd;
		}
		else
		{
			cnr.crFrom = (nState == DOWN) ? GetColor(COLOR_3DSHADOW, crBkgnd) : 
							(nState == HOT) ? GetColor(COLOR_3DHILIGHT, crBkgnd) : crBkgnd;

			cnr.crTo = (nState == DOWN) ? GetColor(COLOR_3DHILIGHT, crBkgnd) : 
							(nState == HOT) ? GetColor(COLOR_3DSHADOW, crBkgnd) : crBkgnd;
		}

		cnr.bInfillBetween = FALSE;
		DrawCorner(pDC, nRadius, &cnr);
	}
}

void CRoundCorner::DrawBottomLeft(CDC* pDC, LPRECT pRect, int nRadius, DWORD dwStyle, COLORREF crBkgnd)
{
	if (!nRadius)
		return;

	BOOL bRender = (dwStyle & RC_RENDER) && (crBkgnd != -1);
	BOOL bThin = !bRender && (dwStyle & RC_THIN);

	int nState = (dwStyle & RC_DOWN) ? DOWN : (dwStyle & RC_UP) ? HOT : COLD;

	Corner cnr;

	cnr.ptQuadrant = CPoint(-1, -1);
	cnr.ptOriginOffset = CPoint(pRect->left + nRadius, pRect->bottom - nRadius);
	cnr.nAngleFrom = 180;
	cnr.nAngleTo = 270;

	if (bRender)
	{
		int nLine = 0;
		int nMaxLine = min(NUMFACTORS, nRadius);

		cnr.bInfillBetween = TRUE;

		do
		{
			int nFactor = (nLine * NUMFACTORS) / nMaxLine;

			cnr.crFrom = (nState == DOWN) ? Lighter(crBkgnd, fHiliteFactor[nFactor]) : 
							(nState == HOT) ? Darker(crBkgnd, fShadowFactor[nFactor]) : crBkgnd;

			cnr.crTo = (nState == DOWN) ? Darker(crBkgnd, fShadowFactor[nFactor]) : 
							(nState == HOT) ? Lighter(crBkgnd, fHiliteFactor[nFactor]) : crBkgnd;

			DrawCorner(pDC, nRadius, &cnr);
			nRadius--;
			nLine++;
		}
		while (nLine < nMaxLine);
	}
	else
	{
		if (!bThin)
		{
			cnr.crFrom = (nState == DOWN) ? GetColor(COLOR_3DHILIGHT, crBkgnd) : 
							(nState == HOT) ? GetColor(COLOR_3DDKSHADOW, crBkgnd) : crBkgnd;

			cnr.crTo = (nState == DOWN) ? GetColor(COLOR_3DSHADOW, crBkgnd) : 
							(nState == HOT) ? GetColor(COLOR_3DFACE, crBkgnd) : crBkgnd;

			cnr.bInfillBetween = TRUE;
			DrawCorner(pDC, nRadius, &cnr);
			nRadius--;

			cnr.crFrom = (nState == DOWN) ? GetColor(COLOR_3DFACE, crBkgnd) : 
							(nState == HOT) ? GetColor(COLOR_3DSHADOW, crBkgnd) : crBkgnd;

			cnr.crTo = (nState == DOWN) ? GetColor(COLOR_3DDKSHADOW, crBkgnd) : 
							(nState == HOT) ? GetColor(COLOR_3DHILIGHT, crBkgnd) : crBkgnd;
		}
		else
		{
			cnr.crFrom = (nState == DOWN) ? GetColor(COLOR_3DHIGHLIGHT, crBkgnd) : 
							(nState == HOT) ? GetColor(COLOR_3DSHADOW, crBkgnd) : crBkgnd;

			cnr.crTo = (nState == DOWN) ? GetColor(COLOR_3DSHADOW, crBkgnd) : 
							(nState == HOT) ? GetColor(COLOR_3DHILIGHT, crBkgnd) : crBkgnd;
		}

		cnr.bInfillBetween = FALSE;
		DrawCorner(pDC, nRadius, &cnr);
	}
}

void CRoundCorner::DrawBottomRight(CDC* pDC, LPRECT pRect, int nRadius, DWORD dwStyle, COLORREF crBkgnd)
{
	if (!nRadius)
		return;

	BOOL bRender = (dwStyle & RC_RENDER) && (crBkgnd != -1);
	BOOL bThin = !bRender && (dwStyle & RC_THIN);

	int nState = (dwStyle & RC_DOWN) ? DOWN : (dwStyle & RC_UP) ? HOT : COLD;

	Corner cnr;

	cnr.ptQuadrant = CPoint(1, -1);
	cnr.ptOriginOffset = CPoint(pRect->right - nRadius, pRect->bottom - nRadius);
	cnr.nAngleFrom = 90;
	cnr.nAngleTo = 180;

	if (bRender)
	{
		int nLine = 0;
		int nMaxLine = min(NUMFACTORS, nRadius);

		cnr.bInfillBetween = TRUE;

		do
		{
			int nFactor = (nLine * NUMFACTORS) / nMaxLine;

			cnr.crFrom = (nState == DOWN) ? Lighter(crBkgnd, fHiliteFactor[nFactor]) : 
							(nState == HOT) ? Darker(crBkgnd, fShadowFactor[nFactor]) : crBkgnd;

			cnr.crTo = cnr.crFrom;

			DrawCorner(pDC, nRadius, &cnr);
			nRadius--;
			nLine++;
		}
		while (nLine < nMaxLine);
	}
	else
	{
		if (!bThin)
		{
			cnr.crFrom = (nState == DOWN) ? GetColor(COLOR_3DHILIGHT, crBkgnd) : 
							(nState == HOT) ? GetColor(COLOR_3DDKSHADOW, crBkgnd) : crBkgnd;

			cnr.crTo = cnr.crFrom;

			cnr.bInfillBetween = TRUE;
			DrawCorner(pDC, nRadius, &cnr);
			nRadius--;

			cnr.crFrom = (nState == DOWN) ? GetColor(COLOR_3DFACE, crBkgnd) : 
							(nState == HOT) ? GetColor(COLOR_3DSHADOW, crBkgnd) : crBkgnd;
		}
		else
		{
			cnr.crFrom = (nState == DOWN) ? GetColor(COLOR_3DHIGHLIGHT, crBkgnd) : 
							(nState == HOT) ? GetColor(COLOR_3DSHADOW, crBkgnd) : crBkgnd;
		}

		cnr.crTo = cnr.crFrom;

		cnr.bInfillBetween = FALSE;
		DrawCorner(pDC, nRadius, &cnr);
	}
}

void CRoundCorner::DrawCorner(CDC* pDC, int nRadius, Corner* pCorner)
{
	CPoint ptBase(0, nRadius);
	int nDecision = 1 - nRadius;

	while (ptBase.y >= ptBase.x)
	{
		// translate to target quadrant
		CPoint ptQuadrant(ptBase.x * pCorner->ptQuadrant.x, ptBase.y * pCorner->ptQuadrant.y);

		// flip to adjacent octant
		int nFlip = ((pCorner->ptQuadrant.x > 0 && pCorner->ptQuadrant.y > 0) ||
					(pCorner->ptQuadrant.x < 0 && pCorner->ptQuadrant.y < 0)) ? 1 : -1;
		
		CPoint ptOctant(ptQuadrant.y * nFlip, ptQuadrant.x * nFlip);

		// adjust y value for MM_TEXT
		ptQuadrant.y = -ptQuadrant.y;
		ptOctant.y = -ptOctant.y;

		// offset to actual position
		ptQuadrant.Offset(pCorner->ptOriginOffset);
		ptOctant.Offset(pCorner->ptOriginOffset);

		// draw pixels
		double dRad = asin((double)ptBase.x / nRadius);
		int nAngle = (int)((dRad * 180) / Pi);

		COLORREF crQuadrant = GetColor(pCorner->nAngleFrom + nAngle, 
									pCorner->nAngleFrom, 
									pCorner->nAngleTo, 
									pCorner->crFrom, 
									pCorner->crTo);

		pDC->SetPixelV(ptQuadrant, crQuadrant);

		COLORREF crOctant = GetColor(pCorner->nAngleTo - nAngle, 
									pCorner->nAngleFrom, 
									pCorner->nAngleTo, 
									pCorner->crFrom, 
									pCorner->crTo);

		pDC->SetPixelV(ptOctant, crOctant);

		// next iteration
		if (nDecision < 0)
		{
			nDecision += (2 * ptBase.x + 3);
			ptBase.x++;
		}
		else
		{
			nDecision += (2 * (ptBase.x - ptBase.y) + 5);
			ptBase.y--;
			ptBase.x++;

			if (pCorner->bInfillBetween)
			{
				// draw fill in pixel
				ptQuadrant.y += pCorner->ptQuadrant.y;
				ptOctant.x -= pCorner->ptQuadrant.x;

				// draw pixels
				pDC->SetPixelV(ptQuadrant, crQuadrant);
				pDC->SetPixelV(ptOctant, crOctant);
			}
		}
	}
}

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

COLORREF CRoundCorner::GetColor(int nColor, COLORREF crBase)
{
	if (crBase != -1)
	{
		switch (nColor)
		{
		case COLOR_3DHILIGHT:
			return Lighter(crBase);
			
		case COLOR_3DSHADOW:	
			return Darker(crBase, 0.75f);
			
		case COLOR_3DDKSHADOW:	
			return Darker(crBase, 0.2f);

		case COLOR_3DFACE:	
//		case COLOR_PARENTBKGND:
			return crBase;
		}
	}

	// default
	if (nColor == COLOR_PARENTBKGND)
		nColor = COLOR_3DFACE;

	return ::GetSysColor(nColor);
}

COLORREF CRoundCorner::Darker(COLORREF crBase, float fFactor)
{
	if (fFactor <= 0.0f)
		return 0;

	else if (fFactor >= 1.0f)
		return crBase;

	fFactor = min(fFactor, 1.0f);
	fFactor = max(fFactor, 0.0f);

	BYTE bRed, bBlue, bGreen;
	BYTE bRedShadow, bBlueShadow, bGreenShadow;

	bRed = GetRValue(crBase);
	bBlue = GetBValue(crBase);
	bGreen = GetGValue(crBase);

	bRedShadow = (BYTE)(bRed * fFactor) + (bRed % 2);
	bBlueShadow = (BYTE)(bBlue * fFactor) + (bBlue % 2);
	bGreenShadow = (BYTE)(bGreen * fFactor) + (bGreen % 2);

	return RGB(bRedShadow, bGreenShadow, bBlueShadow);
}

COLORREF CRoundCorner::Lighter(COLORREF crBase, float fFactor) 
{
	if (fFactor <= 0.0f)
		return crBase;

	else if (fFactor >= 1.0f)
		return RGB(255, 255, 255);

	fFactor = min(fFactor, 1.0f);
	fFactor = max(fFactor, 0.0f);

	BYTE bRed, bBlue, bGreen;
	BYTE bRedHilite, bBlueHilite, bGreenHilite;

	bRed = GetRValue(crBase);
	bBlue = GetBValue(crBase);
	bGreen = GetGValue(crBase);

	bRedHilite = min(255, bRed + (BYTE)(255 * fFactor));
	bBlueHilite = min(255, bBlue + (BYTE)(255 * fFactor));
	bGreenHilite = min(255, bGreen + (BYTE)(255 * fFactor));

	return RGB(bRedHilite, bGreenHilite, bBlueHilite);
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Software Developer Maptek
Australia Australia
.dan.g. is a naturalised Australian and has been developing commercial windows software since 1998.

Comments and Discussions