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.
How does this get us transparency? First we
SelectBitmap(hdcMem, g_hbmMask); BitBlt(hdc, 0, 0, bm.bmWidth, bm.bmHeight, hdcMem, 0, 0, SRCAND); SelectBitmap(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...
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
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...
A much more efficient way involves using the way
This works perfectly to our advantage, since all we need to do is set the background colour to the colour we want
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
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:
PROCEDURE CreateBitmapMask(hbmColour: HBITMAP; crTransparent: COLORREF): HBITMAP; VAR hdcMem, hdcMem2 : HDC; hbmMask : HBITMAP; bm : BITMAP; BEGIN (* Create monochrome (1 bit) mask bitmap. *) FUNC GetBITMAP(hbmColour, bm); hbmMask := CreateBitmap(bm.bmWidth, bm.bmHeight, 1, 1, NIL); (* Get some HDCs that are compatible with the display driver *) hdcMem := CreateCompatibleDC(NIL); hdcMem2 := CreateCompatibleDC(NIL); FUNC SelectBitmap(hdcMem, hbmColour); FUNC SelectBitmap(hdcMem2, hbmMask); (* Set the background colour of the colour image to the colour you want to be transparent. *) FUNC 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. *) FUNC 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. *) FUNC BitBlt(hdcMem, 0, 0, bm.bmWidth, bm.bmHeight, hdcMem2, 0, 0, SRCINVERT); (* Clean up. *) FUNC DeleteDC(hdcMem); FUNC DeleteDC(hdcMem2); RETURN hbmMask; END CreateBitmapMask;
NOTE: This function calls
Now that we have our handy dandy function, we can create a mask from the original picture as soon as we load it:
| WM_CREATE : g_hbmBall := LoadBitmapId(Instance, IDB_BALL); IF g_hbmBall = NIL THEN FUNC MessageBox(hwnd, "Could not load IDB_BALL!", "Error", MB_OK BOR MB_ICONEXCLAMATION); END; g_hbmMask := CreateBitmapMask(g_hbmBall, RGB(0, 0, 0)); IF g_hbmMask = NIL THEN FUNC MessageBox(hwnd, "Could not create mask!", "Error", MB_OK BOR MB_ICONEXCLAMATION); END;
The second parameter is of course the colour from the original image that we want to be transparent, in this case black.
How does all this work?
.. you may be asking. Well hopefully your experience with programming means that you understand binary operations such as OR, XOR, AND, NOT and so on. I'm not going to explain this process completely, but I will try to show how I used it for this example. If my explanation isn't clear enough (which it's bound to not be), reading up on binary operations should help you understand it. Understanding it isn't critical for using it right now, and you can just get away with trusting that it works if you want.
However, the rest of our colour image isn't black, and if the destination also isn't black, then we get a combination of the source and destination colours, the result you can see in the second ball on the second row in the example picture. This is the whole reason for using the mask to set the pixels we want to colour to black first, so that when we use OR with the colour image, the coloured pixels don't get mixed up with whatever is underneath them.
This is the
This is all a little GDI mojo that depends on it's colour vs. monochrome handling, and it hurts my head to think about it too much, but it really makes sense... honest.
The example code in the project bmp_two that goes along with this section contains the code for the example picture
on this page. It consists of first drawing the mask and the colour image exactly as they are using
The background in this example is set to gray to make the transparency more obvious, as using these operations on a white or black background makes it hard to tell if they're actually working or not.
Copyright © 1998-2011, Brook Miles. All rights reserved. Adapted for Modula-2 by Frank Schoonjans, with permission.