Click here to Skip to main content
15,892,809 members
Articles / Desktop Programming / MFC

Enhanced Progress Bar Control

Rate me:
Please Sign up or sign in to vote.
4.88/5 (28 votes)
11 Jun 2002CPOL 235.2K   8.5K   76  
An enhanced progress control that supports gradient shading, formatted text, animation, tooltips, shape, 'snake' and reverse modes, and vertical modes
///////////////////////////////////////////////////////////////////////////////
// class CRgnX
//
// Author:  Yury Goltsman
// email:   ygprg@go.to
// page:    http://go.to/ygprg
// Copyright � 2000, Yury Goltsman
//
// This code provided "AS IS," without warranty of any kind.
// You may freely use or modify this code provided this
// Copyright is included in all derived versions.
//
// version : 1.0
// This code based on function created by Jean-Edouard Lachand-Robert
//

#include "stdafx.h"
#include "RgnX.h"

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

static HRGN BitmapToRegion(HBITMAP hBmp, COLORREF cTransparentColor, COLORREF cTolerance);

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

CRgnX::CRgnX()
{

}

CRgnX::~CRgnX()
{

}

BOOL CRgnX::FromBitmap(HBITMAP hBmp, COLORREF clrTransparent, COLORREF clrTolerance)
{
	HRGN hRgn = BitmapToRegion(hBmp, clrTransparent, clrTolerance);
	if(!hRgn)
		return FALSE;
	if(GetSafeHandle())
		DeleteObject();
	return Attach(hRgn);
}

BOOL CRgnX::FromBitmapResource(UINT nBitmapID, COLORREF clrTransparent, COLORREF clrTolerance)
{
	CBitmap bmp;
	bmp.LoadBitmap(nBitmapID);
	return FromBitmap((HBITMAP)bmp.GetSafeHandle(), clrTransparent,clrTolerance);
}

BOOL CRgnX::FromBitmapResource(LPCTSTR lpszBitmapID, COLORREF clrTransparent, COLORREF clrTolerance)
{
	CBitmap bmp;
	bmp.LoadBitmap(lpszBitmapID);
	return FromBitmap((HBITMAP)bmp.GetSafeHandle(), clrTransparent,clrTolerance);
}

BOOL CRgnX::FromText(UINT nStringID, LPRECT pRC, DWORD dwStyle, CFont* pFont, LPDRAWTEXTPARAMS lpDTParams)
{
	CString s;
	s.LoadString(nStringID);
	return CRgnX::FromText(s, pRC, dwStyle, pFont, lpDTParams);
}

BOOL CRgnX::FromText(LPCTSTR lpszText, LPRECT pRC, DWORD dwStyle, CFont* pFont, LPDRAWTEXTPARAMS lpDTParams)
{
	if(!pRC)
	{
		ASSERT(0); // must be specified
		return FALSE;
	}

	CDC dc;
	dc.CreateCompatibleDC(NULL);
	CFont* pOldFont = NULL;
	if(pFont)
		pOldFont = dc.SelectObject(pFont);
	if(dwStyle & DT_CALCRECT)
	{
		// recalc rect if DT_CALCRECT specified
		DrawTextEx(dc.GetSafeHdc(), (LPTSTR)lpszText, -1, pRC, dwStyle, lpDTParams);
		dwStyle &= ~DT_CALCRECT;
	}

	HBITMAP hBmp = ::CreateBitmap(pRC->right - pRC->left, pRC->bottom - pRC->top, 1, 1, NULL);
	HBITMAP hOldBmp = (HBITMAP)::SelectObject(dc.GetSafeHdc(), hBmp);
	if(!hOldBmp)
	{
		ASSERT(0);
		return FALSE;
	}

	dc.SetTextColor(0); // black
	dc.SetBkColor(0xffffff); // white

	// fill bitmap
	dc.FillSolidRect(pRC, 0xffffff); // white
	::DrawTextEx(dc.GetSafeHdc(), (LPTSTR)lpszText, -1, pRC, dwStyle, lpDTParams);
	
	if(pOldFont)
	dc.SelectObject(pOldFont);
	::SelectObject(dc.GetSafeHdc(), hOldBmp);

	// create rgn
	BOOL fOk = FromBitmap(hBmp, 0xffffff);
	
	::DeleteObject(hBmp);
	
	return fOk;
}

/////////////////////////////////////////////////////////////////////
// Converts a bitmap to a region
//
//	BitmapToRegion :	Create a region from the "non-transparent" pixels of a bitmap
//	Author :			Jean-Edouard Lachand-Robert (http://www.geocities.com/Paris/LeftBank/1160/resume.htm), June 1998.
//
//	hBmp :				Source bitmap
//	cTransparentColor :	Color base for the "transparent" pixels (default is black)
//	cTolerance :		Color tolerance for the "transparent" pixels.
//
//	A pixel is assumed to be transparent if the value of each of its 3 components (blue, green and red) is 
//	greater or equal to the corresponding value in cTransparentColor and is lower or equal to the 
//	corresponding value in cTransparentColor + cTolerance.
//

HRGN BitmapToRegion(HBITMAP hBmp, COLORREF cTransparentColor, COLORREF cTolerance)
{
	HRGN hRgn = NULL;

	if(hBmp)
	{
		// Create a memory DC inside which we will scan the bitmap content
		HDC hMemDC = CreateCompatibleDC(NULL);
		if(hMemDC)
		{
			// Get bitmap size
			BITMAP bm;
			GetObject(hBmp, sizeof(bm), &bm);

			// Create a 32 bits depth bitmap and select it into the memory DC 
			BITMAPINFOHEADER RGB32BITSBITMAPINFO = {	
					sizeof(BITMAPINFOHEADER),	// biSize 
					bm.bmWidth,					// biWidth; 
					bm.bmHeight,				// biHeight; 
					1,							// biPlanes; 
					32,							// biBitCount 
					BI_RGB,						// biCompression; 
					0,							// biSizeImage; 
					0,							// biXPelsPerMeter; 
					0,							// biYPelsPerMeter; 
					0,							// biClrUsed; 
					0							// biClrImportant; 
			};
			VOID * pbits32; 
			HBITMAP hbm32 = CreateDIBSection(hMemDC, (BITMAPINFO *)&RGB32BITSBITMAPINFO, DIB_RGB_COLORS, &pbits32, NULL, 0);
			if(hbm32)
			{
				HBITMAP holdBmp = (HBITMAP)SelectObject(hMemDC, hbm32);

				// Create a DC just to copy the bitmap into the memory DC
				HDC hDC = CreateCompatibleDC(hMemDC);
				if(hDC)
				{
					// Get how many bytes per row we have for the bitmap bits (rounded up to 32 bits)
					BITMAP bm32;
					GetObject(hbm32, sizeof(bm32), &bm32);
					while(bm32.bmWidthBytes % 4)
						bm32.bmWidthBytes++;

					// Copy the bitmap into the memory DC
					HBITMAP holdBmp = (HBITMAP)SelectObject(hDC, hBmp);
					BitBlt(hMemDC, 0, 0, bm.bmWidth, bm.bmHeight, hDC, 0, 0, SRCCOPY);

					// For better performances, we will use the ExtCreateRegion() function to create the
					// region. This function take a RGNDATA structure on entry. We will add rectangles by
					// amount of ALLOC_UNIT number in this structure.
					#define ALLOC_UNIT	100
					DWORD maxRects = ALLOC_UNIT;
					HANDLE hData = GlobalAlloc(GMEM_MOVEABLE, sizeof(RGNDATAHEADER) + (sizeof(RECT) * maxRects));
					RGNDATA *pData = (RGNDATA *)GlobalLock(hData);
					pData->rdh.dwSize = sizeof(RGNDATAHEADER);
					pData->rdh.iType = RDH_RECTANGLES;
					pData->rdh.nCount = pData->rdh.nRgnSize = 0;
					SetRect(&pData->rdh.rcBound, MAXLONG, MAXLONG, 0, 0);

					// Keep on hand highest and lowest values for the "transparent" pixels
					BYTE lr = GetRValue(cTransparentColor);
					BYTE lg = GetGValue(cTransparentColor);
					BYTE lb = GetBValue(cTransparentColor);
					BYTE hr = min(0xff, lr + GetRValue(cTolerance));
					BYTE hg = min(0xff, lg + GetGValue(cTolerance));
					BYTE hb = min(0xff, lb + GetBValue(cTolerance));

					// Scan each bitmap row from bottom to top (the bitmap is inverted vertically)
					BYTE *p32 = (BYTE *)bm32.bmBits + (bm32.bmHeight - 1) * bm32.bmWidthBytes;
					for(int y = 0; y < bm.bmHeight; y++)
					{
						// Scan each bitmap pixel from left to right
						for(int x = 0; x < bm.bmWidth; x++)
						{
							// Search for a continuous range of "non transparent pixels"
							int x0 = x;
							LONG *p = (LONG *)p32 + x;
							while(x < bm.bmWidth)
							{
								BYTE b = GetRValue(*p);
								if(b >= lr && b <= hr)
								{
									b = GetGValue(*p);
									if(b >= lg && b <= hg)
									{
										b = GetBValue(*p);
										if(b >= lb && b <= hb)
											// This pixel is "transparent"
											break;
									}
								}
								p++;
								x++;
							}

							if(x > x0)
							{
								// Add the pixels (x0, y) to (x, y+1) as a new rectangle in the region
								if(pData->rdh.nCount >= maxRects)
								{
									GlobalUnlock(hData);
									maxRects += ALLOC_UNIT;
									hData = GlobalReAlloc(hData, sizeof(RGNDATAHEADER) + (sizeof(RECT) * maxRects), GMEM_MOVEABLE);
									pData = (RGNDATA *)GlobalLock(hData);
								}
								RECT *pr = (RECT *)&pData->Buffer;
								SetRect(&pr[pData->rdh.nCount], x0, y, x, y+1);
								if(x0 < pData->rdh.rcBound.left)
									pData->rdh.rcBound.left = x0;
								if(y < pData->rdh.rcBound.top)
									pData->rdh.rcBound.top = y;
								if(x > pData->rdh.rcBound.right)
									pData->rdh.rcBound.right = x;
								if(y+1 > pData->rdh.rcBound.bottom)
									pData->rdh.rcBound.bottom = y+1;
								pData->rdh.nCount++;

								// On Windows98, ExtCreateRegion() may fail if the number of rectangles is too
								// large (ie: > 4000). Therefore, we have to create the region by multiple steps.
								if(pData->rdh.nCount == 2000)
								{
									HRGN h = ExtCreateRegion(NULL, sizeof(RGNDATAHEADER) + (sizeof(RECT) * maxRects), pData);
									if(hRgn)
									{
										CombineRgn(hRgn, hRgn, h, RGN_OR);
										DeleteObject(h);
									}
									else
										hRgn = h;
									pData->rdh.nCount = 0;
									SetRect(&pData->rdh.rcBound, MAXLONG, MAXLONG, 0, 0);
								}
							}
						}

						// Go to next row (remember, the bitmap is inverted vertically)
						p32 -= bm32.bmWidthBytes;
					}

					// Create or extend the region with the remaining rectangles
					HRGN h = ExtCreateRegion(NULL, sizeof(RGNDATAHEADER) + (sizeof(RECT) * maxRects), pData);
					if(hRgn)
					{
						CombineRgn(hRgn, hRgn, h, RGN_OR);
						DeleteObject(h);
					}
					else
						hRgn = h;

					// Clean up
					GlobalFree(hData);
					SelectObject(hDC, holdBmp);
					DeleteDC(hDC);
				}

				DeleteObject(SelectObject(hMemDC, holdBmp));
			}

			DeleteDC(hMemDC);
		}	
	}

	return hRgn;
}

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)
Israel Israel
Yury is Software Engineer since 1988.
His programming experience includes C#/VB.NET, WPF, C/C++(MFC/STL), Borland Delphi & C++ (VCL), JavaScript, HTML, CSS, XML, SQL, VB6, DirectX, Flash.
He has worked on PCs (DOS/Win3.1-Vista) and PocketPCs (WinCE).

Yury was born in Ukraine, but currently based in Jerusalem.

Comments and Discussions