Handle chad elements in ICC profiles
Parse them, so we can change white-points correctly, and write them as required by ICCv4 Change-Id: I6d28fb6a85585f80a9867df45864069efda956c5 Reviewed-by: Giuseppe D'Angelo <giuseppe.dangelo@kdab.com>bb10
parent
04e5b86f9e
commit
48d582e0b7
|
|
@ -94,9 +94,6 @@ QColorMatrix QColorSpacePrimaries::toXyzMatrix() const
|
|||
// Now we have scaled conversion to XYZ relative to the given whitepoint
|
||||
toXyz = toXyz * QColorMatrix::fromScale(whiteScale);
|
||||
|
||||
// But we want a conversion to XYZ relative to D50
|
||||
toXyz = QColorMatrix::chromaticAdaptation(wXyz) * toXyz;
|
||||
|
||||
return toXyz;
|
||||
}
|
||||
|
||||
|
|
@ -162,6 +159,9 @@ QColorSpacePrivate::QColorSpacePrivate(const QColorSpacePrimaries &primaries,
|
|||
{
|
||||
Q_ASSERT(primaries.areValid());
|
||||
toXyz = primaries.toXyzMatrix();
|
||||
chad = QColorMatrix::chromaticAdaptation(whitePoint);
|
||||
toXyz = chad * toXyz;
|
||||
|
||||
identifyColorSpace();
|
||||
setTransferFunction();
|
||||
}
|
||||
|
|
@ -175,7 +175,8 @@ QColorSpacePrivate::QColorSpacePrivate(const QPointF &whitePoint,
|
|||
, gamma(gamma)
|
||||
, whitePoint(whitePoint)
|
||||
{
|
||||
toXyz = QColorMatrix::chromaticAdaptation(this->whitePoint);
|
||||
chad = QColorMatrix::chromaticAdaptation(this->whitePoint);
|
||||
toXyz = chad;
|
||||
setTransferFunction();
|
||||
}
|
||||
|
||||
|
|
@ -186,7 +187,8 @@ QColorSpacePrivate::QColorSpacePrivate(const QPointF &whitePoint, const QList<ui
|
|||
, gamma(0)
|
||||
, whitePoint(whitePoint)
|
||||
{
|
||||
toXyz = QColorMatrix::chromaticAdaptation(this->whitePoint);
|
||||
chad = QColorMatrix::chromaticAdaptation(this->whitePoint);
|
||||
toXyz = chad;
|
||||
setTransferFunctionTable(transferFunctionTable);
|
||||
setTransferFunction();
|
||||
}
|
||||
|
|
@ -211,6 +213,8 @@ QColorSpacePrivate::QColorSpacePrivate(const QColorSpacePrimaries &primaries, co
|
|||
{
|
||||
Q_ASSERT(primaries.areValid());
|
||||
toXyz = primaries.toXyzMatrix();
|
||||
chad = QColorMatrix::chromaticAdaptation(whitePoint);
|
||||
toXyz = chad * toXyz;
|
||||
setTransferFunctionTable(transferFunctionTable);
|
||||
identifyColorSpace();
|
||||
initialize();
|
||||
|
|
@ -228,11 +232,12 @@ QColorSpacePrivate::QColorSpacePrivate(const QColorSpacePrimaries &primaries,
|
|||
Q_ASSERT(primaries.areValid());
|
||||
toXyz = primaries.toXyzMatrix();
|
||||
whitePoint = QColorVector(primaries.whitePoint);
|
||||
chad = QColorMatrix::chromaticAdaptation(whitePoint);
|
||||
toXyz = chad * toXyz;
|
||||
setTransferFunctionTables(redTransferFunctionTable,
|
||||
greenTransferFunctionTable,
|
||||
blueTransferFunctionTable);
|
||||
identifyColorSpace();
|
||||
setToXyzMatrix();
|
||||
}
|
||||
|
||||
void QColorSpacePrivate::identifyColorSpace()
|
||||
|
|
@ -310,6 +315,8 @@ void QColorSpacePrivate::setToXyzMatrix()
|
|||
QColorSpacePrimaries colorSpacePrimaries(primaries);
|
||||
toXyz = colorSpacePrimaries.toXyzMatrix();
|
||||
whitePoint = QColorVector(colorSpacePrimaries.whitePoint);
|
||||
chad = QColorMatrix::chromaticAdaptation(whitePoint);
|
||||
toXyz = chad * toXyz;
|
||||
}
|
||||
|
||||
void QColorSpacePrivate::setTransferFunctionTable(const QList<uint16_t> &transferFunctionTable)
|
||||
|
|
@ -930,7 +937,9 @@ void QColorSpace::setPrimaries(const QPointF &whitePoint, const QPointF &redPoin
|
|||
return;
|
||||
}
|
||||
QColorMatrix toXyz = primaries.toXyzMatrix();
|
||||
if (QColorVector(primaries.whitePoint) == d_ptr->whitePoint && toXyz == d_ptr->toXyz)
|
||||
QColorMatrix chad = QColorMatrix::chromaticAdaptation(QColorVector(whitePoint));
|
||||
toXyz = chad * toXyz;
|
||||
if (QColorVector(primaries.whitePoint) == d_ptr->whitePoint && toXyz == d_ptr->toXyz && chad == d_ptr->chad)
|
||||
return;
|
||||
detach();
|
||||
if (d_ptr->transformModel == TransformModel::ElementListProcessing)
|
||||
|
|
@ -940,6 +949,7 @@ void QColorSpace::setPrimaries(const QPointF &whitePoint, const QPointF &redPoin
|
|||
d_ptr->primaries = QColorSpace::Primaries::Custom;
|
||||
d_ptr->colorModel = QColorSpace::ColorModel::Rgb;
|
||||
d_ptr->toXyz = toXyz;
|
||||
d_ptr->chad = chad;
|
||||
d_ptr->whitePoint = QColorVector(primaries.whitePoint);
|
||||
d_ptr->identifyColorSpace();
|
||||
}
|
||||
|
|
@ -982,13 +992,13 @@ void QColorSpace::setWhitePoint(const QPointF &whitePoint)
|
|||
if (d_ptr->transformModel == QColorSpace::TransformModel::ThreeComponentMatrix) {
|
||||
if (d_ptr->colorModel == QColorSpace::ColorModel::Rgb) {
|
||||
// Rescale toXyz to new whitepoint
|
||||
QColorMatrix chad = QColorMatrix::chromaticAdaptation(d_ptr->whitePoint);
|
||||
QColorMatrix rawToXyz = chad.inverted() * d_ptr->toXyz;
|
||||
QColorMatrix rawToXyz = d_ptr->chad.inverted() * d_ptr->toXyz;
|
||||
QColorVector whiteScale = rawToXyz.inverted().map(wXyz);
|
||||
rawToXyz = rawToXyz * QColorMatrix::fromScale(whiteScale);
|
||||
d_ptr->toXyz = QColorMatrix::chromaticAdaptation(wXyz) * rawToXyz;
|
||||
d_ptr->chad = QColorMatrix::chromaticAdaptation(wXyz);
|
||||
d_ptr->toXyz = d_ptr->chad * rawToXyz;
|
||||
} else if (d_ptr->colorModel == QColorSpace::ColorModel::Gray) {
|
||||
d_ptr->toXyz = QColorMatrix::chromaticAdaptation(wXyz);
|
||||
d_ptr->chad = d_ptr->toXyz = QColorMatrix::chromaticAdaptation(wXyz);
|
||||
}
|
||||
}
|
||||
d_ptr->whitePoint = wXyz;
|
||||
|
|
|
|||
|
|
@ -110,6 +110,7 @@ public:
|
|||
// Three component matrix data:
|
||||
QColorTrc trc[3];
|
||||
QColorMatrix toXyz;
|
||||
QColorMatrix chad;
|
||||
|
||||
// Element list processing data:
|
||||
struct TransferElement {
|
||||
|
|
|
|||
|
|
@ -244,7 +244,7 @@ struct mpetTagData : GenericTagData {
|
|||
};
|
||||
|
||||
struct Sf32TagData : GenericTagData {
|
||||
quint32_be value[1];
|
||||
quint32_be value[9];
|
||||
};
|
||||
|
||||
struct MatrixElement {
|
||||
|
|
@ -378,9 +378,16 @@ QByteArray toIccProfile(const QColorSpace &space)
|
|||
return spaceDPtr->iccProfile;
|
||||
Q_ASSERT(spaceDPtr->isThreeComponentMatrix());
|
||||
|
||||
constexpr int tagCount = 9;
|
||||
constexpr uint profileDataOffset = 128 + 4 + 12 * tagCount;
|
||||
constexpr uint variableTagTableOffsets = 128 + 4 + 12 * 5;
|
||||
int fixedLengthTagCount = 5;
|
||||
bool writeChad = false;
|
||||
if (!spaceDPtr->whitePoint.isNull() && spaceDPtr->whitePoint != QColorVector::D50()) {
|
||||
writeChad = true;
|
||||
fixedLengthTagCount++;
|
||||
}
|
||||
|
||||
const int tagCount = fixedLengthTagCount + 4;
|
||||
const uint profileDataOffset = 128 + 4 + 12 * tagCount;
|
||||
const uint variableTagTableOffsets = 128 + 4 + 12 * fixedLengthTagCount;
|
||||
uint currentOffset = 0;
|
||||
uint rTrcOffset, gTrcOffset, bTrcOffset;
|
||||
uint rTrcSize, gTrcSize, bTrcSize;
|
||||
|
|
@ -410,19 +417,23 @@ QByteArray toIccProfile(const QColorSpace &space)
|
|||
stream << uint(0) << uint(0) << uint(0) << uint(0) << uint(0) << uint(0) << uint(0);
|
||||
|
||||
// Tag table:
|
||||
currentOffset = profileDataOffset;
|
||||
stream << uint(tagCount);
|
||||
stream << uint(Tag::rXYZ) << uint(profileDataOffset + 00) << uint(20);
|
||||
stream << uint(Tag::gXYZ) << uint(profileDataOffset + 20) << uint(20);
|
||||
stream << uint(Tag::bXYZ) << uint(profileDataOffset + 40) << uint(20);
|
||||
stream << uint(Tag::wtpt) << uint(profileDataOffset + 60) << uint(20);
|
||||
stream << uint(Tag::cprt) << uint(profileDataOffset + 80) << uint(12);
|
||||
currentOffset += 92;
|
||||
if (writeChad) {
|
||||
stream << uint(Tag::chad) << uint(currentOffset) << uint(44);
|
||||
currentOffset += 44;
|
||||
}
|
||||
// From here the offset and size will be updated later:
|
||||
stream << uint(Tag::rTRC) << uint(0) << uint(0);
|
||||
stream << uint(Tag::gTRC) << uint(0) << uint(0);
|
||||
stream << uint(Tag::bTRC) << uint(0) << uint(0);
|
||||
stream << uint(Tag::desc) << uint(0) << uint(0);
|
||||
// TODO: consider adding 'chad' tag (required in ICC >=4 when we have non-D50 whitepoint)
|
||||
currentOffset = profileDataOffset;
|
||||
|
||||
// Tag data:
|
||||
stream << uint(Tag::XYZ_) << uint(0);
|
||||
|
|
@ -443,7 +454,19 @@ QByteArray toIccProfile(const QColorSpace &space)
|
|||
stream << toFixedS1516(spaceDPtr->whitePoint.z);
|
||||
stream << uint(Tag::text) << uint(0);
|
||||
stream << uint(IccTag('N', '/', 'A', '\0'));
|
||||
currentOffset += 92;
|
||||
if (writeChad) {
|
||||
QColorMatrix chad = QColorMatrix::chromaticAdaptation(spaceDPtr->whitePoint);
|
||||
stream << uint(Tag::sf32) << uint(0);
|
||||
stream << toFixedS1516(chad.r.x);
|
||||
stream << toFixedS1516(chad.g.x);
|
||||
stream << toFixedS1516(chad.b.x);
|
||||
stream << toFixedS1516(chad.r.y);
|
||||
stream << toFixedS1516(chad.g.y);
|
||||
stream << toFixedS1516(chad.b.y);
|
||||
stream << toFixedS1516(chad.r.z);
|
||||
stream << toFixedS1516(chad.g.z);
|
||||
stream << toFixedS1516(chad.b.z);
|
||||
}
|
||||
|
||||
// From now on the data is variable sized:
|
||||
rTrcOffset = currentOffset;
|
||||
|
|
@ -1117,6 +1140,35 @@ static bool parseGrayMatrix(const QByteArray &data, const QHash<Tag, TagEntry> &
|
|||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool parseChad(const QByteArray &data, const TagEntry &tagEntry, QColorSpacePrivate *colorspaceDPtr)
|
||||
{
|
||||
if (tagEntry.size < sizeof(Sf32TagData) || qsizetype(tagEntry.size) > data.size())
|
||||
return false;
|
||||
const Sf32TagData chadtag = qFromUnaligned<Sf32TagData>(data.constData() + tagEntry.offset);
|
||||
if (chadtag.type != uint32_t(Tag::sf32)) {
|
||||
qCWarning(lcIcc, "fromIccProfile: bad chad data type");
|
||||
return false;
|
||||
}
|
||||
QColorMatrix chad;
|
||||
chad.r.x = fromFixedS1516(chadtag.value[0]);
|
||||
chad.g.x = fromFixedS1516(chadtag.value[1]);
|
||||
chad.b.x = fromFixedS1516(chadtag.value[2]);
|
||||
chad.r.y = fromFixedS1516(chadtag.value[3]);
|
||||
chad.g.y = fromFixedS1516(chadtag.value[4]);
|
||||
chad.b.y = fromFixedS1516(chadtag.value[5]);
|
||||
chad.r.z = fromFixedS1516(chadtag.value[6]);
|
||||
chad.g.z = fromFixedS1516(chadtag.value[7]);
|
||||
chad.b.z = fromFixedS1516(chadtag.value[8]);
|
||||
|
||||
if (!chad.isValid()) {
|
||||
qCWarning(lcIcc, "fromIccProfile: invalid chad matrix");
|
||||
return false;
|
||||
}
|
||||
colorspaceDPtr->chad = chad;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool parseTRCs(const QByteArray &data, const QHash<Tag, TagEntry> &tagIndex, QColorSpacePrivate *colorspaceDPtr, bool isColorSpaceTypeGray)
|
||||
{
|
||||
TagEntry rTrc;
|
||||
|
|
@ -1279,6 +1331,12 @@ bool fromIccProfile(const QByteArray &data, QColorSpace *colorSpace)
|
|||
} else {
|
||||
Q_UNREACHABLE();
|
||||
}
|
||||
if (tagIndex.contains(Tag::chad)) {
|
||||
if (!parseChad(data, tagIndex[Tag::chad], colorspaceDPtr))
|
||||
return false;
|
||||
} else {
|
||||
colorspaceDPtr->chad = QColorMatrix::chromaticAdaptation(colorspaceDPtr->whitePoint);
|
||||
}
|
||||
|
||||
// Reset the matrix to our canonical values:
|
||||
if (colorspaceDPtr->primaries != QColorSpace::Primaries::Custom)
|
||||
|
|
|
|||
Loading…
Reference in New Issue