10

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.

Cœur
  • 37,241
  • 25
  • 195
  • 267
Mordachai
  • 9,412
  • 6
  • 60
  • 112
  • 3
    GDI is stuck at 24bpp. TransparentBlt() is a bit unusual, it is documented to support 32bpp. Time to move to GDI+ perhaps. – Hans Passant Oct 15 '10 at 15:31

4 Answers4

5

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)

Chris Becke
  • 34,244
  • 12
  • 79
  • 148
  • In this particular case, the issue is that I'm starting with a 4bit color bitmap. Then, in order to display it in a nice way when disabled, I'd like to make a gray scale rendering of it, and then use that as the button face (using the masked transparency technique as before). But the code that I'm using to produce the gray scale is GDI+ based, which I suspect creates a 32 bit color image rather than a 24 bit color image. – Mordachai Oct 15 '10 at 14:06
  • 1
    @Mordachai: Given what you just described, can you make the mask from the original 4-bit image and use it with the GDI-created one? – Adrian McCarthy Oct 15 '10 at 15:46
  • I like the idea for my purposes. But I'd still be interested in a generic "how to create a monochrome bit mask for *ANY* source HBITMAP". – Mordachai Oct 15 '10 at 18:11
  • I think the only way to do it for _any_ HBITMAP is to write your own. Many of these color-mapping techniques in plain GDI are specific to palette-based images and aren't applicable to non-palette images. – Adrian McCarthy Oct 18 '10 at 18:14
3

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
Ujjwal Singh
  • 4,908
  • 4
  • 37
  • 54
  • Thanks for the idea. I ended up deciding not to use this technique at all, so this coding branch is currently defunct. I may return to it in the future, and will try out this idea again then. ;) – Mordachai Jan 03 '11 at 18:28
2

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.

vt.
  • 1,325
  • 12
  • 27
  • Just to make this method clearer, a. 3) SelectObject is selecting the bitmap created with CreateDIBSection into a new DC created in 1) b. For creating the mask, you need to make sure you call SetBkColor on the new 24bit DC created in 1), I accidentially had SetBkColor called on the 32bit DC, and it took a while to troubleshoot. – frank Feb 14 '16 at 01:09
0

An alternate method would be to scan the pixels yourself and generate and monochrome bitmap based on the source color (or source alpha versus a threshold).

Note that if you're using GDI+, then, depending on the operation, the pixels may have been antialiased, resulting in none of them being an exact match for your "transparent" color.

Adrian McCarthy
  • 45,555
  • 16
  • 123
  • 175
  • I saved out the GDI+ generated gray scale image as a 32bit depth PNG, then sampled the pixels in Paint. They're okay. However, I did double check the resulting HBITMAP that GDI+ produces (via Bitmap::GetHBitmap()) and it is indeed always 32bit depth, despite the underlying Bitmap instance being PixelFormat24bppRGB). – Mordachai Oct 15 '10 at 16:07