Winapi 在WM_PAINT中绘制(太慢)会导致闪烁吗?
我想用下面的代码在WM_PAINT消息处理程序中画很多线Winapi 在WM_PAINT中绘制(太慢)会导致闪烁吗?,winapi,gdi+,gdi,Winapi,Gdi+,Gdi,我想用下面的代码在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;
//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,客户端rect就会闪烁。我认为原因是Gdiplus::DrawLines太慢了
有没有办法解决闪烁问题?
谢谢。使用双缓冲区。这是一个关于Win32 C++应用程序的问题,特别是OnPaint函数和DC设备。
这里有几个链接可以帮助您检查双缓冲区的实现是否一切正常:所以问题是使用双缓冲区。这是一个关于Win32 C++应用程序的问题,特别是OnPaint函数和DC设备。
这里有几个链接可以帮助您检查双缓冲区的实现是否一切正常:所以问题是,我没有自己处理WM_ERASEBKGND消息。问题是我没有自己处理WM_ERASEBKGND消息。闪烁可能是由于绘制速度慢以及其他原因造成的。通常,请尝试/确保以下几点: 尽量不要依赖WM_橡皮擦BKGND消息,即返回非零,或者尽可能在WNDCLASS::hbrBackground中指定NULL。通常,绘制方法绘制脏区域的所有背景,然后不需要进行擦除 如果需要擦除,通常可以对其进行优化,使WM_ERASEBKGND返回非零,如果设置了PAINTSTRUCT::fErase,则paint方法还可以通过绘制常规绘制内容未覆盖的区域来确保擦除 如果合理可行,请编写paint方法,使其不会在一次调用中重新绘制相同的像素。例如,若要制作带有红色边框的蓝色矩形,请不要使用FillRectred,然后用FillRectblue重新喷涂其内部。尽可能多地画一次每个像素 对于复杂的控件/窗口,通常可以优化绘制方法,以便通过正确组织控件数据轻松跳过脏rect PAINTSTRUCT::rcPaint之外的许多绘制 更改控件状态时,仅使控件的最小所需区域无效
如果不是顶级窗口,考虑使用CSJPARTDC。如果paint方法不依赖于系统设置将clipping rectangle剪切到控件的客户端rect,则此类样式将带来更好的性能
如果您看到控件/窗口大小调整的闪烁,请考虑不要使用CSH-HeReDead和CSVVReDRAW。相反,手动使WM_大小中控件的相关部分无效。这通常只允许使控件的较小部分无效
如果您看到控件滚动时闪烁,请不要使整个客户端无效,而是使用ScrollWindow并仅使显示新滚动内容的小区域无效 如果以上所有操作都失败,则使用双缓冲闪烁可能是由缓慢的绘画以及其他因素引起的。通常,请尝试/确保以下几点: 尽量不要依赖WM_橡皮擦BKGND消息,即返回非零,或者尽可能在WNDCLASS::hbrBackground中指定NULL。通常,绘制方法绘制脏区域的所有背景,然后不需要进行擦除 如果需要擦除,通常可以对其进行优化,使WM_ERASEBKGND返回非零,如果设置了PAINTSTRUCT::fErase,则paint方法还可以通过绘制常规绘制内容未覆盖的区域来确保擦除 如果合理可行,请编写paint方法,使其不会在一次调用中重新绘制相同的像素。例如,若要制作带有红色边框的蓝色矩形,请不要使用FillRectred,然后用FillRectblue重新喷涂其内部。尽可能多地画一次每个像素 对于复杂的控件/窗口,通常可以优化绘制方法,以便通过正确组织控件数据轻松跳过脏rect PAINTSTRUCT::rcPaint之外的许多绘制 更改控件状态时,仅使控件的最小所需区域无效
如果不是顶级窗口,考虑使用CSJPARTDC。如果paint方法不依赖于系统设置将clipping rectangle剪切到控件的客户端rect,则此类样式将带来更好的性能
如果您看到控件/窗口大小调整的闪烁,请考虑不要使用CSH-HeReDead和CSVVReDRAW。相反,手动使WM_大小中控件的相关部分无效。这通常只允许使控件的较小部分无效
如果您看到控件滚动时闪烁,请不要使整个客户端无效,而是使用ScrollWindow并仅使显示新滚动内容的小区域无效 如果以上所有操作都失败,则使用双缓冲如果您的线条恰好超出了DC图形的边界,那么Win32/GDI+在剪裁时会非常慢。比如,比你自己的滚动慢两个数量级 剪辑功能。这里有一些实现Lang/BalsKi的C代码,我从原来的C++库中搜索出来,这是20年前的C++。应该很容易移植回来 如果您的线条可以延伸到客户端矩形之外,请调用CliplinInstitute。。。在将您的观点交给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;
}
如果您的线条恰好超出了DC图形的边界,那么Win32/GDI+在剪裁时会非常慢。例如,与滚动您自己的剪裁函数相比,最多要慢两个数量级。这里有一些实现Lang/BalsKi的C代码,我从原来的C++库中搜索出来,这是20年前的C++。应该很容易移植回来 如果您的线条可以延伸到客户端矩形之外,请调用CliplinInstitute。。。在将您的观点交给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;
}
除非hdcMem直接绑定到hdc,否则它不应该与由于最终位BLT而绘制线条的速度相关。也许还有别的事情发生?e、 g由于最终BitBlt,BitBltIt中的撕裂不应与绘制线条的速度有关,除非hdcMem直接与hdc相连。也许还有别的事情发生?e、 g在BitBlt中撕裂,但在结尾使用BitBlt。。。这不是有效的双重缓冲吗?也就是说,这个答案需要更多的扩展和/或支持。是的,我明白了。现在我无法给出更详细的答案。但几年前,我写了一个应用C++,MFC,GDI+,它的工作非常精细,线图形大约有10000个点,甚至更多,如果用户选择一个更大的时间段,但这是使用BitBlt在最后…这不是有效的双重缓冲吗?也就是说,这个答案需要更多的扩展和/或支持。是的,我明白了。现在我无法给出更详细的答案。但几年前,我编写了一个C++、MFC、GDI+应用程序,它的工作原理非常精细,如果用户选择了更大的时间段,则可以使用大约10000点甚至更多的线图形。