2
For general Scribus (>=1.3.2) copyright and licensing information please refer
3
to the COPYING file provided with the program. Following this notice may exist
4
a copyright and/or license notice that predates the release of Scribus 1.3.2
5
for which a new license (GPL+exception) is in place.
14
#include "sclcms2colormgmtengineimpl.h"
15
#include "sclcms2colorprofileimpl.h"
16
#include "sclcms2colortransformimpl.h"
18
#ifndef cmsFLAGS_PRESERVEBLACK
19
#define cmsFLAGS_PRESERVEBLACK 0x8000
22
QSharedPointer<ScColorProfileCache> ScLcms2ColorMgmtEngineImpl::m_profileCache;
23
QSharedPointer<ScColorTransformPool> ScLcms2ColorMgmtEngineImpl::m_transformPool;
25
ScLcms2ColorMgmtEngineImpl::ScLcms2ColorMgmtEngineImpl()
26
: ScColorMgmtEngineData("Littlecms v2", 1)
29
m_profileCache = QSharedPointer<ScColorProfileCache>(new ScColorProfileCache());
31
m_transformPool = QSharedPointer<ScColorTransformPool>(new ScColorTransformPool());
32
cmsUInt16Number alarmCodes[cmsMAXCHANNELS] = { 0 };
33
alarmCodes[1] = 0xFFFF;
34
cmsSetAlarmCodes(alarmCodes);
35
cmsSetLogErrorHandler(&cmsErrorHandler);
38
QList<ScColorProfileInfo> ScLcms2ColorMgmtEngineImpl::getAvailableProfileInfo(const QString& directory, bool recursive)
40
QList<ScColorProfileInfo> profileInfos;
42
QDir d(directory, "*", QDir::Name, QDir::Files | QDir::Readable | QDir::Dirs | QDir::NoSymLinks);
43
if ((!d.exists()) || (d.count() == 0))
47
cmsHPROFILE hIn = NULL;
49
for (uint dc = 0; dc < d.count(); ++dc)
52
if (file == "." || file == "..")
54
QFileInfo fi(directory + "/" + file);
55
if (fi.isDir() && !recursive)
57
else if (fi.isDir() && !file.startsWith('.'))
59
QList<ScColorProfileInfo> profileInfos2 = getAvailableProfileInfo(fi.filePath()+"/", true);
60
profileInfos.append(profileInfos2);
64
ScColorProfileInfo profileInfo;
65
profileInfo.file = fi.filePath();
67
QFile f(fi.filePath());
68
QByteArray bb(40, ' ');
69
if (!f.open(QIODevice::ReadOnly)) {
70
profileInfo.debug = QString("couldn't open %1 as color profile").arg(fi.filePath());
71
profileInfos.append(profileInfo);
74
int len = f.read(bb.data(), 40);
76
if (len == 40 && bb[36] == 'a' && bb[37] == 'c' && bb[38] == 's' && bb[39] == 'p')
78
const QByteArray profilePath( QString(directory + "/" + file).toLocal8Bit() );
79
hIn = cmsOpenProfileFromFile(profilePath.data(), "r");
83
cmsUInt32Number descSize = cmsGetProfileInfo(hIn, cmsInfoDescription, "en", "US", NULL, 0);
86
wchar_t* descData = (wchar_t*) malloc(descSize + sizeof(wchar_t));
87
descSize = cmsGetProfileInfo(hIn, cmsInfoDescription, "en", "US", descData, descSize);
90
uint stringLen = descSize / sizeof(wchar_t);
91
descData[stringLen] = 0;
92
if (sizeof(wchar_t) == sizeof(QChar)) {
93
profileInfo.description = QString::fromUtf16((ushort *) descData);
95
profileInfo.description = QString::fromUcs4((uint *) descData);
101
cmsUInt32Number descSize = cmsGetProfileInfoASCII(hIn, cmsInfoDescription, "en", "US", NULL, 0);
104
char* descData = (char*) malloc(descSize + sizeof(char));
105
descSize = cmsGetProfileInfoASCII(hIn, cmsInfoDescription, "en", "US", descData, descSize);
108
profileInfo.description = QString(descData);
113
if (profileInfo.description.isEmpty())
115
cmsCloseProfile(hIn);
116
profileInfo.debug = QString("Color profile %1 is broken : no valid description").arg(fi.filePath());
117
profileInfos.append(profileInfo);
120
profileInfo.colorSpace = translateLcmsColorSpaceType( cmsGetColorSpace(hIn) );
121
profileInfo.deviceClass = translateLcmsProfileClass( cmsGetDeviceClass(hIn) );
122
profileInfos.append(profileInfo);
123
cmsCloseProfile(hIn);
131
ScColorProfile ScLcms2ColorMgmtEngineImpl::openProfileFromFile(const QString& filePath)
133
// Search profile in profile cache first
134
ScColorProfile profile = m_profileCache->profile(filePath);
135
if (!profile.isNull())
137
cmsHPROFILE lcmsProf = NULL;
138
QFile file(filePath);
139
if (file.open(QFile::ReadOnly))
141
// We do not use lcms cmsOpenProfileFromFile() to avoid limitations
142
// of I/O on 8bit filenames on Windows
143
QByteArray data = file.readAll();
146
lcmsProf = cmsOpenProfileFromMem(data.data(), data.size());
149
ScLcms2ColorProfileImpl* profData = new ScLcms2ColorProfileImpl(lcmsProf);
150
profData->m_profileData = data;
151
profData->m_profilePath = filePath;
152
profile = ScColorProfile(dynamic_cast<ScColorProfileData*>(profData));
153
m_profileCache->addProfile(profile);
155
if (profile.isNull() && lcmsProf)
157
cmsCloseProfile(lcmsProf);
166
ScColorProfile ScLcms2ColorMgmtEngineImpl::openProfileFromMem(const QByteArray& data)
168
ScColorProfile profile;
169
cmsHPROFILE lcmsProf = cmsOpenProfileFromMem((const void *) data.data(), data.size());
172
ScLcms2ColorProfileImpl* profData = new ScLcms2ColorProfileImpl(lcmsProf);
173
QString desc = profData->productDescription();
175
profData->m_profilePath = QString("memprofile://%1").arg(desc);
176
profData->m_profileData = data;
177
profile = ScColorProfile(dynamic_cast<ScColorProfileData*>(profData));
179
if (profile.isNull() && lcmsProf)
181
cmsCloseProfile(lcmsProf);
187
ScColorProfile ScLcms2ColorMgmtEngineImpl::createProfile_sRGB()
189
QString internalProfilePath("memprofile://Internal sRGB profile");
190
ScColorProfile profile = m_profileCache->profile(internalProfilePath);
191
if (!profile.isNull())
194
cmsHPROFILE lcmsProf = cmsCreate_sRGBProfile();
197
ScLcms2ColorProfileImpl* profData = new ScLcms2ColorProfileImpl(lcmsProf);
198
profData->m_profilePath = internalProfilePath;
199
profile = ScColorProfile(dynamic_cast<ScColorProfileData*>(profData));
200
m_profileCache->addProfile(profile);
202
if (profile.isNull() && lcmsProf)
204
cmsCloseProfile(lcmsProf);
210
ScColorProfile ScLcms2ColorMgmtEngineImpl::createProfile_Lab()
212
QString internalProfilePath("memprofile://Internal Lab profile");
213
ScColorProfile profile = m_profileCache->profile(internalProfilePath);
214
if (!profile.isNull())
217
cmsHPROFILE lcmsProf = cmsCreateLab2Profile(NULL);
220
ScLcms2ColorProfileImpl* profData = new ScLcms2ColorProfileImpl(lcmsProf);
221
profData->m_profilePath = internalProfilePath;
222
profile = ScColorProfile(dynamic_cast<ScColorProfileData*>(profData));
223
m_profileCache->addProfile(profile);
225
if (profile.isNull() && lcmsProf)
227
cmsCloseProfile(lcmsProf);
233
ScColorTransform ScLcms2ColorMgmtEngineImpl::createTransform(const ScColorProfile& inputProfile , eColorFormat inputFormat,
234
const ScColorProfile& outputProfile, eColorFormat outputFormat,
235
eRenderIntent renderIntent, long transformFlags)
237
ScColorTransform transform(NULL);
238
if (inputProfile.isNull() || outputProfile.isNull())
240
const ScLcms2ColorProfileImpl* lcmsInputProf = dynamic_cast<const ScLcms2ColorProfileImpl*>(inputProfile.data());
241
const ScLcms2ColorProfileImpl* lcmsOutputProf = dynamic_cast<const ScLcms2ColorProfileImpl*>(outputProfile.data());
242
if (!lcmsInputProf || !lcmsOutputProf)
245
transformFlags &= (~Ctf_Softproofing);
246
transformFlags &= (~Ctf_GamutCheck);
248
ScColorTransformInfo transInfo;
249
transInfo.inputProfile = inputProfile.productDescription();
250
transInfo.outputProfile = outputProfile.productDescription();
251
transInfo.proofingProfile = QString();
252
transInfo.inputFormat = inputFormat;
253
transInfo.outputFormat = outputFormat;
254
transInfo.renderIntent = renderIntent;
255
transInfo.proofingIntent = (eRenderIntent) 0;
256
transInfo.flags = transformFlags;
258
bool nullTransform = false;
259
if (transInfo.inputProfile == transInfo.outputProfile)
261
// This is a null transform
262
transInfo.inputProfile = QString();
263
transInfo.outputProfile = QString();
264
transInfo.proofingProfile = QString();
265
transInfo.renderIntent = (eRenderIntent) 0;
266
transInfo.proofingIntent = (eRenderIntent) 0;
268
nullTransform = true;
271
transform = m_transformPool->findTransform(transInfo);
272
if (transform.isNull())
274
cmsUInt32Number lcmsFlags = translateFlagsToLcmsFlags(transformFlags);
275
cmsUInt32Number lcmsInputFmt = translateFormatToLcmsFormat(inputFormat);
276
cmsUInt32Number lcmsOutputFmt = translateFormatToLcmsFormat(outputFormat);
277
int lcmsIntent = translateIntentToLcmsIntent(renderIntent);
279
lcmsFlags |= cmsFLAGS_NULLTRANSFORM;
280
cmsHTRANSFORM hTransform = NULL;
281
hTransform = cmsCreateTransform(lcmsInputProf->m_profileHandle , lcmsInputFmt,
282
lcmsOutputProf->m_profileHandle, lcmsOutputFmt,
283
lcmsIntent, lcmsFlags | cmsFLAGS_LOWRESPRECALC);
286
ScLcms2ColorTransformImpl* newTrans = new ScLcms2ColorTransformImpl(hTransform);
287
newTrans->setTransformInfo(transInfo);
288
transform = ScColorTransform(dynamic_cast<ScColorTransformData*>(newTrans));
289
m_transformPool->addTransform(transform, true);
295
ScColorTransform ScLcms2ColorMgmtEngineImpl::createProofingTransform(const ScColorProfile& inputProfile , eColorFormat inputFormat,
296
const ScColorProfile& outputProfile, eColorFormat outputFormat,
297
const ScColorProfile& proofProfile , eRenderIntent renderIntent,
298
eRenderIntent proofingIntent, long transformFlags)
300
ScColorTransform transform(NULL);
301
if (inputProfile.isNull() || outputProfile.isNull())
303
const ScLcms2ColorProfileImpl* lcmsInputProf = dynamic_cast<const ScLcms2ColorProfileImpl*>(inputProfile.data());
304
const ScLcms2ColorProfileImpl* lcmsOutputProf = dynamic_cast<const ScLcms2ColorProfileImpl*>(outputProfile.data());
305
const ScLcms2ColorProfileImpl* lcmsProofingProf = dynamic_cast<const ScLcms2ColorProfileImpl*>(proofProfile.data());
306
if (!lcmsInputProf || !lcmsOutputProf || !lcmsProofingProf)
309
ScColorTransformInfo transInfo;
310
transInfo.inputProfile = inputProfile.productDescription();
311
transInfo.outputProfile = outputProfile.productDescription();
312
transInfo.proofingProfile = proofProfile.productDescription();
313
transInfo.inputFormat = inputFormat;
314
transInfo.outputFormat = outputFormat;
315
transInfo.renderIntent = renderIntent;
316
transInfo.proofingIntent = proofingIntent;
317
transInfo.flags = transformFlags;
319
cmsUInt32Number lcmsFlags = translateFlagsToLcmsFlags(transformFlags);
320
cmsUInt32Number lcmsInputFmt = translateFormatToLcmsFormat(inputFormat);
321
cmsUInt32Number lcmsOutputFmt = translateFormatToLcmsFormat(outputFormat);
322
int lcmsIntent = translateIntentToLcmsIntent(renderIntent);
323
int lcmsPrfIntent = translateIntentToLcmsIntent(proofingIntent);
325
if (transInfo.inputProfile != transInfo.proofingProfile)
327
if (transInfo.proofingProfile == transInfo.outputProfile)
329
transInfo.proofingIntent = Intent_Relative_Colorimetric;
330
lcmsPrfIntent = translateIntentToLcmsIntent(Intent_Relative_Colorimetric);
332
transform = m_transformPool->findTransform(transInfo);
333
if (transform.isNull())
335
cmsHTRANSFORM hTransform = NULL;
336
hTransform = cmsCreateProofingTransform(lcmsInputProf->m_profileHandle , lcmsInputFmt,
337
lcmsOutputProf->m_profileHandle, lcmsOutputFmt,
338
lcmsProofingProf->m_profileHandle, lcmsIntent,
339
lcmsPrfIntent, lcmsFlags | cmsFLAGS_SOFTPROOFING);
342
ScLcms2ColorTransformImpl* newTrans = new ScLcms2ColorTransformImpl(hTransform);
343
newTrans->setTransformInfo(transInfo);
344
transform = ScColorTransform(dynamic_cast<ScColorTransformData*>(newTrans));
345
m_transformPool->addTransform(transform, true);
351
transformFlags &= (~Ctf_Softproofing);
352
transformFlags &= (~Ctf_GamutCheck);
353
lcmsFlags = translateFlagsToLcmsFlags(transformFlags);
354
transInfo.flags = transformFlags;
355
transInfo.renderIntent = proofingIntent;
356
transInfo.proofingIntent = (eRenderIntent) 0;
357
if (transInfo.inputProfile == transInfo.outputProfile)
359
lcmsFlags |= cmsFLAGS_NULLTRANSFORM;
360
transInfo.inputProfile = QString();
361
transInfo.outputProfile = QString();
362
transInfo.proofingProfile = QString();
363
transInfo.renderIntent = (eRenderIntent) 0;
364
transInfo.proofingIntent = (eRenderIntent) 0;
367
transform = m_transformPool->findTransform(transInfo);
368
if (transform.isNull())
370
cmsHTRANSFORM hTransform = NULL;
371
hTransform = cmsCreateTransform(lcmsInputProf->m_profileHandle , lcmsInputFmt,
372
lcmsOutputProf->m_profileHandle, lcmsOutputFmt,
373
lcmsPrfIntent, lcmsFlags | cmsFLAGS_LOWRESPRECALC);
376
ScLcms2ColorTransformImpl* newTrans = new ScLcms2ColorTransformImpl(hTransform);
377
newTrans->setTransformInfo(transInfo);
378
transform = ScColorTransform(dynamic_cast<ScColorTransformData*>(newTrans));
379
m_transformPool->addTransform(transform, true);
386
cmsUInt32Number ScLcms2ColorMgmtEngineImpl::translateFlagsToLcmsFlags(long flags)
388
cmsUInt32Number lFlags = 0;
389
if (flags & Ctf_BlackPointCompensation)
390
lFlags |= cmsFLAGS_BLACKPOINTCOMPENSATION;
391
if (flags & Ctf_BlackPreservation)
392
lFlags |= cmsFLAGS_PRESERVEBLACK;
393
if (flags & Ctf_Softproofing)
394
lFlags |= cmsFLAGS_SOFTPROOFING;
395
if (flags & Ctf_GamutCheck)
396
lFlags |= cmsFLAGS_GAMUTCHECK;
400
cmsUInt32Number ScLcms2ColorMgmtEngineImpl::translateFormatToLcmsFormat(eColorFormat format)
402
cmsUInt32Number lFormat = 0;
403
if (format == Format_RGB_8)
404
lFormat = TYPE_RGB_8;
405
if (format == Format_RGB_16)
406
lFormat = TYPE_RGB_16;
407
if (format == Format_RGBA_8)
408
lFormat = TYPE_RGBA_8;
409
if (format == Format_RGBA_16)
410
lFormat = TYPE_RGBA_16;
411
if (format == Format_ARGB_8)
412
lFormat = TYPE_ARGB_8;
413
if (format == Format_ARGB_16)
414
lFormat = TYPE_ARGB_16;
415
if (format == Format_BGRA_8)
416
lFormat = TYPE_BGRA_8;
417
if (format == Format_BGRA_16)
418
lFormat = TYPE_BGRA_16;
419
if (format == Format_CMYK_8)
420
lFormat = TYPE_CMYK_8;
421
if (format == Format_CMYK_16)
422
lFormat = TYPE_CMYK_16;
423
if (format == Format_CMYKA_8)
424
lFormat = (COLORSPACE_SH(PT_CMYK)|EXTRA_SH(1)|CHANNELS_SH(4)|BYTES_SH(1));
425
if (format == Format_CMYKA_16)
426
lFormat = (COLORSPACE_SH(PT_CMYK)|EXTRA_SH(1)|CHANNELS_SH(4)|BYTES_SH(2));
427
if (format == Format_YMCK_8)
428
lFormat = (COLORSPACE_SH(PT_CMYK)|CHANNELS_SH(4)|BYTES_SH(1)|DOSWAP_SH(1)|SWAPFIRST_SH(1));
429
if (format == Format_YMCK_16)
430
lFormat = (COLORSPACE_SH(PT_CMYK)|CHANNELS_SH(4)|BYTES_SH(2)|DOSWAP_SH(1)|SWAPFIRST_SH(1));
431
if (format == Format_GRAY_8)
432
lFormat = TYPE_GRAY_8;
433
if (format == Format_GRAY_16)
434
lFormat = TYPE_GRAY_16;
435
if (format == Format_LabA_8)
436
lFormat = COLORSPACE_SH(PT_Lab)|CHANNELS_SH(3)|BYTES_SH(1)|EXTRA_SH(1);
440
int ScLcms2ColorMgmtEngineImpl::translateIntentToLcmsIntent(eRenderIntent intent, eRenderIntent defIntent)
442
int lIntent = defIntent;
443
if (intent == Intent_Perceptual)
444
lIntent = INTENT_PERCEPTUAL;
445
if (intent == Intent_Relative_Colorimetric)
446
lIntent = INTENT_RELATIVE_COLORIMETRIC;
447
if (intent == Intent_Saturation)
448
lIntent = INTENT_SATURATION;
449
if (intent == Intent_Absolute_Colorimetric)
450
lIntent = INTENT_ABSOLUTE_COLORIMETRIC;
454
eColorSpaceType ScLcms2ColorMgmtEngineImpl::translateLcmsColorSpaceType(cmsColorSpaceSignature signature)
456
eColorSpaceType colorSpaceType = ColorSpace_Unknown;
457
if (signature == cmsSigXYZData)
458
colorSpaceType = ColorSpace_XYZ;
459
if (signature == cmsSigLabData)
460
colorSpaceType = ColorSpace_Lab;
461
if (signature == cmsSigLuvData)
462
colorSpaceType = ColorSpace_Luv;
463
if (signature == cmsSigYCbCrData)
464
colorSpaceType = ColorSpace_YCbCr;
465
if (signature == cmsSigYxyData)
466
colorSpaceType = ColorSpace_Yxy;
467
if (signature == cmsSigRgbData)
468
colorSpaceType = ColorSpace_Rgb;
469
if (signature == cmsSigGrayData)
470
colorSpaceType = ColorSpace_Gray;
471
if (signature == cmsSigHsvData)
472
colorSpaceType = ColorSpace_Hsv;
473
if (signature == cmsSigHlsData)
474
colorSpaceType = ColorSpace_Hls;
475
if (signature == cmsSigCmykData)
476
colorSpaceType = ColorSpace_Cmyk;
477
if (signature == cmsSigCmyData)
478
colorSpaceType = ColorSpace_Cmy;
479
return colorSpaceType;
482
eProfileClass ScLcms2ColorMgmtEngineImpl::translateLcmsProfileClass(cmsProfileClassSignature signature)
484
eProfileClass profileClass = Class_Unknown;
485
if (signature == cmsSigInputClass)
486
profileClass = Class_Input;
487
if (signature == cmsSigDisplayClass)
488
profileClass = Class_Display;
489
if (signature == cmsSigOutputClass)
490
profileClass = Class_Output;
491
if (signature == cmsSigLinkClass)
492
profileClass = Class_Link;
493
if (signature == cmsSigAbstractClass)
494
profileClass = Class_Abstract;
495
if (signature == cmsSigColorSpaceClass)
496
profileClass = Class_ColorSpace;
497
if (signature == cmsSigNamedColorClass)
498
profileClass = Class_NamedColor;
502
void ScLcms2ColorMgmtEngineImpl::cmsErrorHandler(cmsContext contextID, cmsUInt32Number /*ErrorCode*/,
503
const char *ErrorText)
505
std::string msg = std::string("Littlecms : ") + ErrorText;
506
std::cerr << msg.c_str() << std::endl;