From 298330bd436ca9437aa8023363f916b12811c7bf Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 31 Jan 2012 13:32:22 +0100 Subject: Add support for cleanup functions for data-constructed QImages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 Reviewed-by: Michalina Ziemba Reviewed-by: Gunnar Sletta --- src/gui/image/qimage.cpp | 68 ++++++++++++++++++++++-------- src/gui/image/qimage.h | 9 ++-- src/gui/image/qimage_p.h | 5 ++- tests/auto/gui/image/qimage/tst_qimage.cpp | 37 ++++++++++++++++ 4 files changed, 96 insertions(+), 23 deletions(-) diff --git a/src/gui/image/qimage.cpp b/src/gui/image/qimage.cpp index e77733c984..07af19d06a 100644 --- a/src/gui/image/qimage.cpp +++ b/src/gui/image/qimage.cpp @@ -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; @@ -615,6 +618,18 @@ bool QImageData::checkForAlphaPixels() const {Image Viewer Example}, {Scribble Example}, {Pixelator Example} */ +/*! + \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 @@ -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(data), width, height, 0, format, true); + d = QImageData::create(const_cast(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(data), width, height, bytesPerLine, format, true); + d = QImageData::create(const_cast(data), width, height, bytesPerLine, format, true, cleanupFunction, cleanupInfo); } /*! diff --git a/src/gui/image/qimage.h b/src/gui/image/qimage.h index b805a3297f..5a3ae8f886 100644 --- a/src/gui/image/qimage.h +++ b/src/gui/image/qimage.h @@ -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[]); diff --git a/src/gui/image/qimage_p.h b/src/gui/image/qimage_p.h index a021679c18..fd1370d1d3 100644 --- a/src/gui/image/qimage_p.h +++ b/src/gui/image/qimage_p.h @@ -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 diff --git a/tests/auto/gui/image/qimage/tst_qimage.cpp b/tests/auto/gui/image/qimage/tst_qimage.cpp index 173c299b69..cda887d8e1 100644 --- a/tests/auto/gui/image/qimage/tst_qimage.cpp +++ b/tests/auto/gui/image/qimage/tst_qimage.cpp @@ -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(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" -- cgit v1.2.3