【问题】
在以前的双缓冲Demo中,发现这个在移动一张牌的时候,效率还能忍受过去。但是当应用双缓冲技术到显示多张纸牌的时候,我们会发现效率已经不能再让人忍受了。移动纸牌发生飘移;发牌的时候,当把定时器间隔修改为1ms的时候,速度还是很慢。为此需要对双缓冲的效率进行改善。
【解决方法】
经过调查,解决方法具体如下:
1. 将创建内存画布的工作移动到OnSize函数中去做,也就是当窗口大小发生变化的时候,才能去再次创建内存画布,否则就用原来已经创建好的。
- void CDemoDlg::OnSize(UINT nType, int cx, int cy)
- {
- CDialog::OnSize(nType, cx, cy);
- // TODO: Add your message handler code here
- CClientDC clientDC(this);
- CreateMemoryCanvas(&clientDC);
- }
2. InvalidateRect这个函数在使用的时候,要指定刷新区域。因为计算机的绘图效率很高,但是显示效率却很低。在OnPaint函数中去判断是否与指定的刷新区域相交,如果相交,则进行绘图显示,否则不去显示。这样做极大地提高了刷新效率。
- void CDemoDlg::OnPaint()
- {
- CPaintDC dc(this); // device context for painting
- dc.GetClipBox(m_clipRect);
- CRect intersectRect;
- // 将背景图片显示在内存DC中
- int nDestX = 0;
- int nDestY = 0;
- CRect bkRect;
- for (nDestY = 0; nDestY <= m_clientRect.bottom /*- BK_WIDTH*/;
- nDestY +=BK_WIDTH)
- {
- for (nDestX = 0; nDestX <= m_clientRect.right /*- BK_WIDTH*/;
- nDestX += BK_WIDTH)
- {
- bkRect.left = nDestX;
- bkRect.top = nDestY;
- bkRect.right = nDestX + BK_WIDTH;
- bkRect.bottom = nDestY + BK_WIDTH;
- // 如果当前区域与指定刷新区域相交,则进行绘图显示。
- if (intersectRect.IntersectRect(bkRect, m_clipRect))
- {
- DisplayBmp(&dc, &m_memDC, IDB_BITMAP6, nDestX, nDestY);
- }
- }
- }
- // 把纸牌2显示在内存DC中。
- if (intersectRect.IntersectRect(m_rectBmp2, m_clipRect))
- {
- DisplayBmp(&dc, &m_memDC, IDB_BITMAP2, m_rectBmp2.left, m_rectBmp2.top);
- }
- // 把纸牌1显示在内存DC中。
- if (intersectRect.IntersectRect(m_rectBmp1, m_clipRect))
- {
- DisplayBmp(&dc, &m_memDC, IDB_BITMAP1, m_rectBmp1.left, m_rectBmp1.top);
- }
- // 将内存DC上的图象拷贝到前台
- dc.BitBlt(0, 0, m_clientRect.Width(), m_clientRect.Height(),
- &m_memDC, 0, 0, SRCCOPY);
- }
- void CDemoDlg::OnMouseMove(UINT nFlags, CPoint point)
- {
- // TODO: Add your message handler code here and/or call default
- // 如果可以移动,则更新当前纸牌矩形区域
- if (m_bRemove)
- {
- // 更新当前纸牌矩形区域
- CRect oldRect = m_rectBmp1;
- m_rectBmp1.left = m_rectBmp1.left + (point.x - m_ptOld.x);
- m_rectBmp1.top = m_rectBmp1.top + (point.y - m_ptOld.y);
- m_rectBmp1.bottom = m_rectBmp1.top + CARD_HEIGHT;
- m_rectBmp1.right = m_rectBmp1.left + CARD_WIDTH;
- CRect newRect = m_rectBmp1;
- // 这里为了擦除移牌的痕迹,刷新的区域 = 该纸牌的旧矩形区域 + 该纸牌的新矩形区域。
- m_paintRect.UnionRect(oldRect, newRect);
- InvalidateRect(m_paintRect, FALSE);
- // 记录当前坐标,为旧的坐标
- m_ptOld = point;
- }
- CDialog::OnMouseMove(nFlags, point);
- }
【解决后的效果】:
在以前移动一张纸牌,刷新所需要的时间大概为47ms.(这里要画104张纸牌)。 通过以上的改善措施后,刷新所需要的时间不到1ms(这里只画一张纸牌)。