I am building a setup program that has a back button and a next button, typical for setup programs.
However, I seem to be having an problem, while the initial draw works, if I change the button bitmap to a activated one, then switch back to the deactivated one, it seems like the alpha layer is getting added on and on.
Better representation of what is happening:
| Deactivated state. | ![]() |
| After I click the Next button, bringing me to the next page, button switches to a activated bitmap. | ![]() |
| If I click the Back button, going back to the first page, I get this instead of the first state I expected. | ![]() |
Here's my WM_PAINT that draws the back button
InvalidateRect(hWnd, NULL, 1); // Clear the window after a button state change requiring redraw
HBRUSH hBrushBtn;
RECT rc;
GetClientRect(hWnd, &rc);
hBrushBtn = (HBRUSH)GetStockObject(NULL_BRUSH);
BITMAP bitmap01;
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hBackBtn, &ps);
FillRect(hdc, &rc, (HBRUSH)(COLOR_WINDOW + 1)); // Attempt #1 at fixing the bug, it did not work.
PremultiplyAlpha(hdc, hBackBtnTmpImg); // Premultiplied alpha function call
HDC hdcMem01 = CreateCompatibleDC(hdc);
HGDIOBJ oldBitmap01 = SelectObject(hdcMem01, hBackBtnTmpImg);
GetObjectW(hBackBtnTmpImg, sizeof(bitmap01), &bitmap01);
BLENDFUNCTION bf = { AC_SRC_OVER, 0, 255, AC_SRC_ALPHA };
AlphaBlend(hdc, 0, 0, bitmap01.bmWidth, bitmap01.bmHeight, hdcMem01, 0, 0, bitmap01.bmWidth, bitmap01.bmHeight, bf); // Paint the image
SelectObject(hdcMem01, oldBitmap01);
DeleteDC(hdcMem01);
EndPaint(hBackBtn, &ps);
And here's the function that premultiplies the bitmap
void PremultiplyAlpha(HDC hDC, HBITMAP hBmp)
{
BITMAP bm = { 0 };
GetObject(hBmp, sizeof(bm), &bm);
BITMAPINFOHEADER bminfoheader;
::ZeroMemory(&bminfoheader, sizeof(BITMAPINFOHEADER));
bminfoheader.biSize = sizeof(BITMAPINFOHEADER);
bminfoheader.biWidth = bm.bmWidth;
bminfoheader.biHeight = bm.bmHeight;
bminfoheader.biPlanes = 1;
bminfoheader.biBitCount = 32;
bminfoheader.biCompression = BI_RGB;
HDC windowDC = CreateCompatibleDC(hDC);
LPBYTE pBitData = (LPBYTE) ::LocalAlloc(LPTR, bm.bmWidth * bm.bmHeight * sizeof(DWORD));
if (pBitData == NULL) return;
LPBYTE pData = pBitData;
GetDIBits(windowDC, hBmp, 0, bm.bmHeight, pData, (BITMAPINFO*)&bminfoheader, DIB_RGB_COLORS); // load pixel info
for (int y = 0; y < bm.bmHeight; y++) {
BYTE *pPixel = (BYTE *)pData + bm.bmWidth * 4 * y;
for (int x = 0; x < bm.bmWidth; x++) {
pPixel[0] = pPixel[0] * pPixel[3] / 255;
pPixel[1] = pPixel[1] * pPixel[3] / 255;
pPixel[2] = pPixel[2] * pPixel[3] / 255;
pPixel += 4;
}
}
SetDIBits(windowDC, hBmp, 0, bm.bmHeight, pData, (BITMAPINFO*)&bminfoheader, DIB_RGB_COLORS); // save the pixel info for later manipulation
::LocalFree(pBitData);
}
I tried my best to resolve this by making three iterations of the function that is supposed to do the same thing, all of which failed.


