Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to generate a monochrome bit mask for a 32bit bitmap

Under Win32, it is a common technique to generate a monochrome bitmask from a bitmap for transparency use by doing the following:

SetBkColor(hdcSource, clrTransparency);
VERIFY(BitBlt(hdcMask, 0, 0, bm.bmWidth, bm.bmHeight, hdcSource, 0, 0, SRCCOPY));

This assumes that hdcSource is a memory DC holding the source image, and hdcMask is a memory DC holding a monochrome bitmap of the same size (so both are 32x32, but the source is 4 bit color, while the target is 1bit monochrome).

However, this seems to fail for me when the source is 32 bit color + alpha. Instead of getting a monochrome bitmap in hdcMask, I get a mask that is all black. No bits get set to white (1). Whereas this works for the 4bit color source.

My search-foo is failing, as I cannot seem to find any references to this particular problem.

I have isolated that this is indeed the issue in my code: i.e. if I use a source bitmap that is 16 color (4bit), it works; if I use a 32 bit image, it produces the all-black mask.

Is there an alternate method I should be using in the case of 32 bit color images? Is there an issue with the alpha channel that overrides the normal behavior of the above technique?

Thanks for any help you may have to offer!

ADDENDUM: I am still unable to find a technique that creates a valid monochrome bitmap for my GDI+ produced source bitmap.

I have somewhat alleviated my particular issue by simply not generating a monochrome bitmask at all, and instead I'm using TransparentBlt(), which seems to get it right (but I don't know what they're doing internally that's any different that allows them to correctly mask the image).

It might be useful to have a really good, working function:

HBITMAP CreateTransparencyMask(HDC hdc, HBITMAP hSource, COLORREF crTransparency);

Where it always creates a valid transparency mask, regardless of the color depth of hSource.

like image 531
Mordachai Avatar asked Oct 15 '10 13:10

Mordachai


3 Answers

You can't do it if there is an alpha channel. COLORREF's use the top 8 bits for a number of purposes, including specifying wether or not the lower 3 bytes are a color table index into the current palette, or a RGB triplet. As such you can't specify anything except 0x00 in the upper byte of clrTransparency.

If you have an alpha bitmap then, to GDI that remains "unaware" of the alpha channel, theres no sane way to actually compare a 24bit BkColor with 32bit pixels in the bitmap.

I would expect GDI to treat the alpha channel in 32bpp bitmaps as "Reserved", and only successfully compare pixels where the reserved channel is zero. i.e. your mask color must be fully transparent anyway to have a chance of succeeding. (and, if youve made a legitimate premultiplied bitmap, that implies the RGV values would be zero too, rather constraining your choice of mask colors :P)

like image 152
Chris Becke Avatar answered Oct 19 '22 21:10

Chris Becke


Can do :)
As pointed out by 'Chris Becke' above, GDI can compare only if the reserved Alpha channel is zero.
The HBITMAP got from BITMAP::GetHBITMAP() returns an HBITMAP with Alpha Channel all set to 0xFF.
This must be 0x00 for SetBkColor() Comparison to work.
Hence, the Soln: Loop through each pixel and set the Alpha Component to Zero.

Bitmap img(L"X.bmp");
HBITMAP hBM;
img.GetHBITMAP(Color::White, &hBM);
BITMAP bm;
GetObject(g_hbmBall, sizeof(BITMAP), &bm);
for(UINT i = 0, n = -1; i &lt bm.bmHeight; i++)
    for(UINT j = 0; j &lt bm.bmWidth; j++)
    {
        n += 4; // Once per Pixel of 4 Bytes
        ((LPBYTE)bm.bmBits)[n] = 0;
    }
// Now SetBkColor and BitBlt will work as expected
like image 32
Ujjwal Singh Avatar answered Oct 19 '22 22:10

Ujjwal Singh


The method that worked for me was to convert the bitmap from 32 bit to 24 bit first.

1. CreateCompatibleDC
2. CreateDIBSection with 24 as the biBitCount.
3. SelectObject
4. BitBlt from 32bit DC to 24 bit. This removes alpha.
5. BitBlt from 24 bit DC to the monochrome DC works as expected.

On my machine this executes faster than the double loop from Ujjwal's answer.

like image 24
vt. Avatar answered Oct 19 '22 22:10

vt.