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 // Must detach if input is fromRawData(); nullTerminated() is trick to do that and nothing else
1210 QByteArray ownedIccProfile = iccProfile.nullTerminated();
1211 if (QIcc::fromIccProfile(ownedIccProfile, &colorSpace))
1212 return colorSpace;
1213 colorSpace.detach();
1214 colorSpace.d_ptr->iccProfile = std::move(ownedIccProfile);
1215 return colorSpace;
1216}
1217
1218/*!
1219 Returns \c true if the color space is valid. For a color space with \c TransformModel::ThreeComponentMatrix
1220 that means both primaries and transfer functions set, and implies isValidTarget().
1221 For a color space with \c TransformModel::ElementListProcessing it means it has a valid source transform, to
1222 check if it also a valid target color space use isValidTarget().
1223
1224 \sa isValidTarget()
1225*/
1226bool QColorSpace::isValid() const noexcept
1227{
1228 if (!d_ptr)
1229 return false;
1230 return d_ptr->isValid();
1231}
1232
1233/*!
1234 \since 6.8
1235
1236 Returns \c true if the color space is a valid target color space.
1237*/
1238bool QColorSpace::isValidTarget() const noexcept
1239{
1240 if (!d_ptr)
1241 return false;
1242 if (!d_ptr->isThreeComponentMatrix())
1243 return !d_ptr->mBA.isEmpty();
1244 return d_ptr->isValid();
1245}
1246
1247/*!
1248 \internal
1249*/
1250bool QColorSpacePrivate::isValid() const noexcept
1251{
1252 if (!isThreeComponentMatrix())
1253 return !mAB.isEmpty();
1254 if (!toXyz.isValid())
1255 return false;
1256 if (colorModel == QColorSpace::ColorModel::Gray) {
1257 if (!trc[0].isValid())
1258 return false;
1259 } else if (colorModel == QColorSpace::ColorModel::Rgb){
1260 if (!trc[0].isValid() || !trc[1].isValid() || !trc[2].isValid())
1261 return false;
1262 } else {
1263 return false;
1264 }
1265 return true;
1266}
1267
1268/*!
1269 \fn bool QColorSpace::operator==(const QColorSpace &colorSpace1, const QColorSpace &colorSpace2)
1270
1271 Returns \c true if colorspace \a colorSpace1 is equal to colorspace \a colorSpace2;
1272 otherwise returns \c false
1273*/
1274
1275/*!
1276 \fn bool QColorSpace::operator!=(const QColorSpace &colorSpace1, const QColorSpace &colorSpace2)
1277
1278 Returns \c true if colorspace \a colorSpace1 is not equal to colorspace \a colorSpace2;
1279 otherwise returns \c false
1280*/
1281
1283 const QColorSpacePrivate::TransferElement &other)
1284{
1285 return element.trc[0] == other.trc[0]
1286 && element.trc[1] == other.trc[1]
1287 && element.trc[2] == other.trc[2]
1288 && element.trc[3] == other.trc[3];
1289}
1290
1291static bool compareElement(const QColorMatrix &element,
1292 const QColorMatrix &other)
1293{
1294 return element == other;
1295}
1296
1297static bool compareElement(const QColorVector &element,
1298 const QColorVector &other)
1299{
1300 return element == other;
1301}
1302
1303static bool compareElement(const QColorCLUT &element,
1304 const QColorCLUT &other)
1305{
1306 if (element.gridPointsX != other.gridPointsX)
1307 return false;
1308 if (element.gridPointsY != other.gridPointsY)
1309 return false;
1310 if (element.gridPointsZ != other.gridPointsZ)
1311 return false;
1312 if (element.gridPointsW != other.gridPointsW)
1313 return false;
1314 if (element.table.size() != other.table.size())
1315 return false;
1316 for (qsizetype i = 0; i < element.table.size(); ++i) {
1317 if (element.table[i] != other.table[i])
1318 return false;
1319 }
1320 return true;
1321}
1322
1323template<typename T>
1324static bool compareElements(const T &element, const QColorSpacePrivate::Element &other)
1325{
1326 return compareElement(element, std::get<T>(other));
1327}
1328
1329/*!
1330 \internal
1331*/
1332bool QColorSpace::equals(const QColorSpace &other) const
1333{
1334 if (d_ptr == other.d_ptr)
1335 return true;
1336 if (!d_ptr)
1337 return false;
1338 return d_ptr->equals(other.d_ptr.constData());
1339}
1340
1341/*!
1342 \internal
1343*/
1345{
1346 if (!other)
1347 return false;
1348
1349 if (namedColorSpace && other->namedColorSpace)
1350 return namedColorSpace == other->namedColorSpace;
1351
1352 const bool valid1 = isValid();
1353 const bool valid2 = other->isValid();
1354 if (valid1 != valid2)
1355 return false;
1356 if (!valid1 && !valid2) {
1357 if (!iccProfile.isEmpty() || !other->iccProfile.isEmpty())
1358 return iccProfile == other->iccProfile;
1359 return false;
1360 }
1361
1362 // At this point one or both color spaces are unknown, and must be compared in detail instead
1363
1364 if (transformModel != other->transformModel)
1365 return false;
1366
1368 if (isPcsLab != other->isPcsLab)
1369 return false;
1370 if (colorModel != other->colorModel)
1371 return false;
1372 if (mAB.count() != other->mAB.count())
1373 return false;
1374 if (mBA.count() != other->mBA.count())
1375 return false;
1376
1377 // Compare element types
1378 for (qsizetype i = 0; i < mAB.count(); ++i) {
1379 if (mAB[i].index() != other->mAB[i].index())
1380 return false;
1381 }
1382 for (qsizetype i = 0; i < mBA.count(); ++i) {
1383 if (mBA[i].index() != other->mBA[i].index())
1384 return false;
1385 }
1386
1387 // Compare element contents
1388 for (qsizetype i = 0; i < mAB.count(); ++i) {
1389 if (!std::visit([&](auto &&elm) { return compareElements(elm, other->mAB[i]); }, mAB[i]))
1390 return false;
1391 }
1392 for (qsizetype i = 0; i < mBA.count(); ++i) {
1393 if (!std::visit([&](auto &&elm) { return compareElements(elm, other->mBA[i]); }, mBA[i]))
1394 return false;
1395 }
1396
1397 return true;
1398 }
1399
1400 if (primaries != QColorSpace::Primaries::Custom && other->primaries != QColorSpace::Primaries::Custom) {
1401 if (primaries != other->primaries)
1402 return false;
1403 } else {
1404 if (toXyz != other->toXyz)
1405 return false;
1406 }
1407
1408 if (transferFunction != QColorSpace::TransferFunction::Custom && other->transferFunction != QColorSpace::TransferFunction::Custom) {
1409 if (transferFunction != other->transferFunction)
1410 return false;
1411 if (transferFunction == QColorSpace::TransferFunction::Gamma)
1412 return (qAbs(gamma - other->gamma) <= (1.0f / 512.0f));
1413 return true;
1414 }
1415
1416 if (trc[0] != other->trc[0] ||
1417 trc[1] != other->trc[1] ||
1418 trc[2] != other->trc[2])
1419 return false;
1420
1421 return true;
1422}
1423
1424/*!
1425 Generates and returns a color space transformation from this color space to
1426 \a colorspace.
1427*/
1428QColorTransform QColorSpace::transformationToColorSpace(const QColorSpace &colorspace) const
1429{
1430 if (!isValid())
1431 return QColorTransform();
1432
1433 if (*this == colorspace)
1434 return QColorTransform();
1435 if (!colorspace.isValidTarget()) {
1436 qWarning() << "QColorSpace::transformationToColorSpace: colorspace not a valid target";
1437 return QColorTransform();
1438 }
1439
1440 return d_ptr->transformationToColorSpace(colorspace.d_ptr.get());
1441}
1442
1443/*!
1444 Returns the color space as a QVariant.
1445 \since 5.15
1446*/
1447QColorSpace::operator QVariant() const
1448{
1449 return QVariant::fromValue(*this);
1450}
1451
1452/*!
1453 Returns the name or short description. If a description hasn't been given
1454 in setDescription(), the original name of the profile is returned if the
1455 profile is unmodified, a guessed name is returned if the profile has been
1456 recognized as a known color space, otherwise an empty string is returned.
1457
1458 \since 6.2
1459*/
1460QString QColorSpace::description() const noexcept
1461{
1462 if (d_ptr)
1463 return d_ptr->userDescription.isEmpty() ? d_ptr->description : d_ptr->userDescription;
1464 return QString();
1465}
1466
1467/*!
1468 Sets the name or short description of the color space to \a description.
1469
1470 If set to empty description() will return original or guessed descriptions
1471 instead.
1472
1473 \since 6.2
1474*/
1475void QColorSpace::setDescription(const QString &description)
1476{
1477 detach();
1478 d_ptr->iccProfile = {};
1479 d_ptr->userDescription = description;
1480}
1481
1482/*****************************************************************************
1483 QColorSpace stream functions
1484 *****************************************************************************/
1485#if !defined(QT_NO_DATASTREAM)
1486/*!
1487 \fn QDataStream &operator<<(QDataStream &stream, const QColorSpace &colorSpace)
1488 \relates QColorSpace
1489
1490 Writes the given \a colorSpace to the given \a stream as an ICC profile.
1491
1492 \sa QColorSpace::iccProfile(), {Serializing Qt Data Types}
1493*/
1494
1495QDataStream &operator<<(QDataStream &s, const QColorSpace &image)
1496{
1497 s << image.iccProfile();
1498 return s;
1499}
1500
1501/*!
1502 \fn QDataStream &operator>>(QDataStream &stream, QColorSpace &colorSpace)
1503 \relates QColorSpace
1504
1505 Reads a color space from the given \a stream and stores it in the given
1506 \a colorSpace.
1507
1508 \sa QColorSpace::fromIccProfile(), {Serializing Qt Data Types}
1509*/
1510
1511QDataStream &operator>>(QDataStream &s, QColorSpace &colorSpace)
1512{
1513 QByteArray iccProfile;
1514 s >> iccProfile;
1515 colorSpace = QColorSpace::fromIccProfile(iccProfile);
1516 return s;
1517}
1518#endif // QT_NO_DATASTREAM
1519
1520#ifndef QT_NO_DEBUG_STREAM
1522{
1523 return dbg << ":Transfer";
1524}
1525QDebug operator<<(QDebug dbg, const QColorMatrix &)
1526{
1527 return dbg << ":Matrix";
1528}
1529QDebug operator<<(QDebug dbg, const QColorVector &)
1530{
1531 return dbg << ":Offset";
1532}
1533QDebug operator<<(QDebug dbg, const QColorCLUT &)
1534{
1535 return dbg << ":CLUT";
1536}
1537QDebug operator<<(QDebug dbg, const QList<QColorSpacePrivate::Element> &elements)
1538{
1539 for (auto &&element : elements)
1540 std::visit([&](auto &&elm) { dbg << elm; }, element);
1541 return dbg;
1542}
1543QDebug operator<<(QDebug dbg, const QColorSpace &colorSpace)
1544{
1545 QDebugStateSaver saver(dbg);
1546 dbg.nospace();
1547 dbg << "QColorSpace(";
1548 if (colorSpace.d_ptr) {
1549 if (colorSpace.d_ptr->namedColorSpace)
1550 dbg << colorSpace.d_ptr->namedColorSpace << ", ";
1551 else
1552 dbg << colorSpace.colorModel() << ", ";
1553 if (!colorSpace.isValid()) {
1554 dbg << "Invalid";
1555 if (!colorSpace.d_ptr->iccProfile.isEmpty())
1556 dbg << " with profile data";
1557 } else if (colorSpace.d_ptr->isThreeComponentMatrix()) {
1558 dbg << colorSpace.primaries() << ", " << colorSpace.transferFunction();
1559 if (colorSpace.transferFunction() == QColorSpace::TransferFunction::Gamma)
1560 dbg << "=" << colorSpace.gamma();
1561 } else {
1562 if (colorSpace.d_ptr->isPcsLab)
1563 dbg << "PCSLab, ";
1564 else
1565 dbg << "PCSXYZ, ";
1566 dbg << "A2B" << colorSpace.d_ptr->mAB;
1567 if (!colorSpace.d_ptr->mBA.isEmpty())
1568 dbg << ", B2A" << colorSpace.d_ptr->mBA;
1569 }
1570 }
1571 dbg << ')';
1572 return dbg;
1573}
1574#endif
1575
1576QT_END_NAMESPACE
1577
1578#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)