Dartz
Dartz

Reputation: 11

Read pixels (raw bytes) from an hbitmap without copying?

I have an HBITMAP handle to a device-dependent bitmap, resulting from this code:

// copy screen to bitmap
HDC     hScreen = GetDC(NULL);//don't use hwnd, it doesn't work for non native windows
HDC     hDC = CreateCompatibleDC(hScreen);
HBITMAP hBitmap = CreateCompatibleBitmap(hScreen, w, h);
HGDIOBJ old_obj = SelectObject(hDC, hBitmap);
BOOL    bRet = BitBlt(hDC, 0, 0, w, h, hScreen, x1, y1, SRCCOPY);

I want to access (read-only) the bits of the bitmap, read some values, do some calculations, then discard it.

According to this answer, I shouldn't use GetDIBits() or GetBitmapBits() since they copy the data, but instead I should use:

BITMAP bitmap;
GetObject(hBitmap, sizeof(bitmap), (LPVOID)&bitmap);

But according to GetObject's documentation:

If hgdiobj is a handle to a bitmap created by calling CreateDIBSection, and the specified buffer is large enough, the GetObject function returns a DIBSECTION structure. In addition, the bmBits member of the BITMAP structure contained within the DIBSECTION will contain a pointer to the bitmap's bit values.

If hgdiobj is a handle to a bitmap created by any other means, GetObject returns only the width, height, and color format information of the bitmap. You can obtain the bitmap's bit values by calling the GetDIBits or GetBitmapBits function.

Which is pointed to in a comment on the above answer:

According to this https://msdn.microsoft.com/en-us/library/dd144904(v=vs.85).aspx if bitmap is not created by CreateDIBSection bitmap.bmBit pointer will be null.

So, is there anyway to access the bits of the bitmap (RGB values), without copying them?

If it helps, the device on which the bitmap is dependent is always a screen, so I guess it's always a 24bit or 32bit bitmap.

In this article, regarding how to save an HBITMAP to a file, there's this section:

// Allocate memory for the BITMAPINFO structure. (This structure  
// contains a BITMAPINFOHEADER structure and an array of RGBQUAD  
// data structures.)  

if (cClrBits < 24) pbmi = (PBITMAPINFO)LocalAlloc(LPTR, sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * (1 << cClrBits));

// There is no RGBQUAD array for these formats: 24-bit-per-pixel or 32-bit-per-pixel 

else pbmi = (PBITMAPINFO)LocalAlloc(LPTR, sizeof(BITMAPINFOHEADER));

So, I don't really understand what's happening here, and which is right?

Upvotes: 0

Views: 371

Answers (1)

Martin Rosenau
Martin Rosenau

Reputation: 18523

I shouldn't use GetDIBits() ... since they copy the data ...
...
If it helps, the device on which the bitmap is dependent is always a screen, so I guess it's always a 24bit or 32bit bitmap.

Depending on the exact hardware that you use, pixels might be stored differently (maybe Rr Gg Bb on one computer and Bb Gg Rr 00 on another one and RG Br gb on a third).

To ensure that you know the data format, you should use GetDIBits() because that function does not only copy the data but it also converts the data to a "known" format which does not depend on the hardware.

would using GetPixel() be more efficient than copying it?

If you only want to inspect a few pixels (e.g. you want to know the color of 10 of the pixels), it should be more efficient.

If you are interested in all 20000 pixels, GetDIBits() should be more efficient.

pbmi = (PBITMAPINFO)LocalAlloc ...
So, I don't really understand what's happening here ...

GetDIBits() supports various bitmap formats, for example:

  • "24-bit": This means 3 bytes per pixel (R, G, B)
  • "256 color": One byte per pixel and a color map that contains the R, G, B definitions of each of the 256 colors

In the case of "24-bit", the BITMAPINFO structure consists only of a BITMAPINFOHEADER structure. I myself often write the code like this in this case:

BITMAPINFOHEADER hdr;
...
GetDIBits(..., (BITMAPINFO *)&hdr, ...);

In the case of "256-color", the BITMAPINFO structure consists of a BITMAPINFOHEADER structure followed by 256 "R,G,B" tuples (the color map) - so the structure needs more memory compared to the "24-bit" case.

Please note that the bottom line of an image is stored first and there are padding bytes at the end of each line if the length of the line is not a multiple of 4 bytes.

Upvotes: 0

Related Questions