Skip navigation

Tag Archives: C C++

This example just shows how to use DevIL to open, create, and save an image

Great tutorial here too

Pretty neat.

#undef _UNICODE

#include <stdio.h>
#include <stdlib.h>

#include <windows.h>


#include "il.h"
#include "ilu.h"  // for image creation and manipulation funcs.

#pragma comment( lib, "DevIL.lib" )
#pragma comment( lib, "ILU.lib" ) 

// Wow. DevIL is amazing.

// From http://gpwiki.org/index.php/DevIL:Tutorials:Basics

// The library consists of three sub-libraries:
//  * IL - main DevIL library. It allows you to load and save images to files. Every function in this library have 'il' prefixed to their name.
//  * ILU - this library contains functions for altering images. Every function in this library have 'ilu' prefixed to their name.
//  * ILUT - this library connects DevIL with OpenGL. Every function in this library have 'ilut' prefixed to their name. 

int main()
{
  ilInit();
  printf("DevIL has been initialized\n");
  
  // Loading an image
  ILboolean result = ilLoadImage( "4px.png" ) ;

  if( result == true )
  {
    printf("the image loaded successfully\n");
  }
  else
  {
    printf("The image failed to load\n" ) ;
    
    ILenum err = ilGetError() ;
    printf( "the error %d\n", err );
    printf( "string is %s\n", ilGetString( err ) );
  }

  int size = ilGetInteger( IL_IMAGE_SIZE_OF_DATA ) ;
  printf("Data size:  %d\n", size );
  ILubyte * bytes = ilGetData() ;

  for( int i = 0 ; i < size; i++ )
  {
    // see we should see the byte data of the image now.
    printf( "%d\n", bytes[ i ] );
  }


  // Other info.
  int bpp = ilGetInteger( IL_IMAGE_BITS_PER_PIXEL ) ;
  
  printf("bpp: %d   IL_IMAGE_FORMAT: %d\n",
    
    bpp,
    ilGetInteger( IL_IMAGE_FORMAT )
    
  ) ;


  ilClearColour( 0, 255, 0, 255 ) ;




  // generate image data as just array of bytes.

  // get width and height of your desktop
  int width  = GetSystemMetrics( SM_CXVIRTUALSCREEN ) ;
  int height = GetSystemMetrics( SM_CYVIRTUALSCREEN ) ;

  int bytesToUsePerPixel = 3 ;  // RGB
  // we coulda used 4, if we wanted a "transparency" component,
  // but we don't want this png to use trans.

  // duh, the size of a byte is obviously ONE BYTE,
  // but this is just a formality to PERHAPS
  // make the code a bit more clear.
  int sizeOfByte = sizeof( unsigned char ) ;

  // So this computation gets the size
  // that the image data array should be.
  // (width*height)   *   (#bytes to use per pixel) really.
  int theSize = width * height * sizeOfByte * bytesToUsePerPixel ;

  unsigned char * imData =(unsigned char*)malloc( theSize ) ;

  for( int i = 0 ; i < theSize ; i++ )
  {
    imData[ i ] = i % 255 ;
  }
  
  
  // Let's make an image now.
  // the next line "generates an image" within the
  // DevIL STATE MACHINE.

  // DevIL is cool because it has this concept of
  // your "currently selected image".

  // So, when you call all the DevIL library functions,
  // (such as ilTexImage to change an images properties),
  // notice how there's no passing of any big data struct,
  // pointer to an image, or anything like that.
  
  // That's because the ONLY reference YOU get
  // as the user of the OpenIL API IS this INT
  // value:
  ILuint imageID = ilGenImage() ;

  // there.  We just created a NEW image (which will start out
  // to being 1x1 pixels or something like that)
  // our REFERENCE to this 1x1 pixel image IS
  // the integer number imageID.

  // NOW, we "select-in" the image we just generated
  // as the one we're currently working on.
  ilBindImage( imageID ) ;

  // From HENCE FORTH (until we call ilBindImage() again)
  // WE ARE WORKING ON "imageID".  ANY calls to
  // ilTexImage, or ilGetInteger, or WHATEVER are in
  // reference to the LAST IMAGE WE LAST BOUND.

  // So the new image we just created is rather.. empty.
  printf("New image!  width=%d,  height=%d,  bpp=%d\n",

    ilGetInteger( IL_IMAGE_WIDTH ),
    ilGetInteger( IL_IMAGE_HEIGHT ),
    ilGetInteger( IL_IMAGE_BPP )
    );

  printf("About to tex up your image\n");
  ilTexImage(
    
    width,
    height,
    
    1,  // OpenIL supports 3d textures!  but we don't want it to be 3d.  so
    // we just set this to be 1
    
    3,  // 3 channels:  one for R , one for G, one for B
    
    IL_RGB,  // duh, yeah use rgb!  coulda been rgba if we wanted trans
    
    IL_UNSIGNED_BYTE,  // the type of data the imData array contains (next)
    
    imData  // and the array of bytes represneting the actual image data
    
  ) ;

  printf("Your image was texxed\n");

  printf("Now width=%d,  height=%d,  bpp=%d\n",

    ilGetInteger( IL_IMAGE_WIDTH ),
    ilGetInteger( IL_IMAGE_HEIGHT ),
    ilGetInteger( IL_IMAGE_BPP )
    );

  // allow openIL to overwrite the file
  // we created last time
  ilEnable(IL_FILE_OVERWRITE);
 
  // actually save out as png
  ilSave( IL_PNG, "output.png" ) ;

  // now try saving as jpg
  ilSave( IL_JPG, "output.jpg" ) ;
  
  // now save as bmp
  ilSave( IL_BMP, "output.bmp" ) ;


  // Look at the file sizes!  For this TYPE
  // of image, png is the best because its
  // flat colors repeated.

  // On my mahcin eht png is 94KB and the BMP is 11000kB!
  // the jpg comes out at 3000kb.

  // PNG RULES!!

  // lets give another example where jpg will
  // be better...

  // cahnge im data to being just pure noise
  for( int i = 0 ; i < theSize ; i++ )
  {
    imData[ i ] = rand()%255 ;
  }


  // now set the data in the image with this new data array
  ilSetData( imData ) ;

  // save again

  // actually save out as png
  ilSave( IL_PNG, "outputNoise.png" ) ;

  // now try saving as jpg
  ilSave( IL_JPG, "outputNoise.jpg" ) ;
  
  // now save as bmp
  ilSave( IL_BMP, "outputNoise.bmp" ) ;


  // Wowo!   So for this last test, PNG was
  // worse than jpg and bmp!  that's remarkable.
  // I actually didn't know that coudl happen.

  printf("and done");

}

As usual, download the code from esnips! (thanks esnips!)

How do you load a PNG image in C++?

Well, most people use libpng

But WAIT!! That library is NSFDU. “NOT SAFE FOR DIRECT USE” – Yes i just made that up, but its absolutely true.

The API is AWFUL. If you don’t believe me, spend a day with it, struggle through it, THEN check out OpenIL

In less than 50 lines, I’m loading an image and looking at its raw data

Compare with bare minimal libpng direct use example. This is an example of how DIFFICULT using libpng directly is.

DevIL’s BRILLIANT API mimics OpenGL’s state-machine approach to image handling. Its fantastic, and for anyone who’s ever used OpenGL a bit at least, picking it up will be a breeze and everything is as you think (hope?) it should be.

#include <stdio.h>
#include <stdlib.h>

#undef _UNICODE
#include "il.h"

#pragma comment( lib, "DevIL.lib" )

// Wow. DevIL is amazing.

// From http://gpwiki.org/index.php/DevIL:Tutorials:Basics

// The library consists of three sub-libraries:
//  * IL - main DevIL library. It allows you to load and save images to files. Every function in this library have 'il' prefixed to their name.
//  * ILU - this library contains functions for altering images. Every function in this library have 'ilu' prefixed to their name.
//  * ILUT - this library connects DevIL with OpenGL. Every function in this library have 'ilut' prefixed to their name. 

int main()
{
  ilInit();
  printf("DevIL has been initialized\n");
  
  // Loading an image
  ILboolean result = ilLoadImage( "4px.png" ) ;

  if( result == true )
  {
    printf("the image loaded successfully\n");
  }
  else
  {
    printf("The image failed to load\n" ) ;
    
    ILenum err = ilGetError() ;
    printf( "the error %d\n", err );
    printf( "string is %s\n", ilGetString( err ) );
  }

  int size = ilGetInteger( IL_IMAGE_SIZE_OF_DATA ) ;
  printf("Data size:  %d\n", size );
  ILubyte * bytes = ilGetData() ;

  for( int i = 0 ; i < size; i++ )
  {
    // see we should see the byte data of the image now.
    printf( "%d\n", bytes[ i ] );
  }
}

Download the code (with DevIL binaries included!) on esnips! (thanks esnips!)

How can I attach a console to my win app?

A common question that usually points people to this 1997 article (which remarkably should still work!).

Besides a few codeguru or codeproject articles that do the same, here is a simple example that only takes 3 lines of code on top of your basic window creation code

All you have to know:

  // The important lines:
  AllocConsole() ;
  AttachConsole( GetCurrentProcessId() ) ;
  freopen( "CON", "w", stdout ) ;


#include <windows.h>

#include <stdio.h>
#include <stdlib.h>

// Function prototypes.
LRESULT CALLBACK WndProc( HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam );
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR szCmdLine, int iCmdShow );

// In a C++ Windows app, the starting point is WinMain().
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR szCmdLine, int iCmdShow )          
{

  // these next few lines create and attach a console
  // to this process.  note that each process is only allowed one console.
  AllocConsole() ;
  AttachConsole( GetCurrentProcessId() ) ;
  freopen( "CON", "w", stdout ) ;

  printf("HELLO!!! I AM THE CONSOLE!" ) ;


  WNDCLASSEX wc = { 0 };
  wc.cbSize = sizeof( WNDCLASSEX ) ;
  wc.cbClsExtra = 0;  // ignore for now
  wc.cbWndExtra = 0;  // ignore for now
  wc.hbrBackground = (HBRUSH)GetStockObject( WHITE_BRUSH );
  wc.hCursor = LoadCursor( NULL, IDC_ARROW ); 
  wc.hIcon = LoadIcon( NULL, IDI_APPLICATION );
  wc.hInstance = hInstance;
  wc.lpfnWndProc = WndProc;
  wc.lpszClassName = TEXT("Philip");
  wc.lpszMenuName = 0;
  wc.style = CS_HREDRAW | CS_VREDRAW; // Redraw the window

  RegisterClassEx( &wc );

  HWND hwnd = CreateWindowEx( 0, TEXT("Philip"), TEXT("window's title!"), WS_OVERLAPPEDWINDOW, 10, 10, 200, 200, NULL, NULL, hInstance, NULL );      

  ShowWindow(hwnd, iCmdShow );
  UpdateWindow(hwnd);

  MSG msg;

  while( GetMessage( &msg, NULL, 0, 0 ) )
  {
    TranslateMessage( &msg );
    DispatchMessage( &msg );
  }

  return msg.wParam;    // return from WinMain
}

LRESULT CALLBACK WndProc( HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam )
{
  switch( message )
  {
  case WM_CREATE:
    // upon creation, let the speaker beep at 50Hz, for 10ms.
    Beep( 50, 10 );
    printf("HELLO!!! I AM THE CONSOLE!" ) ;
    return 0;
    break;

  case WM_PAINT:
    {
      // we would place our Windows painting code here.
      HDC hdc;
      PAINTSTRUCT ps;
      hdc = BeginPaint( hwnd, &ps );

      // draw a circle and a 2 squares
      Ellipse( hdc, 20, 20, 160, 160 );
      Rectangle( hdc, 50, 50, 90, 90 );
      Rectangle( hdc, 100, 50, 140, 90 );
      printf("HELLO!!! I AM THE CONSOLE!" ) ;

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

  case WM_LBUTTONDOWN:
    printf("STOP POKING MEEE!!!\n") ;
    break;

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

  }

  return DefWindowProc( hwnd, message, wparam, lparam );
}



Additional ref: Msdn on console handling in general

MSDN on “consoles”

Console functions on msdn

well they’re included in devIL.

Also here, uh, gives you the files via installer

The HWND of the desktop “window” is actually 0.

You can get the DC of the desktop window using:


HDC desktopDC = GetDC( HWND_DESKTOP ) ;

// or
HDC desktopDC2 = GetDC( NULL ) ;

// or
HDC desktopDC3 = GetDC( 0 ) ;

// same diff 

// don't forget to
ReleaseDC( HWND_DESKTOP, desktopDC ) ;
// when you're done!

make a screensaver in C++ win32

citing my collective sources.

MSDN link

I’ve always wanted to create a screensaver, just never got around to it.

There are a few things on codeproject that explain how to create a screensaver in C#.

But its easier in C++, in my opinion

Finally, here it is! A starter.

You CAN hook in OpenGL or whatever, but this just uses a simple backbuffer

You could easily create something like mystify or some kind of fractal thing using this starter kit

#include <windows.h>
#include <scrnsave.h>
#include <commctrl.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#ifdef UNICODE
#pragma comment(lib, "scrnsavw.lib")
#else
#pragma comment(lib, "scrnsave.lib")
#endif
#pragma comment(lib, "comctl32.lib")




// COMPILE AND RENAME .EXE FILE TO .SCR.
// DOUBLE CLICK TO LAUNCH.
// MOVE TO C:\WINDOWS\SYSTEM32 DIRECTORY
// (AS WHATEVER.SCR) AND IT WILL AUTOMATICALLY
// APPEAR IN THE DROPDOWN MENU AS A VALID
// SCREENSAVER TO CHOOSE!

// WORKS WHEN COMPILED UNDER VISUAL STUDIO 2005.

// DOES __NOT__ WORK UNDER VISUAL STUDIO 2008.



#pragma region globals
HWND winhwnd ;
HBITMAP backBMP ;
HDC backDC ;
int backBufferCX, backBufferCY ;

int totalPoints ;


#define TIMER 1
#pragma endregion




LRESULT WINAPI ScreenSaverProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
  switch(message)
  {
  case WM_CREATE:
    {
      // Create a compatible bitmap with the width, height
      // of the desktop.
      winhwnd = hwnd ; // save.

      backBufferCX = GetSystemMetrics( SM_CXVIRTUALSCREEN ) ;  // SM_CXSCREEN is just the primary.  this is BOTH.
      backBufferCY = GetSystemMetrics( SM_CYVIRTUALSCREEN ) ;

      //HDC desktopHdc = GetDC( NULL ) ; // give me the hdc of the desktop.
      HDC winhdc = GetDC( winhwnd ) ; // give me the hdc of this app.
      backBMP = (HBITMAP)CreateCompatibleBitmap( winhdc, backBufferCX, backBufferCY ) ;
      



      // now, you need to associate a dc with this
      // bitmap.  the DC holds all the info about brushes, etc.
      backDC = CreateCompatibleDC( winhdc ) ;
      ReleaseDC( winhwnd, winhdc ) ;


      // select it in.  here we have to associate
      // the DC we made (compatible with the window's hdc)
      // with the bitmap we made (compatible with the window's bitmap)
      SelectObject( backDC, backBMP ) ;

      
      totalPoints = 0 ;

      char buf[300] ;
      
      sprintf( buf, "desktop width:  %d     height:  %d", backBufferCX, backBufferCY ) ;
      TextOutA( backDC, 20, 20, buf, strlen( buf ) );
      //MessageBoxA( NULL, buf, "Debug info", MB_OK ) ;





      // Timer for animation
      SetTimer( winhwnd, TIMER, 24, NULL ); 
    }
    break;
  case WM_DESTROY:
    PostQuitMessage(0);
    break;
  case WM_PAINT:
    // blit the back buffer onto the screen
    {
      PAINTSTRUCT ps ;

      HDC hdc = BeginPaint( hwnd, &ps );
      BitBlt( hdc, 0, 0, backBufferCX, backBufferCY, backDC, 0, 0, SRCCOPY ) ;

      EndPaint( hwnd, &ps ) ;
    }
    break;

  case WM_TIMER:

    // draw an extra point in a random spot.
    SetPixel( backDC, rand()%backBufferCX, rand()%backBufferCY, RGB( 255,0,0 ) ) ;

    TextOutA( backDC, rand()%backBufferCX, rand()%backBufferCY, "B", 1 );
    totalPoints++ ;

    if( totalPoints > 300 )
    {
      // 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 keeping the system's BLACK_BRUSH

      totalPoints = 0 ;
    }

    RECT r ;
    GetClientRect( winhwnd, &r );
    InvalidateRect( hwnd, &r, false ) ;
    

    break;
  default:
    return DefScreenSaverProc(hwnd, message, wParam, lParam);
  }
  return 0;
}

BOOL WINAPI ScreenSaverConfigureDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
  return FALSE;
}

BOOL WINAPI RegisterDialogClasses(HANDLE hInst)
{
  return TRUE;
}



Get the code from my esnips (thanks esnips!)

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