调整窗口大小会导致位图被覆盖

Posted

技术标签:

【中文标题】调整窗口大小会导致位图被覆盖【英文标题】:Resizing the window results in bitmaps being overwritten 【发布时间】:2020-02-02 07:42:20 【问题描述】:

我一直在用 Visual C++ 处理位图和渲染,遇到了一个奇怪的问题。

我正在尝试在用户调整窗口大小时调整绘图区域的大小。现在,不仅扩大窗口不会扩大可见的绘图区域(白色边框出现在原始窗口边界之外),而且改变窗口大小似乎弄乱了我的位图。

这是游戏在启动时的样子:

更改窗口的宽度或高度后,紫色字符现在已被橙色字符替换。

最后,三个都变成蓝色了。

如果我继续下去,所有的字符都会被绿草背景位图覆盖。

位图的渲染顺序似乎与此相反。添加第二行字符确认这在渲染代码中没有问题 - 内存中的实际位图正在以某种方式发生变化。删除调整大小代码会导致位图错误停止重现。

当我尝试重新初始化设备上下文时,我的调整大小代码几乎可以肯定是一个错误,但我不确定它是什么。

这是相关的设置代码:

HBITMAP player_blue = (HBITMAP) LoadImage (NULL, L"Images/test_player_blue.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
HBITMAP player_orange = (HBITMAP) LoadImage (NULL, L"Images/test_player_orange.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
HBITMAP player_purple = (HBITMAP) LoadImage (NULL, L"Images/test_player_purple.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
HBITMAP background_bitmap = (HBITMAP) LoadImage (NULL, L"Images/test_background.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);

  HDC hdc = GetDC (Window);
  HDC hdc_temp = CreateCompatibleDC (hdc);
  HBITMAP bitmap_buffer = CreateCompatibleBitmap (hdc, resize.screen_width, resize.screen_height);
  SelectObject (hdc, bitmap_buffer);

来自 wWinMain() 的主循环:

  while (!Exit)
    
    while (PeekMessage (&msg, nullptr, 0, 0, PM_REMOVE))
      
      DispatchMessage(&msg);
      
    Render ();
    

调整大小的回调函数:

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
  
  switch (message)
    
    case WM_SIZE:

      RECT window_rect;
      if (GetWindowRect (Window, &window_rect))
        
        hdc = GetDC (Window);
        hdc_temp = CreateCompatibleDC (hdc);
        bitmap_buffer = CreateCompatibleBitmap (hdc, resize.screen_width, resize.screen_height);
        SelectObject (hdc, bitmap_buffer);

        screen_width = window_rect.right - window_rect.left;
        screen_height = window_rect.bottom - window_rect.top;
        
      break;

    default:
      return DefWindowProc(hWnd, message, wParam, lParam);
    
  return 0;
  

通过BitBlt()实际渲染:

void Render ()
  
  SelectObject (hdc_temp, background_bitmap);
  BitBlt (hdc, 0, 0, 800, 600, hdc_temp, 0, 0, SRCCOPY);

  SelectObject (hdc_temp, player_blue);
  BitBlt (hdc, 0, 0, 126, 126, hdc_temp, 0, 0, SRCCOPY);
  SelectObject (hdc_temp, player_orange);
  BitBlt (hdc, 126, 0, 126, 126, hdc_temp, 0, 0, SRCCOPY);
  SelectObject (hdc_temp, player_purple);
  BitBlt (hdc, 252, 0, 126, 126, hdc_temp, 0, 0, SRCCOPY);
  

【问题讨论】:

好久没用windows API了,难道你不需要把原来的句柄恢复到hdc_temp吗?例如:Render() HGDIOBJ h = SelectObject (hdc_temp, background_bitmap); /*existing code*/ SelectObject(hdc_temp, h); 【参考方案1】:

正如 Ken 所说,您正在泄漏宝贵的 GDI 资源。再调整一些大小,您的窗口可能会变黑:)

请看https://docs.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-createcompatibledc:

When you no longer need the memory DC, call the DeleteDC function.您可能不需要在每次调整大小时都创建该 DC。

CreateCompatibleBitmap 也是如此。

【讨论】:

谢谢 - 看起来那是罪魁祸首。关于如何正确调整绘图区域大小的任何见解? @NightmareGames - 这取决于你想做什么:只是调整背景大小,还是缩放你的游戏片段?无论如何,在处理WM_SIZE 消息时,您只需要像您已经做的那样获取新的大小(并且可能准备好 DC)。确保在将新的 GDI 资源分配给现有句柄时,首先清除以前的内容。另请注意,您不能删除当前选择到任何 DC 中的资源,因此良好的做法是(正如 Ken 评论的那样)在退出 Render() 时保存先前选择的对象。另一种选择是使用SaveDC()RestoreDC()

以上是关于调整窗口大小会导致位图被覆盖的主要内容,如果未能解决你的问题,请参考以下文章

调整窗口大小时位图消失

MFC 网格是绝对布局,在调整窗口大小时会被剪裁

调整 OpenGL 窗口的大小会导致它崩溃

当 QML 滚动条位于底部并且窗口高度被调整大小时,它不会更新

调整窗口大小后交换渲染缓冲区会导致问题

窗口大小更改导致我的条形图覆盖 - Matplotlib