有没有办法在更改与 HDC 关联的位图大小后更新 Graphics 对象?
Is there a way to update Graphics object after changing size of bitmap assosiated with HDC?
1.问题
我有两个缓冲区。主缓冲区,显示在屏幕上,辅助缓冲区在其中绘制所有内容,然后传递给主缓冲区。
Graphics 对象是从辅助缓冲区创建的,该缓冲区与大小为 800x600 的位图相关联。当然,当您调整 window 大小时,位图的大小也必须更改,以防止出现裁剪问题。
辅助 HDC 得到更新,位图被复制到主 HDC。
问题是与生成剪裁的次级 HDC 关联的图形对象中遗留了某些内容。尽管已经更新为更大的区域 (1000x1000),但绘图区域仍保持 800x600。
我的问题是我应该在 Graphics 对象内部更新什么(不必从现有 HDC 显式地重新创建它)以使其绘图区域适合位图大小。
2。我尝试了什么
我尝试的第一件事是从更新的 HDC 重新创建图形对象。此方法有效并且绘图区域适合位图的大小。然而,它不符合设计标准。图形应该是可重复使用的。
我还尝试使用 SetClip 方法更新图形对象的剪辑区域。虽然这似乎不是问题所在。
这就是我创建缓冲区的方式。
HDC CoreWindowFrame::InitPaint(HWND hWnd)
{
windowHdc = GetDC(hWnd);
HDC secondaryBuffer = CreateCompatibleDC(windowHdc);
HBITMAP map = CreateCompatibleBitmap(windowHdc, width, height);
SelectObject(secondaryBuffer, map);
return secondaryBuffer;
}
调整大小时调用此函数
void CoreWindowFrame::UpdateBitmap(int width, int height)
{
HBITMAP map = CreateCompatibleBitmap(windowHdc, width, height);
SelectObject(secondaryBuffer, map);
}
这是消息处理:
case WM_SIZE:
ConsoleWrite("WM_SIZE RECIEVED");
width = *((unsigned short*)&lParam);
height = ((unsigned short*)&lParam)[1];
UpdateBitmap(width, height);
break;
case WM_PAINT:
ConsoleWrite("WM_PAINT RECIEVE");
windowGraphics->Clear(Color(255, 255, 255));
Pen* testPen = new Pen(Color(255, 0, 0), 1.0F);
windowGraphics->DrawLine(testPen, 0, 0, calls*3, 100);
BitBlt(windowHdc, 0, 0, updatedWidth, updatedHeight, secondaryBuffer, 0, 0, MERGECOPY);
3。预期结果
图形对象应该是可重复使用的,不应在每次刷新时将其丢弃并替换为新对象。因此,它必须相应地更新,以防任何东西被调整大小或改变。我希望该区域适合辅助 HDC 中当前更新的位图的大小。如代码中所示,位图已正确更新。 Graphics 对象没有。它的行为就像它仍然记得导致此行为的先前 BitMap 中的一些值。
windowHdc = GetDC(hWnd);
HDC secondaryBuffer = CreateCompatibleDC(windowHdc);
HBITMAP map = CreateCompatibleBitmap(windowHdc, width, height);
SelectObject(secondaryBuffer, map);
return secondaryBuffer;
从 GetDC
或 BeginPaint
获得的 HDC
不能重复使用,如注释中所述。
然而,您可以重用内存位图(来自 CreateCompatibleBitmap
)和重用内存 dc(从 CreateCompatibleDC
获得),尽管重用内存 dc 通常没有意义。
此外,需要进行清理以避免资源泄漏。完成 GetDC
后调用 ReleaseDC
。请参阅相关 release/delete 函数的文档。
为简单的绘画例程执行此操作:
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
Gdiplus::Graphics gr(hdc);
Gdiplus::Pen testPen(Gdiplus::Color(255, 0, 0), 1.0F);
gr.Clear(Gdiplus::Color::White);
gr.DrawLine(&testPen, 0, 0, 100, 100);
EndPaint(hwnd, &ps);
return 0;
}
通常你不需要做更多的事情。只需调用 InvalidateRect
响应 WM_SIZE
即可更新绘画。
对于双缓冲,或在 canvas 上绘图,您可以创建一个内存位图并重新使用它。如果 window 大小发生变化,则必须为旧位图调用 DeleteObject
,并根据新大小创建新位图。或者您可以创建一个匹配最大 window 大小的位图,然后将此位图用于所有 window 大小。
请参阅下面的双缓冲绘制示例(但在这种情况下不需要重复使用 hbitmap
)
HBITMAP hbitmap = NULL;
void InitPaint(HWND hwnd)
{
HDC hdc = GetDC(hwnd);
//create a bitmap with max size:
int w = GetSystemMetrics(SM_CXFULLSCREEN);
int h = GetSystemMetrics(SM_CYFULLSCREEN);
hbitmap = CreateCompatibleBitmap(hdc, w, h);
ReleaseDC(hwnd, hdc);
}
void cleanup()
{
if (hbitmap)
DeleteObject(hbitmap);
}
...
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
RECT rc; GetClientRect(hwnd, &rc);
int w = rc.right;
int h = rc.bottom;
HDC memdc = CreateCompatibleDC(hdc);
HBITMAP oldbmp = (HBITMAP)SelectObject(memdc, hbitmap);
Gdiplus::Graphics gr(memdc);
Gdiplus::Pen testPen(Gdiplus::Color(255, 0, 0), 1.0F);
gr.Clear(Gdiplus::Color(255, 255, 255));
gr.DrawLine(&testPen, 0, 0, w, h);
BitBlt(hdc, 0, 0, w, h, memdc, 0, 0, SRCCOPY);
//cleanup:
SelectObject(memdc, oldbmp);
DeleteDC(memdc);
EndPaint(hwnd, &ps);
return 0;
}
摘要:
GetDC
不创建任何东西。它只是 returns 对系统使用的现有句柄的引用。完成此手柄后,松开它。没有办法进一步优化它。
可以为您的程序创建其他 GDI 对象,例如画笔或位图,并且可以重复使用它们。 create/destroy 一支笔只需要几纳秒,因此通常不值得为存储这些对象和跟踪而增加复杂性。
主要瓶颈是在屏幕上绘图时,例如画一条线。你必须与显卡和显示器对话,这需要一段时间。您可以使用双缓冲在位图上绘制,然后 BitBlt
在屏幕上绘制。 BitBlt
也可能很慢,具体取决于系统。
对于动画,如果您看到闪烁,请使用双缓冲。
为了获得更好的性能,您可以使用更新的技术,例如 Direct2D
如果动画仍然太慢,请考虑使用第二个线程来 运行 任何数学类型的计算(第二个线程不应引用任何 window 句柄,例如来自 HDC
GetDC
或 BeginPaint
)
1.问题
我有两个缓冲区。主缓冲区,显示在屏幕上,辅助缓冲区在其中绘制所有内容,然后传递给主缓冲区。
Graphics 对象是从辅助缓冲区创建的,该缓冲区与大小为 800x600 的位图相关联。当然,当您调整 window 大小时,位图的大小也必须更改,以防止出现裁剪问题。 辅助 HDC 得到更新,位图被复制到主 HDC。
问题是与生成剪裁的次级 HDC 关联的图形对象中遗留了某些内容。尽管已经更新为更大的区域 (1000x1000),但绘图区域仍保持 800x600。
我的问题是我应该在 Graphics 对象内部更新什么(不必从现有 HDC 显式地重新创建它)以使其绘图区域适合位图大小。
2。我尝试了什么
我尝试的第一件事是从更新的 HDC 重新创建图形对象。此方法有效并且绘图区域适合位图的大小。然而,它不符合设计标准。图形应该是可重复使用的。
我还尝试使用 SetClip 方法更新图形对象的剪辑区域。虽然这似乎不是问题所在。
这就是我创建缓冲区的方式。
HDC CoreWindowFrame::InitPaint(HWND hWnd)
{
windowHdc = GetDC(hWnd);
HDC secondaryBuffer = CreateCompatibleDC(windowHdc);
HBITMAP map = CreateCompatibleBitmap(windowHdc, width, height);
SelectObject(secondaryBuffer, map);
return secondaryBuffer;
}
调整大小时调用此函数
void CoreWindowFrame::UpdateBitmap(int width, int height)
{
HBITMAP map = CreateCompatibleBitmap(windowHdc, width, height);
SelectObject(secondaryBuffer, map);
}
这是消息处理:
case WM_SIZE:
ConsoleWrite("WM_SIZE RECIEVED");
width = *((unsigned short*)&lParam);
height = ((unsigned short*)&lParam)[1];
UpdateBitmap(width, height);
break;
case WM_PAINT:
ConsoleWrite("WM_PAINT RECIEVE");
windowGraphics->Clear(Color(255, 255, 255));
Pen* testPen = new Pen(Color(255, 0, 0), 1.0F);
windowGraphics->DrawLine(testPen, 0, 0, calls*3, 100);
BitBlt(windowHdc, 0, 0, updatedWidth, updatedHeight, secondaryBuffer, 0, 0, MERGECOPY);
3。预期结果
图形对象应该是可重复使用的,不应在每次刷新时将其丢弃并替换为新对象。因此,它必须相应地更新,以防任何东西被调整大小或改变。我希望该区域适合辅助 HDC 中当前更新的位图的大小。如代码中所示,位图已正确更新。 Graphics 对象没有。它的行为就像它仍然记得导致此行为的先前 BitMap 中的一些值。
从windowHdc = GetDC(hWnd); HDC secondaryBuffer = CreateCompatibleDC(windowHdc); HBITMAP map = CreateCompatibleBitmap(windowHdc, width, height); SelectObject(secondaryBuffer, map); return secondaryBuffer;
GetDC
或 BeginPaint
获得的 HDC
不能重复使用,如注释中所述。
然而,您可以重用内存位图(来自 CreateCompatibleBitmap
)和重用内存 dc(从 CreateCompatibleDC
获得),尽管重用内存 dc 通常没有意义。
此外,需要进行清理以避免资源泄漏。完成 GetDC
后调用 ReleaseDC
。请参阅相关 release/delete 函数的文档。
为简单的绘画例程执行此操作:
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
Gdiplus::Graphics gr(hdc);
Gdiplus::Pen testPen(Gdiplus::Color(255, 0, 0), 1.0F);
gr.Clear(Gdiplus::Color::White);
gr.DrawLine(&testPen, 0, 0, 100, 100);
EndPaint(hwnd, &ps);
return 0;
}
通常你不需要做更多的事情。只需调用 InvalidateRect
响应 WM_SIZE
即可更新绘画。
对于双缓冲,或在 canvas 上绘图,您可以创建一个内存位图并重新使用它。如果 window 大小发生变化,则必须为旧位图调用 DeleteObject
,并根据新大小创建新位图。或者您可以创建一个匹配最大 window 大小的位图,然后将此位图用于所有 window 大小。
请参阅下面的双缓冲绘制示例(但在这种情况下不需要重复使用 hbitmap
)
HBITMAP hbitmap = NULL;
void InitPaint(HWND hwnd)
{
HDC hdc = GetDC(hwnd);
//create a bitmap with max size:
int w = GetSystemMetrics(SM_CXFULLSCREEN);
int h = GetSystemMetrics(SM_CYFULLSCREEN);
hbitmap = CreateCompatibleBitmap(hdc, w, h);
ReleaseDC(hwnd, hdc);
}
void cleanup()
{
if (hbitmap)
DeleteObject(hbitmap);
}
...
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
RECT rc; GetClientRect(hwnd, &rc);
int w = rc.right;
int h = rc.bottom;
HDC memdc = CreateCompatibleDC(hdc);
HBITMAP oldbmp = (HBITMAP)SelectObject(memdc, hbitmap);
Gdiplus::Graphics gr(memdc);
Gdiplus::Pen testPen(Gdiplus::Color(255, 0, 0), 1.0F);
gr.Clear(Gdiplus::Color(255, 255, 255));
gr.DrawLine(&testPen, 0, 0, w, h);
BitBlt(hdc, 0, 0, w, h, memdc, 0, 0, SRCCOPY);
//cleanup:
SelectObject(memdc, oldbmp);
DeleteDC(memdc);
EndPaint(hwnd, &ps);
return 0;
}
摘要:
GetDC
不创建任何东西。它只是 returns 对系统使用的现有句柄的引用。完成此手柄后,松开它。没有办法进一步优化它。
可以为您的程序创建其他 GDI 对象,例如画笔或位图,并且可以重复使用它们。 create/destroy 一支笔只需要几纳秒,因此通常不值得为存储这些对象和跟踪而增加复杂性。
主要瓶颈是在屏幕上绘图时,例如画一条线。你必须与显卡和显示器对话,这需要一段时间。您可以使用双缓冲在位图上绘制,然后 BitBlt
在屏幕上绘制。 BitBlt
也可能很慢,具体取决于系统。
对于动画,如果您看到闪烁,请使用双缓冲。
为了获得更好的性能,您可以使用更新的技术,例如 Direct2D
如果动画仍然太慢,请考虑使用第二个线程来 运行 任何数学类型的计算(第二个线程不应引用任何 window 句柄,例如来自 HDC
GetDC
或 BeginPaint
)