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); }
One Comment
Being an old programmer, but new to C++ and Win32, I found this example very helpful, including the PeekMessage loop.