Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
wine-mirror
GitHub Repository: wine-mirror/wine
Path: blob/master/libs/lcms2/src/cmsvirt.c
4391 views
1
//---------------------------------------------------------------------------------
2
//
3
// Little Color Management System
4
// Copyright (c) 1998-2024 Marti Maria Saguer
5
//
6
// Permission is hereby granted, free of charge, to any person obtaining
7
// a copy of this software and associated documentation files (the "Software"),
8
// to deal in the Software without restriction, including without limitation
9
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
10
// and/or sell copies of the Software, and to permit persons to whom the Software
11
// is furnished to do so, subject to the following conditions:
12
//
13
// The above copyright notice and this permission notice shall be included in
14
// all copies or substantial portions of the Software.
15
//
16
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
18
// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
//
24
//---------------------------------------------------------------------------------
25
//
26
27
#include "lcms2_internal.h"
28
29
// Virtual (built-in) profiles
30
// -----------------------------------------------------------------------------------
31
32
static
33
cmsBool SetTextTags(cmsHPROFILE hProfile, const wchar_t* Description)
34
{
35
cmsMLU *DescriptionMLU, *CopyrightMLU;
36
cmsBool rc = FALSE;
37
cmsContext ContextID = cmsGetProfileContextID(hProfile);
38
39
DescriptionMLU = cmsMLUalloc(ContextID, 1);
40
CopyrightMLU = cmsMLUalloc(ContextID, 1);
41
42
if (DescriptionMLU == NULL || CopyrightMLU == NULL) goto Error;
43
44
if (!cmsMLUsetWide(DescriptionMLU, "en", "US", Description)) goto Error;
45
if (!cmsMLUsetWide(CopyrightMLU, "en", "US", L"No copyright, use freely")) goto Error;
46
47
if (!cmsWriteTag(hProfile, cmsSigProfileDescriptionTag, DescriptionMLU)) goto Error;
48
if (!cmsWriteTag(hProfile, cmsSigCopyrightTag, CopyrightMLU)) goto Error;
49
50
rc = TRUE;
51
52
Error:
53
54
if (DescriptionMLU)
55
cmsMLUfree(DescriptionMLU);
56
if (CopyrightMLU)
57
cmsMLUfree(CopyrightMLU);
58
return rc;
59
}
60
61
62
static
63
cmsBool SetSeqDescTag(cmsHPROFILE hProfile, const char* Model)
64
{
65
cmsBool rc = FALSE;
66
cmsContext ContextID = cmsGetProfileContextID(hProfile);
67
cmsSEQ* Seq = cmsAllocProfileSequenceDescription(ContextID, 1);
68
69
if (Seq == NULL) return FALSE;
70
71
Seq->seq[0].deviceMfg = (cmsSignature) 0;
72
Seq->seq[0].deviceModel = (cmsSignature) 0;
73
74
#ifdef CMS_DONT_USE_INT64
75
Seq->seq[0].attributes[0] = 0;
76
Seq->seq[0].attributes[1] = 0;
77
#else
78
Seq->seq[0].attributes = 0;
79
#endif
80
81
Seq->seq[0].technology = (cmsTechnologySignature) 0;
82
83
cmsMLUsetASCII( Seq->seq[0].Manufacturer, cmsNoLanguage, cmsNoCountry, "Little CMS");
84
cmsMLUsetASCII( Seq->seq[0].Model, cmsNoLanguage, cmsNoCountry, Model);
85
86
if (!_cmsWriteProfileSequence(hProfile, Seq)) goto Error;
87
88
rc = TRUE;
89
90
Error:
91
if (Seq)
92
cmsFreeProfileSequenceDescription(Seq);
93
94
return rc;
95
}
96
97
98
99
// This function creates a profile based on White point, primaries and
100
// transfer functions.
101
cmsHPROFILE CMSEXPORT cmsCreateRGBProfileTHR(cmsContext ContextID,
102
const cmsCIExyY* WhitePoint,
103
const cmsCIExyYTRIPLE* Primaries,
104
cmsToneCurve* const TransferFunction[3])
105
{
106
cmsHPROFILE hICC;
107
cmsMAT3 MColorants;
108
cmsCIEXYZTRIPLE Colorants;
109
cmsCIExyY MaxWhite;
110
cmsMAT3 CHAD;
111
cmsCIEXYZ WhitePointXYZ;
112
113
hICC = cmsCreateProfilePlaceholder(ContextID);
114
if (!hICC) // can't allocate
115
return NULL;
116
117
cmsSetProfileVersion(hICC, 4.4);
118
119
cmsSetDeviceClass(hICC, cmsSigDisplayClass);
120
cmsSetColorSpace(hICC, cmsSigRgbData);
121
cmsSetPCS(hICC, cmsSigXYZData);
122
123
cmsSetHeaderRenderingIntent(hICC, INTENT_PERCEPTUAL);
124
125
126
// Implement profile using following tags:
127
//
128
// 1 cmsSigProfileDescriptionTag
129
// 2 cmsSigMediaWhitePointTag
130
// 3 cmsSigRedColorantTag
131
// 4 cmsSigGreenColorantTag
132
// 5 cmsSigBlueColorantTag
133
// 6 cmsSigRedTRCTag
134
// 7 cmsSigGreenTRCTag
135
// 8 cmsSigBlueTRCTag
136
// 9 Chromatic adaptation Tag
137
// This conforms a standard RGB DisplayProfile as says ICC, and then I add (As per addendum II)
138
// 10 cmsSigChromaticityTag
139
140
141
if (!SetTextTags(hICC, L"RGB built-in")) goto Error;
142
143
if (WhitePoint) {
144
145
if (!cmsWriteTag(hICC, cmsSigMediaWhitePointTag, cmsD50_XYZ())) goto Error;
146
147
cmsxyY2XYZ(&WhitePointXYZ, WhitePoint);
148
_cmsAdaptationMatrix(&CHAD, NULL, &WhitePointXYZ, cmsD50_XYZ());
149
150
// This is a V4 tag, but many CMM does read and understand it no matter which version
151
if (!cmsWriteTag(hICC, cmsSigChromaticAdaptationTag, (void*) &CHAD)) goto Error;
152
}
153
154
if (WhitePoint && Primaries) {
155
156
MaxWhite.x = WhitePoint -> x;
157
MaxWhite.y = WhitePoint -> y;
158
MaxWhite.Y = 1.0;
159
160
if (!_cmsBuildRGB2XYZtransferMatrix(&MColorants, &MaxWhite, Primaries)) goto Error;
161
162
Colorants.Red.X = MColorants.v[0].n[0];
163
Colorants.Red.Y = MColorants.v[1].n[0];
164
Colorants.Red.Z = MColorants.v[2].n[0];
165
166
Colorants.Green.X = MColorants.v[0].n[1];
167
Colorants.Green.Y = MColorants.v[1].n[1];
168
Colorants.Green.Z = MColorants.v[2].n[1];
169
170
Colorants.Blue.X = MColorants.v[0].n[2];
171
Colorants.Blue.Y = MColorants.v[1].n[2];
172
Colorants.Blue.Z = MColorants.v[2].n[2];
173
174
if (!cmsWriteTag(hICC, cmsSigRedColorantTag, (void*) &Colorants.Red)) goto Error;
175
if (!cmsWriteTag(hICC, cmsSigBlueColorantTag, (void*) &Colorants.Blue)) goto Error;
176
if (!cmsWriteTag(hICC, cmsSigGreenColorantTag, (void*) &Colorants.Green)) goto Error;
177
}
178
179
180
if (TransferFunction) {
181
182
// Tries to minimize space. Thanks to Richard Hughes for this nice idea
183
if (!cmsWriteTag(hICC, cmsSigRedTRCTag, (void*) TransferFunction[0])) goto Error;
184
185
if (TransferFunction[1] == TransferFunction[0]) {
186
187
if (!cmsLinkTag (hICC, cmsSigGreenTRCTag, cmsSigRedTRCTag)) goto Error;
188
189
} else {
190
191
if (!cmsWriteTag(hICC, cmsSigGreenTRCTag, (void*) TransferFunction[1])) goto Error;
192
}
193
194
if (TransferFunction[2] == TransferFunction[0]) {
195
196
if (!cmsLinkTag (hICC, cmsSigBlueTRCTag, cmsSigRedTRCTag)) goto Error;
197
198
} else {
199
200
if (!cmsWriteTag(hICC, cmsSigBlueTRCTag, (void*) TransferFunction[2])) goto Error;
201
}
202
}
203
204
if (Primaries) {
205
if (!cmsWriteTag(hICC, cmsSigChromaticityTag, (void*) Primaries)) goto Error;
206
}
207
208
209
return hICC;
210
211
Error:
212
if (hICC)
213
cmsCloseProfile(hICC);
214
return NULL;
215
}
216
217
cmsHPROFILE CMSEXPORT cmsCreateRGBProfile(const cmsCIExyY* WhitePoint,
218
const cmsCIExyYTRIPLE* Primaries,
219
cmsToneCurve* const TransferFunction[3])
220
{
221
return cmsCreateRGBProfileTHR(NULL, WhitePoint, Primaries, TransferFunction);
222
}
223
224
225
226
// This function creates a profile based on White point and transfer function.
227
cmsHPROFILE CMSEXPORT cmsCreateGrayProfileTHR(cmsContext ContextID,
228
const cmsCIExyY* WhitePoint,
229
const cmsToneCurve* TransferFunction)
230
{
231
cmsHPROFILE hICC;
232
cmsCIEXYZ tmp;
233
234
hICC = cmsCreateProfilePlaceholder(ContextID);
235
if (!hICC) // can't allocate
236
return NULL;
237
238
cmsSetProfileVersion(hICC, 4.4);
239
240
cmsSetDeviceClass(hICC, cmsSigDisplayClass);
241
cmsSetColorSpace(hICC, cmsSigGrayData);
242
cmsSetPCS(hICC, cmsSigXYZData);
243
cmsSetHeaderRenderingIntent(hICC, INTENT_PERCEPTUAL);
244
245
246
// Implement profile using following tags:
247
//
248
// 1 cmsSigProfileDescriptionTag
249
// 2 cmsSigMediaWhitePointTag
250
// 3 cmsSigGrayTRCTag
251
252
// This conforms a standard Gray DisplayProfile
253
254
// Fill-in the tags
255
256
if (!SetTextTags(hICC, L"gray built-in")) goto Error;
257
258
259
if (WhitePoint) {
260
261
cmsxyY2XYZ(&tmp, WhitePoint);
262
if (!cmsWriteTag(hICC, cmsSigMediaWhitePointTag, (void*) &tmp)) goto Error;
263
}
264
265
if (TransferFunction) {
266
267
if (!cmsWriteTag(hICC, cmsSigGrayTRCTag, (void*) TransferFunction)) goto Error;
268
}
269
270
return hICC;
271
272
Error:
273
if (hICC)
274
cmsCloseProfile(hICC);
275
return NULL;
276
}
277
278
279
280
cmsHPROFILE CMSEXPORT cmsCreateGrayProfile(const cmsCIExyY* WhitePoint,
281
const cmsToneCurve* TransferFunction)
282
{
283
return cmsCreateGrayProfileTHR(NULL, WhitePoint, TransferFunction);
284
}
285
286
// This is a devicelink operating in the target colorspace with as many transfer functions as components
287
288
cmsHPROFILE CMSEXPORT cmsCreateLinearizationDeviceLinkTHR(cmsContext ContextID,
289
cmsColorSpaceSignature ColorSpace,
290
cmsToneCurve* const TransferFunctions[])
291
{
292
cmsHPROFILE hICC;
293
cmsPipeline* Pipeline;
294
cmsInt32Number nChannels;
295
296
hICC = cmsCreateProfilePlaceholder(ContextID);
297
if (!hICC)
298
return NULL;
299
300
cmsSetProfileVersion(hICC, 4.4);
301
302
cmsSetDeviceClass(hICC, cmsSigLinkClass);
303
cmsSetColorSpace(hICC, ColorSpace);
304
cmsSetPCS(hICC, ColorSpace);
305
306
cmsSetHeaderRenderingIntent(hICC, INTENT_PERCEPTUAL);
307
308
// Set up channels
309
nChannels = cmsChannelsOfColorSpace(ColorSpace);
310
311
// Creates a Pipeline with prelinearization step only
312
Pipeline = cmsPipelineAlloc(ContextID, nChannels, nChannels);
313
if (Pipeline == NULL) goto Error;
314
315
316
// Copy tables to Pipeline
317
if (!cmsPipelineInsertStage(Pipeline, cmsAT_BEGIN, cmsStageAllocToneCurves(ContextID, nChannels, TransferFunctions)))
318
goto Error;
319
320
// Create tags
321
if (!SetTextTags(hICC, L"Linearization built-in")) goto Error;
322
if (!cmsWriteTag(hICC, cmsSigAToB0Tag, (void*) Pipeline)) goto Error;
323
if (!SetSeqDescTag(hICC, "Linearization built-in")) goto Error;
324
325
// Pipeline is already on virtual profile
326
cmsPipelineFree(Pipeline);
327
328
// Ok, done
329
return hICC;
330
331
Error:
332
cmsPipelineFree(Pipeline);
333
if (hICC)
334
cmsCloseProfile(hICC);
335
336
337
return NULL;
338
}
339
340
cmsHPROFILE CMSEXPORT cmsCreateLinearizationDeviceLink(cmsColorSpaceSignature ColorSpace,
341
cmsToneCurve* const TransferFunctions[])
342
{
343
return cmsCreateLinearizationDeviceLinkTHR(NULL, ColorSpace, TransferFunctions);
344
}
345
346
// Ink-limiting algorithm
347
//
348
// Sum = C + M + Y + K
349
// If Sum > InkLimit
350
// Ratio= 1 - (Sum - InkLimit) / (C + M + Y)
351
// if Ratio <0
352
// Ratio=0
353
// endif
354
// Else
355
// Ratio=1
356
// endif
357
//
358
// C = Ratio * C
359
// M = Ratio * M
360
// Y = Ratio * Y
361
// K: Does not change
362
363
static
364
int InkLimitingSampler(CMSREGISTER const cmsUInt16Number In[], CMSREGISTER cmsUInt16Number Out[], CMSREGISTER void* Cargo)
365
{
366
cmsFloat64Number InkLimit = *(cmsFloat64Number *) Cargo;
367
cmsFloat64Number SumCMY, SumCMYK, Ratio;
368
369
InkLimit = (InkLimit * 655.35);
370
371
SumCMY = (cmsFloat64Number) In[0] + In[1] + In[2];
372
SumCMYK = SumCMY + In[3];
373
374
if (SumCMYK > InkLimit) {
375
376
Ratio = 1 - ((SumCMYK - InkLimit) / SumCMY);
377
if (Ratio < 0)
378
Ratio = 0;
379
}
380
else Ratio = 1;
381
382
Out[0] = _cmsQuickSaturateWord(In[0] * Ratio); // C
383
Out[1] = _cmsQuickSaturateWord(In[1] * Ratio); // M
384
Out[2] = _cmsQuickSaturateWord(In[2] * Ratio); // Y
385
386
Out[3] = In[3]; // K (untouched)
387
388
return TRUE;
389
}
390
391
// This is a devicelink operating in CMYK for ink-limiting
392
393
cmsHPROFILE CMSEXPORT cmsCreateInkLimitingDeviceLinkTHR(cmsContext ContextID,
394
cmsColorSpaceSignature ColorSpace,
395
cmsFloat64Number Limit)
396
{
397
cmsHPROFILE hICC;
398
cmsPipeline* LUT;
399
cmsStage* CLUT;
400
cmsInt32Number nChannels;
401
402
if (ColorSpace != cmsSigCmykData) {
403
cmsSignalError(ContextID, cmsERROR_COLORSPACE_CHECK, "InkLimiting: Only CMYK currently supported");
404
return NULL;
405
}
406
407
if (Limit < 1.0 || Limit > 400) {
408
409
cmsSignalError(ContextID, cmsERROR_RANGE, "InkLimiting: Limit should be between 1..400");
410
if (Limit < 1) Limit = 1;
411
if (Limit > 400) Limit = 400;
412
}
413
414
hICC = cmsCreateProfilePlaceholder(ContextID);
415
if (!hICC) // can't allocate
416
return NULL;
417
418
cmsSetProfileVersion(hICC, 4.4);
419
420
cmsSetDeviceClass(hICC, cmsSigLinkClass);
421
cmsSetColorSpace(hICC, ColorSpace);
422
cmsSetPCS(hICC, ColorSpace);
423
424
cmsSetHeaderRenderingIntent(hICC, INTENT_PERCEPTUAL);
425
426
427
// Creates a Pipeline with 3D grid only
428
LUT = cmsPipelineAlloc(ContextID, 4, 4);
429
if (LUT == NULL) goto Error;
430
431
432
nChannels = cmsChannelsOf(ColorSpace);
433
434
CLUT = cmsStageAllocCLut16bit(ContextID, 17, nChannels, nChannels, NULL);
435
if (CLUT == NULL) goto Error;
436
437
if (!cmsStageSampleCLut16bit(CLUT, InkLimitingSampler, (void*) &Limit, 0)) goto Error;
438
439
if (!cmsPipelineInsertStage(LUT, cmsAT_BEGIN, _cmsStageAllocIdentityCurves(ContextID, nChannels)) ||
440
!cmsPipelineInsertStage(LUT, cmsAT_END, CLUT) ||
441
!cmsPipelineInsertStage(LUT, cmsAT_END, _cmsStageAllocIdentityCurves(ContextID, nChannels)))
442
goto Error;
443
444
// Create tags
445
if (!SetTextTags(hICC, L"ink-limiting built-in")) goto Error;
446
447
if (!cmsWriteTag(hICC, cmsSigAToB0Tag, (void*) LUT)) goto Error;
448
if (!SetSeqDescTag(hICC, "ink-limiting built-in")) goto Error;
449
450
// cmsPipeline is already on virtual profile
451
cmsPipelineFree(LUT);
452
453
// Ok, done
454
return hICC;
455
456
Error:
457
if (LUT != NULL)
458
cmsPipelineFree(LUT);
459
460
if (hICC != NULL)
461
cmsCloseProfile(hICC);
462
463
return NULL;
464
}
465
466
cmsHPROFILE CMSEXPORT cmsCreateInkLimitingDeviceLink(cmsColorSpaceSignature ColorSpace, cmsFloat64Number Limit)
467
{
468
return cmsCreateInkLimitingDeviceLinkTHR(NULL, ColorSpace, Limit);
469
}
470
471
472
// Creates a fake Lab identity.
473
cmsHPROFILE CMSEXPORT cmsCreateLab2ProfileTHR(cmsContext ContextID, const cmsCIExyY* WhitePoint)
474
{
475
cmsHPROFILE hProfile;
476
cmsPipeline* LUT = NULL;
477
478
hProfile = cmsCreateRGBProfileTHR(ContextID, WhitePoint == NULL ? cmsD50_xyY() : WhitePoint, NULL, NULL);
479
if (hProfile == NULL) return NULL;
480
481
cmsSetProfileVersion(hProfile, 2.1);
482
483
cmsSetDeviceClass(hProfile, cmsSigAbstractClass);
484
cmsSetColorSpace(hProfile, cmsSigLabData);
485
cmsSetPCS(hProfile, cmsSigLabData);
486
487
if (!SetTextTags(hProfile, L"Lab identity built-in")) return NULL;
488
489
// An identity LUT is all we need
490
LUT = cmsPipelineAlloc(ContextID, 3, 3);
491
if (LUT == NULL) goto Error;
492
493
if (!cmsPipelineInsertStage(LUT, cmsAT_BEGIN, _cmsStageAllocIdentityCLut(ContextID, 3)))
494
goto Error;
495
496
if (!cmsWriteTag(hProfile, cmsSigAToB0Tag, LUT)) goto Error;
497
cmsPipelineFree(LUT);
498
499
return hProfile;
500
501
Error:
502
503
if (LUT != NULL)
504
cmsPipelineFree(LUT);
505
506
if (hProfile != NULL)
507
cmsCloseProfile(hProfile);
508
509
return NULL;
510
}
511
512
513
cmsHPROFILE CMSEXPORT cmsCreateLab2Profile(const cmsCIExyY* WhitePoint)
514
{
515
return cmsCreateLab2ProfileTHR(NULL, WhitePoint);
516
}
517
518
519
// Creates a fake Lab V4 identity.
520
cmsHPROFILE CMSEXPORT cmsCreateLab4ProfileTHR(cmsContext ContextID, const cmsCIExyY* WhitePoint)
521
{
522
cmsHPROFILE hProfile;
523
cmsPipeline* LUT = NULL;
524
525
hProfile = cmsCreateRGBProfileTHR(ContextID, WhitePoint == NULL ? cmsD50_xyY() : WhitePoint, NULL, NULL);
526
if (hProfile == NULL) return NULL;
527
528
cmsSetProfileVersion(hProfile, 4.4);
529
530
cmsSetDeviceClass(hProfile, cmsSigAbstractClass);
531
cmsSetColorSpace(hProfile, cmsSigLabData);
532
cmsSetPCS(hProfile, cmsSigLabData);
533
534
if (!SetTextTags(hProfile, L"Lab identity built-in")) goto Error;
535
536
// An empty LUTs is all we need
537
LUT = cmsPipelineAlloc(ContextID, 3, 3);
538
if (LUT == NULL) goto Error;
539
540
if (!cmsPipelineInsertStage(LUT, cmsAT_BEGIN, _cmsStageAllocIdentityCurves(ContextID, 3)))
541
goto Error;
542
543
if (!cmsWriteTag(hProfile, cmsSigAToB0Tag, LUT)) goto Error;
544
cmsPipelineFree(LUT);
545
546
return hProfile;
547
548
Error:
549
550
if (LUT != NULL)
551
cmsPipelineFree(LUT);
552
553
if (hProfile != NULL)
554
cmsCloseProfile(hProfile);
555
556
return NULL;
557
}
558
559
cmsHPROFILE CMSEXPORT cmsCreateLab4Profile(const cmsCIExyY* WhitePoint)
560
{
561
return cmsCreateLab4ProfileTHR(NULL, WhitePoint);
562
}
563
564
565
// Creates a fake XYZ identity
566
cmsHPROFILE CMSEXPORT cmsCreateXYZProfileTHR(cmsContext ContextID)
567
{
568
cmsHPROFILE hProfile;
569
cmsPipeline* LUT = NULL;
570
571
hProfile = cmsCreateRGBProfileTHR(ContextID, cmsD50_xyY(), NULL, NULL);
572
if (hProfile == NULL) return NULL;
573
574
cmsSetProfileVersion(hProfile, 4.4);
575
576
cmsSetDeviceClass(hProfile, cmsSigAbstractClass);
577
cmsSetColorSpace(hProfile, cmsSigXYZData);
578
cmsSetPCS(hProfile, cmsSigXYZData);
579
580
if (!SetTextTags(hProfile, L"XYZ identity built-in")) goto Error;
581
582
// An identity LUT is all we need
583
LUT = cmsPipelineAlloc(ContextID, 3, 3);
584
if (LUT == NULL) goto Error;
585
586
if (!cmsPipelineInsertStage(LUT, cmsAT_BEGIN, _cmsStageAllocIdentityCurves(ContextID, 3)))
587
goto Error;
588
589
if (!cmsWriteTag(hProfile, cmsSigAToB0Tag, LUT)) goto Error;
590
cmsPipelineFree(LUT);
591
592
return hProfile;
593
594
Error:
595
596
if (LUT != NULL)
597
cmsPipelineFree(LUT);
598
599
if (hProfile != NULL)
600
cmsCloseProfile(hProfile);
601
602
return NULL;
603
}
604
605
606
cmsHPROFILE CMSEXPORT cmsCreateXYZProfile(void)
607
{
608
return cmsCreateXYZProfileTHR(NULL);
609
}
610
611
612
//sRGB Curves are defined by:
613
//
614
//If R'sRGB,G'sRGB, B'sRGB < 0.04045
615
//
616
// R = R'sRGB / 12.92
617
// G = G'sRGB / 12.92
618
// B = B'sRGB / 12.92
619
//
620
//
621
//else if R'sRGB,G'sRGB, B'sRGB >= 0.04045
622
//
623
// R = ((R'sRGB + 0.055) / 1.055)^2.4
624
// G = ((G'sRGB + 0.055) / 1.055)^2.4
625
// B = ((B'sRGB + 0.055) / 1.055)^2.4
626
627
static
628
cmsToneCurve* Build_sRGBGamma(cmsContext ContextID)
629
{
630
cmsFloat64Number Parameters[5];
631
632
Parameters[0] = 2.4;
633
Parameters[1] = 1. / 1.055;
634
Parameters[2] = 0.055 / 1.055;
635
Parameters[3] = 1. / 12.92;
636
Parameters[4] = 0.04045;
637
638
return cmsBuildParametricToneCurve(ContextID, 4, Parameters);
639
}
640
641
// Create the ICC virtual profile for sRGB space
642
cmsHPROFILE CMSEXPORT cmsCreate_sRGBProfileTHR(cmsContext ContextID)
643
{
644
cmsCIExyY D65 = { 0.3127, 0.3290, 1.0 };
645
cmsCIExyYTRIPLE Rec709Primaries = {
646
{0.6400, 0.3300, 1.0},
647
{0.3000, 0.6000, 1.0},
648
{0.1500, 0.0600, 1.0}
649
};
650
cmsToneCurve* Gamma22[3];
651
cmsHPROFILE hsRGB;
652
653
// cmsWhitePointFromTemp(&D65, 6504);
654
Gamma22[0] = Gamma22[1] = Gamma22[2] = Build_sRGBGamma(ContextID);
655
if (Gamma22[0] == NULL) return NULL;
656
657
hsRGB = cmsCreateRGBProfileTHR(ContextID, &D65, &Rec709Primaries, Gamma22);
658
cmsFreeToneCurve(Gamma22[0]);
659
if (hsRGB == NULL) return NULL;
660
661
if (!SetTextTags(hsRGB, L"sRGB built-in")) {
662
cmsCloseProfile(hsRGB);
663
return NULL;
664
}
665
666
return hsRGB;
667
}
668
669
cmsHPROFILE CMSEXPORT cmsCreate_sRGBProfile(void)
670
{
671
return cmsCreate_sRGBProfileTHR(NULL);
672
}
673
674
/**
675
* Oklab colorspace profile (experimental)
676
*
677
* This virtual profile cannot be saved as an ICC file
678
*/
679
cmsHPROFILE CMSEXPORT cmsCreate_OkLabProfile(cmsContext ctx)
680
{
681
cmsStage* XYZPCS = _cmsStageNormalizeFromXyzFloat(ctx);
682
cmsStage* PCSXYZ = _cmsStageNormalizeToXyzFloat(ctx);
683
684
const double M_D65_D50[] =
685
{
686
1.047886, 0.022919, -0.050216,
687
0.029582, 0.990484, -0.017079,
688
-0.009252, 0.015073, 0.751678
689
};
690
691
const double M_D50_D65[] =
692
{
693
0.955512609517083, -0.023073214184645, 0.063308961782107,
694
-0.028324949364887, 1.009942432477107, 0.021054814890112,
695
0.012328875695483, -0.020535835374141, 1.330713916450354
696
};
697
698
cmsStage* D65toD50 = cmsStageAllocMatrix(ctx, 3, 3, M_D65_D50, NULL);
699
cmsStage* D50toD65 = cmsStageAllocMatrix(ctx, 3, 3, M_D50_D65, NULL);
700
701
const double M_D65_LMS[] =
702
{
703
0.8189330101, 0.3618667424, -0.1288597137,
704
0.0329845436, 0.9293118715, 0.0361456387,
705
0.0482003018, 0.2643662691, 0.6338517070
706
};
707
708
const double M_LMS_D65[] =
709
{
710
1.227013851103521, -0.557799980651822, 0.281256148966468,
711
-0.040580178423281, 1.112256869616830, -0.071676678665601,
712
-0.076381284505707, -0.421481978418013, 1.586163220440795
713
};
714
715
cmsStage* D65toLMS = cmsStageAllocMatrix(ctx, 3, 3, M_D65_LMS, NULL);
716
cmsStage* LMStoD65 = cmsStageAllocMatrix(ctx, 3, 3, M_LMS_D65, NULL);
717
718
cmsToneCurve* CubeRoot = cmsBuildGamma(ctx, 1.0 / 3.0);
719
cmsToneCurve* Cube = cmsBuildGamma(ctx, 3.0);
720
721
cmsToneCurve* Roots[3] = { CubeRoot, CubeRoot, CubeRoot };
722
cmsToneCurve* Cubes[3] = { Cube, Cube, Cube };
723
724
cmsStage* NonLinearityFw = cmsStageAllocToneCurves(ctx, 3, Roots);
725
cmsStage* NonLinearityRv = cmsStageAllocToneCurves(ctx, 3, Cubes);
726
727
const double M_LMSprime_OkLab[] =
728
{
729
0.2104542553, 0.7936177850, -0.0040720468,
730
1.9779984951, -2.4285922050, 0.4505937099,
731
0.0259040371, 0.7827717662, -0.8086757660
732
};
733
734
const double M_OkLab_LMSprime[] =
735
{
736
0.999999998450520, 0.396337792173768, 0.215803758060759,
737
1.000000008881761, -0.105561342323656, -0.063854174771706,
738
1.000000054672411, -0.089484182094966, -1.291485537864092
739
};
740
741
cmsStage* LMSprime_OkLab = cmsStageAllocMatrix(ctx, 3, 3, M_LMSprime_OkLab, NULL);
742
cmsStage* OkLab_LMSprime = cmsStageAllocMatrix(ctx, 3, 3, M_OkLab_LMSprime, NULL);
743
744
cmsPipeline* AToB = cmsPipelineAlloc(ctx, 3, 3);
745
cmsPipeline* BToA = cmsPipelineAlloc(ctx, 3, 3);
746
747
cmsHPROFILE hProfile = cmsCreateProfilePlaceholder(ctx);
748
if (!hProfile) // can't allocate
749
goto error;
750
751
cmsSetProfileVersion(hProfile, 4.4);
752
753
cmsSetDeviceClass(hProfile, cmsSigColorSpaceClass);
754
cmsSetColorSpace(hProfile, cmsSig3colorData);
755
cmsSetPCS(hProfile, cmsSigXYZData);
756
757
cmsSetHeaderRenderingIntent(hProfile, INTENT_RELATIVE_COLORIMETRIC);
758
759
/**
760
* Conversion PCS (XYZ/D50) to OkLab
761
*/
762
if (!cmsPipelineInsertStage(BToA, cmsAT_END, PCSXYZ)) goto error;
763
if (!cmsPipelineInsertStage(BToA, cmsAT_END, D50toD65)) goto error;
764
if (!cmsPipelineInsertStage(BToA, cmsAT_END, D65toLMS)) goto error;
765
if (!cmsPipelineInsertStage(BToA, cmsAT_END, NonLinearityFw)) goto error;
766
if (!cmsPipelineInsertStage(BToA, cmsAT_END, LMSprime_OkLab)) goto error;
767
768
if (!cmsWriteTag(hProfile, cmsSigBToA0Tag, BToA)) goto error;
769
770
if (!cmsPipelineInsertStage(AToB, cmsAT_END, OkLab_LMSprime)) goto error;
771
if (!cmsPipelineInsertStage(AToB, cmsAT_END, NonLinearityRv)) goto error;
772
if (!cmsPipelineInsertStage(AToB, cmsAT_END, LMStoD65)) goto error;
773
if (!cmsPipelineInsertStage(AToB, cmsAT_END, D65toD50)) goto error;
774
if (!cmsPipelineInsertStage(AToB, cmsAT_END, XYZPCS)) goto error;
775
776
if (!cmsWriteTag(hProfile, cmsSigAToB0Tag, AToB)) goto error;
777
778
cmsPipelineFree(BToA);
779
cmsPipelineFree(AToB);
780
781
cmsFreeToneCurve(CubeRoot);
782
cmsFreeToneCurve(Cube);
783
784
return hProfile;
785
786
error:
787
cmsPipelineFree(BToA);
788
cmsPipelineFree(AToB);
789
790
cmsFreeToneCurve(CubeRoot);
791
cmsFreeToneCurve(Cube);
792
cmsCloseProfile(hProfile);
793
794
return NULL;
795
796
}
797
798
799
typedef struct {
800
cmsFloat64Number Brightness;
801
cmsFloat64Number Contrast;
802
cmsFloat64Number Hue;
803
cmsFloat64Number Saturation;
804
cmsBool lAdjustWP;
805
cmsCIEXYZ WPsrc, WPdest;
806
807
} BCHSWADJUSTS, *LPBCHSWADJUSTS;
808
809
810
static
811
int bchswSampler(CMSREGISTER const cmsUInt16Number In[], CMSREGISTER cmsUInt16Number Out[], CMSREGISTER void* Cargo)
812
{
813
cmsCIELab LabIn, LabOut;
814
cmsCIELCh LChIn, LChOut;
815
cmsCIEXYZ XYZ;
816
LPBCHSWADJUSTS bchsw = (LPBCHSWADJUSTS) Cargo;
817
818
819
cmsLabEncoded2Float(&LabIn, In);
820
821
822
cmsLab2LCh(&LChIn, &LabIn);
823
824
// Do some adjusts on LCh
825
826
LChOut.L = LChIn.L * bchsw ->Contrast + bchsw ->Brightness;
827
LChOut.C = LChIn.C + bchsw -> Saturation;
828
LChOut.h = LChIn.h + bchsw -> Hue;
829
830
831
cmsLCh2Lab(&LabOut, &LChOut);
832
833
// Move white point in Lab
834
if (bchsw->lAdjustWP) {
835
cmsLab2XYZ(&bchsw->WPsrc, &XYZ, &LabOut);
836
cmsXYZ2Lab(&bchsw->WPdest, &LabOut, &XYZ);
837
}
838
839
// Back to encoded
840
841
cmsFloat2LabEncoded(Out, &LabOut);
842
843
return TRUE;
844
}
845
846
847
// Creates an abstract profile operating in Lab space for Brightness,
848
// contrast, Saturation and white point displacement
849
850
cmsHPROFILE CMSEXPORT cmsCreateBCHSWabstractProfileTHR(cmsContext ContextID,
851
cmsUInt32Number nLUTPoints,
852
cmsFloat64Number Bright,
853
cmsFloat64Number Contrast,
854
cmsFloat64Number Hue,
855
cmsFloat64Number Saturation,
856
cmsUInt32Number TempSrc,
857
cmsUInt32Number TempDest)
858
{
859
cmsHPROFILE hICC;
860
cmsPipeline* Pipeline;
861
BCHSWADJUSTS bchsw;
862
cmsCIExyY WhitePnt;
863
cmsStage* CLUT;
864
cmsUInt32Number Dimensions[MAX_INPUT_DIMENSIONS];
865
cmsUInt32Number i;
866
867
bchsw.Brightness = Bright;
868
bchsw.Contrast = Contrast;
869
bchsw.Hue = Hue;
870
bchsw.Saturation = Saturation;
871
if (TempSrc == TempDest) {
872
873
bchsw.lAdjustWP = FALSE;
874
}
875
else {
876
bchsw.lAdjustWP = TRUE;
877
cmsWhitePointFromTemp(&WhitePnt, TempSrc);
878
cmsxyY2XYZ(&bchsw.WPsrc, &WhitePnt);
879
cmsWhitePointFromTemp(&WhitePnt, TempDest);
880
cmsxyY2XYZ(&bchsw.WPdest, &WhitePnt);
881
882
}
883
884
hICC = cmsCreateProfilePlaceholder(ContextID);
885
if (!hICC) // can't allocate
886
return NULL;
887
888
cmsSetDeviceClass(hICC, cmsSigAbstractClass);
889
cmsSetColorSpace(hICC, cmsSigLabData);
890
cmsSetPCS(hICC, cmsSigLabData);
891
892
cmsSetHeaderRenderingIntent(hICC, INTENT_PERCEPTUAL);
893
894
// Creates a Pipeline with 3D grid only
895
Pipeline = cmsPipelineAlloc(ContextID, 3, 3);
896
if (Pipeline == NULL) {
897
cmsCloseProfile(hICC);
898
return NULL;
899
}
900
901
for (i=0; i < MAX_INPUT_DIMENSIONS; i++) Dimensions[i] = nLUTPoints;
902
CLUT = cmsStageAllocCLut16bitGranular(ContextID, Dimensions, 3, 3, NULL);
903
if (CLUT == NULL) goto Error;
904
905
906
if (!cmsStageSampleCLut16bit(CLUT, bchswSampler, (void*) &bchsw, 0)) {
907
908
// Shouldn't reach here
909
goto Error;
910
}
911
912
if (!cmsPipelineInsertStage(Pipeline, cmsAT_END, CLUT)) {
913
goto Error;
914
}
915
916
// Create tags
917
if (!SetTextTags(hICC, L"BCHS built-in")) return NULL;
918
919
cmsWriteTag(hICC, cmsSigMediaWhitePointTag, (void*) cmsD50_XYZ());
920
921
cmsWriteTag(hICC, cmsSigAToB0Tag, (void*) Pipeline);
922
923
// Pipeline is already on virtual profile
924
cmsPipelineFree(Pipeline);
925
926
// Ok, done
927
return hICC;
928
929
Error:
930
cmsPipelineFree(Pipeline);
931
cmsCloseProfile(hICC);
932
return NULL;
933
}
934
935
936
CMSAPI cmsHPROFILE CMSEXPORT cmsCreateBCHSWabstractProfile(cmsUInt32Number nLUTPoints,
937
cmsFloat64Number Bright,
938
cmsFloat64Number Contrast,
939
cmsFloat64Number Hue,
940
cmsFloat64Number Saturation,
941
cmsUInt32Number TempSrc,
942
cmsUInt32Number TempDest)
943
{
944
return cmsCreateBCHSWabstractProfileTHR(NULL, nLUTPoints, Bright, Contrast, Hue, Saturation, TempSrc, TempDest);
945
}
946
947
948
// Creates a fake NULL profile. This profile return 1 channel as always 0.
949
// Is useful only for gamut checking tricks
950
cmsHPROFILE CMSEXPORT cmsCreateNULLProfileTHR(cmsContext ContextID)
951
{
952
cmsHPROFILE hProfile;
953
cmsPipeline* LUT = NULL;
954
cmsStage* PostLin;
955
cmsStage* OutLin;
956
cmsToneCurve* EmptyTab[3];
957
cmsUInt16Number Zero[2] = { 0, 0 };
958
const cmsFloat64Number PickLstarMatrix[] = { 1, 0, 0 };
959
960
hProfile = cmsCreateProfilePlaceholder(ContextID);
961
if (!hProfile) // can't allocate
962
return NULL;
963
964
cmsSetProfileVersion(hProfile, 4.4);
965
966
if (!SetTextTags(hProfile, L"NULL profile built-in")) goto Error;
967
968
969
cmsSetDeviceClass(hProfile, cmsSigOutputClass);
970
cmsSetColorSpace(hProfile, cmsSigGrayData);
971
cmsSetPCS(hProfile, cmsSigLabData);
972
973
// Create a valid ICC 4 structure
974
LUT = cmsPipelineAlloc(ContextID, 3, 1);
975
if (LUT == NULL) goto Error;
976
977
EmptyTab[0] = EmptyTab[1] = EmptyTab[2] = cmsBuildTabulatedToneCurve16(ContextID, 2, Zero);
978
PostLin = cmsStageAllocToneCurves(ContextID, 3, EmptyTab);
979
OutLin = cmsStageAllocToneCurves(ContextID, 1, EmptyTab);
980
cmsFreeToneCurve(EmptyTab[0]);
981
982
if (!cmsPipelineInsertStage(LUT, cmsAT_END, PostLin))
983
goto Error;
984
985
if (!cmsPipelineInsertStage(LUT, cmsAT_END, cmsStageAllocMatrix(ContextID, 1, 3, PickLstarMatrix, NULL)))
986
goto Error;
987
988
if (!cmsPipelineInsertStage(LUT, cmsAT_END, OutLin))
989
goto Error;
990
991
if (!cmsWriteTag(hProfile, cmsSigBToA0Tag, (void*) LUT)) goto Error;
992
if (!cmsWriteTag(hProfile, cmsSigMediaWhitePointTag, cmsD50_XYZ())) goto Error;
993
994
cmsPipelineFree(LUT);
995
return hProfile;
996
997
Error:
998
999
if (LUT != NULL)
1000
cmsPipelineFree(LUT);
1001
1002
if (hProfile != NULL)
1003
cmsCloseProfile(hProfile);
1004
1005
return NULL;
1006
}
1007
1008
cmsHPROFILE CMSEXPORT cmsCreateNULLProfile(void)
1009
{
1010
return cmsCreateNULLProfileTHR(NULL);
1011
}
1012
1013
1014
static
1015
int IsPCS(cmsColorSpaceSignature ColorSpace)
1016
{
1017
return (ColorSpace == cmsSigXYZData ||
1018
ColorSpace == cmsSigLabData);
1019
}
1020
1021
1022
static
1023
void FixColorSpaces(cmsHPROFILE hProfile,
1024
cmsColorSpaceSignature ColorSpace,
1025
cmsColorSpaceSignature PCS,
1026
cmsUInt32Number dwFlags)
1027
{
1028
if (dwFlags & cmsFLAGS_GUESSDEVICECLASS) {
1029
1030
if (IsPCS(ColorSpace) && IsPCS(PCS)) {
1031
1032
cmsSetDeviceClass(hProfile, cmsSigAbstractClass);
1033
cmsSetColorSpace(hProfile, ColorSpace);
1034
cmsSetPCS(hProfile, PCS);
1035
return;
1036
}
1037
1038
if (IsPCS(ColorSpace) && !IsPCS(PCS)) {
1039
1040
cmsSetDeviceClass(hProfile, cmsSigOutputClass);
1041
cmsSetPCS(hProfile, ColorSpace);
1042
cmsSetColorSpace(hProfile, PCS);
1043
return;
1044
}
1045
1046
if (IsPCS(PCS) && !IsPCS(ColorSpace)) {
1047
1048
cmsSetDeviceClass(hProfile, cmsSigInputClass);
1049
cmsSetColorSpace(hProfile, ColorSpace);
1050
cmsSetPCS(hProfile, PCS);
1051
return;
1052
}
1053
}
1054
1055
cmsSetDeviceClass(hProfile, cmsSigLinkClass);
1056
cmsSetColorSpace(hProfile, ColorSpace);
1057
cmsSetPCS(hProfile, PCS);
1058
}
1059
1060
1061
1062
// This function creates a named color profile dumping all the contents of transform to a single profile
1063
// In this way, LittleCMS may be used to "group" several named color databases into a single profile.
1064
// It has, however, several minor limitations. PCS is always Lab, which is not very critic since this
1065
// is the normal PCS for named color profiles.
1066
static
1067
cmsHPROFILE CreateNamedColorDevicelink(cmsHTRANSFORM xform)
1068
{
1069
_cmsTRANSFORM* v = (_cmsTRANSFORM*) xform;
1070
cmsHPROFILE hICC = NULL;
1071
cmsUInt32Number i, nColors;
1072
cmsNAMEDCOLORLIST *nc2 = NULL, *Original = NULL;
1073
1074
// Create an empty placeholder
1075
hICC = cmsCreateProfilePlaceholder(v->ContextID);
1076
if (hICC == NULL) return NULL;
1077
1078
// Critical information
1079
cmsSetDeviceClass(hICC, cmsSigNamedColorClass);
1080
cmsSetColorSpace(hICC, v ->ExitColorSpace);
1081
cmsSetPCS(hICC, cmsSigLabData);
1082
1083
// Tag profile with information
1084
if (!SetTextTags(hICC, L"Named color devicelink")) goto Error;
1085
1086
Original = cmsGetNamedColorList(xform);
1087
if (Original == NULL) goto Error;
1088
1089
nColors = cmsNamedColorCount(Original);
1090
nc2 = cmsDupNamedColorList(Original);
1091
if (nc2 == NULL) goto Error;
1092
1093
// Colorant count now depends on the output space
1094
nc2 ->ColorantCount = cmsPipelineOutputChannels(v ->Lut);
1095
1096
// Make sure we have proper formatters
1097
cmsChangeBuffersFormat(xform, TYPE_NAMED_COLOR_INDEX,
1098
FLOAT_SH(0) | COLORSPACE_SH(_cmsLCMScolorSpace(v ->ExitColorSpace))
1099
| BYTES_SH(2) | CHANNELS_SH(cmsChannelsOfColorSpace(v ->ExitColorSpace)));
1100
1101
// Apply the transfor to colorants.
1102
for (i=0; i < nColors; i++) {
1103
cmsDoTransform(xform, &i, nc2 ->List[i].DeviceColorant, 1);
1104
}
1105
1106
if (!cmsWriteTag(hICC, cmsSigNamedColor2Tag, (void*) nc2)) goto Error;
1107
cmsFreeNamedColorList(nc2);
1108
1109
return hICC;
1110
1111
Error:
1112
if (hICC != NULL) cmsCloseProfile(hICC);
1113
return NULL;
1114
}
1115
1116
1117
// This structure holds information about which MPU can be stored on a profile based on the version
1118
1119
typedef struct {
1120
cmsBool IsV4; // Is a V4 tag?
1121
cmsTagSignature RequiredTag; // Set to 0 for both types
1122
cmsTagTypeSignature LutType; // The LUT type
1123
int nTypes; // Number of types (up to 5)
1124
cmsStageSignature MpeTypes[5]; // 5 is the maximum number
1125
1126
} cmsAllowedLUT;
1127
1128
#define cmsSig0 ((cmsTagSignature) 0)
1129
1130
static const cmsAllowedLUT AllowedLUTTypes[] = {
1131
1132
{ FALSE, cmsSig0, cmsSigLut16Type, 4, { cmsSigMatrixElemType, cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType } },
1133
{ FALSE, cmsSig0, cmsSigLut16Type, 3, { cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType } },
1134
{ FALSE, cmsSig0, cmsSigLut16Type, 2, { cmsSigCurveSetElemType, cmsSigCLutElemType } },
1135
{ TRUE, cmsSig0, cmsSigLutAtoBType, 1, { cmsSigCurveSetElemType } },
1136
{ TRUE , cmsSigAToB0Tag, cmsSigLutAtoBType, 3, { cmsSigCurveSetElemType, cmsSigMatrixElemType, cmsSigCurveSetElemType } },
1137
{ TRUE , cmsSigAToB0Tag, cmsSigLutAtoBType, 3, { cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType } },
1138
{ TRUE , cmsSigAToB0Tag, cmsSigLutAtoBType, 5, { cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType, cmsSigMatrixElemType, cmsSigCurveSetElemType }},
1139
{ TRUE , cmsSigBToA0Tag, cmsSigLutBtoAType, 1, { cmsSigCurveSetElemType }},
1140
{ TRUE , cmsSigBToA0Tag, cmsSigLutBtoAType, 3, { cmsSigCurveSetElemType, cmsSigMatrixElemType, cmsSigCurveSetElemType }},
1141
{ TRUE , cmsSigBToA0Tag, cmsSigLutBtoAType, 3, { cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType }},
1142
{ TRUE , cmsSigBToA0Tag, cmsSigLutBtoAType, 5, { cmsSigCurveSetElemType, cmsSigMatrixElemType, cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType }}
1143
};
1144
1145
#define SIZE_OF_ALLOWED_LUT (sizeof(AllowedLUTTypes)/sizeof(cmsAllowedLUT))
1146
1147
// Check a single entry
1148
static
1149
cmsBool CheckOne(const cmsAllowedLUT* Tab, const cmsPipeline* Lut)
1150
{
1151
cmsStage* mpe;
1152
int n;
1153
1154
for (n=0, mpe = Lut ->Elements; mpe != NULL; mpe = mpe ->Next, n++) {
1155
1156
if (n >= Tab ->nTypes) return FALSE;
1157
if (cmsStageType(mpe) != Tab ->MpeTypes[n]) return FALSE;
1158
}
1159
1160
return (n == Tab ->nTypes);
1161
}
1162
1163
1164
static
1165
const cmsAllowedLUT* FindCombination(const cmsPipeline* Lut, cmsBool IsV4, cmsTagSignature DestinationTag)
1166
{
1167
cmsUInt32Number n;
1168
1169
for (n=0; n < SIZE_OF_ALLOWED_LUT; n++) {
1170
1171
const cmsAllowedLUT* Tab = AllowedLUTTypes + n;
1172
1173
if (IsV4 ^ Tab -> IsV4) continue;
1174
if ((Tab ->RequiredTag != 0) && (Tab ->RequiredTag != DestinationTag)) continue;
1175
1176
if (CheckOne(Tab, Lut)) return Tab;
1177
}
1178
1179
return NULL;
1180
}
1181
1182
1183
// Does convert a transform into a device link profile
1184
cmsHPROFILE CMSEXPORT cmsTransform2DeviceLink(cmsHTRANSFORM hTransform, cmsFloat64Number Version, cmsUInt32Number dwFlags)
1185
{
1186
cmsHPROFILE hProfile = NULL;
1187
cmsUInt32Number FrmIn, FrmOut;
1188
cmsInt32Number ChansIn, ChansOut;
1189
int ColorSpaceBitsIn, ColorSpaceBitsOut;
1190
_cmsTRANSFORM* xform = (_cmsTRANSFORM*) hTransform;
1191
cmsPipeline* LUT = NULL;
1192
cmsStage* mpe;
1193
cmsContext ContextID = cmsGetTransformContextID(hTransform);
1194
const cmsAllowedLUT* AllowedLUT;
1195
cmsTagSignature DestinationTag;
1196
cmsProfileClassSignature deviceClass;
1197
1198
_cmsAssert(hTransform != NULL);
1199
1200
// Check if the pipeline holding is valid
1201
if (xform -> Lut == NULL) return NULL;
1202
1203
// Get the first mpe to check for named color
1204
mpe = cmsPipelineGetPtrToFirstStage(xform ->Lut);
1205
1206
// Check if is a named color transform
1207
if (mpe != NULL) {
1208
1209
if (cmsStageType(mpe) == cmsSigNamedColorElemType) {
1210
return CreateNamedColorDevicelink(hTransform);
1211
}
1212
}
1213
1214
// First thing to do is to get a copy of the transformation
1215
LUT = cmsPipelineDup(xform ->Lut);
1216
if (LUT == NULL) return NULL;
1217
1218
// Time to fix the Lab2/Lab4 issue.
1219
if ((xform ->EntryColorSpace == cmsSigLabData) && (Version < 4.0)) {
1220
1221
if (!cmsPipelineInsertStage(LUT, cmsAT_BEGIN, _cmsStageAllocLabV2ToV4curves(ContextID)))
1222
goto Error;
1223
}
1224
1225
// On the output side too. Note that due to V2/V4 PCS encoding on lab we cannot fix white misalignments
1226
if ((xform ->ExitColorSpace) == cmsSigLabData && (Version < 4.0)) {
1227
1228
dwFlags |= cmsFLAGS_NOWHITEONWHITEFIXUP;
1229
if (!cmsPipelineInsertStage(LUT, cmsAT_END, _cmsStageAllocLabV4ToV2(ContextID)))
1230
goto Error;
1231
}
1232
1233
1234
hProfile = cmsCreateProfilePlaceholder(ContextID);
1235
if (!hProfile) goto Error; // can't allocate
1236
1237
cmsSetProfileVersion(hProfile, Version);
1238
1239
FixColorSpaces(hProfile, xform -> EntryColorSpace, xform -> ExitColorSpace, dwFlags);
1240
1241
// Optimize the LUT and precalculate a devicelink
1242
1243
ChansIn = cmsChannelsOfColorSpace(xform -> EntryColorSpace);
1244
ChansOut = cmsChannelsOfColorSpace(xform -> ExitColorSpace);
1245
1246
ColorSpaceBitsIn = _cmsLCMScolorSpace(xform -> EntryColorSpace);
1247
ColorSpaceBitsOut = _cmsLCMScolorSpace(xform -> ExitColorSpace);
1248
1249
FrmIn = COLORSPACE_SH(ColorSpaceBitsIn) | CHANNELS_SH(ChansIn)|BYTES_SH(2);
1250
FrmOut = COLORSPACE_SH(ColorSpaceBitsOut) | CHANNELS_SH(ChansOut)|BYTES_SH(2);
1251
1252
deviceClass = cmsGetDeviceClass(hProfile);
1253
1254
if (deviceClass == cmsSigOutputClass)
1255
DestinationTag = cmsSigBToA0Tag;
1256
else
1257
DestinationTag = cmsSigAToB0Tag;
1258
1259
// Check if the profile/version can store the result
1260
if (dwFlags & cmsFLAGS_FORCE_CLUT)
1261
AllowedLUT = NULL;
1262
else
1263
AllowedLUT = FindCombination(LUT, Version >= 4.0, DestinationTag);
1264
1265
if (AllowedLUT == NULL) {
1266
1267
// Try to optimize
1268
_cmsOptimizePipeline(ContextID, &LUT, xform ->RenderingIntent, &FrmIn, &FrmOut, &dwFlags);
1269
AllowedLUT = FindCombination(LUT, Version >= 4.0, DestinationTag);
1270
1271
}
1272
1273
// If no way, then force CLUT that for sure can be written
1274
if (AllowedLUT == NULL) {
1275
1276
cmsStage* FirstStage;
1277
cmsStage* LastStage;
1278
1279
dwFlags |= cmsFLAGS_FORCE_CLUT;
1280
_cmsOptimizePipeline(ContextID, &LUT, xform ->RenderingIntent, &FrmIn, &FrmOut, &dwFlags);
1281
1282
// Put identity curves if needed
1283
FirstStage = cmsPipelineGetPtrToFirstStage(LUT);
1284
if (FirstStage != NULL && FirstStage ->Type != cmsSigCurveSetElemType)
1285
if (!cmsPipelineInsertStage(LUT, cmsAT_BEGIN, _cmsStageAllocIdentityCurves(ContextID, ChansIn)))
1286
goto Error;
1287
1288
LastStage = cmsPipelineGetPtrToLastStage(LUT);
1289
if (LastStage != NULL && LastStage ->Type != cmsSigCurveSetElemType)
1290
if (!cmsPipelineInsertStage(LUT, cmsAT_END, _cmsStageAllocIdentityCurves(ContextID, ChansOut)))
1291
goto Error;
1292
1293
AllowedLUT = FindCombination(LUT, Version >= 4.0, DestinationTag);
1294
}
1295
1296
// Somethings is wrong...
1297
if (AllowedLUT == NULL) {
1298
goto Error;
1299
}
1300
1301
1302
if (dwFlags & cmsFLAGS_8BITS_DEVICELINK)
1303
cmsPipelineSetSaveAs8bitsFlag(LUT, TRUE);
1304
1305
// Tag profile with information
1306
if (!SetTextTags(hProfile, L"devicelink")) goto Error;
1307
1308
// Store result
1309
if (!cmsWriteTag(hProfile, DestinationTag, LUT)) goto Error;
1310
1311
1312
if (xform -> InputColorant != NULL) {
1313
if (!cmsWriteTag(hProfile, cmsSigColorantTableTag, xform->InputColorant)) goto Error;
1314
}
1315
1316
if (xform -> OutputColorant != NULL) {
1317
if (!cmsWriteTag(hProfile, cmsSigColorantTableOutTag, xform->OutputColorant)) goto Error;
1318
}
1319
1320
if ((deviceClass == cmsSigLinkClass) && (xform ->Sequence != NULL)) {
1321
if (!_cmsWriteProfileSequence(hProfile, xform ->Sequence)) goto Error;
1322
}
1323
1324
// Set the white point
1325
if (deviceClass == cmsSigInputClass) {
1326
if (!cmsWriteTag(hProfile, cmsSigMediaWhitePointTag, &xform ->EntryWhitePoint)) goto Error;
1327
}
1328
else {
1329
if (!cmsWriteTag(hProfile, cmsSigMediaWhitePointTag, &xform ->ExitWhitePoint)) goto Error;
1330
}
1331
1332
1333
// Per 7.2.15 in spec 4.3
1334
cmsSetHeaderRenderingIntent(hProfile, xform ->RenderingIntent);
1335
1336
cmsPipelineFree(LUT);
1337
return hProfile;
1338
1339
Error:
1340
if (LUT != NULL) cmsPipelineFree(LUT);
1341
cmsCloseProfile(hProfile);
1342
return NULL;
1343
}
1344
1345