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