1

In my main class that controls a window, I have this function, where pixmapItem is a QGraphicsPixmapItem* defined in the class header:

void updateDisplay() {
    uchar *data = new ...; // array of pixel data
    ...
    QImage image = QImage(data, width, height,
                          width, QImage::Format_Indexed8);

    pixmapItem->setPixmap(QPixmap::fromImage(image));
}

My question is: How can I destroy data when it is not needed anymore? "Not needed anymore" means that the function above or another function in my class sets the pixmap to another image.

I have seen that QImage has cleanup functions that may help, but the documentation is not really clear on how to use them and how to pass parameters such as the data pointer of the image to delete.

mimo
  • 2,469
  • 2
  • 28
  • 49
  • Notice that normally, if possible, it's a better idea to let the `QImage` allocate the memory for pixels by itself - so you can be sure that it's sized and aligned correctly, and it is owned/automatically destroyed by the QImage instance; after the QImage is constructed, you can have a pointer to the pixel data using the `bits` method. – Matteo Italia Dec 05 '16 at 06:47

5 Answers5

3

You can delete the data when the QImage is destroyed; this, as shown by @JeremyFriesner above, can be simply done by defining the QImage instance in an inner scope, and allocate/deallocate the data outside it.

However, why bother with all that work? That QImage constructor is there to accomodate complex use cases, where you already have the pixel data from some other source, or you need to keep it for a "strange" lifetime.

Your case instead is way simpler, the pixel data has exactly the same lifetime as the QImage, so it's a better idea to let it allocate the memory for pixels by itself; that way you can be sure that it's sized and aligned correctly, and it is owned/automatically destroyed by the QImage instance; after the QImage object is constructed, you can have a pointer to the pixel data using the bits method and do whatever you want with it.

This way, your code would simply become:

void updateDisplay() {
    QImage image = QImage(width, height,
                          width, QImage::Format_Indexed8);
    uchar *data = image.bits();
    ... 

    pixmapItem->setPixmap(QPixmap::fromImage(image));
}

simpler, safer and with zero risk of memory leaks.

Matteo Italia
  • 123,740
  • 17
  • 206
  • 299
2

From Qt documentation

Constructs an image with the given width, height and format, that uses an existing memory buffer, data. The width and height must be specified in pixels, data must be 32-bit aligned, and each scanline of data in the image must also be 32-bit aligned.

The buffer must remain valid throughout the life of the QImage. The image does not delete the buffer at destruction.

You need to implement your own way to delete that buffer

Swift - Friday Pie
  • 12,777
  • 2
  • 19
  • 42
  • Yes, I know the image does not destroy ``data``. But I don't know how to achieve this in the particular example I gave! – mimo Dec 05 '16 at 06:31
2

So you need to delete data yourself, but of course the trick is not to delete it too soon -- in particular, you don't want to delete it while the QImage object might still be using it. The easiest way to ensure that in your case is to delete it only after the QImage object has been destroyed:

void updateDisplay() {
   uchar *data = new ...; // array of pixel data
   ...
   {
      QImage image = QImage(data, width, height,
                      width, QImage::Format_Indexed8);

      pixmapItem->setPixmap(QPixmap::fromImage(image));
   }
   delete [] data;
}

(note the use of curly braces to create an inner scope, and thereby ensure that the QImage object gets popped off of the stack and destroyed before the delete [] data line executes!)

Of course an easier and safer approach would be to avoid the when-to-delete question altogether, by never manually allocating the array in the first place. Instead, let the QImage object allocate its own data array and just write into that instead:

QImage image(width, height, QImage::Format_Indexed8);
char * data = image.bits();
// write into (data) here if you want to, but don't delete [] data, ever!  
// instead, the QImage destructor will do any necessary deletes for you
Jeremy Friesner
  • 70,199
  • 15
  • 131
  • 234
2

You can subclass QImage with your own destructor that does the clean up.

class NativeBufferQImage : public QImage {
    uchar *data;

    // other stuff

public:
    ~NativeBufferQImage() {
        delete[] this->data; 
    }
};

Also note that since QImage is derived from QPaintDevice and QPaintDevice has a virtual destructor, so if someone delete your NativeBufferQImage by a pointer to its base class (i.e. QImage), your destructor will be called too.

Community
  • 1
  • 1
frogatto
  • 28,539
  • 11
  • 83
  • 129
  • `QImage` does not derive from `QObject`. It inherits from `QPaintDevice`. But the virtual destructor is still there. – thuga Dec 05 '16 at 07:26
  • @thuga Ohh... thank you for noting that. will edit my answer. – frogatto Dec 05 '16 at 07:30
  • this removes advantage to use images with shared data or use data with longer life span that QImage object. Subclass QImage to use a smart pointer? What is the specific use case, when you have to use independent data buffer to construct QImage? – Swift - Friday Pie Dec 05 '16 at 16:05
1

The best way to do it is not to do it:

void updateDisplay() {
    QImage image(width, height, QImage::Format_Indexed8);
    auto const data = image.bits();
    // you can modify the data here
    pixmapItem->setPixmap(QPixmap::fromImage(image));
}
Kuba hasn't forgotten Monica
  • 95,931
  • 16
  • 151
  • 313
  • it's not always the best method.. Qt's internal storage is a fragmented mess.. creating image from data buffer allows to use memory pool. Qt Ofc, no one should just hang around unmanaged pointers XD – Swift - Friday Pie Dec 05 '16 at 16:00
  • A fragmented mess? Qt uses the memory allocator provided by the C++ runtime. On any sane platform it should be a decent non-fragmenting allocator. And the asker of the question indicated no special needs in that area. – Kuba hasn't forgotten Monica Dec 05 '16 at 16:11
  • @ Kuba Ober that quite naïve view of it. No, C++ runtime doesn't guarantee that memory fragmentation would not occur, also all objects once allocated, are static, so defragmentation is not possible. As developer embedded software, where any fragmenting scenario is not allowed, I aware that Qt doesn't meet even "decent" level of fragmentation management, because most of Qt objects are non-movable. That's one of reasons why they have own implementation of containers, like QList. Images can be a large amount of data, sometimes gigabytes, that may quickly cause deterioration of heap space – Swift - Friday Pie Dec 07 '16 at 19:27
  • As far as standards are concerned: sure, the runtime doesn't *have* to guarantee it. But it may, and in most cases it does. E.g. the memory allocator in msvcrt is non-fragmenting: once an allocation is made, the block used for that allocation will not be cut up into smaller chunks. – Kuba hasn't forgotten Monica Dec 07 '16 at 19:36
  • That's actually bad thing in scenario I think of, I saw project that operates about several thousands of images (textures), processing them for rendering, downloading, uploading, generating. They do not use Qt, but that exact behavior you described had cause program to run out of memory within hour. The allocated block sdo not fragment, after freeing there are holes left, then you'll need to allocate smaller chunk, or large chunk.. and you have "no memory". It can work for days today because it uses memory pool to store all texture data. Oh if you curious, look for "Second Life" viewer. – Swift - Friday Pie Dec 07 '16 at 19:41