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