Skip navigation

How to create a backbuffer win32

This example shows how quickly

The steps:

  1. Get a window up
  2. In WM_CREATE (or just anywhere before message loop start), initialize the backbuffer
    • BACKBUFFER_SURFACE = CreateCompatibleBitmap()
    • BACKBUFFER_DRAWTOOL = CreateCompatibleDC()
    • SelectObject() your createdCompatibleBitmap into your CreatedCompatibleDC
  3. All drawing should happen to your BACKBUFFER_SURFACE __via__ your BACKBUFFER_DRAWTOOL
  4. In a draw() routine that runs once a loop, BitBlt( BACKBUFFER_DRAWTOOL into main window DC )
#include <windows.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>


HWND winhwnd;
HDC		backDC;
HBITMAP backBufferBMP;
int		backBufferCX;
int		backBufferCY;
TCHAR * winClassName = TEXT("backbufferwin") ;

// prototypes
LRESULT CALLBACK WndProc ( HWND, UINT, WPARAM, LPARAM );
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd );


inline void draw()
{
  HDC winhdc = GetDC( winhwnd ) ;
  // Now copy the back buffer to the front buffer.
  BitBlt(

    winhdc,	// destination buffer
    0, 0,	// x, y to start writing to in the destination buffer
    // counted from top left corner of dest HDC (window)

    // Width, height to copy for
    backBufferCX, backBufferCY,

    backDC, // SOURCE buffer.  We're taking from that back canvas

    0, 0,		// x pt, y pt to START copying from
    // SOURCE.  counted from top left again

    // And we just want a straight copy.
    SRCCOPY );

  ReleaseDC( winhwnd, winhdc ) ;
}




void InitBackBuffer()
{
  backBufferCX = 512;
  backBufferCY = 300;

  HDC winhdc = GetDC( winhwnd );

  /*

  Imagine you have 2 things:
  - a canvas on which to draw (call this your HBITMAP)
  - set of brushes you will use to draw (call this your HDC)

  1.  HBITMAP.  Canvas
  ___________________
  |       *         |
  |   *             |
  |     HBITMAP     |
  |    draw here    |
  |_________________|

  2.  HDC.  Set of brushes.
  <-*
  []-*
  ()-*

  */

  // Create the HBITMAP "canvas", or surface on which we will draw.
  backBufferBMP = CreateCompatibleBitmap( winhdc, backBufferCX, backBufferCY );
  if(backBufferBMP == NULL)
    printf( "failed to create backBufferBMP" );


  // Create the HDC "device context", or collection of tools
  // and brushes that we can use to draw on our canvas
  backDC = CreateCompatibleDC( winhdc );
  if(backDC == NULL)
    printf( "failed to create the backDC" );

  // Permanently associate the surface on which to draw
  // ("canvas") (HBITMAP), with the "set of brushes" (HDC)

  // "select in" the backBufferBMP into the backDC
  HBITMAP oldbmp = (HBITMAP)SelectObject( backDC, backBufferBMP );


  // Delete the "canvas" that the backDC was
  // going to draw to.  We don't care about it.
  DeleteObject( oldbmp );



  /// Release the dc of the main window itself
  ReleaseDC( winhwnd, winhdc );
}


// Just as said every C++ program starts at main,              //
// every Windows program will start at WinMain.                //
/////////////////////////////////////////////////////////////////
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,
                   LPSTR lpCmdLine, int nShowCmd )
{
#pragma region get window up
  WNDCLASSEX window = { 0 } ;
  window.cbSize			= sizeof(WNDCLASSEX);
  window.hbrBackground	= (HBRUSH) GetStockObject(WHITE_BRUSH);		// BLACK_BRUSH, DKGRAY_BRUSH
  window.hCursor = LoadCursor(NULL, IDC_HAND);			// load hand cursor.. make to guitar
  window.hIcon = LoadIcon(NULL, IDI_APPLICATION);		// large icon for app
  window.hIconSm = LoadIcon(NULL, IDI_APPLICATION);		// small icon that shows up in top left of window
  window.hInstance = hInstance;							// program's instance handle that was first passed to WinMain by windows when program was first run
  window.lpfnWndProc = WndProc;								// function pointer to WndProc function
  window.lpszClassName = winClassName;							// window class name
  window.lpszMenuName = NULL;									// menu name -- but our app has no menu now
  window.style = CS_HREDRAW | CS_VREDRAW;				// window style --

  if(!RegisterClassEx( &window ))
  {
    MessageBox(NULL, TEXT("Something's wrong with the WNDCLASSEX structure you defined.. quitting"), TEXT("Error"), MB_OK);
    return 1;		// return from WinMain.. i.e. quit
  }

  // CREATE THE WINDOW AND KEEP THE HANDLE TO IT.
  winhwnd = CreateWindowEx(	0/*WS_EX_TOPMOST*/,			// extended window style.. this sets the window to being always on top
    winClassName,			// window class name.. defined above
    TEXT("Double buffering and using a backbuffer"),// title bar of window
    WS_OVERLAPPEDWINDOW,// window style
    400, 200,		        // initial x, y start position of window
    800, 600,	          // initial width, height of window.  Can also be CW_USEDEFAULT to let Windows choose these values
    NULL, NULL,			  	// parent window, window menu
    hInstance, NULL);		// program instance handle, creation params

  // now we show and paint our window, so it appears
  ShowWindow( winhwnd, nShowCmd );		// you have to ask that your Window be shown to see it
  UpdateWindow(winhwnd);					// paint the window
#pragma endregion

#pragma region message loop

  MSG			msg;

  while(1)
  {
    if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
    {
      if(msg.message == WM_QUIT)
      {
        PostQuitMessage(0);
        break;
      }
      TranslateMessage(&msg);	// translates 'character' keyboard messages
      DispatchMessage(&msg);	// send off to WndProc for processing
    }
    else
    {
      // could put more update code in here
      // before drawing.
      draw();
    }
  }
#pragma endregion

  return 0;	// end program once we exit the message loop
}




////////////////////////////////////////////////////////////////////////
// WndProc - "Window procedure" function
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
  switch(message)
  {
  case WM_CREATE:
    {
      // As soon as the window is first created, create
      // the back buffer.
      InitBackBuffer() ;

      // now just draw something into it for fun
      char buf[300];
      sprintf( buf, "HELLO.  Left click to add stars.  Right click to clear." );
      TextOutA( backDC, 20, 20, buf, strlen(buf) );

      return 0;
    }
    break;

  case WM_PAINT:
    {
      PAINTSTRUCT	ps;

      HDC winhdc = BeginPaint(hwnd, &ps);

      // Do nothing here.  All drawing happens in draw(),
      // and you draw to the backbuffer, NOT the
      // front buffer.

      EndPaint(hwnd, &ps);
      return 0;
    }
    break;

  case WM_LBUTTONDOWN:	// this message occurs when the left mouse button is clicked in our window's client area
    {
      int xPos = LOWORD( lParam );
      int yPos = HIWORD( lParam );

      // Set pixel @ pos of click IN BACK BUFFER
      // to red.
      SetPixel( backDC, xPos, yPos, RGB( 255, 0, 0 ) );

      return 0;
    }
    break;

  case WM_RBUTTONDOWN:
    {
      // clear by filling out the back buffer with black rectangle
      HBRUSH oldBrush = (HBRUSH)SelectObject( backDC, GetStockObject( BLACK_BRUSH ) ) ;
      Rectangle( backDC, 0, 0, backBufferCX, backBufferCY );
      SelectObject( backDC, oldBrush ) ; // put the old brush back, not bothering
      // to catch the system BLACK_BRUSH
    }
    break;

  case WM_DESTROY:
    {
      PostQuitMessage(0);
      return 0;
    }
    break;
  }

  // If message was NOT handled by us, we pass it off to
  // the windows operating system to handle it.
  return DefWindowProc(hwnd, message, wParam, lParam);
}





One Comment

    • Rob
    • Posted March 19, 2012 at 12:35 pm
    • Permalink

    Being an old programmer, but new to C++ and Win32, I found this example very helpful, including the PeekMessage loop.


Leave a comment