Click here to Skip to main content
15,895,011 members
Articles / Desktop Programming / MFC

Style Toolkit - Use advanced graphics techniques to create a custom user interface

Rate me:
Please Sign up or sign in to vote.
4.97/5 (112 votes)
11 Aug 2008CPOL15 min read 229.5K   18.1K   403  
The Style Toolkit allows you to modernize the look of your programs using gradients, transparency, PNG images, and more.
//
// Layers.cpp: source file for the Layers class.
//
// Author:  Darren Sessions
//          
//
// Description:
//   
//		The Layers class contains all the structures and arrays used by the 
//		Style Toolkit.
//
// History
//     Version 1.1 - 2008 August 1
//     - More features added
//
//     Version 1.1 - 2008 July 22
//     - 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 "Layers.h"

#include "math.h"

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

//=============================================================================
// Construction/Destruction
//=============================================================================

//=============================================================================
// empty constructor
Layers::Layers()
//=============================================================================
{
	Init();
}

//=============================================================================
// pre allocate alloc number of layers and strings
Layers::Layers(int alloc)
//=============================================================================
{
	Init(alloc);
}

//=============================================================================
// Layers Initializer
void Layers::Init(int alloc)
//=============================================================================
{
	::ZeroMemory(&Frame, sizeof(FRAME));
	m_Layers.reserve(alloc);
	m_Strings.reserve(alloc);
}

//=============================================================================
// destructor
Layers::~Layers()
//=============================================================================
{
	// delete the image objects
	for(image = m_Images.begin(); image != m_Images.end(); ++image)
	{
		if((*image).pImage) delete (*image).pImage;
	}

	// delete the region objects
	for(layer = m_Layers.begin(); layer != m_Layers.end(); ++layer)
	{
		if((*layer).pRegion) delete (*layer).pRegion;
	}
}

//=============================================================================
// copy constructor
Layers::Layers(const Layers& other)
//=============================================================================
{
	Copy(other);
}

//=============================================================================
// copy function
void Layers::Copy(const Layers& other)
//=============================================================================
{
	// copy frame
	memcpy(&Frame, &other.Frame, sizeof(FRAME));

	// Copy layers and strings
	CopyLayers(other);
}

//=============================================================================
// copy function helper
void Layers::CopyLayers(const Layers& other)
//=============================================================================
{
	// copy OPERATION
	for(clayer = other.m_Layers.begin(); clayer != other.m_Layers.end(); ++clayer)
	{
		m_Layers.push_back(*clayer);
	}

	// copy TEXTINFO
	for(cwsIter = other.m_Strings.begin(); cwsIter != other.m_Strings.end(); ++cwsIter)
	{
		m_Strings.push_back(*cwsIter);
	}

	// copy IMAGEINFO
	for(cimage = other.m_Images.begin(); cimage != other.m_Images.end(); ++cimage)
	{
		m_Images.push_back(*cimage);
	}
}

//=============================================================================
// operator =
Layers& Layers::operator=(const Layers& other)
//=============================================================================
{
	m_Layers.clear();
	m_Strings.clear();
	m_Images.clear();
	Copy(other);
	return *this;
}

//=============================================================================
void Layers::MakeFrame(SRect sr)
//=============================================================================
{
	Frame.frect = sr.rect;
	// default to rects
	Frame.OuterBorder.shape = RECTANGLE;
	Frame.MiddleBorder.shape = RECTANGLE;
	Frame.InnerBorder.shape = RECTANGLE;
	MakeFrameRgn();
}

//=============================================================================
void Layers::MakeFrameRgn()
//=============================================================================
{
	Region rgn(Frame.frect);
	Frame.clipRgn.MakeEmpty();
	Frame.clipRgn.Union(&rgn);
}

//=============================================================================
//
// AddLayer()
//
// Purpose:     Adds a layer to the stack.  A transparent layer will be created
//				and set to the Frames rect just prior to painting 
//
// Parameters:  None
//				
// Returns:     None
//
int Layers::AddLayer()
{
	OPERATION Layer;
	::ZeroMemory(&Layer, sizeof(OPERATION));

	m_Layers.push_back(Layer);
	return (int)(m_Layers.size() - 1);
}

//=============================================================================
//
// AddLayer()
//
// Purpose:     Adds a layer to the stack.  If only the rect is passed
//				a transparent layer will be created
//
// Parameters:  sr		- [in] Rect boundary of this layer
//				clr		- [in] optional - color of this layer
//				
// Returns:     None
//
int Layers::AddLayer(SRect sr, Clr clr)
{
	OPERATION Layer;
	::ZeroMemory(&Layer, sizeof(OPERATION));

	Layer.lrect = sr.rect;
	Layer.clr1 = clr.value;

	m_Layers.push_back(Layer);

	return (int)(m_Layers.size() - 1);
}

//=============================================================================
//
// SetShape()
//
// Purpose:     Sets the border style and radius
//
// Parameters:  style	- [in] the border style 
//				radius	- [in] optional - the corner radius for round borders
//				
// Returns:     None
//
void Layers::SetShape(int shape, int radius)
{
	MakeFrameRgn();
	
	// make all the borders the same
	Frame.OuterBorder.radius = radius;
	Frame.MiddleBorder.radius = radius;
	Frame.InnerBorder.radius = radius;

	Frame.OuterBorder.shape = shape;
	Frame.MiddleBorder.shape = shape;
	Frame.InnerBorder.shape = shape;

	// only the outer border can be transition
	if(shape == TRANSITION)
	{
		Frame.MiddleBorder.shape = ROUNDRECT;
		Frame.InnerBorder.shape = ROUNDRECT;
	}
}

//=============================================================================
//
// SetClipping()
//
// Purpose:     Sets the Graphics clipping region to the frames tlclr border
//
// Parameters:  None
//				
// Returns:     None
//
void Layers::SetClipping(Graphics* pGraphics)
{
	Frame.OuterBorder.SetBorderClip(pGraphics, Frame.frect);
}
//=============================================================================
//
// RestoreClipping()
//
// Purpose:     Restores the Graphics clipping region
//
// Parameters:  None
//				
// Returns:     None
//
void Layers::RestoreClipping(Graphics* pGraphics)
{
	pGraphics->ResetClip();
}

//=============================================================================
//
// PaintBorders()
//
// Purpose:     Paints the Outer, Middle and Inner borders based on the 
//				borders paramaters.
//
// Parameters:  None
//				
// Returns:     None
//
void Layers::PaintBorders()
{
	Rect bdr(Frame.frect);
	int width, ovlap; 

	if( (width = Frame.OuterBorder.width) )
	{
		ovlap = width;
		if( (Frame.MiddleBorder.shape != RECTANGLE) && (Frame.MiddleBorder.width > 0) &&
			(Frame.MiddleBorder.ulclr.GetA() == 255) ) ovlap++;
		Frame.OuterBorder.Draw(m_pGdi, bdr, Frame.OuterBorder.ulclr, Frame.OuterBorder.brclr, ovlap);
		bdr.Inflate(-width, -width);
	}

	if( (width = Frame.MiddleBorder.width) )
	{
		ovlap = width;
		if( (Frame.InnerBorder.shape != RECTANGLE) && (Frame.InnerBorder.width > 0) &&
			(Frame.InnerBorder.ulclr.GetA() == 255) ) ovlap++;
		Frame.MiddleBorder.Draw(m_pGdi, bdr, Frame.MiddleBorder.ulclr, Frame.MiddleBorder.brclr, ovlap);
		bdr.Inflate(-width, -width);
	}

	if( (width = Frame.InnerBorder.width) )
	{
		Frame.InnerBorder.Draw(m_pGdi, bdr, Frame.InnerBorder.ulclr, Frame.InnerBorder.brclr, width);
	}

}

//=============================================================================
//
// PaintLayers()
//
// Purpose:		Paints the solid or gradient base rectangle
//				The rectangle is clipped with the Frame struct
//
//=============================================================================
void Layers::PaintLayers()
{
	Color colors[3];

	if(m_pLayer->LayerType == SOLID)
	{
		SolidBrush br(m_pLayer->clr1);
		Fill(&br);
	}
	else if(m_pLayer->LayerType == TRIGRAD)
	{
		colors[0] = m_pLayer->clr1;
		colors[1] = m_pLayer->clr2;
		colors[2] = m_pLayer->clr3;

		REAL positions[] = { 0.0f, m_pLayer->rParams[0], 1.0f };

		LinearGradientMode mode = (LinearGradientMode)m_pLayer->gradMode;

		LinearGradientBrush lgbr(m_pLayer->lrect, Color::Black, Color::White, mode);
		lgbr.SetInterpolationColors(colors, positions, 3);

		Fill(&lgbr);
	}
	else if(m_pLayer->LayerType == BIGRAD)
	{
		// check center point for minus flag
		float dp = m_pLayer->rParams[0];

		colors[0] = m_pLayer->clr1;

		if(dp < 0.0f)
		{
			colors[1] = m_pLayer->clr2;
			dp = 1.0f + dp;
		}
		else
		{
			colors[1] = m_pLayer->clr1;
		}
		colors[2] = m_pLayer->clr2;

		REAL positions[] = { 0.0f, dp, 1.0f };
		
		LinearGradientMode mode = (LinearGradientMode)m_pLayer->gradMode;

		LinearGradientBrush lgbr(	m_pLayer->lrect, m_pLayer->clr1, 
									m_pLayer->clr2, mode);

		lgbr.SetWrapMode(WrapModeClamp);
		lgbr.SetInterpolationColors(colors, positions, 3);

		Fill(&lgbr);
	}
	else if(m_pLayer->LayerType == RADIAL)
	{
		GraphicsPath path;
		Rect pr = m_pLayer->lrect;

		path.AddEllipse(pr);
		path.CloseFigure();

		PathGradientBrush pgr(&path);

		int count = 1;
		Color clr = m_pLayer->clr1;
		Color t[1];	t[0] = m_pLayer->clr2; 

		pgr.SetCenterPoint( Point(pr.X + pr.Width/2, pr.Y + pr.Height/2) );
		pgr.SetSurroundColors(t, &count);
		pgr.SetCenterColor(clr);

		m_pGdi->FillPath(&pgr, &path);
	}
	else if(m_pLayer->LayerType == IMAGE)
	{
		PaintImage();
	}
	else if(m_pLayer->LayerType == STRING)
	{
		PaintString();
	}
	else if(m_pLayer->LayerType == EFFECTSTRING)
	{
		PaintEffectString();
	}
	else if(m_pLayer->LayerType >= TOP_EDGE && m_pLayer->LayerType <= BOTTOM_EDGE_BEVEL)
	{
		PaintBar();
	}
	else if(m_pLayer->LayerType == CREATERGN)
	{
		GraphicsPath path;
		GetPath(&path);
		Region rgn(&path);
		m_pLayer->pRegion = rgn.Clone();
	}
	else if(m_pLayer->LayerType == COMBINEMOD)
	{
		Region *pR0, *pR1;
		pR0 = m_Layers[m_pLayer->rgnIndex[0]].pRegion;
		pR1 = m_Layers[m_pLayer->rgnIndex[1]].pRegion;

		CombineRgns(pR0, pR1, m_pLayer->clipMode);
	}
	else if(m_pLayer->LayerType == COMBINENEW)
	{
		Region *pR0, *pR1;
		pR0 = m_Layers[m_pLayer->rgnIndex[0]].pRegion;
		pR1 = m_Layers[m_pLayer->rgnIndex[1]].pRegion;

		m_pLayer->pRegion = pR0->Clone();

		CombineRgns(m_pLayer->pRegion, pR1, m_pLayer->clipMode);
	}
	else if(m_pLayer->LayerType == APPLYCLIP)
	{
		m_pGdi->SetClip(m_Layers[m_pLayer->rgnIndex[0]].pRegion);
	}
	else if(m_pLayer->LayerType == RESTORECLIP)
	{
		Frame.OuterBorder.SetBorderClip(m_pGdi, Frame.frect);
	}
	else if(m_pLayer->LayerType == REGIONBLUR)
	{
		RegionBlur(m_Layers[m_pLayer->rgnIndex[0]].pRegion, m_pLayer->width);
	}
	else if(m_pLayer->LayerType == WRAP)
	{
		colors[0] = m_pLayer->clr1;
		colors[1] = m_pLayer->clr2;
		colors[2] = m_pLayer->clr3;

		REAL positions[] = { 0.0f, 0.5, 1.0f };

		//LinearGradientMode mode = (LinearGradientMode)m_pLayer->gradMode;
		LinearGradientMode mode = (LinearGradientMode)HORIZ;

		Rect sr = m_pLayer->lrect; sr.Width = m_pLayer->width;
		LinearGradientBrush lgbr(sr, Color::Black, Color::White, mode);
		lgbr.SetInterpolationColors(colors, positions, 3);

		Fill(&lgbr);
	}
	else if(m_pLayer->LayerType == SETSMOOTH)
	{
		SmoothingMode sm = SmoothingMode(m_pLayer->clipMode);
		m_pGdi->SetSmoothingMode(sm);
	}
	else if(m_pLayer->LayerType == RESTORESMOOTH)
	{
		// this is the default smoothing mode
		m_pGdi->SetSmoothingMode(SmoothingModeAntiAlias);
	}
	else if(m_pLayer->LayerType == APPLYXFORM)
	{
		Transform(m_pLayer->gradMode);
	}
	
}

//
// fills based on the layer shape
//
void Layers::Fill(Brush* pBrush)
{
	Rect r = m_pLayer->lrect;
	int style = Frame.OuterBorder.shape;

	if(style == RECTANGLE)
	{
		m_pGdi->FillRectangle(pBrush, r);
	}
	else if(style == ELLIPSE)
	{
		m_pGdi->FillEllipse(pBrush, r);
	}
	else if( (style == ROUNDRECT) || (style == TRANSITION) )
	{
		Color brdr = m_pLayer->clr1;
		int radius = Frame.OuterBorder.radius;

		Frame.OuterBorder.FillRoundRect(m_pGdi, pBrush, r, brdr, radius);
	}
}

//
// Get a path based on the layer shape
//
void Layers::GetPath(GraphicsPath *pPath)
{
	if(m_pLayer->LayerShape == RECTANGLE)
	{
		pPath->AddRectangle(m_pLayer->lrect);
	}
	else if(m_pLayer->LayerShape == ELLIPSE)
	{
		pPath->AddEllipse(m_pLayer->lrect);
	}
	else if(m_pLayer->LayerShape == ROUNDRECT)
	{
		Frame.OuterBorder.GetRoundRectPath(pPath, m_pLayer->lrect);
	}
}

//
//	CombineRgns()
//	
void Layers::CombineRgns(Region* pRgn1, Region* pRgn2, int mode)
{
	if(m_pLayer->clipMode == INTERSECT)
	{
		pRgn1->Intersect(pRgn2);
	}
	else if(m_pLayer->clipMode == UNION)
	{
		pRgn1->Union(pRgn2);
	}
	else if(m_pLayer->clipMode == XOR)
	{
		pRgn1->Xor(pRgn2);
	}
	else if(m_pLayer->clipMode == EXCLUDE)
	{
		pRgn1->Exclude(pRgn2);
	}
	else if(m_pLayer->clipMode == COMPLEMENT)
	{
		pRgn1->Complement(pRgn2);
	}
}

void Layers::RegionBlur(Region* pRgn, int amount)
{
	// blur the frame rect
	Rect rc = Frame.frect;

	// size for bitmaps
	int width = rc.Width;
	int height = rc.Height;

   	// move rect to origin for bitmaps
	Rect bmrc = rc;	bmrc.X = 0; bmrc.Y = 0;

	// create a bitmap for src data
	Bitmap sbm(width, height);
	// create a bitmap for dst data
	Bitmap dbm(width, height);

	// create a graphics object
	Graphics gr(&sbm);

	// fill the region
	pRgn->Translate(-rc.X, -rc.Y);
	gr.SetClip(pRgn);
	SolidBrush gbr(m_pLayer->clr1);
	gr.FillRectangle(&gbr, bmrc);
	gr.ResetClip();

	// call blur function
	SigmoidBlur(&dbm, &sbm, amount);
  
	// draw image
	m_pGdi->DrawImage(&dbm, rc, 0, 0, width, height, UnitPixel);
}

//
// This is an optimized version of the blur method from
// this article: http://www.codeproject.com/KB/GDI-plus/aqualize.aspx
//
// Use this function with caution! As the bitmap size and blur amount
// increase, so will the computation time.
//
void Layers::SigmoidBlur(Bitmap* pDst, Bitmap* pSrc, int boxw)
{
	int width = pSrc->GetWidth();
	int height = pSrc->GetHeight();

	// these bitmpas need to be the same size
	ASSERT(width == pDst->GetWidth() && height == pSrc->GetHeight());

	Rect bmrc(0, 0, width, height);

	// lock the src bits
	BitmapData sbmData;
	pSrc->LockBits(&bmrc, ImageLockModeWrite | ImageLockModeRead, PixelFormat32bppARGB, &sbmData);

	// access variables
	int line = sbmData.Stride/4;
	UINT32* src = (UINT32*)sbmData.Scan0;

	// lock the dst bits 
	BitmapData dbmData;
	pDst->LockBits(&bmrc, ImageLockModeWrite | ImageLockModeRead, PixelFormat32bppARGB, &dbmData);
	UINT32* dst = (UINT32*)dbmData.Scan0;

	float a, r, g, b, weight, weightsum;
	UINT32 ia, ir, ig, ib, c;

	float* weights = new float[2*boxw+1];

	weights[boxw] = 1.0f;

	for(int i = 0; i < boxw; i++) 
	{
		float y = Sigmoid((float)i, 0.0f, (float)((boxw + 1)/2), (float)(boxw + 1));
		weights[i] = y;
		weights[boxw * 2 - i] = y;
	}

	// horizontal blur
	for (int row = 0; row < height; row++) 
	{
		for (int col = 0; col < width; col++) 
		{
			r = 0; g = 0; b = 0; a = 0; weightsum = 0;

			for (int i = 0; i < boxw * 2 + 1; i++) 
			{
				int x = col - boxw + i;
				if (x < 0) 
				{
					i += -x;
					x = 0;
				}
				if (x > width - 1)
					break;

				c = src[x + row*line];

				weight = weights[i];
				a += ((c >> 24) & 0x000000FF) * weight;
				r += ((c >> 16) & 0x000000FF) * weight;
				g += ((c >>  8) & 0x000000FF) * weight;
				b += ((c >>  0) & 0x000000FF) * weight;
				weightsum += weight;
			}

			ia = min((UINT32)(a/weightsum), 255);
			ir = min((UINT32)(r/weightsum), 255);
			ig = min((UINT32)(g/weightsum), 255);
			ib = min((UINT32)(b/weightsum), 255);

			dst[col + row*line] = ((ia<<24) | (ir<<16) | (ig<<8) | (ib<<0));
		}
	}

	// vertical blur
	for (int col = 0; col < width; col++) 
	{
		for (int row = 0; row < height; row++) 
		{
			r = 0; g = 0; b = 0; a = 0; weightsum = 0;

			for (int i = 0; i < boxw * 2 + 1; i++) 
			{
				int y = row - boxw + i;
				if (y < 0) 
				{
					i += -y;
					y = 0;
				}
				if (y > height - 1)
					break;

				c = dst[col + y * line];

				weight = weights[i];
				a += ((c >> 24) & 0x000000FF) * weight;
				r += ((c >> 16) & 0x000000FF) * weight;
				g += ((c >>  8) & 0x000000FF) * weight;
				b += ((c >>  0) & 0x000000FF) * weight;
				weightsum += weight;
			}

			ia = min((UINT32)(a/weightsum), 255);
			ir = min((UINT32)(r/weightsum), 255);
			ig = min((UINT32)(g/weightsum), 255);
			ib = min((UINT32)(b/weightsum), 255);

			dst[col + row*line] = ((ia<<24) | (ir<<16) | (ig<<8) | (ib<<0));
		}
	}

	pDst->UnlockBits(&dbmData);
	pSrc->UnlockBits(&sbmData);

	delete [] weights;
}

//
// This blur method uses a constant weight for the sliding
// box, which makes the calculation independant of the box
// size.  Also only the endpoints need to be calculated as
// the box slides.  This makes the algorithm much faster,
// but it doesn't look as nice as the SigmoidBlur.
//
void Layers::FastBlur(Bitmap* pDst, Bitmap* pSrc, int boxw)
{
	// this function is not finished :)
	ASSERT(FALSE);
#if 0
	// the approach is here
	// http://www.gamasutra.com/features/20010209/Listing3.cpp

	int width = pSrc->GetWidth();
	int height = pSrc->GetHeight();

	// these bitmpas need to be the same size
	ASSERT(width == pDst->GetWidth() && height == pSrc->GetHeight());

	Rect bmrc(0, 0, width, height);

	// lock the src bits
	BitmapData sbmData;
	pSrc->LockBits(&bmrc, ImageLockModeWrite | ImageLockModeRead, PixelFormat32bppARGB, &sbmData);

	// access variables
	int line = sbmData.Stride/4;
	UINT32* src = (UINT32*)sbmData.Scan0;

	// lock the dst bits 
	BitmapData dbmData;
	pDst->LockBits(&bmrc, ImageLockModeWrite | ImageLockModeRead, PixelFormat32bppARGB, &dbmData);
	UINT32* dst = (UINT32*)dbmData.Scan0;

	float a, r, g, b, weight, weightsum;
	UINT32 ia, ir, ig, ib, c, alpha;

	if (boxw >= width) boxw = width-1;

	weightsum = 2*boxw + 1;

	for(int row = 0; row < height; row++)
	{
		//int tot=0;
		r = 0; g = 0; b = 0; a = 0; //weightsum = 0;

		for(int col = 0; col < boxw; col++)
		{
			// tot += src[col];

			c = src[col + row*line];

			a += ((c >> 24) & 0x000000FF);
			r += ((c >> 16) & 0x000000FF);
			g += ((c >>  8) & 0x000000FF);
			b += ((c >>  0) & 0x000000FF);
		}

		for(int col = 0; col < width; col++)
		{
			if( col > boxw)
			{	
				// tot -= src[-boxw-1];

				c = src[(col-boxw-1) + row*line];

				a -= ((c >> 24) & 0x000000FF);
				r -= ((c >> 16) & 0x000000FF);
				g -= ((c >>  8) & 0x000000FF);
				b -= ((c >>  0) & 0x000000FF);
			}

			if( (col + boxw) < width)
			{
				// tot += src[boxw];

				c = src[boxw + row*line];

				a += ((c >> 24) & 0x000000FF);
				r += ((c >> 16) & 0x000000FF);
				g += ((c >>  8) & 0x000000FF);
				b += ((c >>  0) & 0x000000FF);

				alpha = ((c >> 24) & 0x000000FF);
				r = alpha * r/255.0f;
				g = alpha * g/255.0f;
				b = alpha * b/255.0f;
			}
			
			//*dst++ = UINT(tot*mul);

			ia = min((UINT32)(a/weightsum), 255);
			ir = min((UINT32)(r/weightsum), 255);
			ig = min((UINT32)(g/weightsum), 255);
			ib = min((UINT32)(b/weightsum), 255);

			UINT32 clr = ((ia<<24) | (ir<<16) | (ig<<8) | (ib<<0));

			if(clr == 0xff000000)
				int tmp=1;

			dst[col + row*line] = clr;
			
			//src++;
		}
	}	

	pDst->UnlockBits(&dbmData);
	pSrc->UnlockBits(&sbmData);
#endif
}

//=============================================================================
//
// PaintBar()
//
// Purpose:		Paints a bar at the edge of a frame
//
//=============================================================================
void Layers::PaintBar()
{
	// various modes leave artifacts
	SmoothingMode oldMode = m_pGdi->GetSmoothingMode();
	m_pGdi->SetSmoothingMode(SmoothingModeNone);

	LinearGradientMode gtype;

	Rect rc = m_pLayer->lrect;
	int type = m_pLayer->LayerType; 
	int ewidth = m_pLayer->width;

	GraphicsPath path;

	if(type == LEFT_EDGE  || type == LEFT_EDGE_BEVEL)
	{
		gtype = (LinearGradientMode)HORIZ;
		rc.Width = ewidth;

		if(type == LEFT_EDGE)
		{	
			path.AddRectangle(rc);
		}
		else
		{
			Point corners[] = { Point(rc.X, rc.Y), 
								Point(rc.X + ewidth, rc.Y + ewidth),
								Point(rc.X + ewidth, rc.Y + rc.Height - ewidth),
								Point(rc.X, rc.Y + rc.Height) };
			
			path.AddPolygon(corners, 4);
		}
	}
	else if(type == TOP_EDGE || type == TOP_EDGE_BEVEL)
	{
		gtype = (LinearGradientMode)VERT;
		rc.Height = ewidth;

		if(type == TOP_EDGE)
		{	
			path.AddRectangle(rc);
		}
		else
		{
			Point corners[] = { Point(rc.X, rc.Y), 
								Point(rc.X + rc.Width, rc.Y),
								Point(rc.X + rc.Width - ewidth, rc.Y + ewidth),
								Point(rc.X + ewidth, rc.Y + ewidth) };
			
			path.AddPolygon(corners, 4);
		}
	}
	else if(type == RIGHT_EDGE || type == RIGHT_EDGE_BEVEL)
	{
		gtype = (LinearGradientMode)HORIZ;
		rc.X = rc.X + rc.Width - ewidth;
		rc.Width = ewidth;

		if(type == RIGHT_EDGE)
		{	
			path.AddRectangle(rc);
		}
		else
		{
			Point corners[] = { Point(rc.X, rc.Y + ewidth), 
								Point(rc.X + rc.Width, rc.Y),
								Point(rc.X + rc.Width, rc.Y + rc.Height),
								Point(rc.X, rc.Y + rc.Height - ewidth) };
			
			path.AddPolygon(corners, 4);
		}
	}
	else if(type == BOTTOM_EDGE || type == BOTTOM_EDGE_BEVEL)
	{
		gtype = (LinearGradientMode)VERT;
		rc.Y = rc.Y + rc.Height - ewidth;
		rc.Height = ewidth;

		if(type == BOTTOM_EDGE)
		{	
			path.AddRectangle(rc);
		}
		else
		{
			Point corners[] = { Point(rc.X + ewidth, rc.Y), 
								Point(rc.X + rc.Width - ewidth, rc.Y),
								Point(rc.X + rc.Width, rc.Y + rc.Height),
								Point(rc.X, rc.Y + rc.Height) };
			
			path.AddPolygon(corners, 4);
		}
	}

	Color colors[5];
	colors[0] = m_pLayer->clr1;
	colors[1] = m_pLayer->clr2;
	colors[2] = m_pLayer->clr3;

	LinearGradientBrush lgbr(rc, colors[0], colors[2], gtype);

	// the extra interpolation point appears to get rid of some artifacts
	if(m_pLayer->rParams[1] != NULL)
	{
		float profile[3];
		profile[0] = m_pLayer->rParams[0];
		profile[1] = m_pLayer->rParams[1];
		profile[2] = m_pLayer->rParams[2];

		lgbr.SetInterpolationColors(colors, profile, 3);
	}

	// relies on clipping region for round rects
	m_pGdi->FillPath(&lgbr, &path);

	m_pGdi->SetSmoothingMode(oldMode);
}

//=============================================================================
//
// PaintString()
//
// Purpose:     Draws the strings
//
//=============================================================================
void Layers::PaintString()
{
	TEXTINFO* pTI = &m_Strings[m_pLayer->strIndex];

	Font font(pTI->fname, (float)pTI->fsize, pTI->fstyle, UnitPixel);
	SolidBrush br(m_pLayer->clr1);

	RectF rf = MakeRectF(m_pLayer->lrect);

	// add in any offset
	rf.X += pTI->foffset.X;
	rf.Y += pTI->foffset.Y;
	rf.Width -= pTI->foffset.X;
	rf.Height -= pTI->foffset.Y;

	StringFormat sf;
	if(pTI->align == ALIGN_NEAR)
	{
		sf.SetAlignment(StringAlignmentNear);
		sf.SetLineAlignment(StringAlignmentNear);
	}
	else if (pTI->align == ALIGN_CENTER)
	{
		sf.SetAlignment(StringAlignmentCenter);
		sf.SetLineAlignment(StringAlignmentCenter);
	}
	else if (pTI->align == ALIGN_FAR)
	{
		sf.SetAlignment(StringAlignmentFar);
		sf.SetLineAlignment(StringAlignmentFar);
	}

	m_pGdi->DrawString(pTI->string.c_str(), -1, &font, rf, &sf, &br);
}


//=============================================================================
//
// PaintEffectString()
//
// Purpose:     Draws the strings
//
//=============================================================================
void Layers::PaintEffectString()
{
	TEXTINFO* pTI = &m_Strings[m_pLayer->strIndex];

	Font font(pTI->fname, (float)pTI->fsize, pTI->fstyle, UnitPixel);
	SolidBrush br(m_pLayer->clr1);

	WCHAR* wstr = (WCHAR*)pTI->string.c_str();

	SolidBrush br2(m_pLayer->clr2);

	PointF pt(pTI->foffset.X + m_pLayer->lrect.X, pTI->foffset.Y + m_pLayer->lrect.Y);

	if(m_pLayer->rParams[0] == OUTLINE)
	{
		int width = (int)m_pLayer->rParams[1]; 
		int span = 2*width + 1;

		GraphicsPath path;
		StringFormat sf;
		FontFamily ff(pTI->fname);

		path.SetFillMode(FillModeWinding);

		for(int x = 0; x < span; x++)
		{
			for(int y = 0; y < span; y++)
			{
				PointF dp(pt.X + x - width, pt.Y + y - width);
				m_pGdi->DrawString(wstr, -1, &font, dp, &br2);
			}
		}

		// draw face
		m_pGdi->DrawString(wstr, -1, &font, pt, &br);
	}
}


//=============================================================================
//
// PaintImage()
//
// Purpose:     Draws the image set by SetImage
//
//=============================================================================
void Layers::PaintImage()
{

	IMAGEINFO* pII = &m_Images[m_pLayer->imgIndex];

	pII->pImage = new GResource;

	RectF srcF, destF, paneF; Unit unit; 

	// convert pane offset point to float
	PointF dptF((float)pII->destpt.X, (float)pII->destpt.Y);

	// convert src offset point to float
	PointF sptF((float)pII->srcpt.X, (float)pII->srcpt.Y);

	// convert pane rect to float
	Rect rc = Frame.frect;
	paneF.X = (float)rc.X; paneF.Y = (float)rc.Y;
	paneF.Width = (float)rc.Width; paneF.Height = (float)rc.Height;

	if( pII->pImage->Load(pII->resID, pII->resType) )
	{
		// get image dimensions
		pII->pImage->m_pBitmap->GetBounds(&srcF, &unit);

		// offset into src
		srcF.Offset(sptF); srcF.Width -= sptF.X; srcF.Height -= sptF.Y;
		
		// destination is pane rect + destpt offset
		destF = paneF; destF.Offset(dptF);

		// if src is smaller, adjust dest
		if(destF.Width > srcF.Width) destF.Width = srcF.Width;
		if(destF.Height > srcF.Height) destF.Height = srcF.Height;

		// if dest is smaller and clipping is on, adjust src
		if(pII->clip == TRUE)
		{
			if(srcF.Width > destF.Width) srcF.Width = destF.Width;
			if(srcF.Height > destF.Height) srcF.Height = destF.Height;
		}

		if(pII->xform == NOXFORM)
		{
			m_pGdi->DrawImage(*pII->pImage, destF, srcF.X, srcF.Y, srcF.Width, srcF.Height, unit);
		}
		else if(pII->xform == LIGHTEN)
		{
			ColorMatrix HotMat = {	1.05f, 0.00f, 0.00f, 0.00f, 0.00f,
									0.00f, 1.05f, 0.00f, 0.00f, 0.00f,
									0.00f, 0.00f, 1.05f, 0.00f, 0.00f,
									0.00f, 0.00f, 0.00f, 1.00f, 0.00f,
									0.05f, 0.05f, 0.05f, 0.00f, 1.00f	};

			ImageAttributes ia;
			ia.SetColorMatrix(&HotMat);

			m_pGdi->DrawImage(*pII->pImage, destF, srcF.X, srcF.Y, srcF.Width, srcF.Height, unit, &ia);
		}
		else if(pII->xform == GRAYSCALE)
		{
			ColorMatrix GrayMat = {	0.30f, 0.30f, 0.30f, 0.00f, 0.00f,
									0.59f, 0.59f, 0.59f, 0.00f, 0.00f,
									0.11f, 0.11f, 0.11f, 0.00f, 0.00f,
									0.00f, 0.00f, 0.00f, 1.00f, 0.00f,
									0.00f, 0.00f, 0.00f, 0.00f, 1.00f	};

			ImageAttributes ia;
			ia.SetColorMatrix(&GrayMat);

			m_pGdi->DrawImage(*pII->pImage, destF, srcF.X, srcF.Y, srcF.Width, srcF.Height, unit, &ia);
		}
	}
}

void Layers::Transform(int xform)
{
	Rect r = m_pLayer->lrect;

	Bitmap *pBmp = m_pDC->GetBitmap();

	if(xform == LIGHTEN)
	{
		ColorMatrix HotMat = {	1.05f, 0.00f, 0.00f, 0.00f, 0.00f,
								0.00f, 1.05f, 0.00f, 0.00f, 0.00f,
								0.00f, 0.00f, 1.05f, 0.00f, 0.00f,
								0.00f, 0.00f, 0.00f, 1.00f, 0.00f,
								0.05f, 0.05f, 0.05f, 0.00f, 1.00f	};

		ImageAttributes ia;
		ia.SetColorMatrix(&HotMat);
	
		m_pGdi->DrawImage((Image*)pBmp, r, 0, 0, r.Width, r.Height, UnitPixel, &ia);
	}
	else if(xform == GRAYSCALE)
	{
		ColorMatrix GrayMat = {	0.30f, 0.30f, 0.30f, 0.00f, 0.00f,
								0.59f, 0.59f, 0.59f, 0.00f, 0.00f,
								0.11f, 0.11f, 0.11f, 0.00f, 0.00f,
								0.00f, 0.00f, 0.00f, 1.00f, 0.00f,
								0.00f, 0.00f, 0.00f, 0.00f, 1.00f	};

		ImageAttributes ia;
		ia.SetColorMatrix(&GrayMat);
	
		m_pGdi->DrawImage((Image*)pBmp, r, 0, 0, r.Width, r.Height, UnitPixel, &ia);
	}

	delete pBmp;
}

//=============================================================================
//
// Regenerate
//
// Purpose:		Marks the stack to be regenerated on the next paint operation.
//				If a layer index is passed, it will reset all of the layer rects
//				above and including the index to zero, causing them to be 
//				recalcculated from the frame rect
//
// Parameters:  x	- [in] the x offset
//				y	- [in] the y offset
//				
// Returns:     None
//
void Layers::Regenerate(int layerIdx)
{
	Frame.bUseBitmap = FALSE;

	if(layerIdx != -1)
	{
		for(layer = m_Layers.begin() + layerIdx; layer != m_Layers.end(); ++layer)
		{
			(*layer).lrect.Width = 0; (*layer).lrect.Height = 0;
		}
	}
}

//=============================================================================
//
// PaintStack()
//
// Procedure:	1.  Create the borders and clipping region
//				2.  Paint all the layers into the device context
//				3.  Flatten the layers into a bitmap
//
//=============================================================================
void Layers::PaintStack(MemDC* pDC, Graphics* pGraphics)
{
	// the Frame must have a rect
	ASSERT(!(Frame.frect.Width == 0 && Frame.frect.Height == 0));

	// set the Graphics object
	m_pGdi = pGraphics;

	// set the MemDC object
	m_pDC = pDC;

	// set clip region
	SetClipping(m_pGdi);

	for(layer = m_Layers.begin(); layer != m_Layers.end(); ++layer)
	{
		// pointer to this layer
		m_pLayer = &(*layer);

		// if the layer rect is not defined, it uses the Frame rect
		if( (m_pLayer->lrect.Width == 0) && (m_pLayer->lrect.Height == 0) )
		{
			m_pLayer->lrect = Frame.frect;
		}

		if(m_pLayer->LayerShape == UNDEFINED)
		{
			m_pLayer->LayerShape = Frame.OuterBorder.shape;
		}

		// paint the layer
		PaintLayers();
	}

	// restore the clip region
	RestoreClipping(m_pGdi);

	// paint the borders
	PaintBorders();

	// delete dc in case of regeneration
	m_dc.DeleteDC();

	int x		= Frame.frect.X;
	int y		= Frame.frect.Y;
	int width	= Frame.frect.Width; 
	int height	= Frame.frect.Height;

	// store into m_dc
	CBitmap bmp;
	m_dc.CreateCompatibleDC(pDC);
	bmp.CreateCompatibleBitmap(pDC, width, height);
	m_dc.SelectObject(&bmp);
	m_dc.BitBlt(0, 0, width, height, pDC, x, y, SRCCOPY);
	bmp.DeleteObject();

	Frame.bUseBitmap = TRUE;
}

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, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
United States United States
I am currently working as a consultant in Southern California.

I have worked as a Hardware Engineer, Firmware Engineer, Software Engineer and Applications Engineer.

I spent 13 years in the Disk Drive industry and the last 7 working in GPS.

Comments and Discussions