user5699438
user5699438

Reputation:

C++ Stop displaying bitmap

How can I stop displaying the bitmap in my Win32 Project. I have a method that I'm calling in WM_PAINT called LoadBitmap. It's code looks like this:

bool LoadBitmap(LPTSTR szfilename, HDC winhdc, int x, int y) {
    HBITMAP bitmap;
    bitmap = (HBITMAP)LoadImage(NULL, szfilename, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
    if (bitmap == NULL) {
        ::MessageBox(NULL, _T("Bitmap failed"), NULL, MB_OK);
        return false;
    }

    HDC hdc;
    hdc = ::CreateCompatibleDC(winhdc);
    if (hdc == NULL)
    {
        ::MessageBox(NULL, _T("HDC FAILED"), NULL, MB_OK);
        return false;
    }

    BITMAP qbitmap;
    int ireturn = GetObject(reinterpret_cast<HGDIOBJ>(bitmap), sizeof(BITMAP), reinterpret_cast<LPVOID>(&qbitmap));
    if (!ireturn) {
        ::MessageBox(NULL, _T("RETURN FAILED"), NULL, MB_OK);
        return false;
    }
    HBITMAP holdbitmap = (HBITMAP)::SelectObject(hdc, bitmap);
    if (holdbitmap == NULL) {
        ::MessageBox(NULL, _T("HOLD FAILED"), NULL, MB_OK);
        return false;
    }

    BOOL qRetBlit = ::BitBlt(winhdc, x, y, qbitmap.bmWidth, qbitmap.bmHeight, hdc, 0, 0, SRCCOPY);
    if (!qRetBlit)
    {
        ::MessageBox(NULL, _T("BLIT FAILED"), NULL, MB_OK);
        return false;
    }
    ::SelectObject(hdc, holdbitmap);
    ::DeleteDC(hdc);
    ::DeleteObject(bitmap);
    return true;

}

NOTE x and y change continuously. When x and y change however, the previous instance of them stays behind.

How can I stop displaying a bitmap once it has been painted in a new position?

Upvotes: 3

Views: 900

Answers (1)

Barmak Shemirani
Barmak Shemirani

Reputation: 31599

If there is background brush then the old bitmap is erased with each paint call. If there is no background then erase it manually for example with FillRect Here is example with double buffering, assumes there is no background brush.

case WM_PAINT:
{
    ...
    PAINTSTRUCT ps;
    HDC hdc = BeginPaint(hwnd, &ps);
    RECT rc = ps.rcPaint;
    HDC memdc = CreateCompatibleDC(hdc);
    HBITMAP membitmap = CreateCompatibleBitmap(hdc, rc.right, rc.bottom);
    HGDIOBJ oldbitmap = SelectObject(memdc, membitmap);

    //double-buffer ready, do custom paintings here:
    FillRect(memdc, &rc, GetSysColorBrush(COLOR_3DFACE));
    LoadBitmap(filename, memdc, x, y);

    //BitBlt to hdc
    BitBlt(hdc, 0, 0, rc.right, rc.bottom, memdc, 0, 0, SRCCOPY);

    //cleanup:
    SelectObject(hdc, oldbitmap);
    DeleteObject(membitmap);
    DeleteDC(memdc);
    EndPaint(hwnd, &ps);
    return 0;
}

Edit *************

It will be faster to load the bitmap file only once. For each paint request, we need only to draw the bitmap, instead of LoadImage every time + draw.

You can declare HBITMAP handles as static values in window's procedure. Set it up once in WM_CREATE, and clean it up in WM_NCDESTROY. Now we can use them anywhere in side window procedure:

LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp)
{
    static HBITMAP hbitmap_background = NULL;
    static HBITMAP hbitmap_sprite = NULL;

    switch (msg)
    {

    case WM_CREATE:
    {
        hbitmap_background = (HBITMAP)LoadImage(NULL, 
            L"background.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
        if (!hbitmap_background)
            OutputDebugStringW(L"!hbitmap_background\n");

        hbitmap_sprite = (HBITMAP)LoadImage(NULL, 
            L"sprite.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
        if (!hbitmap_sprite)
            OutputDebugStringW(L"!hbitmap_sprite\n");
    }

    case WM_PAINT:
    {
        PAINTSTRUCT ps;
        HDC hdc = BeginPaint(hwnd, &ps);
        RECT rc = ps.rcPaint;

        //setup double-buffering:
        HDC memdc = CreateCompatibleDC(hdc);
        HBITMAP membitmap = CreateCompatibleBitmap(hdc, rc.right, rc.bottom);
        HGDIOBJ oldbitmap = SelectObject(memdc, membitmap);

        //double-buffer ready, do custom paintings here:
        FillRect(memdc, &rc, GetSysColorBrush(COLOR_3DFACE));
        DrawBitmap(hbitmap_background, memdc, 0, 0);
        DrawBitmap(hbitmap_sprite, memdc, 0, 0);

        //BitBlt to hdc
        BitBlt(hdc, 0, 0, rc.right, rc.bottom, memdc, 0, 0, SRCCOPY);

        //cleanup:
        SelectObject(hdc, oldbitmap);
        DeleteObject(membitmap);
        DeleteDC(memdc);
        EndPaint(hwnd, &ps);
        return 0;
    }

    case WM_DESTROY:
        PostQuitMessage(0);
        return 0;

    case WM_NCDESTROY:
    {
        //cleapup bitmap handles
        if (hbitmap_background)
            DeleteObject(hbitmap_background);

        if (hbitmap_sprite)
            DeleteObject(hbitmap_sprite);
    }

    }

    return DefWindowProc(hwnd, msg, wp, lp);
} 

We can change DrawBitmap so it only needs HBITMAP handle

void DrawBitmap(HBITMAP hbitmap, HDC hdc, int x, int y)
{
    if (!hbitmap)
    {
        OutputDebugStringW(L"error\n");
        return;
    }

    BITMAP bm;
    GetObject(hbitmap, sizeof(BITMAP), &bm);    
    HDC memdc = CreateCompatibleDC(hdc);
    HGDIOBJ oldbitmap = SelectObject(memdc, hbitmap);
    BitBlt(hdc, x, y, bm.bmWidth, bm.bmHeight, memdc, 0, 0, SRCCOPY);
    SelectObject(memdc, oldbitmap);
    DeleteDC(memdc);
}

Note, in this example I assume background.bmp is very large, and sprite.bmp is very small. If it is painted the other way around then background will hide the sprite.

Upvotes: 3

Related Questions