您的位置:程序门 -> vc/mfc -> 界面



gdi+的绘图效率问题,大家讨论一下吧!


[收藏此页] [打印本页]选择字色:背景色:字体:[][][]


gdi+的绘图效率问题,大家讨论一下吧![无满意答案结贴]
发表于:2007-08-01 17:04:26 楼主
最近用gdi+绘图,初次使用,感觉绘制效率不太满足要求。搜索了些相关资料,多数也只谈到了使用双缓冲(仅仅是解决了画面的闪烁问题)。了解到directx绘制要快的多(picasa   就使用的directx),但好象没有这个必要,毕竟偶只是在窗口中绘制有动画效果的2维图片。认真研究了一番,也总结了一些提高效率的方法。如果哪位大侠有更好的建议,希望指点一二,偶的这篇文章算是抛砖引玉liao:)

先描述下问题域:在一幅背景画面上有几个圆形按钮,这几个圆形按钮绕成一个大圆环,要做出的效果是:点击按钮,所有的按钮都沿大圆环转动。当然啦!其过程要平滑。


起初,不知道绘制图片的时间大多浪费的什么地方,一步步摸索,相关资料也是少的可怜啊!

我的解决方法:
        1、在对话框初始化的时候,加载图片(包括背景图片、圆形按钮的图片),用类成员指针记录下来。
  2、在onpaint中,把背景和圆形按钮的图片都绘制一次,在内存中把按钮绘制到背景图片的相应的位置上(位置已经计算好了),再一次性画到屏幕上。像这样:
    cdc*   pdc   =   getdc();
                graphics   graphics   (pdc-> getsafehdc());

              //   设定裁剪区域(这个区域我剪切成了一个圆环,目的就是提高绘制速度,反复绘制时,只绘制这个圆环区域)
              if   (!bdrawbkgrd)
                            graphics.setclip   (m_pcliprgn);

                //   内存中绘制
                image*   pimg   =   m_pbkimg-> clone   ();             //   不消耗cpu时间
                graphics*   pgraphics   =   graphics.fromimage   (pimg);
                for   (int   i   =   0;   i   <   7;   i++)
                {
                                pgraphics-> drawimage   (m_pimg[i],   m_place[i].x,   m_place[i].y,   m_pimg[i]-> getwidth(),   m_pimg[i]-> getheight());
                }

                //   绘制到屏幕
                graphics.drawimage   (pimg,   0,   0,   pimg-> getwidth(),   pimg-> getheight());

                delete   pgraphics;
delete   pimg;
releasedc   (pdc);

这样反复绘制时的效果不会闪烁,呵呵。

  3、但绘制速度不快,大概是12.5帧/秒。背景图片是1027   x   768的png图片,按钮图片是100   x   110的jpg图片,我的机器是p4   1.7g,512内存,显卡是nvidia   tnt2,绘制效率低了点儿。

        4、一定还能提高帧速!俺研究了半天,试了下cachedbitmap,代码改为:

                cdc*   pdc   =   getdc();
                graphics   graphics   (pdc-> getsafehdc());

              //   设定裁剪区域(这个区域我剪切成了一个圆环,目的就是提高绘制速度,反复绘制时,只绘制这个圆环区域)
              if   (!bdrawbkgrd)
                            graphics.setclip   (m_pcliprgn);

                //   内存中绘制
                image*   pimg   =   m_pbkimg-> clone   ();             //   不消耗cpu时间
                graphics*   pgraphics   =   graphics.fromimage   (pimg);
                for   (int   i   =   0;   i   <   7;   i++)
                {
                          cachedbitmap   cachedbitmap((bitmap*)m_pimg[i],   &graphics);
                          pgraphics-> drawcachedbitmap   (&cachedbitmap,   m_place[i].x,   m_place[i].y);
                }

                //   绘制到屏幕
                graphics.drawimage   (pimg,   0,   0,   pimg-> getwidth(),   pimg-> getheight());

                delete   pgraphics;
delete   pimg;
releasedc   (pdc);

果然!呵呵,帧速提高到了15.5帧/秒

        5、总结了这些经验,希望能够和大家分享下。上面的代码是包装在一个函数中的。让触发事件来调用,在onpaint中也有调用。如果哪位高手有什么好的建议的话,还望不吝赐教!帧速越高越好啊,不知道用gdi+速度能够提高到多少:)24帧以上最好了!
发表于:2007-08-01 17:22:441楼 得分:0
在onpaint中获得dc,最好用cpaintdc   dc(this);这样的形式,这样可以充分利用windows本身的绘图优化裁剪。
对象的构造和删除会耗费大量运算资源,对于双缓冲中的内存位图和dc(你这里是graphics)最好事先创建好,不要再onpaint()中创建和销毁。
你在循环中构建和销毁cachedbitmap对象,也是一样的道理,为什么不在初始化的时候就构建好呢?
另外,你说要全速绘图,必然需要一个循环不停刷新。这种情况,不如将绘图放到单独的函数中,直接在循环中调用这个函数,省却消息循环的消耗;当然在循环中要考虑接收和分发界面消息,防止界面死锁。在onpaint中同样调用这个绘图函数,防止画面错误。
其实可以参考游戏的做法,像你这么简单的图形和电脑配置,使用gdi应该可以达到100fps以上,使用gdi+也不会差多少。
发表于:2007-08-01 17:52:112楼 得分:0
楼上说得有道理。
发表于:2007-08-02 09:40:313楼 得分:0
哦!谢谢   mackz(在相互)   给出的建议!100fps完全足够啦
我修改一下,一会儿把改后的版本贴上来。
同样谢谢   lyg_zy:)
发表于:2007-08-03 08:20:424楼 得分:0
我也很关心这个问题啊!
大家说说吧,有什么好办法啊
发表于:2007-08-03 08:21:325楼 得分:0
兄弟,我的邮箱是stone_hs@163.com能给点directx的资料吗
发表于:2007-08-03 11:08:136楼 得分:0
问题解决的还不够。
为了效率高一些,我把cdc指针和graphics等对象的初始化都放到oninitdialog中了,作为成员变量,现在我修改的函数是这样的:

void   drawdisc   (bool   bdrawbkgrd)
{
        //   设定裁剪区域
        if   (bdrawbkgrd)
        {
                //   绘制背景时需要清空剪切区域,才能把整个背景画出来。
                if   (!m_pgraphics-> isclipempty   ())
                        m_pgraphics-> resetclip   ();

        }
        else
        {
                //   只需要画圆环,为了效率,设定剪切区域。
                m_pgraphics-> setclip   (m_pcliprgn);
        }

        //   暂时用的临时对象(速度还可以),也可以把它作为成员变量,这样更好一些。
        graphics   memgraphics(m_pbkbmp);

        //   在背景图片上画7个按钮
        for   (int   i   =   0;   i   <   7;   i++)
        {
                memgraphics.drawcachedbitmap   (m_pcachedbmp[i],   m_place[i].x,   m_place[i].y);
        }

        //   绘制到屏幕
        m_pgraphics-> drawimage   (m_pbkbmp,   0,   0,   m_pbkbmp-> getwidth(),   m_pbkbmp-> getheight());
}

        这个函数在一个循环中反复调用。绘制速度挺高的,能够达到65fps,还是前面提到的机器配置。但是,背景图片绘制了一次之后,m_pbkbmp就脏了。下次循环用的载入的m_pbkbmp就是已经绘制上7个按钮后的图片,再在这个图片的基础上再绘制7个按钮,显示...呵呵,这样可不是我想要的效果!

        所以,我将上面的函数改成:

void   drawdisc   (bool   bdrawbkgrd)
{
        //   设定裁剪区域
        if   (bdrawbkgrd)
        {
                //   绘制背景时需要清空剪切区域,才能把整个背景画出来。
                if   (!m_pgraphics-> isclipempty   ())
                        m_pgraphics-> resetclip   ();

        }
        else
        {
                //   只需要画圆环,为了效率,设定剪切区域。
                m_pgraphics-> setclip   (m_pcliprgn);
        }

        //   内存中绘制
        bitmap*   pbmp   =   m_pbkbmp-> clone   (0,   0,   m_pbkbmp-> getwidth(),   m_pbkbmp-> getheight(),   pixelformat24bpprgb);
        //   这里m_pmemgraphics是成员变量了
        m_pmemgraphics   =   m_pgraphics-> fromimage   (pbmp);


        //   在内存的背景图片上画7个按钮
        for   (int   i   =   0;   i   <   7;   i++)
        {
                m_pmemgraphics-> drawcachedbitmap   (m_pcachedbmp[i],   m_place[i].x,   m_place[i].y);
        }

        //   绘制到屏幕
        m_pgraphics-> drawimage   (pbmp,   0,   0,   pbmp-> getwidth(),   pbmp-> getheight());

        delete   pbmp;
        pbmp   =   null;
}

        嗯,这次是我看到的是想要的效果。不过,速度慢下来了,只有十几fps....怎样才能避免clone操作和fromimage操作呢?这两个函数浪费了太多的时间!换个其它的方法也行,只要效率提高上去。联想到bitblt函数能够把内存dc绘制到屏幕dc上,猜测如果能够将graphics看成cdc对象也进行类似的操作可能效率会高些吧,好象gdi+中没有这样的方法(这样操作效率是否达到要求还不确定)
发表于:2007-08-03 11:11:397楼 得分:0
对了,还有一个问题:在得到屏幕dc时,用getdc   ()就可以绘制出来,如果用cpaintdc   dc(this);利用dc去绘制图片到屏幕上,却绘制不出来,有谁知道是什么原因吗?谢谢!
发表于:2007-08-22 23:53:118楼 得分:0
cpaintdc   dc(this)必须放在onpaint中的,   graphics对象绘制时可以设置为copy模式或利用clear方法,这样bmp对象应该就不用重新创建。
发表于:2007-08-23 15:34:139楼 得分:0
关注中
发表于:2007-08-27 14:50:5510楼 得分:0
http://www.skinex.cn
发表于:2007-08-30 18:54:3211楼 得分:0
谢谢大家的参与!
兄弟没有什么积分,散不了了。
在csdn上得到了不少的帮助,我做完自己的工程之后会把全部工程代码放上来,其中有不少常见问题的解决方法。。。我会详细描述滴!   算是答谢各位热心了:)
发表于:2007-09-06 17:32:5312楼 得分:0
up
发表于:2007-10-31 19:02:3613楼 得分:0
呵呵真快!今天是2007.10.31了。转眼2个月了
最近模块功能完成的差不多了,誊出手来把gdi+的绘图总结出来,明天贴上来。今天有点儿晚了。。。。
发表于:2007-11-01 10:03:2714楼 得分:0
算了,发个新帖吧。名字就叫:“补充:gdi+的绘图效率问题,大家讨论一下吧!”,希望参与讨论的朋友,打开我的第二个社区主帖。。。


快速检索

热门点击