Coverage Report

Created: 2025-06-10 07:27

/src/ghostpdl/lcms2mt/src/cmsvirt.c
Line
Count
Source (jump to first uncovered line)
1
//---------------------------------------------------------------------------------
2
//
3
//  Little Color Management System
4
//  Copyright (c) 1998-2020 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(cmsContext ContextID, cmsHPROFILE hProfile, const wchar_t* Description)
34
2.47M
{
35
2.47M
    cmsMLU *DescriptionMLU, *CopyrightMLU;
36
2.47M
    cmsBool  rc = FALSE;
37
38
2.47M
    DescriptionMLU  = cmsMLUalloc(ContextID, 1);
39
2.47M
    CopyrightMLU    = cmsMLUalloc(ContextID, 1);
40
41
2.47M
    if (DescriptionMLU == NULL || CopyrightMLU == NULL) goto Error;
42
43
2.47M
    if (!cmsMLUsetWide(ContextID, DescriptionMLU,  "en", "US", Description)) goto Error;
44
2.47M
    if (!cmsMLUsetWide(ContextID, CopyrightMLU,    "en", "US", L"No copyright, use freely")) goto Error;
45
46
2.47M
    if (!cmsWriteTag(ContextID, hProfile, cmsSigProfileDescriptionTag,  DescriptionMLU)) goto Error;
47
2.47M
    if (!cmsWriteTag(ContextID, hProfile, cmsSigCopyrightTag,           CopyrightMLU)) goto Error;
48
49
2.47M
    rc = TRUE;
50
51
2.47M
Error:
52
53
2.47M
    if (DescriptionMLU)
54
2.47M
        cmsMLUfree(ContextID, DescriptionMLU);
55
2.47M
    if (CopyrightMLU)
56
2.47M
        cmsMLUfree(ContextID, CopyrightMLU);
57
2.47M
    return rc;
58
2.47M
}
59
60
61
static
62
cmsBool  SetSeqDescTag(cmsContext ContextID, cmsHPROFILE hProfile, const char* Model)
63
0
{
64
0
    cmsBool  rc = FALSE;
65
0
    cmsSEQ* Seq = cmsAllocProfileSequenceDescription(ContextID, 1);
66
67
0
    if (Seq == NULL) return FALSE;
68
69
0
    Seq->seq[0].deviceMfg = (cmsSignature) 0;
70
0
    Seq->seq[0].deviceModel = (cmsSignature) 0;
71
72
#ifdef CMS_DONT_USE_INT64
73
    Seq->seq[0].attributes[0] = 0;
74
    Seq->seq[0].attributes[1] = 0;
75
#else
76
0
    Seq->seq[0].attributes = 0;
77
0
#endif
78
79
0
    Seq->seq[0].technology = (cmsTechnologySignature) 0;
80
81
0
    cmsMLUsetASCII(ContextID,  Seq->seq[0].Manufacturer, cmsNoLanguage, cmsNoCountry, "Little CMS");
82
0
    cmsMLUsetASCII(ContextID,  Seq->seq[0].Model,        cmsNoLanguage, cmsNoCountry, Model);
83
84
0
    if (!_cmsWriteProfileSequence(ContextID, hProfile, Seq)) goto Error;
85
86
0
    rc = TRUE;
87
88
0
Error:
89
0
    if (Seq)
90
0
        cmsFreeProfileSequenceDescription(ContextID, Seq);
91
92
0
    return rc;
93
0
}
94
95
96
97
// This function creates a profile based on White point, primaries and
98
// transfer functions.
99
cmsHPROFILE CMSEXPORT cmsCreateRGBProfile(cmsContext ContextID,
100
                                          const cmsCIExyY* WhitePoint,
101
                                          const cmsCIExyYTRIPLE* Primaries,
102
                                          cmsToneCurve* const TransferFunction[3])
103
1.23M
{
104
1.23M
    cmsHPROFILE hICC;
105
1.23M
    cmsMAT3 MColorants;
106
1.23M
    cmsCIEXYZTRIPLE Colorants;
107
1.23M
    cmsCIExyY MaxWhite;
108
1.23M
    cmsMAT3 CHAD;
109
1.23M
    cmsCIEXYZ WhitePointXYZ;
110
111
1.23M
    hICC = cmsCreateProfilePlaceholder(ContextID);
112
1.23M
    if (!hICC)                          // can't allocate
113
0
        return NULL;
114
115
1.23M
    cmsSetProfileVersion(ContextID, hICC, 4.3);
116
117
1.23M
    cmsSetDeviceClass(ContextID, hICC,      cmsSigDisplayClass);
118
1.23M
    cmsSetColorSpace(ContextID, hICC,       cmsSigRgbData);
119
1.23M
    cmsSetPCS(ContextID, hICC,              cmsSigXYZData);
120
121
1.23M
    cmsSetHeaderRenderingIntent(ContextID, hICC,  INTENT_PERCEPTUAL);
122
123
124
    // Implement profile using following tags:
125
    //
126
    //  1 cmsSigProfileDescriptionTag
127
    //  2 cmsSigMediaWhitePointTag
128
    //  3 cmsSigRedColorantTag
129
    //  4 cmsSigGreenColorantTag
130
    //  5 cmsSigBlueColorantTag
131
    //  6 cmsSigRedTRCTag
132
    //  7 cmsSigGreenTRCTag
133
    //  8 cmsSigBlueTRCTag
134
    //  9 Chromatic adaptation Tag
135
    // This conforms a standard RGB DisplayProfile as says ICC, and then I add (As per addendum II)
136
    // 10 cmsSigChromaticityTag
137
138
139
1.23M
    if (!SetTextTags(ContextID, hICC, L"RGB built-in")) goto Error;
140
141
1.23M
    if (WhitePoint) {
142
143
1.23M
        if (!cmsWriteTag(ContextID, hICC, cmsSigMediaWhitePointTag, cmsD50_XYZ(ContextID))) goto Error;
144
145
1.23M
        cmsxyY2XYZ(ContextID, &WhitePointXYZ, WhitePoint);
146
1.23M
        _cmsAdaptationMatrix(ContextID, &CHAD, NULL, &WhitePointXYZ, cmsD50_XYZ(ContextID));
147
148
        // This is a V4 tag, but many CMM does read and understand it no matter which version
149
1.23M
        if (!cmsWriteTag(ContextID, hICC, cmsSigChromaticAdaptationTag, (void*) &CHAD)) goto Error;
150
1.23M
    }
151
152
1.23M
    if (WhitePoint && Primaries) {
153
154
0
        MaxWhite.x =  WhitePoint -> x;
155
0
        MaxWhite.y =  WhitePoint -> y;
156
0
        MaxWhite.Y =  1.0;
157
158
0
        if (!_cmsBuildRGB2XYZtransferMatrix(ContextID, &MColorants, &MaxWhite, Primaries)) goto Error;
159
160
0
        Colorants.Red.X   = MColorants.v[0].n[0];
161
0
        Colorants.Red.Y   = MColorants.v[1].n[0];
162
0
        Colorants.Red.Z   = MColorants.v[2].n[0];
163
164
0
        Colorants.Green.X = MColorants.v[0].n[1];
165
0
        Colorants.Green.Y = MColorants.v[1].n[1];
166
0
        Colorants.Green.Z = MColorants.v[2].n[1];
167
168
0
        Colorants.Blue.X  = MColorants.v[0].n[2];
169
0
        Colorants.Blue.Y  = MColorants.v[1].n[2];
170
0
        Colorants.Blue.Z  = MColorants.v[2].n[2];
171
172
0
        if (!cmsWriteTag(ContextID, hICC, cmsSigRedColorantTag,   (void*) &Colorants.Red)) goto Error;
173
0
        if (!cmsWriteTag(ContextID, hICC, cmsSigBlueColorantTag,  (void*) &Colorants.Blue)) goto Error;
174
0
        if (!cmsWriteTag(ContextID, hICC, cmsSigGreenColorantTag, (void*) &Colorants.Green)) goto Error;
175
0
    }
176
177
178
1.23M
    if (TransferFunction) {
179
180
        // Tries to minimize space. Thanks to Richard Hughes for this nice idea
181
0
        if (!cmsWriteTag(ContextID, hICC, cmsSigRedTRCTag,   (void*) TransferFunction[0])) goto Error;
182
183
0
        if (TransferFunction[1] == TransferFunction[0]) {
184
185
0
            if (!cmsLinkTag (ContextID, hICC, cmsSigGreenTRCTag, cmsSigRedTRCTag)) goto Error;
186
187
0
        } else {
188
189
0
            if (!cmsWriteTag(ContextID, hICC, cmsSigGreenTRCTag, (void*) TransferFunction[1])) goto Error;
190
0
        }
191
192
0
        if (TransferFunction[2] == TransferFunction[0]) {
193
194
0
            if (!cmsLinkTag (ContextID, hICC, cmsSigBlueTRCTag, cmsSigRedTRCTag)) goto Error;
195
196
0
        } else {
197
198
0
            if (!cmsWriteTag(ContextID, hICC, cmsSigBlueTRCTag, (void*) TransferFunction[2])) goto Error;
199
0
        }
200
0
    }
201
202
1.23M
    if (Primaries) {
203
0
        if (!cmsWriteTag(ContextID, hICC, cmsSigChromaticityTag, (void*) Primaries)) goto Error;
204
0
    }
205
206
207
1.23M
    return hICC;
208
209
0
Error:
210
0
    if (hICC)
211
0
        cmsCloseProfile(ContextID, hICC);
212
0
    return NULL;
213
1.23M
}
214
215
216
217
// This function creates a profile based on White point and transfer function.
218
cmsHPROFILE CMSEXPORT cmsCreateGrayProfile(cmsContext ContextID,
219
                                           const cmsCIExyY* WhitePoint,
220
                                           const cmsToneCurve* TransferFunction)
221
0
{
222
0
    cmsHPROFILE hICC;
223
0
    cmsCIEXYZ tmp;
224
225
0
    hICC = cmsCreateProfilePlaceholder(ContextID);
226
0
    if (!hICC)                          // can't allocate
227
0
        return NULL;
228
229
0
    cmsSetProfileVersion(ContextID, hICC, 4.3);
230
231
0
    cmsSetDeviceClass(ContextID, hICC,      cmsSigDisplayClass);
232
0
    cmsSetColorSpace(ContextID, hICC,       cmsSigGrayData);
233
0
    cmsSetPCS(ContextID, hICC,              cmsSigXYZData);
234
0
    cmsSetHeaderRenderingIntent(ContextID, hICC,  INTENT_PERCEPTUAL);
235
236
237
    // Implement profile using following tags:
238
    //
239
    //  1 cmsSigProfileDescriptionTag
240
    //  2 cmsSigMediaWhitePointTag
241
    //  3 cmsSigGrayTRCTag
242
243
    // This conforms a standard Gray DisplayProfile
244
245
    // Fill-in the tags
246
247
0
    if (!SetTextTags(ContextID, hICC, L"gray built-in")) goto Error;
248
249
250
0
    if (WhitePoint) {
251
252
0
        cmsxyY2XYZ(ContextID, &tmp, WhitePoint);
253
0
        if (!cmsWriteTag(ContextID, hICC, cmsSigMediaWhitePointTag, (void*) &tmp)) goto Error;
254
0
    }
255
256
0
    if (TransferFunction) {
257
258
0
        if (!cmsWriteTag(ContextID, hICC, cmsSigGrayTRCTag, (void*) TransferFunction)) goto Error;
259
0
    }
260
261
0
    return hICC;
262
263
0
Error:
264
0
    if (hICC)
265
0
        cmsCloseProfile(ContextID, hICC);
266
0
    return NULL;
267
0
}
268
269
270
271
272
// This is a devicelink operating in the target colorspace with as many transfer functions as components
273
274
cmsHPROFILE CMSEXPORT cmsCreateLinearizationDeviceLink(cmsContext ContextID,
275
                                                          cmsColorSpaceSignature ColorSpace,
276
                                                          cmsToneCurve* const TransferFunctions[])
277
0
{
278
0
    cmsHPROFILE hICC;
279
0
    cmsPipeline* Pipeline;
280
0
    cmsUInt32Number nChannels;
281
282
0
    hICC = cmsCreateProfilePlaceholder(ContextID);
283
0
    if (!hICC)
284
0
        return NULL;
285
286
0
    cmsSetProfileVersion(ContextID, hICC, 4.3);
287
288
0
    cmsSetDeviceClass(ContextID, hICC,      cmsSigLinkClass);
289
0
    cmsSetColorSpace(ContextID, hICC,       ColorSpace);
290
0
    cmsSetPCS(ContextID, hICC,              ColorSpace);
291
292
0
    cmsSetHeaderRenderingIntent(ContextID, hICC,  INTENT_PERCEPTUAL);
293
294
    // Set up channels
295
0
    nChannels = cmsChannelsOf(ContextID, ColorSpace);
296
297
    // Creates a Pipeline with prelinearization step only
298
0
    Pipeline = cmsPipelineAlloc(ContextID, nChannels, nChannels);
299
0
    if (Pipeline == NULL) goto Error;
300
301
302
    // Copy tables to Pipeline
303
0
    if (!cmsPipelineInsertStage(ContextID, Pipeline, cmsAT_BEGIN, cmsStageAllocToneCurves(ContextID, nChannels, TransferFunctions)))
304
0
        goto Error;
305
306
    // Create tags
307
0
    if (!SetTextTags(ContextID, hICC, L"Linearization built-in")) goto Error;
308
0
    if (!cmsWriteTag(ContextID, hICC, cmsSigAToB0Tag, (void*) Pipeline)) goto Error;
309
0
    if (!SetSeqDescTag(ContextID, hICC, "Linearization built-in")) goto Error;
310
311
    // Pipeline is already on virtual profile
312
0
    cmsPipelineFree(ContextID, Pipeline);
313
314
    // Ok, done
315
0
    return hICC;
316
317
0
Error:
318
0
    cmsPipelineFree(ContextID, Pipeline);
319
0
    if (hICC)
320
0
        cmsCloseProfile(ContextID, hICC);
321
322
323
0
    return NULL;
324
0
}
325
326
// Ink-limiting algorithm
327
//
328
//  Sum = C + M + Y + K
329
//  If Sum > InkLimit
330
//        Ratio= 1 - (Sum - InkLimit) / (C + M + Y)
331
//        if Ratio <0
332
//              Ratio=0
333
//        endif
334
//     Else
335
//         Ratio=1
336
//     endif
337
//
338
//     C = Ratio * C
339
//     M = Ratio * M
340
//     Y = Ratio * Y
341
//     K: Does not change
342
343
static
344
int InkLimitingSampler(cmsContext ContextID, CMSREGISTER const cmsUInt16Number In[], CMSREGISTER cmsUInt16Number Out[], CMSREGISTER void* Cargo)
345
0
{
346
0
    cmsFloat64Number InkLimit = *(cmsFloat64Number *) Cargo;
347
0
    cmsFloat64Number SumCMY, SumCMYK, Ratio;
348
0
    cmsUNUSED_PARAMETER(ContextID);
349
350
0
    InkLimit = (InkLimit * 655.35);
351
352
0
    SumCMY   = In[0]  + In[1] + In[2];
353
0
    SumCMYK  = SumCMY + In[3];
354
355
0
    if (SumCMYK > InkLimit) {
356
357
0
        Ratio = 1 - ((SumCMYK - InkLimit) / SumCMY);
358
0
        if (Ratio < 0)
359
0
            Ratio = 0;
360
0
    }
361
0
    else Ratio = 1;
362
363
0
    Out[0] = _cmsQuickSaturateWord(In[0] * Ratio);     // C
364
0
    Out[1] = _cmsQuickSaturateWord(In[1] * Ratio);     // M
365
0
    Out[2] = _cmsQuickSaturateWord(In[2] * Ratio);     // Y
366
367
0
    Out[3] = In[3];                                 // K (untouched)
368
369
0
    return TRUE;
370
0
}
371
372
// This is a devicelink operating in CMYK for ink-limiting
373
374
cmsHPROFILE CMSEXPORT cmsCreateInkLimitingDeviceLink(cmsContext ContextID,
375
                                                     cmsColorSpaceSignature ColorSpace,
376
                                                     cmsFloat64Number Limit)
377
0
{
378
0
    cmsHPROFILE hICC;
379
0
    cmsPipeline* LUT;
380
0
    cmsStage* CLUT;
381
0
    cmsUInt32Number nChannels;
382
383
0
    if (ColorSpace != cmsSigCmykData) {
384
0
        cmsSignalError(ContextID, cmsERROR_COLORSPACE_CHECK, "InkLimiting: Only CMYK currently supported");
385
0
        return NULL;
386
0
    }
387
388
0
    if (Limit < 0.0 || Limit > 400) {
389
390
0
        cmsSignalError(ContextID, cmsERROR_RANGE, "InkLimiting: Limit should be between 0..400");
391
0
        if (Limit < 0) Limit = 0;
392
0
        if (Limit > 400) Limit = 400;
393
394
0
    }
395
396
0
    hICC = cmsCreateProfilePlaceholder(ContextID);
397
0
    if (!hICC)                          // can't allocate
398
0
        return NULL;
399
400
0
    cmsSetProfileVersion(ContextID, hICC, 4.3);
401
402
0
    cmsSetDeviceClass(ContextID, hICC,      cmsSigLinkClass);
403
0
    cmsSetColorSpace(ContextID, hICC,       ColorSpace);
404
0
    cmsSetPCS(ContextID, hICC,              ColorSpace);
405
406
0
    cmsSetHeaderRenderingIntent(ContextID, hICC,  INTENT_PERCEPTUAL);
407
408
409
    // Creates a Pipeline with 3D grid only
410
0
    LUT = cmsPipelineAlloc(ContextID, 4, 4);
411
0
    if (LUT == NULL) goto Error;
412
413
414
0
    nChannels = cmsChannelsOf(ContextID, ColorSpace);
415
416
0
    CLUT = cmsStageAllocCLut16bit(ContextID, 17, nChannels, nChannels, NULL);
417
0
    if (CLUT == NULL) goto Error;
418
419
0
    if (!cmsStageSampleCLut16bit(ContextID, CLUT, InkLimitingSampler, (void*) &Limit, 0)) goto Error;
420
421
0
    if (!cmsPipelineInsertStage(ContextID, LUT, cmsAT_BEGIN, _cmsStageAllocIdentityCurves(ContextID, nChannels)) ||
422
0
        !cmsPipelineInsertStage(ContextID, LUT, cmsAT_END, CLUT) ||
423
0
        !cmsPipelineInsertStage(ContextID, LUT, cmsAT_END, _cmsStageAllocIdentityCurves(ContextID, nChannels)))
424
0
        goto Error;
425
426
    // Create tags
427
0
    if (!SetTextTags(ContextID, hICC, L"ink-limiting built-in")) goto Error;
428
429
0
    if (!cmsWriteTag(ContextID, hICC, cmsSigAToB0Tag, (void*) LUT))  goto Error;
430
0
    if (!SetSeqDescTag(ContextID, hICC, "ink-limiting built-in")) goto Error;
431
432
    // cmsPipeline is already on virtual profile
433
0
    cmsPipelineFree(ContextID, LUT);
434
435
    // Ok, done
436
0
    return hICC;
437
438
0
Error:
439
0
    if (LUT != NULL)
440
0
        cmsPipelineFree(ContextID, LUT);
441
442
0
    if (hICC != NULL)
443
0
        cmsCloseProfile(ContextID, hICC);
444
445
0
    return NULL;
446
0
}
447
448
449
// Creates a fake Lab identity.
450
cmsHPROFILE CMSEXPORT cmsCreateLab2Profile(cmsContext ContextID, const cmsCIExyY* WhitePoint)
451
634k
{
452
634k
    cmsHPROFILE hProfile;
453
634k
    cmsPipeline* LUT = NULL;
454
455
634k
    hProfile = cmsCreateRGBProfile(ContextID, WhitePoint == NULL ? cmsD50_xyY(ContextID) : WhitePoint, NULL, NULL);
456
634k
    if (hProfile == NULL) return NULL;
457
458
634k
    cmsSetProfileVersion(ContextID, hProfile, 2.1);
459
460
634k
    cmsSetDeviceClass(ContextID, hProfile, cmsSigAbstractClass);
461
634k
    cmsSetColorSpace(ContextID, hProfile,  cmsSigLabData);
462
634k
    cmsSetPCS(ContextID, hProfile,         cmsSigLabData);
463
464
634k
    if (!SetTextTags(ContextID, hProfile, L"Lab identity built-in")) goto Error;
465
466
    // An identity LUT is all we need
467
634k
    LUT = cmsPipelineAlloc(ContextID, 3, 3);
468
634k
    if (LUT == NULL) goto Error;
469
470
634k
    if (!cmsPipelineInsertStage(ContextID, LUT, cmsAT_BEGIN, _cmsStageAllocIdentityCLut(ContextID, 3)))
471
0
        goto Error;
472
473
634k
    if (!cmsWriteTag(ContextID, hProfile, cmsSigAToB0Tag, LUT)) goto Error;
474
634k
    cmsPipelineFree(ContextID, LUT);
475
476
634k
    return hProfile;
477
478
0
Error:
479
480
0
    if (LUT != NULL)
481
0
        cmsPipelineFree(ContextID, LUT);
482
483
0
    if (hProfile != NULL)
484
0
        cmsCloseProfile(ContextID, hProfile);
485
486
0
    return NULL;
487
634k
}
488
489
490
491
// Creates a fake Lab V4 identity.
492
cmsHPROFILE CMSEXPORT cmsCreateLab4Profile(cmsContext ContextID, const cmsCIExyY* WhitePoint)
493
20.2k
{
494
20.2k
    cmsHPROFILE hProfile;
495
20.2k
    cmsPipeline* LUT = NULL;
496
497
20.2k
    hProfile = cmsCreateRGBProfile(ContextID, WhitePoint == NULL ? cmsD50_xyY(ContextID) : WhitePoint, NULL, NULL);
498
20.2k
    if (hProfile == NULL) return NULL;
499
500
20.2k
    cmsSetProfileVersion(ContextID, hProfile, 4.3);
501
502
20.2k
    cmsSetDeviceClass(ContextID, hProfile, cmsSigAbstractClass);
503
20.2k
    cmsSetColorSpace(ContextID, hProfile,  cmsSigLabData);
504
20.2k
    cmsSetPCS(ContextID, hProfile,         cmsSigLabData);
505
506
20.2k
    if (!SetTextTags(ContextID, hProfile, L"Lab identity built-in")) goto Error;
507
508
    // An empty LUTs is all we need
509
20.2k
    LUT = cmsPipelineAlloc(ContextID, 3, 3);
510
20.2k
    if (LUT == NULL) goto Error;
511
512
20.2k
    if (!cmsPipelineInsertStage(ContextID, LUT, cmsAT_BEGIN, _cmsStageAllocIdentityCurves(ContextID, 3)))
513
0
        goto Error;
514
515
20.2k
    if (!cmsWriteTag(ContextID, hProfile, cmsSigAToB0Tag, LUT)) goto Error;
516
20.2k
    cmsPipelineFree(ContextID, LUT);
517
518
20.2k
    return hProfile;
519
520
0
Error:
521
522
0
    if (LUT != NULL)
523
0
        cmsPipelineFree(ContextID, LUT);
524
525
0
    if (hProfile != NULL)
526
0
        cmsCloseProfile(ContextID, hProfile);
527
528
0
    return NULL;
529
20.2k
}
530
531
532
// Creates a fake XYZ identity
533
cmsHPROFILE CMSEXPORT cmsCreateXYZProfile(cmsContext ContextID)
534
580k
{
535
580k
    cmsHPROFILE hProfile;
536
580k
    cmsPipeline* LUT = NULL;
537
538
580k
    hProfile = cmsCreateRGBProfile(ContextID, cmsD50_xyY(ContextID), NULL, NULL);
539
580k
    if (hProfile == NULL) return NULL;
540
541
580k
    cmsSetProfileVersion(ContextID, hProfile, 4.3);
542
543
580k
    cmsSetDeviceClass(ContextID, hProfile, cmsSigAbstractClass);
544
580k
    cmsSetColorSpace(ContextID, hProfile,  cmsSigXYZData);
545
580k
    cmsSetPCS(ContextID, hProfile,         cmsSigXYZData);
546
547
580k
    if (!SetTextTags(ContextID, hProfile, L"XYZ identity built-in")) goto Error;
548
549
    // An identity LUT is all we need
550
580k
    LUT = cmsPipelineAlloc(ContextID, 3, 3);
551
580k
    if (LUT == NULL) goto Error;
552
553
580k
    if (!cmsPipelineInsertStage(ContextID, LUT, cmsAT_BEGIN, _cmsStageAllocIdentityCurves(ContextID, 3)))
554
0
        goto Error;
555
556
580k
    if (!cmsWriteTag(ContextID, hProfile, cmsSigAToB0Tag, LUT)) goto Error;
557
580k
    cmsPipelineFree(ContextID, LUT);
558
559
580k
    return hProfile;
560
561
0
Error:
562
563
0
    if (LUT != NULL)
564
0
        cmsPipelineFree(ContextID, LUT);
565
566
0
    if (hProfile != NULL)
567
0
        cmsCloseProfile(ContextID, hProfile);
568
569
0
    return NULL;
570
580k
}
571
572
573
574
//sRGB Curves are defined by:
575
//
576
//If  R'sRGB,G'sRGB, B'sRGB < 0.04045
577
//
578
//    R =  R'sRGB / 12.92
579
//    G =  G'sRGB / 12.92
580
//    B =  B'sRGB / 12.92
581
//
582
//
583
//else if  R'sRGB,G'sRGB, B'sRGB >= 0.04045
584
//
585
//    R = ((R'sRGB + 0.055) / 1.055)^2.4
586
//    G = ((G'sRGB + 0.055) / 1.055)^2.4
587
//    B = ((B'sRGB + 0.055) / 1.055)^2.4
588
589
static
590
cmsToneCurve* Build_sRGBGamma(cmsContext ContextID)
591
0
{
592
0
    cmsFloat64Number Parameters[5];
593
594
0
    Parameters[0] = 2.4;
595
0
    Parameters[1] = 1. / 1.055;
596
0
    Parameters[2] = 0.055 / 1.055;
597
0
    Parameters[3] = 1. / 12.92;
598
0
    Parameters[4] = 0.04045;
599
600
0
    return cmsBuildParametricToneCurve(ContextID, 4, Parameters);
601
0
}
602
603
// Create the ICC virtual profile for sRGB space
604
cmsHPROFILE CMSEXPORT cmsCreate_sRGBProfile(cmsContext ContextID)
605
0
{
606
0
       cmsCIExyY       D65 = { 0.3127, 0.3290, 1.0 };
607
0
       cmsCIExyYTRIPLE Rec709Primaries = {
608
0
                                   {0.6400, 0.3300, 1.0},
609
0
                                   {0.3000, 0.6000, 1.0},
610
0
                                   {0.1500, 0.0600, 1.0}
611
0
                                   };
612
0
       cmsToneCurve* Gamma22[3];
613
0
       cmsHPROFILE  hsRGB;
614
615
      // cmsWhitePointFromTemp(ContextID, &D65, 6504);
616
0
       Gamma22[0] = Gamma22[1] = Gamma22[2] = Build_sRGBGamma(ContextID);
617
0
       if (Gamma22[0] == NULL) return NULL;
618
619
0
       hsRGB = cmsCreateRGBProfile(ContextID, &D65, &Rec709Primaries, Gamma22);
620
0
       cmsFreeToneCurve(ContextID, Gamma22[0]);
621
0
       if (hsRGB == NULL) return NULL;
622
623
0
       if (!SetTextTags(ContextID, hsRGB, L"sRGB built-in")) {
624
0
           cmsCloseProfile(ContextID, hsRGB);
625
0
           return NULL;
626
0
       }
627
628
0
       return hsRGB;
629
0
}
630
631
632
633
typedef struct {
634
                cmsFloat64Number Brightness;
635
                cmsFloat64Number Contrast;
636
                cmsFloat64Number Hue;
637
                cmsFloat64Number Saturation;
638
                cmsBool          lAdjustWP;
639
                cmsCIEXYZ WPsrc, WPdest;
640
641
} BCHSWADJUSTS, *LPBCHSWADJUSTS;
642
643
644
static
645
int bchswSampler(cmsContext ContextID, CMSREGISTER const cmsUInt16Number In[], CMSREGISTER cmsUInt16Number Out[], CMSREGISTER void* Cargo)
646
0
{
647
0
    cmsCIELab LabIn, LabOut;
648
0
    cmsCIELCh LChIn, LChOut;
649
0
    cmsCIEXYZ XYZ;
650
0
    LPBCHSWADJUSTS bchsw = (LPBCHSWADJUSTS) Cargo;
651
652
653
0
    cmsLabEncoded2Float(ContextID, &LabIn, In);
654
655
656
0
    cmsLab2LCh(ContextID, &LChIn, &LabIn);
657
658
    // Do some adjusts on LCh
659
660
0
    LChOut.L = LChIn.L * bchsw ->Contrast + bchsw ->Brightness;
661
0
    LChOut.C = LChIn.C + bchsw -> Saturation;
662
0
    LChOut.h = LChIn.h + bchsw -> Hue;
663
664
665
0
    cmsLCh2Lab(ContextID, &LabOut, &LChOut);
666
667
    // Move white point in Lab
668
0
    if (bchsw->lAdjustWP) {
669
0
           cmsLab2XYZ(ContextID, &bchsw->WPsrc, &XYZ, &LabOut);
670
0
           cmsXYZ2Lab(ContextID, &bchsw->WPdest, &LabOut, &XYZ);
671
0
    }
672
673
    // Back to encoded
674
675
0
    cmsFloat2LabEncoded(ContextID, Out, &LabOut);
676
677
0
    return TRUE;
678
0
}
679
680
681
// Creates an abstract profile operating in Lab space for Brightness,
682
// contrast, Saturation and white point displacement
683
684
cmsHPROFILE CMSEXPORT cmsCreateBCHSWabstractProfile(cmsContext ContextID,
685
                                                    cmsUInt32Number nLUTPoints,
686
                                                    cmsFloat64Number Bright,
687
                                                    cmsFloat64Number Contrast,
688
                                                    cmsFloat64Number Hue,
689
                                                    cmsFloat64Number Saturation,
690
                                                    cmsUInt32Number TempSrc,
691
                                                    cmsUInt32Number TempDest)
692
0
{
693
0
    cmsHPROFILE hICC;
694
0
    cmsPipeline* Pipeline;
695
0
    BCHSWADJUSTS bchsw;
696
0
    cmsCIExyY WhitePnt;
697
0
    cmsStage* CLUT;
698
0
    cmsUInt32Number Dimensions[MAX_INPUT_DIMENSIONS];
699
0
    cmsUInt32Number i;
700
701
0
    bchsw.Brightness = Bright;
702
0
    bchsw.Contrast   = Contrast;
703
0
    bchsw.Hue        = Hue;
704
0
    bchsw.Saturation = Saturation;
705
0
    if (TempSrc == TempDest) {
706
707
0
           bchsw.lAdjustWP = FALSE;
708
0
    }
709
0
    else {
710
0
           bchsw.lAdjustWP = TRUE;
711
0
           cmsWhitePointFromTemp(ContextID, &WhitePnt, TempSrc);
712
0
           cmsxyY2XYZ(ContextID, &bchsw.WPsrc, &WhitePnt);
713
0
           cmsWhitePointFromTemp(ContextID, &WhitePnt, TempDest);
714
0
           cmsxyY2XYZ(ContextID, &bchsw.WPdest, &WhitePnt);
715
716
0
    }
717
718
0
    hICC = cmsCreateProfilePlaceholder(ContextID);
719
0
    if (!hICC)                          // can't allocate
720
0
        return NULL;
721
722
0
    cmsSetDeviceClass(ContextID, hICC,      cmsSigAbstractClass);
723
0
    cmsSetColorSpace(ContextID, hICC,       cmsSigLabData);
724
0
    cmsSetPCS(ContextID, hICC,              cmsSigLabData);
725
726
0
    cmsSetHeaderRenderingIntent(ContextID, hICC,  INTENT_PERCEPTUAL);
727
728
    // Creates a Pipeline with 3D grid only
729
0
    Pipeline = cmsPipelineAlloc(ContextID, 3, 3);
730
0
    if (Pipeline == NULL) {
731
0
        cmsCloseProfile(ContextID, hICC);
732
0
        return NULL;
733
0
    }
734
735
0
    for (i=0; i < MAX_INPUT_DIMENSIONS; i++) Dimensions[i] = nLUTPoints;
736
0
    CLUT = cmsStageAllocCLut16bitGranular(ContextID, Dimensions, 3, 3, NULL);
737
0
    if (CLUT == NULL) goto Error;
738
739
740
0
    if (!cmsStageSampleCLut16bit(ContextID, CLUT, bchswSampler, (void*) &bchsw, 0)) {
741
742
        // Shouldn't reach here
743
0
        goto Error;
744
0
    }
745
746
0
    if (!cmsPipelineInsertStage(ContextID, Pipeline, cmsAT_END, CLUT)) {
747
0
        goto Error;
748
0
    }
749
750
    // Create tags
751
0
    if (!SetTextTags(ContextID, hICC, L"BCHS built-in")) return NULL;
752
753
0
    cmsWriteTag(ContextID, hICC, cmsSigMediaWhitePointTag, (void*) cmsD50_XYZ(ContextID));
754
755
0
    cmsWriteTag(ContextID, hICC, cmsSigAToB0Tag, (void*) Pipeline);
756
757
    // Pipeline is already on virtual profile
758
0
    cmsPipelineFree(ContextID, Pipeline);
759
760
    // Ok, done
761
0
    return hICC;
762
763
0
Error:
764
0
    cmsPipelineFree(ContextID, Pipeline);
765
0
    cmsCloseProfile(ContextID, hICC);
766
0
    return NULL;
767
0
}
768
769
770
771
// Creates a fake NULL profile. This profile return 1 channel as always 0.
772
// Is useful only for gamut checking tricks
773
cmsHPROFILE CMSEXPORT cmsCreateNULLProfile(cmsContext ContextID)
774
0
{
775
0
    cmsHPROFILE hProfile;
776
0
    cmsPipeline* LUT = NULL;
777
0
    cmsStage* PostLin;
778
0
    cmsStage* OutLin;
779
0
    cmsToneCurve* EmptyTab[3];
780
0
    cmsUInt16Number Zero[2] = { 0, 0 };
781
0
    const cmsFloat64Number PickLstarMatrix[] = { 1, 0, 0 };
782
783
0
    hProfile = cmsCreateProfilePlaceholder(ContextID);
784
0
    if (!hProfile)                          // can't allocate
785
0
        return NULL;
786
787
0
    cmsSetProfileVersion(ContextID, hProfile, 4.3);
788
789
0
    if (!SetTextTags(ContextID, hProfile, L"NULL profile built-in")) goto Error;
790
791
792
0
    cmsSetDeviceClass(ContextID, hProfile, cmsSigOutputClass);
793
0
    cmsSetColorSpace(ContextID, hProfile,  cmsSigGrayData);
794
0
    cmsSetPCS(ContextID, hProfile,         cmsSigLabData);
795
796
    // Create a valid ICC 4 structure
797
0
    LUT = cmsPipelineAlloc(ContextID, 3, 1);
798
0
    if (LUT == NULL) goto Error;
799
800
0
    EmptyTab[0] = EmptyTab[1] = EmptyTab[2] = cmsBuildTabulatedToneCurve16(ContextID, 2, Zero);
801
0
    PostLin = cmsStageAllocToneCurves(ContextID, 3, EmptyTab);
802
0
    OutLin  = cmsStageAllocToneCurves(ContextID, 1, EmptyTab);
803
0
    cmsFreeToneCurve(ContextID, EmptyTab[0]);
804
805
0
    if (!cmsPipelineInsertStage(ContextID, LUT, cmsAT_END, PostLin))
806
0
        goto Error;
807
808
0
    if (!cmsPipelineInsertStage(ContextID, LUT, cmsAT_END, cmsStageAllocMatrix(ContextID, 1, 3, PickLstarMatrix, NULL)))
809
0
        goto Error;
810
811
0
    if (!cmsPipelineInsertStage(ContextID, LUT, cmsAT_END, OutLin))
812
0
        goto Error;
813
814
0
    if (!cmsWriteTag(ContextID, hProfile, cmsSigBToA0Tag, (void*) LUT)) goto Error;
815
0
    if (!cmsWriteTag(ContextID, hProfile, cmsSigMediaWhitePointTag, cmsD50_XYZ(ContextID))) goto Error;
816
817
0
    cmsPipelineFree(ContextID, LUT);
818
0
    return hProfile;
819
820
0
Error:
821
822
0
    if (LUT != NULL)
823
0
        cmsPipelineFree(ContextID, LUT);
824
825
0
    if (hProfile != NULL)
826
0
        cmsCloseProfile(ContextID, hProfile);
827
828
0
    return NULL;
829
0
}
830
831
832
static
833
int IsPCS(cmsColorSpaceSignature ColorSpace)
834
0
{
835
0
    return (ColorSpace == cmsSigXYZData ||
836
0
            ColorSpace == cmsSigLabData);
837
0
}
838
839
840
static
841
void FixColorSpaces(cmsContext ContextID, cmsHPROFILE hProfile,
842
                              cmsColorSpaceSignature ColorSpace,
843
                              cmsColorSpaceSignature PCS,
844
                              cmsUInt32Number dwFlags)
845
0
{
846
0
    if (dwFlags & cmsFLAGS_GUESSDEVICECLASS) {
847
848
0
            if (IsPCS(ColorSpace) && IsPCS(PCS)) {
849
850
0
                    cmsSetDeviceClass(ContextID, hProfile,      cmsSigAbstractClass);
851
0
                    cmsSetColorSpace(ContextID, hProfile,       ColorSpace);
852
0
                    cmsSetPCS(ContextID, hProfile,              PCS);
853
0
                    return;
854
0
            }
855
856
0
            if (IsPCS(ColorSpace) && !IsPCS(PCS)) {
857
858
0
                    cmsSetDeviceClass(ContextID, hProfile, cmsSigOutputClass);
859
0
                    cmsSetPCS(ContextID, hProfile,         ColorSpace);
860
0
                    cmsSetColorSpace(ContextID, hProfile,  PCS);
861
0
                    return;
862
0
            }
863
864
0
            if (IsPCS(PCS) && !IsPCS(ColorSpace)) {
865
866
0
                   cmsSetDeviceClass(ContextID, hProfile,  cmsSigInputClass);
867
0
                   cmsSetColorSpace(ContextID, hProfile,   ColorSpace);
868
0
                   cmsSetPCS(ContextID, hProfile,          PCS);
869
0
                   return;
870
0
            }
871
0
    }
872
873
0
    cmsSetDeviceClass(ContextID, hProfile,      cmsSigLinkClass);
874
0
    cmsSetColorSpace(ContextID, hProfile,       ColorSpace);
875
0
    cmsSetPCS(ContextID, hProfile,              PCS);
876
0
}
877
878
879
880
// This function creates a named color profile dumping all the contents of transform to a single profile
881
// In this way, LittleCMS may be used to "group" several named color databases into a single profile.
882
// It has, however, several minor limitations. PCS is always Lab, which is not very critic since this
883
// is the normal PCS for named color profiles.
884
static
885
cmsHPROFILE CreateNamedColorDevicelink(cmsContext ContextID, cmsHTRANSFORM xform)
886
0
{
887
0
    _cmsTRANSFORM* v = (_cmsTRANSFORM*) xform;
888
0
    cmsHPROFILE hICC = NULL;
889
0
    cmsUInt32Number i, nColors;
890
0
    cmsNAMEDCOLORLIST *nc2 = NULL, *Original = NULL;
891
0
    cmsUInt32Number InputFormat, OutputFormat;
892
0
    cmsFormatter16 FromInput, ToOutput;
893
894
    // Create an empty placeholder
895
0
    hICC = cmsCreateProfilePlaceholder(ContextID);
896
0
    if (hICC == NULL) return NULL;
897
898
    // Critical information
899
0
    cmsSetDeviceClass(ContextID, hICC, cmsSigNamedColorClass);
900
0
    cmsSetColorSpace(ContextID, hICC, v ->core->ExitColorSpace);
901
0
    cmsSetPCS(ContextID, hICC, cmsSigLabData);
902
903
    // Tag profile with information
904
0
    if (!SetTextTags(ContextID, hICC, L"Named color devicelink")) goto Error;
905
906
0
    Original = cmsGetNamedColorList(xform);
907
0
    if (Original == NULL) goto Error;
908
909
0
    nColors = cmsNamedColorCount(ContextID, Original);
910
0
    nc2     = cmsDupNamedColorList(ContextID, Original);
911
0
    if (nc2 == NULL) goto Error;
912
913
    // Colorant count now depends on the output space
914
0
    nc2 ->ColorantCount = cmsPipelineOutputChannels(ContextID, v ->core->Lut);
915
916
    // Make sure we have proper formatters
917
    // We only can afford to change formatters if previous transform is at least 16 bits
918
0
    if (!(v->core->dwOriginalFlags & cmsFLAGS_CAN_CHANGE_FORMATTER)) {
919
0
        cmsSignalError(ContextID, cmsERROR_NOT_SUITABLE,
920
0
                       "CreateNamedColorDevicelink needs transforms created with at least 16 bits of precision");
921
0
        goto Error;
922
0
    }
923
0
    InputFormat = TYPE_NAMED_COLOR_INDEX;
924
0
    OutputFormat = FLOAT_SH(0) |
925
0
                   COLORSPACE_SH(_cmsLCMScolorSpace(ContextID, v ->core->ExitColorSpace)) |
926
0
                   BYTES_SH(2) |
927
0
                   CHANNELS_SH(cmsChannelsOf(ContextID, v ->core->ExitColorSpace));
928
0
    FromInput = _cmsGetFormatter(ContextID, InputFormat, cmsFormatterInput, CMS_PACK_FLAGS_16BITS).Fmt16;
929
0
    ToOutput  = _cmsGetFormatter(ContextID, OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_16BITS).Fmt16;
930
931
0
    if (FromInput == NULL || ToOutput == NULL) {
932
0
        cmsSignalError(ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported raster format");
933
0
        goto Error;
934
0
    }
935
936
0
    v ->InputFormat  = InputFormat;
937
0
    v ->OutputFormat = OutputFormat;
938
0
    v ->FromInput    = FromInput;
939
0
    v ->ToOutput     = ToOutput;
940
0
    _cmsFindFormatter(ContextID, v, InputFormat, OutputFormat, v->core->dwOriginalFlags);
941
942
    // Apply the transfor to colorants.
943
0
    for (i=0; i < nColors; i++) {
944
0
        cmsDoTransform(ContextID, xform, &i, nc2 ->List[i].DeviceColorant, 1);
945
0
    }
946
947
0
    if (!cmsWriteTag(ContextID, hICC, cmsSigNamedColor2Tag, (void*) nc2)) goto Error;
948
0
    cmsFreeNamedColorList(ContextID, nc2);
949
950
0
    return hICC;
951
952
0
Error:
953
0
    if (hICC != NULL) cmsCloseProfile(ContextID, hICC);
954
0
    return NULL;
955
0
}
956
957
958
// This structure holds information about which MPU can be stored on a profile based on the version
959
960
typedef struct {
961
    cmsBool              IsV4;             // Is a V4 tag?
962
    cmsTagSignature      RequiredTag;      // Set to 0 for both types
963
    cmsTagTypeSignature  LutType;          // The LUT type
964
    int                  nTypes;           // Number of types (up to 5)
965
    cmsStageSignature    MpeTypes[5];      // 5 is the maximum number
966
967
} cmsAllowedLUT;
968
969
#define cmsSig0 ((cmsTagSignature) 0)
970
971
static const cmsAllowedLUT AllowedLUTTypes[] = {
972
973
    { FALSE, cmsSig0,        cmsSigLut16Type, 4, { cmsSigMatrixElemType, cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType } },
974
    { FALSE, cmsSig0,        cmsSigLut16Type, 3, { cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType } },
975
    { FALSE, cmsSig0,        cmsSigLut16Type, 2, { cmsSigCurveSetElemType, cmsSigCLutElemType } },
976
    { TRUE,  cmsSig0,        cmsSigLutAtoBType, 1, { cmsSigCurveSetElemType } },
977
    { TRUE , cmsSigAToB0Tag, cmsSigLutAtoBType,  3,  { cmsSigCurveSetElemType, cmsSigMatrixElemType, cmsSigCurveSetElemType } },
978
    { TRUE , cmsSigAToB0Tag, cmsSigLutAtoBType,  3,  { cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType   } },
979
    { TRUE , cmsSigAToB0Tag, cmsSigLutAtoBType,  5,  { cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType, cmsSigMatrixElemType, cmsSigCurveSetElemType }},
980
    { TRUE , cmsSigBToA0Tag, cmsSigLutBtoAType,  1,  { cmsSigCurveSetElemType }},
981
    { TRUE , cmsSigBToA0Tag, cmsSigLutBtoAType,  3,  { cmsSigCurveSetElemType, cmsSigMatrixElemType, cmsSigCurveSetElemType }},
982
    { TRUE , cmsSigBToA0Tag, cmsSigLutBtoAType,  3,  { cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType }},
983
    { TRUE , cmsSigBToA0Tag, cmsSigLutBtoAType,  5,  { cmsSigCurveSetElemType, cmsSigMatrixElemType, cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType }}
984
};
985
986
0
#define SIZE_OF_ALLOWED_LUT (sizeof(AllowedLUTTypes)/sizeof(cmsAllowedLUT))
987
988
// Check a single entry
989
static
990
cmsBool CheckOne(cmsContext ContextID, const cmsAllowedLUT* Tab, const cmsPipeline* Lut)
991
0
{
992
0
    cmsStage* mpe;
993
0
    int n;
994
995
0
    for (n=0, mpe = Lut ->Elements; mpe != NULL; mpe = mpe ->Next, n++) {
996
997
0
        if (n > Tab ->nTypes) return FALSE;
998
0
        if (cmsStageType(ContextID, mpe) != Tab ->MpeTypes[n]) return FALSE;
999
0
    }
1000
1001
0
    return (n == Tab ->nTypes);
1002
0
}
1003
1004
1005
static
1006
const cmsAllowedLUT* FindCombination(cmsContext ContextID, const cmsPipeline* Lut, cmsBool IsV4, cmsTagSignature DestinationTag)
1007
0
{
1008
0
    cmsUInt32Number n;
1009
1010
0
    for (n=0; n < SIZE_OF_ALLOWED_LUT; n++) {
1011
1012
0
        const cmsAllowedLUT* Tab = AllowedLUTTypes + n;
1013
1014
0
        if (IsV4 ^ Tab -> IsV4) continue;
1015
0
        if ((Tab ->RequiredTag != 0) && (Tab ->RequiredTag != DestinationTag)) continue;
1016
1017
0
        if (CheckOne(ContextID, Tab, Lut)) return Tab;
1018
0
    }
1019
1020
0
    return NULL;
1021
0
}
1022
1023
1024
// Does convert a transform into a device link profile
1025
cmsHPROFILE CMSEXPORT cmsTransform2DeviceLink(cmsContext ContextID, cmsHTRANSFORM hTransform, cmsFloat64Number Version, cmsUInt32Number dwFlags)
1026
0
{
1027
0
    cmsHPROFILE hProfile = NULL;
1028
0
    cmsUInt32Number FrmIn, FrmOut, ChansIn, ChansOut;
1029
0
    int ColorSpaceBitsIn, ColorSpaceBitsOut;
1030
0
    _cmsTRANSFORM* xform = (_cmsTRANSFORM*) hTransform;
1031
0
    cmsPipeline* LUT = NULL;
1032
0
    cmsStage* mpe;
1033
0
    const cmsAllowedLUT* AllowedLUT;
1034
0
    cmsTagSignature DestinationTag;
1035
0
    cmsProfileClassSignature deviceClass;
1036
1037
0
    _cmsAssert(hTransform != NULL);
1038
1039
    // Get the first mpe to check for named color
1040
0
    mpe = cmsPipelineGetPtrToFirstStage(ContextID, xform ->core->Lut);
1041
1042
    // Check if is a named color transform
1043
0
    if (mpe != NULL) {
1044
1045
0
        if (cmsStageType(ContextID, mpe) == cmsSigNamedColorElemType) {
1046
0
            return CreateNamedColorDevicelink(ContextID, hTransform);
1047
0
        }
1048
0
    }
1049
1050
    // First thing to do is to get a copy of the transformation
1051
0
    LUT = cmsPipelineDup(ContextID, xform ->core->Lut);
1052
0
    if (LUT == NULL) return NULL;
1053
1054
    // Time to fix the Lab2/Lab4 issue.
1055
0
    if ((xform ->core->EntryColorSpace == cmsSigLabData) && (Version < 4.0)) {
1056
1057
0
        if (!cmsPipelineInsertStage(ContextID, LUT, cmsAT_BEGIN, _cmsStageAllocLabV2ToV4curves(ContextID)))
1058
0
            goto Error;
1059
0
    }
1060
1061
    // On the output side too. Note that due to V2/V4 PCS encoding on lab we cannot fix white misalignments
1062
0
    if ((xform ->core->ExitColorSpace) == cmsSigLabData && (Version < 4.0)) {
1063
1064
0
        dwFlags |= cmsFLAGS_NOWHITEONWHITEFIXUP;
1065
0
        if (!cmsPipelineInsertStage(ContextID, LUT, cmsAT_END, _cmsStageAllocLabV4ToV2(ContextID)))
1066
0
            goto Error;
1067
0
    }
1068
1069
1070
0
    hProfile = cmsCreateProfilePlaceholder(ContextID);
1071
0
    if (!hProfile) goto Error;                    // can't allocate
1072
1073
0
    cmsSetProfileVersion(ContextID, hProfile, Version);
1074
1075
0
    FixColorSpaces(ContextID, hProfile, xform ->core->EntryColorSpace, xform ->core->ExitColorSpace, dwFlags);
1076
1077
    // Optimize the LUT and precalculate a devicelink
1078
1079
0
    ChansIn  = cmsChannelsOf(ContextID, xform ->core->EntryColorSpace);
1080
0
    ChansOut = cmsChannelsOf(ContextID, xform ->core->ExitColorSpace);
1081
1082
0
    ColorSpaceBitsIn  = _cmsLCMScolorSpace(ContextID, xform ->core->EntryColorSpace);
1083
0
    ColorSpaceBitsOut = _cmsLCMScolorSpace(ContextID, xform ->core->ExitColorSpace);
1084
1085
0
    FrmIn  = COLORSPACE_SH(ColorSpaceBitsIn) | CHANNELS_SH(ChansIn)|BYTES_SH(2);
1086
0
    FrmOut = COLORSPACE_SH(ColorSpaceBitsOut) | CHANNELS_SH(ChansOut)|BYTES_SH(2);
1087
1088
0
    deviceClass = cmsGetDeviceClass(ContextID, hProfile);
1089
1090
0
     if (deviceClass == cmsSigOutputClass)
1091
0
         DestinationTag = cmsSigBToA0Tag;
1092
0
     else
1093
0
         DestinationTag = cmsSigAToB0Tag;
1094
1095
    // Check if the profile/version can store the result
1096
0
    if (dwFlags & cmsFLAGS_FORCE_CLUT)
1097
0
        AllowedLUT = NULL;
1098
0
    else
1099
0
        AllowedLUT = FindCombination(ContextID, LUT, Version >= 4.0, DestinationTag);
1100
1101
0
    if (AllowedLUT == NULL) {
1102
1103
        // Try to optimize
1104
0
        _cmsOptimizePipeline(ContextID, &LUT, xform->core->RenderingIntent, &FrmIn, &FrmOut, &dwFlags);
1105
0
        AllowedLUT = FindCombination(ContextID, LUT, Version >= 4.0, DestinationTag);
1106
1107
0
    }
1108
1109
    // If no way, then force CLUT that for sure can be written
1110
0
    if (AllowedLUT == NULL) {
1111
1112
0
        cmsStage* FirstStage;
1113
0
        cmsStage* LastStage;
1114
1115
0
        dwFlags |= cmsFLAGS_FORCE_CLUT;
1116
0
        _cmsOptimizePipeline(ContextID, &LUT, xform->core->RenderingIntent, &FrmIn, &FrmOut, &dwFlags);
1117
1118
        // Put identity curves if needed
1119
0
        FirstStage = cmsPipelineGetPtrToFirstStage(ContextID, LUT);
1120
0
        if (FirstStage != NULL && FirstStage ->Type != cmsSigCurveSetElemType)
1121
0
             if (!cmsPipelineInsertStage(ContextID, LUT, cmsAT_BEGIN, _cmsStageAllocIdentityCurves(ContextID, ChansIn)))
1122
0
                 goto Error;
1123
1124
0
        LastStage = cmsPipelineGetPtrToLastStage(ContextID, LUT);
1125
0
        if (LastStage != NULL && LastStage ->Type != cmsSigCurveSetElemType)
1126
0
             if (!cmsPipelineInsertStage(ContextID, LUT, cmsAT_END,   _cmsStageAllocIdentityCurves(ContextID, ChansOut)))
1127
0
                 goto Error;
1128
1129
0
        AllowedLUT = FindCombination(ContextID, LUT, Version >= 4.0, DestinationTag);
1130
0
    }
1131
1132
    // Somethings is wrong...
1133
0
    if (AllowedLUT == NULL) {
1134
0
        goto Error;
1135
0
    }
1136
1137
1138
0
    if (dwFlags & cmsFLAGS_8BITS_DEVICELINK)
1139
0
                     cmsPipelineSetSaveAs8bitsFlag(ContextID, LUT, TRUE);
1140
1141
    // Tag profile with information
1142
0
    if (!SetTextTags(ContextID, hProfile, L"devicelink")) goto Error;
1143
1144
    // Store result
1145
0
    if (!cmsWriteTag(ContextID, hProfile, DestinationTag, LUT)) goto Error;
1146
1147
1148
0
    if (xform->core->InputColorant != NULL) {
1149
0
           if (!cmsWriteTag(ContextID, hProfile, cmsSigColorantTableTag, xform->core->InputColorant)) goto Error;
1150
0
    }
1151
1152
0
    if (xform->core->OutputColorant != NULL) {
1153
0
           if (!cmsWriteTag(ContextID, hProfile, cmsSigColorantTableOutTag, xform->core->OutputColorant)) goto Error;
1154
0
    }
1155
1156
0
    if ((deviceClass == cmsSigLinkClass) && (xform ->core->Sequence != NULL)) {
1157
0
        if (!_cmsWriteProfileSequence(ContextID, hProfile, xform ->core->Sequence)) goto Error;
1158
0
    }
1159
1160
    // Set the white point
1161
0
    if (deviceClass == cmsSigInputClass) {
1162
0
        if (!cmsWriteTag(ContextID, hProfile, cmsSigMediaWhitePointTag, &xform->core->EntryWhitePoint)) goto Error;
1163
0
    }
1164
0
    else {
1165
0
         if (!cmsWriteTag(ContextID, hProfile, cmsSigMediaWhitePointTag, &xform ->core->ExitWhitePoint)) goto Error;
1166
0
    }
1167
1168
1169
    // Per 7.2.15 in spec 4.3
1170
0
    cmsSetHeaderRenderingIntent(ContextID, hProfile, xform->core->RenderingIntent);
1171
1172
0
    cmsPipelineFree(ContextID, LUT);
1173
0
    return hProfile;
1174
1175
0
Error:
1176
0
    if (LUT != NULL) cmsPipelineFree(ContextID, LUT);
1177
0
    cmsCloseProfile(ContextID, hProfile);
1178
0
    return NULL;
1179
0
}