Click here to Skip to main content
15,885,309 members
Articles / Desktop Programming / WTL

A Memory DC for WTL

Rate me:
Please Sign up or sign in to vote.
4.17/5 (5 votes)
10 Mar 20023 min read 114.6K   2.1K   19   14
An article detailing a simple memory-based device context for the WTL, to be used when flicker-free double-buffered drawing is desired

Sample Image - WTLMemDC.gif

Abstract

A simple implementation of the memory device context for the WTL.

Objective

The ATL and the WTL are missing some of the advanced drawing tools that are present in other libraries and frameworks. This article, and the included source code, provides one such advanced tool: a memory device context.

Discussion

Developers quickly learn that drawing directly on the client area of a window causes flicker. Most professional applications use a technique known as "double buffered drawing". All drawing operations are performed on a plane that is invisible to the user. When the scene has been fully rendered on the hidden plane, its contents are copied to the plane seen by the user in a single pass.

The WTL does not currently support double buffered drawing. However, it is very simple to implement. The header file, "AtlGdi.h" contains a class that implements a Windows Device Context. There are several subclasses of the CDC object for use in a variety of situations. However, there is no "Memory Device Context" included in the WTL. This document describes how to perform double-buffering with the Win32 SDK, and demonstrates a WTL implementation, based on the CDC class.

Double-buffering with the SDK

While it is not necessary to understand how double-buffering is performed in the SDK, it is of interest to see how it works in relation to the CMemDC described by this article.

While the initialization code for the memory DC can be located in the WM_CREATE message handler of the window it will be used with, this does not work when an existing window is subclassed. This is because the window has to be created before it is subclassed, so the WM_CREATE message is never seen by the subclass. Therefore, it is usually desirable to add that code to the WM_PAINT message handler, with a check to see if the initialization has already occurred. An example of creating and using a memory DC:

C++
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 );
    }
  }
}

Double-buffering with the WTL

The class presented with this article uses virtually the same code, but is based upon the WTL CDC class. The WM_PAINT handler in the application creates an instance of CMemDC with the window handle as an argument:

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

  ...
}

The CMemDC constructor creates the memory DC and the bitmap internally:

C++
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 );
}

The application need only reference the memory DC object instead of creating a CPaintDC within the WM_PAINT handler:

C++
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 );

  ...
}

Then, when the invisible memory DC has been completely painted, its contents can be copied to the visible window simply by calling its Paint function:

C++
void OnPaint( HDC )
{
  ...

  m_memDC->Paint( );
}

Also supplied is a Repaint function, which can be used to refresh the visible window when processing other messages.

Conclusion

Although the implementation of the Memory Device Context class is very simple, it may be one of the most useful classes in a programmer's toolbox. Without this class, or something similar, there is no way to draw on the screen without a great deal of flicker.

License

This article has no explicit license attached to it, but may contain usage terms in the article text or the download files themselves. If in doubt, please contact the author via the discussion board below.

A list of licenses authors might use can be found here.


Written By
Software Developer (Senior)
United States United States
Mr. Howes has been programming since he was first introduced to the Apple II+ at the tender age of ten. After briefly dabbling with circuit design as a combined CS/EE major, he came to his senses and completed a pure CS degree at RIT

All these years later, the programming bug still bites him each day and causes him to go to work, where he writes software for a living. Now he is even learning Cocoa for the Mac!

In his copious amounts of "spare" time, Mr. Howes is learning the art of cabinet-making, flies radio-controlled helicopters and airplanes, and mountain cycles. Next summer he would like to learn how to kayak.

Linked In Page: http://www.linkedin.com/pub/3/54a/578

Comments and Discussions

 
GeneralSome bug Pin
StoneHead6-Dec-07 18:31
StoneHead6-Dec-07 18:31 
GeneralNice really, i wish an update of gdi+ Pin
ChauJohnthan4-Nov-02 18:45
ChauJohnthan4-Nov-02 18:45 
GeneralRe: Nice really, i wish an update of gdi+ Pin
jandrhub25-Nov-02 1:24
jandrhub25-Nov-02 1:24 
GeneralRe: Nice really, i wish an update of gdi+ Pin
Anonymous27-Nov-02 2:33
Anonymous27-Nov-02 2:33 
GeneralPossible Resource Leaks Pin
Henry Jacobs12-Mar-02 10:00
Henry Jacobs12-Mar-02 10:00 
GeneralBitmap size... Pin
Paul A. Howes12-Mar-02 3:21
Paul A. Howes12-Mar-02 3:21 
I looked over several other implementations for the memory DC. I found one thing in common with many of them:

The implementations that create the bitmap to be exactly the size needed, require the buffer to be created and destroyed every time the WM_PAINT handler is called. This results in the code being much more memory efficient than mine, at the expense of performance.

I wanted my implementation to be generic enough that it could be used for custom drawn controls, as well as for the client area of a window. When the CMemDC class is used in generating the client area of a window, the class only needs to be created once in the client window's WM_CREATE handler. At that point in the program's execution, the size of the window has not yet been determined! Rather than trying to guess, the general rule is to create the bitmap sized to be the entire screen. Then, no matter how much the window is resized during program execution, the bitmap and the compatible DC never need to be recreated, only redrawn at the new size.

This really comes down to an issue of efficiency: Memory or speed? There really is no right answer to that question. It depends on the requirements of the software itself. Most of the development I do is imaging or video related, which means I have large bitmaps in memory to start with.

In the case of video, I have to depend on the processor for decoding frames as quickly as possible. Copying the completed bitmaps (frames) from memory to screen needs to happen very rapidly, which is mostly dependant on the system memory, the speed of the bus, and the amount of memory on the video card. If the compatible DC and bitmap have to be created and destroyed for every frame, then valuable CPU time is being wasted -- because of the requirements of my application.

I hope this clears up some of the confusion as to why I implemented the CMemDC class in the way I did. If there are any other questions or comments, please post them! If I receive enough interest, I will update the article and code to reflect the ideas and comments I receive.

--
Paul

"I drank... WHAT?"
GeneralRe: Bitmap size... Pin
jandrhub25-Nov-02 2:11
jandrhub25-Nov-02 2:11 
GeneralAnother (better?) version already exist Pin
Jean-Michel LE FOL11-Mar-02 21:49
Jean-Michel LE FOL11-Mar-02 21:49 
GeneralRe: Another (better?) version already exist Pin
Paul A. Howes11-Mar-02 23:50
Paul A. Howes11-Mar-02 23:50 
GeneralRe: Another (better?) version already exist Pin
Daniel Bowen13-Mar-02 10:32
Daniel Bowen13-Mar-02 10:32 
GeneralRe: Another (better?) version already exist Pin
Ilushka8-Jul-03 22:30
Ilushka8-Jul-03 22:30 
QuestionSize of the screen? Pin
Todd Smith11-Mar-02 19:08
Todd Smith11-Mar-02 19:08 
AnswerRe: Size of the screen? Pin
Paul A. Howes11-Mar-02 23:58
Paul A. Howes11-Mar-02 23:58 
GeneralRe: Size of the screen? Pin
Jörgen Sigvardsson22-Dec-02 3:39
Jörgen Sigvardsson22-Dec-02 3:39 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.