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.cpp482
1 files changed, 343 insertions, 139 deletions
diff --git a/src/plugins/imageformats/tiff/qtiffhandler.cpp b/src/plugins/imageformats/tiff/qtiffhandler.cpp
index b6c50b8..280dba2 100644
--- a/src/plugins/imageformats/tiff/qtiffhandler.cpp
+++ b/src/plugins/imageformats/tiff/qtiffhandler.cpp
@@ -1,48 +1,18 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the plugins of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qtiffhandler_p.h"
-#include <qvariant.h>
+
#include <qcolorspace.h>
#include <qdebug.h>
+#include <qfloat16.h>
#include <qimage.h>
-#include <qglobal.h>
+#include <qloggingcategory.h>
+#include <qvariant.h>
+#include <qvarlengtharray.h>
+#include <qbuffer.h>
+#include <qfiledevice.h>
+
extern "C" {
#include "tiffio.h"
}
@@ -51,6 +21,8 @@ extern "C" {
QT_BEGIN_NAMESPACE
+Q_STATIC_LOGGING_CATEGORY(lcTiff, "qt.imageformats.tiff")
+
tsize_t qtiffReadProc(thandle_t fd, tdata_t buf, tsize_t size)
{
QIODevice *device = static_cast<QIODevice *>(fd);
@@ -90,13 +62,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));
}
@@ -110,14 +102,22 @@ public:
bool openForRead(QIODevice *device);
bool readHeaders(QIODevice *device);
void close();
+ TIFF *openInternal(const char *mode, QIODevice *device);
+#if TIFFLIB_VERSION >= 20221213
+ static int tiffErrorHandler(TIFF *tif, void *user_data, const char *,
+ const char *fmt, va_list ap);
+ static int tiffWarningHandler(TIFF *tif, void *user_data, const char *,
+ const char *fmt, va_list ap);
+#endif
TIFF *tiff;
int compression;
QImageIOHandler::Transformations transformation;
QImage::Format format;
QSize size;
- uint16 photometric;
+ uint16_t photometric;
bool grayscale;
+ bool floatingPoint;
bool headersRead;
int currentDirectory;
int directoryCount;
@@ -143,7 +143,7 @@ static QImageIOHandler::Transformations exif2Qt(int exifOrientation)
case 8: // rotate 270 CW
return QImageIOHandler::TransformationRotate270;
}
- qWarning("Invalid EXIF orientation");
+ qCWarning(lcTiff, "Invalid EXIF orientation");
return QImageIOHandler::TransformationNone;
}
@@ -167,7 +167,7 @@ static int qt2Exif(QImageIOHandler::Transformations transformation)
case QImageIOHandler::TransformationRotate270:
return 8;
}
- qWarning("Invalid Qt image transformation");
+ qCWarning(lcTiff, "Invalid Qt image transformation");
return 1;
}
@@ -196,10 +196,67 @@ void QTiffHandlerPrivate::close()
tiff = 0;
}
+TIFF *QTiffHandlerPrivate::openInternal(const char *mode, QIODevice *device)
+{
+// TIFFLIB_VERSION 20221213 -> 4.5.0
+#if TIFFLIB_VERSION >= 20221213
+ TIFFOpenOptions *opts = TIFFOpenOptionsAlloc();
+ TIFFOpenOptionsSetErrorHandlerExtR(opts, &tiffErrorHandler, this);
+ TIFFOpenOptionsSetWarningHandlerExtR(opts, &tiffWarningHandler, this);
+ auto handle = TIFFClientOpenExt("foo",
+ mode,
+ device,
+ qtiffReadProc,
+ qtiffWriteProc,
+ qtiffSeekProc,
+ qtiffCloseProc,
+ qtiffSizeProc,
+ qtiffMapProc,
+ qtiffUnmapProc,
+ opts);
+ TIFFOpenOptionsFree(opts);
+#else
+ auto handle = TIFFClientOpen("foo",
+ mode,
+ device,
+ qtiffReadProc,
+ qtiffWriteProc,
+ qtiffSeekProc,
+ qtiffCloseProc,
+ qtiffSizeProc,
+ qtiffMapProc,
+ qtiffUnmapProc);
+#endif
+ return handle;
+}
+
+
+#if TIFFLIB_VERSION >= 20221213
+int QTiffHandlerPrivate::tiffErrorHandler(TIFF *tif, void *user_data, const char *,
+ const char *fmt, va_list ap)
+{
+ const auto priv = static_cast<QTiffHandlerPrivate *>(user_data);
+ if (!priv || priv->tiff != tif)
+ return 0;
+ qCCritical(lcTiff) << QString::vasprintf(fmt, ap);
+ return 1;
+}
+
+int QTiffHandlerPrivate::tiffWarningHandler(TIFF *tif, void *user_data, const char *,
+ const char *fmt, va_list ap)
+{
+ const auto priv = static_cast<QTiffHandlerPrivate *>(user_data);
+ if (!priv || priv->tiff != tif)
+ return 0;
+ qCWarning(lcTiff) << QString::vasprintf(fmt, ap);
+ return 1;
+}
+#endif
+
bool QTiffHandlerPrivate::canRead(QIODevice *device)
{
if (!device) {
- qWarning("QTiffHandler::canRead() called with no device");
+ qCWarning(lcTiff, "QTiffHandler::canRead() called with no device");
return false;
}
@@ -223,21 +280,8 @@ bool QTiffHandlerPrivate::openForRead(QIODevice *device)
if (!canRead(device))
return false;
- tiff = TIFFClientOpen("foo",
- "r",
- device,
- qtiffReadProc,
- qtiffWriteProc,
- qtiffSeekProc,
- qtiffCloseProc,
- qtiffSizeProc,
- qtiffMapProc,
- qtiffUnmapProc);
-
- if (!tiff) {
- return false;
- }
- return true;
+ tiff = openInternal("rh", device);
+ return tiff != nullptr;
}
bool QTiffHandlerPrivate::readHeaders(QIODevice *device)
@@ -248,10 +292,13 @@ bool QTiffHandlerPrivate::readHeaders(QIODevice *device)
if (!openForRead(device))
return false;
- TIFFSetDirectory(tiff, currentDirectory);
+ if (!TIFFSetDirectory(tiff, currentDirectory)) {
+ close();
+ return false;
+ }
- 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)) {
@@ -260,17 +307,21 @@ 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;
+ uint16_t sampleFormat;
+ if (!TIFFGetField(tiff, TIFFTAG_SAMPLEFORMAT, &sampleFormat))
+ sampleFormat = SAMPLEFORMAT_VOID;
+ floatingPoint = (sampleFormat == SAMPLEFORMAT_IEEEFP);
grayscale = photometric == PHOTOMETRIC_MINISBLACK || photometric == PHOTOMETRIC_MINISWHITE;
@@ -278,18 +329,20 @@ bool QTiffHandlerPrivate::readHeaders(QIODevice *device)
format = QImage::Format_Mono;
else if (photometric == PHOTOMETRIC_MINISBLACK && bitPerSample == 8 && samplesPerPixel == 1)
format = QImage::Format_Grayscale8;
- else if (photometric == PHOTOMETRIC_MINISBLACK && bitPerSample == 16 && samplesPerPixel == 1)
+ else if (photometric == PHOTOMETRIC_MINISBLACK && bitPerSample == 16 && samplesPerPixel == 1 && !floatingPoint)
format = QImage::Format_Grayscale16;
else if ((grayscale || photometric == PHOTOMETRIC_PALETTE) && bitPerSample == 8 && samplesPerPixel == 1)
format = QImage::Format_Indexed8;
else if (samplesPerPixel < 4)
- if (bitPerSample == 16 && photometric == PHOTOMETRIC_RGB)
- format = QImage::Format_RGBX64;
+ if (bitPerSample == 16 && (photometric == PHOTOMETRIC_RGB || photometric == PHOTOMETRIC_MINISBLACK))
+ format = floatingPoint ? QImage::Format_RGBX16FPx4 : QImage::Format_RGBX64;
+ else if (bitPerSample == 32 && floatingPoint && (photometric == PHOTOMETRIC_RGB || photometric == PHOTOMETRIC_MINISBLACK))
+ format = QImage::Format_RGBX32FPx4;
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
@@ -304,9 +357,25 @@ bool QTiffHandlerPrivate::readHeaders(QIODevice *device)
if (gotField && count && extrasamples[0] == EXTRASAMPLE_UNASSALPHA)
premultiplied = false;
if (premultiplied)
- format = QImage::Format_RGBA64_Premultiplied;
+ format = floatingPoint ? QImage::Format_RGBA16FPx4_Premultiplied : QImage::Format_RGBA64_Premultiplied;
else
- format = QImage::Format_RGBA64;
+ format = floatingPoint ? QImage::Format_RGBA16FPx4 : QImage::Format_RGBA64;
+ } else if (bitPerSample == 32 && floatingPoint && photometric == PHOTOMETRIC_RGB) {
+ if (gotField && count && extrasamples[0] == EXTRASAMPLE_UNASSALPHA)
+ premultiplied = false;
+ if (premultiplied)
+ format = QImage::Format_RGBA32FPx4_Premultiplied;
+ else
+ format = QImage::Format_RGBA32FPx4;
+ } else if (samplesPerPixel == 4 && bitPerSample == 8 && photometric == PHOTOMETRIC_SEPARATED) {
+ uint16_t inkSet;
+ const bool gotInkSetField = TIFFGetField(tiff, TIFFTAG_INKSET, &inkSet);
+ if (!gotInkSetField || inkSet == INKSET_CMYK) {
+ format = QImage::Format_CMYK8888;
+ } else {
+ close();
+ return false;
+ }
} else {
if (premultiplied)
format = QImage::Format_ARGB32_Premultiplied;
@@ -349,18 +418,14 @@ bool QTiffHandler::read(QImage *image)
QImage::Format format = d->format;
- if (image->size() == d->size && image->format() != format)
- image->reinterpretAsFormat(format);
-
- if (image->size() != d->size || image->format() != format)
- *image = QImage(d->size, format);
-
- if (image->isNull()) {
+ if (!QImageIOHandler::allocateImage(d->size, format, image)) {
d->close();
return false;
}
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();
@@ -377,7 +442,7 @@ bool QTiffHandler::read(QImage *image)
}
image->setColorTable(colortable);
} else if (format == QImage::Format_Indexed8) {
- const uint16 tableSize = 256;
+ const uint16_t tableSize = 256;
QList<QRgb> qtColorTable(tableSize);
if (d->grayscale) {
for (int i = 0; i<tableSize; ++i) {
@@ -386,9 +451,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;
@@ -412,25 +477,38 @@ bool QTiffHandler::read(QImage *image)
}
bool format8bit = (format == QImage::Format_Mono || format == QImage::Format_Indexed8 || format == QImage::Format_Grayscale8);
bool format16bit = (format == QImage::Format_Grayscale16);
+ bool formatCmyk32bit = (format == QImage::Format_CMYK8888);
bool format64bit = (format == QImage::Format_RGBX64 || format == QImage::Format_RGBA64 || format == QImage::Format_RGBA64_Premultiplied);
+ bool format64fp = (format == QImage::Format_RGBX16FPx4 || format == QImage::Format_RGBA16FPx4 || format == QImage::Format_RGBA16FPx4_Premultiplied);
+ bool format128fp = (format == QImage::Format_RGBX32FPx4 || format == QImage::Format_RGBA32FPx4 || format == QImage::Format_RGBA32FPx4_Premultiplied);
// Formats we read directly, instead of over RGBA32:
- if (format8bit || format16bit || format64bit) {
+ if (format8bit || format16bit || formatCmyk32bit || format64bit || format64fp || format128fp) {
int bytesPerPixel = image->depth() / 8;
- if (format == QImage::Format_RGBX64)
- bytesPerPixel = 6;
+ if (format == QImage::Format_RGBX64 || format == QImage::Format_RGBX16FPx4)
+ bytesPerPixel = d->photometric == PHOTOMETRIC_RGB ? 6 : 2;
+ else if (format == QImage::Format_RGBX32FPx4)
+ bytesPerPixel = d->photometric == PHOTOMETRIC_RGB ? 12 : 4;
if (TIFFIsTiled(tiff)) {
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);
+ if (byteTileSize > image->sizeInBytes() || byteTileSize / tileLength < byteTileWidth) {
+ d->close();
+ return false;
+ }
+ uchar *buf = (uchar *)_TIFFmalloc(byteTileSize);
+ if (!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) {
@@ -448,19 +526,32 @@ 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;
}
}
}
- if (format == QImage::Format_RGBX64)
- rgb48fixup(image);
+ if (format == QImage::Format_RGBX64 || format == QImage::Format_RGBX16FPx4) {
+ if (d->photometric == PHOTOMETRIC_RGB)
+ rgb48fixup(image, d->floatingPoint);
+ else
+ rgbFixup(image);
+ } else if (format == QImage::Format_RGBX32FPx4) {
+ if (d->photometric == PHOTOMETRIC_RGB)
+ rgb96fixup(image);
+ else
+ rgbFixup(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();
@@ -471,7 +562,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;
@@ -494,7 +585,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);
@@ -558,16 +649,7 @@ bool QTiffHandler::write(const QImage &image)
if (!device()->isWritable())
return false;
- TIFF *const tiff = TIFFClientOpen("foo",
- "wB",
- device(),
- qtiffReadProc,
- qtiffWriteProc,
- qtiffSeekProc,
- qtiffCloseProc,
- qtiffSizeProc,
- qtiffMapProc,
- qtiffUnmapProc);
+ TIFF *const tiff = d->openInternal("wB", device());
if (!tiff)
return false;
@@ -608,8 +690,8 @@ bool QTiffHandler::write(const QImage &image)
return false;
}
// set color space
- if (image.colorSpace().isValid()) {
- QByteArray iccProfile = image.colorSpace().iccProfile();
+ const QByteArray iccProfile = image.colorSpace().iccProfile();
+ if (!iccProfile.isEmpty()) {
if (!TIFFSetField(tiff, TIFFTAG_ICCPROFILE, iccProfile.size(), reinterpret_cast<const void *>(iccProfile.constData()))) {
TIFFClose(tiff);
return false;
@@ -618,7 +700,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)
@@ -640,7 +722,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;
}
@@ -655,12 +737,13 @@ bool QTiffHandler::write(const QImage &image)
QList<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)
|| !TIFFSetField(tiff, TIFFTAG_COMPRESSION, compression == NoCompression ? COMPRESSION_NONE : COMPRESSION_LZW)
|| !TIFFSetField(tiff, TIFFTAG_BITSPERSAMPLE, image.depth())
+ || !TIFFSetField(tiff, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_UINT)
|| !TIFFSetField(tiff, TIFFTAG_ROWSPERSTRIP, defaultStripSize(tiff))) {
TIFFClose(tiff);
return false;
@@ -677,9 +760,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) {
@@ -705,11 +788,15 @@ bool QTiffHandler::write(const QImage &image)
}
}
TIFFClose(tiff);
- } else if (format == QImage::Format_RGBX64) {
+ } else if (format == QImage::Format_RGBX64 || format == QImage::Format_RGBX16FPx4) {
if (!TIFFSetField(tiff, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB)
|| !TIFFSetField(tiff, TIFFTAG_COMPRESSION, compression == NoCompression ? COMPRESSION_NONE : COMPRESSION_LZW)
|| !TIFFSetField(tiff, TIFFTAG_SAMPLESPERPIXEL, 3)
|| !TIFFSetField(tiff, TIFFTAG_BITSPERSAMPLE, 16)
+ || !TIFFSetField(tiff, TIFFTAG_SAMPLEFORMAT,
+ format == QImage::Format_RGBX64
+ ? SAMPLEFORMAT_UINT
+ : SAMPLEFORMAT_IEEEFP)
|| !TIFFSetField(tiff, TIFFTAG_ROWSPERSTRIP, TIFFDefaultStripSize(tiff, 0))) {
TIFFClose(tiff);
return false;
@@ -732,11 +819,59 @@ 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)
|| !TIFFSetField(tiff, TIFFTAG_BITSPERSAMPLE, 16)
+ || !TIFFSetField(tiff, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_UINT)
+ || !TIFFSetField(tiff, TIFFTAG_EXTRASAMPLES, 1, &extrasamples)
+ || !TIFFSetField(tiff, TIFFTAG_ROWSPERSTRIP, TIFFDefaultStripSize(tiff, 0))) {
+ TIFFClose(tiff);
+ return false;
+ }
+ for (int y = 0; y < height; ++y) {
+ if (TIFFWriteScanline(tiff, (void*)image.scanLine(y), y) != 1) {
+ TIFFClose(tiff);
+ return false;
+ }
+ }
+ TIFFClose(tiff);
+ } else if (format == QImage::Format_RGBX32FPx4) {
+ if (!TIFFSetField(tiff, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB)
+ || !TIFFSetField(tiff, TIFFTAG_COMPRESSION, compression == NoCompression ? COMPRESSION_NONE : COMPRESSION_LZW)
+ || !TIFFSetField(tiff, TIFFTAG_SAMPLESPERPIXEL, 3)
+ || !TIFFSetField(tiff, TIFFTAG_BITSPERSAMPLE, 32)
+ || !TIFFSetField(tiff, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_IEEEFP)
+ || !TIFFSetField(tiff, TIFFTAG_ROWSPERSTRIP, TIFFDefaultStripSize(tiff, 0))) {
+ TIFFClose(tiff);
+ return false;
+ }
+ std::unique_ptr<float[]> line(new float[width * 3]);
+ for (int y = 0; y < height; ++y) {
+ const float *srcLine = reinterpret_cast<const float *>(image.constScanLine(y));
+ for (int x = 0; x < width; ++x) {
+ line[x * 3 + 0] = srcLine[x * 4 + 0];
+ line[x * 3 + 1] = srcLine[x * 4 + 1];
+ line[x * 3 + 2] = srcLine[x * 4 + 2];
+ }
+
+ if (TIFFWriteScanline(tiff, (void*)line.get(), y) != 1) {
+ TIFFClose(tiff);
+ return false;
+ }
+ }
+ TIFFClose(tiff);
+ } else if (format == QImage::Format_RGBA16FPx4 || format == QImage::Format_RGBA32FPx4
+ || format == QImage::Format_RGBA16FPx4_Premultiplied
+ || format == QImage::Format_RGBA32FPx4_Premultiplied) {
+ const bool premultiplied = image.format() != QImage::Format_RGBA16FPx4 && image.format() != QImage::Format_RGBA32FPx4;
+ 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)
+ || !TIFFSetField(tiff, TIFFTAG_BITSPERSAMPLE, image.depth() == 64 ? 16 : 32)
+ || !TIFFSetField(tiff, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_IEEEFP)
|| !TIFFSetField(tiff, TIFFTAG_EXTRASAMPLES, 1, &extrasamples)
|| !TIFFSetField(tiff, TIFFTAG_ROWSPERSTRIP, TIFFDefaultStripSize(tiff, 0))) {
TIFFClose(tiff);
@@ -749,6 +884,25 @@ bool QTiffHandler::write(const QImage &image)
}
}
TIFFClose(tiff);
+ } else if (format == QImage::Format_CMYK8888) {
+ if (!TIFFSetField(tiff, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_SEPARATED)
+ || !TIFFSetField(tiff, TIFFTAG_COMPRESSION, compression == NoCompression ? COMPRESSION_NONE : COMPRESSION_LZW)
+ || !TIFFSetField(tiff, TIFFTAG_SAMPLESPERPIXEL, 4)
+ || !TIFFSetField(tiff, TIFFTAG_BITSPERSAMPLE, 8)
+ || !TIFFSetField(tiff, TIFFTAG_INKSET, INKSET_CMYK)
+ || !TIFFSetField(tiff, TIFFTAG_ROWSPERSTRIP, defaultStripSize(tiff))) {
+ TIFFClose(tiff);
+ return false;
+ }
+
+ for (int y = 0; y < image.height(); ++y) {
+ if (TIFFWriteScanline(tiff, (void*)image.scanLine(y), y) != 1) {
+ TIFFClose(tiff);
+ return false;
+ }
+ }
+
+ TIFFClose(tiff);
} else if (!image.hasAlphaChannel()) {
if (!TIFFSetField(tiff, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB)
|| !TIFFSetField(tiff, TIFFTAG_COMPRESSION, compression == NoCompression ? COMPRESSION_NONE : COMPRESSION_LZW)
@@ -780,7 +934,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)
@@ -835,7 +989,7 @@ QVariant QTiffHandler::option(ImageOption option) const
void QTiffHandler::setOption(ImageOption option, const QVariant &value)
{
- if (option == CompressionRatio && value.type() == QVariant::Int)
+ if (option == CompressionRatio && value.metaType().id() == QMetaType::Int)
d->compression = qBound(0, value.toInt(), 1);
if (option == ImageTransformation) {
int transformation = value.toInt();
@@ -893,9 +1047,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)
@@ -904,17 +1058,21 @@ void QTiffHandler::convert32BitOrder(void *buffer, int width)
}
}
-void QTiffHandler::rgb48fixup(QImage *image)
+void QTiffHandler::rgb48fixup(QImage *image, bool floatingPoint)
{
Q_ASSERT(image->depth() == 64);
const int h = image->height();
const int w = image->width();
uchar *scanline = image->bits();
const qsizetype bpl = image->bytesPerLine();
+ quint16 mask = 0xffff;
+ const qfloat16 fp_mask = qfloat16(1.0f);
+ if (floatingPoint)
+ memcpy(&mask, &fp_mask, 2);
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 + 3] = mask;
dst[x * 4 + 2] = dst[x * 3 + 2];
dst[x * 4 + 1] = dst[x * 3 + 1];
dst[x * 4 + 0] = dst[x * 3 + 0];
@@ -923,29 +1081,75 @@ void QTiffHandler::rgb48fixup(QImage *image)
}
}
+void QTiffHandler::rgb96fixup(QImage *image)
+{
+ Q_ASSERT(image->depth() == 128);
+ const int h = image->height();
+ const int w = image->width();
+ uchar *scanline = image->bits();
+ const qsizetype bpl = image->bytesPerLine();
+ for (int y = 0; y < h; ++y) {
+ float *dst = reinterpret_cast<float *>(scanline);
+ for (int x = w - 1; x >= 0; --x) {
+ dst[x * 4 + 3] = 1.0f;
+ dst[x * 4 + 2] = dst[x * 3 + 2];
+ dst[x * 4 + 1] = dst[x * 3 + 1];
+ dst[x * 4 + 0] = dst[x * 3 + 0];
+ }
+ scanline += bpl;
+ }
+}
+
+void QTiffHandler::rgbFixup(QImage *image)
+{
+ Q_ASSERT(d->floatingPoint);
+ if (image->depth() == 64) {
+ const int h = image->height();
+ const int w = image->width();
+ uchar *scanline = image->bits();
+ const qsizetype bpl = image->bytesPerLine();
+ for (int y = 0; y < h; ++y) {
+ qfloat16 *dst = reinterpret_cast<qfloat16 *>(scanline);
+ for (int x = w - 1; x >= 0; --x) {
+ dst[x * 4 + 3] = qfloat16(1.0f);
+ dst[x * 4 + 2] = dst[x];
+ dst[x * 4 + 1] = dst[x];
+ dst[x * 4 + 0] = dst[x];
+ }
+ scanline += bpl;
+ }
+ } else {
+ const int h = image->height();
+ const int w = image->width();
+ uchar *scanline = image->bits();
+ const qsizetype bpl = image->bytesPerLine();
+ for (int y = 0; y < h; ++y) {
+ float *dst = reinterpret_cast<float *>(scanline);
+ for (int x = w - 1; x >= 0; --x) {
+ dst[x * 4 + 3] = 1.0f;
+ dst[x * 4 + 2] = dst[x];
+ dst[x * 4 + 1] = dst[x];
+ dst[x * 4 + 0] = dst[x];
+ }
+ scanline += bpl;
+ }
+ }
+}
+
bool QTiffHandler::ensureHaveDirectoryCount() const
{
if (d->directoryCount > 0)
return true;
- TIFF *tiff = TIFFClientOpen("foo",
- "r",
- device(),
- qtiffReadProc,
- qtiffWriteProc,
- qtiffSeekProc,
- qtiffCloseProc,
- qtiffSizeProc,
- qtiffMapProc,
- qtiffUnmapProc);
+ TIFF *tiff = d->openInternal("rh", device());
+
if (!tiff) {
device()->reset();
return false;
}
- do {
- ++d->directoryCount;
- } while (TIFFReadDirectory(tiff));
+ while (TIFFReadDirectory(tiff))
+ ++d->directoryCount;
TIFFClose(tiff);
device()->reset();
return true;