diff --git a/src/plugins/imageformats/tiff/qtiffhandler.cpp b/src/plugins/imageformats/tiff/qtiffhandler.cpp index 71b2cccc83b4a0cd6b00d401cf2f5a50dc8161e6..93fd1eda6b9f0308586396a1f777389c9d08f696 100644 --- a/src/plugins/imageformats/tiff/qtiffhandler.cpp +++ b/src/plugins/imageformats/tiff/qtiffhandler.cpp @@ -44,18 +44,18 @@ QT_BEGIN_NAMESPACE tsize_t qtiffReadProc(thandle_t fd, tdata_t buf, tsize_t size) { - QIODevice* device = static_cast<QTiffHandler*>(fd)->device(); + QIODevice *device = static_cast<QIODevice *>(fd); return device->isReadable() ? device->read(static_cast<char *>(buf), size) : -1; } tsize_t qtiffWriteProc(thandle_t fd, tdata_t buf, tsize_t size) { - return static_cast<QTiffHandler*>(fd)->device()->write(static_cast<char *>(buf), size); + return static_cast<QIODevice *>(fd)->write(static_cast<char *>(buf), size); } toff_t qtiffSeekProc(thandle_t fd, toff_t off, int whence) { - QIODevice *device = static_cast<QTiffHandler*>(fd)->device(); + QIODevice *device = static_cast<QIODevice *>(fd); switch (whence) { case SEEK_SET: device->seek(off); @@ -78,7 +78,7 @@ int qtiffCloseProc(thandle_t /*fd*/) toff_t qtiffSizeProc(thandle_t fd) { - return static_cast<QTiffHandler*>(fd)->device()->size(); + return static_cast<QIODevice *>(fd)->size(); } int qtiffMapProc(thandle_t /*fd*/, tdata_t* /*pbase*/, toff_t* /*psize*/) @@ -127,21 +127,49 @@ inline void rotate_right_mirror_vertical(QImage *const image) // rotate right->m *image = generated; } -QTiffHandler::QTiffHandler() : QImageIOHandler() +class QTiffHandlerPrivate +{ +public: + QTiffHandlerPrivate(); + ~QTiffHandlerPrivate(); + + static bool canRead(QIODevice *device); + bool openForRead(QIODevice *device); + bool readHeaders(QIODevice *device); + void close(); + + TIFF *tiff; + int compression; + QImage::Format format; + QSize size; + uint16 photometric; + bool grayscale; + bool headersRead; +}; + +QTiffHandlerPrivate::QTiffHandlerPrivate() + : tiff(0) + , compression(QTiffHandler::NoCompression) + , format(QImage::Format_Invalid) + , photometric(false) + , grayscale(false) + , headersRead(false) { - compression = NoCompression; } -bool QTiffHandler::canRead() const +QTiffHandlerPrivate::~QTiffHandlerPrivate() { - if (canRead(device())) { - setFormat("tiff"); - return true; - } - return false; + close(); } -bool QTiffHandler::canRead(QIODevice *device) +void QTiffHandlerPrivate::close() +{ + if (tiff) + TIFFClose(tiff); + tiff = 0; +} + +bool QTiffHandlerPrivate::canRead(QIODevice *device) { if (!device) { qWarning("QTiffHandler::canRead() called with no device"); @@ -155,34 +183,37 @@ bool QTiffHandler::canRead(QIODevice *device) || header == QByteArray::fromRawData("\x4D\x4D\x00\x2A", 4); } -bool QTiffHandler::read(QImage *image) +bool QTiffHandlerPrivate::openForRead(QIODevice *device) { - if (!canRead()) + if (tiff) + return true; + + if (!canRead(device)) return false; - TIFF *const tiff = TIFFClientOpen("foo", - "r", - this, - qtiffReadProc, - qtiffWriteProc, - qtiffSeekProc, - qtiffCloseProc, - qtiffSizeProc, - qtiffMapProc, - qtiffUnmapProc); + tiff = TIFFClientOpen("foo", + "r", + device, + qtiffReadProc, + qtiffWriteProc, + qtiffSeekProc, + qtiffCloseProc, + qtiffSizeProc, + qtiffMapProc, + qtiffUnmapProc); if (!tiff) { return false; } uint32 width; uint32 height; - uint16 photometric; if (!TIFFGetField(tiff, TIFFTAG_IMAGEWIDTH, &width) || !TIFFGetField(tiff, TIFFTAG_IMAGELENGTH, &height) || !TIFFGetField(tiff, TIFFTAG_PHOTOMETRIC, &photometric)) { - TIFFClose(tiff); + close(); return false; } + size = QSize(width, height); // BitsPerSample defaults to 1 according to the TIFF spec. uint16 bitPerSample; @@ -192,12 +223,71 @@ bool QTiffHandler::read(QImage *image) if (!TIFFGetField(tiff, TIFFTAG_SAMPLESPERPIXEL, &samplesPerPixel)) samplesPerPixel = 1; - bool grayscale = photometric == PHOTOMETRIC_MINISBLACK || photometric == PHOTOMETRIC_MINISWHITE; - if (grayscale && bitPerSample == 1 && samplesPerPixel == 1) { - if (image->size() != QSize(width, height) || image->format() != QImage::Format_Mono) - *image = QImage(width, height, QImage::Format_Mono); + grayscale = photometric == PHOTOMETRIC_MINISBLACK || photometric == PHOTOMETRIC_MINISWHITE; + + if (grayscale && bitPerSample == 1 && samplesPerPixel == 1) + format = QImage::Format_Mono; + else if ((grayscale || photometric == PHOTOMETRIC_PALETTE) && bitPerSample == 8 && samplesPerPixel == 1) + format = QImage::Format_Indexed8; + else if (samplesPerPixel < 4) + format = QImage::Format_RGB32; + else + format = QImage::Format_ARGB32; + + headersRead = true; + return true; +} + +bool QTiffHandlerPrivate::readHeaders(QIODevice *device) +{ + if (headersRead) + return true; + + return openForRead(device); +} + +QTiffHandler::QTiffHandler() + : QImageIOHandler() + , d(new QTiffHandlerPrivate) +{ +} + +bool QTiffHandler::canRead() const +{ + if (d->tiff) + return true; + if (QTiffHandlerPrivate::canRead(device())) { + setFormat("tiff"); + return true; + } + return false; +} + +bool QTiffHandler::canRead(QIODevice *device) +{ + return QTiffHandlerPrivate::canRead(device); +} + +bool QTiffHandler::read(QImage *image) +{ + // Open file and read headers if it hasn't already been done. + if (!d->openForRead(device())) + return false; + + QImage::Format format = d->format; + if (format == QImage::Format_RGB32 && image->format() == QImage::Format_ARGB32) + format = image->format(); + + if (image->size() != d->size || image->format() != format) + *image = QImage(d->size, format); + + TIFF *const tiff = d->tiff; + const uint32 width = d->size.width(); + const uint32 height = d->size.height(); + + if (format == QImage::Format_Mono) { QVector<QRgb> colortable(2); - if (photometric == PHOTOMETRIC_MINISBLACK) { + if (d->photometric == PHOTOMETRIC_MINISBLACK) { colortable[0] = 0xff000000; colortable[1] = 0xffffffff; } else { @@ -209,21 +299,19 @@ bool QTiffHandler::read(QImage *image) if (!image->isNull()) { for (uint32 y=0; y<height; ++y) { if (TIFFReadScanline(tiff, image->scanLine(y), y, 0) < 0) { - TIFFClose(tiff); + d->close(); return false; } } } } else { - if ((grayscale || photometric == PHOTOMETRIC_PALETTE) && bitPerSample == 8 && samplesPerPixel == 1) { - if (image->size() != QSize(width, height) || image->format() != QImage::Format_Indexed8) - *image = QImage(width, height, QImage::Format_Indexed8); + if (format == QImage::Format_Indexed8) { if (!image->isNull()) { const uint16 tableSize = 256; QVector<QRgb> qtColorTable(tableSize); - if (grayscale) { + if (d->grayscale) { for (int i = 0; i<tableSize; ++i) { - const int c = (photometric == PHOTOMETRIC_MINISBLACK) ? i : (255 - i); + const int c = (d->photometric == PHOTOMETRIC_MINISBLACK) ? i : (255 - i); qtColorTable[i] = qRgb(c, c, c); } } else { @@ -232,11 +320,11 @@ bool QTiffHandler::read(QImage *image) uint16 *greenTable = 0; uint16 *blueTable = 0; if (!TIFFGetField(tiff, TIFFTAG_COLORMAP, &redTable, &greenTable, &blueTable)) { - TIFFClose(tiff); + d->close(); return false; } if (!redTable || !greenTable || !blueTable) { - TIFFClose(tiff); + d->close(); return false; } @@ -251,7 +339,7 @@ bool QTiffHandler::read(QImage *image) image->setColorTable(qtColorTable); for (uint32 y=0; y<height; ++y) { if (TIFFReadScanline(tiff, image->scanLine(y), y, 0) < 0) { - TIFFClose(tiff); + d->close(); return false; } } @@ -259,19 +347,13 @@ bool QTiffHandler::read(QImage *image) // free redTable, greenTable and greenTable done by libtiff } } else { - QImage::Format format = QImage::Format_ARGB32; - if (samplesPerPixel < 4 && image->format() != QImage::Format_ARGB32) - format = QImage::Format_RGB32; - - if (image->size() != QSize(width, height) || image->format() != format) - *image = QImage(width, height, format); if (!image->isNull()) { const int stopOnError = 1; if (TIFFReadRGBAImageOriented(tiff, width, height, reinterpret_cast<uint32 *>(image->bits()), ORIENTATION_TOPLEFT, stopOnError)) { for (uint32 y=0; y<height; ++y) convert32BitOrder(image->scanLine(y), width); } else { - TIFFClose(tiff); + d->close(); return false; } } @@ -279,7 +361,7 @@ bool QTiffHandler::read(QImage *image) } if (image->isNull()) { - TIFFClose(tiff); + d->close(); return false; } @@ -374,8 +456,7 @@ bool QTiffHandler::read(QImage *image) } } - - TIFFClose(tiff); + d->close(); return true; } @@ -400,7 +481,7 @@ bool QTiffHandler::write(const QImage &image) TIFF *const tiff = TIFFClientOpen("foo", "wB", - this, + device(), qtiffReadProc, qtiffWriteProc, qtiffSeekProc, @@ -413,6 +494,7 @@ bool QTiffHandler::write(const QImage &image) const int width = image.width(); const int height = image.height(); + const int compression = d->compression; if (!TIFFSetField(tiff, TIFFTAG_IMAGEWIDTH, width) || !TIFFSetField(tiff, TIFFTAG_IMAGELENGTH, height) @@ -604,34 +686,13 @@ QByteArray QTiffHandler::name() const QVariant QTiffHandler::option(ImageOption option) const { if (option == Size && canRead()) { - QSize imageSize; - qint64 pos = device()->pos(); - TIFF *tiff = TIFFClientOpen("foo", - "r", - const_cast<QTiffHandler*>(this), - qtiffReadProc, - qtiffWriteProc, - qtiffSeekProc, - qtiffCloseProc, - qtiffSizeProc, - qtiffMapProc, - qtiffUnmapProc); - - if (tiff) { - uint32 width = 0; - uint32 height = 0; - TIFFGetField(tiff, TIFFTAG_IMAGEWIDTH, &width); - TIFFGetField(tiff, TIFFTAG_IMAGELENGTH, &height); - imageSize = QSize(width, height); - TIFFClose(tiff); - } - device()->seek(pos); - if (imageSize.isValid()) - return imageSize; + if (d->readHeaders(device())) + return d->size; } else if (option == CompressionRatio) { - return compression; + return d->compression; } else if (option == ImageFormat) { - return QImage::Format_ARGB32; + if (d->readHeaders(device())) + return d->format; } return QVariant(); } @@ -639,7 +700,7 @@ QVariant QTiffHandler::option(ImageOption option) const void QTiffHandler::setOption(ImageOption option, const QVariant &value) { if (option == CompressionRatio && value.type() == QVariant::Int) - compression = value.toInt(); + d->compression = value.toInt(); } bool QTiffHandler::supportsOption(ImageOption option) const diff --git a/src/plugins/imageformats/tiff/qtiffhandler_p.h b/src/plugins/imageformats/tiff/qtiffhandler_p.h index d6f2e7bd124f4a95bb9de21c1dd1314c17e3eb1b..e07d9c4ebf4ca99ea25ac4d5b7c9ce5da8c06089 100644 --- a/src/plugins/imageformats/tiff/qtiffhandler_p.h +++ b/src/plugins/imageformats/tiff/qtiffhandler_p.h @@ -34,26 +34,28 @@ #ifndef QTIFFHANDLER_P_H #define QTIFFHANDLER_P_H -#include <QtGui/qimageiohandler.h> +#include <QtCore/QScopedPointer> +#include <QtGui/QImageIOHandler> QT_BEGIN_NAMESPACE +class QTiffHandlerPrivate; class QTiffHandler : public QImageIOHandler { public: QTiffHandler(); - bool canRead() const; - bool read(QImage *image); - bool write(const QImage &image); + bool canRead() const Q_DECL_OVERRIDE; + bool read(QImage *image) Q_DECL_OVERRIDE; + bool write(const QImage &image) Q_DECL_OVERRIDE; - QByteArray name() const; + QByteArray name() const Q_DECL_OVERRIDE; static bool canRead(QIODevice *device); - QVariant option(ImageOption option) const; - void setOption(ImageOption option, const QVariant &value); - bool supportsOption(ImageOption option) const; + QVariant option(ImageOption option) const Q_DECL_OVERRIDE; + void setOption(ImageOption option, const QVariant &value) Q_DECL_OVERRIDE; + bool supportsOption(ImageOption option) const Q_DECL_OVERRIDE; enum Compression { NoCompression = 0, @@ -61,7 +63,7 @@ public: }; private: void convert32BitOrder(void *buffer, int width); - int compression; + const QScopedPointer<QTiffHandlerPrivate> d; }; QT_END_NAMESPACE diff --git a/tests/auto/tiff/tst_qtiff.cpp b/tests/auto/tiff/tst_qtiff.cpp index 6a9f9c10a6e6d67e300f1326af6e4dffac64319a..89630c22be581d6d66fedcd2c6cbdbd5c78e1c1f 100644 --- a/tests/auto/tiff/tst_qtiff.cpp +++ b/tests/auto/tiff/tst_qtiff.cpp @@ -394,6 +394,8 @@ void tst_qtiff::readWriteNonDestructive() QVERIFY(buf.open(QIODevice::ReadOnly)); QImageReader reader(&buf); + QCOMPARE(reader.imageFormat(), expectedFormat); + QCOMPARE(reader.size(), image.size()); QImage image2 = reader.read(); QVERIFY2(!image.isNull(), qPrintable(reader.errorString()));