6#include "rgbcolorspace.h"
8#include "rgbcolorspace_p.h"
10#include "absolutecolor.h"
11#include "constpropagatingrawpointer.h"
12#include "constpropagatinguniquepointer.h"
13#include "genericcolor.h"
15#include "helperconstants.h"
16#include "helpermath.h"
17#include "helperqttypes.h"
18#include "initializetranslation.h"
19#include "iohandlerfactory.h"
23#include <qbytearray.h>
25#include <qcoreapplication.h>
29#include <qnamespace.h>
31#include <qsharedpointer.h>
32#include <qstringliteral.h>
35#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
36#include <qcontainerfwd.h>
39#include <qstringlist.h>
56RgbColorSpace::RgbColorSpace(QObject *parent)
58 , d_pointer(new RgbColorSpacePrivate(this))
87 cmsHPROFILE srgb = cmsCreate_sRGBProfile();
88 const bool success = result->d_pointer->initialize(srgb);
89 cmsCloseProfile(srgb);
102 std::optional<QStringList>());
105 result->d_pointer->m_profileCreationDateTime =
QDateTime();
107 result->d_pointer->m_profileManufacturer = tr(
"LittleCMS");
108 result->d_pointer->m_profileModel =
QString();
110 result->d_pointer->m_profileName = tr(
"sRGB color space");
111 result->d_pointer->m_profileMaximumCielchD50Chroma = 132;
161 constexpr auto myContextID =
nullptr;
164 cmsIOHANDLER *myIOHandler =
165 IOHandlerFactory::createReadOnly(myContextID, fileName);
166 if (myIOHandler ==
nullptr) {
171 cmsHPROFILE myProfileHandle =
172 cmsOpenProfileFromIOhandlerTHR(myContextID, myIOHandler);
173 if (myProfileHandle ==
nullptr) {
185 newObject->d_pointer->m_profileAbsoluteFilePath =
187 newObject->d_pointer->m_profileFileSize = myFileInfo.
size();
188 const bool success = newObject->d_pointer->initialize(myProfileHandle);
191 cmsCloseProfile(myProfileHandle);
230bool RgbColorSpacePrivate::initialize(cmsHPROFILE rgbProfileHandle)
232 constexpr auto renderingIntent = INTENT_ABSOLUTE_COLORIMETRIC;
234 m_profileClass = cmsGetDeviceClass(rgbProfileHandle);
235 m_profileColorModel = cmsGetColorSpace(rgbProfileHandle);
240 m_profileCopyright = profileInformation(rgbProfileHandle,
243 m_profileCreationDateTime =
244 profileCreationDateTime(rgbProfileHandle);
246 for (cmsUInt32Number
id : renderingIntentIds) {
247 RgbColorSpace::ProfileRoles directions;
249 RgbColorSpace::ProfileRole::Input,
250 cmsIsIntentSupported(rgbProfileHandle,
id, LCMS_USED_AS_INPUT));
252 RgbColorSpace::ProfileRole::Output,
253 cmsIsIntentSupported(rgbProfileHandle,
id, LCMS_USED_AS_OUTPUT));
255 RgbColorSpace::ProfileRole::Proof,
256 cmsIsIntentSupported(rgbProfileHandle,
id, LCMS_USED_AS_PROOF));
257 m_profileRenderingIntentDirections.insert(
id, directions);
259 m_profileHasClut =
false;
260 const auto intents = m_profileRenderingIntentDirections.keys();
261 for (
auto intent : intents) {
262 const auto directions = m_profileRenderingIntentDirections.value(intent);
263 if (directions.testFlag(RgbColorSpace::ProfileRole::Input)) {
264 m_profileHasClut = cmsIsCLUT(rgbProfileHandle,
267 if (m_profileHasClut) {
271 if (directions.testFlag(RgbColorSpace::ProfileRole::Output)) {
272 m_profileHasClut = cmsIsCLUT(rgbProfileHandle,
274 LCMS_USED_AS_OUTPUT);
275 if (m_profileHasClut) {
279 if (directions.testFlag(RgbColorSpace::ProfileRole::Proof)) {
280 m_profileHasClut = cmsIsCLUT(rgbProfileHandle,
283 if (m_profileHasClut) {
288 m_profileHasMatrixShaper = cmsIsMatrixShaper(rgbProfileHandle);
289 m_profileIccVersion = profileIccVersion(rgbProfileHandle);
290 m_profileManufacturer = profileInformation(rgbProfileHandle,
293 m_profileModel = profileInformation(rgbProfileHandle,
296 m_profileName = profileInformation(rgbProfileHandle,
299 m_profilePcsColorModel = cmsGetPCS(rgbProfileHandle);
300 m_profileTagSignatures = profileTagSignatures(rgbProfileHandle);
330 if (m_profileTagSignatures.contains(QStringLiteral(
"vcgt"))) {
333 m_profileTagWhitepoint = profileReadCmsciexyzTag(rgbProfileHandle,
334 cmsSigMediaWhitePointTag);
335 m_profileTagBlackpoint = profileReadCmsciexyzTag(rgbProfileHandle,
336 cmsSigMediaBlackPointTag);
337 m_profileTagRedPrimary = profileReadCmsciexyzTag(rgbProfileHandle,
338 cmsSigRedColorantTag);
339 m_profileTagGreenPrimary = profileReadCmsciexyzTag(rgbProfileHandle,
340 cmsSigGreenColorantTag);
341 m_profileTagBluePrimary = profileReadCmsciexyzTag(rgbProfileHandle,
342 cmsSigBlueColorantTag);
346 cmsHPROFILE cielabD50ProfileHandle = cmsCreateLab4Profile(
360 constexpr auto flags = cmsFLAGS_NOCACHE;
361 m_transformCielabD50ToRgbHandle = cmsCreateTransform(
363 cielabD50ProfileHandle,
369 m_transformCielabD50ToRgb16Handle = cmsCreateTransform(
371 cielabD50ProfileHandle,
377 m_transformRgbToCielabD50Handle = cmsCreateTransform(
381 cielabD50ProfileHandle,
386 cmsCloseProfile(cielabD50ProfileHandle);
391 if ((m_transformCielabD50ToRgbHandle ==
nullptr)
392 || (m_transformCielabD50ToRgb16Handle ==
nullptr)
393 || (m_transformRgbToCielabD50Handle ==
nullptr)
403 GenericColor candidate;
404 candidate.second = 0;
407 while (!q_pointer->isCielchD50InGamut(candidate)) {
408 candidate.first += gamutPrecisionCielab;
409 if (candidate.first >= 100) {
413 m_cielabD50BlackpointL = candidate.first;
414 candidate.first = 100;
415 while (!q_pointer->isCielchD50InGamut(candidate)) {
416 candidate.first -= gamutPrecisionCielab;
417 if (candidate.first <= m_cielabD50BlackpointL) {
421 m_cielabD50WhitepointL = candidate.first;
424 while (!q_pointer->isOklchInGamut(candidate)) {
425 candidate.first += gamutPrecisionOklab;
426 if (candidate.first >= 1) {
430 m_oklabBlackpointL = candidate.first;
432 while (!q_pointer->isOklchInGamut(candidate)) {
433 candidate.first -= gamutPrecisionOklab;
434 if (candidate.first <= m_oklabBlackpointL) {
438 m_oklabWhitepointL = candidate.first;
442 initializeChromaticityBoundaries();
448RgbColorSpace::~RgbColorSpace() noexcept
450 RgbColorSpacePrivate::deleteTransform(
451 &d_pointer->m_transformCielabD50ToRgb16Handle);
452 RgbColorSpacePrivate::deleteTransform(
453 &d_pointer->m_transformCielabD50ToRgbHandle);
454 RgbColorSpacePrivate::deleteTransform(
455 &d_pointer->m_transformRgbToCielabD50Handle);
462RgbColorSpacePrivate::RgbColorSpacePrivate(RgbColorSpace *backLink)
463 : q_pointer(backLink)
481void RgbColorSpacePrivate::deleteTransform(cmsHTRANSFORM *transformHandle)
483 if ((*transformHandle) !=
nullptr) {
484 cmsDeleteTransform(*transformHandle);
485 (*transformHandle) =
nullptr;
491QString RgbColorSpace::profileAbsoluteFilePath()
const
493 return d_pointer->m_profileAbsoluteFilePath;
498cmsProfileClassSignature RgbColorSpace::profileClass()
const
500 return d_pointer->m_profileClass;
505cmsColorSpaceSignature RgbColorSpace::profileColorModel()
const
507 return d_pointer->m_profileColorModel;
512QString RgbColorSpace::profileCopyright()
const
514 return d_pointer->m_profileCopyright;
519QDateTime RgbColorSpace::profileCreationDateTime()
const
521 return d_pointer->m_profileCreationDateTime;
526qint64 RgbColorSpace::profileFileSize()
const
528 return d_pointer->m_profileFileSize;
533bool RgbColorSpace::profileHasClut()
const
535 return d_pointer->m_profileHasClut;
540bool RgbColorSpace::profileHasMatrixShaper()
const
542 return d_pointer->m_profileHasMatrixShaper;
549 return d_pointer->m_profileIccVersion;
554RgbColorSpace::RenderingIntentDirections RgbColorSpace::profileRenderingIntentDirections()
const
556 return d_pointer->m_profileRenderingIntentDirections;
561QString RgbColorSpace::profileManufacturer()
const
563 return d_pointer->m_profileManufacturer;
568double RgbColorSpace::profileMaximumCielchD50Chroma()
const
570 return d_pointer->m_profileMaximumCielchD50Chroma;
575double RgbColorSpace::profileMaximumOklchChroma()
const
577 return d_pointer->m_profileMaximumOklchChroma;
582QString RgbColorSpace::profileModel()
const
584 return d_pointer->m_profileModel;
589QString RgbColorSpace::profileName()
const
591 return d_pointer->m_profileName;
596cmsColorSpaceSignature RgbColorSpace::profilePcsColorModel()
const
598 return d_pointer->m_profilePcsColorModel;
603std::optional<cmsCIEXYZ> RgbColorSpace::profileTagBlackpoint()
const
605 return d_pointer->m_profileTagBlackpoint;
610std::optional<cmsCIEXYZ> RgbColorSpace::profileTagBluePrimary()
const
612 return d_pointer->m_profileTagBluePrimary;
617std::optional<cmsCIEXYZ> RgbColorSpace::profileTagGreenPrimary()
const
619 return d_pointer->m_profileTagGreenPrimary;
624std::optional<cmsCIEXYZ> RgbColorSpace::profileTagRedPrimary()
const
626 return d_pointer->m_profileTagRedPrimary;
631QStringList RgbColorSpace::profileTagSignatures()
const
633 return d_pointer->m_profileTagSignatures;
638std::optional<cmsCIEXYZ> RgbColorSpace::profileTagWhitepoint()
const
640 return d_pointer->m_profileTagWhitepoint;
658QString RgbColorSpacePrivate::profileInformation(cmsHPROFILE profileHandle, cmsInfoType infoType,
const QString &languageTerritory)
669 languageCode =
list.
at(0).toUtf8();
674 if (languageCode.
size() != 2) {
677 languageCode = QByteArrayLiteral(
"en");
719 const cmsUInt32Number resultLength = cmsGetProfileInfo(
736 const cmsUInt32Number bufferLength = resultLength + 1;
755 wchar_t *buffer =
new wchar_t[bufferLength];
757 for (cmsUInt32Number i = 0; i < bufferLength; ++i) {
777 *(buffer + (bufferLength - 1)) = 0;
845QVersionNumber RgbColorSpacePrivate::profileIccVersion(cmsHPROFILE profileHandle)
866 cmsGetProfileVersion(profileHandle),
878QDateTime RgbColorSpacePrivate::profileCreationDateTime(cmsHPROFILE profileHandle)
881 const bool success = cmsGetHeaderCreationDateTime(profileHandle, &myDateTime);
886 const QDate myDate(myDateTime.tm_year + 1900,
887 myDateTime.tm_mon + 1,
893 const QTime myTime(myDateTime.tm_hour,
895 qBound(0, myDateTime.tm_sec, 59));
912QStringList RgbColorSpacePrivate::profileTagSignatures(cmsHPROFILE profileHandle)
914 const cmsInt32Number count = cmsGetTagCount(profileHandle);
920 const cmsUInt32Number countUnsigned =
static_cast<cmsUInt32Number
>(count);
921 using underlyingType = std::underlying_type<cmsTagSignature>::type;
922 for (cmsUInt32Number i = 0; i < countUnsigned; ++i) {
923 const underlyingType value = cmsGetTagSignature(profileHandle, i);
928 sizeof(underlyingType) == 4,
929 "cmsTagSignature must have 4 bytes for this code to work.");
930 byteArray.
append(
static_cast<char>((value >> 24) & 0xFF));
931 byteArray.
append(
static_cast<char>((value >> 16) & 0xFF));
932 byteArray.
append(
static_cast<char>((value >> 8) & 0xFF));
933 byteArray.
append(
static_cast<char>(value & 0xFF));
952std::optional<cmsCIEXYZ> RgbColorSpacePrivate::profileReadCmsciexyzTag(cmsHPROFILE profileHandle, cmsTagSignature signature)
954 if (!cmsIsTag(profileHandle, signature)) {
958 void *voidPointer = cmsReadTag(profileHandle, signature);
960 if (voidPointer ==
nullptr) {
964 const cmsCIEXYZ result = *
static_cast<cmsCIEXYZ *
>(voidPointer);
982PerceptualColor::GenericColor RgbColorSpace::reduceCielchD50ChromaToFitIntoGamut(
const PerceptualColor::GenericColor &cielchD50color)
const
984 GenericColor referenceColor = cielchD50color;
987 normalizePolar360(referenceColor.second, referenceColor.third);
990 referenceColor.second = qMin<
decltype(referenceColor.second)>(
991 referenceColor.second,
992 profileMaximumCielchD50Chroma());
993 referenceColor.first = qBound(d_pointer->m_cielabD50BlackpointL,
994 referenceColor.first,
995 d_pointer->m_cielabD50WhitepointL);
998 if (isCielchD50InGamut(referenceColor)) {
999 return referenceColor;
1006 GenericColor lowerChroma{referenceColor.first, 0, referenceColor.third};
1007 if (!isCielchD50InGamut(lowerChroma)) {
1012 referenceColor.first = d_pointer->m_cielabD50BlackpointL;
1013 lowerChroma.first = d_pointer->m_cielabD50BlackpointL;
1017 constexpr bool quickApproximate =
true;
1018 if constexpr (quickApproximate) {
1020 GenericColor upperChroma{referenceColor};
1024 while (upperChroma.second - lowerChroma.second > gamutPrecisionCielab) {
1027 temp.second = ((lowerChroma.second + upperChroma.second) / 2);
1028 if (isCielchD50InGamut(temp)) {
1038 temp = referenceColor;
1039 while (temp.second > 0) {
1040 if (isCielchD50InGamut(temp)) {
1043 temp.second -= gamutPrecisionCielab;
1046 if (temp.second < 0) {
1066PerceptualColor::GenericColor RgbColorSpace::reduceOklchChromaToFitIntoGamut(
const PerceptualColor::GenericColor &oklchColor)
const
1068 GenericColor referenceColor = oklchColor;
1071 normalizePolar360(referenceColor.second, referenceColor.third);
1074 referenceColor.second = qMin<
decltype(referenceColor.second)>(
1075 referenceColor.second,
1076 profileMaximumOklchChroma());
1077 referenceColor.first = qBound(d_pointer->m_oklabBlackpointL,
1078 referenceColor.first,
1079 d_pointer->m_oklabWhitepointL);
1082 if (isOklchInGamut(referenceColor)) {
1083 return referenceColor;
1090 GenericColor lowerChroma{referenceColor.first, 0, referenceColor.third};
1091 if (!isOklchInGamut(lowerChroma)) {
1096 referenceColor.first = d_pointer->m_oklabBlackpointL;
1097 lowerChroma.first = d_pointer->m_oklabBlackpointL;
1101 constexpr bool quickApproximate =
true;
1102 if constexpr (quickApproximate) {
1104 GenericColor upperChroma{referenceColor};
1108 while (upperChroma.second - lowerChroma.second > gamutPrecisionOklab) {
1111 temp.second = ((lowerChroma.second + upperChroma.second) / 2);
1112 if (isOklchInGamut(temp)) {
1122 temp = referenceColor;
1123 while (temp.second > 0) {
1124 if (isOklchInGamut(temp)) {
1127 temp.second -= gamutPrecisionOklab;
1130 if (temp.second < 0) {
1147cmsCIELab RgbColorSpace::toCielabD50(
const QRgba64 rgbColor)
const
1149 constexpr qreal maximum =
1150 std::numeric_limits<
decltype(rgbColor.
red())>::max();
1151 const double my_rgb[]{rgbColor.
red() / maximum,
1152 rgbColor.
green() / maximum,
1153 rgbColor.
blue() / maximum};
1154 cmsCIELab cielabD50;
1155 cmsDoTransform(d_pointer->m_transformRgbToCielabD50Handle,
1160 if (cielabD50.L < 0) {
1178PerceptualColor::GenericColor RgbColorSpace::toCielchD50(
const QRgba64 rgbColor)
const
1180 constexpr qreal maximum =
1181 std::numeric_limits<
decltype(rgbColor.
red())>::max();
1182 const double my_rgb[]{rgbColor.
red() / maximum,
1183 rgbColor.
green() / maximum,
1184 rgbColor.
blue() / maximum};
1185 cmsCIELab cielabD50;
1186 cmsDoTransform(d_pointer->m_transformRgbToCielabD50Handle,
1191 if (cielabD50.L < 0) {
1195 cmsCIELCh cielchD50;
1196 cmsLab2LCh(&cielchD50,
1199 return GenericColor{cielchD50.L, cielchD50.C, cielchD50.h};
1214cmsCIELab RgbColorSpace::fromLchToCmsCIELab(
const GenericColor &lch)
1216 const cmsCIELCh myCmsCieLch = lch.reinterpretAsLchToCmscielch();
1236QRgb RgbColorSpace::fromCielchD50ToQRgbBound(
const GenericColor &cielchD50)
const
1238 const auto cielabD50 = fromLchToCmsCIELab(cielchD50);
1239 cmsUInt16Number rgb_int[3];
1240 cmsDoTransform(d_pointer->m_transformCielabD50ToRgb16Handle,
1245 constexpr qreal channelMaximumQReal =
1246 std::numeric_limits<cmsUInt16Number>::max();
1247 constexpr quint8 rgbMaximum = 255;
1248 return qRgb(qRound(rgb_int[0] / channelMaximumQReal * rgbMaximum),
1249 qRound(rgb_int[1] / channelMaximumQReal * rgbMaximum),
1250 qRound(rgb_int[2] / channelMaximumQReal * rgbMaximum));
1257bool RgbColorSpace::isCielchD50InGamut(
const GenericColor &lch)
const
1259 if (!isInRange<
decltype(lch.first)>(0, lch.first, 100)) {
1262 if (!isInRange<
decltype(lch.first)>(
1263 (-1) * d_pointer->m_profileMaximumCielchD50Chroma,
1265 d_pointer->m_profileMaximumCielchD50Chroma
1269 const auto cielabD50 = fromLchToCmsCIELab(lch);
1270 return qAlpha(fromCielabD50ToQRgbOrTransparent(cielabD50)) != 0;
1277bool RgbColorSpace::isOklchInGamut(
const GenericColor &lch)
const
1279 if (!isInRange<
decltype(lch.first)>(0, lch.first, 1)) {
1282 if (!isInRange<
decltype(lch.first)>(
1283 (-1) * d_pointer->m_profileMaximumOklchChroma,
1285 d_pointer->m_profileMaximumOklchChroma
1289 const auto oklab = AbsoluteColor::fromPolarToCartesian(GenericColor(lch));
1290 const auto xyzD65 = AbsoluteColor::fromOklabToXyzD65(oklab);
1291 const auto xyzD50 = AbsoluteColor::fromXyzD65ToXyzD50(xyzD65);
1292 const auto cielabD50 = AbsoluteColor::fromXyzD50ToCielabD50(xyzD50);
1293 const auto cielabD50cms = cielabD50.reinterpretAsLabToCmscielab();
1294 const auto rgb = fromCielabD50ToQRgbOrTransparent(cielabD50cms);
1295 return (qAlpha(rgb) != 0);
1302bool RgbColorSpace::isCielabD50InGamut(
const cmsCIELab &lab)
const
1304 if (!isInRange<
decltype(lab.L)>(0, lab.L, 100)) {
1307 const auto chromaSquare = lab.a * lab.a + lab.b * lab.b;
1308 const auto maximumChromaSquare = qPow(d_pointer->m_profileMaximumCielchD50Chroma, 2);
1309 if (chromaSquare > maximumChromaSquare) {
1312 return qAlpha(fromCielabD50ToQRgbOrTransparent(lab)) != 0;
1329QRgb RgbColorSpace::fromCielabD50ToQRgbOrTransparent(
const cmsCIELab &lab)
const
1331 constexpr QRgb transparentValue = 0;
1332 static_assert(qAlpha(transparentValue) == 0,
1333 "The alpha value of a transparent QRgb must be 0.");
1338 d_pointer->m_transformCielabD50ToRgbHandle,
1345 const bool colorIsValid =
1346 isInRange<double>(0, rgb[0], 1)
1347 && isInRange<double>(0, rgb[1], 1)
1348 && isInRange<double>(0, rgb[2], 1);
1349 if (!colorIsValid) {
1350 return transparentValue;
1354 cmsCIELab roundtripCielabD50;
1357 d_pointer->m_transformRgbToCielabD50Handle,
1359 &roundtripCielabD50,
1362 const qreal actualDeviationSquare =
1363 qPow(lab.L - roundtripCielabD50.L, 2)
1364 + qPow(lab.a - roundtripCielabD50.a, 2)
1365 + qPow(lab.b - roundtripCielabD50.b, 2);
1366 constexpr auto cielabDeviationLimitSquare =
1367 RgbColorSpacePrivate::cielabDeviationLimit
1368 * RgbColorSpacePrivate::cielabDeviationLimit;
1369 const bool actualDeviationIsOkay =
1370 actualDeviationSquare <= cielabDeviationLimitSquare;
1373 if (!actualDeviationIsOkay) {
1374 return transparentValue;
1379 static_cast<QColorFloatType
>(rgb[1]),
1380 static_cast<QColorFloatType
>(rgb[2]));
1392PerceptualColor::GenericColor RgbColorSpace::fromCielchD50ToRgb1(
const PerceptualColor::GenericColor &lch)
const
1394 const auto cielabD50 = fromLchToCmsCIELab(lch);
1398 d_pointer->m_transformCielabD50ToRgbHandle,
1403 return GenericColor(rgb[0], rgb[1], rgb[2]);
1410void RgbColorSpacePrivate::initializeChromaticityBoundaries()
1413 chromaticityBoundaryQColor.
reserve(256 * 6);
1414 for (
int value = 0; value <= 255; ++value) {
1420 chromaticityBoundaryQColor.
append(
QColor(255, value, 0));
1421 chromaticityBoundaryQColor.
append(
QColor(255, 0, value));
1424 chromaticityBoundaryQColor.
append(
QColor(value, 255, 0));
1425 chromaticityBoundaryQColor.
append(
QColor(0, 255, value));
1428 chromaticityBoundaryQColor.
append(
QColor(value, 0, 255));
1429 chromaticityBoundaryQColor.
append(
QColor(0, value, 255));
1432 m_profileMaximumCielchD50Chroma = 0;
1433 m_profileMaximumOklchChroma = 0;
1434 for (
auto &color : chromaticityBoundaryQColor) {
1435 const auto rgb = color.rgba64();
1436 const auto cielabD50 = GenericColor(q_pointer->toCielabD50(rgb));
1438 const auto cielchD50 = AbsoluteColor::fromCartesianToPolar(cielabD50);
1439 m_profileMaximumCielchD50Chroma = qMax(
1440 m_profileMaximumCielchD50Chroma,
1442 m_chromaticityBoundaryByCielchD50Hue360[cielchD50.third] = color;
1444 const auto xyzD50 = AbsoluteColor::fromCielabD50ToXyzD50(cielabD50);
1445 const auto xyzD65 = AbsoluteColor::fromXyzD50ToXyzD65(xyzD50);
1446 const auto oklab = AbsoluteColor::fromXyzD65ToOklab(xyzD65);
1447 const auto oklch = AbsoluteColor::fromCartesianToPolar(oklab);
1448 m_profileMaximumOklchChroma = qMax(
1449 m_profileMaximumOklchChroma,
1451 m_chromaticityBoundaryByOklabHue360[oklch.third] = color;
1454 auto addDuplicates = [](
auto &boundaryMap) {
1455 const auto firstKey = boundaryMap.begin()->first;
1456 const auto firstValue = boundaryMap.begin()->second;
1457 const auto lastKey = boundaryMap.rbegin()->first;
1458 const auto lastValue = boundaryMap.rbegin()->second;
1464 boundaryMap[firstKey + 360] = firstValue;
1465 boundaryMap[lastKey - 360] = lastValue;
1467 addDuplicates(m_chromaticityBoundaryByCielchD50Hue360);
1468 addDuplicates(m_chromaticityBoundaryByOklabHue360);
1470 m_profileMaximumCielchD50Chroma *= chromaDetectionIncrementFactor;
1471 m_profileMaximumCielchD50Chroma += cielabDeviationLimit;
1472 m_profileMaximumCielchD50Chroma = std::min<double>(
1473 m_profileMaximumCielchD50Chroma,
1474 CielchD50Values::maximumChroma);
1476 m_profileMaximumOklchChroma *= chromaDetectionIncrementFactor;
1477 m_profileMaximumOklchChroma += oklabDeviationLimit;
1478 m_profileMaximumOklchChroma = std::min<double>(
1479 m_profileMaximumOklchChroma,
1480 OklchValues::maximumChroma);
1491QColor RgbColorSpace::maxChromaColorByOklabHue360(
double hue360)
const
1493 return d_pointer->maxChromaColorByHue360(
1495 RgbColorSpacePrivate::LchSpace::Oklch);
1506QColor RgbColorSpace::maxChromaColorByCielchD50Hue360(
double hue360)
const
1508 return d_pointer->maxChromaColorByHue360(
1510 RgbColorSpacePrivate::LchSpace::CielchD50);
1522QColor RgbColorSpacePrivate::maxChromaColorByHue360(
double oklabHue360, RgbColorSpacePrivate::LchSpace type)
const
1524 const auto &table = (
type == LchSpace::CielchD50)
1525 ? m_chromaticityBoundaryByCielchD50Hue360
1526 : m_chromaticityBoundaryByOklabHue360;
1534 auto greaterOrEqual =
1535 table.lower_bound(oklabHue360);
1537 if (greaterOrEqual == table.begin()) {
1540 return greaterOrEqual->second;
1547 auto lower = --greaterOrEqual;
1549 if (greaterOrEqual == table.end()) {
1552 return lower->second;
1556 const auto distanceToLower = std::abs(oklabHue360 - lower->first);
1557 const auto distanceToHigher = std::abs(oklabHue360 - greaterOrEqual->first);
1558 if (distanceToLower <= distanceToHigher) {
1559 return lower->second;
1561 return greaterOrEqual->second;
Type type(const QSqlDatabase &db)
const char * versionString()
QStringView countryCode(QStringView coachNumber)
KIOCORE_EXPORT QStringList list(const QString &fileClass)
The namespace of this library.
QMap< cmsUInt32Number, QString > lcmsIntentList()
The rendering intents supported by the LittleCMS library.
QByteArray & append(QByteArrayView data)
const char * constData() const const
void reserve(qsizetype size)
qsizetype size() const const
QColor fromRgbF(float r, float g, float b, float a)
QCoreApplication * instance()
QString absoluteFilePath() const const
void append(QList< T > &&value)
const_reference at(qsizetype i) const const
qsizetype count() const const
void reserve(qsizetype size)
QString name() const const
quint16 blue() const const
quint16 green() const const
quint16 red() const const
QString fromLatin1(QByteArrayView str)
QString fromWCharArray(const wchar_t *string, qsizetype size)
QString number(double n, char format, int precision)
qsizetype size() const const
QStringList split(QChar sep, Qt::SplitBehavior behavior, Qt::CaseSensitivity cs) const const
const_pointer constData() const const
qsizetype size() const const
QVersionNumber fromString(QAnyStringView string, qsizetype *suffixIndex)