diff --git a/src/qvimagecore.cpp b/src/qvimagecore.cpp index 0fecd73b..f4cd1a7f 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 @@ -146,6 +147,16 @@ QVImageCore::ReadData QVImageCore::readFile(const QString &fileName, const QColo readImage = imageReader.read(); } +#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()) + { + QByteArray profileData = readImage.colorSpace().iccProfile(); + 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()) @@ -536,12 +547,65 @@ QColorSpace QVImageCore::detectDisplayColorSpace() const #endif if (!profileData.isEmpty()) - return QColorSpace::fromIccProfile(profileData); + { + 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 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) +{ + 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; +} +#endif + void QVImageCore::jumpToNextFrame() { if (currentFileDetails.isMovieLoaded) diff --git a/src/qvimagecore.h b/src/qvimagecore.h index 20e5f0fd..ab4d06a6 100644 --- a/src/qvimagecore.h +++ b/src/qvimagecore.h @@ -72,6 +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();