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