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

Provided by KDAB

Privacy Policy
Learn to use CMake with our Intro Training
Find out more

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