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"
10#include "qcolortransferfunction_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
20QT_BEGIN_NAMESPACE
21
22Q_CONSTINIT QBasicMutex QColorSpacePrivate::s_lutWriteLock;
23
24Q_CONSTINIT static QAtomicPointer<QColorSpacePrivate> s_predefinedColorspacePrivates[QColorSpace::Bt2100Hlg] = {};
25static void cleanupPredefinedColorspaces()
26{
27 for (QAtomicPointer<QColorSpacePrivate> &ptr : s_predefinedColorspacePrivates) {
28 QColorSpacePrivate *prv = ptr.fetchAndStoreAcquire(newValue: nullptr);
29 if (prv && !prv->ref.deref())
30 delete prv;
31 }
32}
33
34Q_DESTRUCTOR_FUNCTION(cleanupPredefinedColorspaces)
35
36QColorSpacePrimaries::QColorSpacePrimaries(QColorSpace::Primaries primaries)
37{
38 switch (primaries) {
39 case QColorSpace::Primaries::SRgb:
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;
45 case QColorSpace::Primaries::DciP3D65:
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;
51 case QColorSpace::Primaries::AdobeRgb:
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;
57 case QColorSpace::Primaries::ProPhotoRgb:
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 case QColorSpace::Primaries::Bt2020:
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
74bool QColorSpacePrimaries::areValid() const
75{
76 if (!QColorVector::isValidChromaticity(chr: redPoint))
77 return false;
78 if (!QColorVector::isValidChromaticity(chr: greenPoint))
79 return false;
80 if (!QColorVector::isValidChromaticity(chr: bluePoint))
81 return false;
82 if (!QColorVector::isValidChromaticity(chr: whitePoint))
83 return false;
84 return true;
85}
86
87QColorMatrix QColorSpacePrimaries::toXyzMatrix() const
88{
89 // This converts to XYZ in some undefined scale.
90 QColorMatrix toXyz = { .r: QColorVector::fromXYChromaticity(chr: redPoint),
91 .g: QColorVector::fromXYChromaticity(chr: greenPoint),
92 .b: QColorVector::fromXYChromaticity(chr: bluePoint) };
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.
97 const auto wXyz = QColorVector::fromXYChromaticity(chr: whitePoint);
98 QColorVector whiteScale = toXyz.inverted().map(c: wXyz);
99
100 // Now we have scaled conversion to XYZ relative to the given whitepoint
101 toXyz = toXyz * QColorMatrix::fromScale(v: whiteScale);
102
103 return toXyz;
104}
105
106QColorSpacePrivate::QColorSpacePrivate()
107{
108}
109
110QColorSpacePrivate::QColorSpacePrivate(QColorSpace::NamedColorSpace namedColorSpace)
111 : namedColorSpace(namedColorSpace)
112 , colorModel(QColorSpace::ColorModel::Rgb)
113{
114 switch (namedColorSpace) {
115 case QColorSpace::SRgb:
116 primaries = QColorSpace::Primaries::SRgb;
117 transferFunction = QColorSpace::TransferFunction::SRgb;
118 description = QStringLiteral("sRGB");
119 break;
120 case QColorSpace::SRgbLinear:
121 primaries = QColorSpace::Primaries::SRgb;
122 transferFunction = QColorSpace::TransferFunction::Linear;
123 description = QStringLiteral("Linear sRGB");
124 break;
125 case QColorSpace::AdobeRgb:
126 primaries = QColorSpace::Primaries::AdobeRgb;
127 transferFunction = QColorSpace::TransferFunction::Gamma;
128 gamma = 2.19921875f; // Not quite 2.2, see https://www.adobe.com/digitalimag/pdfs/AdobeRGB1998.pdf
129 description = QStringLiteral("Adobe RGB");
130 break;
131 case QColorSpace::DisplayP3:
132 primaries = QColorSpace::Primaries::DciP3D65;
133 transferFunction = QColorSpace::TransferFunction::SRgb;
134 description = QStringLiteral("Display P3");
135 break;
136 case QColorSpace::ProPhotoRgb:
137 primaries = QColorSpace::Primaries::ProPhotoRgb;
138 transferFunction = QColorSpace::TransferFunction::ProPhotoRgb;
139 description = QStringLiteral("ProPhoto RGB");
140 break;
141 case QColorSpace::Bt2020:
142 primaries = QColorSpace::Primaries::Bt2020;
143 transferFunction = QColorSpace::TransferFunction::Bt2020;
144 description = QStringLiteral("BT.2020");
145 break;
146 case QColorSpace::Bt2100Pq:
147 primaries = QColorSpace::Primaries::Bt2020;
148 transferFunction = QColorSpace::TransferFunction::St2084;
149 description = QStringLiteral("BT.2100(PQ)");
150 break;
151 case QColorSpace::Bt2100Hlg:
152 primaries = QColorSpace::Primaries::Bt2020;
153 transferFunction = QColorSpace::TransferFunction::Hlg;
154 description = QStringLiteral("BT.2100(HLG)");
155 break;
156 default:
157 Q_UNREACHABLE();
158 }
159 initialize();
160}
161
162QColorSpacePrivate::QColorSpacePrivate(QColorSpace::Primaries primaries, QColorSpace::TransferFunction transferFunction, float gamma)
163 : primaries(primaries)
164 , transferFunction(transferFunction)
165 , colorModel(QColorSpace::ColorModel::Rgb)
166 , gamma(gamma)
167{
168 identifyColorSpace();
169 initialize();
170}
171
172QColorSpacePrivate::QColorSpacePrivate(const QColorSpacePrimaries &primaries,
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(chr: primaries.whitePoint))
180{
181 Q_ASSERT(primaries.areValid());
182 toXyz = primaries.toXyzMatrix();
183 chad = QColorMatrix::chromaticAdaptation(whitePoint);
184 toXyz = chad * toXyz;
185
186 identifyColorSpace();
187 setTransferFunction();
188}
189
190QColorSpacePrivate::QColorSpacePrivate(QPointF whitePoint,
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(chr: whitePoint))
198{
199 chad = QColorMatrix::chromaticAdaptation(whitePoint: this->whitePoint);
200 toXyz = chad;
201 setTransferFunction();
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(chr: whitePoint))
210{
211 chad = QColorMatrix::chromaticAdaptation(whitePoint: this->whitePoint);
212 toXyz = chad;
213 setTransferFunctionTable(transferFunctionTable);
214 setTransferFunction();
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);
224 identifyColorSpace();
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(chr: primaries.whitePoint))
234{
235 Q_ASSERT(primaries.areValid());
236 toXyz = primaries.toXyzMatrix();
237 chad = QColorMatrix::chromaticAdaptation(whitePoint);
238 toXyz = chad * toXyz;
239 setTransferFunctionTable(transferFunctionTable);
240 identifyColorSpace();
241 initialize();
242}
243
244QColorSpacePrivate::QColorSpacePrivate(const QColorSpacePrimaries &primaries,
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();
255 whitePoint = QColorVector::fromXYChromaticity(chr: primaries.whitePoint);
256 chad = QColorMatrix::chromaticAdaptation(whitePoint);
257 toXyz = chad * toXyz;
258 setTransferFunctionTables(redTransferFunctionTable,
259 greenTransferFunctionTable,
260 blueTransferFunctionTable);
261 identifyColorSpace();
262}
263
264void QColorSpacePrivate::identifyColorSpace()
265{
266 switch (primaries) {
267 case QColorSpace::Primaries::SRgb:
268 if (transferFunction == QColorSpace::TransferFunction::SRgb) {
269 namedColorSpace = QColorSpace::SRgb;
270 if (description.isEmpty())
271 description = QStringLiteral("sRGB");
272 return;
273 }
274 if (transferFunction == QColorSpace::TransferFunction::Linear) {
275 namedColorSpace = QColorSpace::SRgbLinear;
276 if (description.isEmpty())
277 description = QStringLiteral("Linear sRGB");
278 return;
279 }
280 break;
281 case QColorSpace::Primaries::AdobeRgb:
282 if (transferFunction == QColorSpace::TransferFunction::Gamma) {
283 if (qAbs(t: gamma - 2.19921875f) < (1/1024.0f)) {
284 namedColorSpace = QColorSpace::AdobeRgb;
285 if (description.isEmpty())
286 description = QStringLiteral("Adobe RGB");
287 return;
288 }
289 }
290 break;
291 case QColorSpace::Primaries::DciP3D65:
292 if (transferFunction == QColorSpace::TransferFunction::SRgb) {
293 namedColorSpace = QColorSpace::DisplayP3;
294 if (description.isEmpty())
295 description = QStringLiteral("Display P3");
296 return;
297 }
298 break;
299 case QColorSpace::Primaries::ProPhotoRgb:
300 if (transferFunction == QColorSpace::TransferFunction::ProPhotoRgb) {
301 namedColorSpace = QColorSpace::ProPhotoRgb;
302 if (description.isEmpty())
303 description = QStringLiteral("ProPhoto RGB");
304 return;
305 }
306 if (transferFunction == QColorSpace::TransferFunction::Gamma) {
307 // ProPhoto RGB's curve is effectively gamma 1.8 for 8bit precision.
308 if (qAbs(t: gamma - 1.8f) < (1/1024.0f)) {
309 namedColorSpace = QColorSpace::ProPhotoRgb;
310 if (description.isEmpty())
311 description = QStringLiteral("ProPhoto RGB");
312 return;
313 }
314 }
315 break;
316 case QColorSpace::Primaries::Bt2020:
317 if (transferFunction == QColorSpace::TransferFunction::Bt2020) {
318 namedColorSpace = QColorSpace::Bt2020;
319 if (description.isEmpty())
320 description = QStringLiteral("BT.2020");
321 return;
322 }
323 if (transferFunction == QColorSpace::TransferFunction::St2084) {
324 namedColorSpace = QColorSpace::Bt2100Pq;
325 if (description.isEmpty())
326 description = QStringLiteral("BT.2100(PQ)");
327 return;
328 }
329 if (transferFunction == QColorSpace::TransferFunction::Hlg) {
330 namedColorSpace = QColorSpace::Bt2100Hlg;
331 if (description.isEmpty())
332 description = QStringLiteral("BT.2100(HLG)");
333 return;
334 }
335 break;
336 default:
337 break;
338 }
339
340 namedColorSpace = Unknown;
341}
342
343void QColorSpacePrivate::initialize()
344{
345 setToXyzMatrix();
346 setTransferFunction();
347}
348
349void QColorSpacePrivate::setToXyzMatrix()
350{
351 if (primaries == QColorSpace::Primaries::Custom) {
352 toXyz = QColorMatrix();
353 whitePoint = QColorVector::D50();
354 return;
355 }
356 QColorSpacePrimaries colorSpacePrimaries(primaries);
357 toXyz = colorSpacePrimaries.toXyzMatrix();
358 whitePoint = QColorVector::fromXYChromaticity(chr: colorSpacePrimaries.whitePoint);
359 chad = QColorMatrix::chromaticAdaptation(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 }
371 transferFunction = QColorSpace::TransferFunction::Custom;
372 QColorTransferFunction curve;
373 if (table.asColorTransferFunction(transferFn: &curve)) {
374 // Table recognized as a specific curve
375 if (curve.isIdentity()) {
376 transferFunction = QColorSpace::TransferFunction::Linear;
377 gamma = 1.0f;
378 } else if (curve.isSRgb()) {
379 transferFunction = QColorSpace::TransferFunction::SRgb;
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 }
404 transferFunction = QColorSpace::TransferFunction::Custom;
405 QColorTransferFunction curve;
406 if (redTable.asColorTransferFunction(transferFn: &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(transferFn: &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(transferFn: &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(newValue: 0);
428}
429
430void QColorSpacePrivate::setTransferFunction()
431{
432 switch (transferFunction) {
433 case QColorSpace::TransferFunction::Linear:
434 trc[0] = QColorTransferFunction();
435 if (qFuzzyIsNull(f: gamma))
436 gamma = 1.0f;
437 break;
438 case QColorSpace::TransferFunction::Gamma:
439 trc[0] = QColorTransferFunction::fromGamma(gamma);
440 break;
441 case QColorSpace::TransferFunction::SRgb:
442 trc[0] = QColorTransferFunction::fromSRgb();
443 if (qFuzzyIsNull(f: gamma))
444 gamma = 2.31f;
445 break;
446 case QColorSpace::TransferFunction::ProPhotoRgb:
447 trc[0] = QColorTransferFunction::fromProPhotoRgb();
448 if (qFuzzyIsNull(f: gamma))
449 gamma = 1.8f;
450 break;
451 case QColorSpace::TransferFunction::Bt2020:
452 trc[0] = QColorTransferFunction::fromBt2020();
453 if (qFuzzyIsNull(f: gamma))
454 gamma = 2.1f;
455 break;
456 case QColorSpace::TransferFunction::St2084:
457 trc[0] = QColorTransferGenericFunction::pq();
458 break;
459 case QColorSpace::TransferFunction::Hlg:
460 trc[0] = QColorTransferGenericFunction::hlg();
461 break;
462 case QColorSpace::TransferFunction::Custom:
463 break;
464 default:
465 Q_UNREACHABLE();
466 break;
467 }
468 trc[1] = trc[0];
469 trc[2] = trc[0];
470 lut.generated.storeRelease(newValue: 0);
471}
472
473QColorTransform QColorSpacePrivate::transformationToColorSpace(const QColorSpacePrivate *out) const
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;
481 if (isThreeComponentMatrix())
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
492QColorTransform QColorSpacePrivate::transformationToXYZ() const
493{
494 QColorTransform transform;
495 auto ptr = new QColorTransformPrivate;
496 transform.d = ptr;
497 ptr->colorSpaceIn = this;
498 ptr->colorSpaceOut = this;
499 if (isThreeComponentMatrix())
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
509bool QColorSpacePrivate::isThreeComponentMatrix() const
510{
511 return transformModel == QColorSpace::TransformModel::ThreeComponentMatrix;
512}
513
514void QColorSpacePrivate::clearElementListProcessingForEdit()
515{
516 Q_ASSERT(transformModel == QColorSpace::TransformModel::ElementListProcessing);
517 Q_ASSERT(primaries == QColorSpace::Primaries::Custom);
518 Q_ASSERT(transferFunction == QColorSpace::TransferFunction::Custom);
519
520 transformModel = QColorSpace::TransformModel::ThreeComponentMatrix;
521 colorModel = QColorSpace::ColorModel::Rgb;
522 isPcsLab = false;
523 mAB.clear();
524 mBA.clear();
525}
526
527/*!
528 \class QColorSpace
529 \brief The QColorSpace class provides a color space abstraction.
530 \since 5.14
531
532 \ingroup painting
533 \ingroup appearance
534 \inmodule QtGui
535
536 Color values can be interpreted in different ways, and based on the interpretation
537 can live in different spaces. We call this \e {color spaces}.
538
539 QColorSpace provides access to creating several predefined color spaces and
540 can generate QColorTransforms for converting colors from one color space to
541 another.
542
543 QColorSpace can also represent color spaces defined by ICC profiles or embedded
544 in images, that do not otherwise fit the predefined color spaces.
545
546 A color space can generally speaking be conceived as a combination of set of primary
547 colors and a transfer function. The primaries defines the axes of the color space, and
548 the transfer function how values are mapped on the axes.
549 The primaries are for ColorModel::Rgb color spaces defined by three primary colors that
550 represent exactly how red, green, and blue look in this particular color space, and a white
551 color that represents where and how bright pure white is. For grayscale color spaces, only
552 a single white primary is needed. The range of colors expressible by the primary colors is
553 called the gamut, and a color space that can represent a wider range of colors is also
554 known as a wide-gamut color space.
555
556 The transfer function or gamma curve determines how each component in the
557 color space is encoded. These are used because human perception does not operate
558 linearly, and the transfer functions try to ensure that colors will seem evenly
559 spaced to human eyes.
560*/
561
562
563/*!
564 \enum QColorSpace::NamedColorSpace
565
566 Predefined color spaces.
567
568 \value SRgb The sRGB color space, which Qt operates in by default. It is a close approximation
569 of how most classic monitors operate, and a mode most software and hardware support.
570 \l{http://www.color.org/chardata/rgb/srgb.xalter}{ICC registration of sRGB}.
571 \value SRgbLinear The sRGB color space with linear gamma. Useful for gamma-corrected blending.
572 \value AdobeRgb The Adobe RGB color space is a classic wide-gamut color space, using a gamma of 2.2.
573 \l{http://www.color.org/chardata/rgb/adobergb.xalter}{ICC registration of Adobe RGB (1998)}
574 \value DisplayP3 A color-space using the primaries of DCI-P3, but with the whitepoint and transfer
575 function of sRGB. Common in modern wide-gamut screens.
576 \l{http://www.color.org/chardata/rgb/DCIP3.xalter}{ICC registration of DCI-P3}
577 \value ProPhotoRgb The Pro Photo RGB color space, also known as ROMM RGB is a very wide gamut color space.
578 \l{http://www.color.org/chardata/rgb/rommrgb.xalter}{ICC registration of ROMM RGB}
579 \value [since 6.8] Bt2020 BT.2020, also known as Rec.2020 is a basic colorspace of HDR TVs.
580 \l{http://www.color.org/chardata/rgb/BT2020.xalter}{ICC registration of BT.2020}
581 \value [since 6.8] Bt2100Pq BT.2100(PQ), also known as Rec.2100 or HDR10 is an HDR encoding with the same
582 primaries as Bt2020 but using the Perceptual Quantizer transfer function.
583 \l{http://www.color.org/chardata/rgb/BT2100.xalter}{ICC registration of BT.2100}
584 \value [since 6.8] Bt2100Hlg BT.2100 (HLG) is an HDR encoding with the same
585 primaries as Bt2020 but using the Hybrid Log-Gamma transfer function.
586*/
587
588/*!
589 \enum QColorSpace::Primaries
590
591 Predefined sets of primary colors.
592
593 \value Custom The primaries are undefined or does not match any predefined sets.
594 \value SRgb The sRGB primaries
595 \value AdobeRgb The Adobe RGB primaries
596 \value DciP3D65 The DCI-P3 primaries with the D65 whitepoint
597 \value ProPhotoRgb The ProPhoto RGB primaries with the D50 whitepoint
598 \value [since 6.8] Bt2020 The BT.2020 primaries with a D65 whitepoint
599*/
600
601/*!
602 \enum QColorSpace::TransferFunction
603
604 Predefined transfer functions or gamma curves.
605
606 \value Custom The custom or null transfer function
607 \value Linear The linear transfer functions
608 \value Gamma A transfer function that is a real gamma curve based on the value of gamma()
609 \value SRgb The sRGB transfer function, composed of linear and gamma parts
610 \value ProPhotoRgb The ProPhoto RGB transfer function, composed of linear and gamma parts
611 \value [since 6.8] Bt2020 The BT.2020 transfer function, composited of linear and gamma parts
612 \value [since 6.8] St2084 The SMPTE ST 2084 transfer function, also known Perceptual Quantizer(PQ).
613 \value [since 6.8] Hlg The Hybrid log-gamma transfer function.
614
615*/
616
617/*!
618 \enum QColorSpace::TransformModel
619 \since 6.8
620
621 Defines the processing model used for color space transforms.
622
623 \value ThreeComponentMatrix The transform consist of a matrix calculated from primaries and set of transfer functions
624 for each color channel. This is very fast and used by all predefined color spaces. Any color space on this form is
625 reversible and always both valid sources and targets.
626 \value ElementListProcessing The transforms are one or two lists of processing elements that can do many things,
627 each list only process either to the connection color space or from it. This is very flexible, but rather
628 slow, and can only be set by reading ICC profiles (See \l fromIccProfile()). Since the two lists are
629 separate a color space on this form can be a valid source, but not necessarily also a valid target. When changing
630 either primaries or transfer function on a color space on this type it will reset to an empty ThreeComponentMatrix form.
631*/
632
633/*!
634 \enum QColorSpace::ColorModel
635 \since 6.8
636
637 Defines the color model used by the color space data.
638
639 \value Undefined No color model
640 \value Rgb An RGB color model with red, green, and blue colors. Can apply to RGB and grayscale data.
641 \value Gray A gray scale color model. Can only apply to grayscale data.
642 \value Cmyk Can only represent color data defined with cyan, magenta, yellow, and black colors.
643 In effect only QImage::Format_CMYK32. Note Cmyk color spaces will be TransformModel::ElementListProcessing.
644*/
645
646/*!
647 \fn QColorSpace::QColorSpace()
648
649 Creates a new colorspace object that represents an undefined and invalid colorspace.
650 */
651
652/*!
653 Creates a new colorspace object that represents a \a namedColorSpace.
654 */
655QColorSpace::QColorSpace(NamedColorSpace namedColorSpace)
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(expectedValue: nullptr, newValue: tmp, currentValue&: cspriv))
668 cspriv = tmp;
669 else
670 delete tmp;
671 }
672 d_ptr = cspriv;
673 Q_ASSERT(isValid());
674}
675
676/*!
677 Creates a custom color space with the primaries \a primaries, using the transfer function \a transferFunction and
678 optionally \a gamma.
679 */
680QColorSpace::QColorSpace(QColorSpace::Primaries primaries, QColorSpace::TransferFunction transferFunction, float gamma)
681 : d_ptr(new QColorSpacePrivate(primaries, transferFunction, gamma))
682{
683}
684
685/*!
686 Creates a custom color space with the primaries \a primaries, using a gamma transfer function of
687 \a gamma.
688 */
689QColorSpace::QColorSpace(QColorSpace::Primaries primaries, float gamma)
690 : d_ptr(new QColorSpacePrivate(primaries, TransferFunction::Gamma, gamma))
691{
692}
693
694/*!
695 Creates a custom color space with the primaries \a gamut, using a custom transfer function
696 described by \a transferFunctionTable.
697
698 The table should contain at least 2 values, and contain an monotonically increasing list
699 of values from 0 to 65535.
700
701 \since 6.1
702 */
703QColorSpace::QColorSpace(QColorSpace::Primaries gamut, const QList<uint16_t> &transferFunctionTable)
704 : d_ptr(new QColorSpacePrivate(gamut, transferFunctionTable))
705{
706}
707
708/*!
709 Creates a custom grayscale color space with the white point \a whitePoint, using the transfer function \a transferFunction and
710 optionally \a gamma.
711
712 \since 6.8
713*/
714QColorSpace::QColorSpace(QPointF whitePoint, TransferFunction transferFunction, float gamma)
715 : d_ptr(new QColorSpacePrivate(whitePoint, transferFunction, gamma))
716{
717}
718
719/*!
720 Creates a custom grayscale color space with white point \a whitePoint, and using the custom transfer function described by
721 \a transferFunctionTable.
722
723 \since 6.8
724*/
725QColorSpace::QColorSpace(QPointF whitePoint, const QList<uint16_t> &transferFunctionTable)
726 : d_ptr(new QColorSpacePrivate(whitePoint, transferFunctionTable))
727{
728}
729
730/*!
731 Creates a custom colorspace with a primaries based on the chromaticities of the primary colors \a whitePoint,
732 \a redPoint, \a greenPoint and \a bluePoint, and using the transfer function \a transferFunction and optionally \a gamma.
733 */
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 }
743 d_ptr = new QColorSpacePrivate(primaries, transferFunction, gamma);
744}
745
746/*!
747 Creates a custom color space with primaries based on the chromaticities of the primary colors \a whitePoint,
748 \a redPoint, \a greenPoint and \a bluePoint, and using the custom transfer function described by
749 \a transferFunctionTable.
750
751 \since 6.1
752 */
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
760/*!
761 Creates a custom color space with primaries based on the chromaticities of the primary colors \a whitePoint,
762 \a redPoint, \a greenPoint and \a bluePoint, and using the custom transfer functions described by
763 \a redTransferFunctionTable, \a greenTransferFunctionTable, and \a blueTransferFunctionTable.
764
765 \since 6.1
766 */
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
781QT_DEFINE_QESDP_SPECIALIZATION_DTOR(QColorSpacePrivate)
782
783QColorSpace::QColorSpace(const QColorSpace &colorSpace) noexcept = default;
784
785/*! \fn void QColorSpace::swap(QColorSpace &other)
786 \memberswap{color space}
787*/
788
789/*!
790 Returns the predefined primaries of the color space
791 or \c primaries::Custom if it doesn't match any of them.
792*/
793QColorSpace::Primaries QColorSpace::primaries() const noexcept
794{
795 if (Q_UNLIKELY(!d_ptr))
796 return QColorSpace::Primaries::Custom;
797 return d_ptr->primaries;
798}
799
800/*!
801 Returns the predefined transfer function of the color space
802 or \c TransferFunction::Custom if it doesn't match any of them.
803
804 \sa gamma(), setTransferFunction(), withTransferFunction()
805*/
806QColorSpace::TransferFunction QColorSpace::transferFunction() const noexcept
807{
808 if (Q_UNLIKELY(!d_ptr))
809 return QColorSpace::TransferFunction::Custom;
810 return d_ptr->transferFunction;
811}
812
813/*!
814 Returns the gamma value of color spaces with \c TransferFunction::Gamma,
815 an approximate gamma value for other predefined color spaces, or
816 0.0 if no approximate gamma is known.
817
818 \sa transferFunction()
819*/
820float QColorSpace::gamma() const noexcept
821{
822 if (Q_UNLIKELY(!d_ptr))
823 return 0.0f;
824 return d_ptr->gamma;
825}
826
827/*!
828 Sets the transfer function to \a transferFunction and \a gamma.
829
830 \sa transferFunction(), gamma(), withTransferFunction()
831*/
832void QColorSpace::setTransferFunction(QColorSpace::TransferFunction transferFunction, float gamma)
833{
834 if (transferFunction == TransferFunction::Custom)
835 return;
836 if (!d_ptr) {
837 d_ptr = new QColorSpacePrivate(Primaries::Custom, transferFunction, gamma);
838 return;
839 }
840 if (d_ptr->transferFunction == transferFunction && d_ptr->gamma == gamma)
841 return;
842 detach();
843 if (d_ptr->transformModel == TransformModel::ElementListProcessing)
844 d_ptr->clearElementListProcessingForEdit();
845 d_ptr->iccProfile = {};
846 d_ptr->description = QString();
847 d_ptr->transferFunction = transferFunction;
848 d_ptr->gamma = gamma;
849 d_ptr->identifyColorSpace();
850 d_ptr->setTransferFunction();
851}
852
853/*!
854 Sets the transfer function to \a transferFunctionTable.
855
856 \since 6.1
857 \sa withTransferFunction()
858*/
859void QColorSpace::setTransferFunction(const QList<uint16_t> &transferFunctionTable)
860{
861 if (!d_ptr) {
862 d_ptr = new QColorSpacePrivate(Primaries::Custom, transferFunctionTable);
863 d_ptr->ref.ref();
864 return;
865 }
866 detach();
867 if (d_ptr->transformModel == TransformModel::ElementListProcessing)
868 d_ptr->clearElementListProcessingForEdit();
869 d_ptr->iccProfile = {};
870 d_ptr->description = QString();
871 d_ptr->setTransferFunctionTable(transferFunctionTable);
872 d_ptr->gamma = 0;
873 d_ptr->identifyColorSpace();
874 d_ptr->setTransferFunction();
875}
876
877/*!
878 Sets the transfer functions to \a redTransferFunctionTable,
879 \a greenTransferFunctionTable and \a blueTransferFunctionTable.
880
881 \since 6.1
882 \sa withTransferFunctions()
883*/
884void QColorSpace::setTransferFunctions(const QList<uint16_t> &redTransferFunctionTable,
885 const QList<uint16_t> &greenTransferFunctionTable,
886 const QList<uint16_t> &blueTransferFunctionTable)
887{
888 if (!d_ptr) {
889 d_ptr = new QColorSpacePrivate();
890 d_ptr->setTransferFunctionTables(redTransferFunctionTable,
891 greenTransferFunctionTable,
892 blueTransferFunctionTable);
893 d_ptr->ref.ref();
894 return;
895 }
896 detach();
897 if (d_ptr->transformModel == TransformModel::ElementListProcessing)
898 d_ptr->clearElementListProcessingForEdit();
899 d_ptr->iccProfile = {};
900 d_ptr->description = QString();
901 d_ptr->setTransferFunctionTables(redTransferFunctionTable,
902 greenTransferFunctionTable,
903 blueTransferFunctionTable);
904 d_ptr->gamma = 0;
905 d_ptr->identifyColorSpace();
906}
907
908/*!
909 Returns a copy of this color space, except using the transfer function
910 \a transferFunction and \a gamma.
911
912 \sa transferFunction(), gamma(), setTransferFunction()
913*/
914QColorSpace QColorSpace::withTransferFunction(QColorSpace::TransferFunction transferFunction, float gamma) const
915{
916 if (!isValid() || transferFunction == TransferFunction::Custom)
917 return *this;
918 if (d_ptr->transferFunction == transferFunction && d_ptr->gamma == gamma)
919 return *this;
920 QColorSpace out(*this);
921 out.setTransferFunction(transferFunction, gamma);
922 return out;
923}
924
925/*!
926 Returns a copy of this color space, except using the transfer function
927 described by \a transferFunctionTable.
928
929 \since 6.1
930 \sa transferFunction(), setTransferFunction()
931*/
932QColorSpace QColorSpace::withTransferFunction(const QList<uint16_t> &transferFunctionTable) const
933{
934 if (!isValid())
935 return *this;
936 QColorSpace out(*this);
937 out.setTransferFunction(transferFunctionTable);
938 return out;
939}
940
941/*!
942 Returns a copy of this color space, except using the transfer functions
943 described by \a redTransferFunctionTable, \a greenTransferFunctionTable and
944 \a blueTransferFunctionTable.
945
946 \since 6.1
947 \sa setTransferFunctions()
948*/
949QColorSpace QColorSpace::withTransferFunctions(const QList<uint16_t> &redTransferFunctionTable,
950 const QList<uint16_t> &greenTransferFunctionTable,
951 const QList<uint16_t> &blueTransferFunctionTable) const
952{
953 if (!isValid())
954 return *this;
955 QColorSpace out(*this);
956 out.setTransferFunctions(redTransferFunctionTable, greenTransferFunctionTable, blueTransferFunctionTable);
957 return out;
958}
959
960/*!
961 Sets the primaries to those of the \a primariesId set.
962
963 \sa primaries()
964*/
965void QColorSpace::setPrimaries(QColorSpace::Primaries primariesId)
966{
967 if (primariesId == Primaries::Custom)
968 return;
969 if (!d_ptr) {
970 d_ptr = new QColorSpacePrivate(primariesId, TransferFunction::Custom, 0.0f);
971 return;
972 }
973 if (d_ptr->primaries == primariesId)
974 return;
975 detach();
976 if (d_ptr->transformModel == TransformModel::ElementListProcessing)
977 d_ptr->clearElementListProcessingForEdit();
978 d_ptr->iccProfile = {};
979 d_ptr->description = QString();
980 d_ptr->primaries = primariesId;
981 d_ptr->colorModel = QColorSpace::ColorModel::Rgb;
982 d_ptr->identifyColorSpace();
983 d_ptr->setToXyzMatrix();
984}
985
986/*!
987 Set primaries to the chromaticities of \a whitePoint, \a redPoint, \a greenPoint
988 and \a bluePoint.
989
990 \sa primaries()
991*/
992void QColorSpace::setPrimaries(const QPointF &whitePoint, const QPointF &redPoint,
993 const QPointF &greenPoint, const QPointF &bluePoint)
994{
995 QColorSpacePrimaries primaries(whitePoint, redPoint, greenPoint, bluePoint);
996 if (!primaries.areValid())
997 return;
998 if (!d_ptr) {
999 d_ptr = new QColorSpacePrivate(primaries, TransferFunction::Custom, 0.0f);
1000 return;
1001 }
1002 QColorMatrix toXyz = primaries.toXyzMatrix();
1003 QColorMatrix chad = QColorMatrix::chromaticAdaptation(whitePoint: QColorVector::fromXYChromaticity(chr: whitePoint));
1004 toXyz = chad * toXyz;
1005 if (QColorVector::fromXYChromaticity(chr: primaries.whitePoint) == d_ptr->whitePoint
1006 && toXyz == d_ptr->toXyz && chad == d_ptr->chad)
1007 return;
1008 detach();
1009 if (d_ptr->transformModel == TransformModel::ElementListProcessing)
1010 d_ptr->clearElementListProcessingForEdit();
1011 d_ptr->iccProfile = {};
1012 d_ptr->description = QString();
1013 d_ptr->primaries = QColorSpace::Primaries::Custom;
1014 d_ptr->colorModel = QColorSpace::ColorModel::Rgb;
1015 d_ptr->toXyz = toXyz;
1016 d_ptr->chad = chad;
1017 d_ptr->whitePoint = QColorVector::fromXYChromaticity(chr: primaries.whitePoint);
1018 d_ptr->identifyColorSpace();
1019}
1020
1021/*!
1022 Returns the white point used for this color space. Returns a null QPointF if not defined.
1023
1024 \since 6.8
1025*/
1026QPointF QColorSpace::whitePoint() const
1027{
1028 if (Q_UNLIKELY(!d_ptr))
1029 return QPointF();
1030 return d_ptr->whitePoint.toChromaticity();
1031}
1032
1033/*!
1034 Sets the white point to used for this color space to \a whitePoint.
1035
1036 \since 6.8
1037*/
1038void QColorSpace::setWhitePoint(QPointF whitePoint)
1039{
1040 if (Q_UNLIKELY(!d_ptr)) {
1041 d_ptr = new QColorSpacePrivate(whitePoint, TransferFunction::Custom, 0.0f);
1042 return;
1043 }
1044 if (QColorVector::fromXYChromaticity(chr: whitePoint) == d_ptr->whitePoint)
1045 return;
1046 detach();
1047 if (d_ptr->transformModel == TransformModel::ElementListProcessing)
1048 d_ptr->clearElementListProcessingForEdit();
1049 d_ptr->iccProfile = {};
1050 d_ptr->description = QString();
1051 d_ptr->primaries = QColorSpace::Primaries::Custom;
1052 // An RGB color model stays RGB, a gray stays gray, but an undefined one can now be considered gray
1053 if (d_ptr->colorModel == QColorSpace::ColorModel::Undefined)
1054 d_ptr->colorModel = QColorSpace::ColorModel::Gray;
1055 QColorVector wXyz(QColorVector::fromXYChromaticity(chr: whitePoint));
1056 if (d_ptr->transformModel == QColorSpace::TransformModel::ThreeComponentMatrix) {
1057 if (d_ptr->colorModel == QColorSpace::ColorModel::Rgb) {
1058 // Rescale toXyz to new whitepoint
1059 QColorMatrix rawToXyz = d_ptr->chad.inverted() * d_ptr->toXyz;
1060 QColorVector whiteScale = rawToXyz.inverted().map(c: wXyz);
1061 rawToXyz = rawToXyz * QColorMatrix::fromScale(v: whiteScale);
1062 d_ptr->chad = QColorMatrix::chromaticAdaptation(whitePoint: wXyz);
1063 d_ptr->toXyz = d_ptr->chad * rawToXyz;
1064 } else if (d_ptr->colorModel == QColorSpace::ColorModel::Gray) {
1065 d_ptr->chad = d_ptr->toXyz = QColorMatrix::chromaticAdaptation(whitePoint: wXyz);
1066 }
1067 }
1068 d_ptr->whitePoint = wXyz;
1069 d_ptr->identifyColorSpace();
1070}
1071
1072/*!
1073 Returns the transfrom processing model used for this color space.
1074
1075 \since 6.8
1076*/
1077QColorSpace::TransformModel QColorSpace::transformModel() const noexcept
1078{
1079 if (Q_UNLIKELY(!d_ptr))
1080 return QColorSpace::TransformModel::ThreeComponentMatrix;
1081 return d_ptr->transformModel;
1082}
1083
1084/*!
1085 Returns the color model this color space can represent
1086
1087 \since 6.8
1088*/
1089QColorSpace::ColorModel QColorSpace::colorModel() const noexcept
1090{
1091 if (Q_UNLIKELY(!d_ptr))
1092 return QColorSpace::ColorModel::Undefined;
1093 return d_ptr->colorModel;
1094}
1095
1096/*!
1097 \internal
1098*/
1099void QColorSpace::detach()
1100{
1101 if (d_ptr)
1102 d_ptr.detach();
1103 else
1104 d_ptr = new QColorSpacePrivate;
1105}
1106
1107/*!
1108 Returns an ICC profile representing the color space.
1109
1110 If the color space was generated from an ICC profile, that profile
1111 is returned, otherwise one is generated.
1112
1113 \note Even invalid color spaces may return the ICC profile if they
1114 were generated from one, to allow applications to implement wider
1115 support themselves.
1116
1117 \sa fromIccProfile()
1118*/
1119QByteArray QColorSpace::iccProfile() const
1120{
1121 if (Q_UNLIKELY(!d_ptr))
1122 return QByteArray();
1123 if (!d_ptr->iccProfile.isEmpty())
1124 return d_ptr->iccProfile;
1125 if (!isValid())
1126 return QByteArray();
1127 return QIcc::toIccProfile(space: *this);
1128}
1129
1130/*!
1131 Creates a QColorSpace from ICC profile \a iccProfile.
1132
1133 \note Not all ICC profiles are supported. QColorSpace only supports
1134 RGB or Gray ICC profiles.
1135
1136 If the ICC profile is not supported an invalid QColorSpace is returned
1137 where you can still read the original ICC profile using iccProfile().
1138
1139 \sa iccProfile()
1140*/
1141QColorSpace QColorSpace::fromIccProfile(const QByteArray &iccProfile)
1142{
1143 QColorSpace colorSpace;
1144 if (QIcc::fromIccProfile(data: iccProfile, colorSpace: &colorSpace))
1145 return colorSpace;
1146 colorSpace.detach();
1147 colorSpace.d_ptr->iccProfile = iccProfile;
1148 return colorSpace;
1149}
1150
1151/*!
1152 Returns \c true if the color space is valid. For a color space with \c TransformModel::ThreeComponentMatrix
1153 that means both primaries and transfer functions set, and implies isValidTarget().
1154 For a color space with \c TransformModel::ElementListProcessing it means it has a valid source transform, to
1155 check if it also a valid target color space use isValidTarget().
1156
1157 \sa isValidTarget()
1158*/
1159bool QColorSpace::isValid() const noexcept
1160{
1161 if (!d_ptr)
1162 return false;
1163 return d_ptr->isValid();
1164}
1165
1166/*!
1167 \since 6.8
1168
1169 Returns \c true if the color space is a valid target color space.
1170*/
1171bool QColorSpace::isValidTarget() const noexcept
1172{
1173 if (!d_ptr)
1174 return false;
1175 if (!d_ptr->isThreeComponentMatrix())
1176 return !d_ptr->mBA.isEmpty();
1177 return d_ptr->isValid();
1178}
1179
1180/*!
1181 \internal
1182*/
1183bool QColorSpacePrivate::isValid() const noexcept
1184{
1185 if (!isThreeComponentMatrix())
1186 return !mAB.isEmpty();
1187 if (!toXyz.isValid())
1188 return false;
1189 if (colorModel == QColorSpace::ColorModel::Gray) {
1190 if (!trc[0].isValid())
1191 return false;
1192 } else if (colorModel == QColorSpace::ColorModel::Rgb){
1193 if (!trc[0].isValid() || !trc[1].isValid() || !trc[2].isValid())
1194 return false;
1195 } else {
1196 return false;
1197 }
1198 return true;
1199}
1200
1201/*!
1202 \fn bool QColorSpace::operator==(const QColorSpace &colorSpace1, const QColorSpace &colorSpace2)
1203
1204 Returns \c true if colorspace \a colorSpace1 is equal to colorspace \a colorSpace2;
1205 otherwise returns \c false
1206*/
1207
1208/*!
1209 \fn bool QColorSpace::operator!=(const QColorSpace &colorSpace1, const QColorSpace &colorSpace2)
1210
1211 Returns \c true if colorspace \a colorSpace1 is not equal to colorspace \a colorSpace2;
1212 otherwise returns \c false
1213*/
1214
1215static bool compareElement(const QColorSpacePrivate::TransferElement &element,
1216 const QColorSpacePrivate::TransferElement &other)
1217{
1218 return element.trc[0] == other.trc[0]
1219 && element.trc[1] == other.trc[1]
1220 && element.trc[2] == other.trc[2]
1221 && element.trc[3] == other.trc[3];
1222}
1223
1224static bool compareElement(const QColorMatrix &element,
1225 const QColorMatrix &other)
1226{
1227 return element == other;
1228}
1229
1230static bool compareElement(const QColorVector &element,
1231 const QColorVector &other)
1232{
1233 return element == other;
1234}
1235
1236static bool compareElement(const QColorCLUT &element,
1237 const QColorCLUT &other)
1238{
1239 if (element.gridPointsX != other.gridPointsX)
1240 return false;
1241 if (element.gridPointsY != other.gridPointsY)
1242 return false;
1243 if (element.gridPointsZ != other.gridPointsZ)
1244 return false;
1245 if (element.gridPointsW != other.gridPointsW)
1246 return false;
1247 if (element.table.size() != other.table.size())
1248 return false;
1249 for (qsizetype i = 0; i < element.table.size(); ++i) {
1250 if (element.table[i] != other.table[i])
1251 return false;
1252 }
1253 return true;
1254}
1255
1256template<typename T>
1257static bool compareElements(const T &element, const QColorSpacePrivate::Element &other)
1258{
1259 return compareElement(element, std::get<T>(other));
1260}
1261
1262/*!
1263 \internal
1264*/
1265bool QColorSpace::equals(const QColorSpace &other) const
1266{
1267 if (d_ptr == other.d_ptr)
1268 return true;
1269 if (!d_ptr)
1270 return false;
1271 return d_ptr->equals(other: other.d_ptr.constData());
1272}
1273
1274/*!
1275 \internal
1276*/
1277bool QColorSpacePrivate::equals(const QColorSpacePrivate *other) const
1278{
1279 if (!other)
1280 return false;
1281
1282 if (namedColorSpace && other->namedColorSpace)
1283 return namedColorSpace == other->namedColorSpace;
1284
1285 const bool valid1 = isValid();
1286 const bool valid2 = other->isValid();
1287 if (valid1 != valid2)
1288 return false;
1289 if (!valid1 && !valid2) {
1290 if (!iccProfile.isEmpty() || !other->iccProfile.isEmpty())
1291 return iccProfile == other->iccProfile;
1292 return false;
1293 }
1294
1295 // At this point one or both color spaces are unknown, and must be compared in detail instead
1296
1297 if (transformModel != other->transformModel)
1298 return false;
1299
1300 if (!isThreeComponentMatrix()) {
1301 if (isPcsLab != other->isPcsLab)
1302 return false;
1303 if (colorModel != other->colorModel)
1304 return false;
1305 if (mAB.count() != other->mAB.count())
1306 return false;
1307 if (mBA.count() != other->mBA.count())
1308 return false;
1309
1310 // Compare element types
1311 for (qsizetype i = 0; i < mAB.count(); ++i) {
1312 if (mAB[i].index() != other->mAB[i].index())
1313 return false;
1314 }
1315 for (qsizetype i = 0; i < mBA.count(); ++i) {
1316 if (mBA[i].index() != other->mBA[i].index())
1317 return false;
1318 }
1319
1320 // Compare element contents
1321 for (qsizetype i = 0; i < mAB.count(); ++i) {
1322 if (!std::visit(visitor: [&](auto &&elm) { return compareElements(elm, other->mAB[i]); }, variants: mAB[i]))
1323 return false;
1324 }
1325 for (qsizetype i = 0; i < mBA.count(); ++i) {
1326 if (!std::visit(visitor: [&](auto &&elm) { return compareElements(elm, other->mBA[i]); }, variants: mBA[i]))
1327 return false;
1328 }
1329
1330 return true;
1331 }
1332
1333 if (primaries != QColorSpace::Primaries::Custom && other->primaries != QColorSpace::Primaries::Custom) {
1334 if (primaries != other->primaries)
1335 return false;
1336 } else {
1337 if (toXyz != other->toXyz)
1338 return false;
1339 }
1340
1341 if (transferFunction != QColorSpace::TransferFunction::Custom && other->transferFunction != QColorSpace::TransferFunction::Custom) {
1342 if (transferFunction != other->transferFunction)
1343 return false;
1344 if (transferFunction == QColorSpace::TransferFunction::Gamma)
1345 return (qAbs(t: gamma - other->gamma) <= (1.0f / 512.0f));
1346 return true;
1347 }
1348
1349 if (trc[0] != other->trc[0] ||
1350 trc[1] != other->trc[1] ||
1351 trc[2] != other->trc[2])
1352 return false;
1353
1354 return true;
1355}
1356
1357/*!
1358 Generates and returns a color space transformation from this color space to
1359 \a colorspace.
1360*/
1361QColorTransform QColorSpace::transformationToColorSpace(const QColorSpace &colorspace) const
1362{
1363 if (!isValid())
1364 return QColorTransform();
1365
1366 if (*this == colorspace)
1367 return QColorTransform();
1368 if (!colorspace.isValidTarget()) {
1369 qWarning() << "QColorSpace::transformationToColorSpace: colorspace not a valid target";
1370 return QColorTransform();
1371 }
1372
1373 return d_ptr->transformationToColorSpace(out: colorspace.d_ptr.get());
1374}
1375
1376/*!
1377 Returns the color space as a QVariant.
1378 \since 5.15
1379*/
1380QColorSpace::operator QVariant() const
1381{
1382 return QVariant::fromValue(value: *this);
1383}
1384
1385/*!
1386 Returns the name or short description. If a description hasn't been given
1387 in setDescription(), the original name of the profile is returned if the
1388 profile is unmodified, a guessed name is returned if the profile has been
1389 recognized as a known color space, otherwise an empty string is returned.
1390
1391 \since 6.2
1392*/
1393QString QColorSpace::description() const noexcept
1394{
1395 if (d_ptr)
1396 return d_ptr->userDescription.isEmpty() ? d_ptr->description : d_ptr->userDescription;
1397 return QString();
1398}
1399
1400/*!
1401 Sets the name or short description of the color space to \a description.
1402
1403 If set to empty description() will return original or guessed descriptions
1404 instead.
1405
1406 \since 6.2
1407*/
1408void QColorSpace::setDescription(const QString &description)
1409{
1410 detach();
1411 d_ptr->iccProfile = {};
1412 d_ptr->userDescription = description;
1413}
1414
1415/*****************************************************************************
1416 QColorSpace stream functions
1417 *****************************************************************************/
1418#if !defined(QT_NO_DATASTREAM)
1419/*!
1420 \fn QDataStream &operator<<(QDataStream &stream, const QColorSpace &colorSpace)
1421 \relates QColorSpace
1422
1423 Writes the given \a colorSpace to the given \a stream as an ICC profile.
1424
1425 \sa QColorSpace::iccProfile(), {Serializing Qt Data Types}
1426*/
1427
1428QDataStream &operator<<(QDataStream &s, const QColorSpace &image)
1429{
1430 s << image.iccProfile();
1431 return s;
1432}
1433
1434/*!
1435 \fn QDataStream &operator>>(QDataStream &stream, QColorSpace &colorSpace)
1436 \relates QColorSpace
1437
1438 Reads a color space from the given \a stream and stores it in the given
1439 \a colorSpace.
1440
1441 \sa QColorSpace::fromIccProfile(), {Serializing Qt Data Types}
1442*/
1443
1444QDataStream &operator>>(QDataStream &s, QColorSpace &colorSpace)
1445{
1446 QByteArray iccProfile;
1447 s >> iccProfile;
1448 colorSpace = QColorSpace::fromIccProfile(iccProfile);
1449 return s;
1450}
1451#endif // QT_NO_DATASTREAM
1452
1453#ifndef QT_NO_DEBUG_STREAM
1454QDebug operator<<(QDebug dbg, const QColorSpacePrivate::TransferElement &)
1455{
1456 return dbg << ":Transfer";
1457}
1458QDebug operator<<(QDebug dbg, const QColorMatrix &)
1459{
1460 return dbg << ":Matrix";
1461}
1462QDebug operator<<(QDebug dbg, const QColorVector &)
1463{
1464 return dbg << ":Offset";
1465}
1466QDebug operator<<(QDebug dbg, const QColorCLUT &)
1467{
1468 return dbg << ":CLUT";
1469}
1470QDebug operator<<(QDebug dbg, const QList<QColorSpacePrivate::Element> &elements)
1471{
1472 for (auto &&element : elements)
1473 std::visit(visitor: [&](auto &&elm) { dbg << elm; }, variants: element);
1474 return dbg;
1475}
1476QDebug operator<<(QDebug dbg, const QColorSpace &colorSpace)
1477{
1478 QDebugStateSaver saver(dbg);
1479 dbg.nospace();
1480 dbg << "QColorSpace(";
1481 if (colorSpace.d_ptr) {
1482 if (colorSpace.d_ptr->namedColorSpace)
1483 dbg << colorSpace.d_ptr->namedColorSpace << ", ";
1484 else
1485 dbg << colorSpace.colorModel() << ", ";
1486 if (!colorSpace.isValid()) {
1487 dbg << "Invalid";
1488 if (!colorSpace.d_ptr->iccProfile.isEmpty())
1489 dbg << " with profile data";
1490 } else if (colorSpace.d_ptr->isThreeComponentMatrix()) {
1491 dbg << colorSpace.primaries() << ", " << colorSpace.transferFunction();
1492 if (colorSpace.transferFunction() == QColorSpace::TransferFunction::Gamma)
1493 dbg << "=" << colorSpace.gamma();
1494 } else {
1495 if (colorSpace.d_ptr->isPcsLab)
1496 dbg << "PCSLab, ";
1497 else
1498 dbg << "PCSXYZ, ";
1499 dbg << "A2B" << colorSpace.d_ptr->mAB;
1500 if (!colorSpace.d_ptr->mBA.isEmpty())
1501 dbg << ", B2A" << colorSpace.d_ptr->mBA;
1502 }
1503 }
1504 dbg << ')';
1505 return dbg;
1506}
1507#endif
1508
1509QT_END_NAMESPACE
1510
1511#include "moc_qcolorspace.cpp"
1512

Provided by KDAB

Privacy Policy
Start learning QML with our Intro Training
Find out more

source code of qtbase/src/gui/painting/qcolorspace.cpp