网上找到的一个BitmapToRegion,根据Bitmap与透明色,生成一个区域HRGN
// //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;
}
相应工程的示例代码:
另有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;