Thread: BitBlt (Yet Again)

  1. #1
    Registered User
    Join Date
    Feb 2003
    Posts
    76

    BitBlt (Yet Again)

    Hi after a recent discussion in this forum i decided to adopt the method of using the bitblt function to display my graphics. I was just creating a driver program and I can't get it to work. All I get is a black screen and I was wondering why.
    Code:
    #define WIN32_MEAN_AND_LEAN
    
    #include <windows.h>
    #include <windowsx.h>
    
    inline bool getKeyDown(int vk_key)
    {
    	if (GetAsyncKeyState(vk_key) & 0x8000)
    		return 1;
    	else
    		return 0;
    }
    
    LRESULT CALLBACK messageHandler (HWND winHandle, UINT theMsg, 
    								 WPARAM wParam, LPARAM lParam)
    {
    	PAINTSTRUCT ps;
    	HDC hdc;
    	switch (theMsg)
    	{
    	case WM_CREATE:
    		{
    			return 0;
    			break;
    		}
    	case WM_PAINT:
    		{
    			hdc = BeginPaint(winHandle,&ps);
    			EndPaint(winHandle,&ps);
    			return 0;
    			break;
    		}
    	case WM_DESTROY:
    		{
    			PostQuitMessage(0);
    			return 0;
    			break;
    		}
    	default:
    		break;
    	}
    	return (DefWindowProc(winHandle,theMsg,wParam,lParam));
    }
    
    int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine,
    					int nShowCmd)
    {
    	WNDCLASSEX winclass;
    	HWND windowsHandle1;
    	MSG theMsg;
    	winclass.cbSize = sizeof (WNDCLASSEX);
    	winclass.style = CS_DBLCLKS | CS_OWNDC | CS_HREDRAW | CS_VREDRAW;
    	winclass.cbClsExtra = 0;
    	winclass.cbWndExtra = 0;
    	winclass.hInstance = hInstance;
    	winclass.lpfnWndProc = messageHandler;
    	winclass.hIcon = LoadIcon(NULL,IDI_APPLICATION);
    	winclass.hCursor = LoadCursor(NULL, IDC_ARROW);
    	winclass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
    	winclass.lpszMenuName = NULL;
    	winclass.lpszClassName = "windyClass";
    	winclass.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
    
    	if(!(RegisterClassEx(&winclass)))
    		return 0;
    
    	if (!(windowsHandle1 = CreateWindowEx(NULL, "windyClass", "The First Window",
    		WS_OVERLAPPEDWINDOW| WS_VISIBLE , 0, 0 ,
    		640, 480, NULL, NULL, hInstance, NULL)))
    		return 0;
    		HDC hdc;
    
    	while (1)
    	{
    		if (PeekMessage(&theMsg,NULL,0,0,PM_REMOVE))
    		{
    			if(theMsg.message == WM_QUIT)
    				break;
    		TranslateMessage(&theMsg);
    		DispatchMessage(&theMsg);
    		}
    		// Create Dc And HBITMAP and put them in different things
    		hdc = GetDC(windowsHandle1);
    		HDC hdc2 = CreateCompatibleDC(NULL);
    		HBITMAP theBitmap = CreateCompatibleBitmap(hdc2,640,480);
    		SelectObject(hdc2,theBitmap);
    		// Draw a rectangle on the bitmap
    		HPEN thePen = CreatePen(PS_SOLID,1,RGB(0,255,0));
    		HBRUSH theBrush = CreateSolidBrush(RGB(0,255,0));
    		SelectObject(hdc2,thePen);
    		SelectObject(hdc2,theBrush);
    		Rectangle(hdc2,0,0,640,480);
    		// On the key C blit the bitmap to the screen
    		if (getKeyDown(VK_DOWN))
    		{
    			BitBlt(hdc,0,0,640,680,hdc2,0,0,SRCCOPY);
    		}
    	}
    	ReleaseDC(windowsHandle1,hdc);
    	return 0;
    }

  2. #2
    Master of the Universe! velius's Avatar
    Join Date
    Sep 2003
    Posts
    219
    First in a switch statement if you return you don't need to break or vice versa. Next your code to draw with is in the message loop. It should be in your message handler function during the processing of the WM_PAINT message. You have to call BeginPaint() in order to start painting and EndPaint() to stop painting. Move the code to the WM_PAINT message handler.

    Here is your updated code:
    Code:
    #include <windows.h>
    #include <windowsx.h>
    
    inline bool getKeyDown(int vk_key)
    {
    	if (GetAsyncKeyState(vk_key))
    	return 1;
    		else
    	return 0;
    }
    
    LRESULT CALLBACK messageHandler (HWND winHandle, UINT theMsg, WPARAM wParam, LPARAM lParam)
    {
    	PAINTSTRUCT ps;
    	HDC hdc;
    	HDC hdc2;
    	HBITMAP theBitmap;
    	HPEN thePen;
    	HBRUSH theBrush;
    	static bool state;
    	switch (theMsg)
    	{
    	case WM_KEYDOWN:
    		if (wParam == VK_DOWN)
    		{
    			InvalidateRect(winHandle, NULL, true);
    			state = 1;
    		}
    		return 0;
    	case WM_CREATE:
    		return 0;
    	case WM_PAINT:
    		hdc = BeginPaint(winHandle,&ps);
    		hdc2 = CreateCompatibleDC(NULL);
    		theBitmap = CreateCompatibleBitmap(hdc2,640,480);
    		SelectObject(hdc2,theBitmap);
    		// Draw a rectangle on the bitmap
    		thePen = CreatePen(PS_SOLID,1,RGB(0,255,0));
    		theBrush = CreateSolidBrush(RGB(0,255,0));
    		SelectObject(hdc2,thePen);
    		SelectObject(hdc2,theBrush);
    		Rectangle(hdc2,0,0,640,480);
    		// On the key C blit the bitmap to the screen
    		if (state)
    			BitBlt(hdc,0,0,640,680,hdc2,0,0,SRCCOPY);
    		ReleaseDC(winHandle,hdc);
    		EndPaint(winHandle,&ps);
    		return 0;
    	case WM_DESTROY:
    		PostQuitMessage(0);
    		return 0;
    	default:
    		return (DefWindowProc(winHandle,theMsg,wParam,lParam));
    	}
    }
    
    int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
    {
    	WNDCLASSEX winclass;
    	HWND windowsHandle1;
    	MSG theMsg;
    	winclass.cbSize = sizeof (WNDCLASSEX);
    	winclass.style = CS_DBLCLKS | CS_OWNDC | CS_HREDRAW | CS_VREDRAW;
    	winclass.cbClsExtra = 0;
    	winclass.cbWndExtra = 0;
    	winclass.hInstance = hInstance;
    	winclass.lpfnWndProc = messageHandler;
    	winclass.hIcon = LoadIcon(NULL,IDI_APPLICATION);
    	winclass.hCursor = LoadCursor(NULL, IDC_ARROW);
    	winclass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
    	winclass.lpszMenuName = NULL;
    	winclass.lpszClassName = "windyClass";
    	winclass.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
    
    	if(!(RegisterClassEx(&winclass)))
    		return 1; // you should return 1 to indicate an error
    
    	if (!(windowsHandle1 = CreateWindowEx(NULL, "windyClass", "The First Window",
    		WS_OVERLAPPEDWINDOW| WS_VISIBLE , 0, 0 ,
    		640, 480, NULL, NULL, hInstance, NULL)))
    	return 1; // you should return 1 to indicate an error
    
    	while (1)
    	{
    		if (PeekMessage(&theMsg,NULL,0,0,PM_REMOVE))
    		{
    			if(theMsg.message == WM_QUIT)
    			break;
    			TranslateMessage(&theMsg);
    			DispatchMessage(&theMsg);
    		}
    	}
    
    	return 0;
    }
    While you're breakin' down my back n'
    I been rackin' out my brain
    It don't matter how we make it
    'Cause it always ends the same
    You can push it for more mileage
    But your flaps r' wearin' thin
    And I could sleep on it 'til mornin'
    But this nightmare never ends
    Don't forget to call my lawyers
    With ridiculous demands
    An you can take the pity so far
    But it's more than I can stand
    'Cause this couchtrip's gettin' older
    Tell me how long has it been
    'Cause 5 years is forever
    An you haven't grown up yet
    -- You Could Be Mine - Guns N' Roses

  3. #3
    Registered User
    Join Date
    Feb 2003
    Posts
    76
    First thanks for the help, but now im getting really confused. I was taught that the WM_PAINT message is only called when the window needs to be refresed. I also was told that you should try and keep your emssage handler as small as possible and not make it overly complex. Hense why I deal with the keyboard outside the handler.
    Is in not possible to deal with grahics outside the message handler because it just makes a lot more sense to me and in my opinion makes your code a lot cleaner.
    The problem I now have is that it just creates loads of white squares. I want it tocreate a green rectangle like the function suggests it should do.
    Can anyone just do a specific example of the bare minimum you need to do to copy a coulour image from your bitmap to the screen. Including all the HDC's that you have to declare. Make thanks in advance

  4. #4
    jasondoucette.com JasonD's Avatar
    Join Date
    Mar 2003
    Posts
    278
    The original code also had the problem of not releasing the DC for every time it 'Gets' it.

    You are right, the WM_PAINT message is for refreshing windows, BUT, it must refresh any animation that may be going on in the window, therefore it should be closely tied to any such drawing procedures. Basically, you can have your WM_PAINT draw a bitmap to paint the client area, and you can have other code that changes the bitmap and forces a WM_PAINT message by InvalidateRect() (it places a WM_PAINT message in the queue), and perhaps even UpdateWindow() (it forces the application to handle any WM_PAINT message that is in the queue immediately, but it does NOT place a WM_PAINT message in the queue itself), to make sure this update to the bitmap is seen. This way only one procedure actually draws to the window (i.e. there is no need for code duplication), and WM_PAINT handles both the animation drawing, and refreshing drawing (which correctly handles refreshing the window during the middle of some animation, as well, without any extra code).

  5. #5
    Yes, my avatar is stolen anonytmouse's Avatar
    Join Date
    Dec 2002
    Posts
    2,544
    /*
    I have fixed the code with comments included inline.
    Use the up and down keys to toggle colour/color.
    Some things to note:
    - When you use SelectObject you must save the old object and put it at cleanup.
    - When you create a gdi object you must destroy it. The function to do this with can be found in the docs of the creation function.
    - Code is much more readable when you use functions as below.
    - Use names like hBitmap instead of theBitmap. The h signifies the variable is a handle to an object.
    - There is similar code (without the double buffering) available here:
    http://msdn.microsoft.com/library/en...olors_5noz.asp
    */

    Code:
    #include <windows.h>
    #include <windowsx.h>
    
    static BOOL state;
    
    void PaintWindow(HWND hwnd, HDC hdc) {
    
    	HDC hdc2;
    	HBITMAP theBitmap;
    	HBITMAP hOldBitmap;
    	HPEN thePen;
    	HPEN hOldPen;
    	HBRUSH theBrush;
    	HBRUSH hOldBrush;
    
    	hdc2 = CreateCompatibleDC(hdc);
    	// Note: We pass the hdc of the original window - not the 
    	// created hdc which is still monochrome...
    	theBitmap = CreateCompatibleBitmap(hdc,640,480);
    
    	// Save old bitmap so we can restore it later...
    	hOldBitmap = SelectObject(hdc2,theBitmap);
    
    	// Create a pen and brush to draw with...
    	thePen = CreatePen(PS_SOLID,1,RGB(0,255,0)); // Green
    	theBrush = CreateSolidBrush(RGB(0,255,0));   // Green
    
    	// Put new objects into our hdc2.
    	// Save old objects so we can restore them later...
    	hOldPen = SelectObject(hdc2,thePen);
    	hOldBrush = SelectObject(hdc2,theBrush);
    
    	// Draw a rectangle on the bitmap...
    	Rectangle(hdc2,0,0,640,480);
    
    	// On the key C blit the bitmap to the screen
    	// Note that all of this code should be surrounded with
    	// if (state)... Otherwise it is a total waste.
    	if (state) BitBlt(hdc,0,0,640,680,hdc2,0,0,SRCCOPY);
    
    	// Now put back the old stuff into hdc2...
    	SelectObject(hdc2, hOldBitmap);
    	SelectObject(hdc2, hOldPen);
    	SelectObject(hdc2, hOldBrush);
    
    	// Destroy the stuff we created...
    	DeleteObject(theBitmap);
    	DeleteObject(thePen);
    	DeleteObject(theBrush);
    	DeleteDC(hdc2);
    
    }
    
    LRESULT CALLBACK messageHandler (HWND winHandle, UINT theMsg, WPARAM wParam, LPARAM lParam)
    {
    	switch (theMsg)
    	{
    	case WM_KEYDOWN:
    		if (wParam == VK_DOWN)
    		{
    			state = TRUE;
    			InvalidateRect(winHandle, NULL, TRUE);
    		}
    		else if (wParam == VK_UP) {
    			state = FALSE;
    			InvalidateRect(winHandle, NULL, TRUE);
    		}
    		return 0;
    	case WM_CREATE:
    		return 0;
    	case WM_PAINT: {
    
    		PAINTSTRUCT ps;
    		HDC hdc;
    
    		// Note that BeginPaint will fill the update region
    		// with the background brush. This will cause flicker and
    		// defeats the purpose of double buffering!!
    		hdc = BeginPaint(winHandle,&ps);
    
    		PaintWindow(winHandle, hdc);
    
    		// NOTE: We do not call ReleaseDC on hdc.
    		// This is only required when we obtained it with GetDC, etc.
    		EndPaint(winHandle,&ps);
    
    		return 0;
    	}
    	case WM_DESTROY:
    		PostQuitMessage(0);
    		return 0;
    	default:
    		return (DefWindowProc(winHandle,theMsg,wParam,lParam));
    	}
    }
    
    int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
    {
    	WNDCLASSEX winclass;
    	HWND windowsHandle1;
    	MSG theMsg;
    	BOOL bRet;
    
    	winclass.cbSize = sizeof (WNDCLASSEX);
    	winclass.style = CS_DBLCLKS | CS_OWNDC | CS_HREDRAW | CS_VREDRAW;
    	winclass.cbClsExtra = 0;
    	winclass.cbWndExtra = 0;
    	winclass.hInstance = hInstance;
    	winclass.lpfnWndProc = messageHandler;
    	winclass.hIcon = LoadIcon(NULL,IDI_APPLICATION);
    	winclass.hCursor = LoadCursor(NULL, IDC_ARROW);
    	winclass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
    	winclass.lpszMenuName = NULL;
    	winclass.lpszClassName = "windyClass";
    	winclass.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
    
    	if(!(RegisterClassEx(&winclass)))
    		return 1; // you should return 1 to indicate an error
    
    	if (!(windowsHandle1 = CreateWindowEx(0, "windyClass", "The First Window",
    		WS_OVERLAPPEDWINDOW| WS_VISIBLE , 0, 0 ,
    		640, 480, NULL, NULL, hInstance, NULL)))
    	return 1; // you should return 1 to indicate an error
    
    	// Unless you have a good reason, use GetMessage loop
    	// and not PeekMessage...
    	while ( bRet = GetMessage(&theMsg, NULL, 0, 0) ) {
    
    		if (bRet == -1) break;
    
    		TranslateMessage(&theMsg);
    		DispatchMessage(&theMsg);
    	}
    
    	return 0;
    }

  6. #6
    Registered User
    Join Date
    Feb 2003
    Posts
    76
    Hi thanks to all you guys for your help. But I dont wanan change my coding habbits so i asked someone on ICQ. About hit and he had a look and said use CreateBitmap instead of CreateCompatibleBitmap. It sacrfises a bit of speed but at least it works the way its meant too.
    Eventually I want to move to direct x but all this COM rubbish is putting me off lol.

    Thanks again all

  7. #7
    train spotter
    Join Date
    Aug 2001
    Location
    near a computer
    Posts
    3,868
    >>But I dont wanan change my coding habbits so i asked someone on ICQ. About hit and he had a look and said use CreateBitmap instead of CreateCompatibleBitmap.<<

    And create a BMP that may not be the same as your HDC.

    Not only that it will not solve your problem....

    from MSDN
    "However, the bitmap can only be selected into a device context if the bitmap and the DC have the same format."

    Thats why we use the compatible version.

    >> The problem I now have is that it just creates loads of white squares.

    If the squares remain then set the dhc to white first (use BitBlt( , , , , , , , WHITENESS) or fill the whole area with a white pen.

    The white rectangle could be green in a black and white image...

    >>- There is similar code (without the double buffering) available here:

    This example is not 'double buffered'.

    Double buffering keeps a copy of the screen DC to draw to the paint hdc WHILE it uses another copy of teh screen hdc to create the next image. Then the app 'swaps' them and continues.
    This way the app does not have to redraw its hdc in response to EVERY paint msg.
    It has a frame ready if an external event triggers a paint ( ie other app moves across your apps client area).
    "Man alone suffers so excruciatingly in the world that he was compelled to invent laughter."
    Friedrich Nietzsche

    "I spent a lot of my money on booze, birds and fast cars......the rest I squandered."
    George Best

    "If you are going through hell....keep going."
    Winston Churchill

  8. #8
    Yes, my avatar is stolen anonytmouse's Avatar
    Join Date
    Dec 2002
    Posts
    2,544
    > The problem I now have is that it just creates loads of white squares.

    >If the squares remain then set the dhc to white first (use BitBlt( , , , , , , , WHITENESS) or fill the whole area with a white pen.

    This problem was caused by the original code passing the wrong hdc to CreateCompatibleBitmap (ie. The buffer hdc instead of the original). This meant that the created bitmap was monochrome and quite useless.

    >This example is not 'double buffered'.

    >Double buffering keeps a copy of the screen DC to draw to the paint hdc WHILE it uses another copy of teh screen hdc to create the next image. Then the app 'swaps' them and continues.
    This way the app does not have to redraw its hdc in response to EVERY paint msg.
    It has a frame ready if an external event triggers a paint ( ie other app moves across your apps client area).

    Cool. Can we call this example single buffered then?

    Anyway the code works and the original poster can use it if they wish.

  9. #9
    Registered User
    Join Date
    Feb 2003
    Posts
    76
    Yeah thanks everyone. I sort of grasp the concept. i created a demo program using a Compatible Bitmap and it works. I also put it in its own functionw hich is called by the windows paint emssage. I would like to take this time to thank everyone who has helped me through this. God Bless You All !!!!

  10. #10
    Carnivore ('-'v) Hunter2's Avatar
    Join Date
    May 2002
    Posts
    2,879
    You don't necessarily have to do your drawing in WM_PAINT. Personally, I don't.. I made a GDI graphics wrapper class for my game Space Shooterz, which uses GetDC() to get a permanent HDC that can be drawn to whenever you want.

    The code isn't bug-free (?), but it works well enough for me. If you want an alternate solution to using WM_PAINT all the time (although it seems to be the preferred method), you can take a look at my code at the link in my signature (download Space Shooterz, the wrapper class is in GFX.cpp/h).
    Just Google It. √

    (\ /)
    ( . .)
    c(")(") This is bunny. Copy and paste bunny into your signature to help him gain world domination.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. BitBlt()
    By Gordon in forum Windows Programming
    Replies: 2
    Last Post: 05-29-2008, 12:38 AM
  2. Which BitBlt Raster Op?
    By SMurf in forum Windows Programming
    Replies: 1
    Last Post: 03-01-2006, 01:52 PM
  3. another BitBlt question..
    By btq in forum Windows Programming
    Replies: 6
    Last Post: 10-11-2002, 04:28 PM
  4. bitblt
    By canine in forum Windows Programming
    Replies: 3
    Last Post: 07-10-2002, 12:17 AM
  5. Problems with BitBlt
    By Isometric in forum Windows Programming
    Replies: 6
    Last Post: 02-05-2002, 09:20 PM