3

我正在创建一个游戏,其中很多绘图是在 WM_PAINT 消息期间完成的。有几个不同的地方窗口无效,迫使它重绘。我将所有内容都绘制到屏幕外 DC,然后将其绘制到窗口 - 以创建不闪烁的“框架”。

然而,每隔一段时间,一切都会突然开始被错误地绘制。在我使用的五个位图中,前三个或多或少地(但不完全)正确地绘制。如中,所有颜色信息对于这三个都是正确的。在这三个之后绘制的另外两个用错误的颜色绘制 - 我认为白色仍然绘制为白色,但其他一切都绘制为灰色。而且我不是在说灰度,我的意思是除了白色之外的所有东西都是相同的颜色 - 灰色。

此外,当这种情况开始发生时,通常所有东西都被画得太高——大约 20 到 30 像素。此外,字体和消息框停止工作 - 所有文本都以默认字体绘制(但奇怪的是,正确的颜色),消息框仅短暂出现,没有文本,然后消失 - 但必须像常规一样关闭(你必须按 Enter 键,否则如果您单击主窗口,它会在打开消息框接受输入时执行通常的操作 - 闪烁并发出错误提示音)。所以一切都被王室搞砸了。

我已经在这个项目上工作了一段时间,而且我最近才开始看到这个错误——尽管我根本没有修改下面的代码。真的很难测试什么可以做到这一点,因为它似乎只是偶尔发生一次。

这是我在 WndProc 中的 WM_CREATE 和 WM_PAINT 中的代码:

case WM_CREATE:
    {
        hdc = GetDC(hWnd);
        hdcmem = CreateCompatibleDC(hdc);
        RECT rc;
        GetClientRect(hWnd, &rc);
        hdcbm = CreateCompatibleBitmap(hdc, rc.right, rc.bottom);
        hbcmem = CreateCompatibleDC(hdcmem);
        hdcbmold = (HBITMAP)SelectObject(hdcmem, hdcbm);

        // Load bitmaps
        bg = LoadBitmap(hInst, MAKEINTRESOURCE(IDB_BACKGROUND));
        mainCont = LoadBitmap(hInst, MAKEINTRESOURCE(IDB_GAME_CONT));
        if(bg == NULL || mainCont == NULL)
            ThrowError("A bitmap failed to load.");
    }
    break;
case WM_PAINT:
{
    PAINTSTRUCT ps;
    BeginPaint(hWnd, &ps);

    // Background
    hdcold = (HBITMAP)SelectObject(hbcmem, bg);
    BitBlt(hdcmem, 0, 0, 237, 196, hbcmem, 0, 0, SRCCOPY);
    BitBlt(hdcmem, 237, 0, 237, 196, hbcmem, 0, 0, SRCCOPY);
    BitBlt(hdcmem, 237 * 2, 0, 237, 196, hbcmem, 0, 0, SRCCOPY);
    BitBlt(hdcmem, 0, 196, 237, 196, hbcmem, 0, 0, SRCCOPY);
    BitBlt(hdcmem, 237, 196, 237, 196, hbcmem, 0, 0, SRCCOPY);
    BitBlt(hdcmem, 237 * 2, 196, 237, 196, hbcmem, 0, 0, SRCCOPY);

    // Main Game Container
    hdcold = (HBITMAP)SelectObject(hbcmem, mainCont);
    BitBlt(hdcmem, 26, 26, 300, 300, hbcmem, 0, 0, SRCCOPY);

    // Side Info
    HBITMAP side = LoadBitmap(hInst, MAKEINTRESOURCE(IDB_SIDEINFO));
    hdcold = (HBITMAP)SelectObject(hbcmem, side);
    BitBlt(hdcmem, 339, 26, 154, 300, hbcmem, 0, 0, SRCCOPY);
    DrawLevelNumber(game.map.levelnumber);

    if (color)
        sprites = LoadBitmap(hInst, MAKEINTRESOURCE(IDB_COLOR_SPRITES));
    else sprites = LoadBitmap(hInst, MAKEINTRESOURCE(IDB_BLACKWHITE_SPRITES));
    hdcold = (HBITMAP)SelectObject(hbcmem, sprites);

    // Find x and y coordinate for the top left of the visible screen
    int x = game.player.x, y = game.player.y, ypos = 0;
    if (x < 4)  x = 4;
    if (x > 27) x = 27;
    if (y < 4)  y = 4;
    if (y > 27) y = 27;
    x -= 4;
    y -= 4;

    // Draw lower layer
    for (int i = 0; i < 9; i++)
    {
        for (int j = 0; j < 9; j++)
        {
            if (game.map.Layer_Two[x + i][y + j] != 0)
            {
                int xpos = game.get_pos(game.map.Layer_Two[x + i][y + j].get(), ypos, false);
                BitBlt(hdcmem, (i * 32) + 32, (j * 32) + 32, 32, 32, hbcmem, xpos, ypos, SRCCOPY);
            }
        }
    }

    // Draw upper layer
    for (int i = 0; i < 9; i++)
    {
        for (int j = 0; j < 9; j++)
        {
            if ((game.map.Layer_Two[x + i][y + j] != 0 && game.map.Layer_One[x + i][y + j] >= 64 && game.map.Layer_One[x + i][y + j] <= 111))
            {
                int xpos = game.get_pos(game.map.Layer_One[x + i][y + j].get(), ypos, true);
                BitBlt(hdcmem, (i * 32) + 32, (j * 32) + 32, 32, 32, hbcmem, xpos + 96, ypos, SRCPAINT);
                BitBlt(hdcmem, (i * 32) + 32, (j * 32) + 32, 32, 32, hbcmem, xpos, ypos, SRCAND);
            } else {
                int xpos = game.get_pos(game.map.Layer_One[x + i][y + j].get(), ypos, false);
                BitBlt(hdcmem, (i * 32) + 32, (j * 32) + 32, 32, 32, hbcmem, xpos, ypos, SRCCOPY);
            }
        }
    }

    // If it isn't started, show title
    if (!game.started)
    {

        HDC tmphdc = CreateCompatibleDC(hdcmem);
        HDC tmp = CreateCompatibleDC(tmphdc);
        RECT rc;
        GetClientRect(hWnd, &rc);
        string str = game.map.leveltitle.substr(0, game.map.leveltitle.length() - 1);
        TCHAR* tch = new TCHAR[str.length()];
        mbstowcs_s(NULL, tch, _tcslen(tch), str.c_str(), str.length());
        HFONT font = CreateFont(25, 0, 0, 0, FW_BOLD, false, false, false, ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, FF_DONTCARE, NULL);
        SelectObject(tmp, font);
        DrawText(tmp, tch, str.length(), &rc, DT_CALCRECT);
        rc.right += 16;
        HBITMAP tmpbm = CreateCompatibleBitmap(hdcmem, rc.right, rc.bottom);
        HBITMAP tmpold = (HBITMAP)SelectObject(tmphdc, tmpbm);

        HBRUSH hbr = CreateSolidBrush(RGB(255, 255, 255));
        HPEN pen = CreatePen(PS_SOLID, 1, RGB(255, 255, 255));
        SelectObject(hdcmem, pen);
        SelectObject(hdcmem, hbr);
        Rectangle(hdcmem, 176 - (rc.right / 2), 243, 177 + (rc.right / 2), 248);
        hbr = CreateSolidBrush(RGB(128, 128, 128));
        pen = CreatePen(PS_SOLID, 1, RGB(128, 128, 128));
        SelectObject(hdcmem, pen);
        SelectObject(hdcmem, hbr);
        Rectangle(hdcmem, 176 - (rc.right / 2), 294, 177 + (rc.right / 2), 299);

        HBITMAP left = LoadBitmap(hInst, MAKEINTRESOURCE(IDB_LEFT));
        hdcold = (HBITMAP)SelectObject(hbcmem, left);
        BitBlt(hdcmem, 176 - (rc.right / 2) - 4, 243, 4, 56, hbcmem, 0, 0, SRCCOPY);
        HBITMAP right = LoadBitmap(hInst, MAKEINTRESOURCE(IDB_RIGHT));
        hdcold = (HBITMAP)SelectObject(hbcmem, right);
        BitBlt(hdcmem, 176 + (rc.right / 2) + ((rc.right % 2) != 0), 243, 4, 56, hbcmem, 0, 0, SRCCOPY);

        SelectObject(tmphdc, font);
        SetTextColor(tmphdc, RGB(255, 255, 0));
        SetBkColor(tmphdc, RGB(0, 0, 0));
        DrawText(tmphdc, tch, str.length(), &rc, DT_CENTER);
        BITMAP structBitmapHeader;
        memset( &structBitmapHeader, 0, sizeof(BITMAP) );
        HGDIOBJ hBitmap = GetCurrentObject(tmphdc, OBJ_BITMAP);
        GetObject(hBitmap, sizeof(BITMAP), &structBitmapHeader);
        BitBlt(hdcmem, 176 - (rc.right / 2), 247, structBitmapHeader.bmWidth, structBitmapHeader.bmHeight, tmphdc, 0, 0, SRCCOPY);

        DeleteDC(tmphdc);
        DeleteDC(tmp);
    }

    // If paused
    if (game.paused)
    {
        RECT rc;
        rc.top = 32;
        rc.left = 32;
        rc.bottom = 330;
        rc.right = 330;
        BitBlt(hdcmem, 32, 32, 288, 288, NULL, 0, 0, BLACKNESS);
        HFONT font = CreateFont(50, 0, 0, 0, FW_NORMAL, false, false, false, ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, FF_DONTCARE, NULL);
        SelectObject(hdcmem, font);
        SetTextColor(hdcmem, RGB(255, 0, 0));
        SetBkColor(hdcmem, RGB(0, 0, 0));
        DrawText(hdcmem, L"PAUSED", 6, &rc, (DT_CENTER + DT_SINGLELINE + DT_VCENTER));
    }

    nums = LoadBitmap(hInst, MAKEINTRESOURCE(IDB_NUMBERS));
    hdcold = (HBITMAP)SelectObject(hbcmem, nums);
    for (int i = 100; i > 0; i /= 10) // coins and time left
    {
        int tmp;
        if (i == 100)
            tmp = game.coinsleft / 100;
        if (i == 10)
            tmp = ((game.coinsleft % 100) - (game.coinsleft % 10)) / 10;
        if (i == 1)
            tmp = game.coinsleft % 10;
        if (game.coinsleft < i && i > 1)
            tmp = 10;
        int ypos = game.get_num_pos(tmp, (game.coinsleft == 0));
        BitBlt(hdcmem, 417 + ((3 - (int)floor(log10((double)i)) * 17)), 215, 17, 23, hbcmem, 0, ypos, SRCCOPY);

        if (i == 100)
            tmp = game.timeleft / 100;
        if (i == 10)
            tmp = ((game.timeleft % 100) - (game.timeleft % 10)) / 10;
        if (i == 1)
            tmp = game.timeleft % 10;
        if (game.timeleft < i && i > 1)
            tmp = 10;
        if (game.map.timelimit == 0)
            tmp = 11;
        ypos = game.get_num_pos(tmp, (game.timeleft < 16 || game.map.timelimit == 0));
        BitBlt(hdcmem, 369 + ((3 - (int)floor(log10((double)i))) * 17), 125, 17, 23, hbcmem, 0, ypos, SRCCOPY);
    }

    if (game.onhint)
    {
        HBITMAP sidebg = LoadBitmap(hInst, MAKEINTRESOURCE(IDB_SIDEBG));
        hdcold = (HBITMAP)SelectObject(hbcmem, sidebg);
        BitBlt(hdcmem, 353, 165, 127, 146, hbcmem, 0, 0, SRCCOPY);
        HFONT font = CreateFont(18, 0, 0, 0, FW_BOLD, true, false, false, ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, FF_DONTCARE, NULL);
        SelectObject(hdcmem, font);
        SetTextColor(hdcmem, RGB(0, 255, 255));
        SetBkColor(hdcmem, RGB(0, 0, 0));
        RECT rc;
        rc.top = 168;
        rc.left = 356;
        rc.bottom = 308;
        rc.right = 477;
        string str = "Hint: " + game.map.hint;
        TCHAR* tch = new TCHAR[str.length()];
        mbstowcs_s(NULL, tch, _tcslen(tch), str.c_str(), str.length());
        DrawText(hdcmem, tch, str.length(), &rc, DT_CENTER + DT_WORDBREAK);
    } else {
        hdcold = (HBITMAP)SelectObject(hbcmem, sprites);                // LOWER SIDE INFO
        if (game.player.key1 > 0)
            BitBlt(hdcmem, 352, 247, 32, 32, hbcmem, 192, 160, SRCCOPY);
        else BitBlt(hdcmem, 352, 247, 32, 32, hbcmem, 0, 0, SRCCOPY);
        if (game.player.key2 > 0)
            BitBlt(hdcmem, 384, 247, 32, 32, hbcmem, 192, 128, SRCCOPY);
        else BitBlt(hdcmem, 384, 247, 32, 32, hbcmem, 0, 0, SRCCOPY);
        if (game.player.key3 > 0)
            BitBlt(hdcmem, 416, 247, 32, 32, hbcmem, 192, 224, SRCCOPY);
        else BitBlt(hdcmem, 416, 247, 32, 32, hbcmem, 0, 0, SRCCOPY);
        if (game.player.key4)
            BitBlt(hdcmem, 448, 247, 32, 32, hbcmem, 192, 192, SRCCOPY);
        else BitBlt(hdcmem, 448, 247, 32, 32, hbcmem, 0, 0, SRCCOPY);
        if (game.player.mod1)
            BitBlt(hdcmem, 352, 279, 32, 32, hbcmem, 192, 256, SRCCOPY);
        else BitBlt(hdcmem, 352, 279, 32, 32, hbcmem, 0, 0, SRCCOPY);
        if (game.player.mod2)
            BitBlt(hdcmem, 384, 279, 32, 32, hbcmem, 192, 288, SRCCOPY);
        else BitBlt(hdcmem, 384, 279, 32, 32, hbcmem, 0, 0, SRCCOPY);
        if (game.player.mod3)
            BitBlt(hdcmem, 416, 279, 32, 32, hbcmem, 192, 320, SRCCOPY);
        else BitBlt(hdcmem, 416, 279, 32, 32, hbcmem, 0, 0, SRCCOPY);
        if (game.player.mod4)
            BitBlt(hdcmem, 448, 279, 32, 32, hbcmem, 192, 352, SRCCOPY);
        else BitBlt(hdcmem, 448, 279, 32, 32, hbcmem, 0, 0, SRCCOPY);
    }
    BitBlt(hdc, 0, 0, 518, 401, hdcmem, 0, 0, SRCCOPY);

    EndPaint(hWnd, &ps);
}
    break;
case WM_DESTROY:
    SelectObject(hdc,hdcold);
    DeleteDC(hdcmem);
    DeleteDC(hbcmem);
    ReleaseDC(hWnd, hdc);
    DeleteObject(bg);
    PostQuitMessage(0);
    break;

我也非常感谢任何人就我的代码中的语法、效率和/或更好的做事方式提供的任何建议/批评。我对 Win32 很陌生。

4

1 回答 1

9

您正在泄漏 GDI 对象。

每次创建画笔或加载位图时,您都在创建 GDI 对象。Windows 限制了您可以创建的 GDI 对象的数量。如果您重复创建对象而不删除它们,您将达到限制并且创建更多对象将失败。发生这种情况时,您的显示开始出现错误 - 您看到错误的颜色、默认字体等。

任务管理器将向您显示一个进程已分配的 GDI 对象的数量(在“进程”选项卡中,转到“查看”|“选择列”)。该值可能会略有波动,但不应随着时间的推移而增长。

使用 GDI 的标准模式是:

  1. 创建您的对象(例如,使用 CreateSolidBrush 的画笔)。
  2. 使用 SelectObject 将您的对象选择到设备上下文中,并记住原始对象(返回值)。
  3. 做你的画什么的。
  4. 使用 SelectObject 将原始对象选择回设备上下文。(当然,这也会从设备上下文中取消选择您的对象。)
  5. 使用 DeleteObject 删除您的对象。

您只执行了第 2 步的一半,并跳过了第 4 步和第 5 步。

然后注意步骤 1 和 5 不需要在每次绘制操作中重复。您可以将它们分别移动到程序初始化和终止。并且可以跳过程序终止时的清理,因为操作系统无论如何都会这样做。

最后,加载位图是一个比较慢的操作,所以你绝对不想重复这样做。这就是 GDI 为对象提供句柄的意义所在。

于 2013-03-24T11:32:30.033 回答