From 210b6f8eab8e01cddeaf12dc990741f2fa3b21fd Mon Sep 17 00:00:00 2001 From: "J.D. Purcell" Date: Fri, 10 May 2024 22:47:41 -0400 Subject: [PATCH 1/2] Workaround for ICC profile parsing bug in Qt --- src/qvimagecore.cpp | 63 ++++++++++++++++++++++++++++++++++++++++++++- src/qvimagecore.h | 1 + 2 files changed, 63 insertions(+), 1 deletion(-) diff --git a/src/qvimagecore.cpp b/src/qvimagecore.cpp index 0fecd73b..7709d15d 100644 --- a/src/qvimagecore.cpp +++ b/src/qvimagecore.cpp @@ -3,6 +3,7 @@ #include "qvwin32functions.h" #include "qvcocoafunctions.h" #include "qvlinuxx11functions.h" +#include #include #include #include @@ -147,6 +148,13 @@ QVImageCore::ReadData QVImageCore::readFile(const QString &fileName, const QColo } #if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) + // Work around Qt ICC profile parsing bug + if (!readImage.colorSpace().isValid() && !readImage.colorSpace().iccProfile().isEmpty()) + { + QByteArray profileData = readImage.colorSpace().iccProfile(); + if (removeTinyDataTagsFromIccProfile(profileData)) + readImage.setColorSpace(QColorSpace::fromIccProfile(profileData)); + } // Assume image is sRGB if it doesn't specify if (!readImage.colorSpace().isValid()) readImage.setColorSpace(QColorSpace::SRgb); @@ -536,12 +544,65 @@ QColorSpace QVImageCore::detectDisplayColorSpace() const #endif if (!profileData.isEmpty()) - return QColorSpace::fromIccProfile(profileData); + { + QColorSpace colorSpace = QColorSpace::fromIccProfile(profileData); + if (!colorSpace.isValid() && removeTinyDataTagsFromIccProfile(profileData)) + colorSpace = QColorSpace::fromIccProfile(profileData); + return colorSpace; + } #endif return {}; } +// Workaround for QTBUG-125241 +bool QVImageCore::removeTinyDataTagsFromIccProfile(QByteArray &profile) +{ +#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) + const int offsetTagCount = 128; + const qsizetype length = profile.length(); + qsizetype offset = offsetTagCount; + char *data = profile.data(); + bool foundTinyData = false; + // read tag count + if (length - offset < 4) + return false; + quint32 tagCount = qFromBigEndian(data + offset); + offset += 4; + // so we don't have to worry about overflows + if (tagCount > 99999) + return false; + // loop through tags + if (length - offset < qsizetype(tagCount * 12)) + return false; + while (tagCount) + { + tagCount -= 1; + const quint32 dataSize = qFromBigEndian(data + offset + 8); + if (dataSize >= 12) + { + // this tag is fine + offset += 12; + continue; + } + // qt will fail on this tag, remove it + foundTinyData = true; + if (tagCount) + { + // shift subsequent tags back + std::memmove(data + offset, data + offset + 12, tagCount * 12); + } + // zero fill gap at end + std::memset(data + offset + (tagCount * 12), 0, 12); + // decrement tag count + qToBigEndian(qFromBigEndian(data + offsetTagCount) - 1, data + offsetTagCount); + } + return foundTinyData; +#else + return false; +#endif +} + void QVImageCore::jumpToNextFrame() { if (currentFileDetails.isMovieLoaded) diff --git a/src/qvimagecore.h b/src/qvimagecore.h index 20e5f0fd..e68473d7 100644 --- a/src/qvimagecore.h +++ b/src/qvimagecore.h @@ -72,6 +72,7 @@ class QVImageCore : public QObject static QString getPixmapCacheKey(const QString &absoluteFilePath, const qint64 &fileSize, const QColorSpace &targetColorSpace); QColorSpace getTargetColorSpace() const; QColorSpace detectDisplayColorSpace() const; + static bool removeTinyDataTagsFromIccProfile(QByteArray &profile); void settingsUpdated(); From b61406f0502d15a80117ec5ba9a334e78c1f3572 Mon Sep 17 00:00:00 2001 From: "J.D. Purcell" Date: Wed, 19 Jun 2024 18:44:12 -0400 Subject: [PATCH 2/2] Limit workaround to affected Qt version. --- src/qvimagecore.cpp | 13 ++++++++----- src/qvimagecore.h | 2 ++ 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/qvimagecore.cpp b/src/qvimagecore.cpp index 7709d15d..f4cd1a7f 100644 --- a/src/qvimagecore.cpp +++ b/src/qvimagecore.cpp @@ -147,7 +147,7 @@ QVImageCore::ReadData QVImageCore::readFile(const QString &fileName, const QColo readImage = imageReader.read(); } -#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) +#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) && QT_VERSION < QT_VERSION_CHECK(6, 7, 2) // Work around Qt ICC profile parsing bug if (!readImage.colorSpace().isValid() && !readImage.colorSpace().iccProfile().isEmpty()) { @@ -155,6 +155,9 @@ QVImageCore::ReadData QVImageCore::readFile(const QString &fileName, const QColo if (removeTinyDataTagsFromIccProfile(profileData)) readImage.setColorSpace(QColorSpace::fromIccProfile(profileData)); } +#endif + +#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) // Assume image is sRGB if it doesn't specify if (!readImage.colorSpace().isValid()) readImage.setColorSpace(QColorSpace::SRgb); @@ -546,8 +549,10 @@ QColorSpace QVImageCore::detectDisplayColorSpace() const if (!profileData.isEmpty()) { QColorSpace colorSpace = QColorSpace::fromIccProfile(profileData); +#if QT_VERSION < QT_VERSION_CHECK(6, 7, 2) if (!colorSpace.isValid() && removeTinyDataTagsFromIccProfile(profileData)) colorSpace = QColorSpace::fromIccProfile(profileData); +#endif return colorSpace; } #endif @@ -555,10 +560,10 @@ QColorSpace QVImageCore::detectDisplayColorSpace() const return {}; } +#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) && QT_VERSION < QT_VERSION_CHECK(6, 7, 2) // Workaround for QTBUG-125241 bool QVImageCore::removeTinyDataTagsFromIccProfile(QByteArray &profile) { -#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) const int offsetTagCount = 128; const qsizetype length = profile.length(); qsizetype offset = offsetTagCount; @@ -598,10 +603,8 @@ bool QVImageCore::removeTinyDataTagsFromIccProfile(QByteArray &profile) qToBigEndian(qFromBigEndian(data + offsetTagCount) - 1, data + offsetTagCount); } return foundTinyData; -#else - return false; -#endif } +#endif void QVImageCore::jumpToNextFrame() { diff --git a/src/qvimagecore.h b/src/qvimagecore.h index e68473d7..ab4d06a6 100644 --- a/src/qvimagecore.h +++ b/src/qvimagecore.h @@ -72,7 +72,9 @@ class QVImageCore : public QObject static QString getPixmapCacheKey(const QString &absoluteFilePath, const qint64 &fileSize, const QColorSpace &targetColorSpace); QColorSpace getTargetColorSpace() const; QColorSpace detectDisplayColorSpace() const; +#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) && QT_VERSION < QT_VERSION_CHECK(6, 7, 2) static bool removeTinyDataTagsFromIccProfile(QByteArray &profile); +#endif void settingsUpdated();