How to create a backbuffer win32
This example shows how quickly
The steps:
- Get a window up
- 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
- All drawing should happen to your BACKBUFFER_SURFACE __via__ your BACKBUFFER_DRAWTOOL
- 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);
}