Reputation: 889
I find it difficult to understand the excerpt below from MSDN site on GetDIBits()
function:
If lpvBits is NULL and the bit count member of BITMAPINFO is initialized to zero, GetDIBits fills in a BITMAPINFOHEADER structure or BITMAPCOREHEADER without the color table. This technique can be used to query bitmap attributes.
Question-1: What is meant by "the bit count member of BITMAPINFO"? Does it mean some_bmi.bmiHeader.biBitCount
?
Question-2: What is meant by "GetDIBits fills in a BITMAPINFOHEADER structure or BITMAPCOREHEADER without the color table"? What color table is there to fill in those structures? None of them seems to have a member related to the color table. Is this about the array some_bmi.bmiColors
?
Question-3: Is there a way to use GetDIBits()
to get the color table(i.e. the array mapping indexes to colors) for a bitmap?
EDIT:
From the comments so far, it looks like breaking the question down into smaller parts was not effective. I will try it another way.
This is what I understand from the part I quoted from MSDN at the beginning:
Assuming the function call is GetDIBits(hdc, hbmp, uStartScan, cScanLines, lpvBits, lpbi, uUsage)
; if lpvBits is NULL and lpvBits->bmiHeader.biBitCount
is initialized to zero, GetDIBits() fills in lpbi->bmiHeader
only and lpbi->bmiColors
is not modified.
Is this the correct way to understand it? And if so, is there a way to get GetDIBits() to fill in lpbi->bmiColors
, such as initializing lpvBits->bmiHeader.biBitCount
to bit-depth of the bitmap?
I tried testing Question-1's assumption as follows but GetDIBits() fails in that case:
void test(HWND hWnd) {
// get a memory DC
HDC hdc = GetDC(hWnd);
HDC hdcmem = CreateCompatibleDC(hdc); // has 1x1 mono bitmap selected
// into it initially by default
// select a 16x16 mono bmp into it
const int bmp_h = 16, bmp_w = 16;
const int bits_per_px = 1;
HBITMAP hbmp = CreateCompatibleBitmap(hdcmem, bmp_h, bmp_w); // 16x16 mono bitmap
HGDIOBJ hOldBmp = SelectObject(hdcmem, hbmp);
// initialize BITMAPINFO ptr
// (make sure to allocate a buffer large enough for 2 RGBQUADs
// in case color table is retured by GetDIBits() call)
const int bmi_buf_sz =
sizeof(BITMAPINFO) + sizeof(RGBQUAD) * (1 << bits_per_px); // 2 + 1(extra) RGBQUADs allocated for pbmi->bimColors
BYTE* p_bmi_buf = new BYTE[bmi_buf_sz];
BITMAPINFO* pbmi = reinterpret_cast<BITMAPINFO*>(p_bmi_buf);
ZeroMemory(pbmi, bmi_buf_sz);
// populate BITMAPINFO
pbmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
pbmi->bmiHeader.biBitCount = 1; // set to 1 just to see if GetDIBits()
// fills in pbmi->bmiColors too
// (when set to 0, only pbmi->bmiHeader is filled)
if(!GetDIBits(hdcmem, hbmp,
0, (UINT)bmp_h,
NULL, pbmi, DIB_PAL_COLORS)) {
MessageBox(hWnd, L"GetDIBits() failed!", NULL, MB_OK);
}
// clean-up
delete[] p_bmi_buf;
SelectObject(hdcmem, hOldBmp); // push hbmp out
DeleteObject(hbmp);
DeleteDC(hdcmem);
ReleaseDC(hWnd, hdc);
}
Upvotes: 1
Views: 2507
Reputation: 889
Although the accepted answer covers the details, this is more of a direct answer to the questions in the OP.
Assuming the function call is GetDIBits(hdc, hbmp, uStartScan, cScanLines, lpvBits, lpbi, uUsage)
;
Question-1:
That is correct. "The bit count member of BITMAPINFO" refers to lpbi->bmiHeader.biBitCount
.
Question-2:
When GetDIBits()
is called to get DIB bits(i.e. with a non-null lpvBits
and an appropriately initialized lpbi->bmiHeader
), lpbi->bmiColors
also gets filled by the function with the color table(if bit depth is less then 24 bpp).
Unfortunately, this is not clear in the documentation of the function. With that in mind, what the quoted part means is that, when lpvBits
is NULL and lpbi->bmiHeader.biBitCount
is zero, the function fills lpbi->bmiHeader
only, and does not modify lpbi->bimColor
(as opposed to when caling the function to get DIB bits).
Question-3:
You can get the function to return color table(for bitmaps with 8-bbp or less) in lpbi->bmiColors
by calling it with a non-null lpvBits
and an appropriately initialized lpbi->bmiHeader
. IOW, when you call the function to get DIB bits as usual, it fills lpbi->bmiColors
as well.
Question in EDIT section:
If lpvBits is NULL and
lpvBits->bmiHeader.biBitCount
is initialized to zero, GetDIBits() fills inlpbi->bmiHeader
only andlpbi->bmiColors
is not modified.Is this the correct way to understand it?
Yes, that is correct.
And if so, is there a way to get GetDIBits() to fill in
lpbi->bmiColors
, such as initializinglpvBits->bmiHeader.biBitCount
to bit-depth of the bitmap?
Yes, there is a way to get the function to return the color table, but as explained in answer to Q2, initializing lpvBits->bmiHeader.biBitCount
to bit-depth of the bitmap alone is not enough. All members of lpvBits->bmiHeader
must be appropriately initialized and lpvBits
must be non-null. This is basically the same as calling the function to get the DIB bits.
Upvotes: 1
Reputation: 31669
The easiest way to get the color table is with GetDibColorTable
:
HDC memdc = CreateCompatibleDC(NULL);
HBITMAP oldbmp = (HBITMAP)SelectObject(memdc, hbitmap);
int ncolors = 1 << bm.bmBitsPixel;
std::vector<RGBQUAD> rgb(ncolors);
if(ncolors == GetDIBColorTable(memdc, 0, ncolors, &rgb[0]))
{
//success!
}
SelectObject(memdc, oldbmp);
DeleteDC(memdc);
Back to your question: GetDIBits
expects pbmi
to contain all zeros (except for bmiHeader.biSize
member). So pbmi->bmiHeader.biBitCount
should be zero.
//pbmi->bmiHeader.biBitCount = 1; <<= comment out this line
This should work; however, as it is stated in documentation, this will only fill the info header, not the color table. To get the color table you have to make another call to GetDIBits
with enough allocation for the dib bits.
Moreover DIB_PAL_COLORS
will return palette index array (I am not sure what you can do with that). You may want to use DIB_RGB_COLORS
flag which will return actual colors when a color table is present.
Try this with a bitmap loaded from file:
HBITMAP hbitmap = (HBITMAP)LoadImage(0, L"8bit.bmp",
IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION | LR_LOADFROMFILE);
BITMAP bm;
GetObject(hbitmap, sizeof(bm), &bm);
//don't continue for hi color bitmaps
if(bm.bmBitsPixel > 8) return;
int ncolors = 1 << bm.bmBitsPixel;
HDC memdc = CreateCompatibleDC(NULL);
int bmpinfo_size = sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * ncolors;
std::vector<BYTE> buf(bmpinfo_size);
BITMAPINFO* bmpinfo = (BITMAPINFO*)buf.data();
bmpinfo->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
if(!GetDIBits(memdc, hbitmap, 0, bm.bmHeight, NULL, bmpinfo, DIB_RGB_COLORS))
{
DWORD err = GetLastError();
//...
}
int dibsize = ((bm.bmWidth * bm.bmBitsPixel + 31) / 32) * 4 * bm.bmHeight;
std::vector<BYTE> dib(dibsize);
if(!GetDIBits(memdc, hbitmap, 0, (UINT)bm.bmHeight, &dib[0], bmpinfo, DIB_RGB_COLORS))
{
DWORD err = GetLastError();
//...
}
Now bmpinfo->bmiColors
should contain the same values as rgb
array shown earlier.
BITMAPINFO
and BITMAPINFOHEADER
:
The above structures are declared as follows:
typedef struct tagBITMAPINFO {
BITMAPINFOHEADER bmiHeader;
RGBQUAD bmiColors[1];
} BITMAPINFO, FAR *LPBITMAPINFO, *PBITMAPINFO;
typedef struct tagBITMAPINFOHEADER{
DWORD biSize;
LONG biWidth;
LONG biHeight;
WORD biPlanes;
WORD biBitCount;
DWORD biCompression;
DWORD biSizeImage;
LONG biXPelsPerMeter;
LONG biYPelsPerMeter;
DWORD biClrUsed;
DWORD biClrImportant;
} BITMAPINFOHEADER, FAR *LPBITMAPINFOHEADER, *PBITMAPINFOHEADER;
So BITMAPINFO
does not have biBitCount
member. But it does have bmiHeader.biBitCount
member.
When you declare a BITMAPINFOHEADER
variable, you have to set biSize
member (that's Windows' idea of version control). When you declare a BITMAPINFO
variable, you have to make sure it's BITMAPINFOHEADER
is taken care of.
Note that most of the time you don't have to worry about palette. For example LoadImage
will return a compatible bitmap (if you don't specify LR_CREATEDIBSECTION
) and you can use that right away.
Upvotes: 1