Parse color space name from ICC profile
Adds parsing of ICCv2 and ICCv4 description tags. Change-Id: I8647e1529d4127950624f16c475d8dba610149b6 Reviewed-by: Eirik Aavitsland <eirik.aavitsland@qt.io>
This commit is contained in:
parent
a2c1109152
commit
dcc4d54fc9
@ -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;
|
||||
|
@ -42,8 +42,9 @@
|
||||
#include <qbuffer.h>
|
||||
#include <qbytearray.h>
|
||||
#include <qdatastream.h>
|
||||
#include <qloggingcategory.h>
|
||||
#include <qendian.h>
|
||||
#include <qloggingcategory.h>
|
||||
#include <qstring.h>
|
||||
|
||||
#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<quint16> utf16hostendian(stringLen);
|
||||
qFromBigEndian<ushort>(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;
|
||||
|
BIN
tests/auto/gui/painting/qcolorspace/resources/HP_ZR30w.icc
Normal file
BIN
tests/auto/gui/painting/qcolorspace/resources/HP_ZR30w.icc
Normal file
Binary file not shown.
@ -53,6 +53,7 @@ private slots:
|
||||
void toIccProfile_data();
|
||||
void toIccProfile();
|
||||
|
||||
void fromIccProfile_data();
|
||||
void fromIccProfile();
|
||||
|
||||
void imageConversion_data();
|
||||
@ -142,21 +143,38 @@ void tst_QColorSpace::toIccProfile()
|
||||
QCOMPARE(iccProfile2, iccProfile);
|
||||
}
|
||||
|
||||
void tst_QColorSpace::fromIccProfile_data()
|
||||
{
|
||||
QTest::addColumn<QString>("testProfile");
|
||||
QTest::addColumn<QColorSpace::ColorSpaceId>("colorSpaceId");
|
||||
QTest::addColumn<QColorSpace::TransferFunction>("transferFunction");
|
||||
QTest::addColumn<QString>("description");
|
||||
|
||||
QString prefix = QFINDTESTDATA("resources/");
|
||||
// Read the official sRGB ICCv2 profile:
|
||||
QTest::newRow("sRGB2014 (ICCv2)") << prefix + "sRGB2014.icc" << QColorSpace::SRgb
|
||||
<< QColorSpace::TransferFunction::SRgb << QString("sRGB2014");
|
||||
// My monitor's profile:
|
||||
QTest::newRow("HP ZR30w (ICCv4)") << prefix + "HP_ZR30w.icc" << QColorSpace::Unknown
|
||||
<< QColorSpace::TransferFunction::Gamma << QString("HP Z30i");
|
||||
}
|
||||
|
||||
void tst_QColorSpace::fromIccProfile()
|
||||
{
|
||||
// Read the official sRGB ICCv2 profile:
|
||||
QString prefix = QFINDTESTDATA("resources/");
|
||||
QFile file(prefix + "sRGB2014.icc");
|
||||
QFETCH(QString, testProfile);
|
||||
QFETCH(QColorSpace::ColorSpaceId, colorSpaceId);
|
||||
QFETCH(QColorSpace::TransferFunction, transferFunction);
|
||||
QFETCH(QString, description);
|
||||
|
||||
QFile file(testProfile);
|
||||
file.open(QIODevice::ReadOnly);
|
||||
QByteArray iccProfile = file.readAll();
|
||||
QColorSpace stdSRgb = QColorSpace::fromIccProfile(iccProfile);
|
||||
QVERIFY(stdSRgb.isValid());
|
||||
QColorSpace fileColorSpace = QColorSpace::fromIccProfile(iccProfile);
|
||||
QVERIFY(fileColorSpace.isValid());
|
||||
|
||||
QCOMPARE(stdSRgb.gamut(), QColorSpace::Gamut::SRgb);
|
||||
QCOMPARE(stdSRgb.transferFunction(), QColorSpace::TransferFunction::SRgb);
|
||||
QCOMPARE(stdSRgb.colorSpaceId(), QColorSpace::SRgb);
|
||||
|
||||
QCOMPARE(stdSRgb, QColorSpace(QColorSpace::SRgb));
|
||||
QCOMPARE(fileColorSpace.colorSpaceId(), colorSpaceId);
|
||||
QCOMPARE(fileColorSpace.transferFunction(), transferFunction);
|
||||
QCOMPARE(QColorSpacePrivate::get(fileColorSpace)->description, description);
|
||||
}
|
||||
|
||||
void tst_QColorSpace::imageConversion_data()
|
||||
|
Loading…
Reference in New Issue
Block a user