Copyright © 2001 by Feng Yuan (author of Windows Graphics Programming: Win32 GDI and DirectDraw, www.fengyuan.com). All rights reserved.
Version 1.00: Oct 21, 2000. 1.01: Feb 19, 2001 (add sample project link)
How to use AlphaBlend, with constant alpha, with per-pixel alpha channel, and with external 32-bpp bitmaps.
Calling AlphaBlend with 32-bpp DIB section
As no image processing package is known to be able to generate 32-bpp BMP file, which is the native bitmap format supported by GDI, TGA, the simplest bitmap format supporting alpha channel is used. For example Jasc Paint Shop Pro can generate 32-bpp uncomressed TGA files with a single 8-bit alpha channel.
Using Paint Shop Pro to Edit Bitmap with Alpha Channel (mask)
TGA files can be easily loaded into a Window program, to create a 32-bpp DIB section, which can be used by AlphaBlend. The alpha channel can be pre-multipled to RGB channels to generated pre-multiplied 32-bpp bitmap needed by AlphaBlend to enable per-pixel alpha.
Uncompressed TGA file is extremely simple. For example, 32-bpp uncompressed TGA file generated by Paint Shop Pro consists of 18-byte header, image pixel array, and ignorable footer with creator information.
The header can be defined as:
#pragma pack(push, 1) typedef struct { BYTE IDLength; // 0 BYTE ColorMapType; // 0 BYTE ImageType; // 2: Truecolor image data WORD CMapStart; // 0 WORD CMapLength; // 0 BYTE CMapDepth; // 0 WORD XOffset; // 0 WORD YOffset; // 0 WORD Width; // width WORD Height; // height BYTE PixelDepth; // 32 for 32-bpp image BYTE ImageDescriptor; // 8 for 8-bit alpha } TGA_Header; #pragma pack(pop)
HBITMAP Load32bppTga(const TCHAR * pFileName, bool bPreMultiply) { HANDLE handle = CreateFile(pFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if ( handle == INVALID_HANDLE_VALUE ) return NULL; TGA_Header header; DWORD dwRead = 0; ReadFile(handle, & header, sizeof(header), & dwRead, NULL); if ( (header.IDLength!=0) || (header.ColorMapType!=0) || (header.ImageType!=2) || (header.PixelDepth!=32) || (header.ImageDescriptor!=8) ) { CloseHandle(handle); return NULL; } BITMAPINFO bmp = { { sizeof(BITMAPINFOHEADER), header.Width, header.Height, 1, 32 } }; void * pBits = NULL; HBITMAP hBmp = CreateDIBSection(NULL, & bmp, DIB_RGB_COLORS, & pBits, NULL, NULL); if ( hBmp==NULL ) { CloseHandle(handle); return NULL; } ReadFile(handle, pBits, header.Width * header.Height * 4, & dwRead, NULL); CloseHandle(handle); if ( bPreMultiply ) { for (int y=0; y<header.Height; y++) { BYTE * pPixel = (BYTE *) pBits + header.Width * 4 * y; for (int x=0; x<header.Width; x++) { pPixel[0] = pPixel[0] * pPixel[3] / 255; pPixel[1] = pPixel[1] * pPixel[3] / 255; pPixel[2] = pPixel[2] * pPixel[3] / 255; pPixel += 4; } } } return hBmp; }
void AlphaDraw(HDC hDC, int x, int y, int width, int height, HBITMAP hBmp) { HDC hMemDC = CreateCompatibleDC(hDC); HGDIOBJ hOld = SelectObject(hMemDC, hBmp); HBRUSH hBrush = CreateSolidBrush(RGB(0xFF, 0xFF, 0)); SelectObject(hDC, hBrush); Ellipse(hDC, x, y, width*2, height * 2); // a yellow circle in the background SelectObject(hDC, GetStockObject(WHITE_BRUSH)); DeleteObject(hBrush); BitBlt(hDC, x, y, width, height, hMemDC, 0, 0, SRCCOPY); // display the bitmap BLENDFUNCTION pixelblend = { AC_SRC_OVER, 0, 255, AC_SRC_ALPHA }; AlphaBlend(hDC, x, y+height, width, height, hMemDC, 0, 0, width, height, pixelblend); // blend with per-pixel alpha BLENDFUNCTION blend50 = { AC_SRC_OVER, 0, 128, 0 }; AlphaBlend(hDC, x+width, y, width, height, hMemDC, 0, 0, width, height, blend50); // 50% blending SelectObject(hMemDC, hOld); DeleteObject(hMemDC); }