Converting a bitmap to a region

WEBINAR: On-demand webcast

How to Boost Database Development Productivity on Linux, Docker, and Kubernetes with Microsoft SQL Server 2017 REGISTER >

The function BitmapToRegion() below, as its name implies, create a region from a bitmap. It scans the bitmap content (using a memory DC associated to a 32 bit DIB section), find continguous lines of non "transparent" pixels, add these lines to a RGNDATA structure, and finally, create the resulting region by calling ExtCreateRgn(). The function takes three arguments : the bitmap to scan, the base color which indicates the minimum value for the "transparents" pixels, and the tolerance color which, added to the base color, gives the maximum value for theses "transparents" pixels.

The sample Win32 project you can download shows how to use BitmapToRegion() to create a non-rectangular splash screen. The resulting region is equal to the "winnt256.bmp" bitmap, minus the black and "almost black" pixels. This region is then passed as an argument to the SetWindowRgn() function, with a handle to a window we created before. At last, we have only to paint de bitmap into the window to get the non-rectangular splash screen !

BitmapToRegion() is a pure Win32 function. It doesn't make any use of the MFC. Its code is standalone and only need to be compiled in a C++ file (a C compiler implies some minor modifications in the code). The function has been tested successfully on both Window 95, Windows 98 and Windows NT4. On the two formers systems, we only had have to modify the function slightly, because ExtCreateRgn() failed to create regions containing more than 4000 rectangles. The performances are quite good : on a PII-400 under NT4, the function spend only 70 ms to scan a 575x846x8 drawing (from the great artist Luis Royo) and create a corresponding region of more than 21000 rectangles ! / // BitmapToRegion : Create a region from the "non-transparent" pixels of a bitma // Author : Jean-Edouard Lachand-Robert (, June 1998 / // hBmp : Source bitma // 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 = 0, COLORREF cTolerance = 0x101010) { HRGN hRgn = NULL; ASSERT(hBmp); if (hBmp) { // Create a memory DC inside which we will scan the bitmap conten HDC hMemDC = CreateCompatibleDC(NULL); ASSERT(hMemDC); if (hMemDC) { // Get bitmap siz 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); ASSERT(hbm32); if (hbm32) { HBITMAP holdBmp = (HBITMAP)SelectObject(hMemDC, hbm32); // Create a DC just to copy the bitmap into the memory D HDC hDC = CreateCompatibleDC(hMemDC); ASSERT(hDC); if (hDC) { // Get how many bytes per row we have for the bitmap bits (rounded up to 32 bits BITMAP bm32; VERIFY(GetObject(hbm32, sizeof(bm32), &bm32)); while (bm32.bmWidthBytes % 4) bm32.bmWidthBytes++; // Copy the bitmap into the memory D HBITMAP holdBmp = (HBITMAP)SelectObject(hDC, hBmp); VERIFY(BitBlt(hMemDC, 0, 0, bm.bmWidth, bm.bmHeight, hDC, 0, 0, SRCCOPY)); // For better performances, we will use the ExtCreateRegion() function to create th // region. This function take a RGNDATA structure on entry. We will add rectangles b // 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" pixel 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 righ 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 regio if (pData->rdh.nCount >= maxRects) { GlobalUnlock(hData); maxRects += ALLOC_UNIT; VERIFY(hData = GlobalReAlloc(hData, sizeof(RGNDATAHEADER) + (sizeof(RECT) * maxRects), GMEM_MOVEABLE)); pData = (RGNDATA *)GlobalLock(hData); ASSERT(pData); } 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-> pData-> = 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 to // 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); ASSERT(h); 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 rectangle HRGN h = ExtCreateRegion(NULL, sizeof(RGNDATAHEADER) + (sizeof(RECT) * maxRects), pData); ASSERT(h); if (hRgn) { CombineRgn(hRgn, hRgn, h, RGN_OR); DeleteObject(h); } else hRgn = h; // Clean u SelectObject(hDC, holdBmp); DeleteDC(hDC); } DeleteObject(SelectObject(hMemDC, holdBmp)); } DeleteDC(hMemDC); } } return hRgn; }

Download source and demo project - 175KB


  • Some questions confused me.

    Posted by Rudy Xiong on 06/03/2015 05:09am

    1.while (bm32.bmWidthBytes % 4) bm32.bmWidthBytes++; Since you have set 'biBitCount' to be 32 when you create the 'BITMAPINFOHEADER', so 'bm32.bmWidthBytes' will always be exactly division by 4, right? 2.pData-rdh.rcBound? when I removed all code about this member, it seems nothing was affected. 3.The pData was not released, won't it cause memory leak?

  • You must have javascript enabled in order to post comments.

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • As all sorts of data becomes available for storage, analysis and retrieval - so called 'Big Data' - there are potentially huge benefits, but equally huge challenges...
  • The agile organization needs knowledge to act on, quickly and effectively. Though many organizations are clamouring for "Big Data", not nearly as many know what to do with it...
  • Cloud-based integration solutions can be confusing. Adding to the confusion are the multiple ways IT departments can deliver such integration...

Most Popular Programming Stories

More for Developers

RSS Feeds

Thanks for your registration, follow us on our social networks to keep up-to-date