//
//BitmapToRegion : Create a region from the "non-transparent" pixels of a bitmap//
//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 = 0, COLORREF cTolerance = 0x101010)
{
HRGN hRgn
=NULL;

ASSERT(hBmp);
if(hBmp)
{
//Create a memory DC inside which we will scan the bitmap content HDC hMemDC =CreateCompatibleDC(NULL);
ASSERT(hMemDC);
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);
ASSERT(hbm32);
if(hbm32)
{
HBITMAP holdBmp
=(HBITMAP)SelectObject(hMemDC, hbm32);//Create a DC just to copy the bitmap into the memory DC 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 DC 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 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 100DWORD 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;
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->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);
ASSERT(h);
if(hRgn)
{
CombineRgn(hRgn, hRgn, h, RGN_OR);
DeleteObject(h);
}
elsehRgn=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);
ASSERT(h);
if(hRgn)
{
CombineRgn(hRgn, hRgn, h, RGN_OR);
DeleteObject(h);
}
elsehRgn=h;//Clean up,这儿需要再调用一下GlobalFree,否则有内存泄漏 SelectObject(hDC, holdBmp);
DeleteDC(hDC);
}

DeleteObject(SelectObject(hMemDC, holdBmp));
}

DeleteDC(hMemDC);
}
}
returnhRgn;
}

相应工程的示例代码: 

bmp2rgnfix.zip

  

另有Delphi版本的BitmapToRegion

function BitmapToRegion(bmp: TBitmap; TransparentColor: TColor =clBlack;
RedTol: Byte
= 1; GreenTol: Byte = 1; BlueTol: Byte = 1): HRGN;constAllocUnit= 100;typePRectArray=^TRectArray;
TRectArray
= array[0..(MaxInt div SizeOf(TRect)) - 1] ofTRect;varpr: PRectArray;//used to access the rects array of RgnData by index h: HRGN; //Handles to regions RgnData: PRgnData; //Pointer to structure RGNDATA used to create regions lr, lg, lb, hr, hg, hb: Byte; //values for lowest and hightest trans. colors x, y, x0: Integer; //coordinates of current rect of visible pixels b: PByteArray; //used to easy the task of testing the byte pixels (R,G,B) ScanLinePtr: Pointer; //Pointer to current ScanLine being scanned ScanLineInc: Integer; //Offset to next bitmap scanline (can be negative) maxRects: Cardinal; //Number of rects to realloc memory by chunks of AllocUnit beginResult := 0;{Keep on hand lowest and highest values for the "transparent" pixels}lr :=GetRValue(TransparentColor);
lg :
=GetGValue(TransparentColor);
lb :
=GetBValue(TransparentColor);
hr :
= Min($FF, lr +RedTol);
hg :
= Min($FF, lg +GreenTol);
hb :
= Min($FF, lb +BlueTol);{ensures that the pixel format is 32-bits per pixel}bmp.PixelFormat :=pf32bit;{alloc initial region data}maxRects :=AllocUnit;
GetMem(RgnData, SizeOf(RGNDATAHEADER)
+ (SizeOf(TRect) *maxRects));try with RgnData^.rdh do begindwSize :=SizeOf(RGNDATAHEADER);
iType :
=RDH_RECTANGLES;
nCount :
= 0;
nRgnSize :
= 0;
SetRect(rcBound, MAXLONG, MAXLONG,
0, 0);end;{scan each bitmap row - the orientation doesn't matter (Bottom-up or not)}ScanLinePtr := bmp.ScanLine[0];
ScanLineInc :
= Integer(bmp.ScanLine[1]) -Integer(ScanLinePtr);for y := 0 to bmp.Height - 1 do beginx := 0;while x < bmp.Width do beginx0 :=x;while x < bmp.Width do beginb := @PByteArray(ScanLinePtr)[x *SizeOf(TRGBQuad)];//BGR-RGB: Windows 32bpp BMPs are made of BGRa quads (not RGBa) if (b[2] >= lr) and (b[2] <= hr) and(b[1] >= lg) and (b[1] <= hg) and(b[0] >= lb) and (b[0] <= hb) thenBreak;//pixel is transparent Inc(x);end;{test to see if we have a non-transparent area in the image} if x > x0 then begin {increase RgnData by AllocUnit rects if we exceeds maxRects} if RgnData^.rdh.nCount >= maxRects then beginInc(maxRects, AllocUnit);
ReallocMem(RgnData, SizeOf(RGNDATAHEADER)
+ (SizeOf(TRect) *MaxRects));end;{Add the rect (x0, y)-(x, y+1) as a new visible area in the region}pr := @RgnData^.Buffer; //Buffer is an array of rects with RgnData^.rdh do beginSetRect(pr[nCount], x0, y, x, y+ 1);{adjust the bound rectangle of the region if we are "out-of-bounds"} if x0 < rcBound.Left thenrcBound.Left :=x0;if y < rcBound.Top thenrcBound.Top :=y;if x > rcBound.Right thenrcBound.Right :=x;if y + 1 > rcBound.Bottom thenrcBound.Bottom := y + 1;
Inc(nCount);
end;end; //if x > x0 {Need to create the region by muliple calls to ExtCreateRegion, 'cause} {it will fail on Windows 98 if the number of rectangles is too large} if RgnData^.rdh.nCount = 2000 then beginh := ExtCreateRegion(nil, SizeOf(RGNDATAHEADER) + (SizeOf(TRect) *maxRects), RgnData^);if Result > 0 then begin //Expand the current region CombineRgn(Result, Result, h, RGN_OR);
DeleteObject(h);
end else //First region, assign it to Result Result :=h;
RgnData^.rdh.nCount :
= 0;
SetRect(RgnData^.rdh.rcBound, MAXLONG, MAXLONG,
0, 0);end;
Inc(x);
end; //scan every sample byte of the image Inc(Integer(ScanLinePtr), ScanLineInc);end;{need to call ExCreateRegion one more time because we could have left} {a RgnData with less than 2000 rects, so it wasn't yet created/combined}h := ExtCreateRegion(nil, SizeOf(RGNDATAHEADER) + (SizeOf(TRect) *MaxRects),
RgnData^);
if Result > 0 then beginCombineRgn(Result, Result, h, RGN_OR);
DeleteObject(h);
end elseResult :=h;finallyFreeMem(RgnData, SizeOf(RGNDATAHEADER)+ (SizeOf(TRect) *MaxRects));end;end;

I
've supplied a couple of simple examples of using this function for beginners: {This first example sets the region of a TForm} procedureTForm1.Button1Click(Sender: TObject);varARgn: HRGN;
ABitmap: TBitmap;
beginABitmap := TBitmap.Create;tryABitmap.LoadFromFile('C:\MyImage.bmp');
ARgn :
=BitmapToRegion(ABitmap, clFuchsia);
SetWindowRgn(Form1.Handle, ARgn, True);
finallyABitmap.Free;end;end;{This second example sets the region of a TPanel} procedureTForm1.Button1Click(Sender: TObject);varARgn: HRGN;
ABitmap: TBitmap;
beginABitmap := TBitmap.Create;tryABitmap.LoadFromFile('C:\MyImage.bmp');
ARgn :
=BitmapToRegion(ABitmap, clFuchsia);
SetWindowRgn(Panel1.Handle, ARgn, True);
finallyABitmap.Free;end;end;

 

标签: none

添加新评论