From dcc4d54fc9340fcbed3ffddbe1cf99e6ca70d4f9 Mon Sep 17 00:00:00 2001 From: Allan Sandfeld Jensen Date: Fri, 19 Jul 2019 14:46:20 +0200 Subject: Parse color space name from ICC profile Adds parsing of ICCv2 and ICCv4 description tags. Change-Id: I8647e1529d4127950624f16c475d8dba610149b6 Reviewed-by: Eirik Aavitsland --- src/gui/painting/qcolorspace.cpp | 21 +++++++---- src/gui/painting/qicc.cpp | 77 ++++++++++++++++++++++++++++++++++++++-- 2 files changed, 89 insertions(+), 9 deletions(-) (limited to 'src') diff --git a/src/gui/painting/qcolorspace.cpp b/src/gui/painting/qcolorspace.cpp index d058505e68..4a1ed373f5 100644 --- a/src/gui/painting/qcolorspace.cpp +++ b/src/gui/painting/qcolorspace.cpp @@ -247,12 +247,14 @@ bool QColorSpacePrivate::identifyColorSpace() case QColorSpace::Gamut::SRgb: if (transferFunction == QColorSpace::TransferFunction::SRgb) { id = QColorSpace::SRgb; - description = QStringLiteral("sRGB"); + if (description.isEmpty()) + description = QStringLiteral("sRGB"); return true; } if (transferFunction == QColorSpace::TransferFunction::Linear) { id = QColorSpace::SRgbLinear; - description = QStringLiteral("Linear sRGB"); + if (description.isEmpty()) + description = QStringLiteral("Linear sRGB"); return true; } break; @@ -260,7 +262,8 @@ bool QColorSpacePrivate::identifyColorSpace() if (transferFunction == QColorSpace::TransferFunction::Gamma) { if (qAbs(gamma - 2.19921875f) < (1/1024.0f)) { id = QColorSpace::AdobeRgb; - description = QStringLiteral("Adobe RGB"); + if (description.isEmpty()) + description = QStringLiteral("Adobe RGB"); return true; } } @@ -268,21 +271,24 @@ bool QColorSpacePrivate::identifyColorSpace() case QColorSpace::Gamut::DciP3D65: if (transferFunction == QColorSpace::TransferFunction::SRgb) { id = QColorSpace::DisplayP3; - description = QStringLiteral("Display P3"); + if (description.isEmpty()) + description = QStringLiteral("Display P3"); return true; } break; case QColorSpace::Gamut::ProPhotoRgb: if (transferFunction == QColorSpace::TransferFunction::ProPhotoRgb) { id = QColorSpace::ProPhotoRgb; - description = QStringLiteral("ProPhoto RGB"); + if (description.isEmpty()) + description = QStringLiteral("ProPhoto RGB"); return true; } if (transferFunction == QColorSpace::TransferFunction::Gamma) { // ProPhoto RGB's curve is effectively gamma 1.8 for 8bit precision. if (qAbs(gamma - 1.8f) < (1/1024.0f)) { id = QColorSpace::ProPhotoRgb; - description = QStringLiteral("ProPhoto RGB"); + if (description.isEmpty()) + description = QStringLiteral("ProPhoto RGB"); return true; } } @@ -290,7 +296,8 @@ bool QColorSpacePrivate::identifyColorSpace() case QColorSpace::Gamut::Bt2020: if (transferFunction == QColorSpace::TransferFunction::Bt2020) { id = QColorSpace::Bt2020; - description = QStringLiteral("BT.2020"); + if (description.isEmpty()) + description = QStringLiteral("BT.2020"); return true; } break; diff --git a/src/gui/painting/qicc.cpp b/src/gui/painting/qicc.cpp index 53055b426c..1941981d30 100644 --- a/src/gui/painting/qicc.cpp +++ b/src/gui/painting/qicc.cpp @@ -42,8 +42,9 @@ #include #include #include -#include #include +#include +#include #include "qcolorspace_p.h" #include "qcolortrc_p.h" @@ -117,6 +118,7 @@ enum class Tag : quint32 { bkpt = IccTag('b', 'k', 'p', 't'), mft1 = IccTag('m', 'f', 't', '1'), mft2 = IccTag('m', 'f', 't', '2'), + mluc = IccTag('m', 'l', 'u', 'c'), mAB_ = IccTag('m', 'A', 'B', ' '), mBA_ = IccTag('m', 'B', 'A', ' '), chad = IccTag('c', 'h', 'a', 'd'), @@ -164,6 +166,25 @@ struct ParaTagData : GenericTagData { quint32_be parameter[1]; }; +struct DescTagData : GenericTagData { + quint32_be asciiDescriptionLength; + char asciiDescription[1]; + // .. we ignore the rest +}; + +struct MlucTagRecord { + quint16_be languageCode; + quint16_be countryCode; + quint32_be size; + quint32_be offset; +}; + +struct MlucTagData : GenericTagData { + quint32_be recordCount; + quint32_be recordSize; // = sizeof(MlucTagRecord) + MlucTagRecord records[1]; +}; + // For both mAB and mBA struct mABTagData : GenericTagData { quint8 inputChannels; @@ -535,6 +556,53 @@ bool parseTRC(const QByteArray &data, const TagEntry &tagEntry, QColorTrc &gamma return false; } +bool parseDesc(const QByteArray &data, const TagEntry &tagEntry, QString &descName) +{ + const GenericTagData *tag = (const GenericTagData *)(data.constData() + tagEntry.offset); + + // Either 'desc' (ICCv2) or 'mluc' (ICCv4) + if (tag->type == quint32(Tag::desc)) { + if (tagEntry.size < sizeof(DescTagData)) + return false; + const DescTagData *desc = (const DescTagData *)(data.constData() + tagEntry.offset); + const quint32 len = desc->asciiDescriptionLength; + if (len < 1) + return false; + if (tagEntry.size - 12 < len) + return false; + if (desc->asciiDescription[len - 1] != '\0') + return false; + descName = QString::fromLatin1(desc->asciiDescription, len - 1); + return true; + } + if (tag->type != quint32(Tag::mluc)) + return false; + + if (tagEntry.size < sizeof(MlucTagData)) + return false; + const MlucTagData *mluc = (const MlucTagData *)(data.constData() + tagEntry.offset); + if (mluc->recordCount < 1) + return false; + if (mluc->recordSize < 12) + return false; + // We just use the primary record regardless of language or country. + const quint32 stringOffset = mluc->records[0].offset; + const quint32 stringSize = mluc->records[0].size; + if (tagEntry.size < stringOffset || tagEntry.size - stringOffset < stringSize ) + return false; + if ((stringSize | stringOffset) & 1) + return false; + quint32 stringLen = stringSize / 2; + const ushort *unicodeString = (const ushort *)(data.constData() + tagEntry.offset + stringOffset); + // The given length shouldn't include 0-termination, but might. + if (stringLen > 1 && unicodeString[stringLen - 1] == 0) + --stringLen; + QVarLengthArray utf16hostendian(stringLen); + qFromBigEndian(unicodeString, stringLen, utf16hostendian.data()); + descName = QString::fromUtf16(utf16hostendian.data(), stringLen); + return true; +} + bool fromIccProfile(const QByteArray &data, QColorSpace *colorSpace) { if (data.size() < qsizetype(sizeof(ICCProfileHeader))) { @@ -690,7 +758,12 @@ bool fromIccProfile(const QByteArray &data, QColorSpace *colorSpace) colorspaceDPtr->transferFunction = QColorSpace::TransferFunction::Custom; } - // FIXME: try to parse the description.. + if (tagIndex.contains(Tag::desc)) { + if (!parseDesc(data, tagIndex[Tag::desc], colorspaceDPtr->description)) + qCWarning(lcIcc) << "fromIccProfile: Failed to parse description"; + else + qCDebug(lcIcc) << "fromIccProfile: Description" << colorspaceDPtr->description; + } if (!colorspaceDPtr->identifyColorSpace()) colorspaceDPtr->id = QColorSpace::Unknown; -- cgit v1.2.3