Skip to content

Commit

Permalink
Merge pull request #683 from jdpurcell/issue673
Browse files Browse the repository at this point in the history
Workaround for ICC profile parsing bug in Qt
  • Loading branch information
jurplel committed Jun 25, 2024
2 parents d296753 + b61406f commit 34311df
Show file tree
Hide file tree
Showing 2 changed files with 68 additions and 1 deletion.
66 changes: 65 additions & 1 deletion src/qvimagecore.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include "qvwin32functions.h"
#include "qvcocoafunctions.h"
#include "qvlinuxx11functions.h"
#include <cstring>
#include <random>
#include <QMessageBox>
#include <QDir>
Expand Down Expand Up @@ -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())
Expand Down Expand Up @@ -565,12 +576,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<quint32>(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<quint32>(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<quint32>(data + offsetTagCount) - 1, data + offsetTagCount);
}
return foundTinyData;
}
#endif

void QVImageCore::jumpToNextFrame()
{
if (currentFileDetails.isMovieLoaded)
Expand Down
3 changes: 3 additions & 0 deletions src/qvimagecore.h
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,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();

Expand Down

0 comments on commit 34311df

Please sign in to comment.