我想在WM_PAINT消息处理程序中使用以下代码绘制很多行。
//DrawLine with double buffering
LRESULT CALLBACK CMyDoc::OnPaint(HWND hWnd, WPARAM wParam, LPARAM lParam)
{
std::vector<Gdiplus::Point> points;
std::vector<Gdiplus::Point>::iterator iter1, iter2;
HDC hdc, hdcMem;
HBITMAP hbmScreen, hbmOldBitmap;
PAINTSTRUCT ps;
RECT rect;
hdc = BeginPaint(hWnd, &ps);
//Create memory dc
hdcMem = CreateCompatibleDC(hdc);
GetClientRect(hWnd, &rect);
hbmScreen = CreateCompatibleBitmap(hdc, rect.right, rect.bottom);
hbmOldBitmap = (HBITMAP)SelectObject(hdcMem, hbmScreen);
//Fill the rect with white
FillRect(hdcMem, &rect, (HBRUSH)GetStockObject(WHITE_BRUSH));
//Draw the lines
Gdiplus::Graphics graphics(hdcMem);
Gdiplus::Pen blackPen(Gdiplus::Color(255, 0, 0));
points = m_pPolyLine->GetPoints();
for (iter1 = points.begin(); iter1 != points.end(); iter1++) {
for (iter2 = iter1 + 1; iter2 != points.end(); iter2++)
graphics.DrawLine(&blackPen, *iter1, *iter2);
}
//Copy the bitmap from memory dc to the real dc
BitBlt(hdc, 0, 0, rect.right, rect.bottom, hdcMem, 0, 0, SRCCOPY);
//Clean up
SelectObject(hdcMem, hbmOldBitmap);
DeleteObject(hbmScreen);
DeleteDC(hdcMem);
EndPaint(hWnd, &ps);
return 0;
}
但是,如果点的大小超过20,则客户端矩形只会闪烁。我认为原因是Gdiplus :: DrawLines太慢了。
有没有解决闪烁问题的方法? 感谢。
答案 0 :(得分:4)
闪烁可能是由于慢速绘画以及其他因素造成的。通常,请尝试/确保以下内容:
尝试不依赖WM_ERASEBKGND
消息,即返回非零,或者如果可能,请在NULL
中指定WNDCLASS::hbrBackground
。通常,绘画方法会绘制脏区域的所有背景,然后不需要进行擦除。
如果你需要擦除,它通常可以进行优化,以便WM_ERASEBKGND
返回非零值,然后paint方法通过绘制未被常规绘制内容覆盖的区域来确保“擦除” ,如果设置了PAINTSTRUCT::fErase
。
如果可行,请编写paint方法,使其在一次调用中不重绘相同的像素。例如。要使用红色边框制作蓝色矩形,请不要FillRect(red)
,然后使用FillRect(blue)
重新绘制其内部部分。尝试尽可能多地绘制每个像素一次。
对于复杂的控件/窗口,通常可以优化paint方法,通过正确组织控件数据,轻松跳过脏矩形(PAINTSTRUCT::rcPaint
)之外的大量绘画。
更改控件状态时,仅使控件所需的最小区域无效。
如果它不是顶级窗口,请考虑使用CS_PARENTDC
。如果您的绘制方法不依赖于系统设置剪切矩形到控件的客户端矩形,则此类样式将导致更好的性能。
如果您看到控件/窗口调整大小的闪烁,请考虑不使用CS_HREDRAW
和CS_VREDRAW
。而是手动使WM_SIZE
中的控件的相关部分无效。这通常只允许使控件的较小部分无效。
如果您看到控件滚动时出现闪烁,请不要使整个客户端无效,但请使用ScrollWindow()
并仅使展示新(滚动内容)内容的小区域无效。
如果上述所有内容都失败,请使用双缓冲。
答案 1 :(得分:3)
使用双缓冲区。这是Win32 C ++应用程序的一个棘手问题,特别是OnPaint函数和DC工具。
以下是一些链接,可帮助您检查双缓冲区的实现是否一切正常:Flicker Free Drawing In MFC和SO问题“Reduce flicker with GDI+ and C++”
答案 2 :(得分:0)
如果你的线条恰好超出DC(图形)的范围,那么Win32 / GDI +在裁剪时会非常缓慢。比如,滚动自己的剪辑功能要慢两个数量级。下面是一些实现Liang / Barsky的C#代码 - 我从20年前最初使用C ++的旧库中获取了这个代码。应该很容易移回。
如果你的线条可以延伸到客户端矩形之外,请在你的点上调用ClipLine(rect,...),然后再将它们交给Graphics :: DrawLine。
private static bool clipTest(double dp, double dq, ref double du1, ref double du2)
{
double dr;
if (dp < 0.0)
{
dr = dq / dp;
if (dr > du2)
{
return false;
}
else if (dr > du1)
{
du1 = dr;
}
}
else
{
if (dp > 0.0)
{
dr = dq / dp;
if (dr < du1)
{
return false;
}
else if (dr < du2)
{
du2 = dr;
}
}
else
{
if (dq < 0.0)
{
return false;
}
}
}
return true;
}
public static bool ClipLine(Rectangle clipRect, ref int x1, ref int y1, ref int x2, ref int y2)
{
double dx1 = (double)x1;
double dx2 = (double)x2;
double dy1 = (double)y1;
double dy2 = (double)y2;
double du1 = 0;
double du2 = 1;
double deltaX = dx2 - dx1;
double deltaY;
if (clipTest(-deltaX, dx1 - clipRect.Left, ref du1, ref du2))
{
if (clipTest(deltaX, clipRect.Right - dx1, ref du1, ref du2))
{
deltaY = dy2 - dy1;
if (clipTest(-deltaY, dy1 - clipRect.Top, ref du1, ref du2))
{
if (clipTest(deltaY, clipRect.Bottom - dy1, ref du1, ref du2))
{
if (du2 < 1.0)
{
x2 = DoubleRoundToInt(dx1 + du2 * deltaX);
y2 = DoubleRoundToInt(dy1 + du2 * deltaY);
}
if (du1 > 0.0)
{
x1 = DoubleRoundToInt(dx1 + du1 * deltaX);
y1 = DoubleRoundToInt(dy1 + du1 * deltaY);
}
return x1 != x2 || y1 != y2;
}
}
}
}
return false;
}
答案 3 :(得分:-1)
问题是我自己没有处理WM_ERASEBKGND消息。