Issue with redrawing premultiplied alpha bitmap with Win32 GDI

44 Views Asked by At

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. Deactivated State
After I click the Next button, bringing me to the next page, button switches to a activated bitmap. Activated State
If I click the Back button, going back to the first page, I get this instead of the first state I expected. DeactivatedState after going from activated

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.

0

There are 0 best solutions below