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!

    Part I: Software Engineering and Computer Games
    Part II: Software Engineering and Computer Games Reference