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::ProPhotoRgb] = {};
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;
63 default:
64 Q_UNREACHABLE();
65 }
66}
67
69{
71 return false;
73 return false;
75 return false;
77 return false;
78 return true;
79}
80
82{
83 // This converts to XYZ in some undefined scale.
87
88 // Since the white point should be (1.0, 1.0, 1.0) in the
89 // input, we can figure out the scale by using the
90 // inverse conversion on the white point.
92 QColorVector whiteScale = toXyz.inverted().map(wXyz);
93
94 // Now we have scaled conversion to XYZ relative to the given whitepoint
95 toXyz = toXyz * QColorMatrix::fromScale(whiteScale);
96
97 return toXyz;
98}
99
103
105 : namedColorSpace(namedColorSpace)
106 , colorModel(QColorSpace::ColorModel::Rgb)
107{
108 switch (namedColorSpace) {
112 description = QStringLiteral("sRGB");
113 break;
117 description = QStringLiteral("Linear sRGB");
118 break;
122 gamma = 2.19921875f; // Not quite 2.2, see https://www.adobe.com/digitalimag/pdfs/AdobeRGB1998.pdf
123 description = QStringLiteral("Adobe RGB");
124 break;
128 description = QStringLiteral("Display P3");
129 break;
133 description = QStringLiteral("ProPhoto RGB");
134 break;
135 default:
136 Q_UNREACHABLE();
137 }
138 initialize();
139}
140
142 : primaries(primaries)
143 , transferFunction(transferFunction)
144 , colorModel(QColorSpace::ColorModel::Rgb)
145 , gamma(gamma)
146{
147 identifyColorSpace();
148 initialize();
149}
150
152 QColorSpace::TransferFunction transferFunction,
153 float gamma)
154 : primaries(QColorSpace::Primaries::Custom)
155 , transferFunction(transferFunction)
156 , colorModel(QColorSpace::ColorModel::Rgb)
157 , gamma(gamma)
158 , whitePoint(QColorVector::fromXYChromaticity(primaries.whitePoint))
159{
160 Q_ASSERT(primaries.areValid());
161 toXyz = primaries.toXyzMatrix();
163 toXyz = chad * toXyz;
164
167}
168
170 QColorSpace::TransferFunction transferFunction,
171 float gamma)
172 : primaries(QColorSpace::Primaries::Custom)
173 , transferFunction(transferFunction)
174 , colorModel(QColorSpace::ColorModel::Gray)
175 , gamma(gamma)
176 , whitePoint(QColorVector::fromXYChromaticity(whitePoint))
177{
178 chad = QColorMatrix::chromaticAdaptation(this->whitePoint);
179 toXyz = chad;
181}
182
183QColorSpacePrivate::QColorSpacePrivate(const QPointF &whitePoint, const QList<uint16_t> &transferFunctionTable)
184 : primaries(QColorSpace::Primaries::Custom)
185 , transferFunction(QColorSpace::TransferFunction::Custom)
186 , colorModel(QColorSpace::ColorModel::Gray)
187 , gamma(0)
188 , whitePoint(QColorVector::fromXYChromaticity(whitePoint))
189{
190 chad = QColorMatrix::chromaticAdaptation(this->whitePoint);
191 toXyz = chad;
192 setTransferFunctionTable(transferFunctionTable);
194}
195
196QColorSpacePrivate::QColorSpacePrivate(QColorSpace::Primaries primaries, const QList<uint16_t> &transferFunctionTable)
197 : primaries(primaries)
198 , transferFunction(QColorSpace::TransferFunction::Custom)
199 , colorModel(QColorSpace::ColorModel::Rgb)
200 , gamma(0)
201{
202 setTransferFunctionTable(transferFunctionTable);
204 initialize();
205}
206
207QColorSpacePrivate::QColorSpacePrivate(const QColorSpacePrimaries &primaries, const QList<uint16_t> &transferFunctionTable)
208 : primaries(QColorSpace::Primaries::Custom)
209 , transferFunction(QColorSpace::TransferFunction::Custom)
210 , colorModel(QColorSpace::ColorModel::Rgb)
211 , gamma(0)
212 , whitePoint(QColorVector::fromXYChromaticity(primaries.whitePoint))
213{
214 Q_ASSERT(primaries.areValid());
215 toXyz = primaries.toXyzMatrix();
217 toXyz = chad * toXyz;
218 setTransferFunctionTable(transferFunctionTable);
220 initialize();
221}
222
224 const QList<uint16_t> &redTransferFunctionTable,
225 const QList<uint16_t> &greenTransferFunctionTable,
226 const QList<uint16_t> &blueTransferFunctionTable)
227 : primaries(QColorSpace::Primaries::Custom)
228 , transferFunction(QColorSpace::TransferFunction::Custom)
229 , colorModel(QColorSpace::ColorModel::Rgb)
230 , gamma(0)
231{
232 Q_ASSERT(primaries.areValid());
233 toXyz = primaries.toXyzMatrix();
236 toXyz = chad * toXyz;
237 setTransferFunctionTables(redTransferFunctionTable,
238 greenTransferFunctionTable,
239 blueTransferFunctionTable);
241}
242
244{
245 switch (primaries) {
249 if (description.isEmpty())
250 description = QStringLiteral("sRGB");
251 return;
252 }
255 if (description.isEmpty())
256 description = QStringLiteral("Linear sRGB");
257 return;
258 }
259 break;
262 if (qAbs(gamma - 2.19921875f) < (1/1024.0f)) {
264 if (description.isEmpty())
265 description = QStringLiteral("Adobe RGB");
266 return;
267 }
268 }
269 break;
273 if (description.isEmpty())
274 description = QStringLiteral("Display P3");
275 return;
276 }
277 break;
281 if (description.isEmpty())
282 description = QStringLiteral("ProPhoto RGB");
283 return;
284 }
286 // ProPhoto RGB's curve is effectively gamma 1.8 for 8bit precision.
287 if (qAbs(gamma - 1.8f) < (1/1024.0f)) {
289 if (description.isEmpty())
290 description = QStringLiteral("ProPhoto RGB");
291 return;
292 }
293 }
294 break;
295 default:
296 break;
297 }
298
300}
301
307
309{
313 return;
314 }
315 QColorSpacePrimaries colorSpacePrimaries(primaries);
316 toXyz = colorSpacePrimaries.toXyzMatrix();
317 whitePoint = QColorVector::fromXYChromaticity(colorSpacePrimaries.whitePoint);
319 toXyz = chad * toXyz;
320}
321
322void QColorSpacePrivate::setTransferFunctionTable(const QList<uint16_t> &transferFunctionTable)
323{
324 QColorTransferTable table(transferFunctionTable.size(), transferFunctionTable);
325 if (!table.isEmpty() && !table.checkValidity()) {
326 qWarning() << "Invalid transfer function table given to QColorSpace";
327 trc[0].m_type = QColorTrc::Type::Uninitialized;
328 return;
329 }
332 if (table.asColorTransferFunction(&curve)) {
333 // Table recognized as a specific curve
334 if (curve.isIdentity()) {
336 gamma = 1.0f;
337 } else if (curve.isSRgb()) {
339 }
340 trc[0].m_type = QColorTrc::Type::Function;
341 trc[0].m_fun = curve;
342 } else {
343 trc[0].m_type = QColorTrc::Type::Table;
344 trc[0].m_table = table;
345 }
346}
347
348void QColorSpacePrivate::setTransferFunctionTables(const QList<uint16_t> &redTransferFunctionTable,
349 const QList<uint16_t> &greenTransferFunctionTable,
350 const QList<uint16_t> &blueTransferFunctionTable)
351{
352 QColorTransferTable redTable(redTransferFunctionTable.size(), redTransferFunctionTable);
353 QColorTransferTable greenTable(greenTransferFunctionTable.size(), greenTransferFunctionTable);
354 QColorTransferTable blueTable(blueTransferFunctionTable.size(), blueTransferFunctionTable);
355 if (!redTable.isEmpty() && !greenTable.isEmpty() && !blueTable.isEmpty() &&
356 !redTable.checkValidity() && !greenTable.checkValidity() && !blueTable.checkValidity()) {
357 qWarning() << "Invalid transfer function table given to QColorSpace";
358 trc[0].m_type = QColorTrc::Type::Uninitialized;
359 trc[1].m_type = QColorTrc::Type::Uninitialized;
360 trc[2].m_type = QColorTrc::Type::Uninitialized;
361 return;
362 }
365 if (redTable.asColorTransferFunction(&curve)) {
366 trc[0].m_type = QColorTrc::Type::Function;
367 trc[0].m_fun = curve;
368 } else {
369 trc[0].m_type = QColorTrc::Type::Table;
370 trc[0].m_table = redTable;
371 }
372 if (greenTable.asColorTransferFunction(&curve)) {
373 trc[1].m_type = QColorTrc::Type::Function;
374 trc[1].m_fun = curve;
375 } else {
376 trc[1].m_type = QColorTrc::Type::Table;
377 trc[1].m_table = greenTable;
378 }
379 if (blueTable.asColorTransferFunction(&curve)) {
380 trc[2].m_type = QColorTrc::Type::Function;
381 trc[2].m_fun = curve;
382 } else {
383 trc[2].m_type = QColorTrc::Type::Table;
384 trc[2].m_table = blueTable;
385 }
386 lut.generated.storeRelease(0);
387}
388
390{
391 switch (transferFunction) {
393 trc[0].m_type = QColorTrc::Type::Function;
395 if (qFuzzyIsNull(gamma))
396 gamma = 1.0f;
397 break;
399 trc[0].m_type = QColorTrc::Type::Function;
401 break;
403 trc[0].m_type = QColorTrc::Type::Function;
405 if (qFuzzyIsNull(gamma))
406 gamma = 2.31f;
407 break;
409 trc[0].m_type = QColorTrc::Type::Function;
411 if (qFuzzyIsNull(gamma))
412 gamma = 1.8f;
413 break;
415 break;
416 default:
417 Q_UNREACHABLE();
418 break;
419 }
420 trc[1] = trc[0];
421 trc[2] = trc[0];
422 lut.generated.storeRelease(0);
423}
424
426{
427 Q_ASSERT(out);
428 QColorTransform combined;
429 auto ptr = new QColorTransformPrivate;
430 combined.d = ptr;
431 ptr->colorSpaceIn = this;
432 ptr->colorSpaceOut = out;
434 ptr->colorMatrix = toXyz;
435 else
436 ptr->colorMatrix = QColorMatrix::identity();
437 if (out->isThreeComponentMatrix())
438 ptr->colorMatrix = out->toXyz.inverted() * ptr->colorMatrix;
439 if (ptr->isIdentity())
440 return QColorTransform();
441 return combined;
442}
443
445{
447 auto ptr = new QColorTransformPrivate;
448 transform.d = ptr;
449 ptr->colorSpaceIn = this;
450 ptr->colorSpaceOut = this;
452 ptr->colorMatrix = toXyz;
453 else
454 ptr->colorMatrix = QColorMatrix::identity();
455 // Convert to XYZ relative to our white point, not the regular D50 white point.
456 if (!chad.isNull())
457 ptr->colorMatrix = chad.inverted() * ptr->colorMatrix;
458 else if (!whitePoint.isNull())
459 ptr->colorMatrix = QColorMatrix::chromaticAdaptation(whitePoint).inverted() * ptr->colorMatrix;
460 return transform;
461}
462
467
480
598{
599 if (namedColorSpace < QColorSpace::SRgb || namedColorSpace > QColorSpace::ProPhotoRgb) {
600 qWarning() << "QColorSpace attempted constructed from invalid QColorSpace::NamedColorSpace: " << int(namedColorSpace);
601 return;
602 }
603 // The defined namespaces start at 1:
604 auto &atomicRef = s_predefinedColorspacePrivates[static_cast<int>(namedColorSpace) - 1];
605 QColorSpacePrivate *cspriv = atomicRef.loadAcquire();
606 if (!cspriv) {
607 auto *tmp = new QColorSpacePrivate(namedColorSpace);
608 tmp->ref.ref();
609 if (atomicRef.testAndSetOrdered(nullptr, tmp, cspriv))
610 cspriv = tmp;
611 else
612 delete tmp;
613 }
614 d_ptr = cspriv;
615 Q_ASSERT(isValid());
616}
617
623 : d_ptr(new QColorSpacePrivate(primaries, transferFunction, gamma))
624{
625}
626
632 : d_ptr(new QColorSpacePrivate(primaries, TransferFunction::Gamma, gamma))
633{
634}
635
645QColorSpace::QColorSpace(QColorSpace::Primaries gamut, const QList<uint16_t> &transferFunctionTable)
646 : d_ptr(new QColorSpacePrivate(gamut, transferFunctionTable))
647{
648}
649
656QColorSpace::QColorSpace(const QPointF &whitePoint, TransferFunction transferFunction, float gamma)
657 : d_ptr(new QColorSpacePrivate(whitePoint, transferFunction, gamma))
658{
659}
660
667QColorSpace::QColorSpace(const QPointF &whitePoint, const QList<uint16_t> &transferFunctionTable)
668 : d_ptr(new QColorSpacePrivate(whitePoint, transferFunctionTable))
669{
670}
671
676QColorSpace::QColorSpace(const QPointF &whitePoint, const QPointF &redPoint,
677 const QPointF &greenPoint, const QPointF &bluePoint,
678 QColorSpace::TransferFunction transferFunction, float gamma)
679{
680 QColorSpacePrimaries primaries(whitePoint, redPoint, greenPoint, bluePoint);
681 if (!primaries.areValid()) {
682 qWarning() << "QColorSpace attempted constructed from invalid primaries:" << whitePoint << redPoint << greenPoint << bluePoint;
683 return;
684 }
686}
687
695QColorSpace::QColorSpace(const QPointF &whitePoint, const QPointF &redPoint,
696 const QPointF &greenPoint, const QPointF &bluePoint,
697 const QList<uint16_t> &transferFunctionTable)
698 : d_ptr(new QColorSpacePrivate({whitePoint, redPoint, greenPoint, bluePoint}, transferFunctionTable))
699{
700}
701
709QColorSpace::QColorSpace(const QPointF &whitePoint, const QPointF &redPoint,
710 const QPointF &greenPoint, const QPointF &bluePoint,
711 const QList<uint16_t> &redTransferFunctionTable,
712 const QList<uint16_t> &greenTransferFunctionTable,
713 const QList<uint16_t> &blueTransferFunctionTable)
714 : d_ptr(new QColorSpacePrivate({whitePoint, redPoint, greenPoint, bluePoint},
715 redTransferFunctionTable,
716 greenTransferFunctionTable,
717 blueTransferFunctionTable))
718{
719}
720
721QColorSpace::~QColorSpace() = default;
722
724
725QColorSpace::QColorSpace(const QColorSpace &colorSpace) noexcept = default;
726
738{
739 if (Q_UNLIKELY(!d_ptr))
741 return d_ptr->primaries;
742}
743
756
764float QColorSpace::gamma() const noexcept
765{
766 if (Q_UNLIKELY(!d_ptr))
767 return 0.0f;
768 return d_ptr->gamma;
769}
770
777{
779 return;
780 if (!d_ptr) {
782 return;
783 }
784 if (d_ptr->transferFunction == transferFunction && d_ptr->gamma == gamma)
785 return;
786 detach();
789 d_ptr->iccProfile = {};
790 d_ptr->description = QString();
792 d_ptr->gamma = gamma;
793 d_ptr->identifyColorSpace();
794 d_ptr->setTransferFunction();
795}
796
803void QColorSpace::setTransferFunction(const QList<uint16_t> &transferFunctionTable)
804{
805 if (!d_ptr) {
806 d_ptr = new QColorSpacePrivate(Primaries::Custom, transferFunctionTable);
807 d_ptr->ref.ref();
808 return;
809 }
810 detach();
813 d_ptr->iccProfile = {};
814 d_ptr->description = QString();
815 d_ptr->setTransferFunctionTable(transferFunctionTable);
816 d_ptr->gamma = 0;
817 d_ptr->identifyColorSpace();
818 d_ptr->setTransferFunction();
819}
820
828void QColorSpace::setTransferFunctions(const QList<uint16_t> &redTransferFunctionTable,
829 const QList<uint16_t> &greenTransferFunctionTable,
830 const QList<uint16_t> &blueTransferFunctionTable)
831{
832 if (!d_ptr) {
833 d_ptr = new QColorSpacePrivate();
834 d_ptr->setTransferFunctionTables(redTransferFunctionTable,
835 greenTransferFunctionTable,
836 blueTransferFunctionTable);
837 d_ptr->ref.ref();
838 return;
839 }
840 detach();
843 d_ptr->iccProfile = {};
844 d_ptr->description = QString();
845 d_ptr->setTransferFunctionTables(redTransferFunctionTable,
846 greenTransferFunctionTable,
847 blueTransferFunctionTable);
848 d_ptr->gamma = 0;
849 d_ptr->identifyColorSpace();
850}
851
859{
861 return *this;
862 if (d_ptr->transferFunction == transferFunction && d_ptr->gamma == gamma)
863 return *this;
864 QColorSpace out(*this);
865 out.setTransferFunction(transferFunction, gamma);
866 return out;
867}
868
876QColorSpace QColorSpace::withTransferFunction(const QList<uint16_t> &transferFunctionTable) const
877{
878 if (!isValid())
879 return *this;
880 QColorSpace out(*this);
881 out.setTransferFunction(transferFunctionTable);
882 return out;
883}
884
893QColorSpace QColorSpace::withTransferFunctions(const QList<uint16_t> &redTransferFunctionTable,
894 const QList<uint16_t> &greenTransferFunctionTable,
895 const QList<uint16_t> &blueTransferFunctionTable) const
896{
897 if (!isValid())
898 return *this;
899 QColorSpace out(*this);
900 out.setTransferFunctions(redTransferFunctionTable, greenTransferFunctionTable, blueTransferFunctionTable);
901 return out;
902}
903
910{
911 if (primariesId == Primaries::Custom)
912 return;
913 if (!d_ptr) {
914 d_ptr = new QColorSpacePrivate(primariesId, TransferFunction::Custom, 0.0f);
915 return;
916 }
917 if (d_ptr->primaries == primariesId)
918 return;
919 detach();
922 d_ptr->iccProfile = {};
923 d_ptr->description = QString();
924 d_ptr->primaries = primariesId;
926 d_ptr->identifyColorSpace();
927 d_ptr->setToXyzMatrix();
928}
929
936void QColorSpace::setPrimaries(const QPointF &whitePoint, const QPointF &redPoint,
937 const QPointF &greenPoint, const QPointF &bluePoint)
938{
939 QColorSpacePrimaries primaries(whitePoint, redPoint, greenPoint, bluePoint);
940 if (!primaries.areValid())
941 return;
942 if (!d_ptr) {
944 return;
945 }
946 QColorMatrix toXyz = primaries.toXyzMatrix();
948 toXyz = chad * toXyz;
950 && toXyz == d_ptr->toXyz && chad == d_ptr->chad)
951 return;
952 detach();
955 d_ptr->iccProfile = {};
956 d_ptr->description = QString();
959 d_ptr->toXyz = toXyz;
960 d_ptr->chad = chad;
962 d_ptr->identifyColorSpace();
963}
964
971{
972 if (Q_UNLIKELY(!d_ptr))
973 return QPointF();
974 return d_ptr->whitePoint.toChromaticity();
975}
976
982void QColorSpace::setWhitePoint(const QPointF &whitePoint)
983{
984 if (Q_UNLIKELY(!d_ptr)) {
986 return;
987 }
989 return;
990 detach();
993 d_ptr->iccProfile = {};
994 d_ptr->description = QString();
996 // An RGB color model stays RGB, a gray stays gray, but an undefined one can now be considered gray
1002 // Rescale toXyz to new whitepoint
1003 QColorMatrix rawToXyz = d_ptr->chad.inverted() * d_ptr->toXyz;
1004 QColorVector whiteScale = rawToXyz.inverted().map(wXyz);
1005 rawToXyz = rawToXyz * QColorMatrix::fromScale(whiteScale);
1007 d_ptr->toXyz = d_ptr->chad * rawToXyz;
1008 } else if (d_ptr->colorModel == QColorSpace::ColorModel::Gray) {
1009 d_ptr->chad = d_ptr->toXyz = QColorMatrix::chromaticAdaptation(wXyz);
1010 }
1011 }
1012 d_ptr->whitePoint = wXyz;
1013 d_ptr->identifyColorSpace();
1014}
1015
1027
1034{
1035 if (Q_UNLIKELY(!d_ptr))
1037 return d_ptr->colorModel;
1038}
1039
1044{
1045 if (d_ptr)
1046 d_ptr.detach();
1047 else
1048 d_ptr = new QColorSpacePrivate;
1049}
1050
1064{
1065 if (Q_UNLIKELY(!d_ptr))
1066 return QByteArray();
1067 if (!d_ptr->iccProfile.isEmpty())
1068 return d_ptr->iccProfile;
1069 if (!isValid())
1070 return QByteArray();
1071 return QIcc::toIccProfile(*this);
1072}
1073
1086{
1087 QColorSpace colorSpace;
1088 if (QIcc::fromIccProfile(iccProfile, &colorSpace))
1089 return colorSpace;
1090 colorSpace.detach();
1091 colorSpace.d_ptr->iccProfile = iccProfile;
1092 return colorSpace;
1093}
1094
1103bool QColorSpace::isValid() const noexcept
1104{
1105 if (!d_ptr)
1106 return false;
1107 return d_ptr->isValid();
1108}
1109
1115bool QColorSpace::isValidTarget() const noexcept
1116{
1117 if (!d_ptr)
1118 return false;
1119 if (!d_ptr->isThreeComponentMatrix())
1120 return !d_ptr->mBA.isEmpty();
1121 return d_ptr->isValid();
1122}
1123
1127bool QColorSpacePrivate::isValid() const noexcept
1128{
1130 return !mAB.isEmpty();
1131 if (!toXyz.isValid())
1132 return false;
1134 if (!trc[0].isValid())
1135 return false;
1136 } else {
1137 if (!trc[0].isValid() || !trc[1].isValid() || !trc[2].isValid())
1138 return false;
1139 }
1140 return true;
1141}
1142
1159{
1160 return element.trc[0] == other.trc[0]
1161 && element.trc[1] == other.trc[1]
1162 && element.trc[2] == other.trc[2]
1163 && element.trc[3] == other.trc[3];
1164}
1165
1166static bool compareElement(const QColorMatrix &element,
1167 const QColorMatrix &other)
1168{
1169 return element == other;
1170}
1171
1172static bool compareElement(const QColorVector &element,
1173 const QColorVector &other)
1174{
1175 return element == other;
1176}
1177
1178static bool compareElement(const QColorCLUT &element,
1179 const QColorCLUT &other)
1180{
1181 if (element.gridPointsX != other.gridPointsX)
1182 return false;
1183 if (element.gridPointsY != other.gridPointsY)
1184 return false;
1185 if (element.gridPointsZ != other.gridPointsZ)
1186 return false;
1187 if (element.gridPointsW != other.gridPointsW)
1188 return false;
1189 if (element.table.size() != other.table.size())
1190 return false;
1191 for (qsizetype i = 0; i < element.table.size(); ++i) {
1192 if (element.table[i] != other.table[i])
1193 return false;
1194 }
1195 return true;
1196}
1197
1198template<typename T>
1199static bool compareElements(const T &element, const QColorSpacePrivate::Element &other)
1200{
1201 return compareElement(element, std::get<T>(other));
1202}
1203
1207bool QColorSpace::equals(const QColorSpace &other) const
1208{
1209 if (d_ptr == other.d_ptr)
1210 return true;
1211 if (!d_ptr)
1212 return false;
1213 return d_ptr->equals(other.d_ptr.constData());
1214}
1215
1220{
1221 if (!other)
1222 return false;
1223
1224 if (namedColorSpace && other->namedColorSpace)
1225 return namedColorSpace == other->namedColorSpace;
1226
1227 const bool valid1 = isValid();
1228 const bool valid2 = other->isValid();
1229 if (valid1 != valid2)
1230 return false;
1231 if (!valid1 && !valid2) {
1232 if (!iccProfile.isEmpty() || !other->iccProfile.isEmpty())
1233 return iccProfile == other->iccProfile;
1234 return false;
1235 }
1236
1237 // At this point one or both color spaces are unknown, and must be compared in detail instead
1238
1239 if (transformModel != other->transformModel)
1240 return false;
1241
1242 if (!isThreeComponentMatrix()) {
1243 if (isPcsLab != other->isPcsLab)
1244 return false;
1245 if (colorModel != other->colorModel)
1246 return false;
1247 if (mAB.count() != other->mAB.count())
1248 return false;
1249 if (mBA.count() != other->mBA.count())
1250 return false;
1251
1252 // Compare element types
1253 for (qsizetype i = 0; i < mAB.count(); ++i) {
1254 if (mAB[i].index() != other->mAB[i].index())
1255 return false;
1256 }
1257 for (qsizetype i = 0; i < mBA.count(); ++i) {
1258 if (mBA[i].index() != other->mBA[i].index())
1259 return false;
1260 }
1261
1262 // Compare element contents
1263 for (qsizetype i = 0; i < mAB.count(); ++i) {
1264 if (!std::visit([&](auto &&elm) { return compareElements(elm, other->mAB[i]); }, mAB[i]))
1265 return false;
1266 }
1267 for (qsizetype i = 0; i < mBA.count(); ++i) {
1268 if (!std::visit([&](auto &&elm) { return compareElements(elm, other->mBA[i]); }, mBA[i]))
1269 return false;
1270 }
1271
1272 return true;
1273 }
1274
1276 if (primaries != other->primaries)
1277 return false;
1278 } else {
1279 if (toXyz != other->toXyz)
1280 return false;
1281 }
1282
1284 if (transferFunction != other->transferFunction)
1285 return false;
1287 return (qAbs(gamma - other->gamma) <= (1.0f / 512.0f));
1288 return true;
1289 }
1290
1291 if (trc[0] != other->trc[0] ||
1292 trc[1] != other->trc[1] ||
1293 trc[2] != other->trc[2])
1294 return false;
1295
1296 return true;
1297}
1298
1304{
1305 if (!isValid())
1306 return QColorTransform();
1307
1308 if (*this == colorspace)
1309 return QColorTransform();
1310 if (!colorspace.isValidTarget()) {
1311 qWarning() << "QColorSpace::transformationToColorSpace: colorspace not a valid target";
1312 return QColorTransform();
1313 }
1314
1315 return d_ptr->transformationToColorSpace(colorspace.d_ptr.get());
1316}
1317
1322QColorSpace::operator QVariant() const
1323{
1324 return QVariant::fromValue(*this);
1325}
1326
1336{
1337 if (d_ptr)
1338 return d_ptr->userDescription.isEmpty() ? d_ptr->description : d_ptr->userDescription;
1339 return QString();
1340}
1341
1350void QColorSpace::setDescription(const QString &description)
1351{
1352 detach();
1353 d_ptr->iccProfile = {};
1355}
1356
1357/*****************************************************************************
1358 QColorSpace stream functions
1359 *****************************************************************************/
1360#if !defined(QT_NO_DATASTREAM)
1371{
1372 s << image.iccProfile();
1373 return s;
1374}
1375
1387{
1388 QByteArray iccProfile;
1389 s >> iccProfile;
1390 colorSpace = QColorSpace::fromIccProfile(iccProfile);
1391 return s;
1392}
1393#endif // QT_NO_DATASTREAM
1394
1395#ifndef QT_NO_DEBUG_STREAM
1397{
1398 return dbg << ":Transfer";
1399}
1401{
1402 return dbg << ":Matrix";
1403}
1405{
1406 return dbg << ":Offset";
1407}
1409{
1410 return dbg << ":CLUT";
1411}
1412QDebug operator<<(QDebug dbg, const QList<QColorSpacePrivate::Element> &elements)
1413{
1414 for (auto &&element : elements)
1415 std::visit([&](auto &&elm) { dbg << elm; }, element);
1416 return dbg;
1417}
1418QDebug operator<<(QDebug dbg, const QColorSpace &colorSpace)
1419{
1420 QDebugStateSaver saver(dbg);
1421 dbg.nospace();
1422 dbg << "QColorSpace(";
1423 if (colorSpace.d_ptr) {
1424 if (colorSpace.d_ptr->namedColorSpace)
1425 dbg << colorSpace.d_ptr->namedColorSpace << ", ";
1426 if (!colorSpace.isValid()) {
1427 dbg << "Invalid";
1428 if (!colorSpace.d_ptr->iccProfile.isEmpty())
1429 dbg << " with profile data";
1430 } else if (colorSpace.d_ptr->isThreeComponentMatrix()) {
1431 dbg << colorSpace.primaries() << ", " << colorSpace.transferFunction();
1432 dbg << ", gamma=" << colorSpace.gamma();
1433 } else {
1434 if (colorSpace.d_ptr->isPcsLab)
1435 dbg << "PCSLab, ";
1436 else
1437 dbg << "PCSXYZ, ";
1438 dbg << "A2B" << colorSpace.d_ptr->mAB;
1439 if (!colorSpace.d_ptr->mBA.isEmpty())
1440 dbg << ", B2A" << colorSpace.d_ptr->mBA;
1441 }
1442 }
1443 dbg << ')';
1444 return dbg;
1445}
1446#endif
1447
1449
1450#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:32
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:40
void setWhitePoint(const 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 fromProPhotoRgb()
static QColorTransferFunction fromSRgb()
The QColorTransform class is a transformation between color spaces.
QColorTransferFunction m_fun
Definition qcolortrc_p.h:90
Type m_type
Definition qcolortrc_p.h:89
QColorTransferTable m_table
Definition qcolortrc_p.h:91
static constexpr QColorVector D50()
static constexpr QPointF D65Chromaticity()
static constexpr bool isValidChromaticity(const QPointF &chr)
constexpr bool isNull() const noexcept
static constexpr QColorVector fromXYChromaticity(QPointF chr)
QPointF toChromaticity() const
static constexpr QPointF D50Chromaticity()
\inmodule QtCore\reentrant
Definition qdatastream.h:46
\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:397
bool isEmpty() const noexcept
Definition qlist.h:401
qsizetype count() const noexcept
Definition qlist.h:398
void clear()
Definition qlist.h:434
\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:536
bool fromIccProfile(const QByteArray &data, QColorSpace *colorSpace)
Definition qicc.cpp:1256
QByteArray toIccProfile(const QColorSpace &space)
Definition qicc.cpp:371
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::ProPhotoRgb]
#define Q_UNLIKELY(x)
static bool initialize()
Definition qctf.cpp:94
typedef QByteArray(EGLAPIENTRYP PFNQGSGETDISPLAYSPROC)()
bool qFuzzyIsNull(qfloat16 f) noexcept
Definition qfloat16.h:349
#define qWarning
Definition qlogging.h:166
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]