Thread: Drawing images without choking the memory.

  1. #1
    Registered User Queatrix's Avatar
    Join Date
    Apr 2005
    Posts
    1,342

    Unhappy Drawing images without beating up the memory.

    When I bulild games, I use BitBlt to draw my images. But a problem that I seem to come acrossed over and over is that BitBlt seems to choke my computer's memory. This is what I do for non transparent images:
    Code:
    BitBlt(hdc, 0, 0, 0, 0, hdcMemory, 0, 0, SRCAND);
    BitBlt(hdc, 0, 0, 0, 0, hdcMemory, 0, 0, SRCPAINT);
    Is this more memory efficient?
    Code:
    BitBlt(hdc, 0, 0, 0, 0, hdcMemory, 0, 0, SRCCOPY);
    And If not what is?

    Thanks in advance, August.
    Last edited by Queatrix; 08-31-2005 at 04:56 PM.

  2. #2
    train spotter
    Join Date
    Aug 2001
    Location
    near a computer
    Posts
    3,868
    >>When I bulild games, I use BitBlt to draw my images.

    When and how many times do you call BitBlt()? Are you using double buffering?


    >>But a problem that I seem to come acrossed over and over is that BitBlt seems to choke my computer's memory.

    Are you using a .NET version complier or if not, taking lots of care to return/free all GDI objects?

    What do you mean by 'choke'? Slow down or stop drawing correctly?

    Post some code?......
    "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

  3. #3
    Registered User Queatrix's Avatar
    Join Date
    Apr 2005
    Posts
    1,342
    Quote Originally Posted by novacain
    When and how many times do you call BitBlt()? Are you using double buffering?
    I call it to draw my images every time WM_TIMER is called.
    Quote Originally Posted by novacain
    What do you mean by 'choke'? Slow down or stop drawing correctly?
    Both!
    Quote Originally Posted by novacain
    Are you using a .NET version complier or if not, taking lots of care to return/free all GDI objects?
    I really don't know what you mean, but I don't think I use a .NET version.
    Last edited by Queatrix; 08-31-2005 at 08:23 PM.

  4. #4
    train spotter
    Join Date
    Aug 2001
    Location
    near a computer
    Posts
    3,868
    Post the drawing code. Sounds like you have a GDI reource leak, even small GDI leaks can cause big problems.

    What complier/IDE are you using?

    ie

    Code:
    hdc = GetDC(hWnd);
    hPen = CreatePen(.......);
    SelectObject(hdc, hPen);// lost current or default pen
    
    //draw
    
    DeleteObject(hPen);//may not delete pen as still in a DC
    ReleaseDC(hdc);//may not release as not in orignal state
    "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

  5. #5
    Registered User Queatrix's Avatar
    Join Date
    Apr 2005
    Posts
    1,342
    This is what I call every time the timer loops:

    Code:
    HDC hdcMemory;
    hdcMemory = CreateCompatibleDC(hdc);
     
    SelectObject(hdcMemory, hbmMan);
    BitBlt(hdc, ManX, ManY, bm.bmWidth, bm.bmHeight, hdcMemory, 0, 0, SRCCOPY);
     
    DeleteDC(hdcMemory);
    And this whenever I want transparent images:

    Code:
    HDC hdcMemory;
    hdcMemory = CreateCompatibleDC(hdc);
     
    SelectObject(hdcMemory, hbmManMask);
    BitBlt(hdc, ManX, ManY, bm.bmWidth, bm.bmHeight, hdcMemory, 0, 0, SRCAND);
    SelectObject(hdcMemory, hbmMan);
    BitBlt(hdc, ManX, ManY, bm.bmWidth, bm.bmHeight, hdcMemory, 0, 0, SRCPAINT);
     
    DeleteDC(hdcMemory);
    I am using Dev-C++.

    Code:
     
    	  case WM_TIMER:
    		 if(hbmManMask && hbmMan)
    		 {
    			HDC hdcWindow;
    			hdcWindow = GetDC(hwnd);
    			Update(hdcWindow,hwnd);
    			Draw(hdcWindow,hwnd);
    			ReleaseDC(hwnd,hdcWindow);
    		 }
    	  break;
    Last edited by Queatrix; 09-01-2005 at 07:30 AM.

  6. #6
    erstwhile
    Join Date
    Jan 2002
    Posts
    2,227
    Each DC, whether already 'attached' to a window (such as returned from GetDC) or a memory DC (such as returned by CreateCompatibleDC) comes with default gdi objects associated with it (bitmap,font,pen,brush,region). If you SelectObject a gdi object into any dc you must ensure that you have some mechanism to save what SelectObject returns ie. the original gdi object of that type associated with that device context so that you can restore it when done (see the 'remarks' section for SelectObject). If you don't (and you aren't) then you will get gdi memory leaks.

    So, either save the return value of SelectObject as an appropriate gdi object to restore the dc to whatever state it was in before you started fiddling with it or use SaveDC/RestoreDC to do it all at once ie. SaveDC before you fiddle around with the device context and then RestoreDC when you're done. It is imperative that you restore a memory device context to its original state prior to using DeleteDC on it - if the dc has 'live' gdi objects associated with it when you delete the dc then you are likely to get gdi memory leaks.
    CProgramming FAQ
    Caution: this person may be a carrier of the misinformation virus.

  7. #7
    Registered User
    Join Date
    May 2005
    Posts
    24
    you only need to call CreateCompatibleDC() once for each memory device context since GetDC() returns the same value for a given HWND. as far as saving and restoring the value of SelectObject(), it isn't really necessary for memory device contexts because your drawing routines will just select the appropriate GDI object as needed. if you create your windows with a WNDCLASSEX initialized with the CS_OWNDC flag the same can be done with screen device contexts, too, and you wouldn't have to call GetDC()/ReleaseDC() repeatedly either.

  8. #8
    Registered User Queatrix's Avatar
    Join Date
    Apr 2005
    Posts
    1,342
    So the following script should work:

    Create my HDCs as globals:
    Code:
    HDC hdc;
    HDC hdcMemory;
    These are my switch messages:
    Code:
    	case WM_CREATE:
                    hdc = GetDC(hwnd); 
                    hdcMemory = CreateCompatibleDC(hdc);
                    SelectObject(hdcMemory, hbmManMask);
    	break;
    	  case WM_TIMER:
    		 if(hbmManMask && hbmMan)
    		 {
    			BitBlt(hdc, ManX, ManY, 10, 50, hdcMemory, 0, 0, SRCCOPY);
    		 }
    	  break;
    	case WM_DESTROY:
                    ReleaseDC(hwnd,hdc);
                    DeleteDC(hdcMemory);
    	break;

  9. #9
    erstwhile
    Join Date
    Jan 2002
    Posts
    2,227
    Quote Originally Posted by legend
    as far as saving and restoring the value of SelectObject(), it isn't really necessary for memory device contexts because your drawing routines will just select the appropriate GDI object as needed.
    No, if you don't save/restore a gdi object from any device context, including a memory dc, you will leak memory:
    Quote Originally Posted by msdn
    After an application retrieves a handle, it must call the SelectObject function to replace the default object. However, the application should save the handle identifying the default object and use this handle to replace the new object when it is no longer needed. When the application finishes drawing with the new object, it must restore the default object by calling the SelectObject function and then delete the new object by calling the DeleteObject function. Failing to delete objects causes serious performance problems.

    http://msdn.microsoft.com/library/de...vcons_5u5v.asp
    edit: >>These are my switch messages:<<

    You should save the return value from SelectObject on the memory dc so that you can restore it to its original state prior to deleting it.
    CProgramming FAQ
    Caution: this person may be a carrier of the misinformation virus.

  10. #10
    Registered /usr
    Join Date
    Aug 2001
    Location
    Newport, South Wales, UK
    Posts
    1,273
    I'm not sure that that's 100% true, Ken, given that some objects (brush, pen) are most likely drawn from GetStockObject and thus are references to static, Windows-controlled GDI objects. You couldn't delete them even if you wanted to, so...

    But default bitmaps are most definately "unique" and need to be handled appropriately.

  11. #11
    Registered User Queatrix's Avatar
    Join Date
    Apr 2005
    Posts
    1,342
    Okay, how about this?

    Create my HDCs as globals:
    Code:
    HDC hdc;
    HDC hdcMemory;
    These are my switch messages:
    Code:
    	case WM_CREATE:
    hdc = GetDC(hwnd); 
    hdcMemory = CreateCompatibleDC(hdc);
    SelectObject(hdcMemory, hbmManMask);
    	break;
    	 case WM_TIMER:
    		 if(hbmManMask && hbmMan)
    		 {
    			SaveDC(hdcMemory);
    			BitBlt(hdc, ManX, ManY, 10, 50, hdcMemory, 0, 0, SRCCOPY);
    			RestoreDC(hdcMemory,0);
    		 }
    	 break;
    	case WM_DESTROY:
    ReleaseDC(hwnd,hdc);
    DeleteDC(hdcMemory);
    	break;

  12. #12
    erstwhile
    Join Date
    Jan 2002
    Posts
    2,227
    Quote Originally Posted by SMurf
    I'm not sure that that's 100% true, Ken, given that some objects (brush, pen) are most likely drawn from GetStockObject and thus are references to static, Windows-controlled GDI objects. You couldn't delete them even if you wanted to, so...

    But default bitmaps are most definately "unique" and need to be handled appropriately.
    That's a good point SMurf - thanks for pointing that out. To be honest I was thinking mostly about bitmaps; a memory device context is born with a 1x1 monochrome bitmap.

    >>How about this

    Just save the relevant dc information prior to altering the dc and restore it just before you delete/release the dc. If you're going to use SaveDC then save it's return value (or use a relative, negative offset, eg. using -1 restores the last saved dc).
    Code:
    case WM_CREATE:
      hdc = GetDC(hwnd); 
      hdcMemory = CreateCompatibleDC(hdc);
      SaveDC(hdcMemory);
      SelectObject(hdcMemory, hbmManMask);
      break;
    case WM_TIMER:
      if(hbmManMask && hbmMan)
        {
          BitBlt(hdc, ManX, ManY, 10, 50, hdcMemory, 0, 0, SRCCOPY);
        }
      break;
    case WM_DESTROY:
      ReleaseDC(hwnd,hdc);
      RestoreDC(hdcMemory,-1);
      DeleteDC(hdcMemory);
      break;
    Last edited by Ken Fitlike; 09-04-2005 at 02:10 AM.
    CProgramming FAQ
    Caution: this person may be a carrier of the misinformation virus.

  13. #13
    Registered User Queatrix's Avatar
    Join Date
    Apr 2005
    Posts
    1,342
    That works, but it doesn't bring down the mem usage, it brings it up about 100k. Why is that?

  14. #14
    train spotter
    Join Date
    Aug 2001
    Location
    near a computer
    Posts
    3,868
    >>it brings it up about 100k. Why is that?
    Probably the memory the saved DC requires.

    legend's post only applies to DCs obtained with GetDC() if you have also specified that the window will 'own' a DC. Then instead of GetDC() returning a DC with default GDI objects it returns one with the last selected objects.

    His method will leak GDI, just at a slower rate.

    To track GDI leaks open the TaskManager and add 'GDI objects' to the list. (Options->Select Columns)
    Run your app..

    Ken's code will not leak but I still prefer to save the return and replace it method.

    Code:
    HBITMAP    hOriginalBMP=NULL;
    
    case WM_CREATE:
      hdc = GetDC(hwnd); 
      hdcMemory = CreateCompatibleDC(hdc);
       hOriginalBMP=SelectObject(hdcMemory, hbmManMask);
      break;
    case WM_TIMER:
      if(hbmManMask && hbmMan)
        {
          BitBlt(hdc, ManX, ManY, 10, 50, hdcMemory, 0, 0, SRCCOPY);
        }
      break;
    case WM_DESTROY:
      ReleaseDC(hwnd,hdc);
      SelectObject(hdcMemory,hOriginalBMP);
      DeleteDC(hdcMemory);
      break;
    "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

  15. #15
    Registered User Queatrix's Avatar
    Join Date
    Apr 2005
    Posts
    1,342
    I have what I wanted to know now. Thanks everyone!




    -

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Help with insert/delete binary search tree
    By Nazgulled in forum C Programming
    Replies: 39
    Last Post: 03-25-2009, 04:24 PM
  2. Suggestions on this C style code
    By Joelito in forum C Programming
    Replies: 11
    Last Post: 06-07-2007, 03:22 AM
  3. Relate memory allocation in struct->variable
    By Niara in forum C Programming
    Replies: 4
    Last Post: 03-23-2007, 03:06 PM
  4. Memory allocation and deallocation
    By Micko in forum C++ Programming
    Replies: 3
    Last Post: 08-19-2005, 06:45 PM
  5. Accessing Video Memory Information...need help
    By KneeLess in forum C++ Programming
    Replies: 8
    Last Post: 08-24-2003, 03:53 PM