Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Workaround for ICC profile parsing bug in Qt #683

Merged
merged 2 commits into from
Jun 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -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<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 @@ -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();

Expand Down
Loading