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