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