VC双缓冲绘图

AI权益加码!Claude Code、Cursor等20+工具免费用! 购周边限时加赠Coding Plan Lite,畅享主流AI工具!学习进阶更高效! 阅读详情

最早在vchelp上发表过的一篇文章,VChelp已经上不了了,又有好多人问人关于VC双缓冲绘图方面的问题,下面我将这篇文章再重新发出来,供大家学习一下:

 

  用vc做的画图程序,当所画的图形大于屏幕时,当拖动滚动条时屏幕就会出现严重的闪烁情况,为了解决这一问题就得使用双缓冲来解决这一问题,程序产生严重的闪烁问题是因为画图过程中前后两次的画面反差很大造成的人的视觉的闪烁。因为在VC中每次在调用OnDraw时系统都是先用背景画刷将画布清除再执行画图命令,这样在你每次移动滚动条时每执行一次OnDraw就会有一个空白页,这样和你的最终结果图象之间有一个很大的反差,因而看起来闪烁,而且滚动条滚动越快闪烁越严重。你也可以将背景画刷设为NULL,这样可以解决闪烁问题,但是不能将先前的图象擦除,这样整个屏幕显得很乱。下面将利用双缓冲来解决这一问题的思路给大家作一下简单的介绍。

  我先来解释一下在mfc里面很关键的设备环境描述符,也就是所谓的DC(device context)。

还是从历史来看吧,dos时代,我们如果要绘图,必须通过一系列系统函数来启动图形环境(用过turbo pascal或者turbo c的人该还有印象吧),这之间对各种硬件的初始化参数都不相同,非常的烦人,常常还要查阅硬件手册,那时的程序智能针对最流行的硬件来编写,对不流行的就没有办法了。windows操作系统为了屏蔽不同的硬件环境,让编程时候不考虑具体的硬件差别,采取了一系列办法,设备环境描述符就是这样产生的。简单的说,设备描述符抽象了不同的硬件环境为标准环境,用户编写时使用的是这个虚拟的标准环境,而不是真实的硬件,与真实硬件打交道的工作一般交给了系统和驱动程序完成(这同样解释了为什么我们需要经常更新驱动程序的问题)。使用在windows图形系统(gdi,而不包括direct x)上面,就体现在一系列的图形DC上面,我们如果要在gdi上面绘图,就必须先得到图形DC的句柄(handle),然后指定句柄的基础上进行图形操作。

再来回忆一下,我们怎么在sdk的环境下面绘图呢,我想这个大家都不太清楚吧,但是确实很基础。在windows的sdk环境下面,我们用传统的c编写程序,在需要的绘图地方(比如响应WM_PAINT消息的分支)这样做:

hdc = GetDC( hwnd );
oldGdiObject = SelectObject( hdc,newGdiObject );
...绘图操作...
SelectObject( hdc,oldGdiObject );
DeleteObject( newGdiObject );
ReleaseDC( hdc);

或者是这样

BeginPaint( hwnd,&ps );//PAINTSTRUCT ps -- ps is a paint struct 
...绘图操作...
EndPaint( hwnd )

这就是大概的过程,我们看到了hdc(图形DC句柄)的应用,在绘图的部分,每一个绘图函数基本上也要用到这个句柄,最后我们还必须释放它,否则将严重影响性能。每次我们都必须调用GetDC这个api函数得到(不能用全局变量保存结果重复使用,我在后面解释)。这些是最最基本的windows图形操作的方式,相比dos时代简单了些,但是有些概念也难理解了些。vb里面的简单的point函数其实最后也是被转化为这样的方式来执行,系统帮助做了很多事情。

到了mfc里面,由于有了封装,所有的hdc被隐藏在对象中做为隐藏参数传递(就是DC类的this啦~~),所以我们的关键话题就转变为了怎样得到想要的DC类而已,这个过程其实大同小异的。在消息响应的过程中,WM_PAINT被转变为OnDraw(),OnPaint()一系列函数来响应,这些函数一般都有个参数CDC *pDC传入进来,因此在这些函数里面,我们就只需要直接画图就可以了,和以前sdk的方式一样。

但是WM_PAINT消息响应的频度太高了,比如最小化最大化,移动窗体,覆盖等等都引起重绘,经常的这样画图,很是消耗性能;在有些场合,比如随机作图的场合,每一次就改变,还导致了程序的无法实现。怎么解决后一种问题呢。

ms在msdn的例子里面交给我们document/view的经典解决办法,将图形的数据存储在document类里面,view类只是根据这些数据绘图。比如你要画个圆,只是将圆心和半径存在document里面,view类根据这个里面的数据在屏幕上面重新绘制。那么,我们只需要随机产生一次数据就可以了。

这样还是存在性能的问题,于是我们开始考虑另外的解决方法。我们知道,将内存中的图片原样输出到屏幕是很快的,这也是我们在dos时代经常做的事情,能不能在windows也重新利用呢?答案就是内存缓冲绘图,我们今天的主题。

我们还是回到DC上来,既然DC是绘图对象,我们也就可以自己来在内存里面造一个,让它等于我们想要的图,图(CBitmap)可以存储在document类里面,每一次刷新屏幕都只是将这个图输出到屏幕上面,每一次作图都是在内存里面绘制,保存在document的图里面,必要时还可以将图输出到外存保存。这样既保证了速度,也解决了随机的问题,在复杂作图的情况下对内存的开销也不大(总是一副图片的大小)。这是一个很好的解决办法,现在让我们来实现它们。

我们在document类里面保存一个图片

CBitmap m_bmpBuf;//这里面保存了我们做的图,存在于内存中

在view类里面我们需要将这个图拷贝到屏幕上去
位于OnDraw(CDC *pDC)函数中:

CDC dcMem;//以下是输出位图的标准操作
CBitmap *pOldBitmap = NULL;
dcMem.CreateCompatibleDC(NULL);
pOldBitmap = dcMem.SelectObject(&pDoc->m_bmpBuf);
BITMAP bmpinfo;
pDoc->m_bmpBuf.GetBitmap(&bmpinfo);
pDC->BitBlt(0,0,bmpinfo.bmWidth,bmpinfo.bmHeight,&dcMem,0,0,SRCCOPY);
dcMem.SelectObject(pOldBitmap);
dcMem.DeleteDC();

在我们需要画图的函数里面,我们完成绘图工作

CBmpDrawDoc *pDoc = GetDocument(); //得到document中的bitmap对象 
CDC *pDC = GetDC();
CDC dcMem;
dcMem.CreateCompatibleDC(NULL);//这里我们就在内存中虚拟建造了DC
pDoc->m_bmpBuf.DeleteObject();
pDoc->m_bmpBuf.CreateCompatibleBitmap(pDC,100,100);//依附DC创建bitmap
CBitmap *pOldBitmap = dcMem.SelectObject(&pDoc->m_bmpBuf);//我们调入了我们bitmap目标

dcMem.FillSolidRect(0,0,100,100,RGB(255,255,255));//这些时绘图操作,随便你^_^
dcMem.TextOut(0,0,"Hello,world!");
dcMem.Rectangle(20,20,40,40);
dcMem.FillSolidRect(40,40,50,50,RGB(255,0,0));

pDC->BitBlt(0,0,100,100,&dcMem,0,0,SRCCOPY);//第一次拷贝到屏幕
dcMem.SelectObject(pOldBitmap);
dcMem.DeleteDC();


全部的过程就是这样,很简单吧。以此为例子还可以实现2个缓冲或者多个缓冲等等,视具体情况而定。当然在缓冲区还可以实现很多高级的图形操作,比如透明,合成等等,取决于具体的算法,需要对内存直接操作(其实就是当年dos怎么做,现在还怎么做)。

再来解释一下前面说的为什么不能用全局变量保存DC问题。其实DC也是用句柄来标识的,所以也具有句柄的不确定性,就是只能随用随取,不同时间两次取得的是不同的(使用过文件句柄地话,应该很容易理解的)。那么我们用全局变量保存的DC就没什么意义了,下次使用只是什么也画不出来。(这一点的理解可以这样:DC需要占用一定的内存,那么在频繁的页面调度中,位置难免改变,于是用来标志指针的句柄也就不同了)。

delphi 缓冲图(内存图)解决图闪烁问题 很多朋友在做绘图程序的时候,往往出现屏幕不停刷新产生闪烁的问题,这里就告诉大家一个解决办法,缓冲绘图。如果有人是用取反图解决这个问题,那么在直线的时候容易出现斑点,效果不是很好。如果是图片很大,那么缓冲图是解决的好办法。在网上搜索一下内存绘图,或者缓冲绘图,会有很多技术文章,不过都是以vc或者java为主,很少有pascal的代码例子。不过原理都是一样的,大家可以参考他们的文章。我会在最 阅读详情

相关推荐

Delphi Canvas绘图时发生闪烁的解决办法

与Invalidate的区别:Canvas.Refresh 会强制立即重绘,而 Invalidate 方法会标记控件为“需要重绘”,但不会立即执行重绘操作。总的来说,Canvas.Refresh 是一个可以在特定情况下使用的工具,但要谨慎使用以避免出现不必要的闪烁问题。手动触发重绘:通常情况下,控件会在需要重绘时自动调用 OnPaint 事件,但是如果你希望在特定时刻手动触发重绘,可以使用 Canvas.Refresh。组件在绘图时可能会出现闪烁的问题,这通常是由于重绘频率过高或者未使用双缓冲技术导致的。

xiaobin_zb的博客 1474

Delphi组件编程 我用TImage作了一个Edit,即:把TImage变成TEdit

主题:使文本框具有背景图,还有特效文字 说明:我用TImage作了一个Edit,即:把TImage变成TEdit, 这样一来,Edit也能变成透明的。 在文本框的下面是一个新的TEdit,可以在其中输入文字,这个新的文本框是用图像控件制作的。 核心技术:Edit特效,EditBmp(),SetBounds(),LoadFromFile(),mbleft=button Google搜索:TEdit with background graphic, Background-image of TEdit

VC中通过CxImage进行双缓冲绘图

<br />// 返回位图<br />HBITMAP YourClass::GetBitMap(CxImage& cxImage)<br />{<br /> HDC dc = ::GetDC(NULL);<br /> HBITMAP hBitMap = cxImage.MakeBitmap(dc);<br /> ::ReleaseDC(NULL, dc);<br /> return hBitMap;<br />}<br /><br />// 实际绘制<br />void YourClass::PaintBi

requestImage的专栏 2910

delphi 防止控件闪烁

//锁住窗口 防止闪烁 procedure LockWindowByHWND(h:THandle;b:Boolean); procedure TFrmBase.LockWindowByHWND(h: THandle; b: Boolean); begin if h>0 then begin if b then begin SendMessage(h,WM_SETREDRAW,1,0); ReDrawWindow(h,n...

全粘工程师 1238

如何解决delphi xe8编译大程序时内存不够的问题。

Delphi xe8在编译一个大程序的时候,会出现一个内存不够的错误,有时候是以Internal Error的形式出现,有时候是以Out of Memory的形式出现。主要原因是IDE调用各种资源占用了大量内存资源,再以DLL方式调入编译器的时候,就会出现内存不够的情况。 要解决这个问题,需要采用进程外编译的方式,就是让编译器以单独进程的方式运行,而不是以DLL的方式调入IDE的内存空间

A00553344的专栏 1872

Delphi 通过窗口句柄 加粗窗口边框、描边

Delphi 通过窗口句柄 加粗窗口边框、描边

心存敬畏 803

VC双缓冲绘图技术介绍

VC双缓冲绘图技术介绍     双缓冲绘图,它是一种基本的图形图像绘图技术。首先,它在内存中创建一个与屏幕绘图区域一致的对象,然后将图形绘制到内存中的这个对象上,最后把这个对象上的图形数据一次性地拷贝并显示到屏幕上。这种技术能够大大地提高绘图的速度,减少卡顿和闪屏的问题。 我们为什么要使用双缓冲技术来进行绘图?     在应用程序开发中,当图像信息数据量很大时,绘图可能需要几秒钟甚至更长的时间

沉森心与思考同行 9105

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

*******************************************************************例子*******************************************************************   ********************所有的GDI绘图函数使用的都是逻辑坐标(逻辑范围)**************

scollins的专栏 4717

VC 双缓冲绘图

原文地址::http://www.cppblog.com/erran/archive/2006/11/25/15650.html       相关网帖 1.VC 双缓冲技术 ----http://blog.csdn.net/wooden_men/article/details/5662305 VC 双缓冲绘图   BOOL CGraphDlg::OnDraw(CDC* pD

xqhrs232的专栏 3393

VC中的双缓冲绘图技术

之前在做图形绘制的时候,发现在拖动图形时候,会出现闪烁的情况,后来就上网找了一下双缓冲绘图,本文非原创,只是想保存下来,以后要用的时候不用再到处去搜,也希望能帮助有这方面困惑的朋友 原文来自http://www.cppblog.com/wrhwww/archive/2011/03/01/140913.html VC 双缓冲绘图图形图象处理编程过

天上的猩猩的专栏 1063

VC绘图中的双缓冲技术

VC绘图中的双缓冲技术 转自:VC 绘图,使用双缓冲技术实现  ********************所有的GDI绘图函数使用的都是逻辑坐标(逻辑范围)******************* *******************系统默认情况下 物理范围和逻辑范围 是1:1 的对应关系******************* 1. 首先定义类成员: CDC *m_pD

歌行梅村的专栏 1064

VC GDI双缓冲绘图

VC双缓冲绘图实现

架构师之路,举步维艰。 1144

vc 双缓冲绘图

当然你可以直接搜索到能用的代码,并且基本能满足要求。不过这样总不是学习的态度。本着学习分享的态度,现做一些基本的分析吧。 在MSDN上知道,我们图的对象都是窗口的DC,WINDOWS绘图更新时,总是用背景色先填充这个区域,然后才是我们的绘图代码, 这就是说,如果我们绘图的代码与背景色差别较大,不管我们更新速度多快,总会有种闪烁的感觉。 要想避免,通常的做法都是双缓冲了, 具体代码上来了。 vo...

haodawei123的博客 312
上一篇: 通过Post方式访问Web页面
下一篇: 文本的全文内容比对
xsc2001
博客等级 码龄25年 255粉丝 29原创
评论 6
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值