2013-03-23 73 views
3

我正在创建一个游戏,其中在WM_PAINT消息期间完成了很多绘图。有几个不同的地方使窗口失效,迫使它重绘。我已将所有内容都绘制到离屏DC上,然后将其绘制到窗口 - 以创建非闪烁的“帧”。Win32 - 什么会导致不正确的绘图?

然而,每隔一段时间,一切突然开始被错误地绘制。在我使用的五个位图中,前三个保持正确绘制或多或少(但不完全)。如同所有,这三种颜色信息都是正确的。另外两幅是在这三幅作品之后绘制的,画的是错误的颜色 - 我认为白色仍然是白色的,但其他一切都被画成灰色。我不是说灰度,我的意思是除白色之外的所有东西都是相同的颜色 - 灰色。另外,当这种情况开始发生时,通常情况下一切都会被拉得太高 - 约20到30个像素。此外,字体和消息框停止工作 - 所有文本都以默认字体(但奇怪的是,正确的颜色)绘制,并且消息框仅短暂出现,没有文本,然后消失 - 但必须像常规一样被忽略必须按回车键,否则如果你点击主窗口,当输入信息框打开时,它会执行通常的操作 - 闪烁并且显示错误提示音)。所以,一切都被大肆渲染。

我一直在这个项目上工作了一段时间,最近我才开始看到这个错误 - 虽然我根本没有修改过下面的代码。测试可能会做什么真的很难,因为它似乎偶尔会发生一次。

下面是的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很新。

回答

7

您正在泄漏GDI对象。

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

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

使用GDI的标准模式是:

  1. 创建对象(例如,与CreateSolidBrush刷)。
  2. 使用SelectObject将您的对象选入设备上下文,并记住原始对象(返回值)。
  3. 做你的绘画或其他。
  4. 使用SelectObject将原始对象选回到设备上下文中。 (当然,这也可以从设备上下文中取消选择你的​​对象。)
  5. 使用DeleteObject删除你的对象。

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

然后请注意,步骤1和5不需要在每次喷漆操作中重复。您可以将它们分别移至程序初始化和终止。在程序终止时的清理可以被忽略,因为操作系统即将这样做。

最后,加载位图是一个适度缓慢的操作,所以你绝对不想重复这样做。这是GDI为对象提供句柄的要点。

+0

是。完全没有错误检查也是值得注意的。 –

+0

太棒了!这非常有帮助!我的印象是,你必须选择/删除的唯一东西是DC。那么你需要删除什么? –

+0

我认为你需要删除股票对象以外的所有GDI对象。这是您创建的任何位图,画笔,笔,字体,区域或调色板。任何函数的文档创建对象都应该清楚。 – arx

相关问题