Click here to Skip to main content
15,886,199 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
I use to draw device-independent bitmaps (DIBs) to the screen by means of either SetDIBitsToDevice (true size) or StretchDIBits (with zoom).

These functions move pixel data from a memory buffer directly to screen. [This operation is not the same as copying from one DC (Device Context) to another DC nor from a DDB (Device Dependent Bitmap) to another.]

So far so good. Works well with the image depths I am using (biBitCount = 8, 24 or 32).

Now I want to do partial drawing, as if parts of the image were transparent. In other words, attach an alpha value to every pixel. I can satisfy myself with binary values (fully opaque or fully transparent), but plain alpha is good too.

I have the option of reading the background pixels from screen and doing the blend myself, but this solution doesn't look attractive to me, because it involves costly reads, plus the need to convert to 24 bits because the background of a Window is in color.

So I am looking for efficient ways to achieve this effect. I expected to find BlendDIBitsToDevice or StretchBlendDIBits entries or similar, but it seems I need to roll up my sleeves.

Any suggestion ?
Posted
Updated 4-Jun-13 9:29am
v2

1 solution

The solution is to use the AlphaBlend function. The latter requires two things: on one hand a source device context, and on the other hand a source bitmap, which must be selected into it (don't ask me why).

The device context is created as a memory DC which is compatible with the display DC. It is obtained by means of CreateCompatibleDC.

The bitmap is generated as a so called DIB Section, via CreateDIBSection.

Last but not least, you have to know that premultiplied Alpha is required, meaning that all three Red, Green, Blue components must be multiplied by Alpha (and scaled) before blitting. Here is a snippet.
C++
// Initialize the bitmap info structure
struct tagBITMAPINFO
{
    BITMAPV4HEADER bmiHeader;
    RGBQUAD bmiColors[256];
} BufferInfo;

BufferInfo.bmiHeader.bV4Size= sizeof(BITMAPV4HEADER);
BufferInfo.bmiHeader.bV4Width= Width;
BufferInfo.bmiHeader.bV4Height= Height;
BufferInfo.bmiHeader.bV4Planes= 1;
BufferInfo.bmiHeader.bV4BitCount= 32;
BufferInfo.bmiHeader.bV4V4Compression= BI_BITFIELDS;
BufferInfo.bmiHeader.bV4SizeImage= 0;
BufferInfo.bmiHeader.bV4ClrUsed= 0;
BufferInfo.bmiHeader.bV4ClrImportant= 0;
BufferInfo.bmiHeader.bV4AlphaMask= 0xff000000;
BufferInfo.bmiHeader.bV4RedMask=   0x00ff0000;
BufferInfo.bmiHeader.bV4GreenMask= 0x0000ff00;
BufferInfo.bmiHeader.bV4BlueMask=  0x000000ff;
BufferInfo.bmiHeader.bV4CSType= 0;

// Create a compatible device context
HDC hMemoryDC= CreateCompatibleDC(hDC);

// Create a DIB section and select it
byte* Section;
HBITMAP hBitMap = CreateDIBSection(MemoryDC, (BITMAPINFO*)&BufferInfo, DIB_RGB_COLORS, (void**)&Section, NULL, 0x0);
HGDIOBJ Save= SelectObject(hMemoryDC, hBitMap);

// Premultiply by alpha
for (int y= 0; y < Height; y++)
{
    for (int x= 0; x < Width; x++)
    {
        RgbaPixel P= *(RgbaPixel*)&SrcBuffer[y * SrcPitch + 4 * x];
        int Q= P.Alpha * 33025; // 2^31 / 255^2
        P.Red=   (P.Red   * Q) >> 23;
        P.Green= (P.Green * Q) >> 23;
        P.Blue=  (P.Blue  * Q) >> 23;
        *(RgbaPixel*)&Section[y * SrcPitch + 4 * x]= P;
    }
}

// Blending function (required by AlphaBlend)
BLENDFUNCTION Blend;
Blend.BlendOp = AC_SRC_OVER;
Blend.BlendFlags = 0;
Blend.AlphaFormat = AC_SRC_ALPHA;
Blend.SourceConstantAlpha = 0xff;

// Blit with blending
AlphaBlend(
    hDC, 0, 0, (int)(Width * Zoom), (int)(Height * Zoom),
    hMemoryDC, 0, 0, Width, Height,
    Blend);

SelectObject(hMemoryDC, Save);
DeleteObject(hBitMap);
DeleteObject(hMemoryDC);
 
Share this answer
 
v6

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900