diff options
-rw-r--r-- | .qmake.conf | 4 | ||||
-rw-r--r-- | src/3rdparty/libwebp.pri | 25 | ||||
-rw-r--r-- | src/imageformats/configure.json | 162 | ||||
-rw-r--r-- | src/plugins/imageformats/tiff/qtiffhandler.cpp | 19 | ||||
-rw-r--r-- | src/plugins/imageformats/webp/qwebphandler.cpp | 75 | ||||
-rw-r--r-- | src/plugins/imageformats/webp/qwebphandler_p.h | 3 | ||||
-rw-r--r-- | tests/auto/tiff/tst_qtiff.cpp | 40 |
7 files changed, 206 insertions, 122 deletions
diff --git a/.qmake.conf b/.qmake.conf index 1ed62c5..f6895bb 100644 --- a/.qmake.conf +++ b/.qmake.conf @@ -1,3 +1,5 @@ load(qt_build_config) -MODULE_VERSION = 5.13.1 +DEFINES += QT_NO_FOREACH QT_NO_JAVA_STYLE_ITERATORS QT_NO_LINKED_LIST + +MODULE_VERSION = 5.14.0 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..3b38fae 100644 --- a/src/imageformats/configure.json +++ b/src/imageformats/configure.json @@ -16,135 +16,103 @@ "libraries": { "jasper": { - "label": "jasper", + "headers": "jasper/jasper.h", "test": { - "label": "Jasper(header in /usr/include)", - "type": "compile", - "test": { - "include": [ - "string.h", - "jasper/jasper.h" - ], - "qmake": [ - "msvc: LIBS += libjasper.lib", - "else: LIBS += -ljasper" - ], - "main": [ - "// This version of Jasper is broken, according to the old Qt Solutions docs", - "if (strcmp(JAS_VERSION, \"1.900.0\") == 0)", - " return 1;", - "return 0;" - ] - } + "include": [ + "string.h", + "jasper/jasper.h" + ], + "head": [ + "constexpr bool streq(const char *a, const char *b)", + "{", + " return a[0] == b[0] && (!a[0] || streq(a + 1, b + 1));", + "}" + ], + "main": [ + "static_assert(!streq(JAS_VERSION, \"1.900.0\"),", + " \"JasPer version 1.900.0 is considered broken.\");" + ] }, "sources": [ "-ljasper" ] }, "mng": { - "label": "mng", + "headers": "libmng.h", "test": { - "label": "MNG(header in /usr/include)", - "type": "compile", - "test": { - "include": [ - "stdio.h", - "libmng.h" - ], - "qmake": [ - "LIBS += -lmng" - ], - "main": [ - "mng_handle hMNG;", - "mng_cleanup(&hMNG);", + "include": [ + "stdio.h", + "libmng.h" + ], + "main": [ + "mng_handle hMNG;", + "mng_cleanup(&hMNG);", "#if defined(MNG_VERSION_MAJOR)", "#if MNG_VERSION_MAJOR < 1 || (MNG_VERSION_MAJOR == 1 && MNG_VERSION_MINOR == 0 && MNG_VERSION_RELEASE < 9)", "#error System libmng version is less than 1.0.9", "#endif", - "#endif", - "return 0;" - ] - } + "#endif" + ] }, "sources": [ "-lmng" ] }, "tiff": { - "label": "tiff", + "headers": "tiffio.h", "test": { - "label": "TIFF(header in /usr/include)", - "type": "compile", - "test": { - "include": "tiffio.h", - "qmake": [ - "unix|mingw: LIBS += -ltiff", - "else:win32: LIBS += libtiff.lib" - ], - "main": [ - "#if !defined(TIFF_VERSION) && defined(TIFF_VERSION_CLASSIC)", - "// libtiff 4.0 splits it into TIFF_VERSION_CLASSIC and TIFF_VERSION_BIG", - "# define TIFF_VERSION TIFF_VERSION_CLASSIC", - "#endif", - - "#if !defined(TIFF_VERSION)", - "# error \"Required libtiff not found\"", - "#elif TIFF_VERSION < 42", - "# error \"unsupported tiff version\"", - "#endif", + "main": [ + "#if !defined(TIFF_VERSION) && defined(TIFF_VERSION_CLASSIC)", + "// libtiff 4.0 splits it into TIFF_VERSION_CLASSIC and TIFF_VERSION_BIG", + "# define TIFF_VERSION TIFF_VERSION_CLASSIC", + "#endif", - "tdata_t buffer = _TIFFmalloc(128);", - "_TIFFfree(buffer);", + "#if !defined(TIFF_VERSION)", + "# error \"Required libtiff not found\"", + "#elif TIFF_VERSION < 42", + "# error \"unsupported tiff version\"", + "#endif", - "// some libtiff implementations where TIFF_VERSION >= 42 do not", - "// have TIFFReadRGBAImageOriented(), so let's check for it", - "TIFFReadRGBAImageOriented(0, 0, 0, 0, 0, 0);", + "tdata_t buffer = _TIFFmalloc(128);", + "_TIFFfree(buffer);", - "return 0;" - ] - } + "// some libtiff implementations where TIFF_VERSION >= 42 do not", + "// have TIFFReadRGBAImageOriented(), so let's check for it", + "TIFFReadRGBAImageOriented(0, 0, 0, 0, 0, 0);" + ] }, "sources": [ "-ltiff" ] }, "webp": { - "label": "webp", + "headers": [ + "webp/decode.h", + "webp/encode.h", + "webp/demux.h", + "webp/mux.h" + ], "test": { - "label": "WebP(header in /usr/include)", - "type": "compile", - "test": { - "include": [ - "webp/decode.h", - "webp/encode.h", - "webp/demux.h" - ], - "qmake": [ - "LIBS += -lwebp -lwebpdemux" - ], - "main": [ - "#if WEBP_ABI_IS_INCOMPATIBLE(WEBP_DECODER_ABI_VERSION, 0x0203) || WEBP_ABI_IS_INCOMPATIBLE(WEBP_ENCODER_ABI_VERSION, 0x0202)", - "#error \"Incompatible libwebp version\"", - "#endif", - - "WebPDecoderConfig config;", - "WebPDecBuffer *output_buffer = &config.output;", - "WebPBitstreamFeatures *bitstream = &config.input;", - "WebPPicture picture;", - "picture.use_argb = 0;", - "WebPConfig config2;", - "config2.lossless = 0;", - "WebPData data = {};", - "WebPDemuxer *demuxer = WebPDemux(&data);", - "WebPIterator iter;", - "iter.frame_num = 0;", + "main": [ + "#if WEBP_ABI_IS_INCOMPATIBLE(WEBP_DECODER_ABI_VERSION, 0x0203) || WEBP_ABI_IS_INCOMPATIBLE(WEBP_ENCODER_ABI_VERSION, 0x0202)", + "#error \"Incompatible libwebp version\"", + "#endif", - "return 0;" - ] - } + "WebPDecoderConfig config;", + "WebPDecBuffer *output_buffer = &config.output;", + "WebPBitstreamFeatures *bitstream = &config.input;", + "WebPPicture picture;", + "picture.use_argb = 0;", + "WebPConfig config2;", + "config2.lossless = 0;", + "WebPData data = {};", + "WebPDemuxer *demuxer = WebPDemux(&data);", + "WebPIterator iter;", + "iter.frame_num = 0;" + ] }, "sources": [ - "-lwebp -lwebpdemux" + "-lwebp -lwebpdemux -lwebpmux" ] } }, diff --git a/src/plugins/imageformats/tiff/qtiffhandler.cpp b/src/plugins/imageformats/tiff/qtiffhandler.cpp index 06a5472..d7e46cd 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 454d654..f0b8caf 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,16 @@ 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); + } + // Go to first frame if (!WebPDemuxGetFrame(m_demuxer, 1, &m_iter)) return false; @@ -201,17 +214,11 @@ 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()) { @@ -261,8 +268,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; @@ -270,9 +279,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 b2c5546..be02460 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; }; @@ -499,7 +502,7 @@ void tst_qtiff::supportsOption() allOptions.remove(QImageIOHandler::ImageOption(options.at(i))); } - foreach (QImageIOHandler::ImageOption option, allOptions) + for (QImageIOHandler::ImageOption option : qAsConst(allOptions)) QVERIFY(!writer.supportsOption(option)); } @@ -627,5 +630,40 @@ void tst_qtiff::readGray16() QCOMPARE(image.format(), QImage::Format_Grayscale16); } +void tst_qtiff::colorSpace_data() +{ + QTest::addColumn<decltype(QColorSpace::SRgb)>("namedColorSpace"); + + 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(decltype(QColorSpace::SRgb), namedColorSpace); + + QImage image(prefix + "colorful.bmp"); + QVERIFY(!image.isNull()); + + image.setColorSpace(namedColorSpace); + + 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(), namedColorSpace); + QCOMPARE(image2, image); +} + QTEST_MAIN(tst_qtiff) #include "tst_qtiff.moc" |