怎样在内存缓冲中画图(zz)

转载 2006年06月23日 06:27:00

关键字 内存缓冲,画图
原作者姓名 相生昌

介绍
用双缓冲进行绘图可解决在VC中绘图时的闪烁现象

正文
用vc做程序,如何画图是一个大家都很关心,但是却感到很难以理解的问题,因为在mfc的封装之下,没有现成的画图函数供你直接调用,像vb等等里面直接来个point之类的,常常让人感到无从下手。这两天帮人解决了一个用内存缓冲画图的问题,顺便也就谈谈这些东西,也算是总结。

我先来解释一下在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需要占用一定的内存,那么在频繁的页面调度中,位置难免改变,于是用来标志指针的句柄也就不同了)。

怎样在内存缓冲中画图

关键字 内存缓冲,画图原作者姓名 相生昌 介绍用双缓冲进行绘图可解决在VC中绘图时的闪烁现象 正文用vc做程序,如何画图是一个大家都很关心,但是却感到很难以理解的问题,因为在mfc的封装之下,没有现成...
  • jemmy
  • jemmy
  • 2007年06月18日 22:21
  • 1619

delphi 缓冲画图(内存画图)解决画图闪烁问题

介绍 用双缓冲进行绘图可解决在VC中绘图时的闪烁现象 正文 用vc做程序,如何画图是一个大家都很关心,但是却感到很难以理解的问题,因为在mfc的封装之下,没有现成的画图函数供你直接调用,像vb...

MATLAB画图命令zz

一、散点图 1.1.命令  plot 功能  线性二维图。在线条多于一条时,若用户没有指定使用颜色,则plot循环使用由当前坐标轴颜色顺序属性(current axes ColorOr...

采用双缓冲机制实现画图面板

这里只写出了我们自定义View DrawView.java package com.example.doublebuffering; import android.content.Context;...

采用双缓冲实现画图板

本实例要实现一个画图板,当用户在触屏上移动时,即可在屏幕上绘制任意的形状。...

双缓冲画图

Win32API方式头文件里定义://双缓存 HDC m_MemDC; HBITMAP m_hbitMap;//背景图片 HBRUSH m_bkBrush;在onpaint函数里这样写:记得把hdc换...
  • eit520
  • eit520
  • 2012年07月31日 22:43
  • 2456

逻辑坐标,物理坐标,以及双缓冲法画图方法详解

这是我用的是对话框中的PICTURE控件,其实pictures控件就是一个静态文本控件。 我这个例子是基于对话框的。不是基于单文档应用程序的。 首先我们需要搞清楚两个VC中的坐标系。 窗口坐标=...
  • lg2lh
  • lg2lh
  • 2012年03月17日 21:14
  • 2838

使用双缓冲技术实现简单画图板

所谓双缓冲技术其实很简单:当程序需要在指定的View上进行绘制时,程序并不直接绘制到该View组件上,而是先绘制到一个内存中的Bitmap图片上(缓冲)上,等到内存中的Bitmap绘制好了之后,再一次...

VC 画图原理分析(双缓冲) .

用VC做的画图程序,当所画的图形大于屏幕时,在拖动滚动条时屏幕就会出现严重的闪烁,为了解决这一问题,就得使用双缓冲来解决。程序产生严重的闪烁问题是因为画图过程中前后两次的画面反差很大造成的人的视觉的闪...

VC中的双缓冲画图

今天学习了VC中双缓冲画图,我个人理解双缓冲画图解决的问题有 1、当需要画的图比较大或者复杂的时候,在视图上直接画可能较慢,出现卡的现象 2、当在重绘的时候,视图会有我们所不期望的闪动 为了解决...
  • h_j9527
  • h_j9527
  • 2012年02月10日 22:03
  • 594
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:怎样在内存缓冲中画图(zz)
举报原因:
原因补充:

(最多只允许输入30个字)