Coverage Report

Created: 2025-07-23 06:30

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