MS docs CreateDIBSection shennigans ? (2bpp bitmap)

7 views
Skip to first unread message

R.Wieser

unread,
Feb 3, 2021, 2:28:01 AMFeb 3
to
I've been trying to use CreateDIBSection to create a 2bpp bitmap, which
fails (4bpp works). While looking thru the docs.microsoft.com webpage
regarding the function in search of /why/ it fails I stumbled over the
following :

https://docs.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-createdibsection
- - - - - - - - - - - - - - - - - - - -
If the function fails, the return value is NULL, and *ppvBits is NULL.

This function can return the following value.
Return code Description
ERROR_INVALID_PARAMETER One or more of the input parameters is invalid.
- - - - - - - - - - - - - - - - - - - -

And yes, that is as close together as I've posted it.

So, an error exits is indicated by a NULL *AND* a (possible) 0x57 value ?
That doesn't make any sense ...

And no, CreateDIBSection doesn't set the 'GetLastError' value (just tested
it). :-)

So, two questions:

1) How can I check /what/ went wrong ?

2) Does CreateDIBSection accept a 2 bits per color (CGA) setting ?

Regards,
Rudy Wieser


JJ

unread,
Feb 4, 2021, 8:08:01 AMFeb 4
to
On Wed, 3 Feb 2021 08:27:46 +0100, R.Wieser wrote:
> I've been trying to use CreateDIBSection to create a 2bpp bitmap, which
> fails (4bpp works).
>
> 2) Does CreateDIBSection accept a 2 bits per color (CGA) setting ?

BMP format can store 2bpp, but it doesn't officially support 2bpp. So,
almost all softwares will not be able to view the image. With a handcrafted
2bpp BMP, out of (Windows) Paint, Irfanview, FastStone Image Viewer, XnView,
GIMP, Corel PaintShop Pro; and MSIE, Firefox, Chrome; only XnView and Chrome
are able to view it.

> - - - - - - - - - - - - - - - - - - - -
> If the function fails, the return value is NULL, and *ppvBits is NULL.
>
> This function can return the following value.
> Return code Description
> ERROR_INVALID_PARAMETER One or more of the input parameters is invalid.
> - - - - - - - - - - - - - - - - - - - -
>
> And yes, that is as close together as I've posted it.
>
> So, an error exits is indicated by a NULL *AND* a (possible) 0x57 value ?
> That doesn't make any sense ...
>
> And no, CreateDIBSection doesn't set the 'GetLastError' value (just tested
> it). :-)
>
> So, two questions:
>
> 1) How can I check /what/ went wrong ?

I think you misunderstood the documentation. It specifically states:

[quote]
If the function succeeds, the return value is a handle to the newly created
DIB, and *ppvBits points to the bitmap bit values.
[/quote]

So, if the return value is NULL *OR* *ppvBits is NULL, it means that the
function fails. Remember that a NULL handle is basically an invalid handle
value. So, "a handle" generally means a valid handle, unless stated
otherwise. Especially considering that a NULL Bitmap handle isn't associated
with any data.

As for GetLastError, there is indeed a bug where the last error code is not
updated when the function fails. At least when the given biBitCount value is
unsupported. But if CreateDIBSection succeeds, the last error is not
updated.

FYI, in Borland's VCL framework library, it simply use the return value as
the main indicator of success. GetLastError is only used when the return
value is NULL. *ppvBits is not checked and is only used when the function
returns non NULL.

R.Wieser

unread,
Feb 4, 2021, 9:23:26 AMFeb 4
to
JJ,

> BMP format can store 2bpp,

indeed.

> but it doesn't officially support 2bpp.

Who's "it" ? the BMP guys or just MS ? :-)

> I think you misunderstood the documentation.

I did ? I don't think so ...

> It specifically states:
>
> [quote]
> If the function succeeds, the return value is a handle to the newly
> created DIB, and *ppvBits points to the bitmap bit values.
> [/quote]

That part I understood.

The problem is it *also* states :

[quote=me]
This function can return the following value.
Return code Description
ERROR_INVALID_PARAMETER One or more of the input parameters is invalid.
[/quote]

Who/what is returning that "invalid parameter" result ?

> As for GetLastError, there is indeed a bug where the last error code is
> not updated when the function fails. At least when the given biBitCount
> value is unsupported.

Ah. So it boils down to that the documentation forgot to mention that
GetLastError is involved.

I assumed as much, but as it didn't return an error code when it, as far as
I could tell, should have (due to that bug you mentioned) I began to doubt
my assumption. Hence my question.

By the way: I found GdiSetLastError, but no complementing getter ... It
also doesn't help that a google shows that MS doesn't know anything about
that function (anymore).

Regards,
Rudy Wieser


JJ

unread,
Feb 5, 2021, 3:34:21 AMFeb 5
to
On Thu, 4 Feb 2021 15:23:06 +0100, R.Wieser wrote:
>
> Who's "it" ? the BMP guys or just MS ? :-)

Uh, those BMP guys *is* MS, because MS is the one who created BMP.

> Who/what is returning that "invalid parameter" result ?

GetlastError, of course. HBITMAP's integer value shouldn't be treated as an
error code. But due to the bug, CreateDIBSection may forgot to call
SetLastError.

> Ah. So it boils down to that the documentation forgot to mention that
> GetLastError is involved.

I knew it... Turns out that we're both reading different versions of the
documentation. In the Windows 7 SDK (SDK v6.1), the whole "Return Values"
section is this.

[quote]
If the function succeeds, the return value is a handle to the newly created
DIB, and *ppvBits points to the bitmap bit values.

If the function fails, the return value is NULL, and *ppvBits is NULL.

Windows NT/2000/XP: To get extended error information, call GetLastError.
This can be the following value.

Value Meaning
ERROR_INVALID_PARAMETER One or more input parameters is invalid.
[/quote]

SDK for Windows 8+ no longer have any offline documentation. That's when MS
migrated to "community" based documentation - which is less detailed.

Even the SDK documentations for Win95 & NT4.0 no longer describe any
resource data format. Only Win3.x SDK does, albeit briefly.

> By the way: I found GdiSetLastError, but no complementing getter ... It
> also doesn't help that a google shows that MS doesn't know anything about
> that function (anymore).

I've debugged CreateDIBSection. GdiSetLastError does the same thing as
SetLastError, which is to set the last error field in the process' TEB
structure.

CreateDIBSection calls GdiSetLastError with ERROR_INVALID_PARAMETER and
nothing else, but only if BITMAPINFOHEADER.biCompression is JPEG or PNG. Any
other unacceptable BITMAPINFOHEADER fields won't cause it call
GdiSetLastError at all. Instead, it simply returns NULL.

And FYI, it only supports 1, 4, 8, 16, 24, and 32 bits; as expected. No
support for 15 bits either, which Photoshop and/or PaintShop Pro (can't
remember exactly) can actually create and use.

R.Wieser

unread,
Feb 5, 2021, 5:02:06 AMFeb 5
to
JJ,

>> Who's "it" ? the BMP guys or just MS ? :-)
>
> Uh, those BMP guys *is* MS, because MS is the one who created BMP.

I should have been more specific : is "it" the BMP guys who wrote the specs
and just "allowed" the 2bpp mode to exist, but did not officially include it
in their definition, or is "it" MS who, even though supporting 2bpp is as
complicated as 4bpp, have decided not to implement - and thus support - it
for some reason.

"Somehow" I think its the second ...

>> Ah. So it boils down to that the documentation forgot to mention that
>> GetLastError is involved.
>
> I knew it... Turns out that we're both reading different versions of the
> documentation.

Thats why I included the link to the page I found. :-)

> Windows NT/2000/XP: To get extended error information, call GetLastError.
> This can be the following value.

I cannot help but wonder : What would you need to do on Vista, 7, 8 and 10 ?
And why didn't they mention it ? <whistle>

> I've debugged CreateDIBSection. GdiSetLastError does the same thing
> as SetLastError, which is to set the last error field in the process' TEB
> structure.

I was a bit too busy with other stuff to disassemble GDI32 and look at what
GdiSetLastError actually does, but I imagined it could just have been a
"hardlink" to Kernel32's SetLastError.

> And FYI, it only supports 1, 4, 8, 16, 24, and 32 bits; as expected.

Not supporting 2bpp is unexpected to me. For the rest ? Yup, either
values that cleanly divide 8 bits (no remainder) or multiples of it.

I could have imagined that 8bpp would /also/ support an 2, 3, 2 color usage
(similar to 16bpp, just smaller).

> No support for 15 bits either,

Actually ... Although the 16bpp mode uses 16 bits to /store/ the pixel, by
default CreateDIBSection uses just 5 bits for each of the red, green and
blue colors - adding up to a total of just 15 bits. :-)

That stuff makes my brain hurt - just by trying to imagine why such choices
where made.

Regards,
Rudy Wieser


JJ

unread,
Feb 6, 2021, 8:38:04 AMFeb 6
to
On Fri, 5 Feb 2021 11:01:43 +0100, R.Wieser wrote:
>
> I cannot help but wonder : What would you need to do on Vista, 7, 8 and 10 ?
> And why didn't they mention it ? <whistle>

While it might be due to incompetence of the one who filter the
documentation, the filtering itself exist because MS hate their
bad-for-bussiness old Windows versions.

> but I imagined it could just have been a
> "hardlink" to Kernel32's SetLastError.

Ideally, yes. But it's like copy-pasted source code of SetLastError which
was renamed to GdiSetLastError instead.

> Not supporting 2bpp is unexpected to me. For the rest ? Yup, either
> values that cleanly divide 8 bits (no remainder) or multiples of it.

If I read the Windows history, Windows never did support 2-bit display.
Windows (v1.0) development started when EGA doesn't yet exist. CGA's 2-bit
320x200 display is equal to 40x25 text display mode, whose pixel resution is
way too low for Windows. So, Windows uses 1-bit 640x200 display mode instead
- which is used when it was first presented to puclic (at 1983, before EGA
was born). 4-bit display wasn't possible and officially supported until 1
year later after EGA was born.

In EGA display adapter, 640x350 was the only other 2-bit display. But EGA
also has 4-bit 640x350. MS think that, there's no point of using 4-colors
graphics when 16-colors one is already available - especially if the code
for handling 2-bit graphic is not yet available. BMP's lack of support for
2-bit graphics is simply because there's no code for handling 2-bit image.

1-bit image handling on the other hand, is still needed and supported,
because it is used by fax machines and printers. But only for image
processing. Speaking of 1-bit, it'll be interresting if we can use 1-bit
1024x768 screen. :)

> Actually ... Although the 16bpp mode uses 16 bits to /store/ the pixel, by
> default CreateDIBSection uses just 5 bits for each of the red, green and
> blue colors - adding up to a total of just 15 bits. :-)

Oh, right. I forgot about that.

But it depends on the compression type. If it's RGB, it uses R5G5B5. If it's
Bitfields, it uses R5G6B5.

15-bit Super VGA display adapter's native pixel format is R5G5B5, so BMP
can't actually store that format, because it can only store is as RGB
format.

> That stuff makes my brain hurt - just by trying to imagine why such choices
> where made.

Likely due to new innovasions and adaptations. If we have VGA as the first
display technology, we might not have 1-bit and 4-bit pixel formats.

R.Wieser

unread,
Feb 6, 2021, 10:01:36 AMFeb 6
to
JJ,

> While it might be due to incompetence of the one who filter
> the documentation,

You mean, similar to those clbuttic search-and-replace mistakes ? :-)

> the filtering itself exist because MS hate their
> bad-for-bussiness old Windows versions.

You noticed it too. :-\

I'm currently wondering what will happen first : that my XP becomes unsuable
for access of the web (forcing me to move on to ... well, something else),
or that all XP documentation will be eradicated (making it impossible to
research and write anything new for it).

> 4-bit display wasn't possible and officially supported until 1
> year later after EGA was born.

I still cannot quite fathom why the 2bpp mode was not created alongside of
the 4bpp one. I mean, apart from a different ammount of colors both methods
look to be the same, making it pretty-much a freebee.

Than again, I tend to write my stuff with re-usability in mind. :-)

> BMP's lack of support for 2-bit graphics is simply because
> there's no code for handling 2-bit image.

That actually makes some sense. Thanks for the timeline & explanation.

>> Actually ... Although the 16bpp mode uses 16 bits to /store/ the pixel,
>> by
>> default CreateDIBSection uses just 5 bits for each of the red, green and
>> blue colors - adding up to a total of just 15 bits. :-)
>
> Oh, right. I forgot about that.
>
> But it depends on the compression type. If it's RGB, it uses R5G5B5.

Which it does.

> If it's Bitfields, it uses R5G6B5.

:-) Not quite. In that case *you* have to set the bitmasks for each of the
colors.

(pardon me for being a bit of a pedant. Somehow I get the feeling you know
more about the subject than I will ever do)

Years ago I read about how grand that that was, 'cause you could assign
color depth for them depending on the scene: lots of lush green forrest ?
Use a R4G8B4 scheme. Lots of blue sea ? Give B the 8 (or even more)
bits.

>> That stuff makes my brain hurt - just by trying to imagine why such
>> choices
>> where made.
>
> Likely due to new innovasions and adaptations. If we have VGA as the first
> display technology, we might not have 1-bit and 4-bit pixel formats.

True.

As you have pretty-much pointed out, I'm using 20/20 vision looking back
into history. Choices often look simple from that vantage point. The other
way around ? Not so much.

Regards,
Rudy Wieser


Reply all
Reply to author
Forward
0 new messages