case WM_DESTROY:
DeleteObject(g_hbmBall);
PostQuitMessage(0);
break;
Transparent Bitmaps
Transparency
Giving bitmaps the appearance of having transparent sections is quite simple, and involves the use of a black and white Mask image in addition to the colour image that we want to look transparent.
The following conditions need to be met for the effect to work correctly: First off, the colour image must be black in all areas that we want to display as transparent. and second, the mask image must be white in the areas we want transparent, and black elsewhere. The colour and mask images are displayed as the two left most images in the example picture on this page.
BitBlt operations
How does this get us transparency? First we BitBlt() the mask image using the SRCAND operation as the last parameter, and then on top of that we BitBlt() the colour image using the SRCPAINT operation. The result is that the areas we wanted transparent don't change on the destination HDC while the rest of the image is drawn as usual.
SelectObject(hdcMem, g_hbmMask);
BitBlt(hdc, 0, 0, bm.bmWidth, bm.bmHeight, hdcMem, 0, 0, SRCAND);
SelectObject(hdcMem, g_hbmBall);
BitBlt(hdc, 0, bm.bmHeight, bm.bmWidth, bm.bmHeight, hdcMem, 0, 0, SRCPAINT);
Pretty simple eh? Fortunately it is, but one question remains… where does the mask come from? There are basically two ways to get the mask…
• Make it yourself in whatever graphics program you made the colour bitmap in, and this is a reasonable solution if you are using a limited number of graphics in your program. This way you can just add the mask resource to your program and load it with LoadBitmap().
• Generate it when your program runs, by selecting one colour in your original image to be your "transparent" colour, and create a mask that is white everywhere that colour exists, and black everywhere else.
Since the first one is nothing new, you should be able to do things that way yourself if you want to. The second way involves from BitBlt() trickery, and so I will show one way of accomplishing this.
Mask Creation
The simplest way to do it, would be to loop through every pixel on the colour image, check it's value and then set the corresponding pixel on the mask to black or white… SetPixel() is a very slow way to draw images however, and it's not really practical.
A much more efficient way involves using the way BitBlt() converts from colour images to black and white. If you BitBlt() (using SRCCOPY) from an HDC holding a colour image into an HDC holding a black and white image, it will check what colour is set as the Background Colour on the colour image, and set all of those pixels to White, any pixel that is not the background colour will end up Black.
This works perfectly to our advantage, since all we need to do is set the background colour to the colour we want transparent, and BitBlt() from the colour image to the mask image. Note that this only works with a mask bitmap that is monochrome (black and white)… that is bitmaps with a bit depth of 1 bit per pixel. If you try it with a colour image that only has black and white pixels, but the bitmap itself is greater than 1 bit (say 16 or 24 bit) then it won't work.
Remember the first condition for succesful masking above? It was that the colour image needs to be black everywhere we want transparent. Since the bitmap I used in this example already meets that condition it doesn't really need anything special done, but if you're going to use this code for another image that has a different colour that you want transparent (hot pink is a common choice) then we need to take a second step, and that is use the mask we just created to alter the original image, so that everywhere we want transparent is black. It's ok if other places are black too, because they aren't white on the mask, they won't end up transparent. We can accomplish this by BitBlt()ing from the new mask to the original colour image, using the SRCINVERT operation, which sets all the areas that are white in the mask to black in the colour image.
This is all a bit of a complex process, and so it's nice to have a handy utility function that does this all for us, and here it is:
HBITMAP CreateBitmapMask(HBITMAP hbmColour, COLORREF crTransparent) {
HDC hdcMem, hdcMem2;
HBITMAP hbmMask; BITMAP bm;
// Create monochrome (1 bit) mask bitmap.
GetObject(hbmColour, sizeof(BITMAP), &bm);
hbmMask = CreateBitmap(bm.bmWidth, bm.bmHeight, 1, 1, NULL);
// Get some HDCs that are compatible with the display driver
hdcMem = CreateCompatibleDC(0);
hdcMem2 = CreateCompatibleDC(0);
SelectBitmap(hdcMem, hbmColour);
SelectBitmap(hdcMem2, hbmMask);
// Set the background colour of the colour image to the colour
// you want to be transparent.
SetBkColor(hdcMem, crTransparent);
// Copy the bits from the colour image to the B+W mask… everything
// with the background colour ends up white while everythig else ends up
// black… Just what we wanted.
BitBlt(hdcMem2, 0, 0, bm.bmWidth, bm.bmHeight, hdcMem, 0, 0, SRCCOPY);
// Take our new mask and use it to turn the transparent colour in our
// original colour image to black so the transparency effect will
// work right.
BitBlt(hdcMem, 0, 0, bm.bmWidth, bm.bmHeight, hdcMem2, 0, 0, SRCINVERT);
// Clean up.
DeleteDC(hdcMem);
DeleteDC(hdcMem2);
return hbmMask;
}
NOTE: This function call SelectObject() to temporarily select the colour bitmap we pass it into an HDC. A bitmap can't be selected into more than one HDC at a time, so make sure the bitmap isn't selected in to another HDC when you call this function or it will fail. Now that we have our handy dandy function, we can create a mask from the original picture as soon as we load it:
case WM_CREATE: