Archived

This topic is now archived and is closed to further replies.

Coaster Kev

Redrawing Window

Recommended Posts

I''ve been working with a program of mine, and I''m having problems with the GDI. I have a function that renders various bitmaps to the screen. Here is the function:
  
int RenderToWindow(HWND hwnd)
{
	HDC hdc;
	HDC memory;
	PAINTSTRUCT ps;
	BITMAP bm;
	
	hdc = BeginPaint(hwnd, &ps);
	memory = CreateCompatibleDC(hdc);
	
	DeleteObject(SelectObject(memory, chessBoard));
	GetObject(chessBoard, sizeof(BITMAP), &bm);
	
	BitBlt(hdc, 0, 0, bm.bmWidth, bm.bmWidth, memory, 0, 0, SRCCOPY);
	
	CPiece	*displayPiece = new CPiece;
	displayPiece = gameBoard->GetBlack();
	
	while(displayPiece != NULL)
	{
		displayPiece->DisplayGraphic(hwnd, hdc, memory);
		displayPiece = displayPiece->GetNextPiece();
	}
	
	displayPiece = gameBoard->GetWhite();
	
	while(displayPiece != NULL)
	{
		displayPiece->DisplayGraphic(hwnd, hdc, memory);
		displayPiece = displayPiece->GetNextPiece();
	}
	
	DeleteDC(memory);
	DeleteDC(hdc);
	EndPaint(hwnd, &ps);
	
	return 0;
}
  
This function will work fine, the first time the program runs. The DisplayPiece function displays the piece bitmaps, with background transparency. Now, I had this function in my game loop.. but i know that''s a bad idea because i don''t want to redraw anything if there is nothing to update. Here''s the thing though, everytime this function is called, there is no affect on the screen. I''m confused why. Is there something I am supposed to do before attempting to re-render the graphics to the window? Thanks a lot for any information or tips you can give me. I can post more code if needed.

Share this post


Link to post
Share on other sites
A few things to go over here:

BeginPaint/EndPaint should only be called in response to a WM_PAINT msg. That is more than likely why you''re seeing no affect on the screen in your gaming loop call. The explanation for this has to do with Windows and invalidation rects/rgns and I don''t want to get into that now. The thing to remember is never call BeginPaint and EndPaint except in response to WM_PAINT. So, what you should do is have a HDC passed to your Render function. In your WM_PAINT processing that HDC will come from your BeginPaint call. In your normal gaming loop you would call GetDC to obtain a HDC for your rendering window and then pass that HDC to your Render function. Once your done with it you would then call ReleaseDC.

Never DeleteDC a HDC that you got from a call to either BeginPaint or GetDC. In fact, never do anything to a HDC gotten from BeginPaint. Simply call EndPaint and leave it at that. For a HDC you get from a call to GetDC, call ReleaseDC when you''re done with it. The only time you call DeleteDC is for a DC that you have created, such as your memory HDC.

Before you get rid of a HDC via DeleteDC, you should first un-select any objects that you have selected into them. Also, it''s not a good habit to get into deleting objects that have their handle returned to you via SelectObject calls. Standard practice is to SelectObject the object you''re wanting to use, and saving off the handle returned via SelectObject to a temporary HGDIOBJ variable. Then, once you''re done with the HDC, and prior to the HDCs deletion, select the saved variable object handle back into the HDC. Then DeleteDC the HDC that you created.

Finally, you''re creating a new CPiece object and then immediately stepping on its pointer via the gameBoard->GetBlack() call. Since you''ve lost that object you''ll never be able to delete it later, resulting in a memory leak every time this function is called.

My version of your function would look something like this (but with the appropriate checks for NULL handles and such):


int RenderToWindow(HWND hwnd, HDC hdc)
{
BITMAP bm;
HDC memory = CreateCompatibleDC(hdc);
HGDIOBJ hOldBitmap = SelectObject(memory, chessBoard);
GetObject(chessBoard, sizeof(BITMAP), &bm);
BitBlt(hdc, 0, 0, bm.bmWidth, bm.bmWidth, memory, 0, 0, SRCCOPY);
CPiece *displayPiece;
displayPiece = gameBoard->GetBlack();
while(displayPiece)
{
displayPiece->DisplayGraphic(hwnd, hdc, memory);
displayPiece = displayPiece->GetNextPiece();
}
displayPiece = gameBoard->GetWhite();
while(displayPiece)
{
displayPiece->DisplayGraphic(hwnd, hdc, memory);
displayPiece = displayPiece->GetNextPiece();
}
SelectObject(memory, hOldBitmap);
DeleteDC(memory);
return 0;
}


The WM_PAINT processing code could then call this function as such:


case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hDC = BeginPaint(hwnd, &ps);
RenderToWindow(hwnd, hDC);
EndPaint(hwnd, &ps);
}
break;


And your gaming loop call could be:


// do stuff to set up next frame (animation and such)
// stuff ...
// more stuff ...
//
// now display the new frame
//
HDC hDC = GetDC(hwnd);
RenderToWindow(hwnd, hDC);
ReleaseDC(hwnd, hDC);


HTH.

Share this post


Link to post
Share on other sites
Thanks! That works great. There is one more problem I am having. The pieces have a transparent background color that I am using. When the window redraw, the transparent backgrounds turn black. Here is the code for displaying the pieces:


  
void CPiece::DisplayGraphic(HWND hwnd, HDC window, HDC memory)
{
// convert x and y to pixel location

int xPix = pixLocation[xLocation][yLocation].x;
int yPix = pixLocation[xLocation][yLocation].y;
// plot graphic


COLORREF myC = RGB(0, 255, 0);
HBITMAP mask = CreateBitmapMask(graphic, myC);

BITMAP bm;
GetObject(graphic, sizeof(BITMAP), &bm);

SelectBitmap(memory, mask);
BitBlt(window, xPix, yPix, bm.bmWidth, bm.bmHeight, memory, 0, 0, SRCAND);

SelectBitmap(memory, graphic);
BitBlt(window, xPix, yPix, bm.bmWidth, bm.bmHeight, memory, 0, 0, SRCPAINT);

if (selected)
{
HPEN pen;

pen = CreatePen(PS_DASH, 0, RGB(255, 255, 0)); // yellow pen

SelectObject(window, pen);
Rectangle(window, xPix, yPix, xPix + 50, yPix + 50);
}

}



Also.. I''ve been playing around with the Pen at the end of the function to display a yellow rectangle around a display that has been selected. The yellow displays fine, but the inside of the rectange is white. I want the inside to be transparent. Thanks a lot for everything.

Share this post


Link to post
Share on other sites
Huh, this looks familiar for some reason? Have you posted it before?

Anyway, does CreateBitmapMask create a mask bitmap with white for the transparent pixels and black for the mask pixels, or the other way around?

If it creates a mask bitmap with white for the transparent pixels, then your code should work fine, assuming your graphic bitmap has black pixels for the transparent area.

If it creates a mask bitmap with black for the transparent pixels, then you'll have to flip around your BitBlt ROPs. Blit the mask with a SRCPAINT then blit the graphic with a SRCAND. In this case, for this to work, your graphic bitmap must have white pixels for the transparent area.

I'm also wondering what SelectBitmap does, as I have not seen it before in standard GDI. Is it a GDI+ function?

Re: the yellow rectangle, Rectangle fills with the DC's current brush, so you need to use SelectObject(window, GetStockObject(NULL_BRUSH)) before the call to Rectangle. If you don't need the dash effect of the pen, you could just use FrameRect to draw the rectangle, since it doesn't fill the inner portion.

One last thing. You need to DeleteObject your pen that you created to draw the rectangle. It looks like you should also DeleteObject the mask bitmap that I'm assuming was created somewhere in the CreateBitmapMask function.

Have fun.

[edited by - darrell l on November 7, 2002 1:04:28 PM]

Share this post


Link to post
Share on other sites
Here is the CreateBitmapMask function:


  
HBITMAP CreateBitmapMask(HBITMAP hbmColour, COLORREF crTransparent)
{
HDC hdcMem, hdcMem2;
HBITMAP hbmMask;
BITMAP bm;

/*
Create mask bitmap.
*/
GetObject(hbmColour, sizeof(BITMAP), &bm);
hbmMask = CreateBitmap(bm.bmWidth, bm.bmHeight, 1, 1, NULL);

hdcMem = CreateCompatibleDC(0);
hdcMem2 = CreateCompatibleDC(0);

SelectBitmap(hdcMem, hbmColour);
SelectBitmap(hdcMem2, hbmMask);

/*
Set the background colour of the colour image to the colour
you want to be transparent.
*/
SetBkColor(hdcMem, crTransparent);
/*
Copy the bits from the colour image to the B+W mask... everything
with the background colour ends up white while everythig else ends up
black...Just what we wanted.
*/
BitBlt(hdcMem2, 0, 0, bm.bmWidth, bm.bmHeight, hdcMem, 0, 0, SRCCOPY);
/*
Take our new mask and use it to turn the transparent colour in our
original colour image to black so the transparency effect will
work right.
*/
BitBlt(hdcMem, 0, 0, bm.bmWidth, bm.bmHeight, hdcMem2, 0, 0, SRCINVERT);
/*
Clean up.
*/

DeleteDC(hdcMem);
DeleteDC(hdcMem2);

return hbmMask;
}


This isn''t my own code.. it''s borrowed. Also, the images have a green background (RGB(0, 255, 0)). I''m not really sure what your last message is suggesting. Thanks for all your help!

Share this post


Link to post
Share on other sites
OK. That source code helped. It was doing a little more than I thought.

What you need to do is:

1) Make a "mask" HBITMAP member variable in whatever class the "graphic" HBITMAP variable is defined in.

2) Comment out the call to CreateBitmapMask in the CPiece::DisplayGraphic function.

3) Put a call to CreateBitmapMask in a line following the line where you load the "graphic" HBITMAP (I'm assuming you have a call to LoadImage or LoadBitmap to do this.)

4) Put in a DeleteObject of the "mask" HBITMAP at the same place your calling DeleteObject for the "graphic" HBITMAP.

The reason the chess pieces were being drawn with a black background has to do with the SRCINVERT blit that's in the CreatBitmapMask function, and the fact that you were calling that function, for every "graphic" bitmap, every time you wanted to draw the chess piece. What happens is this: The first call to the function converts all of the green pixels in the "graphic" bitmap to black via the SRCINVERT blit. The second call then tried to make a new "mask" for you, but there were no green pixels anymore. So what you ended up with was a "mask" that was completely white, causing the original green pixels, which were now black, to be drawn instead of masked out.

I hope this makes more sense than my last post. If not I'll let someone else give it a go 'cause I'm starting to think I'm just confusing things.

[edited by - darrell l on November 7, 2002 5:22:30 PM]

Share this post


Link to post
Share on other sites