Archived

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

23yrold3yrold

More fun with Win32

Recommended Posts

I''m making a little MSPaint clone (just for learning) and I''m having trouble with resizing the image. Ideally, resizing creates a new bitmap with the new dimensions, clears it, blits the old bitmap to it, selects the new bitmap into the device context and destroys the old one. It seems to hang on to the old one for some reason. Can someone show me my error in this code, or maybe where I can check return values for helpful errors here? Thanks. This is a member function. hdc, hbitmap, width and height are member variables that are hopefully self explanitory.
  
      void ResizeBitmap(int w, int h)
      {
         HDC     temphdc    = CreateCompatibleDC(hdc);
         HBITMAP tempbitmap = CreateCompatibleBitmap(temphdc, w, h);
         SelectObject(temphdc, tempbitmap);
         ::PatBlt(temphdc, 0, 0, w, h, WHITENESS);

         BitBlt(temphdc, 0, 0, width, height, hdc, 0, 0, SRCCOPY);

         DeleteObject(hbitmap);
         hbitmap = tempbitmap;
         SelectObject(hdc, hbitmap);
         DeleteObject(temphdc);

         width  = w;
         height = h;
      }
  
Chris Barry (crbarry at mts.net) My Personal Programming Depot

Share this post


Link to post
Share on other sites
An object selected into a device context cannot be deleted. You need to call SelectObject on temphdc with the return value of the original SelectObject call - all prior to calling DeleteObject on it. ie

HBITMAP hbmOrg = SelectObject( temphdc, tempbitmap );
...
SelectObject( temphdc, hbmOrg );
DeleteObject( temphdc );
...


[ GDNet Start Here | GDNet Search Tool | GDNet FAQ ]
[ MS RTFM [MSDN] | SGI STL Docs | Boost ]
[ Google! | Asking Smart Questions | Jargon File ]
Thanks to Kylotan for the idea!

Share this post


Link to post
Share on other sites
quote:
Original post by 23yrold3yrold
Out of curiosity, if an object is selected into one device context, it can''t be selected into another, can it?

No, it can''t. If that''s your bug, excellent.

[ GDNet Start Here | GDNet Search Tool | GDNet FAQ ]
[ MS RTFM [MSDN] | SGI STL Docs | Boost ]
[ Google! | Asking Smart Questions | Jargon File ]
Thanks to Kylotan for the idea!

Share this post


Link to post
Share on other sites
I''m having trouble getting the dc''s to cough up their hbitmaps, though. It works, but must I declare new hbitmaps (and create bitmaps for them to point at, else it doesn''t work) just to pass to SelectObject just to get the hbitmaps free just so I can swap them? Here''s my code, which, again, works, but I''m curious if there''s a more effecient way. Don''t laugh ......


        void ResizeBitmap(int w, int h)
{
HDC temphdc = CreateCompatibleDC(hdc);
HBITMAP tempbitmap = CreateCompatibleBitmap(temphdc, w, h);
SelectObject(temphdc, tempbitmap);
::PatBlt(temphdc, 0, 0, w, h, WHITENESS);

BitBlt(temphdc, 0, 0, width, height, hdc, 0, 0, SRCCOPY);

HBITMAP temp1 = CreateCompatibleBitmap(hdc, 1, 1), temp2 = CreateCompatibleBitmap(hdc, 1, 1);
hbitmap = (HBITMAP)SelectObject(temphdc, temp1);
tempbitmap = (HBITMAP)SelectObject(hdc, temp2);
SelectObject(hdc, hbitmap);

DeleteObject(temphdc);
DeleteObject(tempbitmap);

width = w;
height = h;
}


Chris Barry (crbarry at mts.net)
My Personal Programming Depot

Share this post


Link to post
Share on other sites
Try this:

      
HGDIOBJ hOldObject = SelectObject(hdc, hJustCreatedObject);
// Use hJustCreatedObject

SelectObject(hdc, hOldObject);
Deleteobject(hJustCreatedObject);

As a side note, shouldn't you be using StretchBlt instead of PatBlt & BitBlt?

Edit: I forgot to mention that your code has a resource leak.

Edit: Fixed DeleteObject call.

[edited by - IndirectX on May 5, 2002 12:41:59 AM]

Share this post


Link to post
Share on other sites
For the resource leak, I assume you mean temp1 and temp2. I''ll get to it And I could stretch it; I just wanted it working now.

DeleteObject() takes two variables??

It''s late and I''m going to bed, so I''ll check your idea tomorrow. Thanks.

Chris Barry (crbarry at mts.net)
My Personal Programming Depot

Share this post


Link to post
Share on other sites
Before you call this line:

DeleteObject(temphdc);

Aren''t you supposed to call ReleaseDC(temphdc);, or something like that?

---
Make it work.
Make it fast.

"Commmmpuuuuterrrr.." --Scotty Star Trek IV:The Voyage Home

Share this post


Link to post
Share on other sites
quote:
Original post by CaptainJester
Before you call this line:

DeleteObject(temphdc);

Aren''t you supposed to call ReleaseDC(temphdc);, or something like that?


No. DCs are released with either ReleaseDC or DeleteDC, wich are mutually exclusive. Each one has its scope (ie, releases particular types of DCs). You don''t use DeleteObject on a DC.

Share this post


Link to post
Share on other sites
i think i got your bug.

you cant select a bitmap back into hdc. windows will ignore your butt. that bitmap belongs to windows.

or as the knowledge base says:
"(Bitmaps can be selected for memory device contexts only, and for only one device context at a time.)"

the relavent piece being "memory device contexts only" when your talking hdc thats not a memory device context.

So what to do? well ive been through this before. just do all your painting on your own private bitmap (the typical backbuffer) then when your ready bitblt it back to the windows bitmap. its the only way and a pretty good way. So after you get that stretch done you just bitblt it right back.
edit: heeh you might be able to get rid of the middle man there but conceptually i was just pointing out keep a permanent backbuffer that is exactly the same as the hdc's. your code deals with that. you can be sloppy or clean. then on draw time you just copy that whole thing into the hdc via bitblt hdc. its fast. not as fast as true flipping but it does get rid of flicker.



so your finalish line in that will be
bitblt(hdc,...);

and they said i was crazy for making a 2d game without ddraw. little did they know i would learn something.

ps tell me when you try a rotation. the windows bit transfer function doesnt work on 98. it compiles and calls just does nothing. cept down there in the little line at the bottom in the documentation it says not supported on windows 98...

writing my own was slow.. and i suddenly realized that i had never seen bitmap rotations in games like diablo. they are nasty expensive in pure software mode.



oh btw that private bitmap that is just like the screen bitmap. keep it persistant. there is no reason to realloc it every function call.



[edited by - declspec on May 6, 2002 2:32:19 PM]

[edited by - declspec on May 6, 2002 2:34:51 PM]

Share this post


Link to post
Share on other sites
Here''s my final working version. Look good?


        void ResizeBitmap(int w, int h)
{
HDC temphdc = CreateCompatibleDC(hdc);
hbitmap = CreateCompatibleBitmap(temphdc, w, h);
HBITMAP tempbitmap = (HBITMAP)SelectObject(hdc, hbitmap);
SelectObject(temphdc, tempbitmap);
::PatBlt(hdc, 0, 0, w, h, WHITENESS);

BitBlt(hdc, 0, 0, width, height, temphdc, 0, 0, SRCCOPY);

DeleteObject(temphdc);
DeleteObject(tempbitmap);

width = w;
height = h;
}


BTW, hdc in that code IS the back buffer It BitBlt''s to the window dc in WM_PAINT. And rotating isn''t that hard using SetPixel, GetPixel and a rotation matrix, right? (famous last words, I''m sure) ....

Chris Barry (crbarry at mts.net)
My Personal Programming Depot

Share this post


Link to post
Share on other sites
oh hmm. thats to your starting question.


now to rotations. yes and no. but rotations are lossy. the pixels dont line up if your rotating say 30 degrees. so you have to make some pixels a guesstimate.

ok you can use two approaches.
1) you rotate by taking the position of the original pixel then find its new coordinate via a rotation. change the float to an int in some way. then paint that pixel on the new bitmap. if you do it this way then some pixels are unpainted on the bitmap. looks like a pattern of spots. fix you could paint a few pixels based on the percentage that the original pixel was actually in the target. a sort of sample as you go to get rid of the spots. lots of repainting though.

2) you go hmmm write a harder algo that takes pixels in the final bitmap via math. then rotates back to a pixels in the original bitmap and samples based on percentage that its in that pixel.


so the key concept is that if you just rotate you get pixels in the middle of your bitmap that never got painted. not huge blocks just dots. single pixels where the round off/ rotation math caused it to not get painted. you got to decide what to do with them.

Share this post


Link to post
Share on other sites
either way painting pixel by pixel from/to a device context is slow slow slow. i mean 1000s of times slower then the equivalent bitblt. very very slow.


so in games youll notice in the older non accelerated games they actually packaged 8 angles of each thing that had to turn in the graphics files. no rotations in older games at all. not even in doom. the only thing that rotated in doom was sprites. again packaged rotations. later 3d had rotations quake for example but even quake in software mode still needed a graphics card...the "software" doesnt refer to every last texture op.



for my game i ended up rotating at load time. that worked. it was uglier but i didnt want to get caught up in artwork.

for you though you have flexibility. you can afford slowness to a point.


Share this post


Link to post
Share on other sites
One thing I think you missed, from the MSDN library docs on CreateCompatibleDC():

quote:

When you no longer need the memory device context, call the DeleteDC function to delete it.



So you shouldn't do DeleteObject(temphdc), you should be doing DeleteDC(temphdc).

[edited by - cgoat on May 7, 2002 1:04:33 PM]

Share this post


Link to post
Share on other sites