1/****************************************************************************
2**
3** Copyright (C) 2018 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtGui module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "qcolorspace.h"
41#include "qcolorspace_p.h"
42
43#include "qcolortransform.h"
44#include "qcolormatrix_p.h"
45#include "qcolortransferfunction_p.h"
46#include "qcolortransform_p.h"
47#include "qicc_p.h"
48
49#include <qatomic.h>
50#include <qmath.h>
51#include <qtransform.h>
52
53#include <qdebug.h>
54
55QT_BEGIN_NAMESPACE
56
57QBasicMutex QColorSpacePrivate::s_lutWriteLock;
58
59static QAtomicPointer<QColorSpacePrivate> s_predefinedColorspacePrivates[QColorSpace::ProPhotoRgb] = {};
60static void cleanupPredefinedColorspaces()
61{
62 for (QAtomicPointer<QColorSpacePrivate> &ptr : s_predefinedColorspacePrivates) {
63 QColorSpacePrivate *prv = ptr.fetchAndStoreAcquire(newValue: nullptr);
64 if (prv && !prv->ref.deref())
65 delete prv;
66 }
67}
68
69Q_DESTRUCTOR_FUNCTION(cleanupPredefinedColorspaces)
70
71QColorSpacePrimaries::QColorSpacePrimaries(QColorSpace::Primaries primaries)
72{
73 switch (primaries) {
74 case QColorSpace::Primaries::SRgb:
75 redPoint = QPointF(0.640, 0.330);
76 greenPoint = QPointF(0.300, 0.600);
77 bluePoint = QPointF(0.150, 0.060);
78 whitePoint = QColorVector::D65Chromaticity();
79 break;
80 case QColorSpace::Primaries::DciP3D65:
81 redPoint = QPointF(0.680, 0.320);
82 greenPoint = QPointF(0.265, 0.690);
83 bluePoint = QPointF(0.150, 0.060);
84 whitePoint = QColorVector::D65Chromaticity();
85 break;
86 case QColorSpace::Primaries::AdobeRgb:
87 redPoint = QPointF(0.640, 0.330);
88 greenPoint = QPointF(0.210, 0.710);
89 bluePoint = QPointF(0.150, 0.060);
90 whitePoint = QColorVector::D65Chromaticity();
91 break;
92 case QColorSpace::Primaries::ProPhotoRgb:
93 redPoint = QPointF(0.7347, 0.2653);
94 greenPoint = QPointF(0.1596, 0.8404);
95 bluePoint = QPointF(0.0366, 0.0001);
96 whitePoint = QColorVector::D50Chromaticity();
97 break;
98 default:
99 Q_UNREACHABLE();
100 }
101}
102
103bool QColorSpacePrimaries::areValid() const
104{
105 if (!QColorVector::isValidChromaticity(chr: redPoint))
106 return false;
107 if (!QColorVector::isValidChromaticity(chr: greenPoint))
108 return false;
109 if (!QColorVector::isValidChromaticity(chr: bluePoint))
110 return false;
111 if (!QColorVector::isValidChromaticity(chr: whitePoint))
112 return false;
113 return true;
114}
115
116QColorMatrix QColorSpacePrimaries::toXyzMatrix() const
117{
118 // This converts to XYZ in some undefined scale.
119 QColorMatrix toXyz = { .r: QColorVector(redPoint),
120 .g: QColorVector(greenPoint),
121 .b: QColorVector(bluePoint) };
122
123 // Since the white point should be (1.0, 1.0, 1.0) in the
124 // input, we can figure out the scale by using the
125 // inverse conversion on the white point.
126 QColorVector wXyz(whitePoint);
127 QColorVector whiteScale = toXyz.inverted().map(c: wXyz);
128
129 // Now we have scaled conversion to XYZ relative to the given whitepoint
130 toXyz = toXyz * QColorMatrix::fromScale(v: whiteScale);
131
132 // But we want a conversion to XYZ relative to D50
133 QColorVector wXyzD50 = QColorVector::D50();
134
135 if (wXyz != wXyzD50) {
136 // Do chromatic adaptation to map our white point to XYZ D50.
137
138 // The Bradford method chromatic adaptation matrix:
139 QColorMatrix abrad = { .r: { 0.8951f, -0.7502f, 0.0389f },
140 .g: { 0.2664f, 1.7135f, -0.0685f },
141 .b: { -0.1614f, 0.0367f, 1.0296f } };
142 QColorMatrix abradinv = { .r: { 0.9869929f, 0.4323053f, -0.0085287f },
143 .g: { -0.1470543f, 0.5183603f, 0.0400428f },
144 .b: { 0.1599627f, 0.0492912f, 0.9684867f } };
145
146 QColorVector srcCone = abrad.map(c: wXyz);
147 QColorVector dstCone = abrad.map(c: wXyzD50);
148
149 if (srcCone.x && srcCone.y && srcCone.z) {
150 QColorMatrix wToD50 = { .r: { dstCone.x / srcCone.x, 0, 0 },
151 .g: { 0, dstCone.y / srcCone.y, 0 },
152 .b: { 0, 0, dstCone.z / srcCone.z } };
153
154
155 QColorMatrix chromaticAdaptation = abradinv * (wToD50 * abrad);
156 toXyz = chromaticAdaptation * toXyz;
157 } else {
158 toXyz.r = {0, 0, 0}; // set to invalid value
159 }
160 }
161
162 return toXyz;
163}
164
165QColorSpacePrivate::QColorSpacePrivate()
166{
167}
168
169QColorSpacePrivate::QColorSpacePrivate(QColorSpace::NamedColorSpace namedColorSpace)
170 : namedColorSpace(namedColorSpace)
171{
172 switch (namedColorSpace) {
173 case QColorSpace::SRgb:
174 primaries = QColorSpace::Primaries::SRgb;
175 transferFunction = QColorSpace::TransferFunction::SRgb;
176 description = QStringLiteral("sRGB");
177 break;
178 case QColorSpace::SRgbLinear:
179 primaries = QColorSpace::Primaries::SRgb;
180 transferFunction = QColorSpace::TransferFunction::Linear;
181 description = QStringLiteral("Linear sRGB");
182 break;
183 case QColorSpace::AdobeRgb:
184 primaries = QColorSpace::Primaries::AdobeRgb;
185 transferFunction = QColorSpace::TransferFunction::Gamma;
186 gamma = 2.19921875f; // Not quite 2.2, see https://www.adobe.com/digitalimag/pdfs/AdobeRGB1998.pdf
187 description = QStringLiteral("Adobe RGB");
188 break;
189 case QColorSpace::DisplayP3:
190 primaries = QColorSpace::Primaries::DciP3D65;
191 transferFunction = QColorSpace::TransferFunction::SRgb;
192 description = QStringLiteral("Display P3");
193 break;
194 case QColorSpace::ProPhotoRgb:
195 primaries = QColorSpace::Primaries::ProPhotoRgb;
196 transferFunction = QColorSpace::TransferFunction::ProPhotoRgb;
197 description = QStringLiteral("ProPhoto RGB");
198 break;
199 default:
200 Q_UNREACHABLE();
201 }
202 initialize();
203}
204
205QColorSpacePrivate::QColorSpacePrivate(QColorSpace::Primaries primaries, QColorSpace::TransferFunction transferFunction, float gamma)
206 : primaries(primaries)
207 , transferFunction(transferFunction)
208 , gamma(gamma)
209{
210 identifyColorSpace();
211 initialize();
212}
213
214QColorSpacePrivate::QColorSpacePrivate(const QColorSpacePrimaries &primaries,
215 QColorSpace::TransferFunction transferFunction,
216 float gamma)
217 : primaries(QColorSpace::Primaries::Custom)
218 , transferFunction(transferFunction)
219 , gamma(gamma)
220{
221 Q_ASSERT(primaries.areValid());
222 toXyz = primaries.toXyzMatrix();
223 whitePoint = QColorVector(primaries.whitePoint);
224 identifyColorSpace();
225 setTransferFunction();
226}
227
228void QColorSpacePrivate::identifyColorSpace()
229{
230 switch (primaries) {
231 case QColorSpace::Primaries::SRgb:
232 if (transferFunction == QColorSpace::TransferFunction::SRgb) {
233 namedColorSpace = QColorSpace::SRgb;
234 if (description.isEmpty())
235 description = QStringLiteral("sRGB");
236 return;
237 }
238 if (transferFunction == QColorSpace::TransferFunction::Linear) {
239 namedColorSpace = QColorSpace::SRgbLinear;
240 if (description.isEmpty())
241 description = QStringLiteral("Linear sRGB");
242 return;
243 }
244 break;
245 case QColorSpace::Primaries::AdobeRgb:
246 if (transferFunction == QColorSpace::TransferFunction::Gamma) {
247 if (qAbs(t: gamma - 2.19921875f) < (1/1024.0f)) {
248 namedColorSpace = QColorSpace::AdobeRgb;
249 if (description.isEmpty())
250 description = QStringLiteral("Adobe RGB");
251 return;
252 }
253 }
254 break;
255 case QColorSpace::Primaries::DciP3D65:
256 if (transferFunction == QColorSpace::TransferFunction::SRgb) {
257 namedColorSpace = QColorSpace::DisplayP3;
258 if (description.isEmpty())
259 description = QStringLiteral("Display P3");
260 return;
261 }
262 break;
263 case QColorSpace::Primaries::ProPhotoRgb:
264 if (transferFunction == QColorSpace::TransferFunction::ProPhotoRgb) {
265 namedColorSpace = QColorSpace::ProPhotoRgb;
266 if (description.isEmpty())
267 description = QStringLiteral("ProPhoto RGB");
268 return;
269 }
270 if (transferFunction == QColorSpace::TransferFunction::Gamma) {
271 // ProPhoto RGB's curve is effectively gamma 1.8 for 8bit precision.
272 if (qAbs(t: gamma - 1.8f) < (1/1024.0f)) {
273 namedColorSpace = QColorSpace::ProPhotoRgb;
274 if (description.isEmpty())
275 description = QStringLiteral("ProPhoto RGB");
276 return;
277 }
278 }
279 break;
280 default:
281 break;
282 }
283
284 namedColorSpace = Unknown;
285}
286
287void QColorSpacePrivate::initialize()
288{
289 setToXyzMatrix();
290 setTransferFunction();
291}
292
293void QColorSpacePrivate::setToXyzMatrix()
294{
295 if (primaries == QColorSpace::Primaries::Custom) {
296 toXyz = QColorMatrix();
297 whitePoint = QColorVector::D50();
298 return;
299 }
300 QColorSpacePrimaries colorSpacePrimaries(primaries);
301 toXyz = colorSpacePrimaries.toXyzMatrix();
302 whitePoint = QColorVector(colorSpacePrimaries.whitePoint);
303}
304
305void QColorSpacePrivate::setTransferFunction()
306{
307 switch (transferFunction) {
308 case QColorSpace::TransferFunction::Linear:
309 trc[0].m_type = QColorTrc::Type::Function;
310 trc[0].m_fun = QColorTransferFunction();
311 if (qFuzzyIsNull(f: gamma))
312 gamma = 1.0f;
313 break;
314 case QColorSpace::TransferFunction::Gamma:
315 trc[0].m_type = QColorTrc::Type::Function;
316 trc[0].m_fun = QColorTransferFunction::fromGamma(gamma);
317 break;
318 case QColorSpace::TransferFunction::SRgb:
319 trc[0].m_type = QColorTrc::Type::Function;
320 trc[0].m_fun = QColorTransferFunction::fromSRgb();
321 if (qFuzzyIsNull(f: gamma))
322 gamma = 2.31f;
323 break;
324 case QColorSpace::TransferFunction::ProPhotoRgb:
325 trc[0].m_type = QColorTrc::Type::Function;
326 trc[0].m_fun = QColorTransferFunction::fromProPhotoRgb();
327 if (qFuzzyIsNull(f: gamma))
328 gamma = 1.8f;
329 break;
330 case QColorSpace::TransferFunction::Custom:
331 break;
332 default:
333 Q_UNREACHABLE();
334 break;
335 }
336 trc[1] = trc[0];
337 trc[2] = trc[0];
338 lut.generated.storeRelease(newValue: 0);
339}
340
341QColorTransform QColorSpacePrivate::transformationToColorSpace(const QColorSpacePrivate *out) const
342{
343 Q_ASSERT(out);
344 QColorTransform combined;
345 auto ptr = new QColorTransformPrivate;
346 combined.d = ptr;
347 combined.d->ref.ref();
348 ptr->colorSpaceIn = this;
349 ptr->colorSpaceOut = out;
350 ptr->colorMatrix = out->toXyz.inverted() * toXyz;
351 return combined;
352}
353
354/*!
355 \class QColorSpace
356 \brief The QColorSpace class provides a color space abstraction.
357 \since 5.14
358
359 \ingroup painting
360 \ingroup appearance
361 \inmodule QtGui
362
363 Color values can be interpreted in different ways, and based on the interpretation
364 can live in different spaces. We call this \e {color spaces}.
365
366 QColorSpace provides access to creating several predefined color spaces and
367 can generate QColorTransforms for converting colors from one color space to
368 another.
369
370 QColorSpace can also represent color spaces defined by ICC profiles or embedded
371 in images, that do not otherwise fit the predefined color spaces.
372
373 A color space can generally speaking be conceived as a combination of set of primary
374 colors and a transfer function. The primaries defines the axes of the color space, and
375 the transfer function how values are mapped on the axes.
376 The primaries are defined by three primary colors that represent exactly how red, green,
377 and blue look in this particular color space, and a white color that represents where
378 and how bright pure white is. The range of colors expressable by the primary colors is
379 called the gamut, and a color space that can represent a wider range of colors is also
380 known as a wide-gamut color space.
381
382 The transfer function or gamma curve determines how each component in the
383 color space is encoded. These are used because human perception does not operate
384 linearly, and the transfer functions try to ensure that colors will seem evenly
385 spaced to human eyes.
386*/
387
388
389/*!
390 \enum QColorSpace::NamedColorSpace
391
392 Predefined color spaces.
393
394 \value SRgb The sRGB color space, which Qt operates in by default. It is a close approximation
395 of how most classic monitors operate, and a mode most software and hardware support.
396 \l{http://www.color.org/chardata/rgb/srgb.xalter}{ICC registration of sRGB}.
397 \value SRgbLinear The sRGB color space with linear gamma. Useful for gamma-corrected blending.
398 \value AdobeRgb The Adobe RGB color space is a classic wide-gamut color space, using a gamma of 2.2.
399 \l{http://www.color.org/chardata/rgb/adobergb.xalter}{ICC registration of Adobe RGB (1998)}
400 \value DisplayP3 A color-space using the primaries of DCI-P3, but with the whitepoint and transfer
401 function of sRGB. Common in modern wide-gamut screens.
402 \l{http://www.color.org/chardata/rgb/DCIP3.xalter}{ICC registration of DCI-P3}
403 \value ProPhotoRgb The Pro Photo RGB color space, also known as ROMM RGB is a very wide gamut color space.
404 \l{http://www.color.org/chardata/rgb/rommrgb.xalter}{ICC registration of ROMM RGB}
405*/
406
407/*!
408 \enum QColorSpace::Primaries
409
410 Predefined sets of primary colors.
411
412 \value Custom The primaries are undefined or does not match any predefined sets.
413 \value SRgb The sRGB primaries
414 \value AdobeRgb The Adobe RGB primaries
415 \value DciP3D65 The DCI-P3 primaries with the D65 whitepoint
416 \value ProPhotoRgb The ProPhoto RGB primaries with the D50 whitepoint
417*/
418
419/*!
420 \enum QColorSpace::TransferFunction
421
422 Predefined transfer functions or gamma curves.
423
424 \value Custom The custom or null transfer function
425 \value Linear The linear transfer functions
426 \value Gamma A transfer function that is a real gamma curve based on the value of gamma()
427 \value SRgb The sRGB transfer function, composed of linear and gamma parts
428 \value ProPhotoRgb The ProPhoto RGB transfer function, composed of linear and gamma parts
429*/
430
431/*!
432 Creates a new colorspace object that represents an undefined and invalid colorspace.
433 */
434QColorSpace::QColorSpace()
435{
436}
437
438/*!
439 Creates a new colorspace object that represents a \a namedColorSpace.
440 */
441QColorSpace::QColorSpace(NamedColorSpace namedColorSpace)
442{
443 if (namedColorSpace < QColorSpace::SRgb || namedColorSpace > QColorSpace::ProPhotoRgb) {
444 qWarning() << "QColorSpace attempted constructed from invalid QColorSpace::NamedColorSpace: " << int(namedColorSpace);
445 return;
446 }
447 // The defined namespaces start at 1:
448 auto &atomicRef = s_predefinedColorspacePrivates[static_cast<int>(namedColorSpace) - 1];
449 QColorSpacePrivate *cspriv = atomicRef.loadAcquire();
450 if (!cspriv) {
451 auto *tmp = new QColorSpacePrivate(namedColorSpace);
452 tmp->ref.ref();
453 if (atomicRef.testAndSetOrdered(expectedValue: nullptr, newValue: tmp, currentValue&: cspriv))
454 cspriv = tmp;
455 else
456 delete tmp;
457 }
458 d_ptr = cspriv;
459 d_ptr->ref.ref();
460 Q_ASSERT(isValid());
461}
462
463/*!
464 Creates a custom color space with the primaries \a primaries, using the transfer function \a transferFunction and
465 optionally \a gamma.
466 */
467QColorSpace::QColorSpace(QColorSpace::Primaries primaries, QColorSpace::TransferFunction transferFunction, float gamma)
468 : d_ptr(new QColorSpacePrivate(primaries, transferFunction, gamma))
469{
470 d_ptr->ref.ref();
471}
472
473/*!
474 Creates a custom color space with the primaries \a primaries, using a gamma transfer function of
475 \a gamma.
476 */
477QColorSpace::QColorSpace(QColorSpace::Primaries primaries, float gamma)
478 : d_ptr(new QColorSpacePrivate(primaries, TransferFunction::Gamma, gamma))
479{
480 d_ptr->ref.ref();
481}
482
483/*!
484 Creates a custom colorspace with a primaries based on the chromaticities of the primary colors \a whitePoint,
485 \a redPoint, \a greenPoint and \a bluePoint, and using the transfer function \a transferFunction and optionally \a gamma.
486 */
487QColorSpace::QColorSpace(const QPointF &whitePoint, const QPointF &redPoint,
488 const QPointF &greenPoint, const QPointF &bluePoint,
489 QColorSpace::TransferFunction transferFunction, float gamma)
490{
491 QColorSpacePrimaries primaries(whitePoint, redPoint, greenPoint, bluePoint);
492 if (!primaries.areValid()) {
493 qWarning() << "QColorSpace attempted constructed from invalid primaries:" << whitePoint << redPoint << greenPoint << bluePoint;
494 d_ptr = nullptr;
495 return;
496 }
497 d_ptr = new QColorSpacePrivate(primaries, transferFunction, gamma);
498 d_ptr->ref.ref();
499}
500
501QColorSpace::~QColorSpace()
502{
503 if (d_ptr && !d_ptr->ref.deref())
504 delete d_ptr;
505}
506
507QColorSpace::QColorSpace(const QColorSpace &colorSpace)
508 : d_ptr(colorSpace.d_ptr)
509{
510 if (d_ptr)
511 d_ptr->ref.ref();
512}
513
514QColorSpace &QColorSpace::operator=(const QColorSpace &colorSpace)
515{
516 QColorSpacePrivate *oldD = d_ptr;
517 d_ptr = colorSpace.d_ptr;
518 if (d_ptr)
519 d_ptr->ref.ref();
520 if (oldD && !oldD->ref.deref())
521 delete oldD;
522 return *this;
523}
524
525/*! \fn void QColorSpace::swap(QColorSpace &other)
526
527 Swaps color space \a other with this color space. This operation is very fast and
528 never fails.
529*/
530
531/*!
532 Returns the predefined primaries of the color space
533 or \c primaries::Custom if it doesn't match any of them.
534*/
535QColorSpace::Primaries QColorSpace::primaries() const noexcept
536{
537 if (Q_UNLIKELY(!d_ptr))
538 return QColorSpace::Primaries::Custom;
539 return d_ptr->primaries;
540}
541
542/*!
543 Returns the predefined transfer function of the color space
544 or \c TransferFunction::Custom if it doesn't match any of them.
545
546 \sa gamma(), setTransferFunction(), withTransferFunction()
547*/
548QColorSpace::TransferFunction QColorSpace::transferFunction() const noexcept
549{
550 if (Q_UNLIKELY(!d_ptr))
551 return QColorSpace::TransferFunction::Custom;
552 return d_ptr->transferFunction;
553}
554
555/*!
556 Returns the gamma value of color spaces with \c TransferFunction::Gamma,
557 an approximate gamma value for other predefined color spaces, or
558 0.0 if no approximate gamma is known.
559
560 \sa transferFunction()
561*/
562float QColorSpace::gamma() const noexcept
563{
564 if (Q_UNLIKELY(!d_ptr))
565 return 0.0f;
566 return d_ptr->gamma;
567}
568
569/*!
570 Sets the transfer function to \a transferFunction and \a gamma.
571
572 \sa transferFunction(), gamma(), withTransferFunction()
573*/
574void QColorSpace::setTransferFunction(QColorSpace::TransferFunction transferFunction, float gamma)
575{
576 if (transferFunction == TransferFunction::Custom)
577 return;
578 if (!d_ptr) {
579 d_ptr = new QColorSpacePrivate(Primaries::Custom, transferFunction, gamma);
580 d_ptr->ref.ref();
581 return;
582 }
583 if (d_ptr->transferFunction == transferFunction && d_ptr->gamma == gamma)
584 return;
585 QColorSpacePrivate::getWritable(colorSpace&: *this); // detach
586 d_ptr->description.clear();
587 d_ptr->transferFunction = transferFunction;
588 d_ptr->gamma = gamma;
589 d_ptr->identifyColorSpace();
590 d_ptr->setTransferFunction();
591}
592
593/*!
594 Returns a copy of this color space, except using the transfer function
595 \a transferFunction and \a gamma.
596
597 \sa transferFunction(), gamma(), setTransferFunction()
598*/
599QColorSpace QColorSpace::withTransferFunction(QColorSpace::TransferFunction transferFunction, float gamma) const
600{
601 if (!isValid() || transferFunction == QColorSpace::TransferFunction::Custom)
602 return *this;
603 if (d_ptr->transferFunction == transferFunction && d_ptr->gamma == gamma)
604 return *this;
605 QColorSpace out(*this);
606 out.setTransferFunction(transferFunction, gamma);
607 return out;
608}
609
610/*!
611 Sets the primaries to those of the \a primariesId set.
612
613 \sa primaries()
614*/
615void QColorSpace::setPrimaries(QColorSpace::Primaries primariesId)
616{
617 if (primariesId == Primaries::Custom)
618 return;
619 if (!d_ptr) {
620 d_ptr = new QColorSpacePrivate(primariesId, TransferFunction::Custom, 0.0f);
621 d_ptr->ref.ref();
622 return;
623 }
624 if (d_ptr->primaries == primariesId)
625 return;
626 QColorSpacePrivate::getWritable(colorSpace&: *this); // detach
627 d_ptr->description.clear();
628 d_ptr->primaries = primariesId;
629 d_ptr->identifyColorSpace();
630 d_ptr->setToXyzMatrix();
631}
632
633/*!
634 Set primaries to the chromaticities of \a whitePoint, \a redPoint, \a greenPoint
635 and \a bluePoint.
636
637 \sa primaries()
638*/
639void QColorSpace::setPrimaries(const QPointF &whitePoint, const QPointF &redPoint,
640 const QPointF &greenPoint, const QPointF &bluePoint)
641{
642 QColorSpacePrimaries primaries(whitePoint, redPoint, greenPoint, bluePoint);
643 if (!primaries.areValid())
644 return;
645 if (!d_ptr) {
646 d_ptr = new QColorSpacePrivate(primaries, TransferFunction::Custom, 0.0f);
647 d_ptr->ref.ref();
648 return;
649 }
650 QColorMatrix toXyz = primaries.toXyzMatrix();
651 if (QColorVector(primaries.whitePoint) == d_ptr->whitePoint && toXyz == d_ptr->toXyz)
652 return;
653 QColorSpacePrivate::getWritable(colorSpace&: *this); // detach
654 d_ptr->description.clear();
655 d_ptr->primaries = QColorSpace::Primaries::Custom;
656 d_ptr->toXyz = toXyz;
657 d_ptr->whitePoint = QColorVector(primaries.whitePoint);
658 d_ptr->identifyColorSpace();
659}
660
661/*!
662 Returns an ICC profile representing the color space.
663
664 If the color space was generated from an ICC profile, that profile
665 is returned, otherwise one is generated.
666
667 \note Even invalid color spaces may return the ICC profile if they
668 were generated from one, to allow applications to implement wider
669 support themselves.
670
671 \sa fromIccProfile()
672*/
673QByteArray QColorSpace::iccProfile() const
674{
675 if (Q_UNLIKELY(!d_ptr))
676 return QByteArray();
677 if (!d_ptr->iccProfile.isEmpty())
678 return d_ptr->iccProfile;
679 if (!isValid())
680 return QByteArray();
681 return QIcc::toIccProfile(space: *this);
682}
683
684/*!
685 Creates a QColorSpace from ICC profile \a iccProfile.
686
687 \note Not all ICC profiles are supported. QColorSpace only supports
688 RGB-XYZ ICC profiles that are three-component matrix-based.
689
690 If the ICC profile is not supported an invalid QColorSpace is returned
691 where you can still read the original ICC profile using iccProfile().
692
693 \sa iccProfile()
694*/
695QColorSpace QColorSpace::fromIccProfile(const QByteArray &iccProfile)
696{
697 QColorSpace colorSpace;
698 if (QIcc::fromIccProfile(data: iccProfile, colorSpace: &colorSpace))
699 return colorSpace;
700 QColorSpacePrivate *d = QColorSpacePrivate::getWritable(colorSpace);
701 d->iccProfile = iccProfile;
702 return colorSpace;
703}
704
705/*!
706 Returns \c true if the color space is valid.
707*/
708bool QColorSpace::isValid() const noexcept
709{
710 return d_ptr
711 && d_ptr->toXyz.isValid()
712 && d_ptr->trc[0].isValid() && d_ptr->trc[1].isValid() && d_ptr->trc[2].isValid();
713}
714
715/*!
716 \relates QColorSpace
717 Returns \c true if colorspace \a colorSpace1 is equal to colorspace \a colorSpace2;
718 otherwise returns \c false
719*/
720bool operator==(const QColorSpace &colorSpace1, const QColorSpace &colorSpace2)
721{
722 if (colorSpace1.d_ptr == colorSpace2.d_ptr)
723 return true;
724 if (!colorSpace1.d_ptr || !colorSpace2.d_ptr)
725 return false;
726
727 if (colorSpace1.d_ptr->namedColorSpace && colorSpace2.d_ptr->namedColorSpace)
728 return colorSpace1.d_ptr->namedColorSpace == colorSpace2.d_ptr->namedColorSpace;
729
730 const bool valid1 = colorSpace1.isValid();
731 const bool valid2 = colorSpace2.isValid();
732 if (valid1 != valid2)
733 return false;
734 if (!valid1 && !valid2) {
735 if (!colorSpace1.d_ptr->iccProfile.isEmpty() || !colorSpace2.d_ptr->iccProfile.isEmpty())
736 return colorSpace1.d_ptr->iccProfile == colorSpace2.d_ptr->iccProfile;
737 }
738
739 // At this point one or both color spaces are unknown, and must be compared in detail instead
740
741 if (colorSpace1.primaries() != QColorSpace::Primaries::Custom && colorSpace2.primaries() != QColorSpace::Primaries::Custom) {
742 if (colorSpace1.primaries() != colorSpace2.primaries())
743 return false;
744 } else {
745 if (colorSpace1.d_ptr->toXyz != colorSpace2.d_ptr->toXyz)
746 return false;
747 }
748
749 if (colorSpace1.transferFunction() != QColorSpace::TransferFunction::Custom &&
750 colorSpace2.transferFunction() != QColorSpace::TransferFunction::Custom) {
751 if (colorSpace1.transferFunction() != colorSpace2.transferFunction())
752 return false;
753 if (colorSpace1.transferFunction() == QColorSpace::TransferFunction::Gamma)
754 return (qAbs(t: colorSpace1.gamma() - colorSpace2.gamma()) <= (1.0f / 512.0f));
755 return true;
756 }
757
758 if (colorSpace1.d_ptr->trc[0] != colorSpace2.d_ptr->trc[0] ||
759 colorSpace1.d_ptr->trc[1] != colorSpace2.d_ptr->trc[1] ||
760 colorSpace1.d_ptr->trc[2] != colorSpace2.d_ptr->trc[2])
761 return false;
762
763 return true;
764}
765
766/*!
767 \fn bool operator!=(const QColorSpace &colorSpace1, const QColorSpace &colorSpace2)
768 \relates QColorSpace
769
770 Returns \c true if colorspace \a colorSpace1 is not equal to colorspace \a colorSpace2;
771 otherwise returns \c false
772*/
773
774/*!
775 Generates and returns a color space transformation from this color space to
776 \a colorspace.
777*/
778QColorTransform QColorSpace::transformationToColorSpace(const QColorSpace &colorspace) const
779{
780 if (!isValid() || !colorspace.isValid())
781 return QColorTransform();
782
783 return d_ptr->transformationToColorSpace(out: colorspace.d_ptr);
784}
785
786/*!
787 Returns the color space as a QVariant.
788 \since 5.15
789*/
790QColorSpace::operator QVariant() const
791{
792 return QVariant(QMetaType::QColorSpace, this);
793}
794
795/*****************************************************************************
796 QColorSpace stream functions
797 *****************************************************************************/
798#if !defined(QT_NO_DATASTREAM)
799/*!
800 \fn QDataStream &operator<<(QDataStream &stream, const QColorSpace &colorSpace)
801 \relates QColorSpace
802
803 Writes the given \a colorSpace to the given \a stream as an ICC profile.
804
805 \sa QColorSpace::iccProfile(), {Serializing Qt Data Types}
806*/
807
808QDataStream &operator<<(QDataStream &s, const QColorSpace &image)
809{
810 s << image.iccProfile();
811 return s;
812}
813
814/*!
815 \fn QDataStream &operator>>(QDataStream &stream, QColorSpace &colorSpace)
816 \relates QColorSpace
817
818 Reads a color space from the given \a stream and stores it in the given
819 \a colorSpace.
820
821 \sa QColorSpace::fromIccProfile(), {Serializing Qt Data Types}
822*/
823
824QDataStream &operator>>(QDataStream &s, QColorSpace &colorSpace)
825{
826 QByteArray iccProfile;
827 s >> iccProfile;
828 colorSpace = QColorSpace::fromIccProfile(iccProfile);
829 return s;
830}
831#endif // QT_NO_DATASTREAM
832
833#ifndef QT_NO_DEBUG_STREAM
834QDebug operator<<(QDebug dbg, const QColorSpace &colorSpace)
835{
836 QDebugStateSaver saver(dbg);
837 dbg.nospace();
838 dbg << "QColorSpace(";
839 if (colorSpace.d_ptr) {
840 if (colorSpace.d_ptr->namedColorSpace)
841 dbg << colorSpace.d_ptr->namedColorSpace << ", ";
842 dbg << colorSpace.primaries() << ", " << colorSpace.transferFunction();
843 dbg << ", gamma=" << colorSpace.gamma();
844 }
845 dbg << ')';
846 return dbg;
847}
848#endif
849
850QT_END_NAMESPACE
851

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