From the documentation on CreateDIBSection
I observe that CreateDIBSection
takes a pointer to a BITMAPINFO
as the second parameter.
However, I came across various places indicating that it might be allowed to pass pointers to other structures (in particular BITMAPV5HEADER
), including
I have a feeling that this makes perfect sense (BITMAPV5HEADER
can be seen as an "extended version" of BITMAPINFO
wrt struct layout), but I could not find a single piece of official documentation on this topic.
Can anybody confirm that passing a BITMAPV5HEADER*
instead of a BITMAPINFO
is actually valid and possibly offer some documentation?
The short answer has to be no. BITMAPV5HEADER*
is not a substitute for BITMAPINFO*
and may not be passed whenever BITMAPINFO*
is expected (simply because BITMAPINFO
contains palette colors after the header, and BITMAPV5HEADER
is just a header).
It is certainly fine to pass BITMAPV5HEADER*
instead of BITMAPINFO*
provided that the BITMAPV5HEADER
is a part of BITMAPINFO
and has required kind of palette color data after it. That is documented, although kind of indirectly, through use instructions and common sense:
BITMAPV5HEADER
is documented to be an "extended version of th BITMAPINFOHEADER
structure", so that part is clear.
BITMAPINFO
is documented to combine a header and color data. The header is included by value as opposed to by pointer, so at this point it's clear the header may not just grow in size as it pleases, otherwise it would be impossible to access BITMAPINFO.bmiColors
and the whole idea of having an extended version of the header would be pointless.
And that problem is then resolved in another place in the documentation ("Remarks" section):
An application should use the information stored in the
biSize
member to locate the color table in aBITMAPINFO
structure, as follows:pColor = ((LPSTR)pBitmapInfo + (WORD)(pBitmapInfo->bmiHeader.biSize));
Although I believe this part was not confusing for you to begin with.
Now for the long answer.
There appear to be two cases when BITMAPV5HEADER*
can be passed for BITMAPINFO*
. Neither of the two is documented in a concrete way, and if for the first one we can apply same common sense we applied above, the second one appears to be a bug in the documentation:
BITMAPINFO.bmiColors
is documented to be NULL
. You can find the complete list of such cases in the documentation for BITMAPINFOHEADER
.When the header is BITMAPV4HEADER
or BITMAPV5HEADER
, the bitmap has 16 or 32 bit colors, and compression is set to BI_BITFIELDS
. In that case the color masks that are documented to follow the header are instead taken from the respective dedicated fields of the headers, and the three DWORD
s that follow the header are ignored.
This is easy to prove by slightly modifying the original code:
typedef struct tagV5BMPINFO {
BITMAPV5HEADER bmiHeader;
DWORD bmiColors[3];
} V5BMPINFO;
int _tmain(int argc, _TCHAR* argv[])
{
V5BMPINFO bmpinfo = { 0 };
BITMAPV5HEADER bmpheader = { 0 };
bmpheader.bV5Size = sizeof(BITMAPV5HEADER);
bmpheader.bV5Width = width;
bmpheader.bV5Height = height;
bmpheader.bV5Planes = 1;
bmpheader.bV5BitCount = 32;
bmpheader.bV5Compression = BI_BITFIELDS;
bmpheader.bV5SizeImage = 400*200*4;
bmpheader.bV5RedMask = 0x00FF0000;
bmpheader.bV5GreenMask = 0x0000FF00;
bmpheader.bV5BlueMask = 0x000000FF;
bmpheader.bV5AlphaMask = 0xFF000000;
bmpheader.bV5CSType = 0x57696e20; // LCS_WINDOWS_COLOR_SPACE
bmpheader.bV5Intent = LCS_GM_BUSINESS;
bmpinfo.bmiHeader = bmpheader;
// Put them in reverse order here compared to the above
bmpinfo.bmiColors[0] = 0x000000FF;
bmpinfo.bmiColors[1] = 0x0000FF00;
bmpinfo.bmiColors[2] = 0x00FF0000;
void* converted = NULL;
HDC screen = GetDC(NULL);
HBITMAP result = CreateDIBSection(screen, reinterpret_cast<BITMAPINFO*>(&bmpinfo), DIB_RGB_COLORS, &converted, NULL, 0);
ReleaseDC(NULL, screen);
DIBSECTION actual_data;
GetObject(result, sizeof(actual_data), &actual_data);
std::cout << std::hex;
std::cout << actual_data.dsBitfields[0] << std::endl;
std::cout << actual_data.dsBitfields[1] << std::endl;
std::cout << actual_data.dsBitfields[2] << std::endl;
std::cout << std::dec;
DeleteObject(result);
return 0;
}
Result:
ff0000 ff00 ff
It would appear the documentation was absentmindedly copied between BITMAPINFOHEADER
, BITMAPV4HEADER
and BITMAPV5HEADER
when it should have been amended for the last two. The closest explanation I was able to find is Bitmap Header Types that at least recognizes the existence of the dedicated mask fields, but still implies the values must be provided both in these fields and after the header in bmiColors
:
The red, green, and blue bitfield masks for
BI_BITFIELD
bitmaps immediately follow theBITMAPINFOHEADER
,BITMAPV4HEADER
, andBITMAPV5HEADER
structures. TheBITMAPV4HEADER
andBITMAPV5HEADER
structures contain additional members for red, green, and blue masks as follows.
(emphasis mine).
We can only conclude from evidence that it is not true.
It is less of documentation than I hoped.