31.3 Transparent bitmaps
We have the option with the cSpriteIcon
of giving it a 'transparent background.' The reason we'll be interested in transparent backgrounds is that we want to have a cSpriteIcon
that shows a bitmap sliding around like a little live creature. If you have two normal bitmaps pasted on the screen close to each other, then the background region of one bitmap is likely to cut an unattractive white corner out of the other bitmap's central image.
How is this effect to be achieved? Since there is a simple way to make text and dotted line backgrounds transparent with the CDC::SetBkMode(TRANSPARENT)
call, you might think it's easy to make bitmaps have transparent backgrounds. But this is not the case. Making bitmaps with transparent backgrounds is something we have to do by hand in two different ways, one for OpenGL, and one for Windows graphics.
OpenGL has alpha blending methods which are in fact rich enough to produce transparent background bitmaps, and that's the route we use in the cGraphicsOpenGL
implementation of the cSpriteIcon
for which transparent()
is TRUE.
The Windows API includes methods called AlphaBlend
and TransparentBlt
that one might think would be useful here. But, prior to Visual Studio.NET, these methods have not seemed to give us what we need. What AlphaBlend
(which is a generalization of TransparentBlt
) does is mainly to overlay a full rectangle bitmap upon an existing image, but with the overlaid rectangle set to a certain level of transparency. Examples of this kind of graphic are the little station logos that one sees in the corners of many TV channels' images. But what we want is a way to write an image which is opaque in some parts (where our 'character' is) and transparent in other parts (where our 'background' is).
As mentioned earlier in this chapter, the MFC Version 7.0 supplied with Visual Studio.NET has a CImage
class that improves the handling of bitmaps. In particular, you can use CImage
to get transparent background bitmaps. However, the Pop Framework currently has its own, older way of doing transparent backgrounds.
In Windows graphics, we can get transparent bitmap backgrounds using a special trick based on something that the wonderful BitBlt
function can do. BitBlt
has nine arguments, and the last argument is an argument called a ROP code, where ROP stands for 'raster operation.' When you want your BitBlt
to just copy the information from one HDC
to another (which is most of the time), you use the SRCCOPY raster operation. But there is a wide range of other possible raster operation codes. The two we will be interested in here are SRCAND and SRCPAINT. The effect of these is to perform, respectively, a bitwise AND
and a bitwise OR
between the color codes of the target and source pixels.
Let's explain this in more detail. Suppose that at some pixel, the color in the target HDC
is called targetcolor, and the color in the corresponding source HDC
pixel is called sourcecolor. Finally, let newtargetcolor be the new color which ends up in the target pixel. The three different raster operations mentioned act like this:
SRCCOPY: newtargetcolor = sourcecolor;
SRCAND: newtargetcolor = sourcecolor AND
targetcolor;
SRCPAINT: newtargetcolor = sourcecolor OR
targetcolor;
What exactly does it mean to combine two colors with AND
or OR
? Recall that in Windows a color is a COLORREF
type, which is a block of 32 bits, of which the right-hand three bytes correspond to the intensities of red, green, and blue respectively. Combining the colors with AND
or OR
just means combining the corresponding bits pair by pair with AND
or OR.
If we were to write WHITE to stand for the value RGB(255,255,255) which has three bytes of all 1s and write BLACK to stand for the COLORREF
value RGB(0,0,0) with three bytes of all 0s, then it's not too hard to see that for any values of targetcolor and sourcecolor:
BLACK = targetcolor AND
BLACK;
targetcolor = targetcolor AND
WHITE;
WHITE = targetcolor OR
WHITE;
targetcolor = targetcolor OR
BLACK;
We are going to use these properties of OR
and AND
in order to create the illusion of a bitmap with a transparent background. Before going into the details, we should examine why this is necessary. Why can't we just put the image which we want on the screen and not paint any background at all? Well, the fast pixel-transfer operation called BitBlt
only works on rectangles of pixels. This has to do with the fact that it takes very little calculation to single out a rectangle of pixels, as opposed to some irregular shape. Then why isn't there a transparent pixel color? Well, moving a pixel involves copying a color value to a memory location, which inevitably is going to change the value already there. The trick we use for getting transparent background bitmaps is not at all obvious: we use two bitmaps and two BitBlts, one with the SRCAND raster operation, and one with the SRCPAINT raster operation. A last point to mention here is that for the hardware on the graphics card, all three kinds of BitBlts
are equally fast. Just as in assembly language the instruction AND
AX BX is executed as fast as MOV
AX BX, doing a SRCAND BitBlt
happens as fast as doing a SRCCOPY BitBlt. You might think of a BitBlt
as a 'super' assembly language instruction that is executed in parallel on many pixels at once.
So now let's look at how Windows bitmaps with transparent backgrounds work. Briefly, the way we're going to implement transparent background bitmaps is to use two bitmaps, a mask bitmap and an image bitmap. First you AND
the mask bitmap with the target, and then you OR
an image bitmap with the screen. The mask has BLACK pixels everywhere that the image is, and it has WHITE pixels everywhere that the image isn't. When you AND
the mask with the target, you cut a black hole in the target just the shape of the image, and you leave the rest of the target alone. The pixels in the 'hole' all get set to black. The image bitmap has the image pixels set to the correct image color values, and the other background pixels of the image bitmap are set to BLACK. When you OR
the image bitmap with the target you lay the image right into the waiting hole, and you don't change the other target pixels.
The cTransportMemoryDC
class encapsulates this trick, automatically generating a source bitmap and mask bitmap in its constructor. See the memorydc.* files for details.
And that's about it for now. Happy programming!
|