diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2024-02-26 18:10:41 +0100 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2024-04-05 18:40:47 +0200 |
commit | 48d582e0b7010532ea748ed3cde9b9bd9979fa18 (patch) | |
tree | 2a38a445499bdb2e42725eb7675875cae71979bb /src/gui | |
parent | 04e5b86f9e695e2ca4516179a214d2eff6a2157e (diff) |
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>
Diffstat (limited to 'src/gui')
-rw-r--r-- | src/gui/painting/qcolorspace.cpp | 32 | ||||
-rw-r--r-- | src/gui/painting/qcolorspace_p.h | 1 | ||||
-rw-r--r-- | src/gui/painting/qicc.cpp | 72 |
3 files changed, 87 insertions, 18 deletions
diff --git a/src/gui/painting/qcolorspace.cpp b/src/gui/painting/qcolorspace.cpp index 044468194a..3ef1ebbeb5 100644 --- a/src/gui/painting/qcolorspace.cpp +++ b/src/gui/painting/qcolorspace.cpp @@ -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; diff --git a/src/gui/painting/qcolorspace_p.h b/src/gui/painting/qcolorspace_p.h index ac09825a69..eaa557e02d 100644 --- a/src/gui/painting/qcolorspace_p.h +++ b/src/gui/painting/qcolorspace_p.h @@ -110,6 +110,7 @@ public: // Three component matrix data: QColorTrc trc[3]; QColorMatrix toXyz; + QColorMatrix chad; // Element list processing data: struct TransferElement { diff --git a/src/gui/painting/qicc.cpp b/src/gui/painting/qicc.cpp index 316ac352ee..8a3e8805b9 100644 --- a/src/gui/painting/qicc.cpp +++ b/src/gui/painting/qicc.cpp @@ -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) |