Home > Net >  How to convert HBITMAP to image.Image
How to convert HBITMAP to image.Image

Time:12-05

I want to retrieve an image from a specific application and convert it to image.Image for later use.

What I have now is a HBITMAP from windows API call. After trying a lots, I can't manage to convert the created HBITMAP to an image.Image (or at least an []byte).

rc := w32.GetClientRect(hwnd)
if rc != nil {
    // create
    HDCScreen := w32.GetWindowDC(hwnd)
    hdc := w32.CreateCompatibleDC(HDCScreen)
    hbmp := w32.CreateCompatibleBitmap(HDCScreen, int(rc.Right)-int(rc.Left), int(rc.Bottom)-int(rc.Top))
    w32.SelectObject(hdc, w32.HGDIOBJ(hbmp))

    // Print to memory hdc
    w32.PrintWindow(hwnd, hdc, 0x00000002)

    // ------------------------------------------------
    var bmpInfo *w32.BITMAPINFO = &w32.BITMAPINFO{}
    bmpInfo.BmiHeader.BiSize = uint32(unsafe.Sizeof(bmpInfo.BmiHeader))

    firstDIBits := w32.GetDIBits(HDCScreen, hbmp, 0, 0, nil, bmpInfo, w32.DIB_RGB_COLORS)
    fmt.Printf("firstDIBits: %v\n", firstDIBits)

    var lpPixels *[]byte
    bmpInfo.BmiHeader.BiBitCount = 32
    bmpInfo.BmiHeader.BiCompression = w32.BI_RGB
    bmpInfo.BmiHeader.BiHeight = int32(math.Abs(float64(bmpInfo.BmiHeader.BiHeight)))
    bmpInfo.BmiHeader.BiCompression = w32.BI_RGB

    secondDIBits := w32.GetDIBits(hdc, hbmp, 0, uint(bmpInfo.BmiHeader.BiHeight), unsafe.Pointer(lpPixels), bmpInfo, w32.DIB_RGB_COLORS)
    fmt.Printf("secondDIBits: %v\n", secondDIBits)
    fmt.Printf("lpPixels: %v\n", lpPixels)

    // ------------------------------------------------

    // copy to clipBoard
    w32.OpenClipboard(0)
    w32.EmptyClipboard()
    w32.SetClipboardData(w32.CF_BITMAP, w32.HANDLE(hbmp))
    w32.CloseClipboard()

    // release
    w32.DeleteDC(hdc)
    w32.DeleteObject(w32.HGDIOBJ(hbmp))
    w32.ReleaseDC(0, HDCScreen)
}

Both of GetDIBits() call return 1 but lpPixels is always nil.

CodePudding user response:

The Bitmap class inherits from the Image class.In my opinion, you couldn't need the conversion.

According to the Doc:GetDIBits function

[out] lpvBits

A pointer to a buffer to receive the bitmap data. If this parameter is NULL, the function passes the dimensions and format of the bitmap to the BITMAPINFO structure pointed to by the lpbi parameter.

I don't know much about Go language, but the lpvBits parameter of GetDIBits seem incorrect. I suggest you could refer to the threads:

Using GetDIBits to load a bitmap

How should I use the GetDIBits function

CodePudding user response:

Finally made it. Posting the code in case it can help someone else. :)

rect := image.Rect(0, 0, 1920, 1080)
img := image.NewRGBA(rect)
rc := w32.GetClientRect(hwnd)
    if rc != nil {
        width := int(rc.Right) - int(rc.Left)
        height := int(rc.Bottom) - int(rc.Top)
        hwndwin := win.HWND(hwnd)
        hdc := win.GetDC(hwndwin)
        if hdc == 0 {
            panic("GetDC failed")
        }
        defer win.ReleaseDC(hwndwin, hdc)

        memory_device := win.CreateCompatibleDC(hdc)
        if memory_device == 0 {
            panic("CreateCompatibleDC failed")
        }
        defer win.DeleteDC(memory_device)

        bitmap := win.CreateCompatibleBitmap(hdc, int32(width), int32(height))
        if bitmap == 0 {
            panic("CreateCompatibleBitmap failed")
        }
        defer win.DeleteObject(win.HGDIOBJ(bitmap))

        var header win.BITMAPINFOHEADER
        header.BiSize = uint32(unsafe.Sizeof(header))
        header.BiPlanes = 1
        header.BiBitCount = 32
        header.BiWidth = int32(width)
        header.BiHeight = int32(-height)
        header.BiCompression = win.BI_RGB
        header.BiSizeImage = 0

        // GetDIBits balks at using Go memory on some systems. The MSDN example uses GlobalAlloc
        // https://docs.microsoft.com/en-gb/windows/desktop/gdi/capturing-an-image
        bitmapDataSize := uintptr(((int64(width)*int64(header.BiBitCount)   31) / 32) * 4 * int64(height))
        hmem := win.GlobalAlloc(win.GMEM_MOVEABLE, bitmapDataSize)
        defer win.GlobalFree(hmem)
        memptr := win.GlobalLock(hmem)
        defer win.GlobalUnlock(hmem)

        old := win.SelectObject(memory_device, win.HGDIOBJ(bitmap))
        if old == 0 {
            panic("SelectObject failed")
        }
        defer win.SelectObject(memory_device, old)

        if !win.BitBlt(memory_device, 0, 0, int32(width), int32(height), hdc, int32(0), int32(0), win.SRCCOPY) {
            panic("BitBlt failed")
        }

        if win.GetDIBits(hdc, bitmap, 0, uint32(height), (*uint8)(memptr), (*win.BITMAPINFO)(unsafe.Pointer(&header)), win.DIB_RGB_COLORS) == 0 {
            panic("GetDIBits failed")
        }

        i := 0
        src := uintptr(memptr)
        for y := 0; y < height; y   {
            for x := 0; x < width; x   {
                v0 := *(*uint8)(unsafe.Pointer(src))
                v1 := *(*uint8)(unsafe.Pointer(src   1))
                v2 := *(*uint8)(unsafe.Pointer(src   2))

                // BGRA => RGBA, and set A to 255
                img.Pix[i], img.Pix[i 1], img.Pix[i 2], img.Pix[i 3] = v2, v1, v0, 255

                i  = 4
                src  = 4
            }
        }
  • Related