File indexing completed on 2024-06-16 04:15:58
0001 /* 0002 * SPDX-FileCopyrightText: 2007-2008 Cyrille Berger <cberger@cberger.net> 0003 * SPDX-FileCopyrightText: 2011 Srikanth Tiyyagura <srikanth.tulasiram@gmail.com> 0004 * 0005 * SPDX-License-Identifier: LGPL-2.1-or-later 0006 */ 0007 0008 #include "IccColorSpaceEngine.h" 0009 0010 #include <klocalizedstring.h> 0011 0012 #include <KoColorModelStandardIds.h> 0013 #include <kis_assert.h> 0014 0015 #include "LcmsColorSpace.h" 0016 0017 // -- KoLcmsColorConversionTransformation -- 0018 0019 class KoLcmsColorConversionTransformation : public KoColorConversionTransformation 0020 { 0021 public: 0022 KoLcmsColorConversionTransformation(const KoColorSpace *srcCs, quint32 srcColorSpaceType, LcmsColorProfileContainer *srcProfile, 0023 const KoColorSpace *dstCs, quint32 dstColorSpaceType, LcmsColorProfileContainer *dstProfile, 0024 Intent renderingIntent, 0025 ConversionFlags conversionFlags) 0026 : KoColorConversionTransformation(srcCs, dstCs, renderingIntent, conversionFlags) 0027 , m_transform(0) 0028 { 0029 Q_ASSERT(srcCs); 0030 Q_ASSERT(dstCs); 0031 Q_ASSERT(renderingIntent < 4); 0032 0033 if ((srcProfile->isLinear() || dstProfile->isLinear()) && 0034 !conversionFlags.testFlag(KoColorConversionTransformation::NoOptimization)) { 0035 0036 conversionFlags |= KoColorConversionTransformation::NoOptimization; 0037 } 0038 conversionFlags |= KoColorConversionTransformation::CopyAlpha; 0039 0040 m_transform = cmsCreateTransform(srcProfile->lcmsProfile(), 0041 srcColorSpaceType, 0042 dstProfile->lcmsProfile(), 0043 dstColorSpaceType, 0044 renderingIntent, 0045 conversionFlags); 0046 0047 Q_ASSERT(m_transform); 0048 } 0049 0050 ~KoLcmsColorConversionTransformation() override 0051 { 0052 cmsDeleteTransform(m_transform); 0053 } 0054 0055 public: 0056 0057 void transform(const quint8 *src, quint8 *dst, qint32 numPixels) const override 0058 { 0059 Q_ASSERT(m_transform); 0060 0061 cmsDoTransform(m_transform, const_cast<quint8 *>(src), dst, numPixels); 0062 0063 } 0064 private: 0065 mutable cmsHTRANSFORM m_transform; 0066 }; 0067 0068 class KoLcmsColorProofingConversionTransformation : public KoColorProofingConversionTransformation 0069 { 0070 public: 0071 KoLcmsColorProofingConversionTransformation(const KoColorSpace *srcCs, quint32 srcColorSpaceType, LcmsColorProfileContainer *srcProfile, 0072 const KoColorSpace *dstCs, quint32 dstColorSpaceType, LcmsColorProfileContainer *dstProfile, 0073 const KoColorSpace *proofingSpace, 0074 Intent renderingIntent, 0075 Intent proofingIntent, 0076 ConversionFlags conversionFlags, 0077 quint8 *gamutWarning, 0078 double adaptationState 0079 ) 0080 : KoColorProofingConversionTransformation(srcCs, dstCs, proofingSpace, renderingIntent, proofingIntent, conversionFlags, gamutWarning, adaptationState) 0081 , m_transform(0) 0082 { 0083 Q_ASSERT(srcCs); 0084 Q_ASSERT(dstCs); 0085 Q_ASSERT(renderingIntent < 4); 0086 0087 if (srcCs->colorDepthId() == Integer8BitsColorDepthID 0088 || srcCs->colorDepthId() == Integer16BitsColorDepthID) { 0089 0090 if ((srcProfile->name().contains(QLatin1String("linear"), Qt::CaseInsensitive) || 0091 dstProfile->name().contains(QLatin1String("linear"), Qt::CaseInsensitive)) && 0092 !conversionFlags.testFlag(KoColorConversionTransformation::NoOptimization)) { 0093 conversionFlags |= KoColorConversionTransformation::NoOptimization; 0094 } 0095 } 0096 conversionFlags |= KoColorConversionTransformation::CopyAlpha; 0097 0098 quint16 alarm[cmsMAXCHANNELS];//this seems to be bgr??? 0099 alarm[0] = (cmsUInt16Number)gamutWarning[2]*256; 0100 alarm[1] = (cmsUInt16Number)gamutWarning[1]*256; 0101 alarm[2] = (cmsUInt16Number)gamutWarning[0]*256; 0102 cmsSetAlarmCodes(alarm); 0103 cmsSetAdaptationState(adaptationState); 0104 0105 KIS_ASSERT(dynamic_cast<const IccColorProfile *>(proofingSpace->profile())); 0106 m_transform = cmsCreateProofingTransform(srcProfile->lcmsProfile(), 0107 srcColorSpaceType, 0108 dstProfile->lcmsProfile(), 0109 dstColorSpaceType, 0110 dynamic_cast<const IccColorProfile *>(proofingSpace->profile())->asLcms()->lcmsProfile(), 0111 renderingIntent, 0112 proofingIntent, 0113 conversionFlags); 0114 cmsSetAdaptationState(1); 0115 0116 Q_ASSERT(m_transform); 0117 } 0118 0119 ~KoLcmsColorProofingConversionTransformation() override 0120 { 0121 cmsDeleteTransform(m_transform); 0122 } 0123 0124 public: 0125 0126 void transform(const quint8 *src, quint8 *dst, qint32 numPixels) const override 0127 { 0128 Q_ASSERT(m_transform); 0129 0130 cmsDoTransform(m_transform, const_cast<quint8 *>(src), dst, numPixels); 0131 0132 } 0133 private: 0134 mutable cmsHTRANSFORM m_transform; 0135 }; 0136 0137 struct IccColorSpaceEngine::Private { 0138 }; 0139 0140 IccColorSpaceEngine::IccColorSpaceEngine() : KoColorSpaceEngine("icc", i18n("ICC Engine")), d(new Private) 0141 { 0142 } 0143 0144 IccColorSpaceEngine::~IccColorSpaceEngine() 0145 { 0146 delete d; 0147 } 0148 0149 const KoColorProfile* IccColorSpaceEngine::addProfile(const QString &filename) 0150 { 0151 KoColorSpaceRegistry *registry = KoColorSpaceRegistry::instance(); 0152 0153 KoColorProfile *profile = new IccColorProfile(filename); 0154 Q_CHECK_PTR(profile); 0155 0156 // this our own loading code; sometimes it fails because of an lcms error 0157 profile->load(); 0158 0159 // and then lcms can read the profile from file itself without problems, 0160 // quite often, and we can initialize it 0161 if (!profile->valid()) { 0162 cmsHPROFILE cmsp = cmsOpenProfileFromFile(filename.toLatin1(), "r"); 0163 if (cmsp) { 0164 profile = LcmsColorProfileContainer::createFromLcmsProfile(cmsp); 0165 } 0166 } 0167 0168 if (profile->valid()) { 0169 dbgPigment << "Valid profile : " << profile->fileName() << profile->name(); 0170 registry->addProfile(profile); 0171 } else { 0172 dbgPigment << "Invalid profile : " << profile->fileName() << profile->name(); 0173 delete profile; 0174 profile = 0; 0175 } 0176 0177 return profile; 0178 } 0179 0180 const KoColorProfile* IccColorSpaceEngine::addProfile(const QByteArray &data) 0181 { 0182 KoColorSpaceRegistry *registry = KoColorSpaceRegistry::instance(); 0183 0184 KoColorProfile *profile = new IccColorProfile(data); 0185 Q_CHECK_PTR(profile); 0186 0187 if (profile->valid()) { 0188 dbgPigment << "Valid profile : " << profile->fileName() << profile->name(); 0189 registry->addProfile(profile); 0190 } else { 0191 dbgPigment << "Invalid profile : " << profile->fileName() << profile->name(); 0192 delete profile; 0193 profile = 0; 0194 } 0195 0196 return profile; 0197 } 0198 0199 const KoColorProfile *IccColorSpaceEngine::getProfile(const QVector<double> &colorants, ColorPrimaries colorPrimaries, TransferCharacteristics transferFunction) 0200 { 0201 KoColorSpaceRegistry *registry = KoColorSpaceRegistry::instance(); 0202 0203 KIS_SAFE_ASSERT_RECOVER( 0204 (!colorants.isEmpty() || colorPrimaries != PRIMARIES_UNSPECIFIED) 0205 && transferFunction != TRC_UNSPECIFIED) 0206 { 0207 if (transferFunction == TRC_LINEAR) { 0208 colorPrimaries = PRIMARIES_ITU_R_BT_2020_2_AND_2100_0; 0209 } else { 0210 colorPrimaries = PRIMARIES_ITU_R_BT_709_5; 0211 } 0212 0213 if (transferFunction == TRC_UNSPECIFIED) { 0214 transferFunction = TRC_IEC_61966_2_1; 0215 } 0216 } 0217 0218 const KoColorProfile *profile = new IccColorProfile(colorants, colorPrimaries, transferFunction); 0219 Q_CHECK_PTR(profile); 0220 0221 if (profile->valid()) { 0222 dbgPigment << "Valid profile : " << profile->fileName() << profile->name(); 0223 registry->addProfile(profile); 0224 } else { 0225 dbgPigment << "Invalid profile : " << profile->fileName() << profile->name(); 0226 delete profile; 0227 profile = nullptr; 0228 } 0229 0230 return profile; 0231 } 0232 0233 void IccColorSpaceEngine::removeProfile(const QString &filename) 0234 { 0235 KoColorSpaceRegistry *registry = KoColorSpaceRegistry::instance(); 0236 0237 KoColorProfile *profile = new IccColorProfile(filename); 0238 Q_CHECK_PTR(profile); 0239 profile->load(); 0240 0241 if (profile->valid() && registry->profileByName(profile->name())) { 0242 registry->removeProfile(profile); 0243 } 0244 } 0245 0246 KoColorConversionTransformation *IccColorSpaceEngine::createColorTransformation(const KoColorSpace *srcColorSpace, 0247 const KoColorSpace *dstColorSpace, 0248 KoColorConversionTransformation::Intent renderingIntent, 0249 KoColorConversionTransformation::ConversionFlags conversionFlags) const 0250 { 0251 KIS_ASSERT(srcColorSpace); 0252 KIS_ASSERT(dstColorSpace); 0253 KIS_ASSERT(dynamic_cast<const IccColorProfile *>(srcColorSpace->profile())); 0254 KIS_ASSERT(dynamic_cast<const IccColorProfile *>(dstColorSpace->profile())); 0255 0256 return new KoLcmsColorConversionTransformation( 0257 srcColorSpace, computeColorSpaceType(srcColorSpace), 0258 dynamic_cast<const IccColorProfile *>(srcColorSpace->profile())->asLcms(), dstColorSpace, computeColorSpaceType(dstColorSpace), 0259 dynamic_cast<const IccColorProfile *>(dstColorSpace->profile())->asLcms(), renderingIntent, conversionFlags); 0260 0261 } 0262 KoColorProofingConversionTransformation *IccColorSpaceEngine::createColorProofingTransformation(const KoColorSpace *srcColorSpace, 0263 const KoColorSpace *dstColorSpace, 0264 const KoColorSpace *proofingSpace, 0265 KoColorConversionTransformation::Intent renderingIntent, 0266 KoColorConversionTransformation::Intent proofingIntent, 0267 KoColorConversionTransformation::ConversionFlags conversionFlags, 0268 quint8 *gamutWarning, 0269 double adaptationState) const 0270 { 0271 KIS_ASSERT(srcColorSpace); 0272 KIS_ASSERT(dstColorSpace); 0273 KIS_ASSERT(dynamic_cast<const IccColorProfile *>(srcColorSpace->profile())); 0274 KIS_ASSERT(dynamic_cast<const IccColorProfile *>(dstColorSpace->profile())); 0275 0276 return new KoLcmsColorProofingConversionTransformation( 0277 srcColorSpace, computeColorSpaceType(srcColorSpace), 0278 dynamic_cast<const IccColorProfile *>(srcColorSpace->profile())->asLcms(), dstColorSpace, computeColorSpaceType(dstColorSpace), 0279 dynamic_cast<const IccColorProfile *>(dstColorSpace->profile())->asLcms(), proofingSpace, renderingIntent, proofingIntent, conversionFlags, gamutWarning, 0280 adaptationState 0281 ); 0282 } 0283 0284 quint32 IccColorSpaceEngine::computeColorSpaceType(const KoColorSpace *cs) const 0285 { 0286 Q_ASSERT(cs); 0287 0288 if (const KoLcmsInfo *lcmsInfo = dynamic_cast<const KoLcmsInfo *>(cs)) { 0289 return lcmsInfo->colorSpaceType(); 0290 } else { 0291 QString modelId = cs->colorModelId().id(); 0292 QString depthId = cs->colorDepthId().id(); 0293 // Compute the depth part of the type 0294 quint32 depthType; 0295 0296 if (depthId == Integer8BitsColorDepthID.id()) { 0297 depthType = BYTES_SH(1); 0298 } else if (depthId == Integer16BitsColorDepthID.id()) { 0299 depthType = BYTES_SH(2); 0300 } else if (depthId == Float16BitsColorDepthID.id()) { 0301 depthType = BYTES_SH(2) | FLOAT_SH(1); 0302 } else if (depthId == Float32BitsColorDepthID.id()) { 0303 depthType = BYTES_SH(4) | FLOAT_SH(1); 0304 } else if (depthId == Float64BitsColorDepthID.id()) { 0305 depthType = BYTES_SH(0) | FLOAT_SH(1); 0306 } else { 0307 qWarning() << "Unknown bit depth"; 0308 return 0; 0309 } 0310 // Compute the model part of the type 0311 quint32 modelType = 0; 0312 0313 if (modelId == RGBAColorModelID.id()) { 0314 if (depthId.startsWith(QLatin1Char('U'))) { 0315 modelType = (COLORSPACE_SH(PT_RGB) | EXTRA_SH(1) | CHANNELS_SH(3) | DOSWAP_SH(1) | SWAPFIRST_SH(1)); 0316 } else if (depthId.startsWith(QLatin1Char('F'))) { 0317 modelType = (COLORSPACE_SH(PT_RGB) | EXTRA_SH(1) | CHANNELS_SH(3)); 0318 } 0319 } else if (modelId == XYZAColorModelID.id()) { 0320 modelType = (COLORSPACE_SH(PT_XYZ) | EXTRA_SH(1) | CHANNELS_SH(3)); 0321 } else if (modelId == LABAColorModelID.id()) { 0322 modelType = (COLORSPACE_SH(PT_Lab) | EXTRA_SH(1) | CHANNELS_SH(3)); 0323 } else if (modelId == CMYKAColorModelID.id()) { 0324 modelType = (COLORSPACE_SH(PT_CMYK) | EXTRA_SH(1) | CHANNELS_SH(4)); 0325 } else if (modelId == GrayAColorModelID.id()) { 0326 modelType = (COLORSPACE_SH(PT_GRAY) | EXTRA_SH(1) | CHANNELS_SH(1)); 0327 } else if (modelId == GrayColorModelID.id()) { 0328 modelType = (COLORSPACE_SH(PT_GRAY) | CHANNELS_SH(1)); 0329 } else if (modelId == YCbCrAColorModelID.id()) { 0330 modelType = (COLORSPACE_SH(PT_YCbCr) | EXTRA_SH(1) | CHANNELS_SH(3)); 0331 } else { 0332 qWarning() << "Cannot convert colorspace to lcms modeltype"; 0333 return 0; 0334 } 0335 return depthType | modelType; 0336 } 0337 } 0338 0339 bool IccColorSpaceEngine::supportsColorSpace(const QString &colorModelId, const QString &colorDepthId, const KoColorProfile *profile) const 0340 { 0341 Q_UNUSED(colorDepthId); 0342 return colorModelId != RGBAColorModelID.id() || !profile || profile->name() != "High Dynamic Range UHDTV Wide Color Gamut Display (Rec. 2020) - SMPTE ST 2084 PQ EOTF"; 0343 }