How to erase the background of a window?

Jabb 6 Reputation points
2022-01-22T18:26:53.023+00:00

Another way than using a layered window, setting layered window attributes caused some issues in my GUI as there are some controls with transparency, pictures with rounded corners being drawn with anti-aliasing and transparency, and other n controls loaded on it which makes it hard to work with a layered window.

My goal is to create a GUI with rounded borders, for that, I will load a picture to behave as the background.

I tried to set WM_ERASEBKGND to true and inside of WM_PAINT use BitBlt with the rasters SRCCOPY | CAPTUREBLT painting an empty bitmap into the window DC, but the window still contains a background.

SetWindowRgn would not help as it doesn't produce good edges.

I have also created a topic on StackOverflow:
https://stackoverflow.com/questions/70773592/how-to-erase-the-background-of-a-window

C++
C++
A high-level, general-purpose programming language, created as an extension of the C programming language, that has object-oriented, generic, and functional features in addition to facilities for low-level memory manipulation.
3,580 questions
{count} vote

2 answers

Sort by: Most helpful
  1. Castorix31 82,231 Reputation points
    2022-01-22T21:53:43.993+00:00

    From comments, a test with SetWindowRgn and a (very) old function to convert a HBITMAP to a Region =>

    (remove space from S leep (editor bug...))

    Test with the BMP version of this image : Butterfly-Deep-Magenta.png
    to use LoadImage (otherwise, GDI+ or WIC to load .PNG)
    (RGB(192, 0, 192) for transparent color)

    #include <windows.h>
    #include <tchar.h>
    
    #pragma comment(linker,"\"/manifestdependency:type='win32' \
    name='Microsoft.Windows.Common-Controls' version='6.0.0.0' \
    processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
    
    HINSTANCE hInst;
    LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
    int nWidth = 600, nHeight = 400;
    #define IDC_BUTTON 11
    
    HBITMAP hBitmap = NULL;
    HRGN BitmapToRegion(HBITMAP hBmp, COLORREF cTransparentColor);
    
    int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPWSTR lpCmdLine, _In_ int nCmdShow)
    {
        hInst = hInstance;
        WNDCLASSEX wcex =
        {
            sizeof(WNDCLASSEX), CS_HREDRAW | CS_VREDRAW, WndProc, 0, 0, hInst, LoadIcon(NULL, IDI_APPLICATION),
            LoadCursor(NULL, IDC_ARROW), (HBRUSH)(COLOR_WINDOW + 1), NULL, TEXT("WindowClass"), NULL,
        };
        if (!RegisterClassEx(&wcex))
            return MessageBox(NULL, TEXT("Cannot register class !"), TEXT("Error"), MB_ICONERROR | MB_OK);
        int nX = (GetSystemMetrics(SM_CXSCREEN) - nWidth) / 2, nY = (GetSystemMetrics(SM_CYSCREEN) - nHeight) / 2;
        HWND hWnd = CreateWindowEx(0, wcex.lpszClassName, TEXT("Test"), WS_POPUP, nX, nY, nWidth, nHeight, NULL, NULL, hInst, NULL);
        //HWND hWnd = CreateWindowEx(0, wcex.lpszClassName, TEXT("Test"), WS_OVERLAPPEDWINDOW, nX, nY, nWidth, nHeight, NULL, NULL, hInst, NULL);
        if (!hWnd)
            return MessageBox(NULL, TEXT("Cannot create window !"), TEXT("Error"), MB_ICONERROR | MB_OK);
        ShowWindow(hWnd, SW_SHOWNORMAL);
        UpdateWindow(hWnd);
        MSG msg;
        while (GetMessage(&msg, NULL, 0, 0))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
        return (int)msg.wParam;
    }
    
    LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
    {
        static HWND hWndButton = NULL, hWndStatic = NULL;
        int wmId, wmEvent;
        PAINTSTRUCT ps;
        HDC hDC;
    
        switch (message)
        {
        case WM_CREATE:
        {
            hWndButton = CreateWindowEx(0, L"Button", L"Click", WS_CHILD | WS_VISIBLE | BS_PUSHLIKE, 50, 60, 60, 32, hWnd, (HMENU)IDC_BUTTON, hInst, NULL);
    
            // https://i.ibb.co/mCf0gTV/Butterfly-Deep-Magenta.png
            hBitmap = (HBITMAP)LoadImage(NULL, L"e:\\Butterfly_DeepMagenta.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
            // Transparent color = Deep Magenta
            HRGN hRegion = BitmapToRegion(hBitmap, RGB(192, 0, 192));
            SetWindowRgn(hWnd, hRegion, true);
            return 0;
        }
        break;
        case WM_COMMAND:
        {
            wmId = LOWORD(wParam);
            wmEvent = HIWORD(wParam);
            switch (wmId)
            {
            case IDC_BUTTON:
            {
                if (wmEvent == BN_CLICKED)
                {
                    Beep(1000, 10);
                }
            }
            break;
            default:
                return DefWindowProc(hWnd, message, wParam, lParam);
            }
        }
        break;
        case WM_NCHITTEST:
            return HTCAPTION;
        case WM_NCRBUTTONDOWN:
        {       
            S leep(200);
            DestroyWindow(hWnd);
        }
        break;
        case WM_PAINT:
        {
            hDC = BeginPaint(hWnd, &ps);
            if (hBitmap)
            {
                BITMAP bm;
                GetObject(hBitmap, sizeof(bm), &bm);
                HDC hDCMem = CreateCompatibleDC(NULL);
                HBITMAP hBitmapOld = (HBITMAP)SelectObject(hDCMem, hBitmap);        
                BitBlt(hDC, 0, 0, bm.bmWidth, bm.bmHeight, hDCMem, 0, 0, SRCCOPY);
                SelectObject(hDCMem, hBitmapOld);
                DeleteDC(hDCMem);       
            }
            EndPaint(hWnd, &ps);
        }
        break;
        case WM_DESTROY:
        {
            PostQuitMessage(0);
            return 0;
        }
        break;
        default:
            return DefWindowProc(hWnd, message, wParam, lParam);
        }
        return 0;
    }
    
    // Old function from the Net...
    HRGN BitmapToRegion(HBITMAP hBmp, COLORREF cTransparentColor)
    {
        HRGN hRgn = NULL;
        if (hBmp == NULL)
            return NULL;
    
        HDC hMemDC = CreateCompatibleDC(NULL);
        if (hMemDC == NULL)
            return NULL;
    
        BITMAP bm;
        GetObject(hBmp, sizeof(bm), &bm);
    
        BITMAPINFOHEADER RGB32BITSBITMAPINFO =
        {
            sizeof(BITMAPINFOHEADER), // biSize
            bm.bmWidth,         // biWidth;
            bm.bmHeight,        // biHeight;
            1,              // biPlanes;
            32,             // biBitCount
            BI_RGB,           // biCompression;
            0,              // biSizeImage;
            0,              // biXPelsPerMeter;
            0,              // biYPelsPerMeter;
            0,              // biClrUsed;
            0             // biClrImportant;
        };
    
        void* pBitmapBits;
        HBITMAP hDIBBitmap = CreateDIBSection(hMemDC, (BITMAPINFO*)&RGB32BITSBITMAPINFO, DIB_RGB_COLORS, &pBitmapBits, NULL, 0);
        if (hDIBBitmap == NULL)
        {
            DeleteDC(hMemDC);
            return NULL;
        }
    
        HBITMAP hBipmapOld = (HBITMAP)SelectObject(hMemDC, hDIBBitmap);
        HDC hDC = CreateCompatibleDC(hMemDC);
        if (hDC)
        {
            BITMAP bm32;
            GetObject(hDIBBitmap, sizeof(bm32), &bm32);
            while (bm32.bmWidthBytes % 4)
            {
                bm32.bmWidthBytes++;
            }
    
            HBITMAP holdBmp = (HBITMAP)SelectObject(hDC, hBmp);
            BitBlt(hMemDC, 0, 0, bm.bmWidth, bm.bmHeight, hDC, 0, 0, SRCCOPY);
    
            DWORD nMaxRects = 100;
            HANDLE hData = GlobalAlloc(GMEM_MOVEABLE, sizeof(RGNDATAHEADER) + (sizeof(RECT) * nMaxRects));
            RGNDATA* pData = (RGNDATA*)GlobalLock(hData);
            pData->rdh.dwSize = sizeof(RGNDATAHEADER);
            pData->rdh.iType = RDH_RECTANGLES;
            pData->rdh.nCount = pData->rdh.nRgnSize = 0;
            SetRect(&pData->rdh.rcBound, MAXLONG, MAXLONG, 0, 0);
    
            // Scan each bitmap row from bottom to top (the bitmap is inverted vertically)
            BYTE* p32 = (BYTE*)bm32.bmBits + (bm32.bmHeight - 1) * bm32.bmWidthBytes;
            for (int y = 0; y < bm.bmHeight; y++)
            {
                // Scan each bitmap pixel from left to right
                for (int x = 0; x < bm.bmWidth; x++)
                {
                    // Search for a continuous range of "non transparent pixels"
                    int x0 = x;
                    LONG* p = (LONG*)p32 + x;
                    while (x < bm.bmWidth)
                    {
                        if (*p == cTransparentColor)
                        {
                            // This pixel is "transparent"
                            break;
                        }
                        p++;
                        x++;
                    }
                    if (x > x0)
                    {
                        // Add the pixels (x0, y) to (x, y+1) as a new rectangle in the region
                        if (pData->rdh.nCount >= nMaxRects)
                        {
                            GlobalUnlock(hData);
                            nMaxRects += 100;
                            hData = GlobalReAlloc(hData, sizeof(RGNDATAHEADER) + (sizeof(RECT) * nMaxRects), GMEM_MOVEABLE);
                            pData = (RGNDATA*)GlobalLock(hData);
                        }
                        RECT* pr = (RECT*)&pData->Buffer;
                        SetRect(&pr[pData->rdh.nCount], x0, y, x, y + 1);
                        if (x0 < pData->rdh.rcBound.left)
                            pData->rdh.rcBound.left = x0;
                        if (y < pData->rdh.rcBound.top)
                            pData->rdh.rcBound.top = y;
                        if (x > pData->rdh.rcBound.right)
                            pData->rdh.rcBound.right = x;
                        if (y + 1 > pData->rdh.rcBound.bottom)
                            pData->rdh.rcBound.bottom = y + 1;
                        pData->rdh.nCount++;
    
                        if (pData->rdh.nCount == 2000)
                        {
                            HRGN h = ExtCreateRegion(NULL, sizeof(RGNDATAHEADER) + (sizeof(RECT) * nMaxRects), pData);
                            if (hRgn)
                            {
                                CombineRgn(hRgn, hRgn, h, RGN_OR);
                                DeleteObject(h);
                            }
                            else
                                hRgn = h;
                            pData->rdh.nCount = 0;
                            SetRect(&pData->rdh.rcBound, MAXLONG, MAXLONG, 0, 0);
                        }
                    }
                }
                p32 -= bm32.bmWidthBytes;
            }
            // Create or extend the region with the remaining rectangles
            HRGN h = ExtCreateRegion(NULL, sizeof(RGNDATAHEADER) + (sizeof(RECT) * nMaxRects), pData);
            if (hRgn)
            {
                CombineRgn(hRgn, hRgn, h, RGN_OR);
                DeleteObject(h);
            }
            else
                hRgn = h;
    
            SelectObject(hDC, hBipmapOld);
            DeleteDC(hDC);
            GlobalUnlock(hData);
            GlobalFree(hData);
        }
        DeleteObject(SelectObject(hMemDC, hBipmapOld));
        DeleteDC(hMemDC);
        return hRgn;
    }
    
    1 person found this answer helpful.

  2. Jabb 6 Reputation points
    2022-01-24T06:42:01.803+00:00

    When I try to post this as a comment it says "characters exceeded"

    Using AlphaBlend and drawing the picture with transparency I get this behavior:
    167675-2022-01-24-03-15-14.gif

    The background is drawn with whatever be underneath it, but this happens only once.

        case WM_PAINT:  
        {  
            hDC = BeginPaint(hWnd, &ps);  
            OutputDebugString(L"WM_PAINT");  
      
            if (hBitmap)  
            {  
                BITMAP bm;  
                GetObject(hBitmap, sizeof(bm), &bm);  
                HDC hDCMem = CreateCompatibleDC(NULL);  
                HBITMAP hBitmapOld = (HBITMAP)SelectObject(hDCMem, hBitmap);  
      
                //SetBkMode(hDC, TRANSPARENT);  
                //SetBkMode(hDCMem, TRANSPARENT);  
      
                //TransparentBlt(hDC, 0, 0, bm.bmWidth, bm.bmHeight, hDCMem, 0, 0, bm.bmWidth, bm.bmHeight, RGB(192, 0, 192));  
      
                bf.BlendOp = AC_SRC_OVER;  
                bf.BlendFlags = 0;  
                bf.AlphaFormat = 1; // 0 - ignore source alpha, AC_SRC_ALPHA (1) - use source alpha  
                bf.SourceConstantAlpha = 10;  
      
                x = 0;  
                y = 0;  
                dx = nWidth;  
                dy = nHeight;  
                AlphaBlend(hDC, x, y, dx, dy, hDCMem, x, y, dx, dy, bf);  
                auto err = GetLastError();  
      
                //BitBlt(hDC, 0, 0, bm.bmWidth, bm.bmHeight, hDCMem, 0, 0, SRCCOPY | CAPTUREBLT);  
                //SelectObject(hDCMem, hBitmapOld);  
                DeleteDC(hDCMem);  
            }  
            EndPaint(hWnd, &ps);  
        }  
        break;  
    

    Tried to set a timer calling WM_PAINT again but the background doesn't change anymore.
    If it could load the transparent bitmap the problem was solved, i dont understand why it trigger only once and drawns whatever be under the window.

    0 comments No comments