闪屏问题及刷新效率

 

闪屏问题及刷新效率
1 简单过程描述
在视图(Cview及其子类)每次重绘的过程中,都调用OnDraw( )函数。在调用Invalidate()、InvalidateRect()函数过程后,系统自动调用OnDraw()函数,完成视图的刷新显示。

以InvalidateRect(LPCRECT lpRect, BOOL bErase = TRUE)为例说明简单过程,调用InvalidateRect()后,系统判断指定的矩形lpRect是否在视图的显示范围(滚动视图显示区域

)内,如果在,就调用OnDraw()函数执行重绘,重绘后按照lpRect指针所指向的矩形指定的无效区域进行视图更新。
视图的更新需要一部分时间,所以我们要考虑减小视图每次更新矩形的区域,也就是减小InvalidateRect()中第一个参数lpRect的大小。所以我们应该在需要刷新时,计算刷新矩

形的大小,然后调用InvalidateRect()函数。
2 闪屏问题的解决
闪屏问题的解决办法是采用双缓冲技术。即创建与设备DC兼容的位图,在位图中绘制,然后Copy到屏幕上。注意,要重载BOOL CDrawView::OnEraseBkgnd(CDC*)函数,返回TRUE;

BOOL CDrawView::OnEraseBkgnd(CDC*)
{
 
return TRUE;
}
不多叙述。
3 刷新效率
我们最理想的刷新元件的思路就是每次只绘制那些在无效矩形区域内的元件,其余的部分不变。实际的情况是我们在调用OnDraw()函数时,必须遍历所有的元件,然后根据情况选

择重绘的元件,将其显示。有两种方法可以选择,一是遍历每个元件的时候判断它是否位于裁减区域内(其对应于无效矩形区域),如在就绘制,否则不进行绘制;第二种方法,

思路差不多,是创建一个与裁减矩形同样大小的位图,在位图DC上绘制所有元件,这个在绘制时,超出位图矩形大小的元件就不被绘制,判断元件是否超出位图矩形的工作有系统

来做。两种方法效率应该差不多,但第二种方法实现简便,效率相对优于第一种方法。
曾进行过如下测试:
每次无效矩形Crect(0,0,100,100)
for (int k=0; k<100 0000; k++)
{
 
pDrawDC->Rectangle(100,100,150,150);
}
在无效矩形外,执行1000,000次绘矩形操作,略有延迟。其实,循环体内,只进行的是系统的判断工作,没有绘制。
证明方法可行。
4实现过程的描述
为消除闪屏和充分考虑绘图效率,采用如下方法。
将每次要更新的矩形绘制在位图中,然后将其复制到OnDraw()函数的pDC中;每次执行元件移动,创建等操作时,定义无效矩形,调用InvalidateRect(rect,FALSE)函数。只有当视

图ReSize时才全部元件重绘,效率能够得到足够的保证。

重载OnDraw()函数
CDC dc;
 
CDC* pDrawDC = pDC;
 
CBitmap bitmap;
 
CBitmap* pOldBitmap;

  // only paint the rect that needs repainting
 
CRect client;
 
pDC->GetClipBox(client);
 
CRect rect = client;

  DocToClient(rect);

  if (!pDC->IsPrinting())
 
{
 
  // draw to offscreen bitmap for fast looking repaints
 
  if (dc.CreateCompatibleDC(pDC))
 
  {
 
    if (bitmap.CreateCompatibleBitmap(pDC, rect.Width(), rect.Height()))
 
    {
 
      OnPrepareDC(&dc, NULL);
 
      pDrawDC = &dc;

        // offset origin more because bitmap is just piece of the whole drawing
 
      dc.OffsetViewportOrg(-rect.left, -rect.top);
 
      pOldBitmap = dc.SelectObject(&bitmap);
 
      dc.SetBrushOrg(rect.left % 8, rect.top % 8);

        // might as well clip to the same rectangle
 
      dc.IntersectClipRect(client);
 
    }
 
  }
 
}

  // paint background
 
CBrush brush;
 
if (!brush.CreateSolidBrush(pDoc->GetPaperColor()))
 
  return;

  brush.UnrealizeObject();
 
pDrawDC->FillRect(client, &brush);

  if (!pDC->IsPrinting() && m_bGrid)
 
  DrawGrid(pDrawDC);

  pDoc->Draw(pDrawDC, this);

  if (pDrawDC != pDC)
 
{
 
  pDC->SetViewportOrg(0, 0);
 
  pDC->SetWindowOrg(0,0);
 
  pDC->SetMapMode(MM_TEXT);
 
  dc.SetViewportOrg(0, 0);
 
  dc.SetWindowOrg(0,0);
 
  dc.SetMapMode(MM_TEXT);
 
  pDC->BitBlt(rect.left, rect.top, rect.Width(), rect.Height(),
 
    &dc, 0, 0, SRCCOPY);
 
  dc.SelectObject(pOldBitmap);
 
}

from )
Flicker free drawing of any control

--------------------------------------------------------------------------------
This article was contributed by Thomas Jansen.
There are two articles dealing with this subject on this site, but i guess sometimes it's needed to free one single control from flickering, and it's not

needed to use a complete new window-class. I will show you a really small solution of the flickering-problem, which uses the CMemDC-class from the article by

Keith Rule. To make a control flicker free you have to go three steps, as described now.

1. Override the OnEraseBkgnd()-routine of the control
Flickering apears, because the complete control is erased and it needs some time to fill the control-area with some text/graphics or whatever. There is a

really short moment, when nothing can bee seen, because the background was erased but nothing is written yet. That's why we don't erase the background of the

control anymore. to do this, we have to override the OnEraseBkgnd()-routine of the control like this:


 
  BOOL CRecordList::OnEraseBkgnd(CDC* pDC)
 
  {
 
        return FALSE;
 
  }

2. Override the OnPaint()-routine of the control

The second thing to do, is to override the OnPaint()-routine. The trick is, to paint the complete control into a memory device-context and copy it in the

original DC via BitBlt(). The CMemDC-class does this work for you. Because we don't erase the background anymore (see above), we need to erase the memory-

device-context with the background-color of the control. A typical OnPaint() should look like this:


 
  void CRecordList::OnPaint()
 
  {
 
        CPaintDC dc(this);
 
        CMemDC memDC(&dc);

          CRect clip;
 
        memDC.GetClipBox(&clip);
 
        memDC.FillSolidRect(clip, GetSysColor(COLOR_WINDOW));

          DefWindowProc(WM_PAINT, (WPARAM)memDC->m_hDC, (LPARAM)0);
 
  }

3. Override the EraseBkgnd()-routine of the control's parent
Imagine the following situation. You have a dialog with the flicker free control in it. And now the user resizes the window. What happens? First of all, the

background of the dialog is erased and after that all controls of the window are redrawn. But when the background is erased and THAN the controls are

redrawn, we still have flickering! That why we have to exclude the controls area out of the clipping-box of the control's parent. And here's how we do it:


 
  BOOL CListBar::OnEraseBkgnd(CDC *pDC)
 
  {
 
        CRect clip;
 
        m_Control->GetWindowRect(&clip);// get rect of the control

          ScreenToClient(&clip);
 
        pDC->ExcludeClipRect(&clip);

          pDC->GetClipBox(&clip);
 
        pDC->FillSolidRect(clip, GetSysColor(COLOR_BTNFACE));

          return FALSE;
 
  }

That's it. You can make near every control flicker free in this way, but for some of the controls, you have to change the routines above slightly. Like for

the ListCtrl in Report-mode, because the columne-header-are

a have to be excluded of the clip-box, like this:


 
  void CRecordList::OnPaint()
 
  {
 
        CPaintDC dc(this);

          CRect headerRect;
 
        GetDlgItem(0)->GetWindowRect(&headerRect);
 
        ScreenToClient(&headerRect);
 
        dc.ExcludeClipRect(&headerRect);

          CMemDC memDC(&dc);
 
        .
 
        .
 
        .
 
  }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值