我对使用GDI渲染图形感到陌生......
我做了一个绘画程序,它运行正常,只是它导致了很多烦人的屏幕闪烁。我承认我的油漆代码并没有真正优化(缺乏时间),但也不应该超级低效,所以我很困惑。
我基本上做的是在init上创建兼容的DC,然后创建兼容的位图。然后我将其选择到兼容的DC中,并绘制到兼容的DC。然后我使用BitBlit()将其复制到窗口hDC ...
有人能告诉我这个屏幕撕裂的可能原因吗? 编辑:顺便说一句,屏幕闪烁只发生在绘制路径期间(在路径被绘制到hMemDC之前,它被绘制到窗口的hDC)
代码示例: (编辑:如果您需要查看您认为相关的更多代码,请注释并进行编辑)
bool Path::DrawTo(HDC hDC)
{
if(hDC == NULL || m_PointVector.size() <= 0) {
return false;
}
switch (m_Tool) {
case Tool_Pen:
{
Point2D p = m_PointVector.at(0);
if(m_PointVector.size() > 1) {
HPEN oldPen = (HPEN)SelectObject(hDC,m_hPen);
MoveToEx(hDC, p.x, p.y, nullptr);
for(UINT i = 1; i < m_PointVector.size(); ++i) {
p = m_PointVector.at(i);
LineTo(hDC,p.x,p.y);
}
SelectObject(hDC,oldPen);
break;
} //else
SetPixel(hDC,p.x-1,p.y,m_Col);
SetPixel(hDC,p.x,p.y,m_Col);
SetPixel(hDC,p.x+1,p.y,m_Col);
SetPixel(hDC,p.x,p.y-1,m_Col);
SetPixel(hDC,p.x,p.y+1,m_Col);
break;
}
case Tool_Line:
{
if(m_PointVector.size() > 1) {
Point2D p = m_PointVector.at(0);
HPEN oldPen = (HPEN)SelectObject(hDC,m_hPen);
MoveToEx(hDC, p.x, p.y, nullptr);
for(UINT i = 1; i < m_PointVector.size(); ++i) {
p = m_PointVector.at(i);
LineTo(hDC,p.x,p.y);
}
SelectObject(hDC,oldPen);
}
break;
}
case Tool_Ellipse:
{
if(m_PointVector.size() > 1) {
HPEN oldPen = (HPEN)SelectObject(hDC,m_hPen);
SelectObject(hDC,m_hBrush);
Point2D p1 = m_PointVector.at(0);
Point2D p2 = m_PointVector.at(1);
if(p1.x > p2.x) {
int iTemp = p1.x;
p1.x = p2.x;
p2.x = iTemp;
}
if(p1.y > p2.y) {
int iTemp = p1.y;
p1.y = p2.y;
p2.y = iTemp;
}
Ellipse(hDC,p1.x,p1.y,p2.x,p2.y);
SelectObject(hDC,oldPen);
}
break;
}
case Tool_Rectangle:
{
if(m_PointVector.size() > 1) {
HPEN oldPen = (HPEN)SelectObject(hDC,m_hPen);
SelectObject(hDC,m_hBrush);
Point2D p1 = m_PointVector.at(0);
Point2D p2 = m_PointVector.at(1);
if(p1.x > p2.x) {
int iTemp = p1.x;
p1.x = p2.x;
p2.x = iTemp;
}
if(p1.y > p2.y) {
int iTemp = p1.y;
p1.y = p2.y;
p2.y = iTemp;
}
Rectangle(hDC,p1.x,p1.y,p2.x,p2.y);
SelectObject(hDC,oldPen);
}
break;
}
case Tool_LineTrack:
{
HPEN oldPen = (HPEN)SelectObject(hDC,m_hPen);
SelectObject(hDC,m_hBrush);
int vSize = (int)m_PointVector.size();
Point2D p = m_PointVector.at(0);
if (vSize <= 1) {
Ellipse(hDC,p.x-10,p.y-10,p.x+10,p.y+10);
}
else {
//draw LineTrack
Point2D pTemp = m_PointVector.at(1);
MoveToEx(hDC,p.x,p.y,nullptr);
for (int i = 1; i < vSize; ++i) {
p = m_PointVector.at(i);
pTemp = m_PointVector.at(i-1);
LineTo(hDC,p.x,p.y);
Ellipse(hDC,pTemp.x-10,pTemp.y-10,pTemp.x+10,pTemp.y+10);
}
Ellipse(hDC,p.x-10,p.y-10,p.x+10,p.y+10);
}
SelectObject(hDC,oldPen);
break;
}
}
return true;
}
LRESULT MyApp::WndProc(HWND hWnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
{
if(iMsg == WM_CREATE)
{
CREATESTRUCT *pCS = (CREATESTRUCT*)lParam;
SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG)pCS->lpCreateParams);
}
else
{
//retrieve the stored "this" pointer
MyApp* pApp = (MyApp*)GetWindowLongPtr(hWnd, GWLP_USERDATA);
switch (iMsg)
{
case WM_PAINT:
{
pApp->Paint();
return 0;
}
case WM_COMMAND:
{
int wmId = LOWORD(wParam);
int wmEvent = HIWORD(wParam);
// Parse the menu selections:
switch (wmId)
{
case IDM_NEW:
{
////
return 0;
}
return 0;
case IDM_LOAD:
{
//////
return 0;
}
case IDM_SAVE:
{
//////
return 0;
}
case IDM_SAVEAS:
{
//////
return 0;
}
case IDM_COLOURMAIN:
{
COLORREF col;
if(MyWin32Funcs::OnColorPick(col)) {
pApp->m_pPath->SetColor1(col);
}
return 0;
}
case IDM_COLOURSECONDARY:
{
COLORREF col;
if(MyWin32Funcs::OnColorPick(col)) {
pApp->m_pPath->SetColor2(col);
}
return 0;
}
case IDM_PEN:
{
pApp->m_pPath->SetTool(Tool_Pen);
return 0;
}
case IDM_LINE:
{
pApp->m_pPath->SetTool(Tool_Line);
return 0;
}
case IDM_ELLIPSE:
{
pApp->m_pPath->SetTool(Tool_Ellipse);
return 0;
}
case IDM_RECTANGLE:
{
pApp->m_pPath->SetTool(Tool_Rectangle);
return 0;
}
case IDM_LINETRACK:
{
pApp->m_pPath->SetTool(Tool_LineTrack);
return 0;
}
default:
{
//////
return 0;
}
}
}
case WM_LBUTTONUP:
{
//////
Point2D p;
p.x = LOWORD(lParam);
p.y = HIWORD(lParam);
switch(pApp->m_pPath->GetTool()) {
case Tool_Pen:
{
pApp->m_bPaintToBitmap = true;
InvalidateRect(pApp->m_hWnd,NULL,true);
break;
}
case Tool_Ellipse:
{
pApp->m_bPaintToBitmap = true;
InvalidateRect(pApp->m_hWnd,NULL,true);
break;
}
case Tool_Rectangle:
{
pApp->m_bPaintToBitmap = true;
InvalidateRect(pApp->m_hWnd,NULL,true);
break;
}
case Tool_Line:
{
pApp->m_pPath->AddPoint(p,pApp->m_pBitmapPainter->GetBmpSize(),pApp->m_pBitmapPainter->GetBmpOffset());
InvalidateRect(pApp->m_hWnd,NULL,false);
break;
}
case Tool_LineTrack:
{
pApp->m_pPath->AddPoint(p,pApp->m_pBitmapPainter->GetBmpSize(),pApp->m_pBitmapPainter->GetBmpOffset());
InvalidateRect(pApp->m_hWnd,NULL,false);
break;
}
}
return 0;
}
case WM_RBUTTONUP:
{
//////
int x = LOWORD(lParam);
int y = HIWORD(lParam);
switch(pApp->m_pPath->GetTool()) {
case Tool_Line:
{
pApp->m_bPaintToBitmap = true;
InvalidateRect(pApp->m_hWnd,NULL,true);
break;
}
case Tool_LineTrack:
{
pApp->m_bPaintToBitmap = true;
InvalidateRect(pApp->m_hWnd,NULL,true);
break;
}
}
return 0;
}
case WM_LBUTTONDOWN:
{
Point2D p;
p.x = LOWORD(lParam);
p.y = HIWORD(lParam);
switch(pApp->m_pPath->GetTool()) {
case Tool_Pen:
pApp->m_pPath->AddPoint(p,pApp->m_pBitmapPainter->GetBmpSize(),pApp->m_pBitmapPainter->GetBmpOffset());
InvalidateRect(pApp->m_hWnd,NULL,false);
break;
}
}
case WM_MOUSEMOVE:
{
Point2D p;
p.x = LOWORD(lParam);
p.y = HIWORD(lParam);
if (wParam & MK_LBUTTON) {
switch(pApp->m_pPath->GetTool()) {
case Tool_Pen:
{
pApp->m_pPath->AddPoint(p,pApp->m_pBitmapPainter->GetBmpSize(),pApp->m_pBitmapPainter->GetBmpOffset());
InvalidateRect(pApp->m_hWnd,NULL,false);
break;
}
case Tool_Ellipse:
{
if( pApp->m_pPath->GetLen() >= 1) {
pApp->m_pPath->SetPointAt(1,p,pApp->m_pBitmapPainter->GetBmpSize(),pApp->m_pBitmapPainter->GetBmpOffset());
}
else {
pApp->m_pPath->AddPoint(p,pApp->m_pBitmapPainter->GetBmpSize(),pApp->m_pBitmapPainter->GetBmpOffset());
}
InvalidateRect(pApp->m_hWnd,NULL,false);
break;
}
case Tool_Rectangle:
{
if( pApp->m_pPath->GetLen() >= 1) {
pApp->m_pPath->SetPointAt(1,p,pApp->m_pBitmapPainter->GetBmpSize(),pApp->m_pBitmapPainter->GetBmpOffset());
}
else {
pApp->m_pPath->AddPoint(p,pApp->m_pBitmapPainter->GetBmpSize(),pApp->m_pBitmapPainter->GetBmpOffset());
}
InvalidateRect(pApp->m_hWnd,NULL,false);
break;
}
}
}
return 0;
}
case WM_CLOSE:
{
//////
return 0;
}
}
PostQuitMessage(0);
return 0;
}
}
}
return DefWindowProc (hWnd, iMsg, wParam, lParam) ;
}
void MyApp::Paint()
{
BeginPaint(m_hWnd,&m_PaintStruct);
if (m_bPaintToBitmap) {
Point2D p;
p.x = BMPXOFFSET;
p.y = BMPYOFFSET;
m_pPath->Offset(p);
m_pPath->DrawTo(m_pBitmapPainter->GetMemDC());
m_pPath->ClrPath();
m_pBitmapPainter->Paint(); //this is where BitBlt() occurs
m_bPaintToBitmap = false;
if(m_pBitmapPainter->IsAdjusted() == false) {
m_pBitmapPainter->SetbAdjusted(true);
}
}
else {
m_pBitmapPainter->Paint(); //this is where BitBlt() occurs
m_pPath->DrawTo(m_hDC);
}
EndPaint(m_hWnd,&m_PaintStruct);
}
非常感谢任何帮助。
答案 0 :(得分:7)
我认为你所看到的是闪烁,而不是撕裂。为了最大限度地减少闪烁,您的WM_PAINT
应该只向窗口DC写入一次。通常,这一项操作是BitBlt
:
HDC hdc = BeginPaint(m_hwnd, &m_PaintStruct);
... paint to bitmap ...
BitBlt(hdc, ...); // blt from bitmap to screen
EndPaint(m_hwnd, &m_PaintStruct);
如果您以多个步骤绘制到窗口DC,则打开闪烁窗口。
您对问题的描述与代码不符。在您的描述中,您说您正在从兼容的DC到窗口hDC进行blitting。但是在您的代码中,BitBlt
后跟m_pPath->DrawTo(m_hDC)
。如果在DrawTo
期间发生刷新,则屏幕将以部分绘制的视图刷新,从而导致闪烁。
答案 1 :(得分:4)
如果您要绘制整个客户区,请覆盖WM_ERASEBKGND
,然后返回TRUE
。这将减少闪烁。
正如其他人指出的那样;使用HDC
给出的WM_PAINT
,因为它可能包含裁剪区域,以及可能优化屏幕更新的其他内容。
修改强>
如果您没有绘制整个客户区域,则可以在您知道WM_PAINT
处理程序不会绘制的区域中执行背景绘制。
答案 2 :(得分:0)
BeginPaint为您提供了应该绘制的HDC。你忽略了它。
答案 3 :(得分:0)
你在运行什么操作系统?如果它的Vista或Windows 7,你是否在某种“兼容模式”下运行,禁用桌面合成?
据推测,Vista引入的桌面窗口管理器(DWM)的一个优点是(source):
在Windows XP中,应用程序在操作系统时直接更新其窗口 要求他们。这些请求可以与异步执行 关于监视器的刷新率或可能的任何更新 正在运行。这些请求的效果是用户 看到窗户撕裂并重新绘图不正确或缓慢。 DWM 窗口呈现的风格消除了撕裂的文物, 提供高品质的桌面体验。最终的好处 用户是系统似乎更具响应性和 经验更清晰。
即使用DWM,合成到桌面将与绘图同步,以避免看到部分绘制的“撕裂”的东西。
如果不是这样,你确定你不会混淆“撕裂”背景擦除闪烁或其他东西吗?