【问题标题】:C++ Stop displaying bitmapC++ 停止显示位图
【发布时间】:2016-03-18 05:34:09
【问题描述】:

如何停止在我的 Win32 项目中显示位图。我有一个在 WM_PAINT 中调用的方法,称为 LoadBitmap。它的代码如下所示:

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;

}

注意 x 和 y 不断变化。 但是,当 x 和 y 发生变化时,它们的前一个实例会留在后面。

位图绘制到新位置后如何停止显示?

【问题讨论】:

  • 在不了解 Win32 的情况下,位图不是可移动的“项”。绘制后,像素将一直保留,直到被擦除。所以要摆脱“痕迹”,你必须用其他东西(在这种情况下为背景颜色)在它上面画。
  • 我该怎么做?
  • 可能有两种方法:让Windows在重绘之前清除窗口(我不知道如何),或者找到一个绘制填充矩形的函数,并用它来擦除轨迹。
  • 您可能还想研究双缓冲。您可以绘制到另一个位图,然后将该位图绘制到窗口。它可以防止闪烁,就像您在不断擦除移动位图留下的痕迹时可能遇到的那样。

标签: c++ windows bitmap


【解决方案1】:

如果有背景画笔,则每次绘制调用都会擦除旧位图。如果没有背景,则手动擦除它,例如使用FillRect 这是双缓冲的示例,假设没有背景画笔。

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;
}

编辑 *************

只加载一次位图文件会更快。对于每个绘制请求,我们只需要绘制位图,而不是每次都加载图像+绘制。

您可以在窗口的过程中将HBITMAP 句柄声明为静态值。在WM_CREATE 中设置一次,然后在WM_NCDESTROY 中进行清理。现在我们可以在侧窗程序的任何地方使用它们:

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);
} 

我们可以改变DrawBitmap,所以它只需要HBITMAP句柄

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);
}

注意,在这个例子中,我假设 background.bmp 非常大,而 sprite.bmp 非常小。如果以相反的方式绘制,则背景将隐藏精灵。

【讨论】:

  • 工作,谢谢。不过,我不确定一旦我有了背景位图会怎样。
  • +1 用于在无效区域上进行 blit,而不是几乎其他人都这样做,即总是将整个位图爆破到绘图 dc 中。
  • @DenverP。如果有背景位图则删除FillRect,没有必要。改用LoadBitmap(background_bitmap, memdc, 0, 0); 然后调用LoadBitmap(filename, memdc, x, y);
  • @BarmakShemirani 谢谢,我正在实现背景画笔,我想知道为什么它不起作用。
  • @BarmakShemirani 我知道这已经过时了,但我需要一些帮助。我尝试做两个LoadBitmaps,第二个没有在重绘时执行。请帮忙。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-04-12
  • 2013-11-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多