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(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(redPoint))
106 return false;
107 if (!QColorVector::isValidChromaticity(greenPoint))
108 return false;
109 if (!QColorVector::isValidChromaticity(bluePoint))
110 return false;
111 if (!QColorVector::isValidChromaticity(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 = { QColorVector(redPoint),
120 QColorVector(greenPoint),
121 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(wXyz);
128
129 // Now we have scaled conversion to XYZ relative to the given whitepoint
130 toXyz = toXyz * QColorMatrix::fromScale(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 = { { 0.8951f, -0.7502f, 0.0389f },
140 { 0.2664f, 1.7135f, -0.0685f },
141 { -0.1614f, 0.0367f, 1.0296f } };
142 QColorMatrix abradinv = { { 0.9869929f, 0.4323053f, -0.0085287f },
143 { -0.1470543f, 0.5183603f, 0.0400428f },
144 { 0.1599627f, 0.0492912f, 0.9684867f } };
145
146 QColorVector srcCone = abrad.map(wXyz);
147 QColorVector dstCone = abrad.map(wXyzD50);
148
149 QColorMatrix wToD50 = { { dstCone.x / srcCone.x, 0, 0 },
150 { 0, dstCone.y / srcCone.y, 0 },
151 { 0, 0, dstCone.z / srcCone.z } };
152
153
154 QColorMatrix chromaticAdaptation = abradinv * (wToD50 * abrad);
155 toXyz = chromaticAdaptation * toXyz;
156 }
157
158 return toXyz;
159}
160
161QColorSpacePrivate::QColorSpacePrivate()
162{
163}
164
165QColorSpacePrivate::QColorSpacePrivate(QColorSpace::NamedColorSpace namedColorSpace)
166 : namedColorSpace(namedColorSpace)
167{
168 switch (namedColorSpace) {
169 case QColorSpace::SRgb:
170 primaries = QColorSpace::Primaries::SRgb;
171 transferFunction = QColorSpace::TransferFunction::SRgb;
172 description = QStringLiteral("sRGB");
173 break;
174 case QColorSpace::SRgbLinear:
175 primaries = QColorSpace::Primaries::SRgb;
176 transferFunction = QColorSpace::TransferFunction::Linear;
177 description = QStringLiteral("Linear sRGB");
178 break;
179 case QColorSpace::AdobeRgb:
180 primaries = QColorSpace::Primaries::AdobeRgb;
181 transferFunction = QColorSpace::TransferFunction::Gamma;
182 gamma = 2.19921875f; // Not quite 2.2, see https://www.adobe.com/digitalimag/pdfs/AdobeRGB1998.pdf
183 description = QStringLiteral("Adobe RGB");
184 break;
185 case QColorSpace::DisplayP3:
186 primaries = QColorSpace::Primaries::DciP3D65;
187 transferFunction = QColorSpace::TransferFunction::SRgb;
188 description = QStringLiteral("Display P3");
189 break;
190 case QColorSpace::ProPhotoRgb:
191 primaries = QColorSpace::Primaries::ProPhotoRgb;
192 transferFunction = QColorSpace::TransferFunction::ProPhotoRgb;
193 description = QStringLiteral("ProPhoto RGB");
194 break;
195 default:
196 Q_UNREACHABLE();
197 }
198 initialize();
199}
200
201QColorSpacePrivate::QColorSpacePrivate(QColorSpace::Primaries primaries, QColorSpace::TransferFunction fun, float gamma)
202 : primaries(primaries)
203 , transferFunction(fun)
204 , gamma(gamma)
205{
206 identifyColorSpace();
207 initialize();
208}
209
210QColorSpacePrivate::QColorSpacePrivate(const QColorSpacePrimaries &primaries,
211 QColorSpace::TransferFunction fun,
212 float gamma)
213 : primaries(QColorSpace::Primaries::Custom)
214 , transferFunction(fun)
215 , gamma(gamma)
216{
217 Q_ASSERT(primaries.areValid());
218 toXyz = primaries.toXyzMatrix();
219 whitePoint = QColorVector(primaries.whitePoint);
220 identifyColorSpace();
221 setTransferFunction();
222}
223
224void QColorSpacePrivate::identifyColorSpace()
225{
226 switch (primaries) {
227 case QColorSpace::Primaries::SRgb:
228 if (transferFunction == QColorSpace::TransferFunction::SRgb) {
229 namedColorSpace = QColorSpace::SRgb;
230 if (description.isEmpty())
231 description = QStringLiteral("sRGB");
232 return;
233 }
234 if (transferFunction == QColorSpace::TransferFunction::Linear) {
235 namedColorSpace = QColorSpace::SRgbLinear;
236 if (description.isEmpty())
237 description = QStringLiteral("Linear sRGB");
238 return;
239 }
240 break;
241 case QColorSpace::Primaries::AdobeRgb:
242 if (transferFunction == QColorSpace::TransferFunction::Gamma) {
243 if (qAbs(gamma - 2.19921875f) < (1/1024.0f)) {
244 namedColorSpace = QColorSpace::AdobeRgb;
245 if (description.isEmpty())
246 description = QStringLiteral("Adobe RGB");
247 return;
248 }
249 }
250 break;
251 case QColorSpace::Primaries::DciP3D65:
252 if (transferFunction == QColorSpace::TransferFunction::SRgb) {
253 namedColorSpace = QColorSpace::DisplayP3;
254 if (description.isEmpty())
255 description = QStringLiteral("Display P3");
256 return;
257 }
258 break;
259 case QColorSpace::Primaries::ProPhotoRgb:
260 if (transferFunction == QColorSpace::TransferFunction::ProPhotoRgb) {
261 namedColorSpace = QColorSpace::ProPhotoRgb;
262 if (description.isEmpty())
263 description = QStringLiteral("ProPhoto RGB");
264 return;
265 }
266 if (transferFunction == QColorSpace::TransferFunction::Gamma) {
267 // ProPhoto RGB's curve is effectively gamma 1.8 for 8bit precision.
268 if (qAbs(gamma - 1.8f) < (1/1024.0f)) {
269 namedColorSpace = QColorSpace::ProPhotoRgb;
270 if (description.isEmpty())
271 description = QStringLiteral("ProPhoto RGB");
272 return;
273 }
274 }
275 break;
276 default:
277 break;
278 }
279
280 namedColorSpace = Unknown;
281}
282
283void QColorSpacePrivate::initialize()
284{
285 setToXyzMatrix();
286 setTransferFunction();
287}
288
289void QColorSpacePrivate::setToXyzMatrix()
290{
291 if (primaries == QColorSpace::Primaries::Custom) {
292 toXyz = QColorMatrix();
293 whitePoint = QColorVector::D50();
294 return;
295 }
296 QColorSpacePrimaries colorSpacePrimaries(primaries);
297 toXyz = colorSpacePrimaries.toXyzMatrix();
298 whitePoint = QColorVector(colorSpacePrimaries.whitePoint);
299}
300
301void QColorSpacePrivate::setTransferFunction()
302{
303 switch (transferFunction) {
304 case QColorSpace::TransferFunction::Linear:
305 trc[0].m_type = QColorTrc::Type::Function;
306 trc[0].m_fun = QColorTransferFunction();
307 if (qFuzzyIsNull(gamma))
308 gamma = 1.0f;
309 break;
310 case QColorSpace::TransferFunction::Gamma:
311 trc[0].m_type = QColorTrc::Type::Function;
312 trc[0].m_fun = QColorTransferFunction::fromGamma(gamma);
313 break;
314 case QColorSpace::TransferFunction::SRgb:
315 trc[0].m_type = QColorTrc::Type::Function;
316 trc[0].m_fun = QColorTransferFunction::fromSRgb();
317 if (qFuzzyIsNull(gamma))
318 gamma = 2.31f;
319 break;
320 case QColorSpace::TransferFunction::ProPhotoRgb:
321 trc[0].m_type = QColorTrc::Type::Function;
322 trc[0].m_fun = QColorTransferFunction::fromProPhotoRgb();
323 if (qFuzzyIsNull(gamma))
324 gamma = 1.8f;
325 break;
326 case QColorSpace::TransferFunction::Custom:
327 break;
328 default:
329 Q_UNREACHABLE();
330 break;
331 }
332 trc[1] = trc[0];
333 trc[2] = trc[0];
334}
335
336QColorTransform QColorSpacePrivate::transformationToColorSpace(const QColorSpacePrivate *out) const
337{
338 Q_ASSERT(out);
339 QColorTransform combined;
340 auto ptr = new QColorTransformPrivate;
341 combined.d = ptr;
342 ptr->colorSpaceIn = this;
343 ptr->colorSpaceOut = out;
344 ptr->colorMatrix = out->toXyz.inverted() * toXyz;
345 return combined;
346}
347
348/*!
349 \class QColorSpace
350 \brief The QColorSpace class provides a color space abstraction.
351 \since 5.14
352
353 \ingroup painting
354 \ingroup appearance
355 \inmodule QtGui
356
357 Color values can be interpreted in different ways, and based on the interpretation
358 can live in different spaces. We call this \e {color spaces}.
359
360 QColorSpace provides access to creating several predefined color spaces and
361 can generate QColorTransforms for converting colors from one color space to
362 another.
363
364 QColorSpace can also represent color spaces defined by ICC profiles or embedded
365 in images, that do not otherwise fit the predefined color spaces.
366
367 A color space can generally speaking be conceived as a combination of set of primary
368 colors and a transfer function. The primaries defines the axes of the color space, and
369 the transfer function how values are mapped on the axes.
370 The primaries are defined by three primary colors that represent exactly how red, green,
371 and blue look in this particular color space, and a white color that represents where
372 and how bright pure white is. The range of colors expressable by the primary colors is
373 called the gamut, and a color space that can represent a wider range of colors is also
374 known as a wide-gamut color space.
375
376 The transfer function or gamma curve determines how each component in the
377 color space is encoded. These are used because human perception does not operate
378 linearly, and the transfer functions try to ensure that colors will seem evenly
379 spaced to human eyes.
380*/
381
382
383/*!
384 \enum QColorSpace::NamedColorSpace
385
386 Predefined color spaces.
387
388 \value SRgb The sRGB color space, which Qt operates in by default. It is a close approximation
389 of how most classic monitors operate, and a mode most software and hardware support.
390 \l{http://www.color.org/chardata/rgb/srgb.xalter}{ICC registration of sRGB}.
391 \value SRgbLinear The sRGB color space with linear gamma. Useful for gamma-corrected blending.
392 \value AdobeRgb The Adobe RGB color space is a classic wide-gamut color space, using a gamma of 2.2.
393 \l{http://www.color.org/chardata/rgb/adobergb.xalter}{ICC registration of Adobe RGB (1998)}
394 \value DisplayP3 A color-space using the primaries of DCI-P3, but with the whitepoint and transfer
395 function of sRGB. Common in modern wide-gamut screens.
396 \l{http://www.color.org/chardata/rgb/DCIP3.xalter}{ICC registration of DCI-P3}
397 \value ProPhotoRgb The Pro Photo RGB color space, also known as ROMM RGB is a very wide gamut color space.
398 \l{http://www.color.org/chardata/rgb/rommrgb.xalter}{ICC registration of ROMM RGB}
399*/
400
401/*!
402 \enum QColorSpace::Primaries
403
404 Predefined sets of primary colors.
405
406 \value Custom The primaries are undefined or does not match any predefined sets.
407 \value SRgb The sRGB primaries
408 \value AdobeRgb The Adobe RGB primaries
409 \value DciP3D65 The DCI-P3 primaries with the D65 whitepoint
410 \value ProPhotoRgb The ProPhoto RGB primaries with the D50 whitepoint
411*/
412
413/*!
414 \enum QColorSpace::TransferFunction
415
416 Predefined transfer functions or gamma curves.
417
418 \value Custom The custom or null transfer function
419 \value Linear The linear transfer functions
420 \value Gamma A transfer function that is a real gamma curve based on the value of gamma()
421 \value SRgb The sRGB transfer function, composed of linear and gamma parts
422 \value ProPhotoRgb The ProPhoto RGB transfer function, composed of linear and gamma parts
423*/
424
425/*!
426 \fn QColorSpace::QColorSpace()
427
428 Creates a new colorspace object that represents an undefined and invalid colorspace.
429 */
430
431/*!
432 Creates a new colorspace object that represents a \a namedColorSpace.
433 */
434QColorSpace::QColorSpace(NamedColorSpace namedColorSpace)
435{
436 if (namedColorSpace < QColorSpace::SRgb || namedColorSpace > QColorSpace::ProPhotoRgb) {
437 qWarning() << "QColorSpace attempted constructed from invalid QColorSpace::NamedColorSpace: " << int(namedColorSpace);
438 return;
439 }
440 // The defined namespaces start at 1:
441 auto &atomicRef = s_predefinedColorspacePrivates[static_cast<int>(namedColorSpace) - 1];
442 QColorSpacePrivate *cspriv = atomicRef.loadAcquire();
443 if (!cspriv) {
444 auto *tmp = new QColorSpacePrivate(namedColorSpace);
445 tmp->ref.ref();
446 if (atomicRef.testAndSetOrdered(nullptr, tmp, cspriv))
447 cspriv = tmp;
448 else
449 delete tmp;
450 }
451 d_ptr = cspriv;
452 Q_ASSERT(isValid());
453}
454
455/*!
456 Creates a custom color space with the primaries \a primaries, using the transfer function \a fun and
457 optionally \a gamma.
458 */
459QColorSpace::QColorSpace(QColorSpace::Primaries primaries, QColorSpace::TransferFunction fun, float gamma)
460 : d_ptr(new QColorSpacePrivate(primaries, fun, gamma))
461{
462}
463
464/*!
465 Creates a custom color space with the primaries \a primaries, using a gamma transfer function of
466 \a gamma.
467 */
468QColorSpace::QColorSpace(QColorSpace::Primaries primaries, float gamma)
469 : d_ptr(new QColorSpacePrivate(primaries, TransferFunction::Gamma, gamma))
470{
471}
472
473/*!
474 Creates a custom colorspace with a primaries based on the chromaticities of the primary colors \a whitePoint,
475 \a redPoint, \a greenPoint and \a bluePoint, and using the transfer function \a fun and optionally \a gamma.
476 */
477QColorSpace::QColorSpace(const QPointF &whitePoint, const QPointF &redPoint,
478 const QPointF &greenPoint, const QPointF &bluePoint,
479 QColorSpace::TransferFunction fun, float gamma)
480{
481 QColorSpacePrimaries primaries(whitePoint, redPoint, greenPoint, bluePoint);
482 if (!primaries.areValid()) {
483 qWarning() << "QColorSpace attempted constructed from invalid primaries:" << whitePoint << redPoint << greenPoint << bluePoint;
484 return;
485 }
486 d_ptr = new QColorSpacePrivate(primaries, fun, gamma);
487}
488
489QColorSpace::~QColorSpace() = default;
490
491QT_DEFINE_QESDP_SPECIALIZATION_DTOR(QColorSpacePrivate)
492
493QColorSpace::QColorSpace(const QColorSpace &colorSpace) noexcept = default;
494
495/*! \fn void QColorSpace::swap(QColorSpace &other)
496
497 Swaps color space \a other with this color space. This operation is very fast and
498 never fails.
499*/
500
501/*!
502 Returns the predefined primaries of the color space
503 or \c primaries::Custom if it doesn't match any of them.
504*/
505QColorSpace::Primaries QColorSpace::primaries() const noexcept
506{
507 if (Q_UNLIKELY(!d_ptr))
508 return QColorSpace::Primaries::Custom;
509 return d_ptr->primaries;
510}
511
512/*!
513 Returns the predefined transfer function of the color space
514 or \c TransferFunction::Custom if it doesn't match any of them.
515
516 \sa gamma(), setTransferFunction(), withTransferFunction()
517*/
518QColorSpace::TransferFunction QColorSpace::transferFunction() const noexcept
519{
520 if (Q_UNLIKELY(!d_ptr))
521 return QColorSpace::TransferFunction::Custom;
522 return d_ptr->transferFunction;
523}
524
525/*!
526 Returns the gamma value of color spaces with \c TransferFunction::Gamma,
527 an approximate gamma value for other predefined color spaces, or
528 0.0 if no approximate gamma is known.
529
530 \sa transferFunction()
531*/
532float QColorSpace::gamma() const noexcept
533{
534 if (Q_UNLIKELY(!d_ptr))
535 return 0.0f;
536 return d_ptr->gamma;
537}
538
539/*!
540 Sets the transfer function to \a transferFunction and \a gamma.
541
542 \sa transferFunction(), gamma(), withTransferFunction()
543*/
544void QColorSpace::setTransferFunction(QColorSpace::TransferFunction transferFunction, float gamma)
545{
546 if (transferFunction == TransferFunction::Custom)
547 return;
548 if (!d_ptr) {
549 d_ptr = new QColorSpacePrivate(Primaries::Custom, transferFunction, gamma);
550 return;
551 }
552 if (d_ptr->transferFunction == transferFunction && d_ptr->gamma == gamma)
553 return;
554 detach();
555 d_ptr->description.clear();
556 d_ptr->transferFunction = transferFunction;
557 d_ptr->gamma = gamma;
558 d_ptr->identifyColorSpace();
559 d_ptr->setTransferFunction();
560}
561
562/*!
563 Returns a copy of this color space, except using the transfer function
564 \a transferFunction and \a gamma.
565
566 \sa transferFunction(), gamma(), setTransferFunction()
567*/
568QColorSpace QColorSpace::withTransferFunction(QColorSpace::TransferFunction transferFunction, float gamma) const
569{
570 if (!isValid() || transferFunction == QColorSpace::TransferFunction::Custom)
571 return *this;
572 if (d_ptr->transferFunction == transferFunction && d_ptr->gamma == gamma)
573 return *this;
574 QColorSpace out(*this);
575 out.setTransferFunction(transferFunction, gamma);
576 return out;
577}
578
579/*!
580 Sets the primaries to those of the \a primariesId set.
581
582 \sa primaries()
583*/
584void QColorSpace::setPrimaries(QColorSpace::Primaries primariesId)
585{
586 if (primariesId == Primaries::Custom)
587 return;
588 if (!d_ptr) {
589 d_ptr = new QColorSpacePrivate(primariesId, TransferFunction::Custom, 0.0f);
590 return;
591 }
592 if (d_ptr->primaries == primariesId)
593 return;
594 detach();
595 d_ptr->description.clear();
596 d_ptr->primaries = primariesId;
597 d_ptr->identifyColorSpace();
598 d_ptr->setToXyzMatrix();
599}
600
601/*!
602 Set primaries to the chromaticities of \a whitePoint, \a redPoint, \a greenPoint
603 and \a bluePoint.
604
605 \sa primaries()
606*/
607void QColorSpace::setPrimaries(const QPointF &whitePoint, const QPointF &redPoint,
608 const QPointF &greenPoint, const QPointF &bluePoint)
609{
610 QColorSpacePrimaries primaries(whitePoint, redPoint, greenPoint, bluePoint);
611 if (!primaries.areValid())
612 return;
613 if (!d_ptr) {
614 d_ptr = new QColorSpacePrivate(primaries, TransferFunction::Custom, 0.0f);
615 return;
616 }
617 QColorMatrix toXyz = primaries.toXyzMatrix();
618 if (QColorVector(primaries.whitePoint) == d_ptr->whitePoint && toXyz == d_ptr->toXyz)
619 return;
620 detach();
621 d_ptr->description.clear();
622 d_ptr->primaries = QColorSpace::Primaries::Custom;
623 d_ptr->toXyz = toXyz;
624 d_ptr->whitePoint = QColorVector(primaries.whitePoint);
625 d_ptr->identifyColorSpace();
626}
627
628/*!
629 \internal
630*/
631void QColorSpace::detach()
632{
633 if (d_ptr)
634 d_ptr.detach();
635 else
636 d_ptr = new QColorSpacePrivate;
637}
638
639/*!
640 Returns an ICC profile representing the color space.
641
642 If the color space was generated from an ICC profile, that profile
643 is returned, otherwise one is generated.
644
645 \note Even invalid color spaces may return the ICC profile if they
646 were generated from one, to allow applications to implement wider
647 support themselves.
648
649 \sa fromIccProfile()
650*/
651QByteArray QColorSpace::iccProfile() const
652{
653 if (Q_UNLIKELY(!d_ptr))
654 return QByteArray();
655 if (!d_ptr->iccProfile.isEmpty())
656 return d_ptr->iccProfile;
657 if (!isValid())
658 return QByteArray();
659 return QIcc::toIccProfile(*this);
660}
661
662/*!
663 Creates a QColorSpace from ICC profile \a iccProfile.
664
665 \note Not all ICC profiles are supported. QColorSpace only supports
666 RGB-XYZ ICC profiles that are three-component matrix-based.
667
668 If the ICC profile is not supported an invalid QColorSpace is returned
669 where you can still read the original ICC profile using iccProfile().
670
671 \sa iccProfile()
672*/
673QColorSpace QColorSpace::fromIccProfile(const QByteArray &iccProfile)
674{
675 QColorSpace colorSpace;
676 if (QIcc::fromIccProfile(iccProfile, &colorSpace))
677 return colorSpace;
678 colorSpace.detach();
679 colorSpace.d_ptr->iccProfile = iccProfile;
680 return colorSpace;
681}
682
683/*!
684 Returns \c true if the color space is valid.
685*/
686bool QColorSpace::isValid() const noexcept
687{
688 return d_ptr
689 && d_ptr->toXyz.isValid()
690 && d_ptr->trc[0].isValid() && d_ptr->trc[1].isValid() && d_ptr->trc[2].isValid();
691}
692
693/*!
694 \fn bool QColorSpace::operator==(const QColorSpace &colorSpace1, const QColorSpace &colorSpace2)
695 \relates QColorSpace
696 Returns \c true if colorspace \a colorSpace1 is equal to colorspace \a colorSpace2;
697 otherwise returns \c false
698*/
699bool operator==(const QColorSpace &colorSpace1, const QColorSpace &colorSpace2)
700{
701 if (colorSpace1.d_ptr == colorSpace2.d_ptr)
702 return true;
703 if (!colorSpace1.d_ptr || !colorSpace2.d_ptr)
704 return false;
705
706 if (colorSpace1.d_ptr->namedColorSpace && colorSpace2.d_ptr->namedColorSpace)
707 return colorSpace1.d_ptr->namedColorSpace == colorSpace2.d_ptr->namedColorSpace;
708
709 const bool valid1 = colorSpace1.isValid();
710 const bool valid2 = colorSpace2.isValid();
711 if (valid1 != valid2)
712 return false;
713 if (!valid1 && !valid2) {
714 if (!colorSpace1.d_ptr->iccProfile.isEmpty() || !colorSpace2.d_ptr->iccProfile.isEmpty())
715 return colorSpace1.d_ptr->iccProfile == colorSpace2.d_ptr->iccProfile;
716 }
717
718 // At this point one or both color spaces are unknown, and must be compared in detail instead
719
720 if (colorSpace1.primaries() != QColorSpace::Primaries::Custom && colorSpace2.primaries() != QColorSpace::Primaries::Custom) {
721 if (colorSpace1.primaries() != colorSpace2.primaries())
722 return false;
723 } else {
724 if (colorSpace1.d_ptr->toXyz != colorSpace2.d_ptr->toXyz)
725 return false;
726 }
727
728 if (colorSpace1.transferFunction() != QColorSpace::TransferFunction::Custom &&
729 colorSpace2.transferFunction() != QColorSpace::TransferFunction::Custom) {
730 if (colorSpace1.transferFunction() != colorSpace2.transferFunction())
731 return false;
732 if (colorSpace1.transferFunction() == QColorSpace::TransferFunction::Gamma)
733 return (qAbs(colorSpace1.gamma() - colorSpace2.gamma()) <= (1.0f / 512.0f));
734 return true;
735 }
736
737 if (colorSpace1.d_ptr->trc[0] != colorSpace2.d_ptr->trc[0] ||
738 colorSpace1.d_ptr->trc[1] != colorSpace2.d_ptr->trc[1] ||
739 colorSpace1.d_ptr->trc[2] != colorSpace2.d_ptr->trc[2])
740 return false;
741
742 return true;
743}
744
745/*!
746 \fn bool QColorSpace::operator!=(const QColorSpace &colorSpace1, const QColorSpace &colorSpace2)
747
748 Returns \c true if colorspace \a colorSpace1 is not equal to colorspace \a colorSpace2;
749 otherwise returns \c false
750*/
751
752/*!
753 Generates and returns a color space transformation from this color space to
754 \a colorspace.
755*/
756QColorTransform QColorSpace::transformationToColorSpace(const QColorSpace &colorspace) const
757{
758 if (!isValid() || !colorspace.isValid())
759 return QColorTransform();
760
761 return d_ptr->transformationToColorSpace(colorspace.d_ptr.get());
762}
763
764/*!
765 Returns the color space as a QVariant.
766 \since 5.15
767*/
768QColorSpace::operator QVariant() const
769{
770 return QVariant::fromValue(*this);
771}
772
773/*****************************************************************************
774 QColorSpace stream functions
775 *****************************************************************************/
776#if !defined(QT_NO_DATASTREAM)
777/*!
778 \fn QDataStream &operator<<(QDataStream &stream, const QColorSpace &colorSpace)
779 \relates QColorSpace
780
781 Writes the given \a colorSpace to the given \a stream as an ICC profile.
782
783 \sa QColorSpace::iccProfile(), {Serializing Qt Data Types}
784*/
785
786QDataStream &operator<<(QDataStream &s, const QColorSpace &image)
787{
788 s << image.iccProfile();
789 return s;
790}
791
792/*!
793 \fn QDataStream &operator>>(QDataStream &stream, QColorSpace &colorSpace)
794 \relates QColorSpace
795
796 Reads a color space from the given \a stream and stores it in the given
797 \a colorSpace.
798
799 \sa QColorSpace::fromIccProfile(), {Serializing Qt Data Types}
800*/
801
802QDataStream &operator>>(QDataStream &s, QColorSpace &colorSpace)
803{
804 QByteArray iccProfile;
805 s >> iccProfile;
806 colorSpace = QColorSpace::fromIccProfile(iccProfile);
807 return s;
808}
809#endif // QT_NO_DATASTREAM
810
811#ifndef QT_NO_DEBUG_STREAM
812QDebug operator<<(QDebug dbg, const QColorSpace &colorSpace)
813{
814 QDebugStateSaver saver(dbg);
815 dbg.nospace();
816 dbg << "QColorSpace(";
817 if (colorSpace.d_ptr) {
818 if (colorSpace.d_ptr->namedColorSpace)
819 dbg << colorSpace.d_ptr->namedColorSpace << ", ";
820 dbg << colorSpace.primaries() << ", " << colorSpace.transferFunction();
821 dbg << ", gamma=" << colorSpace.gamma();
822 }
823 dbg << ')';
824 return dbg;
825}
826#endif
827
828QT_END_NAMESPACE
829