Alpha blending lets you superimpose images, so that both are visible at the same time. There's a Windows API call to do it, but (as you'd expect) it has some gotchas.Alpha blending! What is it? Where did it come from? Why is it so bloody
important all of a sudden? If you're like me (and I know I am), the latest
trends in graphics sort of wash over you, you ignore them, and eventually they
filter down to some level where you can use them without really noticing (cf.
Anti-aliasing
and
32-bit color).
However, I was recently given the task of making the selection rectangle in an
application use this latest gee-whiz technology, and so was forced to be mildly pro-active.
(At least, more so than I care to be at any given moment.)
Alpha blending is the computer equivalent for what the movies call a “dissolve.” When moving from one scene to another, two images are imposed on each other. The old one is gradually faded out, while the new one fades in. While this transition occurs, both images are visible, in decreasing and increasing degrees respectively.
Blending multiple images is the tailfins of the new millennium, making its first appearance in a mainstream GUI back in 2001 with Apple's Mac OS X, and found in Windows
Vista by 2007. (Or you can get it now from Stardock, using Object
Desktop.)
“Math is Hard!”
SF author Robert Heinlein, through his Lazarus Long persona, once commented that a person who couldn't cope with math was sub-human. I admit that, sometimes, the math behind graphics techniques make me feel like a gorilla. Blessedly, alpha blending is conceptually and mathematically simple.
Basically, since all we're doing is combining two images, we can start with each pixel from the backdrop and add to that the difference between the overlay and the backdrop, multiplied by whatever our alpha blend value is:
newColor = backColor + (overlayColor – backColor) * alphaPercentage
This has to be done once for each color in the pixel (red, green and blue). If
you have a genuine 32-bit bitmap, then that fourth, previously unused channel (called
the alpha channel) can be used to store a different blend value for each pixel.
In which case, the formula becomes:
newColor = backColor + (overlayColor – backColor) * (alphaByte div 255)
Figure 1.
I wrote my own alpha blending routine in Delphi, just so I could assure myself
that I knew what was going on. The results are shown in Figure 1.
Download the code that accompanies this article here.
That was unusably slow, of course, but it did allow me to also experiment with
an “unblend” which wasn't something I found elsewhere. By
experimenting with it, I realized that unblending isn't really feasible, because
your new color is an integer from 0 to 255 that you arrived at by multiplying by
some fraction and then rounding. But it did make for some cool experimentation.
Alpha Blending Made Easy. Well, Easy-ish
I was derailed in some of my early attempts because I saw many notes on Usenet
that the Win32 API AlphaBlend was slow. It is slow, of course, relative
to some of the other code out there, but in my experiments, this was well out-weighed
by other factors. In any event, it was fast enough.
BOOL AlphaBlend(
HDC hdcDest, // handle to destination DC
int nXOriginDest, // x-coord of upper-left
corner
int nYOriginDest, // y-coord of upper-left
corner
int nWidthDest, // destination width
int nHeightDest, // destination height
HDC hdcSrc, // handle to source DC
int nXOriginSrc, // x-coord of upper-left
corner
int nYOriginSrc, // y-coord of upper-left
corner
int nWidthSrc, // source width
int nHeightSrc, // source height
BLENDFUNCTION blendFunction // alpha-blending function
);
The AlphaBlend API looks like any other Win32 API we've all come to know and love, with the added bonus that the option field has only one legal value (AC_SRC_OVER). Listing 2 shows the routines needed to blend a bitmap onto a Delphi form. Figure 2 shows
the blended bitmap.
Figure 2.
What this does is take one bitmap and blend it into the overlay. It helpfully stretches your overlay bitmap into whatever size you specify. This is very cool, but this is also what makes it so slow. Make sure your overlay is exactly the same size beforehand, if speed is important.
Speed isn't important in this case. However, it becomes important if you use the alpha blend in its most ubiquitous form: drawing a selection rectangle around objects. And more important than speed is flicker. Avoiding flicker, that is.
A Blended Rectangle
The problem with blending a selection rectangle is that you end up doing the
AlphaBlend a lot. Every time the mouse moves, you've got to erase the old
rectangle and draw the new one in. You can't just erase the rectangle and draw
it in again, or you get the dreaded flicker.
One approach that might work, if the background is static, is to take a snapshot of the background and keep it handy as you resize the rectangle over it (see Figure 3).
These two approaches are both shown in Listing 3, with the flicker heavy version commented out.
Since Alpha blending is primarily
an aesthetic improvement, it makes no sense to have a version that looks bad.
Note also that the “screen cap” version does a little sleight-of-hand
by creating a TPanel descendant that exposes its Canvas property.
Listing 3 shows the simplicity of the snapshot technique: When the mouse goes down, you take a picture of the backdrop, then use that backdrop in the mouse-move routine to do the blending. By doing only one draw, you end up with a flicker-free image as you drag.
Figure 3.
But that presents another problem. You don't want the background to be static, in most cases. As when doing a selection rectangle in windows, you probably want the underlying figure to react (i.e., color itself as selected) in some way.
Blended Rectangle with LIVE NUDE SELECTED OBJECTS!
To take it to the next level, we make a little descendant of the
TShape control, called TShapeWithSelected. This new class has only one feature: when selected, it draws itself with a thick, bright yellow border. You can click on the shapes to select and deselect them, but our interest is on making them change when they're inside the selection rectangle.
Figure 4
The main routine of interest is still DrawSpaceMouseMove, where I've added a
little loop to set objects' Selected property based on whether they're inside or outside the selection rectangle. Beyond that, however, where we set up the Background image, the process is almost the same as the last step. The exception is that, instead
of using a previously captured image, we tell the DrawSpace control to paint itself to the Background bitmap.
This we blend as before, and voila, instant selection with alpha blending and no flicker! You can see this in Listing 4 and Figure 4.
Other Gewgaws
This can get even fancier, if you want to allow the users to alpha blend
selected (and multiply selected) items. Say after selecting the items in our
last example, you want the user to be able to drag them. Wouldn't it be groovy
if he could see alpha blended images of the items as he dragged them, just like
in a real application? Sure it would!
The blending aspect of this is simple enough: We just tell our control to paint
itself within the coordinates of the selection rectangle onto a new, hidden
bitmap. Then we take this new bitmap, blend it with our blue overlay image, and
then use this image to drag around the screen. But I'm going to leave
this one for you, since it also gets heavily into the mechanics of dragging-and-dropping.
Blending in .NET
.NET features GDI+, which has genuine support for the alpha channel in bitmaps.
GDI non-plus will apparently do bad things to this channel at random, so just
don't use it for genuine 32-bit bitmaps. But, as you can see from the previous
listings, you can still get a lot of functionality out of the old Win32
AlphaBlend call.
Keep in mind that AlphaBlend is not supported prior to Windows 2000. If you want
blending on the creakier operating systems, you will have to do it yourself.