Archived

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

shalom

Memory DC's

Recommended Posts

shalom    122
Hi: I''m writing a game in Visual Basic. VB isn''t very good with graphics, so everyone bypasses the VB graphics and uses the GDI. For some reason, though, no-one makes their own device contexts. Everyone uses invisible PictureBox controls (a simple VB control that displays pictures) because they have a DC property. For example, if someone wanted to blit a transparent picture on to the window, they would have three PictureBox controls, let''s say Pic1, Pic2, and Pic3. They would load the picture they want to blit onto in Pic1, the picture they want to blit in Pic2, and a mask in Pic3. They would import the GDI BitBlt function and use the following VB code: BitBlt Pic1.hDC, 0, 0, 100, 100, Pic3.hDC, 0, 0, SRCAND BitBlt Pic1.hDC, 0, 0, 100, 100, Pic2.hDC, 0, 0, SRCPAINT BitBlt Me.hDC, 0, 0, 100, 100, Pic1.hDC, 0, 0, SRCCOPY. This is all fine if your doing a little animation or something, but if I want to make a full game, perhaps using independant classes that may not have access to windows and their PictureBoxes, how do I use the GDI to make memory DC''s? I can''t find it anywhere. How do C programmers, who don''t have access to PictureBox controls, make memory DCs that can be used to temporarily Blt on to and off again? Thanx in advance, Shalom

Share this post


Link to post
Share on other sites
Mr Ekted    122
Windows apps have one or more windows. For any windows you can gets its DC by calling GetDC(hwnd) where hwnd is the handle of the window. This is usually done in response to a paint message (WM_PAINT) but can be done any time. This is what I do...

void Paint (HWND hwnd)
{
HDC dc = GetDC(hwnd);
int savedc = SaveDC(dc);

// there are many DC related draw functions:
// SelectObject(), MoveTo(), LineTo(), SetPixel(),
// BitBlt(), TextOut(), Rectangle(), Ellipse(),
// SetTextColor(), SetTextAlign(), on and on

RestoreDC(dc, savedc);
ReleaseDC(hwnd, dc);
}

DC''s have a ton of state information that you have to make sure is restored before you release them, or you get GDI leaks, so I always save/restore the whole thing.

Share this post


Link to post
Share on other sites
shalom    122
Hi, I can access the window''s DC, but how do I make a temporary DC? For example, let''s take a simple case - double buffering. How would I make a little animation in an imaginary DC and then BitBlt from that imaginary DC to the window''s DC? How do I make a sort of "buffer" DC?

Thanks,
Shalom

Share this post


Link to post
Share on other sites
Guest Anonymous Poster   
Guest Anonymous Poster
hi shalom,

Device Contexts are just some sort of a "reference" to the device and they don''t anything on their own (they don''t hold the picture, they just point to the device that displays it...), and for that reason the term "temporary dc" doesn''t really mean anything cause it''s just a refernce to the device and you have to handle some sort of buffer yourself(by maybe having something hidden you draw into and then copying for the hidden object to the display object).

this is just a thought, havn''t been using VB for grahpics or the such

Share this post


Link to post
Share on other sites
shalom    122
In other words, how do I make a buffer that I can point a device context to, with out having access to a window with my invisible objects and whatever?

Share this post


Link to post
Share on other sites
Mike    141
I''m just writing this off the top of my head and its been a while since I''ve used DCs, but I think this will work:

  
HDC buffer;
HDC window;
HBITMAP temp;

window = GetDC( window_hWnd );
buffer = CreateCompatibleDC( window );

temp = CreateCompatibleBitmap( buffer, width_of_image, height_of_image );

SelectObject( buffer, temp );
DeleteObject( temp );
ReleaseDC( window_hWnd, window );


That will create an offscreen DC you can use to draw to. You must create a bitmap object and select it into the DC or you won''t be able to use the DC. I''m sure that I messed up some of the parameters (I don''t remember them all), but you should be able to look up the API functions and see what the parameters should be.

I don''t really remember VB that well, but I''d guess that the VB code would be something like:

  
DIM buffer as Long
DIM window as Long
DIM temp as Long

Set window = GetDC( Form1.hWnd )
Set buffer = CreateCompatibleDC( window );
Set temp = CreateCompatibleBitmap( buffer, width, height )

Call SelectObject( buffer, temp )
Call DeleteObject( temp );
Call ReleaseDC( Form1.hWnd, window );


Im assuming that since you use BitBlt you know how to declare APIs for use in VB.

If you want direct access to the bits being displayed, I recomend that you create the bitmap object that you are going to select into the buffer DC with CreateDIBSection. That way you will have a pointer to the actual bits which will allow you to do things like alpha blending (I realize that alpha blending can be done with GetPixel/SetPixel, but that is to slow).

Feel free to email me any questions you might have
Mike010@netZero.net

Share this post


Link to post
Share on other sites
shalom    122
Thank you so much, Mike. It seems silly to get stuck on such a simple point. I have my entire game planned out and most of it programmed, and I don''t know how to use the GDI! Well, I''ll try it out.

Share this post


Link to post
Share on other sites
shalom    122
Oh no! I used the following code exactly, and it makes a square the right size in the top left corner of the window, but its all black! What did I do wrong?
  Option Explicit

Private Sub Form_Load()
Dim buffer As Long
Dim window As Long
Dim temp As Long
window = GetDC(Form1.hwnd)
buffer = CreateCompatibleDC(window)
temp = CreateCompatibleBitmap(buffer, Picture1.ScaleWidth, Picture1.ScaleHeight)
SelectObject buffer, temp
DeleteObject temp
ReleaseDC Form1.hwnd, window
BitBlt buffer, 0, 0, Picture1.ScaleWidth, Picture1.ScaleHeight, Picture1.hdc, 0, 0, SRCCOPY
BitBlt Me.hdc, 0, 0, Picture1.ScaleWidth, Picture1.ScaleHeight, buffer, 0, 0, SRCCOPY
Form1.Refresh
DeleteDC buffer
End Sub


Sigh... I''m causing so much trouble with such a simple problem. Oh well.

Share this post


Link to post
Share on other sites
shalom    122
I think that the problem is that the first BitBlt call is not working because I just read that in WinNT (I have 2000), all the pixels are initialized to black, while in Windows 9x/ME they are not initialized. Does anyone see why this call might not go through?

Share this post


Link to post
Share on other sites
Mike    141
I don''t have access to VB here at work. I''ll test it when I get home; in the mean time, here are some things to check:

1) what are your scale units? are you certain that they are pixels?
2) do you have an image loaded into the picture box? i''m guessing you do.
3) are you sure you can Blt to the form itself? I remember always using a picture box as my final destination (I don''t remember why though).

My best guess...

I''m wondering, does the Load function get called before your image is loaded into the picture box? try adding a button to your form that you can click on so that you are certain that the image has been loaded into the picture box.

If your form has an .hDC property, then you do not need to call GetDC. You can just use From1.hDC (or Me.hDC).

Also, why load an image into a picture box, then copy it to a buffer? Are you just doing this for testing purposes?

Share this post


Link to post
Share on other sites
shalom    122
quote:
Original post by Mike
1) what are your scale units? are you certain that they are pixels?
2) do you have an image loaded into the picture box? i''m guessing you do.
3) are you sure you can Blt to the form itself? I remember always using a picture box as my final destination (I don''t remember why though).

My best guess...

I''m wondering, does the Load function get called before your image is loaded into the picture box? try adding a button to your form that you can click on so that you are certain that the image has been loaded into the picture box.

If your form has an .hDC property, then you do not need to call GetDC. You can just use From1.hDC (or Me.hDC).

Also, why load an image into a picture box, then copy it to a buffer? Are you just doing this for testing purposes?

I have updated the code to this and it still comes up with the same black box:
  
Option Explicit

Private Sub Command1_Click()
Dim buffer As Long
Dim temp As Long
buffer = CreateCompatibleDC(hdc)
temp = CreateCompatibleBitmap(buffer, Picture1.ScaleWidth, Picture1.ScaleHeight)
SelectObject buffer, temp
DeleteObject temp
BitBlt buffer, 0, 0, Picture1.ScaleWidth, Picture1.ScaleHeight, Picture1.hdc, 0, 0, SRCCOPY
BitBlt Me.hdc, 0, 0, Picture1.ScaleWidth, Picture1.ScaleHeight, buffer, 0, 0, SRCCOPY
Form1.Refresh
DeleteDC buffer
End Sub

I''m certain everythings in pixels. I have a loaded image and I just tried it with a PictureBox and it came up with the same thing.
BTW - Thanx for all your help.

Share this post


Link to post
Share on other sites
Mike    141
Well... I''m not sure what the problem is. My only remaining recomendations are to change the HDC you use in the call to CreateCompatibleBitmap to the HDC of the form instead of the buffer, and try hard coding in the numbers.

When I get home from work I''ll try it out and see what I can come up with.

Share this post


Link to post
Share on other sites
shalom    122
I figured it out!
I was fiddling around with the code and trying anything. The first argument of CreateCompatibleBitmap should NOT be the buffer dc created by CreateCompatibleDC, it should be the form''s DC.
Thanks for all your help, Mike, now I can get on with the more interesting parts of my game. It''s amazing how sometimes games get stuck over little things.

Shalom

Share this post


Link to post
Share on other sites
Mr Ekted    122
WARNING! You rlast posted code LEAKS the bitmap. You cannot call DeleteObject() on an object selected into a DC. You must first either seelct the original bitmap back into the DC (return value of SelectObject) or SaveDC/RestoreDC like I showed you in my code. It may be minor in a simple app, but if you paint hundreds of times, your display/system will start to get wierd because of the GDI handles being eaten up.

Share this post


Link to post
Share on other sites
shalom    122
Thanks for that information, Mr Ekted. That''s actually quite important because I''m writing an isometric engine which involves hundreds of tiles. Though it''s incovenient because it means I''ll have to keep track of all the hold stuff that was selected out of the DC, but I suppose that only means a few more lines of code.

Share this post


Link to post
Share on other sites
shalom    122
I don''t want any memory leaks or extra private class variables, so please tell me if this hypothetical class is correct:

  class MakeDC{
private:
HDC DC;
Whatever buffer; //In VB the variable types are all

Whatever oldContents; //long anyways, I don''t need a

//real format

MakeDC();
~MakeDC(); //I don''t remember the C++ syntax for destructors

};

MakeDC::MakeDC(){
DC=CreateCompatibleDC(MyWindowDC);
buffer=CreateCompatibleDC(MyWindowDC, MyWidth, MyHeight);
oldContents=SelectObject(DC,buffer);
}

MakeDC::~MakeDC() {
SelectObject(DC,oldContents);
DeleteObject(buffer);
DeleteDC(DC);
}

So, am I correct in saying that that covers all memory leaks?
Thanks, everyone, for their help.

Share this post


Link to post
Share on other sites
Mike    141
you class seems to be correct (except you for you typo in the second line of the constructor... it should be createcompatiblebitmap).

Good call on the mistake in my code; i knew i was forgetting something.

Share this post


Link to post
Share on other sites
Mr Ekted    122
Saving and restoring the BITMAP alone is not sufficient in all cases. You need to restore PEN, BRUSH, PALETTE, FONT, and REGION. If you know you are only calling SelectObject() on the BITMAP, then what you have is fine. However, I really recommend making your class use "int SaveDC(HDC)" and "BOOL RestoreDC(HDC, int)" to make it generic.

Share this post


Link to post
Share on other sites