Add support for cleanup functions for data-constructed QImages

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>
This commit is contained in:
Simon Hausmann 2012-01-31 13:32:22 +01:00 committed by Qt by Nokia
parent 2bf186a2e5
commit 298330bd43
4 changed files with 96 additions and 23 deletions

View 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;
@ -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<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);
}
/*!

View 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[]);

View 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

View 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"