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