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 <qmath.h>
50#include <qtransform.h>
51
52#include <qdebug.h>
53
54QT_BEGIN_NAMESPACE
55
56QBasicMutex QColorSpacePrivate::s_lutWriteLock;
57
58QColorSpacePrimaries::QColorSpacePrimaries(QColorSpace::Gamut gamut)
59{
60 switch (gamut) {
61 case QColorSpace::Gamut::SRgb:
62 redPoint = QPointF(0.640, 0.330);
63 greenPoint = QPointF(0.300, 0.600);
64 bluePoint = QPointF(0.150, 0.060);
65 whitePoint = QColorVector::D65Chromaticity();
66 break;
67 case QColorSpace::Gamut::DciP3D65:
68 redPoint = QPointF(0.680, 0.320);
69 greenPoint = QPointF(0.265, 0.690);
70 bluePoint = QPointF(0.150, 0.060);
71 whitePoint = QColorVector::D65Chromaticity();
72 break;
73 case QColorSpace::Gamut::Bt2020:
74 redPoint = QPointF(0.708, 0.292);
75 greenPoint = QPointF(0.190, 0.797);
76 bluePoint = QPointF(0.131, 0.046);
77 whitePoint = QColorVector::D65Chromaticity();
78 break;
79 case QColorSpace::Gamut::AdobeRgb:
80 redPoint = QPointF(0.640, 0.330);
81 greenPoint = QPointF(0.210, 0.710);
82 bluePoint = QPointF(0.150, 0.060);
83 whitePoint = QColorVector::D65Chromaticity();
84 break;
85 case QColorSpace::Gamut::ProPhotoRgb:
86 redPoint = QPointF(0.7347, 0.2653);
87 greenPoint = QPointF(0.1596, 0.8404);
88 bluePoint = QPointF(0.0366, 0.0001);
89 whitePoint = QColorVector::D50Chromaticity();
90 break;
91 default:
92 Q_UNREACHABLE();
93 }
94}
95
96bool QColorSpacePrimaries::areValid() const
97{
98 if (!QColorVector::isValidChromaticity(redPoint))
99 return false;
100 if (!QColorVector::isValidChromaticity(greenPoint))
101 return false;
102 if (!QColorVector::isValidChromaticity(bluePoint))
103 return false;
104 if (!QColorVector::isValidChromaticity(whitePoint))
105 return false;
106 return true;
107}
108
109QColorMatrix QColorSpacePrimaries::toXyzMatrix() const
110{
111 // This converts to XYZ in some undefined scale.
112 QColorMatrix toXyz = { QColorVector(redPoint),
113 QColorVector(greenPoint),
114 QColorVector(bluePoint) };
115
116 // Since the white point should be (1.0, 1.0, 1.0) in the
117 // input, we can figure out the scale by using the
118 // inverse conversion on the white point.
119 QColorVector wXyz(whitePoint);
120 QColorVector whiteScale = toXyz.inverted().map(wXyz);
121
122 // Now we have scaled conversion to XYZ relative to the given whitepoint
123 toXyz = toXyz * QColorMatrix::fromScale(whiteScale);
124
125 // But we want a conversion to XYZ relative to D50
126 QColorVector wXyzD50 = QColorVector::D50();
127
128 if (wXyz != wXyzD50) {
129 // Do chromatic adaptation to map our white point to XYZ D50.
130
131 // The Bradford method chromatic adaptation matrix:
132 QColorMatrix abrad = { { 0.8951f, -0.7502f, 0.0389f },
133 { 0.2664f, 1.7135f, -0.0685f },
134 { -0.1614f, 0.0367f, 1.0296f } };
135 QColorMatrix abradinv = { { 0.9869929f, 0.4323053f, -0.0085287f },
136 { -0.1470543f, 0.5183603f, 0.0400428f },
137 { 0.1599627f, 0.0492912f, 0.9684867f } };
138
139 QColorVector srcCone = abrad.map(wXyz);
140 QColorVector dstCone = abrad.map(wXyzD50);
141
142 QColorMatrix wToD50 = { { dstCone.x / srcCone.x, 0, 0 },
143 { 0, dstCone.y / srcCone.y, 0 },
144 { 0, 0, dstCone.z / srcCone.z } };
145
146
147 QColorMatrix chromaticAdaptation = abradinv * (wToD50 * abrad);
148 toXyz = chromaticAdaptation * toXyz;
149 }
150
151 return toXyz;
152}
153
154QColorSpacePrivate::QColorSpacePrivate()
155 : id(QColorSpace::Unknown)
156 , gamut(QColorSpace::Gamut::Custom)
157 , transferFunction(QColorSpace::TransferFunction::Custom)
158 , gamma(0.0f)
159 , whitePoint(QColorVector::null())
160 , toXyz(QColorMatrix::null())
161{
162}
163
164QColorSpacePrivate::QColorSpacePrivate(QColorSpace::ColorSpaceId colorSpaceId)
165 : id(colorSpaceId)
166{
167 switch (colorSpaceId) {
168 case QColorSpace::Undefined:
169 gamut = QColorSpace::Gamut::Custom;
170 transferFunction = QColorSpace::TransferFunction::Custom;
171 gamma = 0.0f;
172 description = QStringLiteral("Undefined");
173 break;
174 case QColorSpace::SRgb:
175 gamut = QColorSpace::Gamut::SRgb;
176 transferFunction = QColorSpace::TransferFunction::SRgb;
177 gamma = 2.31f; // ?
178 description = QStringLiteral("sRGB");
179 break;
180 case QColorSpace::SRgbLinear:
181 gamut = QColorSpace::Gamut::SRgb;
182 transferFunction = QColorSpace::TransferFunction::Linear;
183 gamma = 1.0f;
184 description = QStringLiteral("Linear sRGB");
185 break;
186 case QColorSpace::AdobeRgb:
187 gamut = QColorSpace::Gamut::AdobeRgb;
188 transferFunction = QColorSpace::TransferFunction::Gamma;
189 gamma = 2.19921875f; // Not quite 2.2, see https://www.adobe.com/digitalimag/pdfs/AdobeRGB1998.pdf
190 description = QStringLiteral("Adobe RGB");
191 break;
192 case QColorSpace::DisplayP3:
193 gamut = QColorSpace::Gamut::DciP3D65;
194 transferFunction = QColorSpace::TransferFunction::SRgb;
195 gamma = 2.31f; // ?
196 description = QStringLiteral("Display P3");
197 break;
198 case QColorSpace::ProPhotoRgb:
199 gamut = QColorSpace::Gamut::ProPhotoRgb;
200 transferFunction = QColorSpace::TransferFunction::ProPhotoRgb;
201 gamma = 1.8f;
202 description = QStringLiteral("ProPhoto RGB");
203 break;
204 case QColorSpace::Bt2020:
205 gamut = QColorSpace::Gamut::Bt2020;
206 transferFunction = QColorSpace::TransferFunction::Bt2020;
207 gamma = 2.1f; // ?
208 description = QStringLiteral("BT.2020");
209 break;
210 case QColorSpace::Unknown:
211 qWarning("Can not create an unknown color space");
212 Q_FALLTHROUGH();
213 default:
214 Q_UNREACHABLE();
215 }
216 initialize();
217}
218
219QColorSpacePrivate::QColorSpacePrivate(QColorSpace::Gamut gamut, QColorSpace::TransferFunction fun, float gamma)
220 : gamut(gamut)
221 , transferFunction(fun)
222 , gamma(gamma)
223{
224 if (!identifyColorSpace())
225 id = QColorSpace::Unknown;
226 initialize();
227}
228
229QColorSpacePrivate::QColorSpacePrivate(const QColorSpacePrimaries &primaries,
230 QColorSpace::TransferFunction fun,
231 float gamma)
232 : gamut(QColorSpace::Gamut::Custom)
233 , transferFunction(fun)
234 , gamma(gamma)
235{
236 Q_ASSERT(primaries.areValid());
237 toXyz = primaries.toXyzMatrix();
238 whitePoint = QColorVector(primaries.whitePoint);
239 if (!identifyColorSpace())
240 id = QColorSpace::Unknown;
241 setTransferFunction();
242}
243
244bool QColorSpacePrivate::identifyColorSpace()
245{
246 switch (gamut) {
247 case QColorSpace::Gamut::SRgb:
248 if (transferFunction == QColorSpace::TransferFunction::SRgb) {
249 id = QColorSpace::SRgb;
250 if (description.isEmpty())
251 description = QStringLiteral("sRGB");
252 return true;
253 }
254 if (transferFunction == QColorSpace::TransferFunction::Linear) {
255 id = QColorSpace::SRgbLinear;
256 if (description.isEmpty())
257 description = QStringLiteral("Linear sRGB");
258 return true;
259 }
260 break;
261 case QColorSpace::Gamut::AdobeRgb:
262 if (transferFunction == QColorSpace::TransferFunction::Gamma) {
263 if (qAbs(gamma - 2.19921875f) < (1/1024.0f)) {
264 id = QColorSpace::AdobeRgb;
265 if (description.isEmpty())
266 description = QStringLiteral("Adobe RGB");
267 return true;
268 }
269 }
270 break;
271 case QColorSpace::Gamut::DciP3D65:
272 if (transferFunction == QColorSpace::TransferFunction::SRgb) {
273 id = QColorSpace::DisplayP3;
274 if (description.isEmpty())
275 description = QStringLiteral("Display P3");
276 return true;
277 }
278 break;
279 case QColorSpace::Gamut::ProPhotoRgb:
280 if (transferFunction == QColorSpace::TransferFunction::ProPhotoRgb) {
281 id = QColorSpace::ProPhotoRgb;
282 if (description.isEmpty())
283 description = QStringLiteral("ProPhoto RGB");
284 return true;
285 }
286 if (transferFunction == QColorSpace::TransferFunction::Gamma) {
287 // ProPhoto RGB's curve is effectively gamma 1.8 for 8bit precision.
288 if (qAbs(gamma - 1.8f) < (1/1024.0f)) {
289 id = QColorSpace::ProPhotoRgb;
290 if (description.isEmpty())
291 description = QStringLiteral("ProPhoto RGB");
292 return true;
293 }
294 }
295 break;
296 case QColorSpace::Gamut::Bt2020:
297 if (transferFunction == QColorSpace::TransferFunction::Bt2020) {
298 id = QColorSpace::Bt2020;
299 if (description.isEmpty())
300 description = QStringLiteral("BT.2020");
301 return true;
302 }
303 break;
304 default:
305 break;
306 }
307 return false;
308}
309
310void QColorSpacePrivate::initialize()
311{
312 setToXyzMatrix();
313 setTransferFunction();
314}
315
316void QColorSpacePrivate::setToXyzMatrix()
317{
318 if (gamut == QColorSpace::Gamut::Custom) {
319 toXyz = QColorMatrix::null();
320 whitePoint = QColorVector::D50();
321 return;
322 }
323 QColorSpacePrimaries primaries(gamut);
324 toXyz = primaries.toXyzMatrix();
325 whitePoint = QColorVector(primaries.whitePoint);
326}
327
328void QColorSpacePrivate::setTransferFunction()
329{
330 switch (transferFunction) {
331 case QColorSpace::TransferFunction::Linear:
332 trc[0].m_type = QColorTrc::Type::Function;
333 trc[0].m_fun = QColorTransferFunction();
334 break;
335 case QColorSpace::TransferFunction::Gamma:
336 trc[0].m_type = QColorTrc::Type::Function;
337 trc[0].m_fun = QColorTransferFunction::fromGamma(gamma);
338 break;
339 case QColorSpace::TransferFunction::SRgb:
340 trc[0].m_type = QColorTrc::Type::Function;
341 trc[0].m_fun = QColorTransferFunction::fromSRgb();
342 break;
343 case QColorSpace::TransferFunction::ProPhotoRgb:
344 trc[0].m_type = QColorTrc::Type::Function;
345 trc[0].m_fun = QColorTransferFunction::fromProPhotoRgb();
346 break;
347 case QColorSpace::TransferFunction::Bt2020:
348 trc[0].m_type = QColorTrc::Type::Function;
349 trc[0].m_fun = QColorTransferFunction::fromBt2020();
350 break;
351 case QColorSpace::TransferFunction::Custom:
352 break;
353 default:
354 Q_UNREACHABLE();
355 break;
356 }
357 trc[1] = trc[0];
358 trc[2] = trc[0];
359}
360
361QColorTransform QColorSpacePrivate::transformationToColorSpace(const QColorSpacePrivate *out) const
362{
363 Q_ASSERT(out);
364 QColorTransform combined;
365 auto ptr = new QColorTransformPrivate;
366 combined.d = ptr;
367 ptr->colorSpaceIn = this;
368 ptr->colorSpaceOut = out;
369 ptr->colorMatrix = out->toXyz.inverted() * toXyz;
370 return combined;
371}
372
373/*!
374 \class QColorSpace
375 \brief The QColorSpace class provides a color space abstraction.
376 \since 5.14
377
378 \ingroup painting
379 \ingroup appearance
380 \inmodule QtGui
381
382 Color values can be interpreted in different ways, and based on the interpretation
383 can live in different spaces. We call this \e {color spaces}.
384
385 QColorSpace provides access to creating several predefined color spaces and
386 can generate QColorTransforms for converting colors from one color space to
387 another.
388
389 QColorSpace can also represent color spaces defined by ICC profiles or embedded
390 in images, that do not otherwise fit the predefined color spaces.
391
392 A color space can generally speaking be conceived as a combination of a transfer
393 function and a gamut. The gamut defines which colors the color space can represent.
394 A color space that can represent a wider range of colors is also known as a
395 wide-gamut color space. The gamut is defined by three primary colors that represent
396 exactly how red, green, and blue look in this particular color space, and a white
397 color that represents where and how bright pure white is.
398
399 The transfer function or gamma curve determines how each component in the
400 color space is encoded. These are used because human perception does not operate
401 linearly, and the transfer functions try to ensure that colors will seem evenly
402 spaced to human eyes.
403*/
404
405
406/*!
407 \enum QColorSpace::ColorSpaceId
408
409 Predefined color spaces.
410
411 \value Undefined An empty, invalid or unsupported color space.
412 \value Unknown A valid color space that doesn't match any of the predefined color spaces.
413 \value SRgb The sRGB color space, which Qt operates in by default. It is a close approximation
414 of how most classic monitors operate, and a mode most software and hardware support.
415 \l{http://www.color.org/chardata/rgb/srgb.xalter}{ICC registration of sRGB}.
416 \value SRgbLinear The sRGB color space with linear gamma. Useful for gamma-corrected blending.
417 \value AdobeRgb The Adobe RGB color space is a classic wide-gamut color space, using a gamma of 2.2.
418 \l{http://www.color.org/chardata/rgb/adobergb.xalter}{ICC registration of Adobe RGB (1998)}
419 \value DisplayP3 A color-space using the primaries of DCI-P3, but with the whitepoint and transfer
420 function of sRGB. Common in modern wide-gamut screens.
421 \l{http://www.color.org/chardata/rgb/DCIP3.xalter}{ICC registration of DCI-P3}
422 \value ProPhotoRgb The Pro Photo RGB color space, also known as ROMM RGB is a very wide gamut color space.
423 \l{http://www.color.org/chardata/rgb/rommrgb.xalter}{ICC registration of ROMM RGB}
424 \value Bt2020 BT.2020 also known as Rec.2020 is the color space of HDR TVs.
425 \l{http://www.color.org/chardata/rgb/BT2020.xalter}{ICC registration of BT.2020}
426*/
427
428/*!
429 \enum QColorSpace::Gamut
430
431 Predefined gamuts, or sets of primary colors.
432
433 \value Custom The gamut is undefined or does not match any predefined sets.
434 \value SRgb The sRGB gamut
435 \value AdobeRgb The Adobe RGB gamut
436 \value DciP3D65 The DCI-P3 gamut with the D65 whitepoint
437 \value ProPhotoRgb The ProPhoto RGB gamut with the D50 whitepoint
438 \value Bt2020 The BT.2020 gamut
439*/
440
441/*!
442 \enum QColorSpace::TransferFunction
443
444 Predefined transfer functions or gamma curves.
445
446 \value Custom The custom or null transfer function
447 \value Linear The linear transfer functions
448 \value Gamma A transfer function that is a real gamma curve based on the value of gamma()
449 \value SRgb The sRGB transfer function, composed of linear and gamma parts
450 \value ProPhotoRgb The ProPhoto RGB transfer function, composed of linear and gamma parts
451 \value Bt2020 The BT.2020 transfer function, composed of linear and gamma parts
452*/
453
454/*!
455 Creates a new colorspace object that represents \a colorSpaceId.
456 */
457QColorSpace::QColorSpace(QColorSpace::ColorSpaceId colorSpaceId)
458{
459 static QExplicitlySharedDataPointer<QColorSpacePrivate> predefinedColorspacePrivates[QColorSpace::Bt2020];
460 if (colorSpaceId <= QColorSpace::Unknown) {
461 if (!predefinedColorspacePrivates[0])
462 predefinedColorspacePrivates[0] = new QColorSpacePrivate(QColorSpace::Undefined);
463 d_ptr = predefinedColorspacePrivates[0]; // unknown and undefined both returns the static undefined colorspace.
464 } else {
465 if (!predefinedColorspacePrivates[colorSpaceId - 1])
466 predefinedColorspacePrivates[colorSpaceId - 1] = new QColorSpacePrivate(colorSpaceId);
467 d_ptr = predefinedColorspacePrivates[colorSpaceId - 1];
468 }
469
470 Q_ASSERT(colorSpaceId == QColorSpace::Undefined || isValid());
471}
472
473/*!
474 Creates a custom color space with the gamut \a gamut, using the transfer function \a fun and
475 optionally \a gamma.
476 */
477QColorSpace::QColorSpace(QColorSpace::Gamut gamut, QColorSpace::TransferFunction fun, float gamma)
478 : d_ptr(new QColorSpacePrivate(gamut, fun, gamma))
479{
480}
481
482/*!
483 Creates a custom color space with the gamut \a gamut, using a gamma transfer function of
484 \a gamma.
485 */
486QColorSpace::QColorSpace(QColorSpace::Gamut gamut, float gamma)
487 : d_ptr(new QColorSpacePrivate(gamut, TransferFunction::Gamma, gamma))
488{
489}
490
491/*!
492 Creates a custom colorspace with a gamut based on the chromaticities of the primary colors \a whitePoint,
493 \a redPoint, \a greenPoint and \a bluePoint, and using the transfer function \a fun and optionally \a gamma.
494 */
495QColorSpace::QColorSpace(const QPointF &whitePoint, const QPointF &redPoint,
496 const QPointF &greenPoint, const QPointF &bluePoint,
497 QColorSpace::TransferFunction fun, float gamma)
498{
499 QColorSpacePrimaries primaries(whitePoint, redPoint, greenPoint, bluePoint);
500 if (!primaries.areValid()) {
501 qWarning() << "QColorSpace attempted constructed from invalid primaries:" << whitePoint << redPoint << greenPoint << bluePoint;
502 d_ptr = QColorSpace(QColorSpace::Undefined).d_ptr;
503 return;
504 }
505 d_ptr = new QColorSpacePrivate(primaries, fun, gamma);
506}
507
508QColorSpace::~QColorSpace()
509{
510}
511
512QColorSpace::QColorSpace(QColorSpace &&colorSpace) noexcept
513 : d_ptr(std::move(colorSpace.d_ptr))
514{
515}
516
517QColorSpace::QColorSpace(const QColorSpace &colorSpace)
518 : d_ptr(colorSpace.d_ptr)
519{
520}
521
522QColorSpace &QColorSpace::operator=(QColorSpace &&colorSpace) noexcept
523{
524 d_ptr = std::move(colorSpace.d_ptr);
525 return *this;
526}
527
528QColorSpace &QColorSpace::operator=(const QColorSpace &colorSpace)
529{
530 d_ptr = colorSpace.d_ptr;
531 return *this;
532}
533
534/*! \fn void QColorSpace::swap(QColorSpace &other)
535
536 Swaps color space \a other with this color space. This operation is very fast and
537 never fails.
538*/
539
540/*!
541 Returns the id of the predefined color space this object
542 represents or \c Unknown if it doesn't match any of them.
543*/
544QColorSpace::ColorSpaceId QColorSpace::colorSpaceId() const noexcept
545{
546 return d_ptr->id;
547}
548
549/*!
550 Returns the predefined gamut of the color space
551 or \c Gamut::Custom if it doesn't match any of them.
552*/
553QColorSpace::Gamut QColorSpace::gamut() const noexcept
554{
555 return d_ptr->gamut;
556}
557
558/*!
559 Returns the predefined transfer function of the color space
560 or \c TransferFunction::Custom if it doesn't match any of them.
561
562 \sa gamma()
563*/
564QColorSpace::TransferFunction QColorSpace::transferFunction() const noexcept
565{
566 return d_ptr->transferFunction;
567}
568
569/*!
570 Returns the gamma value of color spaces with \c TransferFunction::Gamma,
571 an approximate gamma value for other predefined color spaces, or
572 0.0 if no approximate gamma is known.
573
574 \sa transferFunction()
575*/
576float QColorSpace::gamma() const noexcept
577{
578 return d_ptr->gamma;
579}
580
581/*!
582 Returns an ICC profile representing the color space.
583
584 If the color space was generated from an ICC profile, that profile
585 is returned, otherwise one is generated.
586
587 \note Even invalid color spaces may return the ICC profile if they
588 were generated from one, to allow applications to implement wider
589 support themselves.
590
591 \sa fromIccProfile()
592*/
593QByteArray QColorSpace::iccProfile() const
594{
595 if (!d_ptr->iccProfile.isEmpty())
596 return d_ptr->iccProfile;
597 if (!isValid())
598 return QByteArray();
599 return QIcc::toIccProfile(*this);
600}
601
602/*!
603 Creates a QColorSpace from ICC profile \a iccProfile.
604
605 \note Not all ICC profiles are supported. QColorSpace only supports
606 RGB-XYZ ICC profiles that are three-component matrix-based.
607
608 If the ICC profile is not supported an invalid QColorSpace is returned
609 where you can still read the original ICC profile using iccProfile().
610
611 \sa iccProfile()
612*/
613QColorSpace QColorSpace::fromIccProfile(const QByteArray &iccProfile)
614{
615 QColorSpace colorSpace;
616 if (QIcc::fromIccProfile(iccProfile, &colorSpace))
617 return colorSpace;
618 colorSpace.d_ptr->id = QColorSpace::Undefined;
619 colorSpace.d_ptr->iccProfile = iccProfile;
620 return colorSpace;
621}
622
623/*!
624 Returns \c true if the color space is valid.
625*/
626bool QColorSpace::isValid() const noexcept
627{
628 return d_ptr->id != QColorSpace::Undefined && d_ptr->toXyz.isValid()
629 && d_ptr->trc[0].isValid() && d_ptr->trc[1].isValid() && d_ptr->trc[2].isValid();
630}
631
632/*!
633 \relates QColorSpace
634 Returns \c true if colorspace \a colorSpace1 is equal to colorspace \a colorSpace2;
635 otherwise returns \c false
636*/
637bool operator==(const QColorSpace &colorSpace1, const QColorSpace &colorSpace2)
638{
639 if (colorSpace1.d_ptr == colorSpace2.d_ptr)
640 return true;
641
642 if (colorSpace1.colorSpaceId() == QColorSpace::Undefined && colorSpace2.colorSpaceId() == QColorSpace::Undefined)
643 return colorSpace1.d_ptr->iccProfile == colorSpace2.d_ptr->iccProfile;
644
645 if (colorSpace1.colorSpaceId() != QColorSpace::Unknown && colorSpace2.colorSpaceId() != QColorSpace::Unknown)
646 return colorSpace1.colorSpaceId() == colorSpace2.colorSpaceId();
647
648 if (colorSpace1.gamut() != QColorSpace::Gamut::Custom && colorSpace2.gamut() != QColorSpace::Gamut::Custom) {
649 if (colorSpace1.gamut() != colorSpace2.gamut())
650 return false;
651 } else {
652 if (colorSpace1.d_ptr->toXyz != colorSpace2.d_ptr->toXyz)
653 return false;
654 }
655
656 if (colorSpace1.transferFunction() != QColorSpace::TransferFunction::Custom &&
657 colorSpace2.transferFunction() != QColorSpace::TransferFunction::Custom) {
658 if (colorSpace1.transferFunction() != colorSpace2.transferFunction())
659 return false;
660 if (colorSpace1.transferFunction() == QColorSpace::TransferFunction::Gamma)
661 return (qAbs(colorSpace1.gamma() - colorSpace2.gamma()) <= (1.0f / 512.0f));
662 return true;
663 }
664
665 if (colorSpace1.d_ptr->trc[0] != colorSpace2.d_ptr->trc[0] ||
666 colorSpace1.d_ptr->trc[1] != colorSpace2.d_ptr->trc[1] ||
667 colorSpace1.d_ptr->trc[2] != colorSpace2.d_ptr->trc[2])
668 return false;
669
670 return true;
671}
672
673/*!
674 \fn bool operator!=(const QColorSpace &colorSpace1, const QColorSpace &colorSpace2)
675 \relates QColorSpace
676
677 Returns \c true if colorspace \a colorSpace1 is not equal to colorspace \a colorSpace2;
678 otherwise returns \c false
679*/
680
681/*!
682 Generates and returns a color space transformation from this color space to
683 \a colorspace.
684*/
685QColorTransform QColorSpace::transformationToColorSpace(const QColorSpace &colorspace) const
686{
687 if (!isValid() || !colorspace.isValid())
688 return QColorTransform();
689
690 return d_ptr->transformationToColorSpace(colorspace.d_ptr.constData());
691}
692
693/*****************************************************************************
694 QColorSpace stream functions
695 *****************************************************************************/
696#if !defined(QT_NO_DATASTREAM)
697/*!
698 \fn QDataStream &operator<<(QDataStream &stream, const QColorSpace &colorSpace)
699 \relates QColorSpace
700
701 Writes the given \a colorSpace to the given \a stream as an ICC profile.
702
703 \sa QColorSpace::iccProfile(), {Serializing Qt Data Types}
704*/
705
706QDataStream &operator<<(QDataStream &s, const QColorSpace &image)
707{
708 s << image.iccProfile();
709 return s;
710}
711
712/*!
713 \fn QDataStream &operator>>(QDataStream &stream, QColorSpace &colorSpace)
714 \relates QColorSpace
715
716 Reads a color space from the given \a stream and stores it in the given
717 \a colorSpace.
718
719 \sa QColorSpace::fromIccProfile(), {Serializing Qt Data Types}
720*/
721
722QDataStream &operator>>(QDataStream &s, QColorSpace &colorSpace)
723{
724 QByteArray iccProfile;
725 s >> iccProfile;
726 colorSpace = QColorSpace::fromIccProfile(iccProfile);
727 return s;
728}
729#endif // QT_NO_DATASTREAM
730
731#ifndef QT_NO_DEBUG_STREAM
732QDebug operator<<(QDebug dbg, const QColorSpace &colorSpace)
733{
734 QDebugStateSaver saver(dbg);
735 dbg.nospace();
736 dbg << "QColorSpace(";
737 dbg << colorSpace.colorSpaceId() << ", " << colorSpace.gamut() << ", " << colorSpace.transferFunction();
738 dbg << ", gamma=" << colorSpace.gamma();
739 dbg << ')';
740 return dbg;
741}
742#endif
743
744QT_END_NAMESPACE
745