If you can polymorphise from within an abstract base class member you might just be ready to start graphics lol.
Heres something very small and windows specific that i wrote as a tutorial a year ago (they were posted on NexusChat..if anyone knows what that is). Its also limited to allow drawing within the windows procedure. If you want ill upload the tutorial i wrote for after that, in which you can draw from any location.
Something sdl or allegro might be a bit lighter and easier to handle though.
/*
Tutorial :Creating a window, Loading a Bitmap and then Drawing it using simple Gdi commands.
Description :Basically the Window tutorial but with tiny changs to the LRESULT CALLBACKs definition.
Just skip to if if youve jst done the/are familiar with creating a window using a
WNDCLASSEX=>RegisterClassEx(), and returning a HWND from CreateWindowEx() ,
then updating your MSG's etc
*/
//Including Windows.h
#include<windows.h>
/*
Declare a function that handles the windows events, such as being closed, minimised etc.
The 'WndProc' part is your own choice for the name of your window procedure function.
The parameters (HWND,UINT,WPARAM,LPARAM), return type 'LRESULT' (really just 'long' )
and Calling Convention CALLBACK ( really just '__stdcall'), are the important parts
*/
LRESULT CALLBACK WndProc(HWND,UINT,WPARAM,LPARAM);
//This is (of course) the equivelant 'main', in a program that Handles a Window.
//The WINAPI(__stdcall) Calling convention is imperative
int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nShowCmd){
/*
HINSTANCE hInstance:
Specifies the instance of this program, so if there are multiple versions of this running,
each will have their unique Instance to identify them
HINSTANCE hPrevInstance:
You would assume from its name that it specifies the previous instance of the program,
bu its really just NULL.
LPSTR lpCmdLine:
The Second entry of the programs Command Line : The first is the Program itself.
The second (which is this string) is the name of the file being opened into it.
There are functions for retrieving multiple commandLine arguments where many files
are dragged into an application as it begins.
int nShowCmd:
This is a paramter for showing the window that can be used in the 'ShowWindow'
function (see below)
*/
//A Handle to the window is needed
HWND hwnd;
MSG msg;
//As is a Message structure for holding message data on the window
//A WNDCLASSEX structure is passed to a function to Register the window class with the system
WNDCLASSEX wndClass={0}; //(initialised to 0)
wndClass.cbSize=sizeof(WNDCLASSEX); //the structure contains its size as a variable
wndClass.hbrBackground=(HBRUSH)GetStockObject(GRAY_BRUSH); //Try WHITE_BRUSH,DKGRAY_BRUSH with the 'GetStockObject' function if you dont like this one...
wndClass.hCursor=LoadCursor(hInstance,IDC_ARROW); //IDC=ID of cursor => giving the WNDCLASSEX the cursor handle to be used over the window
wndClass.hIcon=LoadIcon(hInstance,IDI_WINLOGO); //IDI=ID of Icon
wndClass.hIconSm=wndClass.hIcon; //Just using the same 'small icon' as the icon. If this is NULL, the system uses wndClass.hIcon anyway.
wndClass.hInstance=hInstance; //Give it the Instance of the progra,
wndClass.lpfnWndProc=WndProc; //WndProc, the LRESULT CALLBACK function being used to handle the events
wndClass.lpszClassName="Class Name"; //The WindowClass Name: this is important, and is used throughout the Creation and destruction of the window
wndClass.style=CS_VREDRAW|CS_HREDRAW; //The WindowClass Style should be given 'ClassStyle Vertical Re-Draw' | 'ClassStyle Horizontal Re-Draw'
//Register the Class with the system
RegisterClassEx(&wndClass);
//CreateWindowEx Creates the Actual Window
hwnd=CreateWindowEx(
0, //Extra style Parameters not needed
"Class Name", //The Class Name, same as wndClass.lpszClassName
"Windows Name", //The string on the windows title bar
WS_OVERLAPPEDWINDOW, //the windows style: this style is a combination of many attributes eg: System Menu, Resizable, Title Bar
CW_USEDEFAULT, //Coordinate-Window_Use Default, i think this stands for.
CW_USEDEFAULT, //Basically the system allocates coordinates it feels appropriate: if you want though, you can give the exact position you desire
640, //Desired Width
480, //Desired Height
NULL, //Handle to parent window :we only have one and its this
NULL, //Menu Handle, again, none of which is in this
hInstance, //the programs instance (again)
0 //something else (again) that can be 0.
);
ShowWindow(hwnd,SW_NORMAL); //Show, Then Update. Doesnt seem logical but it works..
UpdateWindow(hwnd);
//The second parameter of ShowWindow() could have been the WinMain paramtere 'int nShowCmd'
//But to demonstrate more paramtere shtta can be used, Ive used 'SW_NORMAL' (ShowWindow_Normal), which
//according to MSDN should be used on the first showing of a window, contradictory to the purpose of
//nShowCmd.
//Begin out infinite Loop
for(;;){
//the GetMessage() function waits for an event to occur, and then returns.
if(GetMessage(&msg,hwnd,0,0)){
TranslateMessage(&msg);
DispatchMessage(&msg);
}
if(msg.message==WM_QUIT)break;
}
//End of Main Loop
//To Remove the window class, use UnregisterClass, passing the class name, and the Instance of that class.
UnregisterClass("Class Name",hInstance);
//Were supposed to return this from WinMain.
return msg.wParam;
}
//Fill Out the Windows Procedure function that was declared at the start of the program:
//THE FOLLOWING FUNCTION
// Is Filled with alot of Green text. Once youve read that, scroll to
//the end of the file to lookover a commentless version.
//Comments are good and everything, but if you nearly understand the code and what it does
//They Can get in the way, unless neatly organised eg: Bounding the stuctures and functions
//(In MY opinion...that is...) So ive cut the overbearing comments out in the commented version at
//the bottom of this document. irony.
LRESULT CALLBACK WndProc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam){
/*
--READ--
The Command BitBlt will be used to Draw the Bitmap onto the
Windows Device Context: The Variable HDC hdc;
It accepts two HDC's as arguments: One that will be the source,
and one that will be the destination.
The Source HDC that will be drawn onto the Destination HDC is: HDC SourceDC;
Just like HPENs and HBRUSHes, HDC's have a bitmap in them when they are originally created.
So SelectObject() is used to Select the Bitmap that we Load into the SourceDC,
and the bitmap that was originally loaded into SourceDC will be selected back into it
before it is Deleted.
-SelectObject(): Use to select the HBITMAP into the Source HDC.
-It returns an HBITMAP, which we store off in a handle (HBITMAP SwapBitmap)
-BitBlt(): Use to draw the Source HDC onto the Windows HDC. Used throughought program.
-SelectObject(): Use to Select the HBITMAP that was initially in the Source HDC, back into it:
-ie HBITMAP SwapBitmap, Selected Back into HDC SourceDC
-Delete the HDC SourceDC.
*/
/*
Static variables are being used;
If you havent come accross this, or this use if static,
a 'static' 'storage class' variable inside a function is //'storage class' refers to terms such as extern and static
one that is not Given Memory each time the function calls,
but is instead always there, and so its value will remain
the same next time the function calls, to what it was last time.
I myself consider it good practice for larger objects in functions
that are being called frequently.
*/
static HDC hdc=NULL; //The Handle that will be used for retrieving the Device Context
static HDC SourceDC=NULL; //this HDC will be used to draw the Bitmap onto HDC hdc.
static PAINTSTRUCT paint; //This is used in Getting the Device Context
static HBITMAP
hBitmap=NULL , //The Bitmap We will Load, and will swap with an HDC's bitmap
SwapBitmap=NULL ; //This will be used to keep track of the Bitmap initially in the HDC
//Due to the above being static, the initialiser '=NULL' will only take effect at
//Program initialisation, not each run of this function.
switch(msg){
case WM_CREATE: //This Message is Generated at Window Creation
//Loading the HBITMAP with LoadImage()
// First is your HINSTANCE, though this can be NULL.
// Then the Filename,
// The two 0's are the width and height of the file to be
// loaded. Leave 0 for the Bitmap to be loaded as it is.
hBitmap=(HBITMAP)LoadImage(NULL,"Bitmap.bmp",IMAGE_BITMAP,0,0,LR_LOADFROMFILE); //Typecast to an HBITMAP.
// Then, Create the SourceDC that will hold our hBitmap for Drawing purposes.
SourceDC=CreateCompatibleDC(NULL); // The Parameter is an HDC to be compatible with. Null is fine.
// Then swap the HBITMAP in the Device Context to be the one we want to draw with,
// making sure to store off the old one so it can be Deleted TOGETHER WITH the HDC.
SwapBitmap=(HBITMAP)SelectObject(SourceDC,hBitmap); //Typecasting as SelectObject can return many types of HGDIOBJ's. /Handle to Graphics Device Interface Objects.
break;
case WM_PAINT: //This Message is Generated when the windows Canvas has to be repainted
hdc=BeginPaint(hwnd,&paint);
//BitBlt
BitBlt(hdc,0,0,640,480,SourceDC,0,0,SRCCOPY);
/*
BitBlt() = It Blits Bits.
BitBlt(
HDC Destination, == Device Context to Draw to
int x, == horizontal Coordinate to Start drawing Bits to
int y, == vertical Coordinate to Start drawing bits to
int width, == number of horizontal pixels drawn from source.
int height, == number of vertical pixels drawn from source.
HDC source, == source Device Context.
int srx to start from within source,== Horizontal position from within source to start reading pixels to draw from. That didnt sound to good...
int sry to start from within source,== Horizontal position from within source to start reading pixels to draw from. That didnt sound to good...
Basically, the last two ints allow you to draw part of the source, onto the destination.
For example, a 100*200 image could be partly drawn, drawing the right half of the image:
BitBlt( destinationHDC, 0,0,50,200, SourceHDC, 50,0, SRCCOPY);
DWORD Type of Drawing operation. == SRCCOPY is used here: Copies the source pixel data and pastes the pixels over whatever is on the destination pixel, replacing it.
another flag, such as SRCPAINT, for example, will OR the pixel values such that an additive pixel is created based on the sources pixel and the destination pixel.
Because the Window is filled with Gray (or Grey as i spell it in the UK), SRCPAINT will combine the grey pixel underneath with the images pixel.
Experiment with NOTSRCCOPY, SRCAND and SRCINVERT.
);
*/
EndPaint(hwnd,&paint);
break;
case WM_DESTROY:
case WM_CLOSE:
//Select the HBITMAP that was originally in SourceDC back into it.
SelectObject(SourceDC,SwapBitmap);
//Delete the Bitmap loaded in WM_CREATE. DeleteObject() Deletes HGDIOBJ's such as HPENs and HBRUSHEs, as well as HBITMAPs.
DeleteObject(hBitmap);
//Delete the HDC, Deleting all the Objects selected into it also.
DeleteDC(SourceDC);
//Only checking the events that say the windows being closed
//To Notify the Closing of the program
PostQuitMessage(0);
//PostQuitMessage sends a WM_QUIT message to the Windows Message List,
//Which we detect in our main loop to end our program, in msg.message
return(0);
}
return DefWindowProc(hwnd,msg,wParam,lParam);
/*
Returned from this is the return of the function DefWindowProc
=>Default Window Procedure.
It handles any events you dont consider in the default manner, and has the
exact same parameters as your WndProc:
windowHandle,MessageValue,wParameter,lParameter
*/
}
/*
Just so you can see how small the CallBack function really is:
******************************************************************************
LRESULT CALLBACK WndProc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam){
static HDC hdc=NULL;
static HDC SourceDC=NULL;
static PAINTSTRUCT paint;
static HBITMAP
hBitmap=NULL ,
SwapBitmap=NULL ;
switch(msg){
case WM_CREATE:
hBitmap=(HBITMAP)LoadImage(NULL,"Bitmap.bmp",IMAGE_BITMAP,0,0,LR_LOADFROMFILE);
SourceDC=CreateCompatibleDC(NULL);
SwapBitmap=(HBITMAP)SelectObject(SourceDC,hBitmap);
break;
case WM_PAINT:
hdc=BeginPaint(hwnd,&paint);
BitBlt(hdc,0,0,640,480,SourceDC,0,0,SRCCOPY);
EndPaint(hwnd,&paint);
break;
case WM_DESTROY:
case WM_CLOSE:
SelectObject(SourceDC,SwapBitmap);
DeleteObject(hBitmap);
DeleteDC(SourceDC);
PostQuitMessage(0);
return(0);
}
return DefWindowProc(hwnd,msg,wParam,lParam);
}
//WOw. small.
******************************************************************************
Commands such as LoadImage and BitBlt arent really used well within games.
The Graphics Device Interface wasnt designed for the stress of drawing
60 frames per second. Can be done though: Just not as well as it could be in
an efficient Graphics library such as OpenGL or DirectX when it comes to games.
aab
*/