Since I don't use these functions much, I thought I'd write down some notes on their use.
TransparentBlt
Some of the few new APIs released were bundled in themsimg32
dll and
were probably part of Internet Explorer's effort to paint pretty web pages.
One of them is TransparentBlt
which should have been added to Windows a long time
ago. It simply does a standard blit but uses a colour-key to mask out all colour-key
coloured pixels.

The function operates just like the regular
BitBlt
except that it takes a color-key
so I'm not going into details with it.
GradientFill

Somewhat more interesting is
GradientFill
that can be instructed to paint a smoothly interpolated
colour-band. I did that by hand in the old days, but this is much better as it
even looks good on low-end displays (64K colours and below).
GradientFill takes some arcane arguments and shades both triangles and rectangles.
With this little setup we can draw a neat gradient band:
TRIVERTEX triv[2] =
{
{ rc.left, rc.top,
GetRValue(clrFirst) << 8, GetGValue(clrFirst) << 8,
GetBValue(clrFirst) << 8, 0xFF00 },
{ rc.right, rc.bottom,
GetRValue(clrSecond) << 8, GetGValue(clrSecond) << 8,
GetBValue(clrSecond) << 8, 0xFF00 }
};
GRADIENT_RECT grc = { 0, 1 };
::GradientFill(hDC, triv, 2, &grc, 1,
bVertical ? GRADIENT_FILL_RECT_V : GRADIENT_FILL_RECT_H);
AlphaBlend
AlphaBlend
is where the fun starts. This Win2000 API allows you to blit your image with
dynamic transparency. You can blit an image with 30% transparency to an existing device context and blend
the images.
If you just alpha-blit an image to your device context, you will probably be disappointed.
CDC dcCompat;
dcCompat.CreateCompatibleDC(hDC);
HBITMAP hOldBitmap = dcCompat.SelectBitmap(hBmp);
::SetStretchBltMode(hDC, COLORONCOLOR);
BLENDFUNCTION bf;
bf.BlendOp = AC_SRC_OVER;
bf.BlendFlags = 0;
bf.SourceConstantAlpha = iAlpha;
bf.AlphaFormat = 0;
::AlphaBlend(hDC, rc.left, rc.top, cx, cy, dcCompat, 0, 0, cx, cy, bf);
dcCompat.SelectBitmap(hOldBitmap);
...which produces this result with 30% transparency:
The function actually behaves correctly because what you really want in this case is to specify a colour-key (masked pixels). Unfortunately the function doesn't take one as argument - but instead you can use the function with pr-pixel alpha. In this scenario the source bitmap itself contains a transparency value for each pixel (alpha-channel) where fully transparent pixels have 0% alpha and the visible parts could be 100% for fully opaque or 30% to blend it with the background.
This is really cool but also where the problems begin. First of all, you will either need to predefine the
alpha values in your image or generate the alpha values on the fly.
To use the pr-pixel alpha your image must be 32bpp (RGBA codec; A is the alpha byte).
With a BMP file that already includes the alpha values you are all set to paint some neat translucent graphics.
To load the bitmap resource always remember:
CBitmap bmp;
bmp.LoadBitmap(IDB_LOGO); // Wrong
bmp = AtlLoadBitmapImage(IDB_LOGO, LR_CREATEDIBSECTION);
...because LoadBitmap
is stuck in Windows 3.1 mode and is now defunct.
Now, all this assumes that you are able to create the alpha layer and save a 32bpp BMP file in your
favourite bitmap editor. Unfortunately not even my trusted Paint Shop Pro was able to do that.
The suggestion is then to use another file format such as PNG, but that would require inclusion
of yucky image libraries.
We can also do the alpha values ourselves. One way could be to keep two bitmaps: the source bitmap
and an additional 8-bit bitmap with the alpha-mask and then merge them. I'd prefer to just programmatically
construct the alpha values, but that only works when you have a simple formula to generate them from
(if pixels randomly vary from, say, 30% to 50% alpha it may quickly prove too difficult).
Anyway, creating a bitmap with alpha-values by hand does require a bit of typing, so here are the steps to follow:
First we need to know the dimensions of our bitmap.
BITMAPINFO bmi = { 0 };
bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
BOOL bRes = ::GetDIBits(hDC, hBmp, 0, cy, NULL, &bmi, DIB_RGB_COLORS);
if( !bRes ) return;
The NULL
argument value in there forces Windows to just return the bitmap
size information.
We now have the width and height, so let's ask Windows to give us access to the pixel data, this time in a format we can work with:
DWORD dwTotSize = bmi.bmiHeader.biWidth *
bmi.bmiHeader.biHeight * sizeof(DWORD);
LPDWORD pSrcBits = (LPDWORD) ::LocalAlloc(LPTR, dwTotSize);
bmi.bmiHeader.biBitCount = 32; // Ask for ARGB codec
bmi.bmiHeader.biCompression = BI_RGB;
bRes = ::GetDIBits(hDC, hBmp, 0, cy, pSrcBits, &bmi, DIB_RGB_COLORS);
if( !bRes || bmi.bmiHeader.biBitCount != 32 ) {
::LocalFree(pSrcBits);
return;
}
It is important to specify which colour codec and depth you want the pixels in otherwise
Windows just returns the format as it was read from disk.
We then ask Windows to generate a new bitmap with the same dimensions and 32bpp codec which we will populate with the same pixel data and the new alpha values:
LPDWORD pDest = NULL;
CBitmap bmpDest = ::CreateDIBSection(hDC, &bmi, DIB_RGB_COLORS,
(LPVOID*) &pDest, NULL, 0);
if( bmpDest.IsNull() ) {
::LocalFree(pSrcBits);
return;
}
The DIB Section gives us access to each individual pixel in a memory buffer.
We even asked for 32bpp so Windows kindly made room for the extra Alpha byte in the pixel data.
Now we can generate the alpha values. In this sample we just scan all the pixels and manipulate a colour-key. Since we just created a new empty bitmap we'll have to copy the source image pixels too as we go.
DWORD dwKey = RGB(GetBValue(clrTrans),
GetGValue(clrTrans),
GetRValue(clrTrans));
LPDWORD pSrc = pSrcBits;
DWORD dwShowColor = 0xFF000000;
for( int y = 0; y < abs(bmi.bmiHeader.biHeight); y++ ) {
for( int x = 0; x < bmi.bmiHeader.biWidth; x++ ) {
if( *pSrc != dwKey ) *pDest++ = *pSrc | dwShowColor;
else *pDest++ = 0x00000000;
pSrc++;
}
}
Here we just set the alpha value to 255 (0xFF000000
in ARGB pixel-format) to allow pixels
to become opaque.
Finally we can blit the new image to the device context. We'll need to tweak the arguments for AlphaBlend slightly because we are now using pr-pixel alpha.
CDC dcCompat;
dcCompat.CreateCompatibleDC(hDC);
HBITMAP hOldBitmap = dcCompat.SelectBitmap(bmpDest);
::SetStretchBltMode(hDC, COLORONCOLOR);
BLENDFUNCTION bf;
bf.BlendOp = AC_SRC_OVER;
bf.BlendFlags = 0;
bf.AlphaFormat = AC_SRC_ALPHA;
bf.SourceConstantAlpha = iAlpha;
bRes = ::AlphaBlend(hDC, rc.left, rc.top, cx, cy,
dcCompat, 0, 0, cx, cy, bf);
dcCompat.SelectBitmap(hOldBitmap);
::LocalFree(pSrcBits);
This does a pr-pixel alpha blit. The argument iAlpha
defines how semitransparent
the blit should be. 255 is opaque; at 30 the image is blended with the background and barely visible.

Working with alpha may not always be as fast as you could hope.
You can detect if the system has acceleration on the graphics card by
using GetDeviceCaps
with the SHADEBLENDCAPS
flag.
I bundled the code and polished it a bit in the download available below.
The sample code dynamically loads the alpha functions so it runs on Windows 95 and
similar too, but won't paint stuff on these platforms unless you install a proper msimg32 library.
To make it work you'll need to initialize the msimg32 dll once when your application starts.
::LoadLibrary("msimg32.dll")
Alternatively, you may consider using the GDI+ library. After all it does support many of these operations with trivial implementation. But since Microsoft doesn't seem to want to push for graphics card acceleration for the entire library, you will probably have to live with its sluggish and slow performance for some time to come.
Premultiply your bitmap
So theAlphaBlend
API is cool, and I did mention that if you already had
a bitmap with the proper alpha-channel you were good to go. Well, it's not always
that simple. The AlphaBlend
expects your bitmap data to have its
RGB data premultiplied with the alpha channel. That basically means that the bitmap
must be prepared for quick blitting to the screen and the process of doing so
is very similar to the sample code I just showed above.
There is source code in the sample download.
Just remember that if your alpha bitmap doesn't look as you expected it, you may need
to preprocess the data before you use the bitmap handle.
Source Code Dependencies
Microsoft Visual C++ 6.0Microsoft WTL 7.5 Library
Download Files
![]() | Source Code (40 Kb) |