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 <KoColorProfileQuery.h>
14#include <kis_assert.h>
15
16#include "LcmsColorSpace.h"
18#include "RgbU8ColorSpace.h"
19
20// -- KoLcmsColorConversionTransformation --
21
23{
24public:
25 KoLcmsColorConversionTransformation(const KoColorSpace *srcCs, quint32 srcColorSpaceType, LcmsColorProfileContainer *srcProfile,
26 const KoColorSpace *dstCs, quint32 dstColorSpaceType, LcmsColorProfileContainer *dstProfile,
28 ConversionFlags conversionFlags)
30 , m_transform(0)
31 {
32 Q_ASSERT(srcCs);
33 Q_ASSERT(dstCs);
34 Q_ASSERT(renderingIntent < 4);
35
36 if ((srcProfile->isLinear() || dstProfile->isLinear()) &&
38
40 }
42
43 // unset Krita-only flag
45
46 m_transform = cmsCreateTransform(srcProfile->lcmsProfile(),
47 srcColorSpaceType,
48 dstProfile->lcmsProfile(),
49 dstColorSpaceType,
52
53 Q_ASSERT(m_transform);
54 }
55
57 {
58 cmsDeleteTransform(m_transform);
59 }
60
61public:
62
63 void transform(const quint8 *src, quint8 *dst, qint32 numPixels) const override
64 {
65 Q_ASSERT(m_transform);
66
67 cmsDoTransform(m_transform, const_cast<quint8 *>(src), dst, numPixels);
68
69 }
70private:
71 mutable cmsHTRANSFORM m_transform;
72};
73
75{
76public:
77 KoLcmsColorProofingConversionTransformation(const KoColorSpace *srcCs, quint32 srcColorSpaceType, LcmsColorProfileContainer *srcProfile,
78 const KoColorSpace *dstCs, quint32 dstColorSpaceType, LcmsColorProfileContainer *dstProfile,
81 Intent proofingIntent,
82 bool bpcFirstTransform,
83 quint8 *gamutWarning,
84 ConversionFlags displayConversionFlags
85 )
86 : KoColorProofingConversionTransformation(srcCs, dstCs, proofingSpace, renderingIntent, displayConversionFlags)
87 , m_transform(0)
88 {
89 Q_ASSERT(srcCs);
90 Q_ASSERT(dstCs);
91 Q_ASSERT(renderingIntent < 4);
92
93 bool doBPC1 = bpcFirstTransform;
94 bool doBPC2 = displayConversionFlags.testFlag(KoColorConversionTransformation::BlackpointCompensation);
95
96 if ((srcProfile->isLinear() || dstProfile->isLinear()) &&
97 !displayConversionFlags.testFlag(KoColorConversionTransformation::NoOptimization)) {
99 }
100 displayConversionFlags |= KoColorConversionTransformation::CopyAlpha;
101
102 const double adaptationState = displayConversionFlags.testFlag(KoColorConversionTransformation::NoAdaptationAbsoluteIntent) ? 0.0 : 1.0;
103
104 // unset Krita-only flag
105 displayConversionFlags.setFlag(KoColorConversionTransformation::NoAdaptationAbsoluteIntent, false);
106
107 quint16 alarm[cmsMAXCHANNELS];//this seems to be bgr???
108 alarm[0] = (cmsUInt16Number)gamutWarning[2]*256;
109 alarm[1] = (cmsUInt16Number)gamutWarning[1]*256;
110 alarm[2] = (cmsUInt16Number)gamutWarning[0]*256;
111 cmsSetAlarmCodes(alarm);
112
113 KIS_ASSERT(dynamic_cast<const IccColorProfile *>(proofingSpace->profile()));
114
115 // This more or less does the same thing as cmsCreateProofingTransform in LCMS' cmsxform.c file,
116 // except we try to allow enabling blackpoint compentation on the second bpc too.
117 cmsHPROFILE proof = dynamic_cast<const IccColorProfile *>(proofingSpace->profile())->asLcms()->lcmsProfile();
118 cmsHPROFILE profiles[] = {srcProfile->lcmsProfile(),
119 proof,
120 proof,
121 dstProfile->lcmsProfile()};
122 cmsBool bpc[] = {doBPC1, doBPC1, doBPC2, doBPC2};
123 // Note that of the two transforms that create the proofing transform, the proofing intent is the second intent, not the first!
124 cmsUInt32Number intents[] = {renderingIntent, renderingIntent, INTENT_RELATIVE_COLORIMETRIC, proofingIntent};
125 cmsFloat64Number adaptation[] = {adaptationState, adaptationState, adaptationState, adaptationState};
126 m_transform = cmsCreateExtendedTransform(cmsGetProfileContextID(srcProfile->lcmsProfile()), 4, profiles, bpc, intents, adaptation, proof, 1, srcColorSpaceType, dstColorSpaceType, displayConversionFlags);
127
128 Q_ASSERT(m_transform);
129 }
130
132 {
133 cmsDeleteTransform(m_transform);
134 }
135
136public:
137
138 void transform(const quint8 *src, quint8 *dst, qint32 numPixels) const override
139 {
140 Q_ASSERT(m_transform);
141
142 cmsDoTransform(m_transform, const_cast<quint8 *>(src), dst, numPixels);
143
144 }
145private:
146 mutable cmsHTRANSFORM m_transform;
147};
148
151
153{
154}
155
160
161const KoColorProfile* IccColorSpaceEngine::addProfile(const QString &filename)
162{
164
165 KoColorProfile *profile = new IccColorProfile(filename);
166 Q_CHECK_PTR(profile);
167
168 // this our own loading code; sometimes it fails because of an lcms error
169 profile->load();
170
171 // and then lcms can read the profile from file itself without problems,
172 // quite often, and we can initialize it
173 if (!profile->valid()) {
174 cmsHPROFILE cmsp = cmsOpenProfileFromFile(filename.toLatin1(), "r");
175 if (cmsp) {
177 }
178 }
179
180 if (profile->valid()) {
181 dbgPigment << "Valid profile : " << profile->fileName() << profile->name();
182 registry->addProfile(profile);
183 } else {
184 dbgPigment << "Invalid profile : " << profile->fileName() << profile->name();
185 delete profile;
186 profile = 0;
187 }
188
189 return profile;
190}
191
193{
195
196 KoColorProfile *profile = new IccColorProfile(data);
197 Q_CHECK_PTR(profile);
198
199 if (profile->valid()) {
200 dbgPigment << "Valid profile : " << profile->fileName() << profile->name();
201 registry->addProfile(profile);
202 } else {
203 dbgPigment << "Invalid profile : " << profile->fileName() << profile->name();
204 delete profile;
205 profile = 0;
206 }
207
208 return profile;
209}
210
212{
214
215 KoColorProfileQuery modifiedQuery = query;
216 KIS_SAFE_ASSERT_RECOVER(modifiedQuery.isValid())
217 {
218 if (modifiedQuery.transfer == TRC_LINEAR) {
220 } else {
221 modifiedQuery.primaries = PRIMARIES_ITU_R_BT_709_5;
222 }
223
224 if (modifiedQuery.transfer == TRC_UNSPECIFIED) {
225 modifiedQuery.transfer = TRC_IEC_61966_2_1;
226 }
227 }
228
229 const KoColorProfile *profile = new IccColorProfile(modifiedQuery);
230 Q_CHECK_PTR(profile);
231
232 if (profile->valid()) {
233 dbgPigment << "Valid profile : " << profile->fileName() << profile->name();
234 registry->addProfile(profile);
235 } else {
236 dbgPigment << "Invalid profile : " << profile->fileName() << profile->name();
237 delete profile;
238 profile = nullptr;
239 }
240
241 return profile;
242}
243
244void IccColorSpaceEngine::removeProfile(const QString &filename)
245{
247
248 KoColorProfile *profile = new IccColorProfile(filename);
249 Q_CHECK_PTR(profile);
250 profile->load();
251
252 if (profile->valid() && registry->profileByName(profile->name())) {
253 registry->removeProfile(profile);
254 }
255}
256
258 const KoColorSpace *dstColorSpace,
260 KoColorConversionTransformation::ConversionFlags conversionFlags) const
261{
262 KIS_ASSERT(srcColorSpace);
263 KIS_ASSERT(dstColorSpace);
264 KIS_ASSERT(dynamic_cast<const IccColorProfile *>(srcColorSpace->profile()));
265 KIS_ASSERT(dynamic_cast<const IccColorProfile *>(dstColorSpace->profile()));
266
268 srcColorSpace, computeColorSpaceType(srcColorSpace),
269 dynamic_cast<const IccColorProfile *>(srcColorSpace->profile())->asLcms(), dstColorSpace, computeColorSpaceType(dstColorSpace),
270 dynamic_cast<const IccColorProfile *>(dstColorSpace->profile())->asLcms(), renderingIntent, conversionFlags);
271
272}
274 const KoColorSpace *dstColorSpace,
275 const KoColorSpace *proofingSpace,
278 bool firstTransformBPC,
279 quint8 *gamutWarning,
280 KoColorConversionTransformation::ConversionFlags displayConversionFlags) const
281{
282 KIS_ASSERT(srcColorSpace);
283 KIS_ASSERT(dstColorSpace);
284 KIS_ASSERT(dynamic_cast<const IccColorProfile *>(srcColorSpace->profile()));
285 KIS_ASSERT(dynamic_cast<const IccColorProfile *>(dstColorSpace->profile()));
286
288 srcColorSpace, computeColorSpaceType(srcColorSpace),
289 dynamic_cast<const IccColorProfile *>(srcColorSpace->profile())->asLcms(), dstColorSpace, computeColorSpaceType(dstColorSpace),
290 dynamic_cast<const IccColorProfile *>(dstColorSpace->profile())->asLcms(), proofingSpace, renderingIntent, proofingIntent, firstTransformBPC, gamutWarning,
291 displayConversionFlags
292 );
293}
294
296{
297 Q_ASSERT(cs);
298
299 if (const KoLcmsInfo *lcmsInfo = dynamic_cast<const KoLcmsInfo *>(cs)) {
300 return lcmsInfo->colorSpaceType();
301 } else {
302 QString modelId = cs->colorModelId().id();
303 QString depthId = cs->colorDepthId().id();
304 // Compute the depth part of the type
305 quint32 depthType;
306
307 if (depthId == Integer8BitsColorDepthID.id()) {
308 depthType = BYTES_SH(1);
309 } else if (depthId == Integer16BitsColorDepthID.id()) {
310 depthType = BYTES_SH(2);
311 } else if (depthId == Float16BitsColorDepthID.id()) {
312 depthType = BYTES_SH(2) | FLOAT_SH(1);
313 } else if (depthId == Float32BitsColorDepthID.id()) {
314 depthType = BYTES_SH(4) | FLOAT_SH(1);
315 } else if (depthId == Float64BitsColorDepthID.id()) {
316 depthType = BYTES_SH(0) | FLOAT_SH(1);
317 } else {
318 qWarning() << "Unknown bit depth";
319 return 0;
320 }
321 // Compute the model part of the type
322 quint32 modelType = 0;
323
324 if (modelId == RGBAColorModelID.id()) {
325 if (depthId.startsWith(QLatin1Char('U'))) {
326 modelType = (COLORSPACE_SH(PT_RGB) | EXTRA_SH(1) | CHANNELS_SH(3) | DOSWAP_SH(1) | SWAPFIRST_SH(1));
327 } else if (depthId.startsWith(QLatin1Char('F'))) {
328 modelType = (COLORSPACE_SH(PT_RGB) | EXTRA_SH(1) | CHANNELS_SH(3));
329 }
330 } else if (modelId == XYZAColorModelID.id()) {
331 modelType = (COLORSPACE_SH(PT_XYZ) | EXTRA_SH(1) | CHANNELS_SH(3));
332 } else if (modelId == LABAColorModelID.id()) {
333 modelType = (COLORSPACE_SH(PT_Lab) | EXTRA_SH(1) | CHANNELS_SH(3));
334 } else if (modelId == CMYKAColorModelID.id()) {
335 modelType = (COLORSPACE_SH(PT_CMYK) | EXTRA_SH(1) | CHANNELS_SH(4));
336 } else if (modelId == GrayAColorModelID.id()) {
337 modelType = (COLORSPACE_SH(PT_GRAY) | EXTRA_SH(1) | CHANNELS_SH(1));
338 } else if (modelId == GrayColorModelID.id()) {
339 modelType = (COLORSPACE_SH(PT_GRAY) | CHANNELS_SH(1));
340 } else if (modelId == YCbCrAColorModelID.id()) {
341 modelType = (COLORSPACE_SH(PT_YCbCr) | EXTRA_SH(1) | CHANNELS_SH(3));
342 } else {
343 qWarning() << "Cannot convert colorspace to lcms modeltype";
344 return 0;
345 }
346 return depthType | modelType;
347 }
348}
349
350bool IccColorSpaceEngine::supportsColorSpace(const QString &colorModelId, const QString &colorDepthId, const KoColorProfile *profile) const
351{
352 Q_UNUSED(colorDepthId);
353 return colorModelId != RGBAColorModelID.id() ||
354 !profile ||
356 profile->name() != "High Dynamic Range UHDTV Wide Color Gamut Display (Rec. 2020) - SMPTE ST 2084 PQ EOTF");
357}
#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)"))
@ PRIMARIES_ITU_R_BT_2020_2_AND_2100_0
@ PRIMARIES_ITU_R_BT_709_5
@ TRC_ITU_R_BT_2100_0_PQ
@ TRC_IEC_61966_2_1
LcmsColorProfileContainer * asLcms() const
const KoColorProfile * getProfile(const KoColorProfileQuery &query) override
getProfile This tries to generate a profile with the given characteristics and add it to the registry...
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 * 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
The KoColorProfileQuery struct.
TransferCharacteristics transfer
CICP-compatible enum value representing the white point and primaries.
ColorPrimaries primaries
Rgb Primaries of the profile. When empty, this is a query for a greyscale profile.
bool isValid() const
CICP-compatible enum value representing the transfer function.
virtual bool load()
virtual bool valid() const =0
virtual TransferCharacteristics getTransferCharacteristics() const
getTransferCharacteristics This function should be subclassed at some point so we can get the value f...
const KoColorProfile * profileByName(const QString &name) const
static KoColorSpaceRegistry * instance()
void removeProfile(KoColorProfile *profile)
void addProfile(KoColorProfile *profile)