双缓冲绘图的问题

baidu_27193371 2015-05-25 11:53:06
为什么我用双缓冲绘图后,当改变旋转角度,重绘图形时,图像还是会闪烁,我已经将CDrawVIew类的背景重绘取消掉了代码如下:
void CRotateOwnShipDlg::OnDeltaposSpin(NMHDR *pNMHDR, LRESULT *pResult)
{
LPNMUPDOWN pNMUpDown = reinterpret_cast<LPNMUPDOWN>(pNMHDR);
// TODO: Add your control notification handler code here
CString str;
//获取编辑框内的数字
GetDlgItem(IDC_DRAWCOUNT_EDIT)->GetWindowText(str);
DrawCount=_ttoi(str);
//将旋转角度置为0
Angle=0;
str.Format(_T("%d"),Angle);
GetDlgItem(IDC_ANGLE_EDIT)->SetWindowText(str);
//将速度矢量与航向的夹角设为0
IncludedAngle=0;
str.Format(_T("%d"),IncludedAngle);
GetDlgItem(IDC_INCLUDEDANGLE_EDIT)->SetWindowText(str);
//此值为1,说明点击了Spin向下的箭头
if(pNMUpDown->iDelta==1)
{
DrawCount++;
}
//此值为-1,说明点击了Spin向上的箭头
if(pNMUpDown->iDelta==-1)
{
if(DrawCount>=2)
{
DrawCount--;
}

}
//将DrawCount变为字符串类型
str.Format(_T("%d"),DrawCount);
//在编辑框上显示修改后的数字
GetDlgItem(IDC_DRAWCOUNT_EDIT)->SetWindowText(str);
//当用Spin控件改变当前的数字时,不用点击绘图按钮,直接将图形绘制出来
m_drawview->SetData(DrawCount,Angle);
m_drawview->SetIncludedAngle(IncludedAngle);
m_drawview->Draw();
*pResult = 0;
}


void CRotateOwnShipDlg::OnDeltaposAngleSpin(NMHDR *pNMHDR, LRESULT *pResult)
{
LPNMUPDOWN pNMUpDown = reinterpret_cast<LPNMUPDOWN>(pNMHDR);
// TODO: Add your control notification handler code here
*pResult = 0;
CString str;
//获取旋转角度编辑框内的数字
GetDlgItem(IDC_ANGLE_EDIT)->GetWindowText(str);
Angle=_ttoi(str);
//此值为1说明点击了向下的箭头
if(pNMUpDown->iDelta==1)
{
Angle++;
}
//此值为-1说明点击了向上的箭头
if(pNMUpDown->iDelta==-1)
{
Angle--;
}
//将Angle变为CString型,在编辑框内显示
str.Format(_T("%d"),Angle);
GetDlgItem(IDC_ANGLE_EDIT)->SetWindowText(str);
//当用Spin控件改变当前的数字时,不用点击绘图按钮,直接将图形绘制出来
m_drawview->SetData(DrawCount,Angle);
m_drawview->Draw();
}


void CDrawView::Draw()
{
CDC *pDC=this->GetDC();
GetClientRect(&rect);
//内存DC的初始化
pMemDC=new CDC;
//位图对象的初始化
pBmp=new CBitmap;
//建立与屏幕显示兼容的内存显示设备
pMemDC->CreateCompatibleDC(pDC);
//建立一个与屏幕显示兼容的位图
pBmp->CreateCompatibleBitmap(pDC, rect.Width(), rect.Height());
//将位图选入到内存显示设备中
pMemDC->SelectObject(pBmp);
//先用背景色将位图清除干净
pMemDC->FillSolidRect(rect,RGB(255,255,255));
//将内存中的图拷贝到屏幕上进行显示
pDC->BitBlt(0,0,rect.Width(),rect.Height(),pMemDC,0,0,SRCCOPY);
m_data->Get(&DrawCount,&Angle);
m_data->GetIncludedAngle(&IncludedAngle);
//定义指向类成员函数的指针,并使它指向类的43个绘图函数
void (CDrawView::*p[100])()={&CDrawView::Draw1,&CDrawView::Draw2,&CDrawView::Draw3,&CDrawView::Draw4,
&CDrawView::Draw5,&CDrawView::Draw6,&CDrawView::Draw7,&CDrawView::Draw8,
&CDrawView::Draw9, &CDrawView::Draw10,&CDrawView::Draw11,&CDrawView::Draw12,
&CDrawView::Draw13,&CDrawView::Draw14,&CDrawView::Draw15,&CDrawView::Draw16,
&CDrawView::Draw17,&CDrawView::Draw18,&CDrawView::Draw19,&CDrawView::Draw20,
&CDrawView::Draw21,&CDrawView::Draw22,&CDrawView::Draw23,&CDrawView::Draw24,
&CDrawView::Draw25,&CDrawView::Draw26,&CDrawView::Draw27,&CDrawView::Draw28,
&CDrawView::Draw29,&CDrawView::Draw30,&CDrawView::Draw31,&CDrawView::Draw32,
&CDrawView::Draw33,&CDrawView::Draw34,&CDrawView::Draw35,&CDrawView::Draw36,
&CDrawView::Draw37,&CDrawView::Draw38,&CDrawView::Draw39,&CDrawView::Draw40,
&CDrawView::Draw41,&CDrawView::Draw42,&CDrawView::Draw43,&CDrawView::Draw44,
&CDrawView::Draw45,&CDrawView::Draw46};

//定义for循环,用来判断当前用户输入的是第几个图形,并且调用相应的成员函数将图形绘制出来
for(int i=0;i<100;i++)
{
if((i+1)==DrawCount)
{
(this->*p[i])();
}
}
}

void CDrawView::Draw1()
{
//设置设备DC
CDC *pDC=this->GetDC();
pDC->SetMapMode(MM_ISOTROPIC);
pDC->SetViewportOrg(rect.right/2,rect.bottom/2);
pDC->SetWindowExt(rect.right,rect.bottom);
pDC->SetViewportExt(rect.right,-rect.bottom);
pDC->SelectStockObject(NULL_BRUSH);
//设置画笔
Pen ShipPen(Color(0,0,0),3);
Pen LinePen(Color(200,200,200),1);
//绘制船
DrawShip(&ShipPen,&LinePen,&LinePen);
//将内存中的图拷贝到屏幕上进行显示
pDC->BitBlt(-rect.Width()/2,-rect.Height()/2,rect.Width(),rect.Height(),pMemDC,0,0,SRCCOPY);
//绘图完成后的清理
pBmp->DeleteObject() ;
pMemDC->DeleteDC();
}
void CDrawView::DrawShip(Pen *ShipPen,Pen *BeamPen,Pen *HeadPen)
{
//设置船的长度和宽度
long double Length=100,Width=20;
//设定中心点到船尾的距离
long double OrgToStern=10;
//设定船头相对于船宽所减少的距离
long double LessenDistance=5;
//设置beam line的端点
PointF BeamPoint1=PointF(-Width,0);
PointF BeamPoint2=PointF(Width,0);
//设置Head line的端点
PointF HeadPoint1=PointF(0,0);
PointF HeadPoint2=PointF(0,Length);
//设置船的端点
PointF ShipPoint[6]={PointF(-Width/2,-OrgToStern),PointF(-Width/2,Length-OrgToStern-LessenDistance),
PointF(-Width/2+LessenDistance,Length-OrgToStern),PointF(Width/2-LessenDistance,Length-OrgToStern),
PointF(Width/2,Length-OrgToStern-LessenDistance),PointF(Width/2,-OrgToStern)};
//设置旋转中心点
PointF Center=PointF(0,0);
//绘制船
RotatePolygon(Angle,ShipPoint,6,Center,ShipPen);
//绘制Beam line
RotateLine(Angle,BeamPoint1,BeamPoint2,Center,BeamPen);
//绘制Head line
RotateLine(Angle,HeadPoint1,HeadPoint2,Center,HeadPen);
}
void CDrawView::DrawShip(Pen *ShipPen,Pen *BeamPen,Pen *HeadPen)
{
//设置船的长度和宽度
long double Length=100,Width=20;
//设定中心点到船尾的距离
long double OrgToStern=10;
//设定船头相对于船宽所减少的距离
long double LessenDistance=5;
//设置beam line的端点
PointF BeamPoint1=PointF(-Width,0);
PointF BeamPoint2=PointF(Width,0);
//设置Head line的端点
PointF HeadPoint1=PointF(0,0);
PointF HeadPoint2=PointF(0,Length);
//设置船的端点
PointF ShipPoint[6]={PointF(-Width/2,-OrgToStern),PointF(-Width/2,Length-OrgToStern-LessenDistance),
PointF(-Width/2+LessenDistance,Length-OrgToStern),PointF(Width/2-LessenDistance,Length-OrgToStern),
PointF(Width/2,Length-OrgToStern-LessenDistance),PointF(Width/2,-OrgToStern)};
//设置旋转中心点
PointF Center=PointF(0,0);
//绘制船
RotatePolygon(Angle,ShipPoint,6,Center,ShipPen);
//绘制Beam line
RotateLine(Angle,BeamPoint1,BeamPoint2,Center,BeamPen);
//绘制Head line
RotateLine(Angle,HeadPoint1,HeadPoint2,Center,HeadPen);
}
void CDrawView::RotatePolygon(int RotateAngle,PointF *Point,int PointCount,PointF Center,Pen *pen)
{
//定义旋转后的PointF数组
PointF *PointChange;
PointChange=new PointF[PointCount];
//对坐标数组进行旋转变换,并将新的坐标赋给PointChange数组
for(int i=0;i<PointCount;i++)
{
PointChange[i]=RotatePoint(RotateAngle,Point[i],Center);
}
//对旋转后的坐标进行坐标变换
for(int i=0;i<PointCount;i++)
{
PointChange[i]=ConvertCoodinate(PointChange[i]);
}
//将图像画到内存
Graphics graphic(pMemDC->m_hDC);
//消除锯齿
graphic.SetSmoothingMode(SmoothingModeAntiAlias);
//绘制多边形
graphic.DrawPolygon(pen,PointChange,PointCount);
//删除指针
delete PointChange;
}
void CDrawView::RotateLine(int RotateAngle,PointF Point1,PointF Point2,PointF Center,Pen *pen)
{
//求旋转后的两个端点
PointF Point1Change;
PointF Point2Change;
Point1Change=RotatePoint(RotateAngle,Point1,Center);
Point2Change=RotatePoint(RotateAngle,Point2,Center);
//对旋转后的端点进行坐标转换
Point1Change=ConvertCoodinate(Point1Change);
Point2Change=ConvertCoodinate(Point2Change);
//将图像画到内存
Graphics graphic(pMemDC->m_hDC);
//消除锯齿
graphic.SetSmoothingMode(SmoothingModeAntiAlias);
//绘制直线
graphic.DrawLine(pen,Point1Change,Point2Change);
}
...全文
546 9 打赏 收藏 转发到动态 举报
写回复
用AI写文章
9 条回复
切换为时间正序
请发表友善的回复…
发表回复
baoyz 2015-08-18
  • 打赏
  • 举报
回复
代码没有格式看着眼晕。 双缓冲的意思,是有两个CDC。一个在桌面上,面对用户,一个在内存中,面对程序。 所有的绘图工作都在内存CD上完成。不管画多少次,擦多少次。 都画好后,一次性贴到桌面上来。这样就不可能闪了。 看你的程序,应该是擦除pDC时出现的闪。 就是 ============ //先用背景色将位图清除干净 pMemDC->FillSolidRect(rect,RGB(255,255,255)); //将内存中的图拷贝到屏幕上进行显示 pDC->BitBlt(0,0,rect.Width(),rect.Height(),pMemDC,0,0,SRCCOPY); ===================== 这段,这段不要就行了。
Tinary3v0 2015-05-27
  • 打赏
  • 举报
回复
上面这样改动后,你的绘图应该就不闪烁了。 但是还有地方需要优化。 首先你的pMemDC,pBmp有必要每次Draw的时候都重新创建么? 不需要,直接声明成类的成员变量,然后再View的OnInitialUpdate或Dialog的OnInitialDialog中创建一次就行了。 类似于:
class CDrawView:CView
{
        CDC* pdc;
        CDC memDC;
        CBitmap memBmp;

        BOOL OnInitialUpdate( )
        {
               pdc=this->GetDC();
               GetClientRect(&rect);
               //建立与屏幕显示兼容的内存显示设备
               memDC.CreateCompatibleDC(pdc);
               //建立一个与屏幕显示兼容的位图
               memBmp.CreateCompatibleBitmap(pdc, rect.Width(), rect.Height());
               //将位图选入到内存显示设备中
               memDC.SelectObject(pBmp);
        }

       void Draw( )
       {
              memDC.FillSolidRect(rect,RGB(255,255,255));
 
        Graphics graphic( memDC.m_hDC ); //声明一个Graphics
        m_data->Get(&DrawCount,&Angle);
    m_data->GetIncludedAngle(&IncludedAngle);
    //定义指向类成员函数的指针,并使它指向类的43个绘图函数
    void (CDrawView::*p[100])()={&CDrawView::Draw1,&CDrawView::Draw2,&CDrawView::Draw3,&CDrawView::Draw4,
        &CDrawView::Draw5,&CDrawView::Draw6,&CDrawView::Draw7,&CDrawView::Draw8,
        &CDrawView::Draw9, &CDrawView::Draw10,&CDrawView::Draw11,&CDrawView::Draw12,
        &CDrawView::Draw13,&CDrawView::Draw14,&CDrawView::Draw15,&CDrawView::Draw16,
        &CDrawView::Draw17,&CDrawView::Draw18,&CDrawView::Draw19,&CDrawView::Draw20,
        &CDrawView::Draw21,&CDrawView::Draw22,&CDrawView::Draw23,&CDrawView::Draw24,
        &CDrawView::Draw25,&CDrawView::Draw26,&CDrawView::Draw27,&CDrawView::Draw28,
        &CDrawView::Draw29,&CDrawView::Draw30,&CDrawView::Draw31,&CDrawView::Draw32,
        &CDrawView::Draw33,&CDrawView::Draw34,&CDrawView::Draw35,&CDrawView::Draw36,
        &CDrawView::Draw37,&CDrawView::Draw38,&CDrawView::Draw39,&CDrawView::Draw40,
        &CDrawView::Draw41,&CDrawView::Draw42,&CDrawView::Draw43,&CDrawView::Draw44,
        &CDrawView::Draw45,&CDrawView::Draw46};
 
    //定义for循环,用来判断当前用户输入的是第几个图形,并且调用相应的成员函数将图形绘制出来
    for(int i=0;i<100;i++)
    {
        if((i+1)==DrawCount)
        {
            (this->*p[i])( &graphic); //输入Graphic指针
        }
    }
 
    //将内存中的图拷贝到屏幕上进行显示 
    pdc->BitBlt(0,0,rect.Width(),rect.Height(),&memDC,0,0,SRCCOPY);
       }

        void OnDraw( CDC* pDC ) //屏幕刷新时调用防止窗口被覆盖等情况时屏幕不刷新
        {
              Draw( );
        }
}
Tinary3v0 2015-05-27
  • 打赏
  • 举报
回复
上面这样改动后,再继续改动你的RotatePolygon,DrawShip等函数为需要输入一个Graphic指针的。 类似:
void CDrawView::RotatePolygon(Graphics* graphic,int RotateAngle,PointF *Point,int PointCount,PointF Center,Pen *pen)
{
	//定义旋转后的PointF数组
	PointF *PointChange;
	PointChange=new PointF[PointCount];
	//对坐标数组进行旋转变换,并将新的坐标赋给PointChange数组
	for(int i=0;i<PointCount;i++)
	{
		PointChange[i]=RotatePoint(RotateAngle,Point[i],Center);
	}
	//对旋转后的坐标进行坐标变换
	for(int i=0;i<PointCount;i++)
	{
		PointChange[i]=ConvertCoodinate(PointChange[i]);
	}
	//将图像画到内存
	//消除锯齿
	graphic->SetSmoothingMode(SmoothingModeAntiAlias);
	//绘制多边形
	graphic->DrawPolygon(pen,PointChange,PointCount);
	//删除指针
	delete PointChange;
}
Tinary3v0 2015-05-27
  • 打赏
  • 举报
回复
代码好难看懂! 是void CDrawView::Draw()的问题。 在这个函数当中,你首先创建了一个内存DC指针,并且应该是保存成类成员了; 然后你创建了内存DC使用的Bitmap并选择为内存DC的绘图画布。 其后,你将这个画布清为白色。 清为白色后你直接将内存画布拷贝到了窗口DC。------->这类就是莫名其妙的地方了。 你创建了内存DC清理为白色后为什么直接拷贝到窗口DC,这样做有什么效果啊? 你相当于重复了窗口的背景Ereabackground?? 整个过程完全错误了。 结构应该是这样的 创建了一个内存DC 创建了内存DC使用的Bitmap并选择为内存DC的绘图画布。 将画布清为白色。 使用这个内存DC进行各种绘图; 最后在拷贝到窗口DC。 代码结构应该是:


void CDrawView::Draw()
{ 
	CDC *pDC=this->GetDC();
	GetClientRect(&rect);
	//内存DC的初始化
	pMemDC=new CDC;
	//位图对象的初始化
	pBmp=new CBitmap;
	//建立与屏幕显示兼容的内存显示设备
	pMemDC->CreateCompatibleDC(pDC);
	//建立一个与屏幕显示兼容的位图
	pBmp->CreateCompatibleBitmap(pDC, rect.Width(), rect.Height());
	//将位图选入到内存显示设备中
	pMemDC->SelectObject(pBmp);
	//先用背景色将位图清除干净
	pMemDC->FillSolidRect(rect,RGB(255,255,255));

        m_data->Get(&DrawCount,&Angle);
	m_data->GetIncludedAngle(&IncludedAngle);
	//定义指向类成员函数的指针,并使它指向类的43个绘图函数
	void (CDrawView::*p[100])()={&CDrawView::Draw1,&CDrawView::Draw2,&CDrawView::Draw3,&CDrawView::Draw4,
		&CDrawView::Draw5,&CDrawView::Draw6,&CDrawView::Draw7,&CDrawView::Draw8,
		&CDrawView::Draw9, &CDrawView::Draw10,&CDrawView::Draw11,&CDrawView::Draw12,
		&CDrawView::Draw13,&CDrawView::Draw14,&CDrawView::Draw15,&CDrawView::Draw16,
		&CDrawView::Draw17,&CDrawView::Draw18,&CDrawView::Draw19,&CDrawView::Draw20,
		&CDrawView::Draw21,&CDrawView::Draw22,&CDrawView::Draw23,&CDrawView::Draw24,
		&CDrawView::Draw25,&CDrawView::Draw26,&CDrawView::Draw27,&CDrawView::Draw28,
		&CDrawView::Draw29,&CDrawView::Draw30,&CDrawView::Draw31,&CDrawView::Draw32,
		&CDrawView::Draw33,&CDrawView::Draw34,&CDrawView::Draw35,&CDrawView::Draw36,
		&CDrawView::Draw37,&CDrawView::Draw38,&CDrawView::Draw39,&CDrawView::Draw40,
		&CDrawView::Draw41,&CDrawView::Draw42,&CDrawView::Draw43,&CDrawView::Draw44,
		&CDrawView::Draw45,&CDrawView::Draw46};

	//定义for循环,用来判断当前用户输入的是第几个图形,并且调用相应的成员函数将图形绘制出来
	for(int i=0;i<100;i++)
	{
		if((i+1)==DrawCount)
		{
			(this->*p[i])();
		}
	}

	//将内存中的图拷贝到屏幕上进行显示 
	pDC->BitBlt(0,0,rect.Width(),rect.Height(),pMemDC,0,0,SRCCOPY);
}
代码经过上面这样的改动以后,你后续的Draw1~Draw46等就都需要改动了,改动很简单: Draw1等类型函数都改成下面的结构:
void CDrawView::Draw1( Graphics* graphic ) //增加输入指针
{
	Pen ShipPen(Color(0,0,0),3);
	Pen LinePen(Color(200,200,200),1);
	//绘制船
	DrawShip( graphic, &ShipPen,&LinePen,&LinePen ); //使用Graphic指针绘图
}
Draw函数变成:
void CDrawView::Draw()
{ 
	CDC *pDC=this->GetDC();
	GetClientRect(&rect);
	//内存DC的初始化
	pMemDC=new CDC;
	//位图对象的初始化
	pBmp=new CBitmap;
	//建立与屏幕显示兼容的内存显示设备
	pMemDC->CreateCompatibleDC(pDC);
	//建立一个与屏幕显示兼容的位图
	pBmp->CreateCompatibleBitmap(pDC, rect.Width(), rect.Height());
	//将位图选入到内存显示设备中
	pMemDC->SelectObject(pBmp);
	//先用背景色将位图清除干净
	pMemDC->FillSolidRect(rect,RGB(255,255,255));

        Graphics graphic( pMemeDC->m_hDC ); //声明一个Graphics
        m_data->Get(&DrawCount,&Angle);
	m_data->GetIncludedAngle(&IncludedAngle);
	//定义指向类成员函数的指针,并使它指向类的43个绘图函数
	void (CDrawView::*p[100])()={&CDrawView::Draw1,&CDrawView::Draw2,&CDrawView::Draw3,&CDrawView::Draw4,
		&CDrawView::Draw5,&CDrawView::Draw6,&CDrawView::Draw7,&CDrawView::Draw8,
		&CDrawView::Draw9, &CDrawView::Draw10,&CDrawView::Draw11,&CDrawView::Draw12,
		&CDrawView::Draw13,&CDrawView::Draw14,&CDrawView::Draw15,&CDrawView::Draw16,
		&CDrawView::Draw17,&CDrawView::Draw18,&CDrawView::Draw19,&CDrawView::Draw20,
		&CDrawView::Draw21,&CDrawView::Draw22,&CDrawView::Draw23,&CDrawView::Draw24,
		&CDrawView::Draw25,&CDrawView::Draw26,&CDrawView::Draw27,&CDrawView::Draw28,
		&CDrawView::Draw29,&CDrawView::Draw30,&CDrawView::Draw31,&CDrawView::Draw32,
		&CDrawView::Draw33,&CDrawView::Draw34,&CDrawView::Draw35,&CDrawView::Draw36,
		&CDrawView::Draw37,&CDrawView::Draw38,&CDrawView::Draw39,&CDrawView::Draw40,
		&CDrawView::Draw41,&CDrawView::Draw42,&CDrawView::Draw43,&CDrawView::Draw44,
		&CDrawView::Draw45,&CDrawView::Draw46};

	//定义for循环,用来判断当前用户输入的是第几个图形,并且调用相应的成员函数将图形绘制出来
	for(int i=0;i<100;i++)
	{
		if((i+1)==DrawCount)
		{
			(this->*p[i])( &graphic); //输入Graphic指针
		}
	}

	//将内存中的图拷贝到屏幕上进行显示 
	pDC->BitBlt(0,0,rect.Width(),rect.Height(),pMemDC,0,0,SRCCOPY);
}
worldy 2015-05-26
  • 打赏
  • 举报
回复
闪烁的根本原因是背景重绘,而背景重绘如果不采取措施,在任何WM_ERASEBKGND都会发生,要想不闪烁,最简单的办法就是在WM_ERASEBKGND时候,直接返回TRUE;如果有子控件,窗口的风格还的使用WS_CLIPCHILD风格
schlafenhamster 2015-05-26
  • 打赏
  • 举报
回复
闪烁 是因为擦除背景的原因. 减少 擦除区. 即这次可以重绘的区是不要擦除的.
zgl7903 2015-05-26
  • 打赏
  • 举报
回复
子绘制函数中传入CDC句柄(内存DC) SubDraw1(CDC *pDC, LPRECT rect, …………) { // } SubDraw2(CDC *pDC, LPRECT rect, …………) { // } OnDraw() { CPaintDC dc(this); CDC memDC; CBitmap memBmp; …… CDC *pDC = &memDC; SubDraw1(pDC, SubDraw2(pDC, dc.BitBlt(……, pDC …… }
baidu_27193371 2015-05-25
  • 打赏
  • 举报
回复
DrawShip代码粘贴了两边,
笨笨仔 2015-05-25
  • 打赏
  • 举报
回复
双缓冲绘图不会出现闪烁,除非你的程序有错或在绘制区中出现了控件。 注意方法: 1、在内存中创建一个CDC 2、在内存CDC上绘制 3、将结果写回屏幕

2,643

社区成员

发帖
与我相关
我的任务
社区描述
VC/MFC 硬件/系统
社区管理员
  • 硬件/系统社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧