Link to home
Start Free TrialLog in
Avatar of iks1
iks1

asked on

Memory DC + BitBlt to Screen...

I have an object Cbla and it has two methods: PaintToMemDC and PaintFromMemDCToScreen:

CDC memDC is protected member of Cbla.

void Cbla::PaintToMemDC(CPaintDC *pdc){
      
      memDC.CreateCompatibleDC(pdc);

      CBitmap bitmap;
      bitmap.CreateCompatibleBitmap(pdc, 100, 100);
      CBitmap *oldBitmap = (CBitmap*)memDC.SelectObject(&bitmap);

      memDC.MoveTo(0,0); memDC.LineTo(100,100);

      //memDC.SelectObject(oldBitmap);
}

And:


void Cbla::PaintFromMemDCToScreen(CPaintDC *pdc)
{
      pdc->BitBlt(rand()%150, rand()%150, 100, 100, &memDC, 0, 0, MERGECOPY);
}

I hope that names tells enough.

Obvioulsy since "memDC.SelectObject(oldBitmap);" is commented it will gain a memory leak. But this is the only way I can get that everything on the screen works right: memDCs are sucesfully created in PaintToMemDC method and loaded to the screen in PaintFromMemDCToScreen.
As soon as I uncomment there is no more memory leak but PaintFromMemDCToScreen draws black or random color objects. So something goes totally wrong.

How can I fix this? I just want create some glyphs in PaintToMemDC, save them in memory (CDC or whatever possible) and draw them laterer using PaintFromMemDCToScreen.

Thanks,
-Andrej
Avatar of AlexFM
AlexFM

Keep bitmap and oldObtmap as class members. In class constructor create memory DC, bitmap and select bitmap in it. In destructor restore old bitmap.
BTW, it is better to change PaintFromMemDCToScreen to:

void Cbla::PaintFromMemDCToScreen(CDC *pdc)

Avatar of DanRollins
A agree with AlexFM.  The CBitmap on which you have done your drawing will be destroyed upon exiting the PaintToMemDC() function.  That is equivallent to drawing on a sketchpad, then throwing the top page into the trash... and then wondering why you can no longer see your lovely sketch :-)

If the CBitmap persists... normally by making it part of your class object -- then your drawing will continue to exist when you want to transfer it to the screen.  Once you have blitted to the screen, you can use
    m_bitmap.DeleteObject()
to avoid the memory leak.
Avatar of iks1

ASKER

Hi guys,

Thank you for the answers...

So I changed my methods to:
  void Cbla::PaintFromMemDCToScreen(CPaintDC *pdc)
  void Cbla::PaintToMemDC(CPaintDC *pdc)
and I agree that this is a good idea.

I also moved:
  CDC m_memDC
  CBitmap m_bitmap
  CBitmap *m_pOldMemDCBitmap
under private class declarations and my both methods now look like this:

void Cbla::PaintToMemDC(CDC *pdc){

     m_memDC.CreateCompatibleDC(pdc);

     m_bitmap.CreateCompatibleBitmap(pdc, 100, 100);
     m_oldBitmap = (CBitmap*)m_memDC.SelectObject(&m_bitmap);

     m_memDC.MoveTo(0,0); m_memDC.LineTo(100,100);

     //memDC.SelectObject(oldBitmap);
}

And:

void Cbla::PaintFromMemDCToScreen(CDC *pdc)
{
     pdc->BitBlt(rand()%150, rand()%150, 100, 100, &m_memDC, 0, 0, SRCCOPY);
}

This code works just like it worked before (in my first post) and is still leading to a memory leak although I also do m_bitmap.DeleteObject() in my own Cbla::Destroy() method and in Cbla::~Cbla() destructor.
In this case I would really like to destroy everything in my Destroy() method... So what can I do?

And I can't move memDC.CreateCompatibleDC(pdc) to constructor since I am making an array of this objects like this:

void CTestDraw1Dlg::OnPaint(){
  CPaintDC dc(this); // Device context for painting
...
  Cbla *bla;
  bla=new Cbla[500];
...
  for (i=0; i<500;i++){
    bla[i].PaintToMemDC(&dc);
  }
...
}

Thanks again,
-Andrej
Avatar of iks1

ASKER

Sorry (copy/paste problem):

So I changed my methods to:
  void Cbla::PaintFromMemDCToScreen(CDC *pdc)
  void Cbla::PaintToMemDC(CDC *pdc)

Thanks,
-Andrej
>> And I can't move memDC.CreateCompatibleDC(pdc) to constructor.

Why?
Avatar of iks1

ASKER

AlexFM,

Sorry I am pretty much new to C so this could be done differently but I dont know how...

I am creating an array of my Cbla objects like this:
  Cbla *bla;
  bla=new Cbla[500];
...
  for (i=0; i<500;i++){
    bla[i].PaintToMemDC(&dc);
  }

and as I change my default contructor from Cbla::Cbla() to Cbla::Cbla(CDC *pdc) the compiler complains:
>> error C2512: 'Cbla' : no appropriate default constructor available...

So shuld I leave this constructor alone and create another one with (CDC *pdc) and call it later? Isnt it pretty much the same then if I call PaintToMemDC later?

Or is there a chance to call this new constructor in my case?
Maybe I got all this array of objects initialization wrong...

Thanks again,
-Andrej

Avatar of iks1

ASKER

Okay,

Cbla bla[10]={&dc, &dc, &dc, &dc, &dc, &dc, &dc, &dc, &dc, &dc};

might be also okay but how do I do this for like 500 times? :)

Thanks,
-Andrej
OK, leave constructor empty and change PaintToMemDC so that it initializes data first time when called:

void Cbla::PaintToMemDC(CDC *pdc)
{
    if ( ! m_memDC.m_hDC )
    {
         m_memDC.CreateCompatibleDC(pdc);
         m_bitmap.CreateCompatibleBitmap(pdc, 100, 100);
         m_oldBitmap = (CBitmap*)m_memDC.SelectObject(&m_bitmap);
     }

     m_memDC.MoveTo(0,0); m_memDC.LineTo(100,100);
}

Restore old bitmap in class destructor.
BTW, &dc, &dc, &dc, &dc, &dc, &dc, &dc, &dc, &dc 500 times is good idea, I like it :)
Avatar of iks1

ASKER

AlexFM,

I did what you suggested... but it is still leaking :(

If I don't do:
  m_memDC.SelectObject(m_oldBitmap)
in the same method that I did:
  m_bitmap.CreateCompatibleBitmap(pdc, xx, yy);
  m_oldBitmap = (CBitmap*)m_memDC.SelectObject(&bitmap);
then I got a fancy huge memory leak that sometimes don't release all the allocated memory even then when I close the application...

But if I do it (m_memDC.SelectObject(m_oldBitmap)) - my glyphs are painted randomly :)

Is there any other way to quickly draw something on screen DC from memory? Maybe from some int array or something? Because theese bitmaps are driving me crazy...

Thanks,
-Andrej
Post your code again: class and client code.
Avatar of iks1

ASKER

Hi,

Here it goes:

--- bla.h -----------------------
#if !defined(AFX_BLA_H__E7A016EB_4474_4D53_BD4B_F9225B307E48__INCLUDED_)
#define AFX_BLA_H__E7A016EB_4474_4D53_BD4B_F9225B307E48__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
// bla.h : header file
//

/////////////////////////////////////////////////////////////////////////////
// Cbla window

class Cbla : public CWnd
{
// Construction
public:
      Cbla();
      void PaintFromMemDCToScreen(CDC *pdc);
      
      void Destroy();

      void PaintToMemDC(CDC *pdc);


// Operations
public:

// Overrides
      // ClassWizard generated virtual function overrides
      //{{AFX_VIRTUAL(Cbla)
      //}}AFX_VIRTUAL

// Implementation
public:
      virtual ~Cbla();

      CDC m_memDC;
      CBitmap m_bitmap;
      CBitmap *m_pOldMemDCBitmap;


      // Generated message map functions
protected:
      //{{AFX_MSG(Cbla)
            // NOTE - the ClassWizard will add and remove member functions here.
      //}}AFX_MSG
      DECLARE_MESSAGE_MAP()

};

/////////////////////////////////////////////////////////////////////////////

//{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ will insert additional declarations immediately before the previous line.

#endif // !defined(AFX_BLA_H__E7A016EB_4474_4D53_BD4B_F9225B307E48__INCLUDED_)
--------------- end of bla.h ----

--- bla.cpp ---------------------
// bla.cpp : implementation file
//

#include "stdafx.h"
#include "testdraw1.h"
#include "bla.h"

#include <Wingdi.h>
#include <Windows.h>

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// Cbla
Cbla::Cbla(){

}

void Cbla::PaintFromMemDCToScreen(CDC *pdc)
{
      //--------------------------------------------------
      pdc->BitBlt(rand()%150, rand()%150, 100, 100, &m_memDC, 0, 0, SRCCOPY);
      //--------------------------------------------------
}

Cbla::~Cbla()
{
      m_memDC.SelectObject(m_pOldMemDCBitmap);
}

void Cbla::PaintToMemDC(CDC *pdc){

      CRect rcClient(0,0,100,100);

    if (!m_memDC.m_hDC)
    {
            m_memDC.CreateCompatibleDC(pdc);
            m_bitmap.CreateCompatibleBitmap(pdc, rcClient.Width(), rcClient.Height());
            m_pOldMemDCBitmap = (CBitmap*)m_memDC.SelectObject(&m_bitmap);
     }

     

      //--------------------------------------------------
      m_memDC.Rectangle(CRect(0,0,100,100));
      m_memDC.MoveTo(16,16); m_memDC.LineTo(99,99);
      m_memDC.MoveTo(99,1); m_memDC.LineTo(1,99);
      CString to; to.Format(_T("%d"),rand()%100);
      m_memDC.SetBkMode(TRANSPARENT);
      m_memDC.DrawText(to, CRect (3,2,98,98), DT_NOCLIP);
      //--------------------------------------------------

}


void Cbla::Destroy(){
      m_memDC.SelectObject(m_pOldMemDCBitmap);
}

BEGIN_MESSAGE_MAP(Cbla, CWnd)
      //{{AFX_MSG_MAP(Cbla)
            // NOTE - the ClassWizard will add and remove mapping macros here.
      //}}AFX_MSG_MAP
END_MESSAGE_MAP()


/////////////////////////////////////////////////////////////////////////////
// Cbla message handlers
--------------- end of bla.cpp --

Client app - Test dialog:
--- TDlg2.h ---------------------
#if !defined(AFX_TDLG2_H__73B8CE73_EE51_439A_B1F0_8E0060E84CAB__INCLUDED_)
#define AFX_TDLG2_H__73B8CE73_EE51_439A_B1F0_8E0060E84CAB__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
// TDlg2.h : header file
//

/////////////////////////////////////////////////////////////////////////////
// TDlg2 dialog

class TDlg2 : public CDialog
{
// Construction
public:
      TDlg2(CWnd* pParent = NULL);   // standard constructor

// Dialog Data
      //{{AFX_DATA(TDlg2)
      enum { IDD = IDD_DIALOG1 };
            // NOTE: the ClassWizard will add data members here
      //}}AFX_DATA


// Overrides
      // ClassWizard generated virtual function overrides
      //{{AFX_VIRTUAL(TDlg2)
      public:
      virtual void OnFinalRelease();
      protected:
      virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
      //}}AFX_VIRTUAL

// Implementation
protected:

      // Generated message map functions
      //{{AFX_MSG(TDlg2)
      afx_msg void OnPaint();
      //}}AFX_MSG
      DECLARE_MESSAGE_MAP()
      // Generated OLE dispatch map functions
      //{{AFX_DISPATCH(TDlg2)
            // NOTE - the ClassWizard will add and remove member functions here.
      //}}AFX_DISPATCH
      DECLARE_DISPATCH_MAP()
      DECLARE_INTERFACE_MAP()
};

//{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ will insert additional declarations immediately before the previous line.

#endif // !defined(AFX_TDLG2_H__73B8CE73_EE51_439A_B1F0_8E0060E84CAB__INCLUDED_)
--------------- end of TDlg2.h --

--------------- end of TDlg2.cpp-
// TDlg2.cpp : implementation file
//

#include "stdafx.h"
#include "testdraw1.h"
#include "TDlg2.h"
#include "bla.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// TDlg2 dialog


TDlg2::TDlg2(CWnd* pParent /*=NULL*/)
      : CDialog(TDlg2::IDD, pParent)
{
      EnableAutomation();

      //{{AFX_DATA_INIT(TDlg2)
            // NOTE: the ClassWizard will add member initialization here
      //}}AFX_DATA_INIT
}


void TDlg2::OnFinalRelease()
{
      // When the last reference for an automation object is released
      // OnFinalRelease is called.  The base class will automatically
      // deletes the object.  Add additional cleanup required for your
      // object before calling the base class.

      CDialog::OnFinalRelease();
}

void TDlg2::DoDataExchange(CDataExchange* pDX)
{
      CDialog::DoDataExchange(pDX);
      //{{AFX_DATA_MAP(TDlg2)
            // NOTE: the ClassWizard will add DDX and DDV calls here
      //}}AFX_DATA_MAP
}


BEGIN_MESSAGE_MAP(TDlg2, CDialog)
      //{{AFX_MSG_MAP(TDlg2)
      ON_WM_PAINT()
      //}}AFX_MSG_MAP
END_MESSAGE_MAP()

BEGIN_DISPATCH_MAP(TDlg2, CDialog)
      //{{AFX_DISPATCH_MAP(TDlg2)
            // NOTE - the ClassWizard will add and remove mapping macros here.
      //}}AFX_DISPATCH_MAP
END_DISPATCH_MAP()

// Note: we add support for IID_ITDlg2 to support typesafe binding
//  from VBA.  This IID must match the GUID that is attached to the
//  dispinterface in the .ODL file.

// {041D574B-9525-4FF1-9A66-DB406E3A656B}
static const IID IID_ITDlg2 =
{ 0x41d574b, 0x9525, 0x4ff1, { 0x9a, 0x66, 0xdb, 0x40, 0x6e, 0x3a, 0x65, 0x6b } };

BEGIN_INTERFACE_MAP(TDlg2, CDialog)
      INTERFACE_PART(TDlg2, IID_ITDlg2, Dispatch)
END_INTERFACE_MAP()

/////////////////////////////////////////////////////////////////////////////
// TDlg2 message handlers

void TDlg2::OnPaint()
{
      CPaintDC dc(this); // device context for painting
      
      // TODO: Add your message handler code here

      
      Cbla bla[300];
      int i=0;

      dc.DrawText(_T("PaintToMemDC...           "),
            CRect (2,2,98,98), DT_NOCLIP);
      Sleep(1000);

      for (i=0; i<300;i++){
            bla[i].PaintToMemDC(&dc);
      }

      dc.DrawText(_T("PaintFromMemDCToScreen... "),
            CRect (2,2,98,98), DT_NOCLIP);
      Sleep(1000);

      for (i=0; i<300;i++){
            bla[i].PaintFromMemDCToScreen(&dc);
      }

      dc.DrawText(_T("Destroy...                "),
            CRect (2,2,98,98), DT_NOCLIP);
      Sleep(1000);

      /*for (i=0; i<300;i++){
            bla[i].Destroy();

      }*/

      // Do not call CDialog::OnPaint() for painting messages
}

--- TDlg2.cpp -------------------

Thank you again,
-Andrej
Avatar of iks1

ASKER

Oooops, last comments:

--------------- end of TDlg2.cpp-
and
--- TDlg2.cpp -------------------

are mistaken... Sorry :)

-Andrej
ASKER CERTIFIED SOLUTION
Avatar of AlexFM
AlexFM

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of iks1

ASKER

Okay thanks...

I am on eMbedded VC4 and can't find CrtSetBreakAlloc function... So I was always checking for a leak using Remote Performance Monitor (WCE420) -> CE Memory Statistics -> Memory Load...

So do you have any idea how to check memory usage here?

Cbla is not derived from CWnd any more and was because I copied it out from a different project just to show the leak...

Okay you helped me enough :)

Thanks for everything...
-Andrej
You are still using CreateCompatibleBitmap without deleting the previous one.  That causes a memeory leak as you overwrite one HBITMAP with another.   Somewhere in your code, you need:

          if ( m_bitmap.m_hObject != 0 ) {
                    m_bitmap.DeleteObject();
                    m_bitmap.m_hObject= 0;
          }

This belongs in the code at a location very near to, and just before, the place where you call:

          m_bitmap.CreateCompatibleBitmap(pdc, rcClient.Width(), rcClient.Height());