关于内存绘图的效率

keios 2005-11-17 02:21:06
我们做的项目中,需要绘制大量的二维多边形(其中矩形占大多数),需要处理是上百万的数量级。
目前使用 GDI 在内存中绘图,之后再显示到屏幕,在显示复杂图形时界面刷新很慢,明显达不到要求。
请问对于这样的二维内存绘图算法,是否有比 GDI 更块的图形库?
请大侠推荐一下,谢谢!
...全文
515 24 打赏 收藏 转发到动态 举报
写回复
用AI写文章
24 条回复
切换为时间正序
请发表友善的回复…
发表回复
teli_eurydice 2005-12-14
  • 打赏
  • 举报
回复
howard 2005-12-14
  • 打赏
  • 举报
回复
http://community.csdn.net/Expert/topic/4439/4439152.xml?temp=.3963892

楼下,还有楼上到楼主那个高手帮我个忙,去看看这个问题吧。谢谢了,我好着急啊。自己水平又不高,只能依靠你们了!谢谢了!!1
pclili 2005-12-13
  • 打赏
  • 举报
回复
关注一下.正在做类似的.
teli_eurydice 2005-12-13
  • 打赏
  • 举报
回复
up ,我也正在郁闷呢,矩形没有那么多,但是很多点要画线,还要不同的区域不同的填充,平时还好,但是拖动滚动条就很明显的慢了
goodboyws 2005-12-13
  • 打赏
  • 举报
回复
你可以试试这样的效率
int maxRects = rectList->size();
HANDLE hData = GlobalAlloc(GMEM_MOVEABLE, sizeof(RGNDATAHEADER) + (sizeof(RECT) * maxRects));
RGNDATA *pData = (RGNDATA *)GlobalLock(hData);
pData->rdh.dwSize = sizeof(RGNDATAHEADER);
pData->rdh.iType = RDH_RECTANGLES;
pData->rdh.nCount = pData->rdh.nRgnSize = 0;
SetRect(&pData->rdh.rcBound, MAXLONG, MAXLONG, 0, 0);
RECT *pr = (RECT *)&pData->Buffer;
for ( ;it != rectList->end(); it ++ )
{
pr[pData->rdh.nCount] = *it;
if (pr[pData->rdh.nCount].left < pData->rdh.rcBound.left) pData->rdh.rcBound.left = pr[pData->rdh.nCount].left ;
if (pr[pData->rdh.nCount].top < pData->rdh.rcBound.top) pData->rdh.rcBound.top = pr[pData->rdh.nCount].top;
if (pr[pData->rdh.nCount].right > pData->rdh.rcBound.right) pData->rdh.rcBound.right = pr[pData->rdh.nCount].right;
if (pr[pData->rdh.nCount].bottom+1 > pData->rdh.rcBound.bottom)
pData->rdh.rcBound.bottom = pr[pData->rdh.nCount].bottom+1;
pData->rdh.nCount++;
}
HRGN h = ExtCreateRegion(NULL, sizeof(RGNDATAHEADER) + (sizeof(RECT) * maxRects), pData);

goodboyws 2005-12-13
  • 打赏
  • 举报
回复
合并矩形应该用ExtCreateRegion, 你这么测试当然慢,因为没有任何优化
keios 2005-12-13
  • 打赏
  • 举报
回复
to goodboyws(深夜不眠者)
我的测试结果:
10000 个矩形,一个一个画 vs 合并rgn再画
绘制一遍分别需要时间 0.016 秒 / 0.515 秒

我的代码:
void Cgdi_drawView::testDraw(CDC* pDC, RectList* rectList)
{
COLORREF oldBackColor = pDC->SetBkColor(m_back_color);
int oldBkMode = pDC->SetBkMode(TRANSPARENT);
int oldDrawMode = pDC->SetROP2(R2_MERGEPEN);

CPen pen(PS_SOLID, 1, m_rect_edge_color);
CBrush brush(m_rect_fill_color);

CPen* oldPen = pDC->SelectObject(&pen);
CBrush* oldBrush = pDC->SelectObject(&brush);

for ( RectList::iterator it = rectList->begin();
it != rectList->end();
it ++ )
{
pDC->Rectangle(&(*it));
}

pDC->SelectObject(oldPen);
pDC->SelectObject(oldBrush);
pDC->SetROP2(oldDrawMode);
pDC->SetBkColor(oldBackColor);
pDC->SetBkMode(oldBkMode);
}

void Cgdi_drawView::testDraw_rgn(CDC* pDC, RectList* rectList)
{
if (rectList->empty())
return;

COLORREF oldBackColor = pDC->SetBkColor(m_back_color);
int oldBkMode = pDC->SetBkMode(TRANSPARENT);
int oldDrawMode = pDC->SetROP2(R2_MERGEPEN);

CPen pen(PS_SOLID, 1, m_rect_edge_color);
CBrush brush(m_rect_fill_color);

CPen* oldPen = pDC->SelectObject(&pen);
CBrush* oldBrush = pDC->SelectObject(&brush);

// combine rects
RectList::iterator it = rectList->begin();
CRgn totalRgn;
totalRgn.CreateRectRgnIndirect(&*it);
it ++;

for ( ;
it != rectList->end();
it ++ )
{
CRgn rectRgn;
rectRgn.CreateRectRgnIndirect(&*it);
totalRgn.CombineRgn(&totalRgn, &rectRgn, RGN_OR);
rectRgn.DeleteObject();
}
pDC->PaintRgn(&totalRgn);
totalRgn.DeleteObject();

pDC->SelectObject(oldPen);
pDC->SelectObject(oldBrush);
pDC->SetROP2(oldDrawMode);
pDC->SetBkColor(oldBackColor);
pDC->SetBkMode(oldBkMode);
}

另外我还比较了 GDI/GDI+ ,测试结果表明 GDI 直接一个一个绘制矩形是最快的方法,GDI+ 绘制非常慢,而且Gdiplus::Region 存在 bug ,超过 10万个矩形合并后会 crash .
keios 2005-12-13
  • 打赏
  • 举报
回复
to goodboyws(深夜不眠者)
谢谢你的代码,长知识了。

现在的测试结果是这样:
一个一个画 vs 合并rgn再画
1 万个矩形,0.016 秒 / 0.047 秒
10 万个矩形,1.688 秒 / 0.453 秒

10 万个基本是白茫茫一片了,我觉得合并的效率可能也就在这种状态下才能体现出来。
不过有一点比较奇怪,一个个的画,按道理时间和矩形个数应该是接近线性关系,但是1万到10万时间居然增加100倍!不知道该如何解释
keios 2005-11-25
  • 打赏
  • 举报
回复
to dawndu(东南飞) & Tan18(阿强)
多谢两位,我们的系统是有针对位置进行裁减的,使用的也是类似的树结构,所以屏幕外的东西是不会绘制的。
另外小于一个象素的东西也是不绘制的,但他大于1象素的东西是一定要绘制的。所以绘图效率还是挺重要的。
我准备有时间再使用1万个矩形测试一下效率,届时会告诉大家我测试的结果。
Tan18 2005-11-21
  • 打赏
  • 举报
回复
我不知道你是不是做GIS项目?
上面的朋友都提到了,裁剪是非常重要的一步,
在GIS中基本采用四叉树来切分数据窗口,然后根据当前屏幕坐标转换到数据坐标空间,
利用四叉树的搜索算法,很快可以找到需要绘制的对象。
只绘制需要绘制的对象速度绝对快很多。
另外当视图缩小时,当前屏幕内显示所有的对象,这些有很多对象范围可能已经小于一个屏幕像素,这些对象就不必再绘制了,你还要以调整该值,如小于4个像素就不再绘制,速度会提高很多。
楼上的朋友说得不错,GDI+是要比GDI慢一些。但真正的原因还是在算法上,你看看CAD就知道了。CAD2004就是用的GDI+。
中级伴读 2005-11-21
  • 打赏
  • 举报
回复
一定要做裁剪
su5369 2005-11-21
  • 打赏
  • 举报
回复
标记
dawndu 2005-11-19
  • 打赏
  • 举报
回复
因为GDI+有很多高级的操作,所以实际上很多操作实际上是比GDI慢的
从速度上来将GDI是中规中矩的
其实瓶颈有2个
一个是你绘图算法的问题,比如你遍历一个链表来画这个图形,显然是比较慢的,还有本来应该画在屏幕外的图形你不去裁剪,这是最主要的瓶颈
第二个就是当你在内存中画好后,从system memory贴到显存上去,如果机器不好或系统实现的不完善,这个也是一个瓶颈。但是这个问题在现在的机器上不是问题,还是要看你个人的算法。
说到最好还是你要优化你的算法,裁剪图形。举个例子,我有2个矩形,一个在屏幕内,一个在屏幕外,如果你都画出来,画的过程需要的时间加上这2个矩形图形杯贴到显存的时间就是整个代价;而你判断一下那个矩形该画,哪个不该画只需要一个判断语句,在汇编中就是一个jmp,这2个操作中前面的需要的时间估计是后面判断语句的几百到几千倍
如果你的算法足够好还是不行,那就只能自己操作显存了,directx等都可以,不过一般不需要,90%是自己的算法问题,像游戏中要贴很大的图,就没有办法了,只能换directx
goodboyws 2005-11-18
  • 打赏
  • 举报
回复
一副位图的区域比你的要复杂的多,填充也是很快的
goodboyws 2005-11-18
  • 打赏
  • 举报
回复
填充模式绘制两个矩形 vs 填充模式绘制同样形状的多边形

呵呵,两个矩形么,当然没有影响,你试试1000个
菜牛 2005-11-18
  • 打赏
  • 举报
回复
我试了一下,绘制10000个随机矩形,画刷为空,耗时15-31毫秒,基本是全黑了;画刷默认,耗时600毫秒左右,也就看到几十个矩形了。
菜牛 2005-11-17
  • 打赏
  • 举报
回复
我怎么觉得这个实现有点问题,几百万个矩形,在界面上显示是什么效果?应该是一片茫茫无际的色斑吧。
keios 2005-11-17
  • 打赏
  • 举报
回复
to goodboyws(深夜不眠者)
我刚做了测试,我说的例子:填充模式绘制两个矩形 vs 填充模式绘制同样形状的多边形,
分别是 3571/s 和 3816/s ,确实快一点,但这并没有加上合并矩形的算法时间。
因此我觉得合并后的总体效率并不能得到提高。


to ddmor , hchinside
我曾经看过文章,dx/opengl对3d贴图加速效果明显,但对2d效果甚微,不知道那种说法有无道理。
hchinside 2005-11-17
  • 打赏
  • 举报
回复
是啊,用dx或opengl
ddmor 2005-11-17
  • 打赏
  • 举报
回复
GDI+要比GDI绘图快一点,考虑到图形比较多,效率也高不了哪里去。你可以试试OpenGL,它的绘图接口都由硬件厂商提供,绘图速度是俺了解的最快的一个图形接口。
我倒是觉得goodboyws提到的图形合并意义不大。需要确定图形中哪些部分落在显示区之内,哪些落在显示区之外,以便只显示落在显示区内的那部分图形。这个选择过程称为裁剪。这是由绘图引擎来完成,所以所没有必要优化。而对于出现在显示区内的图形,如果不是填充的多边形,都根本没有产生遮覆,合并的工作徒增工作量。
加载更多回复(4)
包含文件说明: 1. SolveFlashingAndRedrawv1.0.5 纯净版 无闪烁的MFC应用框架,实际使用时把此工程改名成你要建立的项目名称,然后开始开发即可。你熟悉MFC的话研究这个框架的半个小时应该就明白并熟练运用了。 2.SolveFlashingAndRedrawv1.0.5 demo版 利用SolveFlashingAndRedrawv1.0.4框架写的一个示例小程序,主要展示框架要实现的优点特性。 3.VCRn 修改vc工程名工具 ___作者 田彬.exe 用网上找到的一个MFC改工程名称的小工具,很实用。如果你想使用本框架就可以用它来改成你想要的工程名了。 4. 未使用本框架的类似功能简化程序 没有使用框架的程序,实现的功能和Demo类似。但是运行之后改变窗口大小等,会发现图形闪烁很厉害! 5. SolveFlashingAndRedrawv1.0.5 demo版 运行截图.jpg 6. ReadMe.txt 说明文件。 补充说明: 工程使用vc6.0开发,如果你用vc6.0双击.dsw文件无法打开,请先打开vc6.0然后把.dsw拖动到vc上面。 如果这种方法还是无法打开,你新建一个vc6.0 mfc sdi程序,把示例中框架拷贝到这个新工程中,运行即可,代码量不是太多。 框架说明: /****************************************************** SolveFlashingAndRedraw框架说明 ******************************************************/ /** 项目名称: demo框架 版本号: v1.0.5 第一作者: Jef 地址: 中国/江苏 日期: 20100724 电子邮箱: dungeonsnd@126.com 版权: 1.您可以修改及免费使用本程序。 2.修改之后附上您的个人信息发送到上面的作者邮箱,作者负责在全面测试后发布您修改后的新版本。 3.您使用本程序而导致任何伤害以及经济损失,由过错方依法承担所有责任,一概与第一作者及合作单位无关。 4.如果您使用本程序则表示您已经同意此版本协议!否则请勿使用! 项目功能: SolveFlashingAndRedraw框架是MFC解决窗口保存及重绘闪烁问题的一种比较好的方案(Win32解决方法类似)。 版本历史: v1.0.1 20091126 第一版本 v1.0.2 20091212 第二版本 1. 修改了部分变量的名字使其更符合其意义 2. 增加为两个工程,一是带demo例子的,另一是不带demo的纯净版. 3. 修改了其中一个错误. 如 CreateCompatibleDC之后没有调用DeleteDC等. v1.0.3 对v1.0.2进行了整理 v1.0.4 20100416 在v1.0.3的基础上进行整理,并增加了裁剪区,提高了绘图效率! v1.0.5 20100724 1. 添加了一个工具类CMemBmpDc,帮助产生一个内存DC,并把指定的内存位图选进去。方便绘图。 2. 演示了在适当时机如何高效画图,见Demo版的DrawSinwave(bool bDrawOnScreen)函数。 演示了用两种方法来绘图, 方法1. 直接绘图到屏幕上, 同时绘图内存位图上,内存位图不会立即贴到屏幕上减少了内存拷贝的时间,提高了效率, 将来窗口失效时OnPait贴图到屏幕上. 这种方法的优点时减小了不必要的内存拷贝,缺点时当绘图内存复杂并且非常耗时可能会导致闪烁。 故适用于像本Demo的这样绘图(本例函数只绘制一小段直线)。 方法2. 绘制到内存位图上后把应该重绘的这一小块设成裁剪区,然后立即OnPait重绘这个裁剪区。 运行步骤: 直接运行demo里面的程序,在窗口上任意拖拉鼠标画线,然后点击菜单栏的几个示范菜单项,然后移动窗口、 改变窗口大小、最大最小化窗口、用其它窗口覆盖此窗口、鼠标放到任务栏。。。 以上种种操作观察窗口内的图像变化。可以发现窗口内图像几乎看不到闪烁,而且窗口的元素已经保存下来重绘时任然可以看到图像。 如何使用: 进行项目开发时,可以先建立项目,然后把本解决方案框架拷贝到新建项目中即可。 也可以自己根据需要修改纯净版。 其它: 友情提示,小心 View类头文件及View类的实现文件中有说明,使用时别把它弄到你实际项目里哦! 进行大量复杂的图形的输出,而且对效率要求特别高时要考虑适当修改此框架(如增加裁剪区)后再使用哦。 关于如何在此框架的基础上提高绘图效率可以参阅下面的文章 如何提高绘图效率 文章摘录 http://hi.baidu.com/new8sun/blog/item/68ccba8a80c3aadafc1f1079.html MFC双缓冲解决图象闪烁 2009-06-13 23:03 显示图形如何避免闪烁,如何提高显示效率是问得比较多的问题。而且多数人认为MFC的绘图函数效率很低,总是想寻求其它的解决方案。 MFC的绘图效率的确不高但也不差,而且它的绘图函数使用非常简单,只要使用方法得当,再加上一些技巧,用MFC可以得到效率很高的绘图程序。

19,468

社区成员

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

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