Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qcolorspace.cpp
Go to the documentation of this file.
1// Copyright (C) 2020 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qcolorspace.h"
5#include "qcolorspace_p.h"
6
7#include "qcolortransform.h"
8#include "qcolorclut_p.h"
9#include "qcolormatrix_p.h"
11#include "qcolortransform_p.h"
12#include "qicc_p.h"
13
14#include <qatomic.h>
15#include <qmath.h>
16#include <qtransform.h>
17
18#include <qdebug.h>
19
21
23
24Q_CONSTINIT static QAtomicPointer<QColorSpacePrivate> s_predefinedColorspacePrivates[QColorSpace::Bt2100Hlg] = {};
26{
27 for (QAtomicPointer<QColorSpacePrivate> &ptr : s_predefinedColorspacePrivates) {
28 QColorSpacePrivate *prv = ptr.fetchAndStoreAcquire(nullptr);
29 if (prv && !prv->ref.deref())
30 delete prv;
31 }
32}
33
34Q_DESTRUCTOR_FUNCTION(cleanupPredefinedColorspaces)
35
37{
38 switch (primaries) {
40 redPoint = QPointF(0.640, 0.330);
41 greenPoint = QPointF(0.300, 0.600);
42 bluePoint = QPointF(0.150, 0.060);
43 whitePoint = QColorVector::D65Chromaticity();
44 break;
46 redPoint = QPointF(0.680, 0.320);
47 greenPoint = QPointF(0.265, 0.690);
48 bluePoint = QPointF(0.150, 0.060);
49 whitePoint = QColorVector::D65Chromaticity();
50 break;
52 redPoint = QPointF(0.640, 0.330);
53 greenPoint = QPointF(0.210, 0.710);
54 bluePoint = QPointF(0.150, 0.060);
55 whitePoint = QColorVector::D65Chromaticity();
56 break;
58 redPoint = QPointF(0.7347, 0.2653);
59 greenPoint = QPointF(0.1596, 0.8404);
60 bluePoint = QPointF(0.0366, 0.0001);
61 whitePoint = QColorVector::D50Chromaticity();
62 break;
64 redPoint = QPointF(0.708, 0.292);
65 greenPoint = QPointF(0.170, 0.797);
66 bluePoint = QPointF(0.131, 0.046);
67 whitePoint = QColorVector::D65Chromaticity();
68 break;
69 default:
70 Q_UNREACHABLE();
71 }
72}
73
75{
77 return false;
79 return false;
81 return false;
83 return false;
84 return true;
85}
86
88{
89 // This converts to XYZ in some undefined scale.
93
94 // Since the white point should be (1.0, 1.0, 1.0) in the
95 // input, we can figure out the scale by using the
96 // inverse conversion on the white point.
98 QColorVector whiteScale = toXyz.inverted().map(wXyz);
99
100 // Now we have scaled conversion to XYZ relative to the given whitepoint
101 toXyz = toXyz * QColorMatrix::fromScale(whiteScale);
102
103 return toXyz;
104}
105
109
111 : namedColorSpace(namedColorSpace)
112 , colorModel(QColorSpace::ColorModel::Rgb)
113{
114 switch (namedColorSpace) {
118 description = QStringLiteral("sRGB");
119 break;
123 description = QStringLiteral("Linear sRGB");
124 break;
128 gamma = 2.19921875f; // Not quite 2.2, see https://www.adobe.com/digitalimag/pdfs/AdobeRGB1998.pdf
129 description = QStringLiteral("Adobe RGB");
130 break;
134 description = QStringLiteral("Display P3");
135 break;
139 description = QStringLiteral("ProPhoto RGB");
140 break;
144 description = QStringLiteral("BT.2020");
145 break;
149 description = QStringLiteral("BT.2100(PQ)");
150 break;
154 description = QStringLiteral("BT.2100(HLG)");
155 break;
156 default:
157 Q_UNREACHABLE();
158 }
159 initialize();
160}
161
163 : primaries(primaries)
164 , transferFunction(transferFunction)
165 , colorModel(QColorSpace::ColorModel::Rgb)
166 , gamma(gamma)
167{
169 initialize();
170}
171
173 QColorSpace::TransferFunction transferFunction,
174 float gamma)
175 : primaries(QColorSpace::Primaries::Custom)
176 , transferFunction(transferFunction)
177 , colorModel(QColorSpace::ColorModel::Rgb)
178 , gamma(gamma)
179 , whitePoint(QColorVector::fromXYChromaticity(primaries.whitePoint))
180{
181 Q_ASSERT(primaries.areValid());
182 toXyz = primaries.toXyzMatrix();
184 toXyz = chad * toXyz;
185
188}
189
191 QColorSpace::TransferFunction transferFunction,
192 float gamma)
193 : primaries(QColorSpace::Primaries::Custom)
194 , transferFunction(transferFunction)
195 , colorModel(QColorSpace::ColorModel::Gray)
196 , gamma(gamma)
197 , whitePoint(QColorVector::fromXYChromaticity(whitePoint))
198{
199 chad = QColorMatrix::chromaticAdaptation(this->whitePoint);
200 toXyz = chad;
202}
203
204QColorSpacePrivate::QColorSpacePrivate(QPointF whitePoint, const QList<uint16_t> &transferFunctionTable)
205 : primaries(QColorSpace::Primaries::Custom)
206 , transferFunction(QColorSpace::TransferFunction::Custom)
207 , colorModel(QColorSpace::ColorModel::Gray)
208 , gamma(0)
209 , whitePoint(QColorVector::fromXYChromaticity(whitePoint))
210{
211 chad = QColorMatrix::chromaticAdaptation(this->whitePoint);
212 toXyz = chad;
213 setTransferFunctionTable(transferFunctionTable);
215}
216
217QColorSpacePrivate::QColorSpacePrivate(QColorSpace::Primaries primaries, const QList<uint16_t> &transferFunctionTable)
218 : primaries(primaries)
219 , transferFunction(QColorSpace::TransferFunction::Custom)
220 , colorModel(QColorSpace::ColorModel::Rgb)
221 , gamma(0)
222{
223 setTransferFunctionTable(transferFunctionTable);
225 initialize();
226}
227
228QColorSpacePrivate::QColorSpacePrivate(const QColorSpacePrimaries &primaries, const QList<uint16_t> &transferFunctionTable)
229 : primaries(QColorSpace::Primaries::Custom)
230 , transferFunction(QColorSpace::TransferFunction::Custom)
231 , colorModel(QColorSpace::ColorModel::Rgb)
232 , gamma(0)
233 , whitePoint(QColorVector::fromXYChromaticity(primaries.whitePoint))
234{
235 Q_ASSERT(primaries.areValid());
236 toXyz = primaries.toXyzMatrix();
238 toXyz = chad * toXyz;
239 setTransferFunctionTable(transferFunctionTable);
241 initialize();
242}
243
245 const QList<uint16_t> &redTransferFunctionTable,
246 const QList<uint16_t> &greenTransferFunctionTable,
247 const QList<uint16_t> &blueTransferFunctionTable)
248 : primaries(QColorSpace::Primaries::Custom)
249 , transferFunction(QColorSpace::TransferFunction::Custom)
250 , colorModel(QColorSpace::ColorModel::Rgb)
251 , gamma(0)
252{
253 Q_ASSERT(primaries.areValid());
254 toXyz = primaries.toXyzMatrix();
257 toXyz = chad * toXyz;
258 setTransferFunctionTables(redTransferFunctionTable,
259 greenTransferFunctionTable,
260 blueTransferFunctionTable);
262}
263
265{
266 switch (primaries) {
270 if (description.isEmpty())
271 description = QStringLiteral("sRGB");
272 return;
273 }
276 if (description.isEmpty())
277 description = QStringLiteral("Linear sRGB");
278 return;
279 }
280 break;
283 if (qAbs(gamma - 2.19921875f) < (1/1024.0f)) {
285 if (description.isEmpty())
286 description = QStringLiteral("Adobe RGB");
287 return;
288 }
289 }
290 break;
294 if (description.isEmpty())
295 description = QStringLiteral("Display P3");
296 return;
297 }
298 break;
302 if (description.isEmpty())
303 description = QStringLiteral("ProPhoto RGB");
304 return;
305 }
307 // ProPhoto RGB's curve is effectively gamma 1.8 for 8bit precision.
308 if (qAbs(gamma - 1.8f) < (1/1024.0f)) {
310 if (description.isEmpty())
311 description = QStringLiteral("ProPhoto RGB");
312 return;
313 }
314 }
315 break;
319 if (description.isEmpty())
320 description = QStringLiteral("BT.2020");
321 return;
322 }
325 if (description.isEmpty())
326 description = QStringLiteral("BT.2100(PQ)");
327 return;
328 }
331 if (description.isEmpty())
332 description = QStringLiteral("BT.2100(HLG)");
333 return;
334 }
335 break;
336 default:
337 break;
338 }
339
341}
342
348
350{
354 return;
355 }
356 QColorSpacePrimaries colorSpacePrimaries(primaries);
357 toXyz = colorSpacePrimaries.toXyzMatrix();
358 whitePoint = QColorVector::fromXYChromaticity(colorSpacePrimaries.whitePoint);
360 toXyz = chad * toXyz;
361}
362
363void QColorSpacePrivate::setTransferFunctionTable(const QList<uint16_t> &transferFunctionTable)
364{
365 QColorTransferTable table(transferFunctionTable.size(), transferFunctionTable);
366 if (!table.isEmpty() && !table.checkValidity()) {
367 qWarning() << "Invalid transfer function table given to QColorSpace";
368 trc[0].m_type = QColorTrc::Type::Uninitialized;
369 return;
370 }
373 if (table.asColorTransferFunction(&curve)) {
374 // Table recognized as a specific curve
375 if (curve.isIdentity()) {
377 gamma = 1.0f;
378 } else if (curve.isSRgb()) {
380 }
381 trc[0].m_type = QColorTrc::Type::ParameterizedFunction;
382 trc[0].m_fun = curve;
383 } else {
384 trc[0].m_type = QColorTrc::Type::Table;
385 trc[0].m_table = table;
386 }
387}
388
389void QColorSpacePrivate::setTransferFunctionTables(const QList<uint16_t> &redTransferFunctionTable,
390 const QList<uint16_t> &greenTransferFunctionTable,
391 const QList<uint16_t> &blueTransferFunctionTable)
392{
393 QColorTransferTable redTable(redTransferFunctionTable.size(), redTransferFunctionTable);
394 QColorTransferTable greenTable(greenTransferFunctionTable.size(), greenTransferFunctionTable);
395 QColorTransferTable blueTable(blueTransferFunctionTable.size(), blueTransferFunctionTable);
396 if (!redTable.isEmpty() && !greenTable.isEmpty() && !blueTable.isEmpty() &&
397 !redTable.checkValidity() && !greenTable.checkValidity() && !blueTable.checkValidity()) {
398 qWarning() << "Invalid transfer function table given to QColorSpace";
399 trc[0].m_type = QColorTrc::Type::Uninitialized;
400 trc[1].m_type = QColorTrc::Type::Uninitialized;
401 trc[2].m_type = QColorTrc::Type::Uninitialized;
402 return;
403 }
406 if (redTable.asColorTransferFunction(&curve)) {
407 trc[0].m_type = QColorTrc::Type::ParameterizedFunction;
408 trc[0].m_fun = curve;
409 } else {
410 trc[0].m_type = QColorTrc::Type::Table;
411 trc[0].m_table = redTable;
412 }
413 if (greenTable.asColorTransferFunction(&curve)) {
414 trc[1].m_type = QColorTrc::Type::ParameterizedFunction;
415 trc[1].m_fun = curve;
416 } else {
417 trc[1].m_type = QColorTrc::Type::Table;
418 trc[1].m_table = greenTable;
419 }
420 if (blueTable.asColorTransferFunction(&curve)) {
421 trc[2].m_type = QColorTrc::Type::ParameterizedFunction;
422 trc[2].m_fun = curve;
423 } else {
424 trc[2].m_type = QColorTrc::Type::Table;
425 trc[2].m_table = blueTable;
426 }
427 lut.generated.storeRelease(0);
428}
429
431{
432 switch (transferFunction) {
435 if (qFuzzyIsNull(gamma))
436 gamma = 1.0f;
437 break;
440 break;
443 if (qFuzzyIsNull(gamma))
444 gamma = 2.31f;
445 break;
448 if (qFuzzyIsNull(gamma))
449 gamma = 1.8f;
450 break;
453 if (qFuzzyIsNull(gamma))
454 gamma = 2.1f;
455 break;
458 break;
461 break;
463 break;
464 default:
465 Q_UNREACHABLE();
466 break;
467 }
468 trc[1] = trc[0];
469 trc[2] = trc[0];
470 lut.generated.storeRelease(0);
471}
472
474{
475 Q_ASSERT(out);
476 QColorTransform combined;
477 auto ptr = new QColorTransformPrivate;
478 combined.d = ptr;
479 ptr->colorSpaceIn = this;
480 ptr->colorSpaceOut = out;
482 ptr->colorMatrix = toXyz;
483 else
484 ptr->colorMatrix = QColorMatrix::identity();
485 if (out->isThreeComponentMatrix())
486 ptr->colorMatrix = out->toXyz.inverted() * ptr->colorMatrix;
487 if (ptr->isIdentity())
488 return QColorTransform();
489 return combined;
490}
491
493{
495 auto ptr = new QColorTransformPrivate;
496 transform.d = ptr;
497 ptr->colorSpaceIn = this;
498 ptr->colorSpaceOut = this;
500 ptr->colorMatrix = toXyz;
501 else
502 ptr->colorMatrix = QColorMatrix::identity();
503 // Convert to XYZ relative to our white point, not the regular D50 white point.
504 if (!chad.isNull())
505 ptr->colorMatrix = chad.inverted() * ptr->colorMatrix;
506 return transform;
507}
508
513
526
656{
657 if (namedColorSpace < QColorSpace::SRgb || namedColorSpace > QColorSpace::Bt2100Hlg) {
658 qWarning() << "QColorSpace attempted constructed from invalid QColorSpace::NamedColorSpace: " << int(namedColorSpace);
659 return;
660 }
661 // The defined namespaces start at 1:
662 auto &atomicRef = s_predefinedColorspacePrivates[static_cast<int>(namedColorSpace) - 1];
663 QColorSpacePrivate *cspriv = atomicRef.loadAcquire();
664 if (!cspriv) {
665 auto *tmp = new QColorSpacePrivate(namedColorSpace);
666 tmp->ref.ref();
667 if (atomicRef.testAndSetOrdered(nullptr, tmp, cspriv))
668 cspriv = tmp;
669 else
670 delete tmp;
671 }
672 d_ptr = cspriv;
673 Q_ASSERT(isValid());
674}
675
681 : d_ptr(new QColorSpacePrivate(primaries, transferFunction, gamma))
682{
683}
684
690 : d_ptr(new QColorSpacePrivate(primaries, TransferFunction::Gamma, gamma))
691{
692}
693
703QColorSpace::QColorSpace(QColorSpace::Primaries gamut, const QList<uint16_t> &transferFunctionTable)
704 : d_ptr(new QColorSpacePrivate(gamut, transferFunctionTable))
705{
706}
707
714QColorSpace::QColorSpace(QPointF whitePoint, TransferFunction transferFunction, float gamma)
715 : d_ptr(new QColorSpacePrivate(whitePoint, transferFunction, gamma))
716{
717}
718
725QColorSpace::QColorSpace(QPointF whitePoint, const QList<uint16_t> &transferFunctionTable)
726 : d_ptr(new QColorSpacePrivate(whitePoint, transferFunctionTable))
727{
728}
729
734QColorSpace::QColorSpace(const QPointF &whitePoint, const QPointF &redPoint,
735 const QPointF &greenPoint, const QPointF &bluePoint,
736 QColorSpace::TransferFunction transferFunction, float gamma)
737{
738 QColorSpacePrimaries primaries(whitePoint, redPoint, greenPoint, bluePoint);
739 if (!primaries.areValid()) {
740 qWarning() << "QColorSpace attempted constructed from invalid primaries:" << whitePoint << redPoint << greenPoint << bluePoint;
741 return;
742 }
744}
745
753QColorSpace::QColorSpace(const QPointF &whitePoint, const QPointF &redPoint,
754 const QPointF &greenPoint, const QPointF &bluePoint,
755 const QList<uint16_t> &transferFunctionTable)
756 : d_ptr(new QColorSpacePrivate({whitePoint, redPoint, greenPoint, bluePoint}, transferFunctionTable))
757{
758}
759
767QColorSpace::QColorSpace(const QPointF &whitePoint, const QPointF &redPoint,
768 const QPointF &greenPoint, const QPointF &bluePoint,
769 const QList<uint16_t> &redTransferFunctionTable,
770 const QList<uint16_t> &greenTransferFunctionTable,
771 const QList<uint16_t> &blueTransferFunctionTable)
772 : d_ptr(new QColorSpacePrivate({whitePoint, redPoint, greenPoint, bluePoint},
773 redTransferFunctionTable,
774 greenTransferFunctionTable,
775 blueTransferFunctionTable))
776{
777}
778
779QColorSpace::~QColorSpace() = default;
780
782
783QColorSpace::QColorSpace(const QColorSpace &colorSpace) noexcept = default;
784
796{
797 if (Q_UNLIKELY(!d_ptr))
799 return d_ptr->primaries;
800}
801
814
822float QColorSpace::gamma() const noexcept
823{
824 if (Q_UNLIKELY(!d_ptr))
825 return 0.0f;
826 return d_ptr->gamma;
827}
828
835{
837 return;
838 if (!d_ptr) {
840 return;
841 }
842 if (d_ptr->transferFunction == transferFunction && d_ptr->gamma == gamma)
843 return;
844 detach();
847 d_ptr->iccProfile = {};
848 d_ptr->description = QString();
850 d_ptr->gamma = gamma;
851 d_ptr->identifyColorSpace();
852 d_ptr->setTransferFunction();
853}
854
861void QColorSpace::setTransferFunction(const QList<uint16_t> &transferFunctionTable)
862{
863 if (!d_ptr) {
864 d_ptr = new QColorSpacePrivate(Primaries::Custom, transferFunctionTable);
865 d_ptr->ref.ref();
866 return;
867 }
868 detach();
871 d_ptr->iccProfile = {};
872 d_ptr->description = QString();
873 d_ptr->setTransferFunctionTable(transferFunctionTable);
874 d_ptr->gamma = 0;
875 d_ptr->identifyColorSpace();
876 d_ptr->setTransferFunction();
877}
878
886void QColorSpace::setTransferFunctions(const QList<uint16_t> &redTransferFunctionTable,
887 const QList<uint16_t> &greenTransferFunctionTable,
888 const QList<uint16_t> &blueTransferFunctionTable)
889{
890 if (!d_ptr) {
891 d_ptr = new QColorSpacePrivate();
892 d_ptr->setTransferFunctionTables(redTransferFunctionTable,
893 greenTransferFunctionTable,
894 blueTransferFunctionTable);
895 d_ptr->ref.ref();
896 return;
897 }
898 detach();
901 d_ptr->iccProfile = {};
902 d_ptr->description = QString();
903 d_ptr->setTransferFunctionTables(redTransferFunctionTable,
904 greenTransferFunctionTable,
905 blueTransferFunctionTable);
906 d_ptr->gamma = 0;
907 d_ptr->identifyColorSpace();
908}
909
917{
919 return *this;
920 if (d_ptr->transferFunction == transferFunction && d_ptr->gamma == gamma)
921 return *this;
922 QColorSpace out(*this);
923 out.setTransferFunction(transferFunction, gamma);
924 return out;
925}
926
934QColorSpace QColorSpace::withTransferFunction(const QList<uint16_t> &transferFunctionTable) const
935{
936 if (!isValid())
937 return *this;
938 QColorSpace out(*this);
939 out.setTransferFunction(transferFunctionTable);
940 return out;
941}
942
951QColorSpace QColorSpace::withTransferFunctions(const QList<uint16_t> &redTransferFunctionTable,
952 const QList<uint16_t> &greenTransferFunctionTable,
953 const QList<uint16_t> &blueTransferFunctionTable) const
954{
955 if (!isValid())
956 return *this;
957 QColorSpace out(*this);
958 out.setTransferFunctions(redTransferFunctionTable, greenTransferFunctionTable, blueTransferFunctionTable);
959 return out;
960}
961
968{
969 if (primariesId == Primaries::Custom)
970 return;
971 if (!d_ptr) {
972 d_ptr = new QColorSpacePrivate(primariesId, TransferFunction::Custom, 0.0f);
973 return;
974 }
975 if (d_ptr->primaries == primariesId)
976 return;
977 detach();
980 d_ptr->iccProfile = {};
981 d_ptr->description = QString();
982 d_ptr->primaries = primariesId;
984 d_ptr->identifyColorSpace();
985 d_ptr->setToXyzMatrix();
986}
987
994void QColorSpace::setPrimaries(const QPointF &whitePoint, const QPointF &redPoint,
995 const QPointF &greenPoint, const QPointF &bluePoint)
996{
997 QColorSpacePrimaries primaries(whitePoint, redPoint, greenPoint, bluePoint);
998 if (!primaries.areValid())
999 return;
1000 if (!d_ptr) {
1002 return;
1003 }
1004 QColorMatrix toXyz = primaries.toXyzMatrix();
1006 toXyz = chad * toXyz;
1007 if (QColorVector::fromXYChromaticity(primaries.whitePoint) == d_ptr->whitePoint
1008 && toXyz == d_ptr->toXyz && chad == d_ptr->chad)
1009 return;
1010 detach();
1013 d_ptr->iccProfile = {};
1014 d_ptr->description = QString();
1017 d_ptr->toXyz = toXyz;
1018 d_ptr->chad = chad;
1020 d_ptr->identifyColorSpace();
1021}
1022
1029{
1030 if (Q_UNLIKELY(!d_ptr))
1031 return QPointF();
1032 return d_ptr->whitePoint.toChromaticity();
1033}
1034
1041{
1042 if (Q_UNLIKELY(!d_ptr)) {
1044 return;
1045 }
1047 return;
1048 detach();
1051 d_ptr->iccProfile = {};
1052 d_ptr->description = QString();
1054 // An RGB color model stays RGB, a gray stays gray, but an undefined one can now be considered gray
1060 // Rescale toXyz to new whitepoint
1061 QColorMatrix rawToXyz = d_ptr->chad.inverted() * d_ptr->toXyz;
1062 QColorVector whiteScale = rawToXyz.inverted().map(wXyz);
1063 rawToXyz = rawToXyz * QColorMatrix::fromScale(whiteScale);
1065 d_ptr->toXyz = d_ptr->chad * rawToXyz;
1066 } else if (d_ptr->colorModel == QColorSpace::ColorModel::Gray) {
1067 d_ptr->chad = d_ptr->toXyz = QColorMatrix::chromaticAdaptation(wXyz);
1068 }
1069 }
1070 d_ptr->whitePoint = wXyz;
1071 d_ptr->identifyColorSpace();
1072}
1073
1085
1092{
1093 if (Q_UNLIKELY(!d_ptr))
1095 return d_ptr->colorModel;
1096}
1097
1102{
1103 if (d_ptr)
1104 d_ptr.detach();
1105 else
1106 d_ptr = new QColorSpacePrivate;
1107}
1108
1122{
1123 if (Q_UNLIKELY(!d_ptr))
1124 return QByteArray();
1125 if (!d_ptr->iccProfile.isEmpty())
1126 return d_ptr->iccProfile;
1127 if (!isValid())
1128 return QByteArray();
1129 return QIcc::toIccProfile(*this);
1130}
1131
1144{
1145 QColorSpace colorSpace;
1146 if (QIcc::fromIccProfile(iccProfile, &colorSpace))
1147 return colorSpace;
1148 colorSpace.detach();
1149 colorSpace.d_ptr->iccProfile = iccProfile;
1150 return colorSpace;
1151}
1152
1161bool QColorSpace::isValid() const noexcept
1162{
1163 if (!d_ptr)
1164 return false;
1165 return d_ptr->isValid();
1166}
1167
1173bool QColorSpace::isValidTarget() const noexcept
1174{
1175 if (!d_ptr)
1176 return false;
1177 if (!d_ptr->isThreeComponentMatrix())
1178 return !d_ptr->mBA.isEmpty();
1179 return d_ptr->isValid();
1180}
1181
1185bool QColorSpacePrivate::isValid() const noexcept
1186{
1188 return !mAB.isEmpty();
1189 if (!toXyz.isValid())
1190 return false;
1192 if (!trc[0].isValid())
1193 return false;
1195 if (!trc[0].isValid() || !trc[1].isValid() || !trc[2].isValid())
1196 return false;
1197 } else {
1198 return false;
1199 }
1200 return true;
1201}
1202
1219{
1220 return element.trc[0] == other.trc[0]
1221 && element.trc[1] == other.trc[1]
1222 && element.trc[2] == other.trc[2]
1223 && element.trc[3] == other.trc[3];
1224}
1225
1226static bool compareElement(const QColorMatrix &element,
1227 const QColorMatrix &other)
1228{
1229 return element == other;
1230}
1231
1232static bool compareElement(const QColorVector &element,
1233 const QColorVector &other)
1234{
1235 return element == other;
1236}
1237
1238static bool compareElement(const QColorCLUT &element,
1239 const QColorCLUT &other)
1240{
1241 if (element.gridPointsX != other.gridPointsX)
1242 return false;
1243 if (element.gridPointsY != other.gridPointsY)
1244 return false;
1245 if (element.gridPointsZ != other.gridPointsZ)
1246 return false;
1247 if (element.gridPointsW != other.gridPointsW)
1248 return false;
1249 if (element.table.size() != other.table.size())
1250 return false;
1251 for (qsizetype i = 0; i < element.table.size(); ++i) {
1252 if (element.table[i] != other.table[i])
1253 return false;
1254 }
1255 return true;
1256}
1257
1258template<typename T>
1259static bool compareElements(const T &element, const QColorSpacePrivate::Element &other)
1260{
1261 return compareElement(element, std::get<T>(other));
1262}
1263
1267bool QColorSpace::equals(const QColorSpace &other) const
1268{
1269 if (d_ptr == other.d_ptr)
1270 return true;
1271 if (!d_ptr)
1272 return false;
1273 return d_ptr->equals(other.d_ptr.constData());
1274}
1275
1280{
1281 if (!other)
1282 return false;
1283
1284 if (namedColorSpace && other->namedColorSpace)
1285 return namedColorSpace == other->namedColorSpace;
1286
1287 const bool valid1 = isValid();
1288 const bool valid2 = other->isValid();
1289 if (valid1 != valid2)
1290 return false;
1291 if (!valid1 && !valid2) {
1292 if (!iccProfile.isEmpty() || !other->iccProfile.isEmpty())
1293 return iccProfile == other->iccProfile;
1294 return false;
1295 }
1296
1297 // At this point one or both color spaces are unknown, and must be compared in detail instead
1298
1299 if (transformModel != other->transformModel)
1300 return false;
1301
1302 if (!isThreeComponentMatrix()) {
1303 if (isPcsLab != other->isPcsLab)
1304 return false;
1305 if (colorModel != other->colorModel)
1306 return false;
1307 if (mAB.count() != other->mAB.count())
1308 return false;
1309 if (mBA.count() != other->mBA.count())
1310 return false;
1311
1312 // Compare element types
1313 for (qsizetype i = 0; i < mAB.count(); ++i) {
1314 if (mAB[i].index() != other->mAB[i].index())
1315 return false;
1316 }
1317 for (qsizetype i = 0; i < mBA.count(); ++i) {
1318 if (mBA[i].index() != other->mBA[i].index())
1319 return false;
1320 }
1321
1322 // Compare element contents
1323 for (qsizetype i = 0; i < mAB.count(); ++i) {
1324 if (!std::visit([&](auto &&elm) { return compareElements(elm, other->mAB[i]); }, mAB[i]))
1325 return false;
1326 }
1327 for (qsizetype i = 0; i < mBA.count(); ++i) {
1328 if (!std::visit([&](auto &&elm) { return compareElements(elm, other->mBA[i]); }, mBA[i]))
1329 return false;
1330 }
1331
1332 return true;
1333 }
1334
1336 if (primaries != other->primaries)
1337 return false;
1338 } else {
1339 if (toXyz != other->toXyz)
1340 return false;
1341 }
1342
1344 if (transferFunction != other->transferFunction)
1345 return false;
1347 return (qAbs(gamma - other->gamma) <= (1.0f / 512.0f));
1348 return true;
1349 }
1350
1351 if (trc[0] != other->trc[0] ||
1352 trc[1] != other->trc[1] ||
1353 trc[2] != other->trc[2])
1354 return false;
1355
1356 return true;
1357}
1358
1364{
1365 if (!isValid())
1366 return QColorTransform();
1367
1368 if (*this == colorspace)
1369 return QColorTransform();
1370 if (!colorspace.isValidTarget()) {
1371 qWarning() << "QColorSpace::transformationToColorSpace: colorspace not a valid target";
1372 return QColorTransform();
1373 }
1374
1375 return d_ptr->transformationToColorSpace(colorspace.d_ptr.get());
1376}
1377
1382QColorSpace::operator QVariant() const
1383{
1384 return QVariant::fromValue(*this);
1385}
1386
1396{
1397 if (d_ptr)
1398 return d_ptr->userDescription.isEmpty() ? d_ptr->description : d_ptr->userDescription;
1399 return QString();
1400}
1401
1410void QColorSpace::setDescription(const QString &description)
1411{
1412 detach();
1413 d_ptr->iccProfile = {};
1415}
1416
1417/*****************************************************************************
1418 QColorSpace stream functions
1419 *****************************************************************************/
1420#if !defined(QT_NO_DATASTREAM)
1431{
1432 s << image.iccProfile();
1433 return s;
1434}
1435
1447{
1448 QByteArray iccProfile;
1449 s >> iccProfile;
1450 colorSpace = QColorSpace::fromIccProfile(iccProfile);
1451 return s;
1452}
1453#endif // QT_NO_DATASTREAM
1454
1455#ifndef QT_NO_DEBUG_STREAM
1457{
1458 return dbg << ":Transfer";
1459}
1461{
1462 return dbg << ":Matrix";
1463}
1465{
1466 return dbg << ":Offset";
1467}
1469{
1470 return dbg << ":CLUT";
1471}
1472QDebug operator<<(QDebug dbg, const QList<QColorSpacePrivate::Element> &elements)
1473{
1474 for (auto &&element : elements)
1475 std::visit([&](auto &&elm) { dbg << elm; }, element);
1476 return dbg;
1477}
1478QDebug operator<<(QDebug dbg, const QColorSpace &colorSpace)
1479{
1480 QDebugStateSaver saver(dbg);
1481 dbg.nospace();
1482 dbg << "QColorSpace(";
1483 if (colorSpace.d_ptr) {
1484 if (colorSpace.d_ptr->namedColorSpace)
1485 dbg << colorSpace.d_ptr->namedColorSpace << ", ";
1486 else
1487 dbg << colorSpace.colorModel() << ", ";
1488 if (!colorSpace.isValid()) {
1489 dbg << "Invalid";
1490 if (!colorSpace.d_ptr->iccProfile.isEmpty())
1491 dbg << " with profile data";
1492 } else if (colorSpace.d_ptr->isThreeComponentMatrix()) {
1493 dbg << colorSpace.primaries() << ", " << colorSpace.transferFunction();
1495 dbg << "=" << colorSpace.gamma();
1496 } else {
1497 if (colorSpace.d_ptr->isPcsLab)
1498 dbg << "PCSLab, ";
1499 else
1500 dbg << "PCSXYZ, ";
1501 dbg << "A2B" << colorSpace.d_ptr->mAB;
1502 if (!colorSpace.d_ptr->mBA.isEmpty())
1503 dbg << ", B2A" << colorSpace.d_ptr->mBA;
1504 }
1505 }
1506 dbg << ')';
1507 return dbg;
1508}
1509#endif
1510
1512
1513#include "moc_qcolorspace.cpp"
bool ref() noexcept
\inmodule QtCore
Definition qbytearray.h:57
bool isEmpty() const noexcept
Returns true if the byte array has size 0; otherwise returns false.
Definition qbytearray.h:107
QList< QColorVector > table
uint32_t gridPointsY
uint32_t gridPointsW
uint32_t gridPointsX
uint32_t gridPointsZ
QColorMatrix inverted() const
bool isValid() const
static QColorMatrix identity()
static QColorMatrix fromScale(QColorVector v)
static QColorMatrix chromaticAdaptation(const QColorVector &whitePoint)
QColorVector map(const QColorVector &c) const
constexpr bool isNull() const
QColorMatrix toXyzMatrix() const
bool areValid() const
void setTransferFunctionTables(const QList< uint16_t > &redTransferFunctionTable, const QList< uint16_t > &greenTransferFunctionTable, const QList< uint16_t > &blueTransferFunctionTable)
QColorVector whitePoint
QColorTransform transformationToColorSpace(const QColorSpacePrivate *out) const
QList< Element > mAB
QList< Element > mBA
QColorSpace::TransformModel transformModel
void clearElementListProcessingForEdit()
QColorSpace::NamedColorSpace namedColorSpace
void setTransferFunctionTable(const QList< uint16_t > &transferFunctionTable)
QColorTransform transformationToXYZ() const
bool isThreeComponentMatrix() const
static Q_CONSTINIT QBasicMutex s_lutWriteLock
bool isValid() const noexcept
struct QColorSpacePrivate::LUT lut
QColorSpace::Primaries primaries
QColorSpace::TransferFunction transferFunction
QColorSpace::ColorModel colorModel
std::variant< TransferElement, QColorMatrix, QColorVector, QColorCLUT > Element
static constexpr QColorSpace::NamedColorSpace Unknown
bool equals(const QColorSpacePrivate *other) const
The QColorSpace class provides a color space abstraction.
Definition qcolorspace.h:21
Primaries primaries() const noexcept
Returns the predefined primaries of the color space or primaries::Custom if it doesn't match any of t...
friend class QColorSpacePrivate
TransformModel transformModel() const noexcept
Returns the transfrom processing model used for this color space.
Primaries
Predefined sets of primary colors.
Definition qcolorspace.h:35
QColorTransform transformationToColorSpace(const QColorSpace &colorspace) const
Generates and returns a color space transformation from this color space to colorspace.
bool isValid() const noexcept
Returns true if the color space is valid.
QColorSpace withTransferFunctions(const QList< uint16_t > &redTransferFunctionTable, const QList< uint16_t > &greenTransferFunctionTable, const QList< uint16_t > &blueTransferFunctionTable) const
Returns a copy of this color space, except using the transfer functions described by redTransferFunct...
void setTransferFunction(TransferFunction transferFunction, float gamma=0.0f)
Sets the transfer function to transferFunction and gamma.
float gamma() const noexcept
Returns the gamma value of color spaces with TransferFunction::Gamma, an approximate gamma value for ...
static QColorSpace fromIccProfile(const QByteArray &iccProfile)
Creates a QColorSpace from ICC profile iccProfile.
TransferFunction transferFunction() const noexcept
Returns the predefined transfer function of the color space or TransferFunction::Custom if it doesn't...
NamedColorSpace
Predefined color spaces.
Definition qcolorspace.h:24
void setTransferFunctions(const QList< uint16_t > &redTransferFunctionTable, const QList< uint16_t > &greenTransferFunctionTable, const QList< uint16_t > &blueTransferFunctionTable)
Sets the transfer functions to redTransferFunctionTable, greenTransferFunctionTable and blueTransferF...
void setPrimaries(Primaries primariesId)
Sets the primaries to those of the primariesId set.
ColorModel colorModel() const noexcept
Returns the color model this color space can represent.
void setDescription(const QString &description)
Sets the name or short description of the color space to description.
TransferFunction
Predefined transfer functions or gamma curves.
Definition qcolorspace.h:44
void setWhitePoint(QPointF whitePoint)
Sets the white point to used for this color space to whitePoint.
QColorSpace() noexcept=default
Creates a new colorspace object that represents an undefined and invalid colorspace.
QColorSpace withTransferFunction(TransferFunction transferFunction, float gamma=0.0f) const
Returns a copy of this color space, except using the transfer function transferFunction and gamma.
bool isValidTarget() const noexcept
QPointF whitePoint() const
Returns the white point used for this color space.
QString description() const noexcept
Returns the name or short description.
QByteArray iccProfile() const
Returns an ICC profile representing the color space.
static QColorTransferFunction fromGamma(float gamma)
static QColorTransferFunction fromBt2020()
static QColorTransferFunction fromProPhotoRgb()
static QColorTransferFunction fromSRgb()
static QColorTransferGenericFunction pq()
static QColorTransferGenericFunction hlg()
The QColorTransform class is a transformation between color spaces.
QColorTransferFunction m_fun
QColorTransferTable m_table
static constexpr QColorVector D50()
static constexpr QPointF D65Chromaticity()
static constexpr bool isValidChromaticity(const QPointF &chr)
static constexpr QColorVector fromXYChromaticity(QPointF chr)
QPointF toChromaticity() const
static constexpr QPointF D50Chromaticity()
\inmodule QtCore\reentrant
Definition qdatastream.h:47
\inmodule QtCore
\inmodule QtCore
void detach()
If the shared data object's reference count is greater than 1, this function creates a deep copy of t...
qsizetype size() const noexcept
Definition qlist.h:398
bool isEmpty() const noexcept
Definition qlist.h:402
qsizetype count() const noexcept
Definition qlist.h:399
void clear()
Definition qlist.h:435
\inmodule QtCore
Definition qmutex.h:281
\inmodule QtCore\reentrant
Definition qpoint.h:217
QAtomicInt ref
Definition qshareddata.h:21
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
bool isEmpty() const noexcept
Returns true if the string has no characters; otherwise returns false.
Definition qstring.h:192
\inmodule QtCore
Definition qvariant.h:65
static auto fromValue(T &&value) noexcept(std::is_nothrow_copy_constructible_v< T > &&Private::CanUseInternalSpace< T >) -> std::enable_if_t< std::conjunction_v< std::is_copy_constructible< T >, std::is_destructible< T > >, QVariant >
Definition qvariant.h:537
bool fromIccProfile(const QByteArray &data, QColorSpace *colorSpace)
Definition qicc.cpp:1849
QByteArray toIccProfile(const QColorSpace &space)
Definition qicc.cpp:766
Combined button and popup list for selecting options.
Definition image.cpp:4
static void cleanupPredefinedColorspaces()
static bool compareElement(const QColorSpacePrivate::TransferElement &element, const QColorSpacePrivate::TransferElement &other)
QDataStream & operator>>(QDataStream &s, QColorSpace &colorSpace)
QDataStream & operator<<(QDataStream &s, const QColorSpace &image)
static bool compareElements(const T &element, const QColorSpacePrivate::Element &other)
static Q_CONSTINIT QAtomicPointer< QColorSpacePrivate > s_predefinedColorspacePrivates[QColorSpace::Bt2100Hlg]
#define Q_UNLIKELY(x)
typedef QByteArray(EGLAPIENTRYP PFNQGSGETDISPLAYSPROC)()
bool qFuzzyIsNull(qfloat16 f) noexcept
Definition qfloat16.h:349
#define qWarning
Definition qlogging.h:167
static ControlElement< T > * ptr(QWidget *widget)
constexpr T qAbs(const T &t)
Definition qnumeric.h:328
GLuint index
[2]
GLuint GLenum GLenum transform
GLdouble s
[6]
Definition qopenglext.h:235
GLenum GLenum GLsizei void * table
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define QT_DEFINE_QESDP_SPECIALIZATION_DTOR(Class)
#define QStringLiteral(str)
static const QTextHtmlElement elements[Html_NumElements]
ptrdiff_t qsizetype
Definition qtypes.h:165
QTextStream out(stdout)
[7]
QSharedPointer< T > other(t)
[5]