Krita Source Code Documentation
Loading...
Searching...
No Matches
IccColorSpaceEngine.cpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2007-2008 Cyrille Berger <cberger@cberger.net>
3 * SPDX-FileCopyrightText: 2011 Srikanth Tiyyagura <srikanth.tulasiram@gmail.com>
4 *
5 * SPDX-License-Identifier: LGPL-2.1-or-later
6 */
7
9
10#include <klocalizedstring.h>
11
13#include <kis_assert.h>
14
15#include "LcmsColorSpace.h"
16
17// -- KoLcmsColorConversionTransformation --
18
20{
21public:
22 KoLcmsColorConversionTransformation(const KoColorSpace *srcCs, quint32 srcColorSpaceType, LcmsColorProfileContainer *srcProfile,
23 const KoColorSpace *dstCs, quint32 dstColorSpaceType, LcmsColorProfileContainer *dstProfile,
25 ConversionFlags conversionFlags)
27 , m_transform(0)
28 {
29 Q_ASSERT(srcCs);
30 Q_ASSERT(dstCs);
31 Q_ASSERT(renderingIntent < 4);
32
33 if ((srcProfile->isLinear() || dstProfile->isLinear()) &&
35
37 }
39
40 // unset Krita-only flag
42
43 m_transform = cmsCreateTransform(srcProfile->lcmsProfile(),
44 srcColorSpaceType,
45 dstProfile->lcmsProfile(),
46 dstColorSpaceType,
49
50 Q_ASSERT(m_transform);
51 }
52
54 {
55 cmsDeleteTransform(m_transform);
56 }
57
58public:
59
60 void transform(const quint8 *src, quint8 *dst, qint32 numPixels) const override
61 {
62 Q_ASSERT(m_transform);
63
64 cmsDoTransform(m_transform, const_cast<quint8 *>(src), dst, numPixels);
65
66 }
67private:
68 mutable cmsHTRANSFORM m_transform;
69};
70
72{
73public:
74 KoLcmsColorProofingConversionTransformation(const KoColorSpace *srcCs, quint32 srcColorSpaceType, LcmsColorProfileContainer *srcProfile,
75 const KoColorSpace *dstCs, quint32 dstColorSpaceType, LcmsColorProfileContainer *dstProfile,
78 Intent proofingIntent,
79 bool bpcFirstTransform,
80 quint8 *gamutWarning,
81 ConversionFlags displayConversionFlags
82 )
83 : KoColorProofingConversionTransformation(srcCs, dstCs, proofingSpace, renderingIntent, displayConversionFlags)
84 , m_transform(0)
85 {
86 Q_ASSERT(srcCs);
87 Q_ASSERT(dstCs);
88 Q_ASSERT(renderingIntent < 4);
89
90 bool doBPC1 = bpcFirstTransform;
91 bool doBPC2 = displayConversionFlags.testFlag(KoColorConversionTransformation::BlackpointCompensation);
92
93 if ((srcProfile->isLinear() || dstProfile->isLinear()) &&
94 !displayConversionFlags.testFlag(KoColorConversionTransformation::NoOptimization)) {
96 }
97 displayConversionFlags |= KoColorConversionTransformation::CopyAlpha;
98
99 const double adaptationState = displayConversionFlags.testFlag(KoColorConversionTransformation::NoAdaptationAbsoluteIntent) ? 0.0 : 1.0;
100
101 // unset Krita-only flag
102 displayConversionFlags.setFlag(KoColorConversionTransformation::NoAdaptationAbsoluteIntent, false);
103
104 quint16 alarm[cmsMAXCHANNELS];//this seems to be bgr???
105 alarm[0] = (cmsUInt16Number)gamutWarning[2]*256;
106 alarm[1] = (cmsUInt16Number)gamutWarning[1]*256;
107 alarm[2] = (cmsUInt16Number)gamutWarning[0]*256;
108 cmsSetAlarmCodes(alarm);
109
110 KIS_ASSERT(dynamic_cast<const IccColorProfile *>(proofingSpace->profile()));
111
112 // This more or less does the same thing as cmsCreateProofingTransform in LCMS' cmsxform.c file,
113 // except we try to allow enabling blackpoint compentation on the second bpc too.
114 cmsHPROFILE proof = dynamic_cast<const IccColorProfile *>(proofingSpace->profile())->asLcms()->lcmsProfile();
115 cmsHPROFILE profiles[] = {srcProfile->lcmsProfile(),
116 proof,
117 proof,
118 dstProfile->lcmsProfile()};
119 cmsBool bpc[] = {doBPC1, doBPC1, doBPC2, doBPC2};
120 // Note that of the two transforms that create the proofing transform, the proofing intent is the second intent, not the first!
121 cmsUInt32Number intents[] = {renderingIntent, renderingIntent, INTENT_RELATIVE_COLORIMETRIC, proofingIntent};
122 cmsFloat64Number adaptation[] = {adaptationState, adaptationState, adaptationState, adaptationState};
123 m_transform = cmsCreateExtendedTransform(cmsGetProfileContextID(srcProfile->lcmsProfile()), 4, profiles, bpc, intents, adaptation, proof, 1, srcColorSpaceType, dstColorSpaceType, displayConversionFlags);
124
125 Q_ASSERT(m_transform);
126 }
127
129 {
130 cmsDeleteTransform(m_transform);
131 }
132
133public:
134
135 void transform(const quint8 *src, quint8 *dst, qint32 numPixels) const override
136 {
137 Q_ASSERT(m_transform);
138
139 cmsDoTransform(m_transform, const_cast<quint8 *>(src), dst, numPixels);
140
141 }
142private:
143 mutable cmsHTRANSFORM m_transform;
144};
145
148
150{
151}
152
157
158const KoColorProfile* IccColorSpaceEngine::addProfile(const QString &filename)
159{
161
162 KoColorProfile *profile = new IccColorProfile(filename);
163 Q_CHECK_PTR(profile);
164
165 // this our own loading code; sometimes it fails because of an lcms error
166 profile->load();
167
168 // and then lcms can read the profile from file itself without problems,
169 // quite often, and we can initialize it
170 if (!profile->valid()) {
171 cmsHPROFILE cmsp = cmsOpenProfileFromFile(filename.toLatin1(), "r");
172 if (cmsp) {
174 }
175 }
176
177 if (profile->valid()) {
178 dbgPigment << "Valid profile : " << profile->fileName() << profile->name();
179 registry->addProfile(profile);
180 } else {
181 dbgPigment << "Invalid profile : " << profile->fileName() << profile->name();
182 delete profile;
183 profile = 0;
184 }
185
186 return profile;
187}
188
190{
192
193 KoColorProfile *profile = new IccColorProfile(data);
194 Q_CHECK_PTR(profile);
195
196 if (profile->valid()) {
197 dbgPigment << "Valid profile : " << profile->fileName() << profile->name();
198 registry->addProfile(profile);
199 } else {
200 dbgPigment << "Invalid profile : " << profile->fileName() << profile->name();
201 delete profile;
202 profile = 0;
203 }
204
205 return profile;
206}
207
209{
211
213 (!colorants.isEmpty() || colorPrimaries != PRIMARIES_UNSPECIFIED)
214 && transferFunction != TRC_UNSPECIFIED)
215 {
216 if (transferFunction == TRC_LINEAR) {
218 } else {
219 colorPrimaries = PRIMARIES_ITU_R_BT_709_5;
220 }
221
222 if (transferFunction == TRC_UNSPECIFIED) {
223 transferFunction = TRC_IEC_61966_2_1;
224 }
225 }
226
227 const KoColorProfile *profile = new IccColorProfile(colorants, colorPrimaries, transferFunction);
228 Q_CHECK_PTR(profile);
229
230 if (profile->valid()) {
231 dbgPigment << "Valid profile : " << profile->fileName() << profile->name();
232 registry->addProfile(profile);
233 } else {
234 dbgPigment << "Invalid profile : " << profile->fileName() << profile->name();
235 delete profile;
236 profile = nullptr;
237 }
238
239 return profile;
240}
241
242void IccColorSpaceEngine::removeProfile(const QString &filename)
243{
245
246 KoColorProfile *profile = new IccColorProfile(filename);
247 Q_CHECK_PTR(profile);
248 profile->load();
249
250 if (profile->valid() && registry->profileByName(profile->name())) {
251 registry->removeProfile(profile);
252 }
253}
254
256 const KoColorSpace *dstColorSpace,
258 KoColorConversionTransformation::ConversionFlags conversionFlags) const
259{
260 KIS_ASSERT(srcColorSpace);
261 KIS_ASSERT(dstColorSpace);
262 KIS_ASSERT(dynamic_cast<const IccColorProfile *>(srcColorSpace->profile()));
263 KIS_ASSERT(dynamic_cast<const IccColorProfile *>(dstColorSpace->profile()));
264
266 srcColorSpace, computeColorSpaceType(srcColorSpace),
267 dynamic_cast<const IccColorProfile *>(srcColorSpace->profile())->asLcms(), dstColorSpace, computeColorSpaceType(dstColorSpace),
268 dynamic_cast<const IccColorProfile *>(dstColorSpace->profile())->asLcms(), renderingIntent, conversionFlags);
269
270}
272 const KoColorSpace *dstColorSpace,
273 const KoColorSpace *proofingSpace,
276 bool firstTransformBPC,
277 quint8 *gamutWarning,
278 KoColorConversionTransformation::ConversionFlags displayConversionFlags) const
279{
280 KIS_ASSERT(srcColorSpace);
281 KIS_ASSERT(dstColorSpace);
282 KIS_ASSERT(dynamic_cast<const IccColorProfile *>(srcColorSpace->profile()));
283 KIS_ASSERT(dynamic_cast<const IccColorProfile *>(dstColorSpace->profile()));
284
286 srcColorSpace, computeColorSpaceType(srcColorSpace),
287 dynamic_cast<const IccColorProfile *>(srcColorSpace->profile())->asLcms(), dstColorSpace, computeColorSpaceType(dstColorSpace),
288 dynamic_cast<const IccColorProfile *>(dstColorSpace->profile())->asLcms(), proofingSpace, renderingIntent, proofingIntent, firstTransformBPC, gamutWarning,
289 displayConversionFlags
290 );
291}
292
294{
295 Q_ASSERT(cs);
296
297 if (const KoLcmsInfo *lcmsInfo = dynamic_cast<const KoLcmsInfo *>(cs)) {
298 return lcmsInfo->colorSpaceType();
299 } else {
300 QString modelId = cs->colorModelId().id();
301 QString depthId = cs->colorDepthId().id();
302 // Compute the depth part of the type
303 quint32 depthType;
304
305 if (depthId == Integer8BitsColorDepthID.id()) {
306 depthType = BYTES_SH(1);
307 } else if (depthId == Integer16BitsColorDepthID.id()) {
308 depthType = BYTES_SH(2);
309 } else if (depthId == Float16BitsColorDepthID.id()) {
310 depthType = BYTES_SH(2) | FLOAT_SH(1);
311 } else if (depthId == Float32BitsColorDepthID.id()) {
312 depthType = BYTES_SH(4) | FLOAT_SH(1);
313 } else if (depthId == Float64BitsColorDepthID.id()) {
314 depthType = BYTES_SH(0) | FLOAT_SH(1);
315 } else {
316 qWarning() << "Unknown bit depth";
317 return 0;
318 }
319 // Compute the model part of the type
320 quint32 modelType = 0;
321
322 if (modelId == RGBAColorModelID.id()) {
323 if (depthId.startsWith(QLatin1Char('U'))) {
324 modelType = (COLORSPACE_SH(PT_RGB) | EXTRA_SH(1) | CHANNELS_SH(3) | DOSWAP_SH(1) | SWAPFIRST_SH(1));
325 } else if (depthId.startsWith(QLatin1Char('F'))) {
326 modelType = (COLORSPACE_SH(PT_RGB) | EXTRA_SH(1) | CHANNELS_SH(3));
327 }
328 } else if (modelId == XYZAColorModelID.id()) {
329 modelType = (COLORSPACE_SH(PT_XYZ) | EXTRA_SH(1) | CHANNELS_SH(3));
330 } else if (modelId == LABAColorModelID.id()) {
331 modelType = (COLORSPACE_SH(PT_Lab) | EXTRA_SH(1) | CHANNELS_SH(3));
332 } else if (modelId == CMYKAColorModelID.id()) {
333 modelType = (COLORSPACE_SH(PT_CMYK) | EXTRA_SH(1) | CHANNELS_SH(4));
334 } else if (modelId == GrayAColorModelID.id()) {
335 modelType = (COLORSPACE_SH(PT_GRAY) | EXTRA_SH(1) | CHANNELS_SH(1));
336 } else if (modelId == GrayColorModelID.id()) {
337 modelType = (COLORSPACE_SH(PT_GRAY) | CHANNELS_SH(1));
338 } else if (modelId == YCbCrAColorModelID.id()) {
339 modelType = (COLORSPACE_SH(PT_YCbCr) | EXTRA_SH(1) | CHANNELS_SH(3));
340 } else {
341 qWarning() << "Cannot convert colorspace to lcms modeltype";
342 return 0;
343 }
344 return depthType | modelType;
345 }
346}
347
348bool IccColorSpaceEngine::supportsColorSpace(const QString &colorModelId, const QString &colorDepthId, const KoColorProfile *profile) const
349{
350 Q_UNUSED(colorDepthId);
351 return colorModelId != RGBAColorModelID.id() || !profile || profile->name() != "High Dynamic Range UHDTV Wide Color Gamut Display (Rec. 2020) - SMPTE ST 2084 PQ EOTF";
352}
#define dbgPigment
const KoID Float32BitsColorDepthID("F32", ki18n("32-bit float/channel"))
const KoID YCbCrAColorModelID("YCbCrA", ki18n("YCbCr/Alpha"))
const KoID Float64BitsColorDepthID("F64", ki18n("64-bit float/channel"))
const KoID GrayAColorModelID("GRAYA", ki18n("Grayscale/Alpha"))
const KoID XYZAColorModelID("XYZA", ki18n("XYZ/Alpha"))
const KoID Float16BitsColorDepthID("F16", ki18n("16-bit float/channel"))
const KoID Integer8BitsColorDepthID("U8", ki18n("8-bit integer/channel"))
const KoID Integer16BitsColorDepthID("U16", ki18n("16-bit integer/channel"))
const KoID CMYKAColorModelID("CMYKA", ki18n("CMYK/Alpha"))
const KoID LABAColorModelID("LABA", ki18n("L*a*b*/Alpha"))
const KoID RGBAColorModelID("RGBA", ki18n("RGB/Alpha"))
const KoID GrayColorModelID("GRAY", ki18n("Grayscale (without transparency)"))
ColorPrimaries
The colorPrimaries enum Enum of colorants, follows ITU H.273 for values 0 to 255, and has extra known...
@ PRIMARIES_ITU_R_BT_2020_2_AND_2100_0
@ PRIMARIES_UNSPECIFIED
@ PRIMARIES_ITU_R_BT_709_5
TransferCharacteristics
The transferCharacteristics enum Enum of transfer characteristics, follows ITU H.273 for values 0 to ...
@ TRC_IEC_61966_2_1
LcmsColorProfileContainer * asLcms() const
KoColorConversionTransformation * createColorTransformation(const KoColorSpace *srcColorSpace, const KoColorSpace *dstColorSpace, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags) const override
void removeProfile(const QString &filename) override
KoColorProofingConversionTransformation * createColorProofingTransformation(const KoColorSpace *srcColorSpace, const KoColorSpace *dstColorSpace, const KoColorSpace *proofingSpace, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::Intent proofingIntent, bool firstTransformBPC, quint8 *gamutWarning, KoColorConversionTransformation::ConversionFlags displayConversionFlags) const override
bool supportsColorSpace(const QString &colorModelId, const QString &colorDepthId, const KoColorProfile *profile) const override
quint32 computeColorSpaceType(const KoColorSpace *cs) const
const KoColorProfile * getProfile(const QVector< double > &colorants, ColorPrimaries colorPrimaries, TransferCharacteristics transferFunction) override
getProfile This tries to generate a profile with the given characteristics and add it to the registry...
const KoColorProfile * addProfile(const QString &filename) override
virtual KoID colorModelId() const =0
virtual KoID colorDepthId() const =0
virtual const KoColorProfile * profile() const =0
QString id() const
Definition KoID.cpp:63
KoLcmsColorConversionTransformation(const KoColorSpace *srcCs, quint32 srcColorSpaceType, LcmsColorProfileContainer *srcProfile, const KoColorSpace *dstCs, quint32 dstColorSpaceType, LcmsColorProfileContainer *dstProfile, Intent renderingIntent, ConversionFlags conversionFlags)
void transform(const quint8 *src, quint8 *dst, qint32 numPixels) const override
KoLcmsColorProofingConversionTransformation(const KoColorSpace *srcCs, quint32 srcColorSpaceType, LcmsColorProfileContainer *srcProfile, const KoColorSpace *dstCs, quint32 dstColorSpaceType, LcmsColorProfileContainer *dstProfile, const KoColorSpace *proofingSpace, Intent renderingIntent, Intent proofingIntent, bool bpcFirstTransform, quint8 *gamutWarning, ConversionFlags displayConversionFlags)
void transform(const quint8 *src, quint8 *dst, qint32 numPixels) const override
static IccColorProfile * createFromLcmsProfile(const cmsHPROFILE profile)
#define KIS_SAFE_ASSERT_RECOVER(cond)
Definition kis_assert.h:126
#define KIS_ASSERT(cond)
Definition kis_assert.h:33
#define INTENT_RELATIVE_COLORIMETRIC
Definition kis_global.h:104
virtual bool load()
virtual bool valid() const =0
const KoColorProfile * profileByName(const QString &name) const
static KoColorSpaceRegistry * instance()
void removeProfile(KoColorProfile *profile)
void addProfile(KoColorProfile *profile)