几天前,我终于克服了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. unix exec族函数 关于参数的疑惑

    问题不出在这几个函数,而在于看后文解释器的时候发现一个很奇妙的问题. #include <unistd.h> int execl(const char *pathname, const c ...

  2. SIFT特征提取分析(转载)

    转载自: http://blog.csdn.net/abcjennifer/article/details/7639681 SIFT(Scale-invariant feature transform ...

  3. ural 1057Amount of Degrees ——数位DP

    link:http://acm.timus.ru/problem.aspx?space=1&num=1057 论文: 浅谈数位类统计问题  刘聪 #include <iostream&g ...

  4. linux和window下mkdir函数问题(转-锦曦月)

    通过WIN32宏进行判断   window下mkdir函数   #include<direct.h> int _mkdir( const char *dirname );   linux下 ...

  5. date_default_timezone_set()设置时区

    <?php echo function_exists(date_default_timezone_set)."<br>";//在这他总是返回1,这函数是判断这里面 ...

  6. kuangbin_SegTree E (HDU 1698)

    POJ服务器炸了 还没好呢 然后就只能跳掉一些题目了 这题也是成段更新模板题 本来lazy标记不是很明白 后来学长上课讲了一下就知道原理了 回去看看代码很容易就理解了 #include <cst ...

  7. strcat函数的使用需要注意的问题

    曾被这个函数困扰了好久,然后各种假设,验证:但是最后却发现这个函数并没有什么好讲的,原来的过错一切都源于忽略了“*dst去掉\0,然后加上*src,最后返回*dst”这句话的真正含义:给*dst分配的 ...

  8. 034. asp.netWeb用户控件之三通过用户控件实现用户注册和登录

    用户控件login.ascx代码: <%@ Control Language="C#" AutoEventWireup="true" CodeFile=& ...

  9. 【freemaker】之判断是否为空,表达式的使用

    测试代码 @Test public void test05(){ try { freemakerUtil.fprint(root, "05.ftl",fn+"05.htm ...

  10. sql 下,float和numeric

    搜了下两者的区别,答案其实很明显: numeric支持最长38位长度的数字,只有在你要的数字的长度超过38位时才推荐使用float 原因是,float在保存和提取,以及数学计算等方面,会产生于期待记过 ...