summaryrefslogtreecommitdiffstats
path: root/src/plugins/imageformats/tiff/qtiffhandler.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/imageformats/tiff/qtiffhandler.cpp')
-rw-r--r--src/plugins/imageformats/tiff/qtiffhandler.cpp124
1 files changed, 78 insertions, 46 deletions
diff --git a/src/plugins/imageformats/tiff/qtiffhandler.cpp b/src/plugins/imageformats/tiff/qtiffhandler.cpp
index 65873e1..f0dfe7f 100644
--- a/src/plugins/imageformats/tiff/qtiffhandler.cpp
+++ b/src/plugins/imageformats/tiff/qtiffhandler.cpp
@@ -43,6 +43,9 @@
#include <qdebug.h>
#include <qimage.h>
#include <qglobal.h>
+#include <qbuffer.h>
+#include <qfiledevice.h>
+
extern "C" {
#include "tiffio.h"
}
@@ -90,13 +93,33 @@ toff_t qtiffSizeProc(thandle_t fd)
return static_cast<QIODevice *>(fd)->size();
}
-int qtiffMapProc(thandle_t /*fd*/, tdata_t* /*pbase*/, toff_t* /*psize*/)
+int qtiffMapProc(thandle_t fd, void **base, toff_t *size)
{
+ QIODevice *device = static_cast<QIODevice *>(fd);
+
+ QFileDevice *file = qobject_cast<QFileDevice *>(device);
+ if (file) {
+ *base = file->map(0, file->size());
+ if (*base != nullptr) {
+ *size = file->size();
+ return 1;
+ }
+ } else {
+ QBuffer *buf = qobject_cast<QBuffer *>(device);
+ if (buf) {
+ *base = const_cast<char *>(buf->data().constData());
+ *size = buf->size();
+ return 1;
+ }
+ }
return 0;
}
-void qtiffUnmapProc(thandle_t /*fd*/, tdata_t /*base*/, toff_t /*size*/)
+void qtiffUnmapProc(thandle_t fd, void *base, toff_t /*size*/)
{
+ QFileDevice *file = qobject_cast<QFileDevice *>(static_cast<QIODevice *>(fd));
+ if (file && base)
+ file->unmap(static_cast<uchar *>(base));
}
@@ -116,7 +139,7 @@ public:
QImageIOHandler::Transformations transformation;
QImage::Format format;
QSize size;
- uint16 photometric;
+ uint16_t photometric;
bool grayscale;
bool headersRead;
int currentDirectory;
@@ -205,9 +228,14 @@ bool QTiffHandlerPrivate::canRead(QIODevice *device)
// current implementation uses TIFFClientOpen which needs to be
// able to seek, so sequential devices are not supported
- QByteArray header = device->peek(4);
- return header == QByteArray::fromRawData("\x49\x49\x2A\x00", 4)
- || header == QByteArray::fromRawData("\x4D\x4D\x00\x2A", 4);
+ char h[4];
+ if (device->peek(h, 4) != 4)
+ return false;
+ if ((h[0] == 0x49 && h[1] == 0x49) && (h[2] == 0x2a || h[2] == 0x2b) && h[3] == 0)
+ return true; // Little endian, classic or bigtiff
+ if ((h[0] == 0x4d && h[1] == 0x4d) && h[2] == 0 && (h[3] == 0x2a || h[3] == 0x2b))
+ return true; // Big endian, classic or bigtiff
+ return false;
}
bool QTiffHandlerPrivate::openForRead(QIODevice *device)
@@ -245,8 +273,8 @@ bool QTiffHandlerPrivate::readHeaders(QIODevice *device)
TIFFSetDirectory(tiff, currentDirectory);
- uint32 width;
- uint32 height;
+ uint32_t width;
+ uint32_t height;
if (!TIFFGetField(tiff, TIFFTAG_IMAGEWIDTH, &width)
|| !TIFFGetField(tiff, TIFFTAG_IMAGELENGTH, &height)
|| !TIFFGetField(tiff, TIFFTAG_PHOTOMETRIC, &photometric)) {
@@ -255,15 +283,15 @@ bool QTiffHandlerPrivate::readHeaders(QIODevice *device)
}
size = QSize(width, height);
- uint16 orientationTag;
+ uint16_t orientationTag;
if (TIFFGetField(tiff, TIFFTAG_ORIENTATION, &orientationTag))
transformation = exif2Qt(orientationTag);
// BitsPerSample defaults to 1 according to the TIFF spec.
- uint16 bitPerSample;
+ uint16_t bitPerSample;
if (!TIFFGetField(tiff, TIFFTAG_BITSPERSAMPLE, &bitPerSample))
bitPerSample = 1;
- uint16 samplesPerPixel; // they may be e.g. grayscale with 2 samples per pixel
+ uint16_t samplesPerPixel; // they may be e.g. grayscale with 2 samples per pixel
if (!TIFFGetField(tiff, TIFFTAG_SAMPLESPERPIXEL, &samplesPerPixel))
samplesPerPixel = 1;
@@ -278,13 +306,13 @@ bool QTiffHandlerPrivate::readHeaders(QIODevice *device)
else if ((grayscale || photometric == PHOTOMETRIC_PALETTE) && bitPerSample == 8 && samplesPerPixel == 1)
format = QImage::Format_Indexed8;
else if (samplesPerPixel < 4)
- if (bitPerSample > 8 && photometric == PHOTOMETRIC_RGB)
+ if (bitPerSample == 16 && photometric == PHOTOMETRIC_RGB)
format = QImage::Format_RGBX64;
else
format = QImage::Format_RGB32;
else {
- uint16 count;
- uint16 *extrasamples;
+ uint16_t count;
+ uint16_t *extrasamples;
// If there is any definition of the alpha-channel, libtiff will return premultiplied
// data to us. If there is none, libtiff will not touch it and we assume it to be
// non-premultiplied, matching behavior of tested image editors, and how older Qt
@@ -294,7 +322,7 @@ bool QTiffHandlerPrivate::readHeaders(QIODevice *device)
if (!gotField || !count || extrasamples[0] == EXTRASAMPLE_UNSPECIFIED)
premultiplied = false;
- if (bitPerSample > 8 && photometric == PHOTOMETRIC_RGB) {
+ if (bitPerSample == 16 && photometric == PHOTOMETRIC_RGB) {
// We read 64-bit raw, so unassoc remains unpremultiplied.
if (gotField && count && extrasamples[0] == EXTRASAMPLE_UNASSALPHA)
premultiplied = false;
@@ -356,6 +384,8 @@ bool QTiffHandler::read(QImage *image)
}
TIFF *const tiff = d->tiff;
+ if (TIFFIsTiled(tiff) && TIFFTileSize64(tiff) > uint64_t(image->sizeInBytes())) // Corrupt image
+ return false;
const quint32 width = d->size.width();
const quint32 height = d->size.height();
@@ -372,7 +402,7 @@ bool QTiffHandler::read(QImage *image)
}
image->setColorTable(colortable);
} else if (format == QImage::Format_Indexed8) {
- const uint16 tableSize = 256;
+ const uint16_t tableSize = 256;
QVector<QRgb> qtColorTable(tableSize);
if (d->grayscale) {
for (int i = 0; i<tableSize; ++i) {
@@ -381,9 +411,9 @@ bool QTiffHandler::read(QImage *image)
}
} else {
// create the color table
- uint16 *redTable = 0;
- uint16 *greenTable = 0;
- uint16 *blueTable = 0;
+ uint16_t *redTable = 0;
+ uint16_t *greenTable = 0;
+ uint16_t *blueTable = 0;
if (!TIFFGetField(tiff, TIFFTAG_COLORMAP, &redTable, &greenTable, &blueTable)) {
d->close();
return false;
@@ -418,14 +448,19 @@ bool QTiffHandler::read(QImage *image)
quint32 tileWidth, tileLength;
TIFFGetField(tiff, TIFFTAG_TILEWIDTH, &tileWidth);
TIFFGetField(tiff, TIFFTAG_TILELENGTH, &tileLength);
- uchar *buf = (uchar *)_TIFFmalloc(TIFFTileSize(tiff));
- if (!tileWidth || !tileLength || !buf) {
- _TIFFfree(buf);
+ if (!tileWidth || !tileLength || tileWidth % 16 || tileLength % 16) {
d->close();
return false;
}
quint32 byteWidth = (format == QImage::Format_Mono) ? (width + 7)/8 : (width * bytesPerPixel);
quint32 byteTileWidth = (format == QImage::Format_Mono) ? tileWidth/8 : (tileWidth * bytesPerPixel);
+ tmsize_t byteTileSize = TIFFTileSize(tiff);
+ uchar *buf = (uchar *)_TIFFmalloc(byteTileSize);
+ if (!buf || byteTileSize / tileLength < byteTileWidth) {
+ _TIFFfree(buf);
+ d->close();
+ return false;
+ }
for (quint32 y = 0; y < height; y += tileLength) {
for (quint32 x = 0; x < width; x += tileWidth) {
if (TIFFReadTile(tiff, buf, x, y, 0, 0) < 0) {
@@ -443,7 +478,11 @@ bool QTiffHandler::read(QImage *image)
}
_TIFFfree(buf);
} else {
- for (uint32 y=0; y<height; ++y) {
+ if (image->bytesPerLine() < TIFFScanlineSize(tiff)) {
+ d->close();
+ return false;
+ }
+ for (uint32_t y=0; y<height; ++y) {
if (TIFFReadScanline(tiff, image->scanLine(y), y, 0) < 0) {
d->close();
return false;
@@ -454,8 +493,8 @@ bool QTiffHandler::read(QImage *image)
rgb48fixup(image);
} else {
const int stopOnError = 1;
- if (TIFFReadRGBAImageOriented(tiff, width, height, reinterpret_cast<uint32 *>(image->bits()), qt2Exif(d->transformation), stopOnError)) {
- for (uint32 y=0; y<height; ++y)
+ if (TIFFReadRGBAImageOriented(tiff, width, height, reinterpret_cast<uint32_t *>(image->bits()), qt2Exif(d->transformation), stopOnError)) {
+ for (uint32_t y=0; y<height; ++y)
convert32BitOrder(image->scanLine(y), width);
} else {
d->close();
@@ -466,7 +505,7 @@ bool QTiffHandler::read(QImage *image)
float resX = 0;
float resY = 0;
- uint16 resUnit;
+ uint16_t resUnit;
if (!TIFFGetField(tiff, TIFFTAG_RESOLUTIONUNIT, &resUnit))
resUnit = RESUNIT_INCH;
@@ -489,7 +528,7 @@ bool QTiffHandler::read(QImage *image)
}
}
- uint32 count;
+ uint32_t count;
void *profile;
if (TIFFGetField(tiff, TIFFTAG_ICCPROFILE, &count, &profile)) {
QByteArray iccProfile(reinterpret_cast<const char *>(profile), count);
@@ -613,7 +652,7 @@ bool QTiffHandler::write(const QImage &image)
// configure image depth
const QImage::Format format = image.format();
if (format == QImage::Format_Mono || format == QImage::Format_MonoLSB) {
- uint16 photometric = PHOTOMETRIC_MINISBLACK;
+ uint16_t photometric = PHOTOMETRIC_MINISBLACK;
if (image.colorTable().at(0) == 0xffffffff)
photometric = PHOTOMETRIC_MINISWHITE;
if (!TIFFSetField(tiff, TIFFTAG_PHOTOMETRIC, photometric)
@@ -635,7 +674,7 @@ bool QTiffHandler::write(const QImage &image)
int chunkStart = y;
int chunkEnd = y + chunk.height();
while (y < chunkEnd) {
- if (TIFFWriteScanline(tiff, reinterpret_cast<uint32 *>(chunk.scanLine(y - chunkStart)), y) != 1) {
+ if (TIFFWriteScanline(tiff, reinterpret_cast<uint32_t *>(chunk.scanLine(y - chunkStart)), y) != 1) {
TIFFClose(tiff);
return false;
}
@@ -650,7 +689,7 @@ bool QTiffHandler::write(const QImage &image)
QVector<QRgb> colorTable = effectiveColorTable(image);
bool isGrayscale = checkGrayscale(colorTable);
if (isGrayscale) {
- uint16 photometric = PHOTOMETRIC_MINISBLACK;
+ uint16_t photometric = PHOTOMETRIC_MINISBLACK;
if (colorTable.at(0) == 0xffffffff)
photometric = PHOTOMETRIC_MINISWHITE;
if (!TIFFSetField(tiff, TIFFTAG_PHOTOMETRIC, photometric)
@@ -672,9 +711,9 @@ bool QTiffHandler::write(const QImage &image)
// allocate the color tables
const int tableSize = colorTable.size();
Q_ASSERT(tableSize <= 256);
- QVarLengthArray<uint16> redTable(tableSize);
- QVarLengthArray<uint16> greenTable(tableSize);
- QVarLengthArray<uint16> blueTable(tableSize);
+ QVarLengthArray<uint16_t> redTable(tableSize);
+ QVarLengthArray<uint16_t> greenTable(tableSize);
+ QVarLengthArray<uint16_t> blueTable(tableSize);
// set the color table
for (int i = 0; i<tableSize; ++i) {
@@ -727,7 +766,7 @@ bool QTiffHandler::write(const QImage &image)
} else if (format == QImage::Format_RGBA64
|| format == QImage::Format_RGBA64_Premultiplied) {
const bool premultiplied = image.format() != QImage::Format_RGBA64;
- const uint16 extrasamples = premultiplied ? EXTRASAMPLE_ASSOCALPHA : EXTRASAMPLE_UNASSALPHA;
+ const uint16_t extrasamples = premultiplied ? EXTRASAMPLE_ASSOCALPHA : EXTRASAMPLE_UNASSALPHA;
if (!TIFFSetField(tiff, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB)
|| !TIFFSetField(tiff, TIFFTAG_COMPRESSION, compression == NoCompression ? COMPRESSION_NONE : COMPRESSION_LZW)
|| !TIFFSetField(tiff, TIFFTAG_SAMPLESPERPIXEL, 4)
@@ -775,7 +814,7 @@ bool QTiffHandler::write(const QImage &image)
} else {
const bool premultiplied = image.format() != QImage::Format_ARGB32
&& image.format() != QImage::Format_RGBA8888;
- const uint16 extrasamples = premultiplied ? EXTRASAMPLE_ASSOCALPHA : EXTRASAMPLE_UNASSALPHA;
+ const uint16_t extrasamples = premultiplied ? EXTRASAMPLE_ASSOCALPHA : EXTRASAMPLE_UNASSALPHA;
if (!TIFFSetField(tiff, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB)
|| !TIFFSetField(tiff, TIFFTAG_COMPRESSION, compression == NoCompression ? COMPRESSION_NONE : COMPRESSION_LZW)
|| !TIFFSetField(tiff, TIFFTAG_SAMPLESPERPIXEL, 4)
@@ -811,13 +850,6 @@ bool QTiffHandler::write(const QImage &image)
return true;
}
-#if QT_DEPRECATED_SINCE(5, 13)
-QByteArray QTiffHandler::name() const
-{
- return "tiff";
-}
-#endif
-
QVariant QTiffHandler::option(ImageOption option) const
{
if (option == Size && canRead()) {
@@ -896,9 +928,9 @@ int QTiffHandler::currentImageNumber() const
void QTiffHandler::convert32BitOrder(void *buffer, int width)
{
- uint32 *target = reinterpret_cast<uint32 *>(buffer);
- for (int32 x=0; x<width; ++x) {
- uint32 p = target[x];
+ uint32_t *target = reinterpret_cast<uint32_t *>(buffer);
+ for (int32_t x=0; x<width; ++x) {
+ uint32_t p = target[x];
// convert between ARGB and ABGR
target[x] = (p & 0xff000000)
| ((p & 0x00ff0000) >> 16)
@@ -915,7 +947,7 @@ void QTiffHandler::rgb48fixup(QImage *image)
uchar *scanline = image->bits();
const qsizetype bpl = image->bytesPerLine();
for (int y = 0; y < h; ++y) {
- quint16 *dst = reinterpret_cast<uint16 *>(scanline);
+ quint16 *dst = reinterpret_cast<uint16_t *>(scanline);
for (int x = w - 1; x >= 0; --x) {
dst[x * 4 + 3] = 0xffff;
dst[x * 4 + 2] = dst[x * 3 + 2];