summaryrefslogtreecommitdiffstats
path: root/src/gui
diff options
context:
space:
mode:
authorAllan Sandfeld Jensen <allan.jensen@qt.io>2024-02-26 18:10:41 +0100
committerAllan Sandfeld Jensen <allan.jensen@qt.io>2024-04-05 18:40:47 +0200
commit48d582e0b7010532ea748ed3cde9b9bd9979fa18 (patch)
tree2a38a445499bdb2e42725eb7675875cae71979bb /src/gui
parent04e5b86f9e695e2ca4516179a214d2eff6a2157e (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.cpp32
-rw-r--r--src/gui/painting/qcolorspace_p.h1
-rw-r--r--src/gui/painting/qicc.cpp72
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)