请问如何提高绘图速度。

zpj888 2005-08-26 09:00:57
我所做的是一个多文档程序,定时重绘的。在OnDraw中有大量的图形等要绘制,为了防止闪烁,使用了双缓存技术(其实我不知道算不算),就是先在内存中构造上下文,并进行相关影象模式等的设置,图绘制完成后,再用BitBlt拷贝入当前设备上下文中。
双缓存的使用等代码可以保证没问题,程序也一直运行正常,就是绘图时间太久,绘制并填充1000个矩形一次要近800ms,造成人机交互界面响应速度极慢。请问有什么办法可以提高绘图速度?

说明:绘图用的是GDI+,我测试过如果只填充不运行DrawRectangle可以提高4倍速度,但是如果是绘制1000条直线就又不行了。测试用的机器性能很差,提高机器性能也能一定程度的提高绘图速度。
...全文
1225 20 打赏 收藏 转发到动态 举报
写回复
用AI写文章
20 条回复
切换为时间正序
请发表友善的回复…
发表回复
zpj888 2005-08-31
  • 打赏
  • 举报
回复
好吧,谢谢各位了
yuanss71 2005-08-31
  • 打赏
  • 举报
回复
使用 direct3d或者opengl 这样你就可以使用硬件加速
寻开心 2005-08-30
  • 打赏
  • 举报
回复
关于空间相关的组织结构其实也很简单

你不是有很多图吗,他们在空间当中都有自己的位置,那么所有的图在一起就有一个空间的占位以及相互之间的位置关系,就好比中国地图一样,每个省都有一个自己的区域
比如:可以把整个地图网格化,标定每个网格节点上对应那个省
这种网格化的管理就是一种空间相关的数据结构管理

你每次显示的不是整个地图,显示的只是地图上的一个小部分
所以,你可以确定显示的网格范围是从哪里到哪里,确定那些网格点要显示
然后从网格点找到要显示的省(图),只绘制这些图
由于每次绘制的只是地图的一个部分,其他的和当前可见的网格点无关的图都不用绘制,这样就是通过减少绘制图的数目来提供绘制的速度的办法。

网格划分这仅仅是空间组织的一种方法,其他类似的还有bsp,四叉树之类的等等

这个概念在游戏当中经常使用——场景的组织结构
zpj888 2005-08-30
  • 打赏
  • 举报
回复
是不是这样的,比如我把视图区域划分为80个矩形块,如果某个矩形要重绘,就找出其占有的所有矩形块,然后找出跟这几个矩形块连通的图形,再找出所有这些连通图的矩形块,再找出相连矩形。。。

这样下去,可能会大部分图都连上了吧,而区域划的块如果过多,是不是又影象了填充速度?
zpj888 2005-08-30
  • 打赏
  • 举报
回复
那就是说每张图都要计算出一个网格结构并保存下来了
绘制时找出有动画相连的图,根据网格点计算区域,只填充需要重绘部分的背景?然后绘图?
ss3295 2005-08-30
  • 打赏
  • 举报
回复
ding
Daric 2005-08-30
  • 打赏
  • 举报
回复
1、在绘图前先判断,在下层会被覆盖掉的rect可以不画
2、用多线程来draw,也就是在主线程外create一个工作线程,要开始draw时,由UI来post一个message给工作线程,通知工作线程开始draw,这样UI的响应就不会受绘图时间影响。
wshcdr 2005-08-29
  • 打赏
  • 举报
回复
这么多绘制工作……用direct3d或者opengl算了……

///////////////////////////////
用direct3d或者opengl有这个效果?

^_^
zpj888 2005-08-29
  • 打赏
  • 举报
回复
程序中已经使用了GetClipBox只填充这一部分的背景。happy__888说的空间管理我还是不太了解,比如我一个图形在运动,跟他有交叉关系的可能有很多图,包括直线等,那么这些直线怎么办?
寻开心 2005-08-29
  • 打赏
  • 举报
回复
更本的问题还要从算法上考虑,最快的绘制就是什么都不绘制,减少绘制量是从更本上解决问题的关键
方法一:也许你的那么多内容并不一定要每次都重新绘制,能否从原来的图片上重新生成新的图片呢
方法二:对于你的那么多的要绘制的内容提供一个空间位置姓管的数据结构加以管理,根据可视范围快速跳过一些对象的绘制

单纯绘制的角度来说,DDraw等直接操纵显示内存的方法是可以快一些,但是在使用上要比GDI麻烦一些

先看看有无从数据管理的角度解决问题的方法,这个是最根本的
alphapaopao 2005-08-29
  • 打赏
  • 举报
回复
当然有
zpj888 2005-08-28
  • 打赏
  • 举报
回复
去掉在裁剪区外的绘图过程。可以先用pDC->GetClipBox()得到裁剪区,然后在绘图时判断你的图形是否在这个区内,如果在就画,不在就不画。

就是说在OnDraw中每次判断矩形是否在裁剪区外?
例如我定时在同一个位置绘制1000个一样的矩形,绘制函数在OnDraw中。
graphics.DrawRectangle( &pen, rect );

那么每次绘图时rect是否总在裁减区外的?如果我窗口无动画并且不拖动窗口。
mynamelj 2005-08-28
  • 打赏
  • 举报
回复
MFC如何高效地绘图

[ 作者: TouchMe 添加时间: 2001-12-25 8:21:34 ]



显示图形如何避免闪烁,如何提高显示效率是问得比较多的问题。
而且多数人认为MFC的绘图函数效率很低,总是想寻求其它的解决方案。
MFC的绘图效率的确不高但也不差,而且它的绘图函数使用非常简单,
只要使用方法得当,再加上一些技巧,用MFC可以得到效率很高的绘图程序。
我想就我长期(呵呵当然也只有2年多)使用MFC绘图的经验谈谈
我的一些观点。

1、显示的图形为什么会闪烁?
我们的绘图过程大多放在OnDraw或者OnPaint函数中,OnDraw在进行屏
幕显示时是由OnPaint进行调用的。当窗口由于任何原因需要重绘时,
总是先用背景色将显示区清除,然后才调用OnPaint,而背景色往往与绘图内容
反差很大,这样在短时间内背景色与显示图形的交替出现,使得显示窗口看起来
在闪。如果将背景刷设置成NULL,这样无论怎样重绘图形都不会闪了。
当然,这样做会使得窗口的显示乱成一团,因为重绘时没有背景色对原来
绘制的图形进行清除,而又叠加上了新的图形。
有的人会说,闪烁是因为绘图的速度太慢或者显示的图形太复杂造成的,
其实这样说并不对,绘图的显示速度对闪烁的影响不是根本性的。
例如在OnDraw(CDC *pDC)中这样写:
pDC->MoveTo(0,0);
pDC->LineTo(100,100);
这个绘图过程应该是非常简单、非常快了吧,但是拉动窗口变化时还是会看见
闪烁。其实从道理上讲,画图的过程越复杂越慢闪烁应该越少,因为绘图用的
时间与用背景清除屏幕所花的时间的比例越大人对闪烁的感觉会越不明显。
比如:清楚屏幕时间为1s绘图时间也是为1s,这样在10s内的连续重画中就要闪
烁5次;如果清楚屏幕时间为1s不变,而绘图时间为9s,这样10s内的连续重画
只会闪烁一次。这个也可以试验,在OnDraw(CDC *pDC)中这样写:
for(int i=0;i<100000;i++)
{
pDC->MoveTo(0,i);
pDC->LineTo(1000,i);
}
呵呵,程序有点变态,但是能说明问题。
说到这里可能又有人要说了,为什么一个简单图形看起来没有复杂图形那么
闪呢?这是因为复杂图形占的面积大,重画时造成的反差比较大,所以感觉上要
闪得厉害一些,但是闪烁频率要低。
那为什么动画的重画频率高,而看起来却不闪?这里,我就要再次强调了,
闪烁是什么?闪烁就是反差,反差越大,闪烁越厉害。因为动画的连续两个帧之间
的差异很小所以看起来不闪。如果不信,可以在动画的每一帧中间加一张纯白的帧,
不闪才怪呢。


2、如何避免闪烁
在知道图形显示闪烁的原因之后,对症下药就好办了。首先当然是去掉MFC
提供的背景绘制过程了。实现的方法很多,
* 可以在窗口形成时给窗口的注册类的背景刷付NULL
* 也可以在形成以后修改背景
static CBrush brush(RGB(255,0,0));
SetClassLong(this->m_hWnd,GCL_HBRBACKGROUND,(LONG)(HBRUSH)brush);
* 要简单也可以重载OnEraseBkgnd(CDC* pDC)直接返回TRUE
这样背景没有了,结果图形显示的确不闪了,但是显示也象前面所说的一样,
变得一团乱。怎么办?这就要用到双缓存的方法了。双缓冲就是除了在屏幕上有
图形进行显示以外,在内存中也有图形在绘制。我们可以把要显示的图形先在内存中
绘制好,然后再一次性的将内存中的图形按照一个点一个点地覆盖到屏幕上去(这个
过程非常快,因为是非常规整的内存拷贝)。这样在内存中绘图时,随便用什么反差
大的背景色进行清除都不会闪,因为看不见。当贴到屏幕上时,因为内存中最终的图形
与屏幕显示图形差别很小(如果没有运动,当然就没有差别),这样看起来就不会闪。


3、如何实现双缓冲
首先给出实现的程序,然后再解释,同样是在OnDraw(CDC *pDC)中:

CDC MemDC; //首先定义一个显示设备对象
CBitmap MemBitmap;//定义一个位图对象

//随后建立与屏幕显示兼容的内存显示设备
MemDC.CreateCompatibleDC(NULL);
//这时还不能绘图,因为没有地方画 ^_^
//下面建立一个与屏幕显示兼容的位图,至于位图的大小嘛,可以用窗口的大小
MemBitmap.CreateCompatibleBitmap(pDC,nWidth,nHeight);

//将位图选入到内存显示设备中
//只有选入了位图的内存显示设备才有地方绘图,画到指定的位图上
CBitmap *pOldBit=MemDC.SelectObject(&MemBitmap);

//先用背景色将位图清除干净,这里我用的是白色作为背景
//你也可以用自己应该用的颜色
MemDC.FillSolidRect(0,0,nWidth,nHeight,RGB(255,255,255));

//绘图
MemDC.MoveTo(……);
MemDC.LineTo(……);

//将内存中的图拷贝到屏幕上进行显示
pDC->BitBlt(0,0,nWidth,nHeight,&MemDC,0,0,SRCCOPY);

//绘图完成后的清理
MemBitmap.DeleteObject();
MemDC.DeleteDC();

上面的注释应该很详尽了,废话就不多说了。


4、如何提高绘图的效率
我主要做的是电力系统的网络图形的CAD软件,在一个窗口中往往要显示成千上万个电力元件,而每个元件又是由点、线、圆等基本图形构成。如果真要在一次重绘过程重画这么多元件,可想而知这个过程是非常漫长的。如果加上了图形的浏览功能,鼠标拖动图形滚动时需要进行大量的重绘,速度会慢得让用户将无法忍受。怎么办?只有再研究研究MFC的绘图过程了。
实际上,在OnDraw(CDC *pDC)中绘制的图并不是所有都显示了的,例如:你
在OnDraw中画了两个矩形,在一次重绘中虽然两个矩形的绘制函数都有执行,但是很有可能只有一个显示了,这是因为MFC本身为了提高重绘的效率设置了裁剪区。裁剪区的作用就是:只有在这个区内的绘图过程才会真正有效,在区外的是无效的,即使在区外执行了绘图函数也是不会显示的。因为多数情况下窗口重绘的产生大多是因为窗口部分被遮挡或者窗口有滚动发生,改变的区域并不是整个图形而只有一小部分,这一部分需要改变的就是pDC中的裁剪区了。因为显示(往内存或者显存都叫显示)比绘图过程的计算要费时得多,有了裁剪区后显示的就只是应该显示的部分,大大提高了显示效率。但是这个裁剪区是MFC设置的,它已经为我们提高了显示效率,在进行复杂图形的绘制时如何进一步提高效率呢?那就只有去掉在裁剪区外的绘图过程了。可以先用pDC->GetClipBox()得到裁剪区,然后在绘图时判断你的图形是否在这个区内,如果在就画,不在就不画。
如果你的绘图过程不复杂,这样做可能对你的绘图效率不会有提高。
zpj888 2005-08-28
  • 打赏
  • 举报
回复
有没有更多的建议啊,谢谢了
请问各位大虾是不是有什么双缓存的方法可以进一步提高绘图速度的
alphapaopao 2005-08-27
  • 打赏
  • 举报
回复
algorithms are important too
蒋晟 2005-08-27
  • 打赏
  • 举报
回复
这么多绘制工作……用direct3d或者opengl算了……
zpj888 2005-08-26
  • 打赏
  • 举报
回复
在线等啊,大家都进来聊聊自己想法吧,说不定万一哪天你们也遇到这种问题
zpj888 2005-08-26
  • 打赏
  • 举报
回复
大家多来讨论讨论啊,只要有道理的我都会尽量给分的
或者不用双缓存,有没其他的办法绘图能使屏幕不闪烁而且所有的画面能一起出来
zpj888 2005-08-26
  • 打赏
  • 举报
回复
这个问题考虑过,页面的刷新是由数据的定时更新来驱动。但由于要保证图与图之间的的层的关系,这样做提高的速度不会很明显。图层是自由的,每两个图的图层都不一样
wshcdr 2005-08-26
  • 打赏
  • 举报
回复
只绘制发生了变动的部分

19,468

社区成员

发帖
与我相关
我的任务
社区描述
VC/MFC 图形处理/算法
社区管理员
  • 图形处理/算法社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧