File indexing completed on 2025-02-23 04:10:58
0001 /* 0002 * SPDX-FileCopyrightText: 2019 Dmitry Kazakov <dimula73@gmail.com> 0003 * 0004 * SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "TestLcmsRGBP2020PQColorSpace.h" 0008 0009 #include <simpletest.h> 0010 #include <testpigment.h> 0011 0012 #include "kis_debug.h" 0013 0014 #include "KoColorProfile.h" 0015 #include "KoColorSpaceRegistry.h" 0016 #include "KoColor.h" 0017 #include "KoColorModelStandardIds.h" 0018 0019 inline QString truncated(QString value) { 0020 value.truncate(24); 0021 return value; 0022 } 0023 0024 enum SourceType { 0025 SDR, 0026 HDR, 0027 HDR_PQ 0028 }; 0029 0030 void testRoundTrip(const KoColorSpace *srcCS, const KoColorSpace *dstCS, SourceType sourceIsPQ) 0031 { 0032 /* 0033 * On some systems these colorspaces cannot be created, so don't die: 0034 */ 0035 if (!srcCS | !dstCS) return; 0036 0037 KoColor srcColor(srcCS); 0038 KoColor dstColor(dstCS); 0039 0040 QVector<float> refChannels; 0041 0042 if (sourceIsPQ == HDR) { 0043 refChannels << 2.8; // R 0044 refChannels << 1.8; // G 0045 refChannels << 0.8; // B 0046 refChannels << 0.9; // A 0047 } else if (sourceIsPQ == HDR_PQ) { 0048 refChannels << 0.9; // R (PQ) 0049 refChannels << 0.7; // G (PQ) 0050 refChannels << 0.1; // B (PQ) 0051 refChannels << 0.9; // A 0052 } else if (sourceIsPQ == SDR) { 0053 refChannels << 0.15; // R 0054 refChannels << 0.17; // G 0055 refChannels << 0.19; // B 0056 refChannels << 0.90; // A 0057 } 0058 0059 srcCS->fromNormalisedChannelsValue(srcColor.data(), refChannels); 0060 0061 srcCS->convertPixelsTo(srcColor.data(), dstColor.data(), dstCS, 1, 0062 KoColorConversionTransformation::internalRenderingIntent(), 0063 KoColorConversionTransformation::internalConversionFlags()); 0064 0065 dstCS->convertPixelsTo(dstColor.data(), srcColor.data(), srcCS, 1, 0066 KoColorConversionTransformation::internalRenderingIntent(), 0067 KoColorConversionTransformation::internalConversionFlags()); 0068 0069 QVector<float> result(4); 0070 srcCS->normalisedChannelsValue(srcColor.data(), result); 0071 0072 const QList<KoChannelInfo*> channels = srcCS->channels(); 0073 0074 // 5% tolerance for CMYK, 4% for 8-bit, and 1% for everything else 0075 const float tolerance = 0076 dstCS->colorModelId() == CMYKAColorModelID ? 0.05 : 0077 (dstCS->colorDepthId() == Integer8BitsColorDepthID || 0078 srcCS->colorDepthId() == Integer8BitsColorDepthID) ? 0.04 : 0079 0.01; 0080 0081 bool roundTripIsCorrect = true; 0082 for (int i = 0; i < 4; i++) { 0083 roundTripIsCorrect &= qAbs(refChannels[i] - result[i]) < tolerance; 0084 } 0085 0086 if (!roundTripIsCorrect) { 0087 for (int i = 0; i < 4; i++) { 0088 qDebug() << channels[i]->name() << "ref" << refChannels[i] << "result" << result[i]; 0089 } 0090 } 0091 0092 QVERIFY(roundTripIsCorrect); 0093 } 0094 0095 void testRoundTrip(const KoID &linearColorDepth, const KoID &pqColorDepth, SourceType sourceIsPQ) 0096 { 0097 const KoColorProfile *p2020PQProfile = KoColorSpaceRegistry::instance()->p2020PQProfile(); 0098 const KoColorProfile *p2020G10Profile = KoColorSpaceRegistry::instance()->p2020G10Profile(); 0099 0100 const KoColorSpace *srcCS = KoColorSpaceRegistry::instance()->colorSpace(RGBAColorModelID.id(), linearColorDepth.id(), p2020G10Profile); 0101 const KoColorSpace *dstCS = KoColorSpaceRegistry::instance()->colorSpace(RGBAColorModelID.id(), pqColorDepth.id(), p2020PQProfile);; 0102 0103 if (sourceIsPQ == HDR_PQ) { 0104 std::swap(srcCS, dstCS); 0105 } 0106 0107 testRoundTrip(srcCS, dstCS, sourceIsPQ); 0108 } 0109 0110 void TestLcmsRGBP2020PQColorSpace::test() 0111 { 0112 const KoColorProfile *p2020PQProfile = KoColorSpaceRegistry::instance()->p2020PQProfile(); 0113 const KoColorProfile *p2020G10Profile = KoColorSpaceRegistry::instance()->p2020G10Profile(); 0114 const KoColorProfile *p709G10Profile = KoColorSpaceRegistry::instance()->p709G10Profile(); 0115 0116 QVERIFY(p2020PQProfile); 0117 QVERIFY(p2020G10Profile); 0118 QVERIFY(p709G10Profile); 0119 0120 QVector<KoID> linearModes; 0121 linearModes << Float16BitsColorDepthID; 0122 linearModes << Float32BitsColorDepthID; 0123 0124 QVector<KoID> pqModes; 0125 pqModes << Integer8BitsColorDepthID; 0126 pqModes << Integer16BitsColorDepthID; 0127 pqModes << Float16BitsColorDepthID; 0128 pqModes << Float32BitsColorDepthID; 0129 0130 Q_FOREACH(const KoID &src, linearModes) { 0131 Q_FOREACH(const KoID &dst, pqModes) { 0132 testRoundTrip(src, dst, HDR); 0133 } 0134 } 0135 0136 Q_FOREACH(const KoID &src, linearModes) { 0137 Q_FOREACH(const KoID &dst, pqModes) { 0138 testRoundTrip(src, dst, HDR_PQ); 0139 } 0140 } 0141 } 0142 0143 void TestLcmsRGBP2020PQColorSpace::testInternalConversions() 0144 { 0145 const KoColorProfile *p2020PQProfile = KoColorSpaceRegistry::instance()->p2020PQProfile(); 0146 0147 QVector<KoID> pqModes; 0148 pqModes << Integer16BitsColorDepthID; 0149 pqModes << Float16BitsColorDepthID; 0150 pqModes << Float32BitsColorDepthID; 0151 0152 Q_FOREACH(const KoID &src, pqModes) { 0153 Q_FOREACH(const KoID &dst, pqModes) { 0154 if (src == dst) continue; 0155 0156 const KoColorSpace *srcCS = KoColorSpaceRegistry::instance()->colorSpace(RGBAColorModelID.id(), src.id(), p2020PQProfile); 0157 const KoColorSpace *dstCS = KoColorSpaceRegistry::instance()->colorSpace(RGBAColorModelID.id(), dst.id(), p2020PQProfile); 0158 0159 testRoundTrip(srcCS, dstCS, HDR_PQ); 0160 } 0161 } 0162 } 0163 0164 void TestLcmsRGBP2020PQColorSpace::testConvertToCmyk() 0165 { 0166 const KoColorProfile *p2020PQProfile = KoColorSpaceRegistry::instance()->p2020PQProfile(); 0167 0168 const KoColorSpace *srcCS = KoColorSpaceRegistry::instance()->colorSpace(RGBAColorModelID.id(), Integer16BitsColorDepthID.id(), p2020PQProfile); 0169 const KoColorSpace *dstCS = KoColorSpaceRegistry::instance()->colorSpace(CMYKAColorModelID.id(), Integer8BitsColorDepthID.id(), 0); 0170 0171 testRoundTrip(srcCS, dstCS, SDR); 0172 } 0173 0174 void TestLcmsRGBP2020PQColorSpace::testHDRClippingRec709ToRec2020() 0175 { 0176 const KoColorProfile *p2020G10Profile = KoColorSpaceRegistry::instance()->p2020G10Profile(); 0177 const KoColorProfile *p709G10Profile = KoColorSpaceRegistry::instance()->p709G10Profile(); 0178 0179 QVERIFY(p2020G10Profile); 0180 QVERIFY(p709G10Profile); 0181 0182 const KoColorSpace *p709CS = KoColorSpaceRegistry::instance()->colorSpace(RGBAColorModelID.id(), Float32BitsColorDepthID.id(), p709G10Profile); 0183 const KoColorSpace *p2020CS = KoColorSpaceRegistry::instance()->colorSpace(RGBAColorModelID.id(), Float32BitsColorDepthID.id(), p2020G10Profile); 0184 0185 KoColor p709Color(Qt::white, p709CS); 0186 0187 /** 0188 * Set the colors above the normal range to check if they are clipped during 0189 * the conversion 0190 */ 0191 float* colorsPtr = reinterpret_cast<float*>(p709Color.data()); 0192 colorsPtr[0] = 1.5f; 0193 colorsPtr[1] = 0.7f; 0194 colorsPtr[2] = 0.3f; 0195 0196 KoColor p2020Color = p709Color.convertedTo(p2020CS); 0197 KoColor p709ConvertedColor = p2020Color.convertedTo(p709CS); 0198 0199 QCOMPARE(p709Color, p709ConvertedColor); 0200 } 0201 0202 KISTEST_MAIN(TestLcmsRGBP2020PQColorSpace)