Drawing Bitmaps with Transparency?

I've been teaching myself C++, and am trying to figure out how to blt a bitmap to the screen while keeping the background transparent. TransparentBlt causes a memory leak, which I'm told is because I'm using Windows 98. I'm currently trying to figure out how to use masks. From what I have read online this should work:

void Draw(HDC hDC, POINT ptLocation, int iFrame, int iState)
{
HDC hdcMask;
hdcMask = CreateCompatibleDC(hDC);

SelectObject(hdcMask, m_hbmMask);
BitBlt(hDC, ptLocation.x, ptLocation.y, GetWidth(), GetHeight(),
hdcMask, iState * GetWidth(), iFrame * GetHeight(), SRCAND);
DeleteDC(hdcMask);

HDC hdcMemory;
hdcMemory = CreateCompatibleDC(hDC);

SelectObject(hdcMemory, m_hBitmap);
BitBlt(hDC, ptLocation.x, ptLocation.y, GetWidth(), GetHeight(),
hdcMemory, iState * GetWidth(), iFrame * GetHeight(), SRCPAINT);
DeleteDC(hdcMemory);
}

However, the output of this function is m_hBitmap, with no transparency. Any idea why? Sorry if the answer is patently obvious. If I've left any relevant information out please let me know and I'll post it.
The way I've previously done this:
1. Make the bitmap
2. make the bitmap mask- this is a black & White version of the bitmap - Black will take on the bitmap colours, white will be transparent(background)
3. Blit the bitmap to the destination dc using the XOR ROP
4. Blit the mask to the destination dc using AND ROP
5 Blit the bitmap to the destination dc again using XOR ROP

another way which requires only 2 blits:

1) make the bitmap mask as guestgulkan described (black = bitmap colors, white = transparent)
2) replace all transparent colors on the original bitmap with black (0)

Then to blit:

1) Blit the mask to destination with AND
2) Blit the original to destination with OR
That is what my code does. However, I have realized that the problem wasn't with this code. The mask bitmap was failing to be created and was blank. I'm not sure, but I think it was because I was trying to load a monochrome bitmap with code intended to load a color bitmap. The CopyMemory function calculates the size to be copied by muliplying the number of bits by SizeOf(RGBQUAD). Should I be using something else there?
Haw, I didn't actually look at your code in your original post. I was just posting off of what guestgulkan said. hahaha sorry.

I don't see where you're creating the mask in that code (or where you're replacing the transparent colors of the original image with black).

The mask and memory DC are not temporary objects, either. Like you shouldn't be creating them and deleting them every draw. They should have a lifetime of however long you want the image to be loaded (since they're part of the image). Plus you're deleting them wrong anyway (probably have memory leaks)

I remember reading an article on a simple way to create the mask... but I can't find it now for the life of me.

Anyway I have to get ready for work now... if I have time in a bit I'll try to go over how you can do this. Or maybe during my break at work.
A little context: The Draw function is a part of a graphics class I've been working on. m_hBitmap and m_hbmMask are handles to the color and mask bitmaps the class stores. It is supposed, when passed an HDC and location, to blt the stored bitmap to the given DC at the given point. The mask itself is supposed to be automatically generated by the function on this page:

http://www.winprog.org/tutorial/transparency.html

Unfortunately, it doesn't work, and seems to be returning a blank bitmap.
Sorry, I like totally forgot about this thread yesterday.

I've seen shortcuts like that link for creating masks before. Honestly I never tried any of them out.

Personally, I have no idea how that code is supposed to work. Even if the SetBkColor trick works like he suggests it does (which I see no reason in documentation why it would), there's no way I can see the SRCINVERT blit working, since according to the documentation it's a XOR operation, which means it'd only work if your background color is white.

Unfortunately I'm not on Windows myself, so I can't experiment and find a creative solution.

You could always do this the hard way (GetDIBits and SetDIBits) which is less than optimal, but at least you know it'll work (plus that's always something you can come back to and change later when you find a better approach)

Basically when you load up the original bitmap:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
COLORREF mask[ WIDTH ];
COLORREF image[ WIDTH ];

for( /*each row in the source bitmap*/ )
{
    GetDIBits( /*source bitmap, get as 32 bits, put in 'image'*/ );

    for(int x = 0; x < WIDTH; ++x)
    {
        if( image[x] == transparentcolor)
        {
            mask[x] = RGB(255,255,255);
            image[x] = 0;
        }
        else
            mask[x] = 0;
    }

    SetDIBits( /*source bimap, set as 32 bits, get from 'image' */ );
    SetDIBits( /*mask bitmap, set as 32 bits, get from 'mask' */ );
}



As for your graphics class.... don't create DCs and delete them on the fly. When you load a bitmap, create the mask, and create two offscreen DCs. Load the source and mask bitmaps into their respective DCs and leave them there. Only delete when you unload, and then, be sure you reselect the original bitmaps into the DC.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
class YourClass
{
//...
private:
    HDC m_dcImg;
    HBITMAP m_bmpImg;
    HBITMAP m_oldImg;

    HDC m_dcMask;
    HBITMAP m_bmpMask;
    HBITMAP m_oldMask;
};


void YourClass::LoadABitmap()
{
    m_bmpImg = LoadTheBitmapHowever();
    m_dcImg = CreateCompatibleDC( NULL );
    m_oldImg = (HBITMAP)SelectObject(m_dcImg,m_bmpImg);

    m_bmpMask = CreateBitmap( /* Create a Width*Height monochrome bitmap here */ );
    m_dcMask = CreateCompatibleDC( NULL );
    m_oldMask = (HBITMAP)SelectObject(m_dcMask,m_bmpMask);

    BuildYourMask();
}


void YourClass::CleanUp()
{
    SelectObject(m_dcImg,m_oldImg);
    SelectObject(m_dcMask,m_oldMask);

    DeleteObject(m_bmpImg);
    DeleteObject(m_bmpMask);
    DeleteDC(m_dcImg);
    DeleteDC(m_dcMask);

    // don't delete the DCs with your bitmaps still selected in them
    //  don't delete the 'old' bitmaps
}
Topic archived. No new replies allowed.