几天前,我终于克服了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. POI给Excel添加数字筛选

    HSSFSheet sheet = workbook.createSheet("list"); CellRangeAddress ce = CellRangeAddress.val ...

  2. 【NOIP2012】借教室

    因为本校OJ+1s所以用线段树水过了,不去syz的水库水这题还真不知道线段树过不了= = 原题: 在大学期间,经常需要租借教室.大到院系举办活动,小到学习小组自习讨论,都需要 向学校申请借教室.教室的 ...

  3. HSDB

    select t from java.lang.Thread t $JAVA_HOME/bin/java -classpath $JAVA_HOME/lib/sa-jdi.jar sun.jvm.ho ...

  4. <<精益创业>>读书笔记

    不要以严格地职能部门来组成公司,而是要以人们在各自专长的领域做出表现,组建跨部门的团队 在普通的管理中,如果无法实现目标,要么是计划不足,要么是技术不足 这点我感触比较深,以前在老东家的时间,刚开始是 ...

  5. 手机端的click

    一:click事件的封装 手机端的click事件不是太好用, 1.点击会有延迟, 2.会出现一个灰色区域 就想到了用touch事件代替. touch事件有touchstart,touchmove,to ...

  6. logstash 因为jdk版本不对造成索引时间戳失败

    版本 logstash 1.5.0 RC2 JDK 1.7.15 Logstash推荐使用JDK 1.7.75 每次启动都是会有部分的数据无法替换日志中的时间戳,导致日志时间戳被认定为当前读取时间. ...

  7. 玩转单元测试之DBUnit

    DBunit 是一种扩展于JUnit的数据库驱动测试框架,它使数据库在测试过程之间处于一种已知状态,如果一个测试用例对数据库造成了破坏性影响,它可以帮助避免造成后面的测试失败或者给出错误结果. 虽然不 ...

  8. poj 3083 Children of the Candy Corn

    点击打开链接 Children of the Candy Corn Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 8288 ...

  9. No.6__C#

    第六周 周一:今天特别开心,因为来公司的第一个任务完成了,虽然是在组长的帮助下完成的.但是,还是有很多收获,在实际工作中遇到的问题和麻烦远远超出了书本知识 有些问题简直让人抓狂.现在,上班空余期间,也 ...

  10. (转载)SQL Server 2005 如何启用xp_cmdshell组件

    原文地址:http://www.cnblogs.com/atree/p/SQL_SERVER_xp_cmdshell.html [错误描述]: SQL Server阻止了对组件‘xp_cmdshell ...