最近在做一个物体移动的程序,使用的CDC贴图这些原始手段来完成了,结果很不理想,物体移动很迟缓不流畅;在一个测试中发现,本该3秒钟内完成的速成,最后用了24秒,经过多方查找,最后发现慢在Bitblt这个函数上,因为我其它地方都不变,仅将Bitblt函数屏蔽掉,程序就正好在3秒钟内完成,这才发现Bitblt这个函数效率根本就跟不上快速高频的物体移动处理上,如果,我想在一秒钟内完成物体100次移动,用Bitblt这个函数根本无法实现。
    还是先来看下我的代码:
void CPeopleMove::OnPaint() 
{
CPaintDC dc(this); // device context for painting

// TODO: Add your message handler code here
CRect rcWnd;
CWnd::GetClientRect(rcWnd);
//创建内存(兼容)DC
CDC m_bkMemDC;
m_bkMemDC.CreateCompatibleDC(&dc); //创建兼容位图
CBitmap btScreen;
btScreen.CreateCompatibleBitmap(&dc, rcWnd.Width(), rcWnd.Height()); //将该位图加入到内存DC中
m_bkMemDC.SelectObject(&btScreen);
btScreen.DeleteObject();

//OnDrawing(&m_bkMemDC, rcWnd); dc.BitBlt(rcWnd.left,rcWnd.top,rcWnd.Width(),rcWnd.Height(),&m_bkMemDC,0,0,SRCCOPY);
m_bkMemDC.DeleteDC(); // Do not call CWnd::OnPaint() for painting messages
}    程序设计上是用3秒钟对上面OnPaint函数进行三千次执行完成移动绘制,但事实上却用了24秒钟,后来我将OnPaint中dc.BitBlt语句屏蔽掉,三千次OnPaint执行正好费时3秒,所以问题出现在BitBlt函数上,BitBlt函数占用了大量执行时间。使得绘制频率低,人物移动慢。现在不知道用什么办法才好。
    面对这样的问题,不知道大家有什么好的解决办法,是不是MFC的CDC类无法完成这样的快速移动绘制,难道要用其它什么技术?希望有熟悉这方面的朋友给与指点,感激!

解决方案 »

  1.   

    估计这是MFC不能胜任的任务。BitBlt已经是像素拷贝了,除非你只刷新变更的部分并且变更较少。
    上D3D吧,终极方案。 :)
      

  2.   

    bitblt是硬件加速的函数,效率很高很高。像你这段代码的问题,问题不在于Bitblt,第一就是你需求不合理,100次移动对于任何客户都是无法分辨出来的。第二每次onpaint中都CreateCompatibleBitmap,这就是对你内存的严重损伤,难道你每次的GetClientRect(rcWnd)得到的RECT都会变吗。第三,你的btScreen.DeleteObject()调用在bitblt,之前,你还 bitblt什么啊。目标DC里的位图都被你删除了。
      

  3.   

    ++程序设计上是用3秒钟对上面OnPaint函数进行三千次执行完成移动绘制
    =========================================================
    1000次/秒,人眼根本无法识别,可变相减少更新次数不必要的重复操作可简化
      

  4.   

    这类开发一般都是DirectX、OpenGL吧还有,什么东西要求1秒钟刷新1000次的?FPS游戏都60帧就相当流畅了。当然你这个也还有改进空间的,最大的问题就是你的重绘区域太大了,既然只是一个物体运动,如果背景不变的话,需要重绘的只有物体以前所在区域和现在所在区域,用不着整个窗口都重绘。CPaintDC::m_ps保存得有绘制参数,包含需要重绘区域,你要刷新时使用InvalidateRect标记有改动的区域就是了,不要整个窗口都给Invalidate了。有了区域限制,你在OnPaint中处理时就可以判断是否在重绘区域中,不在就用不着处理了,BitBlt的区域小一点也会更快。
      

  5.   

    是的
    Bitblt是CDI中最快的了
    如果还没能接受恐怕要想其他方法了
      

  6.   

    只要每秒24帧对人眼就是动画了,每秒30帧,就是流畅了,每秒60帧,已是通用液晶显示器的刷新率了,每秒85帧就是CRT显示器的高刷新率了, 没必要弄那么多次刷新, 调整一下需求吧. 
      

  7.   

        感谢你的回复:
        第一,我也不是一定要一千次,只是保证我画面流畅就行,没法实现。
        第二,我的rcWnd并不每次都变,我也不想每次都在Onpaint中创建兼容位图,但是我不这样做又该怎样做呢?
        第三,创建一个兼容位图btScreen是为了准备一个内存m_bkMemDC,我将所有的绘制都在m_bkMemDC上完成,最后再将m_bkMemDC上的内容贴到桌面DC上,所以在准备好m_bkMemDC后,我就将btScreen删除了,后面bitblt的是m_bkMemDC上的内容。    还有,为了难证你的观点,我将
    //创建兼容位图
    // CBitmap btScreen;
    // btScreen.CreateCompatibleBitmap(&dc, rcWnd.Width(), rcWnd.Height()); //将该位图加入到内存DC中
    // m_bkMemDC.SelectObject(&btScreen);
    // btScreen.DeleteObject();
        这四位语句全屏蔽后,程序绘制不出任何东西,一片白色,但是时间到准确了,只用了3秒钟;
      

  8.   


    三楼的说法很正确,你有双缓冲的思想,但是根本就没发挥出来,因为你的内存DC是在onPaint才创建的,这样效率会大降,真正的双缓冲OnPaint(),里面应该只要 BitBlt就行了,你试着把那段代码移出去,自己创建计时器然后重绘。
      

  9.   

    可以考虑初始化的时候CreateDC, 销毁窗口的时候ReleaseDC。绘制、bitblt可以在OnTimer。CDC dc;
    CDC *pDC = this->GetDC();;
    CBitmap bitmap;
    dc.CreateCompatibleDC(pDC);
    GetClientRect(rClient);      // 初始化的时候得到客户区大小,然后在改变的时候再获取客户区大小
    bitmap.CreateCompatibleBitmap(pDC, rClient.Width(), rClient.Height());
    ....
    void *******::OnTimer()

         if (nIDEvent == 1)
         {
          ...
          pDC->BitBlt(0, 0, rClient.Width(), rClient.Height()-25, &dc, 0, 0, SRCCOPY);
         }
    }void *******::OnDestroy() 
    {
    CDialog::OnDestroy(); ReleaseDC(&dc);
    ReleaseDC(pDC);
    }适当调整,具体什么时候创建DC,什么时候绘制
      

  10.   

    SetTimer(1, 10, NULL);
    不就是10毫秒贴一次了吗。。哈哈
      

  11.   

    一般计时器是达不到10ms的精度的,但是可以向机器申请。不过按LZ的要求,只要保证画面流畅就行,完全不需要1000次/s的贴图速度。
      

  12.   

    按照各位的意思,我把创建兼容DC与兼容位图作为全局变量,仅创建一次,改进后的程序由原来的24秒提高到17秒,稍有提高。
    而且我还发现两个问题:
    第一,就是程序运行时,我将鼠标在绘图显示窗口上不停移动时,明显感觉人物移动快很多。感觉鼠标移动好象提搞了程序进程级别一样,程序运行速度明显提高,所以绘图也变得更快了,由原来的24秒提高到10秒内完成。不知道为何?
    第二,dc.BitBlt(rcWnd.left,rcWnd.top,rcWnd.Width(),rcWnd.Height(),&m_bkMemDC,0,0,SRCCOPY);
    中,我将rcWnd大小手动设置的越来越小时,程序运行的也越来越快,感觉还是bitblt存在效率问题。