summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.qmake.conf2
-rw-r--r--LGPL_EXCEPTION.txt22
-rw-r--r--src/plugins/imageformats/tiff/qtiffhandler.cpp115
-rw-r--r--src/plugins/imageformats/tiff/qtiffhandler_p.h1
-rw-r--r--src/plugins/imageformats/webp/qwebphandler.cpp38
-rw-r--r--tests/auto/tiff/tst_qtiff.cpp17
-rw-r--r--tests/auto/webp/images/kollada_noalpha.webpbin0 -> 3570 bytes
-rw-r--r--tests/auto/webp/tst_qwebp.cpp23
-rw-r--r--tests/auto/webp/webp.qrc1
-rw-r--r--tests/shared/images/tiff.qrc1
-rw-r--r--tests/shared/images/tiff/16bpc.tiffbin0 -> 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
new file mode 100644
index 0000000..b5abe5d
--- /dev/null
+++ b/tests/auto/webp/images/kollada_noalpha.webp
Binary files differ
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
new file mode 100644
index 0000000..b1ecf26
--- /dev/null
+++ b/tests/shared/images/tiff/16bpc.tiff
Binary files differ