www.pudn.com > StyleToolkit_demo_src.zip > Border.cpp


// Border.cpp: implementation of the Border class. 
// 
// Author:  Darren Sessions 
//           
// 
// Description: 
//     Border Draws or Fills rounded rectangles for GDI+.  It was implemented 
//	   to overcome the asymmetric issues associated with GDI+ round rectangles 
// 
// History 
//     Version 1.0 - 2008 June 24 
//     - Initial public release 
// 
// License: 
//     This software is released under the Code Project Open License (CPOL), 
//     which may be found here:  http://www.codeproject.com/info/eula.aspx 
//     You are free to use this software in any way you like, except that you  
//     may not sell this source code. 
// 
//     This software is provided "as is" with no expressed or implied warranty. 
//     I accept no liability for any damage or loss of business that this  
//     software may cause. 
// 
/////////////////////////////////////////////////////////////////////////////// 
 
#include "stdafx.h" 
#include "Border.h" 
 
#ifdef _DEBUG 
#undef THIS_FILE 
static char THIS_FILE[]=__FILE__; 
#define new DEBUG_NEW 
#endif 
 
////////////////////////////////////////////////////////////////////// 
// Construction/Destruction 
////////////////////////////////////////////////////////////////////// 
 
Border::Border() 
{ 
} 
 
Border::~Border() 
{ 
 
} 
 
//============================================================================= 
// 
// GetPath() 
// 
// Purpose:     Defines a the appropriate path and places it in the GraphicsPath 
// 
// Parameters:  pPath		- [out] pointer to GraphicsPath that will recieve the  
//									path data 
//				r			- [in]	Rect that defines the round rectangle boundaries 
// 
// Returns:     None 
// 
void Border::GetPath(GraphicsPath *pPath, Rect r) 
{	 
	if(shape == RECTANGLE) 
	{ 
		GetRectPath(pPath, r); 
	} 
	else 
	{ 
		GetRoundRectPath(pPath, r); 
	} 
} 
 
 
//============================================================================= 
// 
// GetRectPath() 
// 
// Purpose:     Defines a Rectangle and places it in the GraphicsPath 
// 
// Parameters:  pPath		- [out] pointer to GraphicsPath that will recieve the  
//									path data 
//				r			- [in]	Rect that defines the round rectangle boundaries 
// 
// Returns:     None 
// 
void Border::GetRectPath(GraphicsPath *pPath, Rect r) 
{ 
	pPath->Reset(); 
	pPath->AddRectangle(r); 
	pPath->CloseFigure(); 
} 
 
//============================================================================= 
// 
// GetRoundRectPath() 
// 
// Purpose:     Defines a Rounded Rectangle and places it in the GraphicsPath 
// 
// Parameters:  pPath		- [out] pointer to GraphicsPath that will recieve the  
//									path data 
//				r			- [in]	Rect that defines the round rectangle boundaries 
// 
// Returns:     None 
// 
void Border::GetRoundRectPath(GraphicsPath *pPath, Rect r) 
{ 
	// set the diameter 
	int dia = 2*radius; 
 
	// this isn't a round rect anymore but it  
	// can occur with progress bars 
	if( (dia > r.Width) || (dia > r.Height) ) 
	{ 
		r.Inflate(0, -(dia/r.Width)); // add other case 
		pPath->AddEllipse(r); 
		return; 
	} 
 
	// define a corner  
	Rect Corner(r.X, r.Y, dia, dia); 
 
	// begin path 
	pPath->Reset(); 
 
	// top left 
	pPath->AddArc(Corner, 180, 90);	 
 
	// tweak needed for radius of 10 (dia of 20) 
	if(dia == 20) 
	{ 
		Corner.Width += 1;  
		Corner.Height += 1;  
		r.Width -=1; r.Height -= 1; 
	} 
 
	// top right 
	Corner.X += (r.Width - dia - 1); 
	pPath->AddArc(Corner, 270, 90);	 
	 
	// bottom right 
	Corner.Y += (r.Height - dia - 1); 
	pPath->AddArc(Corner,   0, 90);	 
	 
	// bottom left 
	Corner.X -= (r.Width - dia - 1); 
	pPath->AddArc(Corner,  90, 90); 
 
	// end path 
	pPath->CloseFigure(); 
} 
 
 
//============================================================================= 
// 
// Draw() 
// 
// Purpose:     Draws a border with a solid pen 
// 
// Parameters:  pGraphics	- [in]	pointer to the Graphics device 
//				r			- [in]	Rect that defines the round rectangle boundaries 
//				ulclr		- [in]	Color value for the pen 
//				brclr		- [in]  Bottom right pen color (SQUARE border only) 
//				width		- [in]  width of the border 
//		 
// Returns:     None 
// 
void Border::Draw(Graphics* pGraphics, Rect r,  Color ulclr, Color brclr, int width) 
{ 
	if(shape == RECTANGLE) 
	{ 
		DrawRect(pGraphics, r, ulclr, brclr, width); 
	} 
	if(shape == ELLIPSE) 
	{ 
		Pen pen(ulclr, 1); 
		pen.SetAlignment(PenAlignmentCenter); 
		RectF rf((float)r.X-1, (float)r.Y-1, (float)r.Width+1, (float)r.Height+1); 
		pGraphics->DrawEllipse(&pen, rf); 
	} 
	else if(shape == TRANSITION) 
	{ 
		// disable smoothing for the clip fill 
		SmoothingMode oldMode = pGraphics->GetSmoothingMode(); 
		pGraphics->SetSmoothingMode(SmoothingModeNone); 
 
		// fill the transition zone using a clip region 
		pGraphics->SetClip(r, CombineModeReplace); 
 
		// fill one pixel width into the round rect if width > 1 
		// this supresses any transition antialiasing 
		Rect cr(r); if(width > 1) cr.Inflate(-1, -1); 
 
		// use local clip function for round rect 
		SetBorderClip(pGraphics, cr, CombineModeExclude); 
 
		// fill with the border color 
		SolidBrush sbr(ulclr); 
		pGraphics->FillRectangle(&sbr, r); 
 
		// clear the clipping region 
		pGraphics->ResetClip(); 
 
		// restore any smoothing mode 
		pGraphics->SetSmoothingMode(oldMode); 
 
		// now draw the round border 
		DrawRoundRect(pGraphics, r, ulclr, width); 
	 
	} 
	else 
	{ 
		DrawRoundRect(pGraphics, r, ulclr, width); 
	} 
} 
 
//============================================================================= 
// 
// DrawRect() 
// 
// Purpose:     Draws a rectangle with a solid pen 
// 
// Parameters:  pGraphics	- [in]	pointer to the Graphics device 
//				r			- [in]	Rect that defines the round rectangle boundaries 
//				color		- [in]	Color value for the brush 
//				width		- [in]  width of the border 
//		 
// Returns:     None 
// 
void Border::DrawRect(Graphics* pGraphics, Rect r,  Color ulclr, Color brclr, int width) 
{ 
	int i, left, top, bottom, right; 
 
	// set to pixel mode 
	Unit oldPageUnit = pGraphics->GetPageUnit(); 
	pGraphics->SetPageUnit(UnitPixel); 
 
	// define the upper left pen 
	Pen ulpen(ulclr, 1); ulpen.SetAlignment(PenAlignmentCenter); 
 
	// define the bottom right pen 
	Pen brpen(brclr, 1); brpen.SetAlignment(PenAlignmentCenter); 
 
	// not sure why 
	Rect rc(r);  
 
	for(i = 0; i < width; i++) 
	{ 
		left = rc.X; top = rc.Y; bottom = rc.GetBottom()-1; right = rc.GetRight()-1; 
 
		// left 
		pGraphics->DrawLine(&ulpen, left, top, left, bottom); 
 
		// top 
		pGraphics->DrawLine(&ulpen, left, top, right, top); 
 
		// right 
		pGraphics->DrawLine(&brpen, right, top+1, right, bottom); 
 
		// bottom 
		pGraphics->DrawLine(&brpen, left+1, bottom, right, bottom); 
 
		rc.Inflate(-1, -1); 
	} 
 
	// restore page unit 
	pGraphics->SetPageUnit(oldPageUnit); 
 
} 
 
//============================================================================= 
// 
// DrawRoundRect() 
// 
// Purpose:     Draws a rounded rectangle with a solid pen 
// 
// Parameters:  pGraphics	- [in]	pointer to the Graphics device 
//				r			- [in]	Rect that defines the round rectangle boundaries 
//				color		- [in]	Color value for the brush 
//				width		- [in]  width of the border 
//		 
// Returns:     None 
// 
void Border::DrawRoundRect(Graphics* pGraphics, Rect r,  Color color, int width) 
{ 
	// set to pixel mode 
	Unit oldPageUnit = pGraphics->GetPageUnit(); 
	pGraphics->SetPageUnit(UnitPixel); 
 
	// define the pen 
	Pen pen(color, 1);	 
	pen.SetAlignment(PenAlignmentCenter); 
 
	// get the corner path 
	GraphicsPath path; 
 
	// get path 
	GetRoundRectPath(&path, r); 
 
	// draw the round rect 
	pGraphics->DrawPath(&pen, &path);	 
 
	// save smoothing mode 
	SmoothingMode oldMode = pGraphics->GetSmoothingMode();  
 
	// if width > 1 
	for(int i=1; iSetSmoothingMode(SmoothingModeNone); 
		// re enable for last line 
		if(i == (width - 1))	pGraphics->SetSmoothingMode(oldMode); 
 
		// left stroke 
		r.Inflate(-1, 0); 
 
		// get the path 
		GetRoundRectPath(&path, r); 
		 
		// draw the round rect 
		pGraphics->DrawPath(&pen, &path); 
 
		// up stroke 
		r.Inflate(0, -1); 
 
		// get the path 
		GetRoundRectPath(&path, r); 
		 
		// draw the round rect 
		pGraphics->DrawPath(&pen, &path); 
	} 
 
	// restore page unit 
	pGraphics->SetPageUnit(oldPageUnit); 
} 
 
//============================================================================= 
// 
// FillRoundRect() 
// 
// Purpose:     Fills a rounded rectangle with a solid brush.  Draws the border 
//				first then fills in the rectangle. 
// 
// Parameters:  pGraphics	- [in]	pointer to the Graphics device 
//				r			- [in]	Rect that defines the round rectangle boundaries 
//				color		- [in]	Color value for the brush 
//				radius		- [in]  radius of the rounded corner 
//		 
// Returns:     None 
// 
void Border::FillRoundRect(Graphics* pGraphics, Rect r,  Color color, int radius) 
{ 
	SolidBrush sbr(color); 
	FillRoundRect(pGraphics, &sbr, r, color, radius); 
} 
 
//============================================================================= 
// 
// FillRoundRect() 
// 
// Purpose:     Fills a rounded rectangle with the passed brush.  Fills the  
//				rectangle then drawst the border. 
// 
// Parameters:  pGraphics	- [in]	pointer to the Graphics device 
//				pBrush		- [in]  pointer to a Brush 
//				r			- [in]	Rect that defines the round rectangle boundaries 
//				color		- [in]	Color value for the border (needed in case the  
//									brush is a type other than solid) 
//				radius		- [in]  radius of the rounded corner 
//		 
// Returns:     None 
// 
void Border::FillRoundRect(Graphics* pGraphics, Brush* pBrush, Rect r, Color border, int radius) 
{ 
	int dia	= 2*radius; 
 
	// set to pixel mode 
	Unit oldPageUnit = pGraphics->GetPageUnit(); 
	pGraphics->SetPageUnit(UnitPixel); 
 
	// define the pen 
	Pen pen(border, 1);	 
	pen.SetAlignment(PenAlignmentCenter); 
 
	// get the corner path 
	GraphicsPath path; 
 
	// get path 
	GetRoundRectPath(&path, r); 
 
	// fill 
	pGraphics->FillPath(pBrush, &path); 
 
	// draw the border last so it will be on top in case the color is different 
	pGraphics->DrawPath(&pen, &path); 
 
	// restore page unit 
	pGraphics->SetPageUnit(oldPageUnit); 
} 
 
//============================================================================= 
// 
// SetBorderClip() 
// 
// Purpose:     Sets the clipping region to the inside of and including this border. 
//				The SetClip(path) of GDI+ has the same problem that a fill path does  
//				on a round rectangle, so use GDI's function instead 
// 
// Parameters:  pGraphics	- [in]	pointer to the Graphics device 
//		 
// Returns:     None 
// 
void Border::SetBorderClip(Graphics* pGraphics, Rect rc, CombineMode mode) 
{ 
	CRgn frgn; int dia; 
	 
	dia = 2*radius; 
 
	if(shape == UNDEFINED) 
	{ 
		// the border needs a shape 
		ASSERT(FALSE); 
	} 
	if(shape == RECTANGLE) 
	{ 
		frgn.CreateRectRgn(rc.X, rc.Y, rc.GetRight()+1, rc.GetBottom()+1); 
	} 
	else if(shape == ELLIPSE) 
	{ 
		frgn.CreateEllipticRgn(rc.X-1, rc.Y-1, rc.GetRight()+2, rc.GetBottom()+2); 
	} 
	// handle this special case - TODO: add for Height < dia 
	else if(rc.Width < dia) 
	{ 
		rc.Width++; rc.Height++; 
		Rect lrc(rc); lrc.Width = 2*dia;  
		Rect rrc(rc); rrc.Width = 2*dia; rrc.X = rrc.X - (rrc.Width - rc.Width); 
		CRgn lrgn, rrgn; 
		lrgn.CreateRoundRectRgn(lrc.X, lrc.Y, lrc.GetRight()+1, lrc.GetBottom()+1, dia+1, dia+1); 
		rrgn.CreateRoundRectRgn(rrc.X, rrc.Y, rrc.GetRight()+1, rrc.GetBottom()+1, dia+1, dia+1); 
		frgn.CreateRectRgn(0,0,0,0); 
		frgn.CombineRgn(&lrgn, &rrgn, RGN_AND); 
	} 
	else 
	{ 
		frgn.CreateRoundRectRgn(rc.X, rc.Y, rc.GetRight()+1, rc.GetBottom()+1, dia+1, dia+1); 
	} 
 
	Region grgn(frgn); 
 
	pGraphics->SetClip(&grgn, mode); 
 
}