summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSimon Hausmann <simon.hausmann@qt.io>2019-09-16 22:44:11 +0200
committerSimon Hausmann <simon.hausmann@qt.io>2019-09-16 22:45:20 +0200
commit20b25042dee9e76a3d371377877960e6d3c134be (patch)
tree8ac8c50cb228a66f1ae624f1f4366f7afcc7f8de
parent637d348107148cde989f3fc995b9576a3c11ecbe (diff)
parent7aa428b89c55dd7268da50939545742bb2f1c244 (diff)
Merge "Merge remote-tracking branch 'origin/dev' into wip/qt6"
-rw-r--r--src/3rdparty/libwebp.pri25
-rw-r--r--src/imageformats/configure.json7
-rw-r--r--src/plugins/imageformats/tiff/qtiffhandler.cpp19
-rw-r--r--src/plugins/imageformats/webp/qwebphandler.cpp81
-rw-r--r--src/plugins/imageformats/webp/qwebphandler_p.h3
-rw-r--r--tests/auto/tiff/tst_qtiff.cpp39
6 files changed, 148 insertions, 26 deletions
diff --git a/src/3rdparty/libwebp.pri b/src/3rdparty/libwebp.pri
index 3e839a2..6f49549 100644
--- a/src/3rdparty/libwebp.pri
+++ b/src/3rdparty/libwebp.pri
@@ -123,18 +123,21 @@ integrity {
QMAKE_CFLAGS += -c99
}
-equals(QT_ARCH, arm)|equals(QT_ARCH, arm64) {
- SOURCES_FOR_NEON += \
- $$PWD/libwebp/src/dsp/alpha_processing_neon.c \
- $$PWD/libwebp/src/dsp/dec_neon.c \
- $$PWD/libwebp/src/dsp/enc_neon.c \
- $$PWD/libwebp/src/dsp/filters_neon.c \
- $$PWD/libwebp/src/dsp/lossless_enc_neon.c \
- $$PWD/libwebp/src/dsp/lossless_neon.c \
- $$PWD/libwebp/src/dsp/rescaler_neon.c \
- $$PWD/libwebp/src/dsp/upsampling_neon.c \
- $$PWD/libwebp/src/dsp/yuv_neon.c
+SOURCES_FOR_NEON += \
+ $$PWD/libwebp/src/dsp/alpha_processing_neon.c \
+ $$PWD/libwebp/src/dsp/dec_neon.c \
+ $$PWD/libwebp/src/dsp/enc_neon.c \
+ $$PWD/libwebp/src/dsp/filters_neon.c \
+ $$PWD/libwebp/src/dsp/lossless_enc_neon.c \
+ $$PWD/libwebp/src/dsp/lossless_neon.c \
+ $$PWD/libwebp/src/dsp/rescaler_neon.c \
+ $$PWD/libwebp/src/dsp/upsampling_neon.c \
+ $$PWD/libwebp/src/dsp/yuv_neon.c
+
+android {
+ arm64-v8a|equals(QT_ARCH, arm64): SOURCES += $$SOURCES_FOR_NEON
+} else: equals(QT_ARCH, arm)|equals(QT_ARCH, arm64) {
contains(QT_CPU_FEATURES.$$QT_ARCH, neon) {
# Default compiler settings include this feature, so just add to SOURCES
SOURCES += $$SOURCES_FOR_NEON
diff --git a/src/imageformats/configure.json b/src/imageformats/configure.json
index 03d59d3..7c6eaef 100644
--- a/src/imageformats/configure.json
+++ b/src/imageformats/configure.json
@@ -117,10 +117,11 @@
"include": [
"webp/decode.h",
"webp/encode.h",
- "webp/demux.h"
+ "webp/demux.h",
+ "webp/mux.h"
],
"qmake": [
- "LIBS += -lwebp -lwebpdemux"
+ "LIBS += -lwebp -lwebpdemux -lwebpmux"
],
"main": [
"#if WEBP_ABI_IS_INCOMPATIBLE(WEBP_DECODER_ABI_VERSION, 0x0203) || WEBP_ABI_IS_INCOMPATIBLE(WEBP_ENCODER_ABI_VERSION, 0x0202)",
@@ -144,7 +145,7 @@
}
},
"sources": [
- "-lwebp -lwebpdemux"
+ "-lwebp -lwebpdemux -lwebpmux"
]
}
},
diff --git a/src/plugins/imageformats/tiff/qtiffhandler.cpp b/src/plugins/imageformats/tiff/qtiffhandler.cpp
index 9f1c090..57def2c 100644
--- a/src/plugins/imageformats/tiff/qtiffhandler.cpp
+++ b/src/plugins/imageformats/tiff/qtiffhandler.cpp
@@ -39,6 +39,7 @@
#include "qtiffhandler_p.h"
#include <qvariant.h>
+#include <qcolorspace.h>
#include <qdebug.h>
#include <qimage.h>
#include <qglobal.h>
@@ -487,6 +488,15 @@ bool QTiffHandler::read(QImage *image)
}
}
+ uint32 count;
+ void *profile;
+ if (TIFFGetField(tiff, TIFFTAG_ICCPROFILE, &count, &profile)) {
+ QByteArray iccProfile(reinterpret_cast<const char *>(profile), count);
+ image->setColorSpace(QColorSpace::fromIccProfile(iccProfile));
+ }
+ // We do not handle colorimetric metadat not on ICC profile form, it seems to be a lot
+ // less common, and would need additional API in QColorSpace.
+
return true;
}
@@ -591,7 +601,14 @@ bool QTiffHandler::write(const QImage &image)
TIFFClose(tiff);
return false;
}
-
+ // set color space
+ if (image.colorSpace().isValid()) {
+ QByteArray iccProfile = image.colorSpace().iccProfile();
+ if (!TIFFSetField(tiff, TIFFTAG_ICCPROFILE, iccProfile.size(), reinterpret_cast<const void *>(iccProfile.constData()))) {
+ TIFFClose(tiff);
+ return false;
+ }
+ }
// configure image depth
const QImage::Format format = image.format();
if (format == QImage::Format_Mono || format == QImage::Format_MonoLSB) {
diff --git a/src/plugins/imageformats/webp/qwebphandler.cpp b/src/plugins/imageformats/webp/qwebphandler.cpp
index 4d6bcbe..3916996 100644
--- a/src/plugins/imageformats/webp/qwebphandler.cpp
+++ b/src/plugins/imageformats/webp/qwebphandler.cpp
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the WebP plugins in the Qt ImageFormats module.
@@ -38,6 +38,7 @@
****************************************************************************/
#include "qwebphandler_p.h"
+#include "webp/mux.h"
#include "webp/encode.h"
#include <qcolor.h>
#include <qimage.h>
@@ -51,6 +52,7 @@ QWebpHandler::QWebpHandler() :
m_quality(75),
m_scanState(ScanNotScanned),
m_features(),
+ m_formatFlags(0),
m_loop(0),
m_frameCount(0),
m_demuxer(NULL),
@@ -151,6 +153,7 @@ bool QWebpHandler::ensureDemuxer()
if (m_demuxer == NULL)
return false;
+ m_formatFlags = WebPDemuxGetI(m_demuxer, WEBP_FF_FORMAT_FLAGS);
return true;
}
@@ -160,6 +163,18 @@ bool QWebpHandler::read(QImage *image)
return false;
if (m_iter.frame_num == 0) {
+ // Read global meta-data chunks first
+ WebPChunkIterator metaDataIter;
+ if ((m_formatFlags & ICCP_FLAG) && WebPDemuxGetChunk(m_demuxer, "ICCP", 1, &metaDataIter)) {
+ const QByteArray iccProfile = QByteArray::fromRawData(reinterpret_cast<const char *>(metaDataIter.chunk.bytes),
+ metaDataIter.chunk.size);
+ m_colorSpace = QColorSpace::fromIccProfile(iccProfile);
+ // ### consider parsing EXIF and/or XMP metadata too.
+ WebPDemuxReleaseChunkIterator(&metaDataIter);
+ } else {
+ m_colorSpace = QColorSpace::Undefined;
+ }
+
// Go to first frame
if (!WebPDemuxGetFrame(m_demuxer, 1, &m_iter))
return false;
@@ -201,23 +216,21 @@ bool QWebpHandler::read(QImage *image)
*image = *m_composited;
}
+ image->setColorSpace(m_colorSpace);
return true;
}
-static int pictureWriter(const quint8 *data, size_t data_size, const WebPPicture *const pic)
-{
- QIODevice *io = reinterpret_cast<QIODevice*>(pic->custom_ptr);
-
- return data_size ? ((quint64)(io->write((const char*)data, data_size)) == data_size) : 1;
-}
-
bool QWebpHandler::write(const QImage &image)
{
if (image.isNull()) {
qWarning() << "source image is null.";
return false;
}
+ if (std::max(image.width(), image.height()) > WEBP_MAX_DIMENSION) {
+ qWarning() << "QWebpHandler::write() source image too large for WebP: " << image.size();
+ return false;
+ }
QImage srcImage = image;
bool alpha = srcImage.hasAlphaChannel();
@@ -257,8 +270,10 @@ bool QWebpHandler::write(const QImage &image)
config.quality = 70; // For lossless, specifies compression effort; 70 is libwebp default
}
config.alpha_quality = config.quality;
- picture.writer = pictureWriter;
- picture.custom_ptr = device();
+ WebPMemoryWriter writer;
+ WebPMemoryWriterInit(&writer);
+ picture.writer = WebPMemoryWrite;
+ picture.custom_ptr = &writer;
if (!WebPEncode(&config, &picture)) {
qWarning() << "failed to encode webp picture, error code: " << picture.error_code;
@@ -266,9 +281,53 @@ bool QWebpHandler::write(const QImage &image)
return false;
}
+ bool res = false;
+ if (image.colorSpace().isValid()) {
+ int copy_data = 0;
+ WebPMux *mux = WebPMuxNew();
+ WebPData image_data = { writer.mem, writer.size };
+ WebPMuxSetImage(mux, &image_data, copy_data);
+ uint8_t vp8xChunk[10];
+ uint8_t flags = 0x20; // Has ICCP chunk, no XMP, EXIF or animation.
+ if (image.hasAlphaChannel())
+ flags |= 0x10;
+ vp8xChunk[0] = flags;
+ vp8xChunk[1] = 0;
+ vp8xChunk[2] = 0;
+ vp8xChunk[3] = 0;
+ const unsigned width = image.width() - 1;
+ const unsigned height = image.height() - 1;
+ vp8xChunk[4] = width & 0xff;
+ vp8xChunk[5] = (width >> 8) & 0xff;
+ vp8xChunk[6] = (width >> 16) & 0xff;
+ vp8xChunk[7] = height & 0xff;
+ vp8xChunk[8] = (height >> 8) & 0xff;
+ vp8xChunk[9] = (height >> 16) & 0xff;
+ WebPData vp8x_data = { vp8xChunk, 10 };
+ if (WebPMuxSetChunk(mux, "VP8X", &vp8x_data, copy_data) == WEBP_MUX_OK) {
+ QByteArray iccProfile = image.colorSpace().iccProfile();
+ WebPData iccp_data = {
+ reinterpret_cast<const uint8_t *>(iccProfile.constData()),
+ static_cast<size_t>(iccProfile.size())
+ };
+ if (WebPMuxSetChunk(mux, "ICCP", &iccp_data, copy_data) == WEBP_MUX_OK) {
+ WebPData output_data;
+ if (WebPMuxAssemble(mux, &output_data) == WEBP_MUX_OK) {
+ res = (output_data.size ==
+ static_cast<size_t>(device()->write(reinterpret_cast<const char *>(output_data.bytes), output_data.size)));
+ }
+ WebPDataClear(&output_data);
+ }
+ }
+ WebPMuxDelete(mux);
+ }
+ if (!res) {
+ res = (writer.size ==
+ static_cast<size_t>(device()->write(reinterpret_cast<const char *>(writer.mem), writer.size)));
+ }
WebPPictureFree(&picture);
- return true;
+ return res;
}
QVariant QWebpHandler::option(ImageOption option) const
diff --git a/src/plugins/imageformats/webp/qwebphandler_p.h b/src/plugins/imageformats/webp/qwebphandler_p.h
index 31574b4..96a8811 100644
--- a/src/plugins/imageformats/webp/qwebphandler_p.h
+++ b/src/plugins/imageformats/webp/qwebphandler_p.h
@@ -41,6 +41,7 @@
#define QWEBPHANDLER_P_H
#include <QtGui/qcolor.h>
+#include <QtGui/qcolorspace.h>
#include <QtGui/qimage.h>
#include <QtGui/qimageiohandler.h>
#include <QtCore/qbytearray.h>
@@ -90,6 +91,7 @@ private:
int m_quality;
mutable ScanState m_scanState;
WebPBitstreamFeatures m_features;
+ uint32_t m_formatFlags;
int m_loop;
int m_frameCount;
QColor m_bgColor;
@@ -97,6 +99,7 @@ private:
WebPData m_webpData;
WebPDemuxer *m_demuxer;
WebPIterator m_iter;
+ QColorSpace m_colorSpace;
QImage *m_composited; // For animation frames composition
};
diff --git a/tests/auto/tiff/tst_qtiff.cpp b/tests/auto/tiff/tst_qtiff.cpp
index 29d85df..8cdc6da 100644
--- a/tests/auto/tiff/tst_qtiff.cpp
+++ b/tests/auto/tiff/tst_qtiff.cpp
@@ -87,6 +87,9 @@ private slots:
void readRgba64();
void readGray16();
+ void colorSpace_data();
+ void colorSpace();
+
private:
QString prefix;
};
@@ -624,5 +627,41 @@ void tst_qtiff::readGray16()
QCOMPARE(image.format(), QImage::Format_Grayscale16);
}
+void tst_qtiff::colorSpace_data()
+{
+ QTest::addColumn<QColorSpace::ColorSpaceId>("colorspaceId");
+
+ QTest::newRow("Undefined") << QColorSpace::Undefined;
+ QTest::newRow("sRGB") << QColorSpace::SRgb;
+ QTest::newRow("sRGB(linear)") << QColorSpace::SRgbLinear;
+ QTest::newRow("AdobeRGB") << QColorSpace::AdobeRgb;
+ QTest::newRow("DisplayP3") << QColorSpace::DisplayP3;
+ QTest::newRow("ProPhotoRgb") << QColorSpace::ProPhotoRgb;
+}
+
+void tst_qtiff::colorSpace()
+{
+ QFETCH(QColorSpace::ColorSpaceId, colorspaceId);
+
+ QImage image(prefix + "colorful.bmp");
+ QVERIFY(!image.isNull());
+
+ image.setColorSpace(colorspaceId);
+
+ QByteArray output;
+ QBuffer buf(&output);
+ QVERIFY(buf.open(QIODevice::WriteOnly));
+ QImageWriter writer(&buf, "tiff");
+ writer.write(image);
+ buf.close();
+
+ QVERIFY(buf.open(QIODevice::ReadOnly));
+ QImageReader reader(&buf);
+ QImage image2 = reader.read();
+
+ QCOMPARE(image2.colorSpace(), QColorSpace(colorspaceId));
+ QCOMPARE(image2, image);
+}
+
QTEST_MAIN(tst_qtiff)
#include "tst_qtiff.moc"