diff options
-rw-r--r-- | .qmake.conf | 2 | ||||
-rw-r--r-- | LGPL_EXCEPTION.txt | 22 | ||||
-rw-r--r-- | src/plugins/imageformats/tiff/qtiffhandler.cpp | 115 | ||||
-rw-r--r-- | src/plugins/imageformats/tiff/qtiffhandler_p.h | 1 | ||||
-rw-r--r-- | src/plugins/imageformats/webp/qwebphandler.cpp | 38 | ||||
-rw-r--r-- | tests/auto/tiff/tst_qtiff.cpp | 17 | ||||
-rw-r--r-- | tests/auto/webp/images/kollada_noalpha.webp | bin | 0 -> 3570 bytes | |||
-rw-r--r-- | tests/auto/webp/tst_qwebp.cpp | 23 | ||||
-rw-r--r-- | tests/auto/webp/webp.qrc | 1 | ||||
-rw-r--r-- | tests/shared/images/tiff.qrc | 1 | ||||
-rw-r--r-- | tests/shared/images/tiff/16bpc.tiff | bin | 0 -> 21414 bytes |
11 files changed, 164 insertions, 56 deletions
diff --git a/.qmake.conf b/.qmake.conf index 76c202a..097d8b9 100644 --- a/.qmake.conf +++ b/.qmake.conf @@ -1,3 +1,3 @@ load(qt_build_config) -MODULE_VERSION = 5.11.2 +MODULE_VERSION = 5.12.0 diff --git a/LGPL_EXCEPTION.txt b/LGPL_EXCEPTION.txt deleted file mode 100644 index 5cdacb9..0000000 --- a/LGPL_EXCEPTION.txt +++ /dev/null @@ -1,22 +0,0 @@ -The Qt Company Qt LGPL Exception version 1.1 - -As an additional permission to the GNU Lesser General Public License version -2.1, the object code form of a "work that uses the Library" may incorporate -material from a header file that is part of the Library. You may distribute -such object code under terms of your choice, provided that: - (i) the header files of the Library have not been modified; and - (ii) the incorporated material is limited to numerical parameters, data - structure layouts, accessors, macros, inline functions and - templates; and - (iii) you comply with the terms of Section 6 of the GNU Lesser General - Public License version 2.1. - -Moreover, you may apply this exception to a modified version of the Library, -provided that such modification does not involve copying material from the -Library into the modified Library's header files unless such material is -limited to (i) numerical parameters; (ii) data structure layouts; -(iii) accessors; and (iv) small macros, templates and inline functions of -five lines or less in length. - -Furthermore, you are not required to apply this additional permission to a -modified version of the Library. diff --git a/src/plugins/imageformats/tiff/qtiffhandler.cpp b/src/plugins/imageformats/tiff/qtiffhandler.cpp index 84221d6..1232b50 100644 --- a/src/plugins/imageformats/tiff/qtiffhandler.cpp +++ b/src/plugins/imageformats/tiff/qtiffhandler.cpp @@ -46,6 +46,8 @@ extern "C" { #include "tiffio.h" } +#include <memory> + QT_BEGIN_NAMESPACE tsize_t qtiffReadProc(thandle_t fd, tdata_t buf, tsize_t size) @@ -273,7 +275,10 @@ bool QTiffHandlerPrivate::readHeaders(QIODevice *device) else if ((grayscale || photometric == PHOTOMETRIC_PALETTE) && bitPerSample == 8 && samplesPerPixel == 1) format = QImage::Format_Indexed8; else if (samplesPerPixel < 4) - format = QImage::Format_RGB32; + if (bitPerSample > 8 && photometric == PHOTOMETRIC_RGB) + format = QImage::Format_RGBX64; + else + format = QImage::Format_RGB32; else { uint16 count; uint16 *extrasamples; @@ -281,11 +286,25 @@ bool QTiffHandlerPrivate::readHeaders(QIODevice *device) // 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 // versions used to save it. + bool premultiplied = true; bool gotField = TIFFGetField(tiff, TIFFTAG_EXTRASAMPLES, &count, &extrasamples); if (!gotField || !count || extrasamples[0] == EXTRASAMPLE_UNSPECIFIED) - format = QImage::Format_ARGB32; - else - format = QImage::Format_ARGB32_Premultiplied; + premultiplied = false; + + if (bitPerSample > 8 && photometric == PHOTOMETRIC_RGB) { + // We read 64-bit raw, so unassoc remains unpremultiplied. + if (gotField && count && extrasamples[0] == EXTRASAMPLE_UNASSALPHA) + premultiplied = false; + if (premultiplied) + format = QImage::Format_RGBA64_Premultiplied; + else + format = QImage::Format_RGBA64; + } else { + if (premultiplied) + format = QImage::Format_ARGB32_Premultiplied; + else + format = QImage::Format_ARGB32; + } } headersRead = true; @@ -321,10 +340,9 @@ bool QTiffHandler::read(QImage *image) return false; QImage::Format format = d->format; - if (format == QImage::Format_RGB32 && - (image->format() == QImage::Format_ARGB32 || - image->format() == QImage::Format_ARGB32_Premultiplied)) - format = image->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); @@ -338,7 +356,8 @@ bool QTiffHandler::read(QImage *image) const quint32 width = d->size.width(); const quint32 height = d->size.height(); - if (format == QImage::Format_Mono || format == QImage::Format_Indexed8 || format == QImage::Format_Grayscale8) { + // Setup color tables + if (format == QImage::Format_Mono || format == QImage::Format_Indexed8) { if (format == QImage::Format_Mono) { QVector<QRgb> colortable(2); if (d->photometric == PHOTOMETRIC_MINISBLACK) { @@ -381,7 +400,14 @@ bool QTiffHandler::read(QImage *image) image->setColorTable(qtColorTable); // free redTable, greenTable and greenTable done by libtiff } + } + bool format8bit = (format == QImage::Format_Mono || format == QImage::Format_Indexed8 || format == QImage::Format_Grayscale8); + bool format64bit = (format == QImage::Format_RGBX64 || format == QImage::Format_RGBA64 || format == QImage::Format_RGBA64_Premultiplied); + if (format8bit || format64bit) { + int bytesPerPixel = image->depth() / 8; + if (format == QImage::Format_RGBX64) + bytesPerPixel = 6; if (TIFFIsTiled(tiff)) { quint32 tileWidth, tileLength; TIFFGetField(tiff, TIFFTAG_TILEWIDTH, &tileWidth); @@ -392,8 +418,8 @@ bool QTiffHandler::read(QImage *image) d->close(); return false; } - quint32 byteWidth = (format == QImage::Format_Mono) ? (width + 7)/8 : width; - quint32 byteTileWidth = (format == QImage::Format_Mono) ? tileWidth/8 : tileWidth; + quint32 byteWidth = (format == QImage::Format_Mono) ? (width + 7)/8 : (width * bytesPerPixel); + quint32 byteTileWidth = (format == QImage::Format_Mono) ? tileWidth/8 : (tileWidth * bytesPerPixel); 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) { @@ -402,7 +428,7 @@ bool QTiffHandler::read(QImage *image) return false; } quint32 linesToCopy = qMin(tileLength, height - y); - quint32 byteOffset = (format == QImage::Format_Mono) ? x/8 : x; + quint32 byteOffset = (format == QImage::Format_Mono) ? x/8 : (x * bytesPerPixel); quint32 widthToCopy = qMin(byteTileWidth, byteWidth - byteOffset); for (quint32 i = 0; i < linesToCopy; i++) { ::memcpy(image->scanLine(y + i) + byteOffset, buf + (i * byteTileWidth), widthToCopy); @@ -418,6 +444,8 @@ bool QTiffHandler::read(QImage *image) } } } + if (format == QImage::Format_RGBX64) + rgb48fixup(image); } else { const int stopOnError = 1; if (TIFFReadRGBAImageOriented(tiff, width, height, reinterpret_cast<uint32 *>(image->bits()), qt2Exif(d->transformation), stopOnError)) { @@ -651,6 +679,50 @@ bool QTiffHandler::write(const QImage &image) } } TIFFClose(tiff); + } else if (format == QImage::Format_RGBX64) { + 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_ROWSPERSTRIP, TIFFDefaultStripSize(tiff, 0))) { + TIFFClose(tiff); + return false; + } + std::unique_ptr<quint16[]> rgb48line(new quint16[width * 3]); + for (int y = 0; y < height; ++y) { + const quint16 *srcLine = reinterpret_cast<const quint16 *>(image.constScanLine(y)); + for (int x = 0; x < width; ++x) { + rgb48line[x * 3 + 0] = srcLine[x * 4 + 0]; + rgb48line[x * 3 + 1] = srcLine[x * 4 + 1]; + rgb48line[x * 3 + 2] = srcLine[x * 4 + 2]; + } + + if (TIFFWriteScanline(tiff, (void*)rgb48line.get(), y) != 1) { + TIFFClose(tiff); + return false; + } + } + TIFFClose(tiff); + } 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; + 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_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 (!image.hasAlphaChannel()) { if (!TIFFSetField(tiff, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB) || !TIFFSetField(tiff, TIFFTAG_COMPRESSION, compression == NoCompression ? COMPRESSION_NONE : COMPRESSION_LZW) @@ -812,6 +884,25 @@ void QTiffHandler::convert32BitOrder(void *buffer, int width) } } +void QTiffHandler::rgb48fixup(QImage *image) +{ + Q_ASSERT(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) { + quint16 *dst = reinterpret_cast<uint16 *>(scanline); + for (int x = w - 1; x >= 0; --x) { + dst[x * 4 + 3] = 0xffff; + 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; + } +} + bool QTiffHandler::ensureHaveDirectoryCount() const { if (d->directoryCount > 0) diff --git a/src/plugins/imageformats/tiff/qtiffhandler_p.h b/src/plugins/imageformats/tiff/qtiffhandler_p.h index c7b074d..2090e38 100644 --- a/src/plugins/imageformats/tiff/qtiffhandler_p.h +++ b/src/plugins/imageformats/tiff/qtiffhandler_p.h @@ -74,6 +74,7 @@ public: }; private: void convert32BitOrder(void *buffer, int width); + void rgb48fixup(QImage *image); const QScopedPointer<QTiffHandlerPrivate> d; bool ensureHaveDirectoryCount() const; }; diff --git a/src/plugins/imageformats/webp/qwebphandler.cpp b/src/plugins/imageformats/webp/qwebphandler.cpp index 3a7bf43..578a701 100644 --- a/src/plugins/imageformats/webp/qwebphandler.cpp +++ b/src/plugins/imageformats/webp/qwebphandler.cpp @@ -174,7 +174,8 @@ bool QWebpHandler::read(QImage *image) if (status != VP8_STATUS_OK) return false; - QImage frame(m_iter.width, m_iter.height, QImage::Format_ARGB32); + QImage::Format format = m_features.has_alpha ? QImage::Format_ARGB32 : QImage::Format_RGB32; + QImage frame(m_iter.width, m_iter.height, format); uint8_t *output = frame.bits(); size_t output_size = frame.sizeInBytes(); #if Q_BYTE_ORDER == Q_LITTLE_ENDIAN @@ -219,13 +220,10 @@ bool QWebpHandler::write(const QImage &image) } QImage srcImage = image; -#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN - if (srcImage.format() != QImage::Format_ARGB32) - srcImage = srcImage.convertToFormat(QImage::Format_ARGB32); -#else /* Q_BIG_ENDIAN */ - if (srcImage.format() != QImage::Format_RGBA8888) - srcImage = srcImage.convertToFormat(QImage::Format_RGBA8888); -#endif + bool alpha = srcImage.hasAlphaChannel(); + QImage::Format newFormat = alpha ? QImage::Format_RGBA8888 : QImage::Format_RGB888; + if (srcImage.format() != newFormat) + srcImage = srcImage.convertToFormat(newFormat); WebPPicture picture; WebPConfig config; @@ -238,19 +236,27 @@ bool QWebpHandler::write(const QImage &image) picture.width = srcImage.width(); picture.height = srcImage.height(); picture.use_argb = 1; -#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN - if (!WebPPictureImportBGRA(&picture, srcImage.bits(), srcImage.bytesPerLine())) { -#else /* Q_BIG_ENDIAN */ - if (!WebPPictureImportRGBA(&picture, srcImage.bits(), srcImage.bytesPerLine())) { -#endif - qWarning() << "failed to import image data to webp picture."; + bool failed = false; + if (alpha) + failed = !WebPPictureImportRGBA(&picture, srcImage.bits(), srcImage.bytesPerLine()); + else + failed = !WebPPictureImportRGB(&picture, srcImage.bits(), srcImage.bytesPerLine()); + if (failed) { + qWarning() << "failed to import image data to webp picture."; WebPPictureFree(&picture); return false; } - config.quality = m_quality < 0 ? 75 : qMin(m_quality, 100); - config.lossless = (config.quality >= 100); + int reqQuality = m_quality < 0 ? 75 : qMin(m_quality, 100); + if (reqQuality < 100) { + config.lossless = 0; + config.quality = reqQuality; + } else { + config.lossless = 1; + config.quality = 70; // For lossless, specifies compression effort; 70 is libwebp default + } + config.alpha_quality = config.quality; picture.writer = pictureWriter; picture.custom_ptr = device(); diff --git a/tests/auto/tiff/tst_qtiff.cpp b/tests/auto/tiff/tst_qtiff.cpp index 1a96ab3..9c815d5 100644 --- a/tests/auto/tiff/tst_qtiff.cpp +++ b/tests/auto/tiff/tst_qtiff.cpp @@ -84,6 +84,8 @@ private slots: void tiled_data(); void tiled(); + void readRgba64(); + private: QString prefix; }; @@ -165,6 +167,7 @@ void tst_qtiff::readImage_data() QTest::newRow("tiled_mono") << QString("tiled_mono.tiff") << QSize(64, 64); QTest::newRow("tiled_oddsize_grayscale") << QString("tiled_oddsize_grayscale.tiff") << QSize(59, 71); QTest::newRow("tiled_oddsize_mono") << QString("tiled_oddsize_mono.tiff") << QSize(59, 71); + QTest::newRow("16bpc") << QString("16bpc.tiff") << QSize(64, 46); } void tst_qtiff::readImage() @@ -384,6 +387,9 @@ void tst_qtiff::readWriteNonDestructive_data() QTest::newRow("tiff argb32pm") << QImage::Format_ARGB32_Premultiplied << QImage::Format_ARGB32_Premultiplied << QImageIOHandler::TransformationRotate90; QTest::newRow("tiff rgb32") << QImage::Format_RGB32 << QImage::Format_RGB32 << QImageIOHandler::TransformationRotate270; QTest::newRow("tiff grayscale") << QImage::Format_Grayscale8 << QImage::Format_Grayscale8 << QImageIOHandler::TransformationFlip; + QTest::newRow("tiff rgb64") << QImage::Format_RGBX64 << QImage::Format_RGBX64 << QImageIOHandler::TransformationNone; + QTest::newRow("tiff rgba64") << QImage::Format_RGBA64 << QImage::Format_RGBA64 << QImageIOHandler::TransformationRotate90; + QTest::newRow("tiff rgba64pm") << QImage::Format_RGBA64_Premultiplied << QImage::Format_RGBA64_Premultiplied << QImageIOHandler::TransformationNone; } void tst_qtiff::readWriteNonDestructive() @@ -592,5 +598,16 @@ void tst_qtiff::tiled() QCOMPARE(expectedImage, tiledImage); } +void tst_qtiff::readRgba64() +{ + QString path = prefix + QString("16bpc.tiff"); + QImageReader reader(path); + QVERIFY(reader.canRead()); + QCOMPARE(reader.imageFormat(), QImage::Format_RGBX64); + QImage image = reader.read(); + QVERIFY(!image.isNull()); + QCOMPARE(image.format(), QImage::Format_RGBX64); +} + QTEST_MAIN(tst_qtiff) #include "tst_qtiff.moc" diff --git a/tests/auto/webp/images/kollada_noalpha.webp b/tests/auto/webp/images/kollada_noalpha.webp Binary files differnew file mode 100644 index 0000000..b5abe5d --- /dev/null +++ b/tests/auto/webp/images/kollada_noalpha.webp diff --git a/tests/auto/webp/tst_qwebp.cpp b/tests/auto/webp/tst_qwebp.cpp index ad4a376..17289a5 100644 --- a/tests/auto/webp/tst_qwebp.cpp +++ b/tests/auto/webp/tst_qwebp.cpp @@ -53,15 +53,18 @@ void tst_qwebp::readImage_data() { QTest::addColumn<QString>("fileName"); QTest::addColumn<QSize>("size"); + QTest::addColumn<bool>("alpha"); - QTest::newRow("kollada") << QString("kollada") << QSize(436, 160); - QTest::newRow("kollada_lossless") << QString("kollada_lossless") << QSize(436, 160); + QTest::newRow("kollada") << QString("kollada") << QSize(436, 160) << true; + QTest::newRow("kollada_lossless") << QString("kollada_lossless") << QSize(436, 160) << true; + QTest::newRow("kollada_noalpha") << QString("kollada_noalpha") << QSize(436, 160) << false; } void tst_qwebp::readImage() { QFETCH(QString, fileName); QFETCH(QSize, size); + QFETCH(bool, alpha); const QString path = QStringLiteral(":/images/") + fileName + QStringLiteral(".webp"); QImageReader reader(path); @@ -69,6 +72,7 @@ void tst_qwebp::readImage() QImage image = reader.read(); QVERIFY2(!image.isNull(), qPrintable(reader.errorString())); QCOMPARE(image.size(), size); + QCOMPARE(image.hasAlphaChannel(), alpha); } void tst_qwebp::readAnimation_data() @@ -136,10 +140,13 @@ void tst_qwebp::writeImage_data() QTest::addColumn<QString>("postfix"); QTest::addColumn<int>("quality"); QTest::addColumn<QSize>("size"); + QTest::addColumn<bool>("alpha"); QTest::addColumn<bool>("needcheck"); - QTest::newRow("kollada-75") << QString("kollada") << QString(".png") << 75 << QSize(436, 160) << false; - QTest::newRow("kollada-100") << QString("kollada") << QString(".png") << 100 << QSize(436, 160) << true; + QTest::newRow("kollada-75") << QString("kollada") << QString(".png") << 75 << QSize(436, 160) << true << false; + QTest::newRow("kollada-100") << QString("kollada") << QString(".png") << 100 << QSize(436, 160) << true << true; + QTest::newRow("kollada_noalpha-75") << QString("kollada_noalpha") << QString(".webp") << 75 << QSize(436, 160) << false << false; + QTest::newRow("kollada_noalpha-100") << QString("kollada_noalpha") << QString(".webp") << 100 << QSize(436, 160) << false << true; } void tst_qwebp::writeImage() @@ -148,6 +155,7 @@ void tst_qwebp::writeImage() QFETCH(QString, postfix); QFETCH(int, quality); QFETCH(QSize, size); + QFETCH(bool, alpha); QFETCH(bool, needcheck); const QString path = QString("%1-%2.webp").arg(fileName).arg(quality); @@ -162,8 +170,13 @@ void tst_qwebp::writeImage() writer.setQuality(quality); QVERIFY2(writer.write(image), qPrintable(writer.errorString())); + QImage reread(path); + QVERIFY(!reread.isNull()); + QVERIFY(reread.size() == size); + QVERIFY(reread.hasAlphaChannel() == alpha); + if (needcheck) - QVERIFY(image == QImage(path)); + QVERIFY(image == reread); } QTEST_MAIN(tst_qwebp) diff --git a/tests/auto/webp/webp.qrc b/tests/auto/webp/webp.qrc index 6519e58..7d0c626 100644 --- a/tests/auto/webp/webp.qrc +++ b/tests/auto/webp/webp.qrc @@ -4,5 +4,6 @@ <file>images/kollada.webp</file> <file>images/kollada_lossless.webp</file> <file>images/kollada_animation.webp</file> + <file>images/kollada_noalpha.webp</file> </qresource> </RCC> diff --git a/tests/shared/images/tiff.qrc b/tests/shared/images/tiff.qrc index 19675ba..91bbf93 100644 --- a/tests/shared/images/tiff.qrc +++ b/tests/shared/images/tiff.qrc @@ -1,5 +1,6 @@ <RCC> <qresource prefix="/"> + <file>tiff/16bpc.tiff</file> <file>tiff/corrupt-data.tif</file> <file>tiff/grayscale-ref.tif</file> <file>tiff/grayscale.tif</file> diff --git a/tests/shared/images/tiff/16bpc.tiff b/tests/shared/images/tiff/16bpc.tiff Binary files differnew file mode 100644 index 0000000..b1ecf26 --- /dev/null +++ b/tests/shared/images/tiff/16bpc.tiff |