summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAllan Sandfeld Jensen <allan.jensen@theqtcompany.com>2015-04-20 11:27:10 +0200
committerKonstantin Ritt <ritt.ks@gmail.com>2015-05-04 17:55:14 +0000
commit4c00a26fc583003059f1854bf1289c28c7f01bd6 (patch)
tree8cd14fb6bda241b4b8032fa22b58c4b3aaf0b5ef
parent5b1a3318125b72563b5de1bf7549088165ec60f4 (diff)
Clean up reading TIFF headers
Split reading TIFF headers from the decoding to make it possible to read correct metadata on undecoded images. This fixes reading the image format from the QImageReader, and is necessary for later patches. Change-Id: Ida27e98252bf95459d87354586d4a5fba348efcb Reviewed-by: Ivan Komissarov <ABBAPOH@gmail.com> Reviewed-by: Konstantin Ritt <ritt.ks@gmail.com>
-rw-r--r--src/plugins/imageformats/tiff/qtiffhandler.cpp219
-rw-r--r--src/plugins/imageformats/tiff/qtiffhandler_p.h20
-rw-r--r--tests/auto/tiff/tst_qtiff.cpp2
3 files changed, 153 insertions, 88 deletions
diff --git a/src/plugins/imageformats/tiff/qtiffhandler.cpp b/src/plugins/imageformats/tiff/qtiffhandler.cpp
index 71b2ccc..93fd1ed 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 d6f2e7b..e07d9c4 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 6a9f9c1..89630c2 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()));