• 网上找到的一个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 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;
                                        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);
                                        }
                                        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);
                        ASSERT(h);
                        if (hRgn)
                        {
                            CombineRgn(hRgn, hRgn, h, RGN_OR);
                            DeleteObject(h);
                        }
                        else
                            hRgn = h;
    
                        // Clean up,这儿需要再调用一下GlobalFree,否则有内存泄漏
                        SelectObject(hDC, holdBmp);
                        DeleteDC(hDC);
                    }
    
                    DeleteObject(SelectObject(hMemDC, holdBmp));
                }
    
                DeleteDC(hMemDC);
            }
        }
    
        return hRgn;
    }

    相应工程的示例代码: 

    bmp2rgnfix.zip

      

    另有Delphi版本的BitmapToRegion

    function BitmapToRegion(bmp: TBitmap; TransparentColor: TColor = clBlack;
      RedTol: Byte = 1; GreenTol: Byte = 1; BlueTol: Byte = 1): HRGN;
    const
      AllocUnit = 100;
    type
      PRectArray = ^TRectArray;
      TRectArray = array[0..(MaxInt div SizeOf(TRect)) - 1] of TRect;
    var
      pr: 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
    begin
      Result := 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
        begin
          dwSize := 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
        begin
          x := 0;
          while x < bmp.Width do
          begin
            x0 := x;
            while x < bmp.Width do
            begin
              b := @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) then
                Break; // 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
              begin
                Inc(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
              begin
                SetRect(pr[nCount], x0, y, x, y + 1);
                { adjust the bound rectangle of the region if we are "out-of-bounds" }
                if x0 < rcBound.Left then
                  rcBound.Left := x0;
                if y < rcBound.Top then
                  rcBound.Top := y;
                if x > rcBound.Right then
                  rcBound.Right := x;
                if y + 1 > rcBound.Bottom then
                  rcBound.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
            begin
              h := 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
        begin
          CombineRgn(Result, Result, h, RGN_OR);
          DeleteObject(h);
        end
        else
          Result := h;
      finally
        FreeMem(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}
    
    procedure TForm1.Button1Click(Sender: TObject);
    var
      ARgn: HRGN;
      ABitmap: TBitmap;
    begin
      ABitmap := TBitmap.Create;
      try
        ABitmap.LoadFromFile('C:MyImage.bmp');
        ARgn := BitmapToRegion(ABitmap, clFuchsia);
        SetWindowRgn(Form1.Handle, ARgn, True);
      finally
        ABitmap.Free;
      end;
    end;
    
    {This second example sets the region of a TPanel}
    
    procedure TForm1.Button1Click(Sender: TObject);
    var
      ARgn: HRGN;
      ABitmap: TBitmap;
    begin
      ABitmap := TBitmap.Create;
      try
        ABitmap.LoadFromFile('C:MyImage.bmp');
        ARgn := BitmapToRegion(ABitmap, clFuchsia);
        SetWindowRgn(Panel1.Handle, ARgn, True);
      finally
        ABitmap.Free;
      end;
    end;
  • 相关阅读:
    变量提升
    前端UI框架和JS类库
    ES6---Map数据结构
    ES6---Set数据结构
    Array.from//Array.of的用法
    闭包的理解和应用场景
    vue-router 的用法
    原型链和作用域链的理解
    WordPress更换了域名 主页、文章、图片路径错误 解决办法
    wordpress 安装新的主题后启动后报错
  • 原文地址:https://www.cnblogs.com/eaglexmw/p/15292616.html
Copyright © 2020-2023  润新知