0
votes

I've managed to avoid Windows GDI for decades, and now I'm paying the price. The following C++ code does not result in the correct colors. It's just supposed to white-fill a window and then alpha blend a single bitmap. All of the alpha is per-pixel alpha in the bitmap.

// Create a memory DC
HDC hdcMem = CreateCompatibleDC(hdc);
HBITMAP hbmMem = CreateCompatibleBitmap(hdc, cxWnd, cyWnd);
SelectObject(hdcMem, hbmMem);

// White-fill the BG
FillRect(hdcMem, &rectWnd, static_cast<HBRUSH>(GetStockObject(WHITE_BRUSH)));

// Alpha blend the bitmap into the memory DC
HDC hdcSrc = CreateCompatibleDC(hdcMem);
HBITMAP hbmOld = static_cast<HBITMAP>(SelectObject(hdcSrc, hbm));
BLENDFUNCTION bfn = { AC_SRC_OVER, 0, 255, AC_SRC_ALPHA };
AlphaBlend(hdcMem, x, y, cxBmp, cyBmp, hdcSrc, 0, 0, cxBmp, cyBmp, bfn);
SelectObject(hdcSrc, hbmOld);
DeleteDC(hdcSrc);

// Blit the memory DC to the screen
BitBlt(hdc, 0, 0, cxWnd, cyWnd, hdcMem, 0, 0, SRCCOPY);

I've verified that all the variable values are correct and that the bitmap in hbm has the correct pixel colors and alphas.

The result shows many wrong colors, e.g., a blue pixel with low alpha should be pale blue but instead is dark red. Pixels with high alpha are much closer to what they should be.

Any help appreciated. TIA.

1
One problem with AlphaBlend is that it works most correctly when RGB is premultiplied with the A value. I.e. the bitmap should never have R > A or G > A or B > A. As a result of this convention fully transparent for RGBA is all zeroes which is black for RGBx, so that e.g. pasting a transparent bitmap in to Word, and exporting the Word document as PDF, results in black areas where the bitmap should be fully transparent.Cheers and hth. - Alf
As well as pre-multiplying your bitmap, which you need to do, it sounds like your source is in ABGR order, whereas GDI needs the pixel data in ARGB order. Try swapping the red and blue values of each pixel.Jonathan Potter
@JonathanPotter: uhm, as i recall that's not correct. i could be wrong. but as i recall the COLORREF format is laid out for reading easily as RGB... in little endian memory dump.Cheers and hth. - Alf
@Jonathan Potter: The pixel values were OK. It was the premultiplying that I was missing. See below. Thanks to all.chrisd

1 Answers

4
votes

The answer, from hints in the comments: Each color channel in each pixel must be premultiplied by the alpha before blending.

get DIB bits
for each pixel
  r = r * alpha / 255
  g = g * alpha / 255
  b = b * alpha / 255
set DIB bits

MS does not make this stuff easy to find. There's not a hint of it in the only alpha blending sample I could find:

http://msdn.microsoft.com/en-us/library/windows/desktop/dd183353(v=vs.85).aspx