我有两个缓冲区。显示在屏幕上的主缓冲区和绘制所有内容然后传递给主数据库的辅助缓冲区。
Graphics对象是从辅助缓冲区创建的,该缓冲区与大小为800x600的位图相关联。当您调整窗口大小时,位图的大小必须更改以防止剪切问题。辅助HDC更新,位图复制到主要。
问题在于与辅助HDC关联的Graphics对象中存在生成剪辑的内容。尽管已经更新到更大的值(1000x1000),但绘图区域仍然保持800x600。
我的问题是我应该在Graphics对象内更新什么(无需从现有的HDC中显式重新创建),以使其绘图区域适合位图大小。
我尝试的第一件事是从更新的HDC重新创建Graphics对象。此方法有效,绘图区域符合位图的大小。但它不符合设计标准。图形应该是可重用的。
我还尝试使用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);
图形对象应该是可重复使用的,每次刷新时都不应该抛弃它并用新的替换。因此,如果调整大小或更改任何内容,则必须相应地进行更新。我希望该区域符合辅助HDC中当前更新的位图的大小。如代码所示,位图可以正确更新。它不是Graphics对象。它的行为就像它仍然记得前一个BitMap中的某些值导致了这种行为。
windowHdc = GetDC(hWnd); HDC secondaryBuffer = CreateCompatibleDC(windowHdc); HBITMAP map = CreateCompatibleBitmap(windowHdc, width, height); SelectObject(secondaryBuffer, map); return secondaryBuffer;
如评论中所述,从HDC
或GetDC
获得的BeginPaint
不能重复使用。
但是,您可以重用内存位图(来自CreateCompatibleBitmap
)并重用内存直流(从CreateCompatibleDC
获得),尽管重用内存直流通常没有意义。
此外,需要进行清理以避免资源泄漏。当你完成ReleaseDC
时,请调用GetDC
。请参阅相关发布/删除功能的文档。
为简单的绘图程序执行此操作:
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
更新油漆。
对于双缓冲或在画布上绘图,您可以创建内存位图并重复使用它。如果窗口大小发生变化,则必须为旧位图调用DeleteObject
,并根据新大小创建新的位图。或者,您可以创建与最大窗口大小匹配的位图,然后将此位图用于所有窗口大小。
请参阅下面的示例了解双缓冲涂料(但在这种情况下不需要重复使用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
没有创造任何东西。它仅返回对系统使用的现有句柄的引用。完成此句柄后,将其释放。没有办法进一步优化这一点。
可以为您的程序创建其他GDI对象,如笔或位图,并且可以重复使用它们。创建/销毁笔只需要几纳秒,因此存储这些对象并保持跟踪通常不值得增加复杂性。
主要的瓶颈是在屏幕上绘制时,例如画线。您必须与显卡通信并与显示器通信,这需要一段时间。您可以使用双缓冲绘制位图,然后在屏幕上绘制BitBlt
。 BitBlt
也可能很慢,具体取决于系统。
对于动画,如果看到闪烁,请使用双缓冲。
为了获得更好的性能,您可以使用Direct2D
等新技术
如果动画仍然太慢,请考虑使用第二个线程来运行任何数学类型计算(第二个线程不应引用任何窗口句柄,例如来自HDC
或GetDC
的BeginPaint
)