diff options
Diffstat (limited to 'src/plugins/imageformats/tiff/qtiffhandler.cpp')
-rw-r--r-- | src/plugins/imageformats/tiff/qtiffhandler.cpp | 124 |
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]; |