2
* Copyright (c) 2002 Patrick Julien <freak@codepimps.org>
3
* Copyright (c) 2005-2006 Casper Boemann <cbr@boemann.dk>
4
* Copyright (c) 2004,2006-2007 Cyrille Berger <cberger@cberger.net>
6
* This library is free software; you can redistribute it and/or
7
* modify it under the terms of the GNU Lesser General Public
8
* License as published by the Free Software Foundation; either
9
* version 2 of the License, or (at your option) any later version.
11
* This library is distributed in the hope that it will be useful,
12
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14
* Lesser General Public License for more details.
16
* You should have received a copy of the GNU Lesser General Public License
17
* along with this library; see the file COPYING.LIB. If not, write to
18
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19
* Boston, MA 02110-1301, USA.
22
#ifndef KOLCMSCOLORSPACE_H_
23
#define KOLCMSCOLORSPACE_H_
28
#include <colorprofiles/KoLcmsColorProfileContainer.h>
29
#include <KoColorSpaceAbstract.h>
30
#include <KoColorSpaceRegistry.h>
32
#include "pigment_export.h"
37
DWORD cmType; // The colorspace type as defined by littlecms
38
icColorSpaceSignature colorSpaceSignature; // The colorspace signature as defined in icm/icc files
41
KoLcmsInfo(DWORD cmType, icColorSpaceSignature colorSpaceSignature) : d(new Private)
44
d->colorSpaceSignature = colorSpaceSignature;
50
virtual quint32 colorSpaceType() const
55
virtual icColorSpaceSignature colorSpaceSignature() const
57
return d->colorSpaceSignature;
63
struct PIGMENTCMS_EXPORT KoLcmsDefaultTransformations {
65
cmsHTRANSFORM fromRGB;
66
static cmsHPROFILE s_RGBProfile;
67
static QMap< QString, QMap< KoLcmsColorProfileContainer*, KoLcmsDefaultTransformations* > > s_transformations;
71
* This is the base class for all colorspaces that are based on the lcms library, for instance
72
* RGB 8bits and 16bits, CMYK 8bits and 16bits, LAB...
74
template<class _CSTraits>
75
class KoLcmsColorSpace : public KoColorSpaceAbstract<_CSTraits>, public KoLcmsInfo {
76
struct KoLcmsColorTransformation : public KoColorTransformation
78
KoLcmsColorTransformation(const KoColorSpace* colorSpace) : KoColorTransformation(), m_colorSpace(colorSpace)
87
~KoLcmsColorTransformation() {
90
cmsDeleteTransform(cmstransform);
91
if (profiles[0] && profiles[0] != csProfile)
92
cmsCloseProfile(profiles[0]);
93
if(profiles[1] && profiles[1] != csProfile)
94
cmsCloseProfile(profiles[1]);
95
if(profiles[2] && profiles[2] != csProfile)
96
cmsCloseProfile(profiles[2]);
99
virtual void transform(const quint8 *src, quint8 *dst, qint32 nPixels) const
101
cmsDoTransform(cmstransform, const_cast<quint8 *>(src), dst, nPixels);
102
qint32 numPixels = nPixels;
103
qint32 pixelSize = m_colorSpace->pixelSize();
104
while (numPixels > 0) {
105
quint8 alpha = m_colorSpace->alpha(src);
106
m_colorSpace->setAlpha(dst, alpha, 1);
114
const KoColorSpace* m_colorSpace;
115
cmsHPROFILE csProfile;
116
cmsHPROFILE profiles[3];
117
cmsHTRANSFORM cmstransform;
121
mutable quint8 * qcolordata; // A small buffer for conversion from and to qcolor.
122
KoLcmsDefaultTransformations* defaultTransformations;
124
mutable cmsHPROFILE lastRGBProfile; // Last used profile to transform to/from RGB
125
mutable cmsHTRANSFORM lastToRGB; // Last used transform to transform to RGB
126
mutable cmsHTRANSFORM lastFromRGB; // Last used transform to transform from RGB
127
KoLcmsColorProfileContainer * profile;
128
KoColorProfile* colorProfile;
133
KoLcmsColorSpace(const QString &id, const QString &name, DWORD cmType,
134
icColorSpaceSignature colorSpaceSignature,
136
: KoColorSpaceAbstract<_CSTraits>(id, name), KoLcmsInfo( cmType, colorSpaceSignature)
140
Q_ASSERT(p); // No profile means the lcms color space can't work
141
Q_ASSERT(profileIsCompatible(p));
142
d->profile = asLcmsProfile(p);
143
Q_ASSERT( d->profile );
146
d->lastRGBProfile = 0;
149
d->defaultTransformations = 0;
152
virtual ~KoLcmsColorSpace()
154
/* cmsCloseProfile(d->lastFromRGB);
155
cmsDeleteTransform( d->defaultFromRGB );
156
cmsDeleteTransform( d->defaultToRGB );*/
157
delete d->colorProfile;
158
delete[] d->qcolordata;
164
// Default pixel buffer for QColor conversion
165
d->qcolordata = new quint8[3];
166
Q_CHECK_PTR(d->qcolordata);
168
Q_ASSERT(d->profile );
170
if( KoLcmsDefaultTransformations::s_RGBProfile == 0 )
172
KoLcmsDefaultTransformations::s_RGBProfile = cmsCreate_sRGBProfile();
174
d->defaultTransformations = KoLcmsDefaultTransformations::s_transformations[ this->id() ][ d->profile ];
175
if( not d->defaultTransformations )
177
d->defaultTransformations = new KoLcmsDefaultTransformations;
178
d->defaultTransformations->fromRGB = cmsCreateTransform(KoLcmsDefaultTransformations::s_RGBProfile,
180
d->profile->lcmsProfile(),
181
this->colorSpaceType(),
184
d->defaultTransformations->toRGB = cmsCreateTransform(d->profile->lcmsProfile(), this->colorSpaceType(),
185
KoLcmsDefaultTransformations::s_RGBProfile, TYPE_BGR_8,
186
INTENT_PERCEPTUAL, 0);
187
KoLcmsDefaultTransformations::s_transformations[ this->id() ][ d->profile ] = d->defaultTransformations;
189
// For conversions from default rgb
190
// d->lastFromRGB = cmsCreate_sRGBProfile();
192
// d->defaultFromRGB = cmsCreateTransform(d->lastFromRGB, TYPE_BGR_8,
193
// d->profile->lcmsProfile(), this->colorSpaceType(),
194
// INTENT_PERCEPTUAL, 0);
196
// d->defaultToRGB = cmsCreateTransform(d->profile->lcmsProfile(), this->colorSpaceType(),
197
// d->lastFromRGB, TYPE_BGR_8,
198
// INTENT_PERCEPTUAL, 0);
203
virtual bool hasHighDynamicRange() const { return false; }
204
virtual const KoColorProfile * profile() const { return d->colorProfile; }
205
virtual KoColorProfile * profile() { return d->colorProfile; }
207
virtual bool profileIsCompatible(const KoColorProfile* profile) const
209
const KoIccColorProfile* p = dynamic_cast<const KoIccColorProfile*>(profile);
210
return (p && p->asLcms()->colorSpaceSignature() == colorSpaceSignature());
213
virtual void fromQColor(const QColor& color, quint8 *dst, const KoColorProfile * koprofile=0) const
215
d->qcolordata[2] = color.red();
216
d->qcolordata[1] = color.green();
217
d->qcolordata[0] = color.blue();
219
KoLcmsColorProfileContainer* profile = asLcmsProfile(koprofile);
222
Q_ASSERT(d->defaultTransformations and d->defaultTransformations->fromRGB);
224
cmsDoTransform(d->defaultTransformations->fromRGB, d->qcolordata, dst, 1);
227
if (d->lastFromRGB == 0 || (d->lastFromRGB != 0 && d->lastRGBProfile != profile->lcmsProfile())) {
228
d->lastFromRGB = cmsCreateTransform(profile->lcmsProfile(), TYPE_BGR_8,
229
d->profile->lcmsProfile(), this->colorSpaceType(),
230
INTENT_PERCEPTUAL, 0);
231
d->lastRGBProfile = profile->lcmsProfile();
234
cmsDoTransform(d->lastFromRGB, d->qcolordata, dst, 1);
237
this->setAlpha(dst, color.alpha() , 1);
240
virtual void toQColor(const quint8 *src, QColor *c, const KoColorProfile * koprofile =0) const
242
KoLcmsColorProfileContainer* profile = asLcmsProfile(koprofile);
244
// Default sRGB transform
245
Q_ASSERT(d->defaultTransformations and d->defaultTransformations->toRGB);
246
cmsDoTransform(d->defaultTransformations->toRGB, const_cast <quint8 *>(src), d->qcolordata, 1);
249
if (d->lastToRGB == 0 || (d->lastToRGB != 0 && d->lastRGBProfile != profile->lcmsProfile())) {
250
d->lastToRGB = cmsCreateTransform(d->profile->lcmsProfile(), this->colorSpaceType(),
251
profile->lcmsProfile(), TYPE_BGR_8,
252
INTENT_PERCEPTUAL, 0);
253
d->lastRGBProfile = profile->lcmsProfile();
255
cmsDoTransform(d->lastToRGB, const_cast <quint8 *>(src), d->qcolordata, 1);
257
c->setRgb(d->qcolordata[2], d->qcolordata[1], d->qcolordata[0]);
258
c->setAlpha( this->alpha(src) );
261
virtual KoColorTransformation *createBrightnessContrastAdjustment(const quint16 *transferValues) const
263
if (!d->profile) return 0;
265
LPGAMMATABLE transferFunctions[3];
266
transferFunctions[0] = cmsBuildGamma(256, 1.0);
267
transferFunctions[1] = cmsBuildGamma(256, 1.0);
268
transferFunctions[2] = cmsBuildGamma(256, 1.0);
270
for(int i =0; i < 256; i++)
271
transferFunctions[0]->GammaTable[i] = transferValues[i];
273
KoLcmsColorTransformation *adj = new KoLcmsColorTransformation(this);
274
adj->profiles[1] = cmsCreateLinearizationDeviceLink(icSigLabData, transferFunctions);
275
cmsSetDeviceClass(adj->profiles[1], icSigAbstractClass);
277
adj->profiles[0] = d->profile->lcmsProfile();
278
adj->profiles[2] = d->profile->lcmsProfile();
279
adj->cmstransform = cmsCreateMultiprofileTransform(adj->profiles, 3, this->colorSpaceType(), this->colorSpaceType(), INTENT_PERCEPTUAL, cmsFLAGS_NOWHITEONWHITEFIXUP);
280
adj->csProfile = d->profile->lcmsProfile();
285
virtual KoColorTransformation *createDesaturateAdjustment() const
287
if (!d->profile) return 0;
289
KoLcmsColorTransformation *adj = new KoLcmsColorTransformation(this);
291
adj->profiles[0] = d->profile->lcmsProfile();
292
adj->profiles[2] = d->profile->lcmsProfile();
293
adj->csProfile = d->profile->lcmsProfile();
298
bchsw.Saturation = -25;
300
adj->profiles[1] = _cmsCreateProfilePlaceholder();
301
if (!adj->profiles[1]) { // can't allocate
306
cmsSetDeviceClass(adj->profiles[1], icSigAbstractClass);
307
cmsSetColorSpace(adj->profiles[1], icSigLabData);
308
cmsSetPCS(adj->profiles[1], icSigLabData);
310
cmsSetRenderingIntent(adj->profiles[1], INTENT_PERCEPTUAL);
312
// Creates a LUT with 3D grid only
315
cmsAlloc3DGrid(Lut, 32, 3, 3);
317
if (!cmsSample3DGrid(Lut, desaturateSampler, static_cast<LPVOID>(&bchsw), 0)) {
318
// Shouldn't reach here
320
cmsCloseProfile(adj->profiles[1]);
327
cmsAddTag(adj->profiles[1], icSigDeviceMfgDescTag, (LPVOID) "(krita internal)");
328
cmsAddTag(adj->profiles[1], icSigProfileDescriptionTag, (LPVOID) "krita saturation abstract profile");
329
cmsAddTag(adj->profiles[1], icSigDeviceModelDescTag, (LPVOID) "saturation built-in");
331
cmsAddTag(adj->profiles[1], icSigMediaWhitePointTag, (LPVOID) cmsD50_XYZ());
333
cmsAddTag(adj->profiles[1], icSigAToB0Tag, (LPVOID) Lut);
335
// LUT is already on virtual profile
338
adj->cmstransform = cmsCreateMultiprofileTransform(adj->profiles, 3, this->colorSpaceType(), this->colorSpaceType(), INTENT_PERCEPTUAL, cmsFLAGS_NOWHITEONWHITEFIXUP);
343
virtual KoColorTransformation *createPerChannelAdjustment(const quint16 * const*transferValues) const
345
if (!d->profile) return 0;
347
LPGAMMATABLE *transferFunctions = new LPGAMMATABLE[ _CSTraits::channels_nb +1];
349
for(uint ch=0; ch < this->colorChannelCount(); ch++) {
350
transferFunctions[ch] = cmsBuildGamma(256, 1.0);
351
for(uint i =0; i < 256; i++) {
352
transferFunctions[ch]->GammaTable[i] = transferValues[ch][i];
356
KoLcmsColorTransformation *adj = new KoLcmsColorTransformation(this);
357
adj->profiles[0] = cmsCreateLinearizationDeviceLink(this->colorSpaceSignature(), transferFunctions);
358
adj->profiles[1] = NULL;
359
adj->profiles[2] = NULL;
360
adj->csProfile = d->profile->lcmsProfile();
361
adj->cmstransform = cmsCreateTransform(adj->profiles[0], this->colorSpaceType(), NULL, this->colorSpaceType(), INTENT_PERCEPTUAL, 0);
363
delete [] transferFunctions;
368
virtual quint8 difference(const quint8* src1, const quint8* src2) const
370
quint8 lab1[8], lab2[8];
371
cmsCIELab labF1, labF2;
373
if (this->alpha(src1) == OPACITY_TRANSPARENT || this->alpha(src2) == OPACITY_TRANSPARENT)
374
return (this->alpha(src1) == this->alpha(src2) ? 0 : 255);
375
Q_ASSERT(this->toLabA16Converter());
376
this->toLabA16Converter()->transform( src1, lab1, 1),
377
this->toLabA16Converter()->transform( src2, lab2, 1),
378
cmsLabEncoded2Float(&labF1, (WORD *)lab1);
379
cmsLabEncoded2Float(&labF2, (WORD *)lab2);
380
qreal diff = cmsDeltaE(&labF1, &labF2);
389
inline KoLcmsColorProfileContainer* lcmsProfile() const {
393
inline static KoLcmsColorProfileContainer* asLcmsProfile(const KoColorProfile* p)
397
const KoIccColorProfile* iccp = dynamic_cast<const KoIccColorProfile*>(p);
404
Q_ASSERT(iccp->asLcms());
406
return iccp->asLcms();
412
} BCHSWADJUSTS, *LPBCHSWADJUSTS;
415
static int desaturateSampler(register WORD In[], register WORD Out[], register LPVOID /*Cargo*/)
417
cmsCIELab LabIn, LabOut;
418
cmsCIELCh LChIn, LChOut;
419
//LPBCHSWADJUSTS bchsw = (LPBCHSWADJUSTS) Cargo;
421
cmsLabEncoded2Float(&LabIn, In);
423
cmsLab2LCh(&LChIn, &LabIn);
425
// Do some adjusts on LCh
427
LChOut.C = 0;//LChIn.C + bchsw->Saturation;
430
cmsLCh2Lab(&LabOut, &LChOut);
433
cmsFloat2LabEncoded(Out, &LabOut);
440
class PIGMENTCMS_EXPORT KoLcmsColorSpaceFactory : public KoColorSpaceFactory, private KoLcmsInfo {
442
KoLcmsColorSpaceFactory(DWORD cmType, icColorSpaceSignature colorSpaceSignature) : KoLcmsInfo(cmType, colorSpaceSignature)
445
virtual bool profileIsCompatible(const KoColorProfile* profile) const
447
const KoIccColorProfile* p = dynamic_cast<const KoIccColorProfile*>(profile);
448
return (p && p->asLcms()->colorSpaceSignature() == colorSpaceSignature());
450
virtual QString colorSpaceEngine() const { return "icc"; }
451
virtual bool isHdr() const { return false; }
452
virtual QList<KoColorConversionTransformationFactory*> colorConversionLinks() const;