几天前,我终于克服了C++窗体重绘时的闪烁问题,用到的技巧就是双缓冲。但是怎样保持住已经绘制的图形呢?也就是仿照Windows自带的画图程序一般,动态的做出一条直线。最容易想到的方法是在MouseMove过程中,不断擦除上次所画的线,然后再画出新的直线,只须增件变量保存开始的点和上次的点即可。这样做的确可以实现动态画线的功能,但是有两点不足之处。一是代码没有放在OnDraw过程中,窗体重绘时,先前所有的图形将被擦除;二是擦除上次直线的时候,难免同之前画好的线相交,将上次的线恢复为背景色的时候,很有可能也切断了之前已经做好的线。

  解决这一问题的办法依然是使用双缓冲,和上次不同的上,用于缓冲的内存DC和位图要保持住,而非随用随建。为了解决第一点不足,需要建立一个内存DC和位图,并在OnDraw过程中将其拷贝到前台。这样欧文们作图的时候将图做在内存DC上,然后使窗口刷新,便看到了所做的图了。当窗体移动或被遮挡时,窗口需要重绘,只是把我们的缓冲重新绘制一遍,因此已经画好的线不会被擦除。解决第二的问题的方法主要有两种,一种是设置画笔的模式为异或模式,这样的话在同意位置画两次的话等同于什么都没画。但是这样会牺牲线条本身的颜色属性,如果背景是单一的颜色,自然是看不出来的,但是如果背景颜色丰富,那我们的线条也就随着多姿多彩了;第二种方法是比较好用的脏矩形法,脏矩形法的主要内容就是每次画面的刷新只更新需要更新的那一块区域,这正是Flash采用的方式,效率比较高。在本程序中为简单起见,没有采用完全的脏矩形法,脏矩形始终定义为整个客户区大小。为此,需要重新定义一个新的缓冲区,用来保存即将变脏的矩形,以备之后恢复所用。在响应WM_MOUSEMOVE消息的过程中,先将新缓冲区上的图形还原到旧缓冲区上,这样可以遮挡住上次的线条。再在旧缓冲区上作图,然后刷新窗口,我们便看到了动态效果。不知道这是不是传说中的三缓冲技术呢?

  以下是代码的分析:

CBitmap memBakBMP; //新缓冲区内存DC
CDC memBakDC; //新缓冲区用的位图
BOOL bClicked; //判断是否应当作图
CPoint ptBegin; //记录图象开始的位置
CBitmap memBMP; //新缓冲区用的位图
CDC memDC; //旧缓冲区内存DC

响应WM_CREATE消息的时候做初始化工作:

int CNSSDrawerView::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CView::OnCreate(lpCreateStruct) == -)
return -;
memDC.CreateCompatibleDC(NULL);
memBMP.CreateCompatibleBitmap(&memDC,,);
memDC.SelectObject(&memBMP);
memDC.FillSolidRect(,,,,GetDC()->GetBkColor());
memBakDC.CreateCompatibleDC(NULL);
memBakBMP.CreateCompatibleBitmap(&memBakDC,,);
memBakDC.SelectObject(&memBakBMP);
return ;
}

相应的,在响应WM_DESTROY消息的时候,也要做善后工作:

void CNSSDrawerView::OnDestroy()
{
CView::OnDestroy();
memDC.DeleteDC();
memBMP.DeleteObject();
memBakDC.DeleteDC();
memBakBMP.DeleteObject();
}

干预WM_ERASEBKGND消息的响应,这里是最容易忽略的地方,要特别注意

BOOL CNSSDrawerView::OnEraseBkgnd(CDC* pDC)
{
return TRUE; //CView::OnEraseBkgnd(pDC);
}

部分变量在构造函数中初始化:

CNSSDrawerView::CNSSDrawerView()
{
ptBegin = ;
bClicked = FALSE;
}

响应WM_LBUTTONDOWN消息,着是画线的开始:

void CNSSDrawerView::OnLButtonDown(UINT nFlags, CPoint point)
{
ptBegin = point;
bClicked = TRUE;
CRect rect;
GetClientRect(&rect);
memBakDC.BitBlt(,,rect.Width(),rect.Height(),&memDC,,,SRCCOPY);
CView::OnLButtonDown(nFlags, point);
}

相应的,WM_LBUTTONUP消息的响应是画线的结束:

void CNSSDrawerView::OnLButtonUp(UINT nFlags, CPoint point)
{
bClicked = FALSE;
CView::OnLButtonUp(nFlags, point);
}

最主要的部分在响应WM_MOUSEMOVE消息的模块中:

void CNSSDrawerView::OnMouseMove(UINT nFlags, CPoint point)
{
if(bClicked)
{
CRect rect;
GetClientRect(&rect);
memDC.BitBlt(,,rect.Width(),rect.Height(),&memBakDC,,,SRCCOPY);
memDC.MoveTo(ptBegin);
memDC.LineTo(point);
Invalidate();
}
CView::OnMouseMove(nFlags, point);
}

VC++双缓冲保持背景不擦除之实现的更多相关文章

  1. VC双缓冲画图技术介绍

    双缓冲画图,它是一种主要的图形图像画图技术.首先,它在内存中创建一个与屏幕画图区域一致的对象,然后将图形绘制到内存中的这个对象上,最后把这个对象上的图形数据一次性地拷贝并显示到屏幕上. 这样的技术能够 ...

  2. MFC VC 双缓冲绘图基本原理与实现,详细解释

    转自:http://blog.csdn.net/foreverhuylee/article/details/21548107 当然你可以直接搜索到能用的代码,并且基本能满足要求.不过这样总不是学习的态 ...

  3. VC 绘图,使用双缓冲技术实现

    VC 绘图,使用双缓冲技术实现 - Cloud-Datacenter-Renewable Energy-Big Data-Model - 博客频道 - CSDN.NET VC 绘图,使用双缓冲技术实现 ...

  4. VC++绘图时,利用双缓冲解决屏幕闪烁 转载

    最近做中国象棋,绘制界面时遇到些问题,绘图过程中屏幕闪烁,估计都会想到利用双缓冲来解决问题,但查了下网上双缓冲的资料,发现基本是MFC的,转化为VC++后,大概代码如下: void DrawBmp(H ...

  5. VC使用双缓冲避免绘图闪烁的正确使用方法【转】

    使用内存DC绘图,然后实现双缓冲,避免绘图闪烁,这个小技术简单但很有效.但是仍然有很多人说使用了双缓冲,图片却仍然有闪烁,分析了几个这样的例子,发现 其实不是双缓冲的技术问题,而是使用者没有正确理解和 ...

  6. VC使用双缓冲制作绘图控件

    最近用VC做了一个画图的控件.控件在使用的时候遇到点问题.在控件里画了图之后切换到其他页面,等再切换回来的时候,发现控件里画的图都不见了.这是因为VC里面,当缩小.遮挡页面后客户区域就会失效,当再次显 ...

  7. VC++中双缓冲技术画图

    用双缓冲,先在内存中绘制,然后拷贝到屏幕DC,这样就不会出现画出去的情况了,前段时间我也是为这个问题费了不少劲.我把我的一段代码给你看一下: CDC *pDC = m_drawbox.GetDC(); ...

  8. Win32 GDI 非矩形区域剪裁,双缓冲技术

    传统的Win32通过GDI提供图形显示的功能,包括了基本的绘图功能,如画线.方块.椭圆等等,高级功能包括了多边形和Bezier的绘制.这样app就不用关心那些图形学的细节了,有点类似于UNIX上的X- ...

  9. 【MFC】MFC绘图不闪烁——双缓冲技术

    MFC绘图不闪烁——双缓冲技术[转] 2010-04-30 09:33:33|  分类: VC|举报|字号 订阅 [转自:http://blog.163.com/yuanlong_zheng@126/ ...

随机推荐

  1. Understanding Linux /proc/cpuinfo

    http://www.richweb.com/cpu_info A hyperthreaded processor has the same number of function units as a ...

  2. 使用response实现文件的下载

    package cn.itcast.request; import java.io.FileInputStream;import java.io.IOException;import java.io. ...

  3. POJ 1006 中国剩余定理

    #include <cstdio> int main() { // freopen("in.txt","r",stdin); ; while(sca ...

  4. mysql 导出慢

    转: 导出 mysqldump -uroot -p discuz  -e --max_allowed_packet=1048576 --net_buffer_length=16384 > dis ...

  5. Timer和TimerTask的用法

    最近在做java课程设计的时候,我用到了timer,于是学习了一下timer的用法. java实现多线程比较常用的两种方法,一种是直接继承Thread类,另一种则是实现Runnable接口.Timer ...

  6. JSP知识点汇总

    有几种方法可以实现服务器内部跳转? 使用request对象提供的方法:request.getRequestDispatcher(String URI).forward(ServletRequest r ...

  7. Asm.Def点大兵

    syzoj上的题,收货很多,orz天天学长 原题: Asm.Def奉命组建一支m人的特种作战小队前往圣迭戈.他有n名候选人,可以在其中任意挑选.由于小队中每个人都有独特的作用,所以次序不同的两种选法被 ...

  8. phpstorm内网远程debug

    家里用的是广电的网络,接了无线路由,在192.168.1.1里面配置了端口转发,将9001转发到192.168.1.101 ,然后在php.ini中相应的配置了xdebug, phpstorm 启动监 ...

  9. 网易开发工程师编程题 比较重量 Java

    比较重量 小明陪小红去看钻石,他们从一堆钻石中随机抽取两颗并比较她们的重量.这些钻石的重量各不相同.在他们们比较了一段时间后,它们看中了两颗钻石g1和g2.现在请你根据之前比较的信息判断这两颗钻石的哪 ...

  10. IE中Keep-Alive机制引起的错误

    我们知道Http协议是基于TCP/IP连接的,也就是说客户端浏览器向服务器发出一个Http请求并得到响应是要建立一条TCP/IP连接的,但是如果每发出一个Http请求客户端就要向服务器端建立一条TCP ...