Saving Bitmaps to an Array



  • On 15/03/2013 at 09:54, xxxxxxxx wrote:

    User Information:
    Cinema 4D Version:   13 
    Platform:      
    Language(s) :     C++  ;

    ---------
    I'm having a hard time figuring out how to create and store multiple bitmaps into an array. Without getting memory leaks.
    All I want to do is push a button on my GeDialog plugin, and have a new image be created and stored in an array.
    I've tried all kinds of experiments to try and get the idea what exactly is going on. But I still can't achieve the simple task of adding multiple bitmaps to an array.

    This is what I've learned so far:

    -If I Alloc a bitmap and a GeDynamicArray as class members. I can create an image using that Alloc and Push the bitmap image into the array. And then I can free them both in the class's destructor.
    That gives me one image in the array. With no memory leaks.
    But...Every time I execute this. Every single image that was previously stored in the array will be replaced by the last one created. So I have an array filled with copies of the same image.

    -If I Alloc a bitmap somewhere other than the class. I can't free it in the class's destructor anymore.
    I must free it in the same local scope where I created it. And that's proving to be difficult to manage.

    -If I use Alloc locally in my button code to create a new image every time the button is pressed. I do get what I want. I do get multiple images saved to the array. And they are all unique images (not the same image) just like I want.
    But I also get memory leaks. Because all of the allocations have not been freed.

    Does anyone a have a simple example of saving images to an array?
    I can't figure out what C++ needs to do this. Without memory leaks.
    In Coffee and Python all of this memory stuff is handled for me. But C++ is different.

    -ScottA



  • On 15/03/2013 at 10:28, xxxxxxxx wrote:

    Hi Scott,

    > >> But...Every time I execute this. Every single image that was previously stored in the array will be replaced by the last one created. So I have an array filled with copies of the same image.

    Can you please share some code? This sounds like an algorithmic or logical error.

    > >> If I Alloc a bitmap somewhere other than the class. I can't free it in the class's destructor anymore.
    > >> I must free it in the same local scope where I created it. And that's proving to be difficult to manage.

    Again, some code would be useful. Please strip uninteresting parts of the code and focus on the important ones.

    In general, you just have to get right when and where to deallocate things you have allocated. Here's a short snippet (not using the C4D API, but that doesn't matter) : http://codepad.org/RISHn15X

    class BaseBitmap {
      
        BaseBitmap() {
        }
      
        ~BaseBitmap() {
        }
      
    public:
      
        static int allocs;
        static int deallocs;
      
        static BaseBitmap* Alloc() {
            allocs++;
            return new BaseBitmap;
        }
      
        static void Free(BaseBitmap*& ptr) {
            if (ptr) {
                deallocs++;
                delete ptr;
                ptr = NULL;
            }
        }
      
    };
      
    int BaseBitmap::allocs = 0;
    int BaseBitmap::deallocs = 0;
      
    class MyDialog {
      
        std::vector<BaseBitmap*> bmps;
      
    public:
      
        ~MyDialog() {
            std::vector<BaseBitmap*>::iterator it = bmps.begin();
            for (; it != bmps.end(); it++) {
                BaseBitmap::Free(*it);
            }
            bmps.clear();
        }
      
        int Message(void* node, int msgType, void* pData) {
            if (msgType == 100) {
                BaseBitmap* bmp = BaseBitmap::Alloc();
                if (bmp) {
                    std::cout << "Adding bitmap..\n";
                    bmps.push_back(bmp);
                }
            }
            return 1;
        }
      
    };
      
    int main() {
        /* scope */ {
            MyDialog dlg;
            dlg.Message(NULL, 20, NULL);
            dlg.Message(NULL, 100, NULL);
            dlg.Message(NULL, 100, NULL);
        }
      
        std::cout << "BaseBitmap Allocs: " << BaseBitmap::allocs << "\n";
        std::cout << "BaseBitmap Deallocs: " << BaseBitmap::deallocs << "\n";
    }
    

    Best,
    -Niklas



  • On 15/03/2013 at 11:44, xxxxxxxx wrote:

    Here's an example that handles and frees the memory properly(no memory leaks).
    I press the button to render and push an image into the array. Then move something in the scene. And press the button again. I should have two images that look differently in the array.
    But all of the images stored in the array end up being the same (the last one created).

    class myDialog : public GeDialog  
    {  
      private:  
          myDialog *dlg;  
          GeDynamicArray<BaseBitmap *> images;     //<---Declared as a class member  
          BaseBitmap *render;                      //<---Declared as a class member  
      
      public:  
          myDialog(void);  
          ~myDialog(void);  
          virtual void DestroyWindow();  
          virtual Bool CreateLayout(void);  
          virtual Bool InitValues(void);  
          virtual Bool CoreMessage(LONG id,const BaseContainer &msg);  
          virtual Bool Command(LONG id,const BaseContainer &msg);  
          virtual LONG Message(const BaseContainer &msg,BaseContainer &result);  
    };  
      
      
    myDialog::myDialog(void)  
    {      
      render = BaseBitmap::Alloc();        //Bitmap is allocated when plugin starts  
    }  
      
    myDialog::~myDialog(void)  
    {  
      BaseBitmap::Free(render);            //Frees the bitmap and the images array when the plugin closes  
      images.FreeArray();  
    }  
      
      
    ...  
      
      
    Bool myDialog::Command(LONG id,const BaseContainer &msg)  
    {  
      //... some other code here  
      
      BaseDocument *doc = GetActiveDocument(); //Get the active document  
      switch (id)   
      {  
          //This button checks the images in the array  
          case 1004:  
          {               
              ShowBitmap(images[0]);    //<---Shows the image in the first array's element  
              ShowBitmap(images[1]);    //<---Shows the image in the second array's element  
          }  
          break;  
      
        //This button renders the scene. Then adds the image into the "images" array every time it's pressed  
          case 10001:           
          {  
             render->Init(100,100);  
              RenderData *renderData = doc->GetActiveRenderData();  
              BaseContainer container = renderData->GetData();          
              container.SetLong(RDATA_XRES, 100);  
              container.SetLong(RDATA_YRES, 100);  
              container.SetLong(RDATA_RENDERENGINE, RDATA_RENDERENGINE_STANDARD);  
              RenderDocument(GetActiveDocument(), container, NULL, NULL, render, RENDERFLAGS_EXTERNAL, NULL);  
              renderData->SetParameter(RDATA_ACTIVEOBJECTONLY, FALSE, DESCFLAGS_SET_0);  
      
              images.Push(render);  
              LONG count = images.GetCount();  
              for(LONG i=0; i<count; i++)  
              {  
                  ShowBitmap(images[i]);      //Show every image currently in the images array  
              }      
              break;  
          }  
      }  
      
      return TRUE;  
    }
    

    If I use Alloc locally in the button (case 10001) code instead of in the class's constructor. I do get different looking images stored in my array as expected.
    But of course then I also get memory leaks because the Allocs are never freed using it that way.
    And if I attempt to free the Allocs created locally in the button code. The images in the array are also deleted. And I end up with an array filled with element that have no data in them.

    I'm caught up in a kind of catch 22 situation.

    -ScottA



  • On 15/03/2013 at 13:11, xxxxxxxx wrote:

    You need to allocate your bitmaps when the button is clicked, NOT in the constructor. Then before you call FreeArray in the destructor, iterate through all the elements in your dynamic array and free them. Merely freeing the array doesn't free everything it contains.

    So like this:

      
    for(LONG i = 0; i < images.GetCount; i++)   
    {   
        BaseBitmap::Free(images[i]);   
    }   
    images.FreeArray();   
    

    Steve



  • On 15/03/2013 at 13:35, xxxxxxxx wrote:

    Hi Steve,
    I've tried that.
    But if I Alloc & Free the bitmaps locally like that I can no longer use the array. Because the freeing has wiped out the image data saved in the array.

    Bool myDialog::Command(LONG id,const BaseContainer &msg)  
    {  
      
      BaseDocument *doc = GetActiveDocument(); //Get the active document  
      switch (id)   
      {  
          //This button checks the images in the array  
          case 1004:  
          {  
              GePrint(LongToString(images.GetCount()));       //<-------- Shows 2 items in the array..But those elements are empty..No images!!               
              ShowBitmap(images[0]);    //<---Shows the image in the first array's element  
              ShowBitmap(images[1]);    //<---Shows the image in the second array's element  
          }  
          break;  
      
        //This button renders the scene. Then adds the image into the "images" array every time it's pressed  
          case 10001:           
          {  
              render = BaseBitmap::Alloc();        //Allocated here instead of the constructor  
              render->Init(100,100);  
              RenderData *renderData = doc->GetActiveRenderData();  
              BaseContainer container = renderData->GetData();          
              container.SetLong(RDATA_XRES, 100);  
              container.SetLong(RDATA_YRES, 100);  
              container.SetLong(RDATA_RENDERENGINE, RDATA_RENDERENGINE_STANDARD);  
              RenderDocument(GetActiveDocument(), container, NULL, NULL, render, RENDERFLAGS_EXTERNAL, NULL);  
              renderData->SetParameter(RDATA_ACTIVEOBJECTONLY, FALSE, DESCFLAGS_SET_0);  
      
              images.Push(render);  
              LONG count = images.GetCount();  
              for(LONG i=0; i<count; i++)  
              {  
                  BaseBitmap::Free(images[i]);  //<--- If I do this. The images in the array get deleted  
                                                //     Leaving behind array elements with no data in them!?      
              }      
              break;  
          }  
      }  
      
      return TRUE;  
    }
    

    -ScottA



  • On 15/03/2013 at 14:34, xxxxxxxx wrote:

    Hi Scott,

                images.Push(render);
                LONG count = images.GetCount();
                for(LONG i=0; i<count; i++)
                {
                    BaseBitmap::Free(images[i]);  //<--- If I do this. The images in the array get deleted
                                                  //     Leaving behind array elements with no data in them!?    
                }    
    

    You are deleting the images, but not the entries in the array. Call GeDynamicArray::FreeArray()
    after you have freed the images.



  • On 15/03/2013 at 15:22, xxxxxxxx wrote:

    Yeah. I know I'm not deleting the entries.
    What I'm trying to point out is if I Alloc and free locally inside the button code like that. I can't use the array anywhere except that tiny little code block.
    Like in the other button's code that's trying to read what's in the array.

    Doing it this local way the images are born and then die all in the tiny scope of the button code.
    I can't use the array for things that persist. Like button images.

    I'm trying to build a class level array that I can add images to. And can be used anywhere in the code of my plugin. An array that I can use to host button images in. And only be freed and killed when the plugin closes.
    But when I do that. I get multiple copies of the same image.

    -ScottA



  • On 15/03/2013 at 15:40, xxxxxxxx wrote:

    No, no. Don't free the bitmaps in the same routine where you allocate them. Free them in the destructor routine just before you free the array itself. At that point you don't need them any more, so they can be freed up.

    That is perfectly legal. It doesn't matter that the allocation takes place in a local routine. You've allocated some memory; it doesn't matter where you free it as long as you do it before the GeDialog is destroyed.

    Steve



  • On 15/03/2013 at 17:12, xxxxxxxx wrote:

    Bingo! I think that's got it Steve.
    This is what I've got. And it seems to do what I need. And no memory leaks:

    class myDialog : public GeDialog  
    {  
      private:  
          myDialog *dlg;  
          GeDynamicArray<BaseBitmap *> images;     //<---Declared as a class member  
          BaseBitmap *render;                      //<---Declared as a class member  
      
      public:  
          myDialog(void);  
          ~myDialog(void);  
          virtual void DestroyWindow();  
          virtual Bool CreateLayout(void);  
          virtual Bool InitValues(void);  
          virtual Bool CoreMessage(LONG id,const BaseContainer &msg);  
          virtual Bool Command(LONG id,const BaseContainer &msg);  
          virtual LONG Message(const BaseContainer &msg,BaseContainer &result);  
    };  
      
      
    myDialog::myDialog(void)  
    {  
    }  
      
    myDialog::~myDialog(void)  
    {  
      LONG count = images.GetCount();  
      for(LONG i=0; i<count; i++)  
      {  
          BaseBitmap::Free(images[i]);    //Frees all the Allocated bitmaps memory created in the Command() method  
      }  
      
      images.FreeArray();                 //Frees the memory used in the array  
    }  
      
      
    ...  
      
      
    Bool myDialog::Command(LONG id,const BaseContainer &msg)  
    {  
      
      BaseDocument *doc = GetActiveDocument();  
      switch (id)   
      {  
          //This button checks the images in the array  
          case 1004:  
          {            
              ShowBitmap(images[0]);                               //Shows the image in the first array's element  
              ShowBitmap(images[1]);                               //Shows the image in the second array's element  
          }  
          break;  
      
          //This button renders the scene. Then adds the image into the "images" array every time it's pressed  
          case 10001:           
          {  
              render = BaseBitmap::Alloc();                        //Allocates a new bitmap instance every time this button is pressed  
              render->Init(100,100);                               //The size of the image to create  
              RenderData *renderData = doc->GetActiveRenderData();  
              BaseContainer container = renderData->GetData();          
              container.SetLong(RDATA_XRES, 100);  
              container.SetLong(RDATA_YRES, 100);  
              container.SetLong(RDATA_RENDERENGINE, RDATA_RENDERENGINE_STANDARD);  
              RenderDocument(GetActiveDocument(), container, NULL, NULL, render, RENDERFLAGS_EXTERNAL, NULL);  
              renderData->SetParameter(RDATA_ACTIVEOBJECTONLY, FALSE, DESCFLAGS_SET_0);  
      
              images.Push(render);  //Adds a new image to the array every time this button is pressed  
                
          }  
          break;  
      }  
      
      return TRUE;  
    }
    

    Thanks a lot Steve 🍺
    I was almost ready to toss my computer out the window!

    -ScottA



  • On 16/03/2013 at 00:44, xxxxxxxx wrote:

    Yeah, sometimes coding can be like that!


Log in to reply