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

WTL的内存DC

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.17/5 (5投票s)

2002年3月11日

3分钟阅读

viewsIcon

117350

downloadIcon

2159

一篇详细介绍 WTL 简单基于内存的设备上下文的文章,用于需要无闪烁双缓冲绘图的情况

Sample Image - WTLMemDC.gif

摘要

WTL 内存设备上下文的简单实现。

目标

ATL 和 WTL 缺少其他库和框架中存在的一些高级绘图工具。本文和随附的源代码提供了一种此类高级工具:内存设备上下文。

讨论

开发人员很快就会了解到,直接在窗口的客户端区域上绘图会导致闪烁。大多数专业的应用程序都使用一种称为“双缓冲绘图”的技术。所有绘图操作都在用户不可见的平面上执行。当场景已在隐藏平面上完全渲染后,其内容将一次性复制到用户可见的平面。

WTL 目前不支持双缓冲绘图。但是,它非常容易实现。头文件“AtlGdi.h”包含一个实现 Windows 设备上下文的类。有几个 CDC 对象的子类可用于各种情况。但是,WTL 中不包含“内存设备上下文”。本文档介绍了如何使用 Win32 SDK 执行双缓冲,并演示了基于 CDC 类的 WTL 实现。

使用 SDK 进行双缓冲

虽然没有必要了解如何在 SDK 中执行双缓冲,但了解它如何与本文描述的 CMemDC 相关是很有意义的。

虽然内存 DC 的初始化代码可以位于它将使用的窗口的 WM_CREATE 消息处理程序中,但这在子类化现有窗口时不起作用。这是因为窗口必须在子类化之前创建,因此子类永远看不到 WM_CREATE 消息。因此,通常希望将该代码添加到 WM_PAINT 消息处理程序,并检查是否已经发生初始化。创建和使用内存 DC 的示例

    int WndProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
    {
      static HDC     hMemDC  = 0;
      static HBITMAP hBitmap = 0;

      int width, height;
      HDC hDC;
      PAINTSTRUCT ps;

      switch( msg )
      {
        case WM_PAINT:
        {
          /* Initialize the memory DC */
          if( ! hMemDC )
          {
            width   = GetSystemMetrics( SM_CXSCREEN );
            height  = GetSystemMetrics( SM_CYSCREEN );
            hDC     = GetDC( hWnd );
            hMemDC  = CreateCompatibleDC( hDC );
            hBitmap = CreateCompatibleBitmap( hDC, width, height );
            SelectBitmap( hMemDC, hBitmap );
            ReleaseDC( hWnd, hDC );
          }

          /* Do all painting using hMemDC */
          ...

          /* Display the memory DC */
          BeginPaint( hWnd, &ps );
          BitBlt( ps.hdc,
                  ps.rcPaint.left,
                  ps.rcPaint.top,
                  ps.rcPaint.right - ps.rcPaint.left,
                  ps.rcPaint.bottom - ps.rcPaint.top,
                  hMemDC,
                  ps.rcPaint.left,
                  ps.rcPaint.top,
                  SRCCOPY );
          EndPaint( hWnd, &ps );
        }

        case WM_DESTROY:
        {
          DeleteDC( hMemDC );
          PostQuitMessage( 0 );
        }
      }
    }    

使用 WTL 进行双缓冲

本文提供的类实际上使用了相同的代码,但基于 WTL CDC 类。应用程序中的 WM_PAINT 处理程序使用窗口句柄作为参数创建 CMemDC 的实例

    void OnPaint( HDC )
    {
      if( ! m_memDC )
      {
        m_memDC = new CMemDC( *this );
      }

      ...
    }    

CMemDC 构造函数在内部创建内存 DC 和位图

    CMemDC( HWND hWnd )
    : CDC( )
    , m_bitmap( 0 )
    , m_hWnd( 0 )
    {
      ATLASSERT( hWnd );
      m_hWnd = hWnd;
      int width  = ::GetSystemMetrics( SM_CXSCREEN );
      int height = ::GetSystemMetrics( SM_CYSCREEN );
      CClientDC dc( hWnd );
      CreateCompatibleDC( dc );
      m_bitmap.CreateCompatibleBitmap( dc, width, height );
      SelectBitmap( m_bitmap );
    }    

应用程序只需要引用内存 DC 对象,而不是在 WM_PAINT 处理程序中创建 CPaintDC

    void OnPaint( HDC )
    {
      ...

      RECT cRect;
      GetClientRect( &cRect );
      m_memDC->FillSolidRect( &cRect, GetSysColor( COLOR_BTNFACE ) );
      m_memDC->DrawEdge( &cRect, EDGE_ETCHED, BF_ADJUST | BF_RECT );
      m_memDC->DrawText( _T( "I'm custom drawn."), 17, &cRect,
          DT_CENTER | DT_SINGLELINE | DT_VCENTER );

      ...
    }    

然后,当不可见的内存 DC 已完全绘制后,只需调用其 Paint 函数即可将其内容复制到可见窗口

    void OnPaint( HDC )
    {
      ...

      m_memDC->Paint( );
    }    

还提供了一个 Repaint 函数,可用于在处理其他消息时刷新可见窗口。

结论

虽然内存设备上下文类的实现非常简单,但它可能是程序员工具箱中最有用的类之一。如果没有这个类或类似的东西,就没有办法在屏幕上绘图而不会产生大量的闪烁。

许可证

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

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

© . All rights reserved.