65.9K
CodeProject 正在变化。 阅读更多。
Home

使用两个简单类实现无闪烁重绘和背景缓冲绘图

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.88/5 (8投票s)

2000年3月28日

viewsIcon

147291

downloadIcon

2035

使双缓冲变得简单的两个类

引言

我在这里介绍两个类,它们可以用于

  1. 无闪烁绘图
  2. 缓冲绘图,例如,背景

这两个类的优点是

  • 可以有多个缓冲区用于多种目的。与其他网站上的一些 CMemDC 类的代码不同,缓冲区和用于访问这些缓冲区的代码之间存在区别。
  • 当我们对所有绘图操作使用 GetSafeCDC() 时,我们在打印或预览期间永远不会遇到问题,因为在这种情况下,我们只是绕过了指向 OnDraw 提供的 CDC 的指针。这种简单的方法永远不会失败。
  • 兼容 16 位和 32 位
  • 缓冲区可以分配一次。如果我们有一个大屏幕,我们不会为每个 OnDraw 事件分配一个新的大位图。如果只需要重绘屏幕的一部分,仍然可以声明和使用一个临时缓冲区。
  • 如果由于系统资源有限而导致缓冲区分配失败,则所有绘图都将无缓冲地进行
  • 如果我们的绘图操作持续很长时间,则可以定期(例如,每秒)向用户显示中间结果。这的实现很简单:在这些操作期间只需调用 CopyToScreen(1000)。这将每秒将后台缓冲区的当前内容复制到屏幕上。

注意:在预览期间,提供给 OnDrawCDC 内部是一个 CPreviewDC。这是一个内部 MFC 类。这个类确实有一些函数被重载了,而不是虚函数。因此,当我使用其他 CMemDCs 实现时,我在打印和预览期间遇到了错误,因为这些 CMemDC 没有调用 CPreviewDC 的函数,而是调用了 CDC 的相应函数。

使用类

有很多方法可以使用这些类。请看 MemDc.h 中的声明。

使用示例,逐步解释

  1. 在您的 CView 派生类的头文件中定义一个或多个 CBufferMemDC。在我的例子中,我定义了两个缓冲区。一个用于静态的通用背景,另一个用于我必须更频繁地在背景上重绘的信息。也可以为绘图的最终结果分配一个额外的缓冲区,以缓冲整个窗口客户区,例如在用户使用 Alt-Tab 在应用程序之间切换的情况下。

    示例

    CBufferMemDC m_BufferBackground;
    // a buffer for the part of drawing which does not change
    
    CBufferMemDC m_BufferFlickerFreeDrawing;
    // a buffer which is used for flicker free redrawing on top of
    // the background
  2. 将以下函数添加到您的 CView 派生类
    static void CalcSizeTotalAreaWnd( CDC* pDC, const YourView* pView,
                                      CRect& totalAreaWnd ) {
    	if ( pDC->IsPrinting() ){
    	
    // if we are printing we get the total area from the clip box. 
    // Perhaps there is a better way to get this information
    
    		pDC->GetClipBox(&totalAreaWnd);
    	
    	} else {
    
    // if we don't print we just take the client area of the window
    
    		pView->GetClientRect( totalAreaWnd );
    	}
    }
  3. 将您的 OnDraw 函数的实现放在另一个函数中。例如
    OnRedraw( CDC* pDC).
  4. 修改您的 OnDraw 函数
    void MyView::OnDraw( CDC* pDC )
    {
    	CRect totalAreaWnd;
    	CalcSizeTotalAreaWnd( pDC, this, totalAreaWnd );
    
    	CMemDC memDC( pDC, totalAreaWnd, totalAreaWnd,
                         &m_BufferBackground );
    
    	if ( pDC->IsPrinting() || m_BufferBackground.IsDirty()){
    	
    // draw what you want to draw, for example a line. We simply call here
    // our routine, which we have defined above. It is important not to
    // use memDC, but GetSafeCDC. See the descriptions of this function
    // in the header file. Note: OnRedraw is only called when the
    // background buffer is dirty or when we are printing. Printing
    // processes are not buffered because this can
    // create very big bitmaps if we use for example A0-printers
    
    		OnRedraw( memDC.GetSafeCDC() );	
    			
    // when we have drawn all, we have all information in the bitmap but 
    // not on the screen. So we copy the contents of the buffer to the
    // screen, to show the user what has happened. The user will see only
    // the result and so we have realized the flicker free redrawing
    
    		memDC.CopyToScreen(0);
    	}
    }

我自己的实现更复杂,因为我不仅要缓冲背景,还要缓冲绘图期间的几个中间状态。但这对于这两个类来说不是问题,因为这两个类确实解决了这类问题的两个主要部分:如何处理缓冲区以及如何进行缓冲。

最后,我不得不说我对最终代码是多么小感到惊讶。

许可证

本文未附加明确的许可证,但可能在文章文本或下载文件本身中包含使用条款。如有疑问,请通过下面的讨论区联系作者。

作者可能使用的许可证列表可以在此处找到。

© . All rights reserved.