Add support for cleanup functions for data-constructed QImages
authorSimon Hausmann <simon.hausmann@nokia.com>
Tue, 31 Jan 2012 12:32:22 +0000 (13:32 +0100)
committerQt by Nokia <qt-info@nokia.com>
Tue, 31 Jan 2012 15:21:17 +0000 (16:21 +0100)
A QImage can be constructed with a provided buffer, that has to be
kept alive for the live-time of the QImage and all its copies.
Frameworks like CoreGraphics or Cairo offer a similar method of
creating image objects and also offer the ability to provide a callback
function that is called when the image is destroyed.

This patch adds this functionality to QImage by extending the
QImage constructors that take a raw image buffer pointer.

Change-Id: Ia6342408c560ef49b498c9e4664b4602febb0fcd
Reviewed-by: Samuel Rødal <samuel.rodal@nokia.com>
Reviewed-by: Michalina Ziemba <michalina.ziemba@nokia.com>
Reviewed-by: Gunnar Sletta <gunnar.sletta@nokia.com>
src/gui/image/qimage.cpp
src/gui/image/qimage.h
src/gui/image/qimage_p.h
tests/auto/gui/image/qimage/tst_qimage.cpp

index e77733c..07af19d 100644 (file)
@@ -124,7 +124,8 @@ QImageData::QImageData()
       dpmx(qt_defaultDpiX() * 100 / qreal(2.54)),
       dpmy(qt_defaultDpiY() * 100 / qreal(2.54)),
       offset(0, 0), own_data(true), ro_data(false), has_alpha_clut(false),
-      is_cached(false), is_locked(false), paintEngine(0)
+      is_cached(false), is_locked(false), cleanupFunction(0), cleanupInfo(0),
+      paintEngine(0)
 {
 }
 
@@ -206,6 +207,8 @@ QImageData * QImageData::create(const QSize &size, QImage::Format format, int nu
 
 QImageData::~QImageData()
 {
+    if (cleanupFunction)
+        cleanupFunction(cleanupInfo);
     if (is_cached)
         QImagePixmapCleanupHooks::executeImageHooks((((qint64) ser_no) << 32) | ((qint64) detach_no));
     delete paintEngine;
@@ -616,6 +619,18 @@ bool QImageData::checkForAlphaPixels() const
 */
 
 /*!
+    \typedef QImageCleanupFunction
+    \since 5.0
+
+    A function with the following signature that can be used to
+    implement basic image memory management:
+
+    \code
+    void myImageCleanupHandler(void *info);
+    \endcode
+*/
+
+/*!
     \enum QImage::InvertMode
 
     This enum type is used to describe how pixel values should be
@@ -771,7 +786,7 @@ QImage::QImage(const QSize &size, Format format)
 
 
 
-QImageData *QImageData::create(uchar *data, int width, int height,  int bpl, QImage::Format format, bool readOnly)
+QImageData *QImageData::create(uchar *data, int width, int height,  int bpl, QImage::Format format, bool readOnly, QImageCleanupFunction cleanupFunction, void *cleanupInfo)
 {
     QImageData *d = 0;
 
@@ -814,6 +829,9 @@ QImageData *QImageData::create(uchar *data, int width, int height,  int bpl, QIm
     d->bytes_per_line = bpl;
     d->nbytes = d->bytes_per_line * height;
 
+    d->cleanupFunction = cleanupFunction;
+    d->cleanupInfo = cleanupInfo;
+
     return d;
 }
 
@@ -823,17 +841,21 @@ QImageData *QImageData::create(uchar *data, int width, int height,  int bpl, QIm
     and \a height must be specified in pixels, \a 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.
+    The buffer must remain valid throughout the life of the QImage and
+    all copies that have not been modified or otherwise detached from
+    the original buffer. The image does not delete the buffer at destruction.
+    You can provide a function pointer \a cleanupFunction along with an
+    extra pointer \a cleanupInfo that will be called when the last copy
+    is destroyed.
 
     If \a format is an indexed color format, the image color table is
     initially empty and must be sufficiently expanded with
     setColorCount() or setColorTable() before the image is used.
 */
-QImage::QImage(uchar* data, int width, int height, Format format)
+QImage::QImage(uchar* data, int width, int height, Format format, QImageCleanupFunction cleanupFunction, void *cleanupInfo)
     : QPaintDevice()
 {
-    d = QImageData::create(data, width, height, 0, format, false);
+    d = QImageData::create(data, width, height, 0, format, false, cleanupFunction, cleanupInfo);
 }
 
 /*!
@@ -845,8 +867,10 @@ QImage::QImage(uchar* data, int width, int height, Format format)
 
     The buffer must remain valid throughout the life of the QImage and
     all copies that have not been modified or otherwise detached from
-    the original buffer. The image does not delete the buffer at
-    destruction.
+    the original buffer. The image does not delete the buffer at destruction.
+    You can provide a function pointer \a cleanupFunction along with an
+    extra pointer \a cleanupInfo that will be called when the last copy
+    is destroyed.
 
     If \a format is an indexed color format, the image color table is
     initially empty and must be sufficiently expanded with
@@ -859,10 +883,10 @@ QImage::QImage(uchar* data, int width, int height, Format format)
     constructing a QImage from raw data, without the possibility of the raw
     data being changed.
 */
-QImage::QImage(const uchar* data, int width, int height, Format format)
+QImage::QImage(const uchar* data, int width, int height, Format format, QImageCleanupFunction cleanupFunction, void *cleanupInfo)
     : QPaintDevice()
 {
-    d = QImageData::create(const_cast<uchar*>(data), width, height, 0, format, true);
+    d = QImageData::create(const_cast<uchar*>(data), width, height, 0, format, true, cleanupFunction, cleanupInfo);
 }
 
 /*!
@@ -871,17 +895,21 @@ QImage::QImage(const uchar* data, int width, int height, Format format)
     and \a height must be specified in pixels. \a bytesPerLine
     specifies the number of bytes per line (stride).
 
-    The buffer must remain valid throughout the life of the
-    QImage. The image does not delete the buffer at destruction.
+    The buffer must remain valid throughout the life of the QImage and
+    all copies that have not been modified or otherwise detached from
+    the original buffer. The image does not delete the buffer at destruction.
+    You can provide a function pointer \a cleanupFunction along with an
+    extra pointer \a cleanupInfo that will be called when the last copy
+    is destroyed.
 
     If \a format is an indexed color format, the image color table is
     initially empty and must be sufficiently expanded with
     setColorCount() or setColorTable() before the image is used.
 */
-QImage::QImage(uchar *data, int width, int height, int bytesPerLine, Format format)
+QImage::QImage(uchar *data, int width, int height, int bytesPerLine, Format format, QImageCleanupFunction cleanupFunction, void *cleanupInfo)
     :QPaintDevice()
 {
-    d = QImageData::create(data, width, height, bytesPerLine, format, false);
+    d = QImageData::create(data, width, height, bytesPerLine, format, false, cleanupFunction, cleanupInfo);
 }
 
 
@@ -891,8 +919,12 @@ QImage::QImage(uchar *data, int width, int height, int bytesPerLine, Format form
     and \a height must be specified in pixels. \a bytesPerLine
     specifies the number of bytes per line (stride).
 
-    The buffer must remain valid throughout the life of the
-    QImage. The image does not delete the buffer at destruction.
+    The buffer must remain valid throughout the life of the QImage and
+    all copies that have not been modified or otherwise detached from
+    the original buffer. The image does not delete the buffer at destruction.
+    You can provide a function pointer \a cleanupFunction along with an
+    extra pointer \a cleanupInfo that will be called when the last copy
+    is destroyed.
 
     If \a format is an indexed color format, the image color table is
     initially empty and must be sufficiently expanded with
@@ -906,10 +938,10 @@ QImage::QImage(uchar *data, int width, int height, int bytesPerLine, Format form
     data being changed.
 */
 
-QImage::QImage(const uchar *data, int width, int height, int bytesPerLine, Format format)
+QImage::QImage(const uchar *data, int width, int height, int bytesPerLine, Format format, QImageCleanupFunction cleanupFunction, void *cleanupInfo)
     :QPaintDevice()
 {
-    d = QImageData::create(const_cast<uchar*>(data), width, height, bytesPerLine, format, true);
+    d = QImageData::create(const_cast<uchar*>(data), width, height, bytesPerLine, format, true, cleanupFunction, cleanupInfo);
 }
 
 /*!
index b805a32..5a3ae8f 100644 (file)
@@ -88,6 +88,7 @@ public:
 #endif
 #endif //QT_NO_IMAGE_TEXT
 
+typedef void (*QImageCleanupFunction)(void*);
 
 class Q_GUI_EXPORT QImage : public QPaintDevice
 {
@@ -128,10 +129,10 @@ public:
     QImage();
     QImage(const QSize &size, Format format);
     QImage(int width, int height, Format format);
-    QImage(uchar *data, int width, int height, Format format);
-    QImage(const uchar *data, int width, int height, Format format);
-    QImage(uchar *data, int width, int height, int bytesPerLine, Format format);
-    QImage(const uchar *data, int width, int height, int bytesPerLine, Format format);
+    QImage(uchar *data, int width, int height, Format format, QImageCleanupFunction cleanupFunction = 0, void *cleanupInfo = 0);
+    QImage(const uchar *data, int width, int height, Format format, QImageCleanupFunction cleanupFunction = 0, void *cleanupInfo = 0);
+    QImage(uchar *data, int width, int height, int bytesPerLine, Format format, QImageCleanupFunction cleanupFunction = 0, void *cleanupInfo = 0);
+    QImage(const uchar *data, int width, int height, int bytesPerLine, Format format, QImageCleanupFunction cleanupFunction = 0, void *cleanupInfo = 0);
 
 #ifndef QT_NO_IMAGEFORMAT_XPM
     explicit QImage(const char * const xpm[]);
index a021679..fd1370d 100644 (file)
@@ -69,7 +69,7 @@ struct Q_GUI_EXPORT QImageData {        // internal image data
     QImageData();
     ~QImageData();
     static QImageData *create(const QSize &size, QImage::Format format, int numColors = 0);
-    static QImageData *create(uchar *data, int w, int h,  int bpl, QImage::Format format, bool readOnly);
+    static QImageData *create(uchar *data, int w, int h,  int bpl, QImage::Format format, bool readOnly, QImageCleanupFunction cleanupFunction = 0, void *cleanupInfo = 0);
 
     QAtomicInt ref;
 
@@ -94,6 +94,9 @@ struct Q_GUI_EXPORT QImageData {        // internal image data
     uint is_cached : 1;
     uint is_locked : 1;
 
+    QImageCleanupFunction cleanupFunction;
+    void* cleanupInfo;
+
     bool checkForAlphaPixels() const;
 
     // Convert the image in-place, minimizing memory reallocation
index 173c299..cda887d 100644 (file)
@@ -144,6 +144,8 @@ private slots:
 
     void deepCopyWhenPaintingActive();
     void scaled_QTBUG19157();
+
+    void cleanupFunctions();
 };
 
 tst_QImage::tst_QImage()
@@ -1997,5 +1999,40 @@ void tst_QImage::scaled_QTBUG19157()
     QVERIFY(!foo.isNull());
 }
 
+static void cleanupFunction(void* info)
+{
+    bool *called = static_cast<bool*>(info);
+    *called = true;
+}
+
+void tst_QImage::cleanupFunctions()
+{
+    QImage bufferImage(64, 64, QImage::Format_ARGB32);
+    bufferImage.fill(0);
+
+    bool called;
+
+    {
+        called = false;
+        {
+            QImage image(bufferImage.bits(), bufferImage.width(), bufferImage.height(), bufferImage.format(), cleanupFunction, &called);
+        }
+        QVERIFY(called);
+    }
+
+    {
+        called = false;
+        QImage *copy = 0;
+        {
+            QImage image(bufferImage.bits(), bufferImage.width(), bufferImage.height(), bufferImage.format(), cleanupFunction, &called);
+            copy = new QImage(image);
+        }
+        QVERIFY(!called);
+        delete copy;
+        QVERIFY(called);
+    }
+
+}
+
 QTEST_MAIN(tst_QImage)
 #include "tst_qimage.moc"