1 //---------------------------------------------------------------------------------
2 //
3 // Little Color Management System
4 // Copyright (c) 1998-2016 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 = { 0.3127, 0.3290, 1.0 };
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 cmsBool lAdjustWP;
683 cmsCIEXYZ WPsrc, WPdest;
684
685 } BCHSWADJUSTS, *LPBCHSWADJUSTS;
686
687
688 static
bchswSampler(register const cmsUInt16Number In[],register cmsUInt16Number Out[],register void * Cargo)689 int bchswSampler(register const cmsUInt16Number In[], register cmsUInt16Number Out[], register void* Cargo)
690 {
691 cmsCIELab LabIn, LabOut;
692 cmsCIELCh LChIn, LChOut;
693 cmsCIEXYZ XYZ;
694 LPBCHSWADJUSTS bchsw = (LPBCHSWADJUSTS) Cargo;
695
696
697 cmsLabEncoded2Float(&LabIn, In);
698
699
700 cmsLab2LCh(&LChIn, &LabIn);
701
702 // Do some adjusts on LCh
703
704 LChOut.L = LChIn.L * bchsw ->Contrast + bchsw ->Brightness;
705 LChOut.C = LChIn.C + bchsw -> Saturation;
706 LChOut.h = LChIn.h + bchsw -> Hue;
707
708
709 cmsLCh2Lab(&LabOut, &LChOut);
710
711 // Move white point in Lab
712 if (bchsw->lAdjustWP) {
713 cmsLab2XYZ(&bchsw->WPsrc, &XYZ, &LabOut);
714 cmsXYZ2Lab(&bchsw->WPdest, &LabOut, &XYZ);
715 }
716
717 // Back to encoded
718
719 cmsFloat2LabEncoded(Out, &LabOut);
720
721 return TRUE;
722 }
723
724
725 // Creates an abstract profile operating in Lab space for Brightness,
726 // contrast, Saturation and white point displacement
727
cmsCreateBCHSWabstractProfileTHR(cmsContext ContextID,int nLUTPoints,cmsFloat64Number Bright,cmsFloat64Number Contrast,cmsFloat64Number Hue,cmsFloat64Number Saturation,int TempSrc,int TempDest)728 cmsHPROFILE CMSEXPORT cmsCreateBCHSWabstractProfileTHR(cmsContext ContextID,
729 int nLUTPoints,
730 cmsFloat64Number Bright,
731 cmsFloat64Number Contrast,
732 cmsFloat64Number Hue,
733 cmsFloat64Number Saturation,
734 int TempSrc,
735 int TempDest)
736 {
737 cmsHPROFILE hICC;
738 cmsPipeline* Pipeline;
739 BCHSWADJUSTS bchsw;
740 cmsCIExyY WhitePnt;
741 cmsStage* CLUT;
742 cmsUInt32Number Dimensions[MAX_INPUT_DIMENSIONS];
743 int i;
744
745 bchsw.Brightness = Bright;
746 bchsw.Contrast = Contrast;
747 bchsw.Hue = Hue;
748 bchsw.Saturation = Saturation;
749 if (TempSrc == TempDest) {
750
751 bchsw.lAdjustWP = FALSE;
752 }
753 else {
754 bchsw.lAdjustWP = TRUE;
755 cmsWhitePointFromTemp(&WhitePnt, TempSrc);
756 cmsxyY2XYZ(&bchsw.WPsrc, &WhitePnt);
757 cmsWhitePointFromTemp(&WhitePnt, TempDest);
758 cmsxyY2XYZ(&bchsw.WPdest, &WhitePnt);
759
760 }
761
762 hICC = cmsCreateProfilePlaceholder(ContextID);
763 if (!hICC) // can't allocate
764 return NULL;
765
766 cmsSetDeviceClass(hICC, cmsSigAbstractClass);
767 cmsSetColorSpace(hICC, cmsSigLabData);
768 cmsSetPCS(hICC, cmsSigLabData);
769
770 cmsSetHeaderRenderingIntent(hICC, INTENT_PERCEPTUAL);
771
772 // Creates a Pipeline with 3D grid only
773 Pipeline = cmsPipelineAlloc(ContextID, 3, 3);
774 if (Pipeline == NULL) {
775 cmsCloseProfile(hICC);
776 return NULL;
777 }
778
779 for (i=0; i < MAX_INPUT_DIMENSIONS; i++) Dimensions[i] = nLUTPoints;
780 CLUT = cmsStageAllocCLut16bitGranular(ContextID, Dimensions, 3, 3, NULL);
781 if (CLUT == NULL) return NULL;
782
783
784 if (!cmsStageSampleCLut16bit(CLUT, bchswSampler, (void*) &bchsw, 0)) {
785
786 // Shouldn't reach here
787 goto Error;
788 }
789
790 if (!cmsPipelineInsertStage(Pipeline, cmsAT_END, CLUT)) {
791 goto Error;
792 }
793
794 // Create tags
795 if (!SetTextTags(hICC, L"BCHS built-in")) return NULL;
796
797 cmsWriteTag(hICC, cmsSigMediaWhitePointTag, (void*) cmsD50_XYZ());
798
799 cmsWriteTag(hICC, cmsSigAToB0Tag, (void*) Pipeline);
800
801 // Pipeline is already on virtual profile
802 cmsPipelineFree(Pipeline);
803
804 // Ok, done
805 return hICC;
806
807 Error:
808 cmsPipelineFree(Pipeline);
809 cmsCloseProfile(hICC);
810 return NULL;
811 }
812
813
cmsCreateBCHSWabstractProfile(int nLUTPoints,cmsFloat64Number Bright,cmsFloat64Number Contrast,cmsFloat64Number Hue,cmsFloat64Number Saturation,int TempSrc,int TempDest)814 CMSAPI cmsHPROFILE CMSEXPORT cmsCreateBCHSWabstractProfile(int nLUTPoints,
815 cmsFloat64Number Bright,
816 cmsFloat64Number Contrast,
817 cmsFloat64Number Hue,
818 cmsFloat64Number Saturation,
819 int TempSrc,
820 int TempDest)
821 {
822 return cmsCreateBCHSWabstractProfileTHR(NULL, nLUTPoints, Bright, Contrast, Hue, Saturation, TempSrc, TempDest);
823 }
824
825
826 // Creates a fake NULL profile. This profile return 1 channel as always 0.
827 // Is useful only for gamut checking tricks
cmsCreateNULLProfileTHR(cmsContext ContextID)828 cmsHPROFILE CMSEXPORT cmsCreateNULLProfileTHR(cmsContext ContextID)
829 {
830 cmsHPROFILE hProfile;
831 cmsPipeline* LUT = NULL;
832 cmsStage* PostLin;
833 cmsToneCurve* EmptyTab;
834 cmsUInt16Number Zero[2] = { 0, 0 };
835
836 hProfile = cmsCreateProfilePlaceholder(ContextID);
837 if (!hProfile) // can't allocate
838 return NULL;
839
840 cmsSetProfileVersion(hProfile, 4.3);
841
842 if (!SetTextTags(hProfile, L"NULL profile built-in")) goto Error;
843
844
845
846 cmsSetDeviceClass(hProfile, cmsSigOutputClass);
847 cmsSetColorSpace(hProfile, cmsSigGrayData);
848 cmsSetPCS(hProfile, cmsSigLabData);
849
850 // An empty LUTs is all we need
851 LUT = cmsPipelineAlloc(ContextID, 1, 1);
852 if (LUT == NULL) goto Error;
853
854 EmptyTab = cmsBuildTabulatedToneCurve16(ContextID, 2, Zero);
855 PostLin = cmsStageAllocToneCurves(ContextID, 1, &EmptyTab);
856 cmsFreeToneCurve(EmptyTab);
857
858 if (!cmsPipelineInsertStage(LUT, cmsAT_END, PostLin))
859 goto Error;
860
861 if (!cmsWriteTag(hProfile, cmsSigBToA0Tag, (void*) LUT)) goto Error;
862 if (!cmsWriteTag(hProfile, cmsSigMediaWhitePointTag, cmsD50_XYZ())) goto Error;
863
864 cmsPipelineFree(LUT);
865 return hProfile;
866
867 Error:
868
869 if (LUT != NULL)
870 cmsPipelineFree(LUT);
871
872 if (hProfile != NULL)
873 cmsCloseProfile(hProfile);
874
875 return NULL;
876 }
877
cmsCreateNULLProfile(void)878 cmsHPROFILE CMSEXPORT cmsCreateNULLProfile(void)
879 {
880 return cmsCreateNULLProfileTHR(NULL);
881 }
882
883
884 static
IsPCS(cmsColorSpaceSignature ColorSpace)885 int IsPCS(cmsColorSpaceSignature ColorSpace)
886 {
887 return (ColorSpace == cmsSigXYZData ||
888 ColorSpace == cmsSigLabData);
889 }
890
891
892 static
FixColorSpaces(cmsHPROFILE hProfile,cmsColorSpaceSignature ColorSpace,cmsColorSpaceSignature PCS,cmsUInt32Number dwFlags)893 void FixColorSpaces(cmsHPROFILE hProfile,
894 cmsColorSpaceSignature ColorSpace,
895 cmsColorSpaceSignature PCS,
896 cmsUInt32Number dwFlags)
897 {
898 if (dwFlags & cmsFLAGS_GUESSDEVICECLASS) {
899
900 if (IsPCS(ColorSpace) && IsPCS(PCS)) {
901
902 cmsSetDeviceClass(hProfile, cmsSigAbstractClass);
903 cmsSetColorSpace(hProfile, ColorSpace);
904 cmsSetPCS(hProfile, PCS);
905 return;
906 }
907
908 if (IsPCS(ColorSpace) && !IsPCS(PCS)) {
909
910 cmsSetDeviceClass(hProfile, cmsSigOutputClass);
911 cmsSetPCS(hProfile, ColorSpace);
912 cmsSetColorSpace(hProfile, PCS);
913 return;
914 }
915
916 if (IsPCS(PCS) && !IsPCS(ColorSpace)) {
917
918 cmsSetDeviceClass(hProfile, cmsSigInputClass);
919 cmsSetColorSpace(hProfile, ColorSpace);
920 cmsSetPCS(hProfile, PCS);
921 return;
922 }
923 }
924
925 cmsSetDeviceClass(hProfile, cmsSigLinkClass);
926 cmsSetColorSpace(hProfile, ColorSpace);
927 cmsSetPCS(hProfile, PCS);
928 }
929
930
931
932 // This function creates a named color profile dumping all the contents of transform to a single profile
933 // In this way, LittleCMS may be used to "group" several named color databases into a single profile.
934 // It has, however, several minor limitations. PCS is always Lab, which is not very critic since this
935 // is the normal PCS for named color profiles.
936 static
CreateNamedColorDevicelink(cmsHTRANSFORM xform)937 cmsHPROFILE CreateNamedColorDevicelink(cmsHTRANSFORM xform)
938 {
939 _cmsTRANSFORM* v = (_cmsTRANSFORM*) xform;
940 cmsHPROFILE hICC = NULL;
941 int i, nColors;
942 cmsNAMEDCOLORLIST *nc2 = NULL, *Original = NULL;
943
944 // Create an empty placeholder
945 hICC = cmsCreateProfilePlaceholder(v->ContextID);
946 if (hICC == NULL) return NULL;
947
948 // Critical information
949 cmsSetDeviceClass(hICC, cmsSigNamedColorClass);
950 cmsSetColorSpace(hICC, v ->ExitColorSpace);
951 cmsSetPCS(hICC, cmsSigLabData);
952
953 // Tag profile with information
954 if (!SetTextTags(hICC, L"Named color devicelink")) goto Error;
955
956 Original = cmsGetNamedColorList(xform);
957 if (Original == NULL) goto Error;
958
959 nColors = cmsNamedColorCount(Original);
960 nc2 = cmsDupNamedColorList(Original);
961 if (nc2 == NULL) goto Error;
962
963 // Colorant count now depends on the output space
964 nc2 ->ColorantCount = cmsPipelineOutputChannels(v ->Lut);
965
966 // Make sure we have proper formatters
967 cmsChangeBuffersFormat(xform, TYPE_NAMED_COLOR_INDEX,
968 FLOAT_SH(0) | COLORSPACE_SH(_cmsLCMScolorSpace(v ->ExitColorSpace))
969 | BYTES_SH(2) | CHANNELS_SH(cmsChannelsOf(v ->ExitColorSpace)));
970
971 // Apply the transfor to colorants.
972 for (i=0; i < nColors; i++) {
973 cmsDoTransform(xform, &i, nc2 ->List[i].DeviceColorant, 1);
974 }
975
976 if (!cmsWriteTag(hICC, cmsSigNamedColor2Tag, (void*) nc2)) goto Error;
977 cmsFreeNamedColorList(nc2);
978
979 return hICC;
980
981 Error:
982 if (hICC != NULL) cmsCloseProfile(hICC);
983 return NULL;
984 }
985
986
987 // This structure holds information about which MPU can be stored on a profile based on the version
988
989 typedef struct {
990 cmsBool IsV4; // Is a V4 tag?
991 cmsTagSignature RequiredTag; // Set to 0 for both types
992 cmsTagTypeSignature LutType; // The LUT type
993 int nTypes; // Number of types (up to 5)
994 cmsStageSignature MpeTypes[5]; // 5 is the maximum number
995
996 } cmsAllowedLUT;
997
998 #define cmsSig0 ((cmsTagSignature) 0)
999
1000 static const cmsAllowedLUT AllowedLUTTypes[] = {
1001
1002 { FALSE, cmsSig0, cmsSigLut16Type, 4, { cmsSigMatrixElemType, cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType } },
1003 { FALSE, cmsSig0, cmsSigLut16Type, 3, { cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType } },
1004 { FALSE, cmsSig0, cmsSigLut16Type, 2, { cmsSigCurveSetElemType, cmsSigCLutElemType } },
1005 { TRUE, cmsSig0, cmsSigLutAtoBType, 1, { cmsSigCurveSetElemType } },
1006 { TRUE , cmsSigAToB0Tag, cmsSigLutAtoBType, 3, { cmsSigCurveSetElemType, cmsSigMatrixElemType, cmsSigCurveSetElemType } },
1007 { TRUE , cmsSigAToB0Tag, cmsSigLutAtoBType, 3, { cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType } },
1008 { TRUE , cmsSigAToB0Tag, cmsSigLutAtoBType, 5, { cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType, cmsSigMatrixElemType, cmsSigCurveSetElemType }},
1009 { TRUE , cmsSigBToA0Tag, cmsSigLutBtoAType, 1, { cmsSigCurveSetElemType }},
1010 { TRUE , cmsSigBToA0Tag, cmsSigLutBtoAType, 3, { cmsSigCurveSetElemType, cmsSigMatrixElemType, cmsSigCurveSetElemType }},
1011 { TRUE , cmsSigBToA0Tag, cmsSigLutBtoAType, 3, { cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType }},
1012 { TRUE , cmsSigBToA0Tag, cmsSigLutBtoAType, 5, { cmsSigCurveSetElemType, cmsSigMatrixElemType, cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType }}
1013 };
1014
1015 #define SIZE_OF_ALLOWED_LUT (sizeof(AllowedLUTTypes)/sizeof(cmsAllowedLUT))
1016
1017 // Check a single entry
1018 static
CheckOne(const cmsAllowedLUT * Tab,const cmsPipeline * Lut)1019 cmsBool CheckOne(const cmsAllowedLUT* Tab, const cmsPipeline* Lut)
1020 {
1021 cmsStage* mpe;
1022 int n;
1023
1024 for (n=0, mpe = Lut ->Elements; mpe != NULL; mpe = mpe ->Next, n++) {
1025
1026 if (n > Tab ->nTypes) return FALSE;
1027 if (cmsStageType(mpe) != Tab ->MpeTypes[n]) return FALSE;
1028 }
1029
1030 return (n == Tab ->nTypes);
1031 }
1032
1033
1034 static
FindCombination(const cmsPipeline * Lut,cmsBool IsV4,cmsTagSignature DestinationTag)1035 const cmsAllowedLUT* FindCombination(const cmsPipeline* Lut, cmsBool IsV4, cmsTagSignature DestinationTag)
1036 {
1037 cmsUInt32Number n;
1038
1039 for (n=0; n < SIZE_OF_ALLOWED_LUT; n++) {
1040
1041 const cmsAllowedLUT* Tab = AllowedLUTTypes + n;
1042
1043 if (IsV4 ^ Tab -> IsV4) continue;
1044 if ((Tab ->RequiredTag != 0) && (Tab ->RequiredTag != DestinationTag)) continue;
1045
1046 if (CheckOne(Tab, Lut)) return Tab;
1047 }
1048
1049 return NULL;
1050 }
1051
1052
1053 // Does convert a transform into a device link profile
cmsTransform2DeviceLink(cmsHTRANSFORM hTransform,cmsFloat64Number Version,cmsUInt32Number dwFlags)1054 cmsHPROFILE CMSEXPORT cmsTransform2DeviceLink(cmsHTRANSFORM hTransform, cmsFloat64Number Version, cmsUInt32Number dwFlags)
1055 {
1056 cmsHPROFILE hProfile = NULL;
1057 cmsUInt32Number FrmIn, FrmOut, ChansIn, ChansOut;
1058 cmsUInt32Number ColorSpaceBitsIn, ColorSpaceBitsOut;
1059 _cmsTRANSFORM* xform = (_cmsTRANSFORM*) hTransform;
1060 cmsPipeline* LUT = NULL;
1061 cmsStage* mpe;
1062 cmsContext ContextID = cmsGetTransformContextID(hTransform);
1063 const cmsAllowedLUT* AllowedLUT;
1064 cmsTagSignature DestinationTag;
1065 cmsProfileClassSignature deviceClass;
1066
1067 _cmsAssert(hTransform != NULL);
1068
1069 // Get the first mpe to check for named color
1070 mpe = cmsPipelineGetPtrToFirstStage(xform ->Lut);
1071
1072 // Check if is a named color transform
1073 if (mpe != NULL) {
1074
1075 if (cmsStageType(mpe) == cmsSigNamedColorElemType) {
1076 return CreateNamedColorDevicelink(hTransform);
1077 }
1078 }
1079
1080 // First thing to do is to get a copy of the transformation
1081 LUT = cmsPipelineDup(xform ->Lut);
1082 if (LUT == NULL) return NULL;
1083
1084 // Time to fix the Lab2/Lab4 issue.
1085 if ((xform ->EntryColorSpace == cmsSigLabData) && (Version < 4.0)) {
1086
1087 if (!cmsPipelineInsertStage(LUT, cmsAT_BEGIN, _cmsStageAllocLabV2ToV4curves(ContextID)))
1088 goto Error;
1089 }
1090
1091 // On the output side too
1092 if ((xform ->ExitColorSpace) == cmsSigLabData && (Version < 4.0)) {
1093
1094 if (!cmsPipelineInsertStage(LUT, cmsAT_END, _cmsStageAllocLabV4ToV2(ContextID)))
1095 goto Error;
1096 }
1097
1098
1099 hProfile = cmsCreateProfilePlaceholder(ContextID);
1100 if (!hProfile) goto Error; // can't allocate
1101
1102 cmsSetProfileVersion(hProfile, Version);
1103
1104 FixColorSpaces(hProfile, xform -> EntryColorSpace, xform -> ExitColorSpace, dwFlags);
1105
1106 // Optimize the LUT and precalculate a devicelink
1107
1108 ChansIn = cmsChannelsOf(xform -> EntryColorSpace);
1109 ChansOut = cmsChannelsOf(xform -> ExitColorSpace);
1110
1111 ColorSpaceBitsIn = _cmsLCMScolorSpace(xform -> EntryColorSpace);
1112 ColorSpaceBitsOut = _cmsLCMScolorSpace(xform -> ExitColorSpace);
1113
1114 FrmIn = COLORSPACE_SH(ColorSpaceBitsIn) | CHANNELS_SH(ChansIn)|BYTES_SH(2);
1115 FrmOut = COLORSPACE_SH(ColorSpaceBitsOut) | CHANNELS_SH(ChansOut)|BYTES_SH(2);
1116
1117 deviceClass = cmsGetDeviceClass(hProfile);
1118
1119 if (deviceClass == cmsSigOutputClass)
1120 DestinationTag = cmsSigBToA0Tag;
1121 else
1122 DestinationTag = cmsSigAToB0Tag;
1123
1124 // Check if the profile/version can store the result
1125 if (dwFlags & cmsFLAGS_FORCE_CLUT)
1126 AllowedLUT = NULL;
1127 else
1128 AllowedLUT = FindCombination(LUT, Version >= 4.0, DestinationTag);
1129
1130 if (AllowedLUT == NULL) {
1131
1132 // Try to optimize
1133 _cmsOptimizePipeline(ContextID, &LUT, xform ->RenderingIntent, &FrmIn, &FrmOut, &dwFlags);
1134 AllowedLUT = FindCombination(LUT, Version >= 4.0, DestinationTag);
1135
1136 }
1137
1138 // If no way, then force CLUT that for sure can be written
1139 if (AllowedLUT == NULL) {
1140
1141 cmsStage* FirstStage;
1142 cmsStage* LastStage;
1143
1144 dwFlags |= cmsFLAGS_FORCE_CLUT;
1145 _cmsOptimizePipeline(ContextID, &LUT, xform ->RenderingIntent, &FrmIn, &FrmOut, &dwFlags);
1146
1147 // Put identity curves if needed
1148 FirstStage = cmsPipelineGetPtrToFirstStage(LUT);
1149 if (FirstStage != NULL && FirstStage ->Type != cmsSigCurveSetElemType)
1150 if (!cmsPipelineInsertStage(LUT, cmsAT_BEGIN, _cmsStageAllocIdentityCurves(ContextID, ChansIn)))
1151 goto Error;
1152
1153 LastStage = cmsPipelineGetPtrToLastStage(LUT);
1154 if (LastStage != NULL && LastStage ->Type != cmsSigCurveSetElemType)
1155 if (!cmsPipelineInsertStage(LUT, cmsAT_END, _cmsStageAllocIdentityCurves(ContextID, ChansOut)))
1156 goto Error;
1157
1158 AllowedLUT = FindCombination(LUT, Version >= 4.0, DestinationTag);
1159 }
1160
1161 // Somethings is wrong...
1162 if (AllowedLUT == NULL) {
1163 goto Error;
1164 }
1165
1166
1167 if (dwFlags & cmsFLAGS_8BITS_DEVICELINK)
1168 cmsPipelineSetSaveAs8bitsFlag(LUT, TRUE);
1169
1170 // Tag profile with information
1171 if (!SetTextTags(hProfile, L"devicelink")) goto Error;
1172
1173 // Store result
1174 if (!cmsWriteTag(hProfile, DestinationTag, LUT)) goto Error;
1175
1176
1177 if (xform -> InputColorant != NULL) {
1178 if (!cmsWriteTag(hProfile, cmsSigColorantTableTag, xform->InputColorant)) goto Error;
1179 }
1180
1181 if (xform -> OutputColorant != NULL) {
1182 if (!cmsWriteTag(hProfile, cmsSigColorantTableOutTag, xform->OutputColorant)) goto Error;
1183 }
1184
1185 if ((deviceClass == cmsSigLinkClass) && (xform ->Sequence != NULL)) {
1186 if (!_cmsWriteProfileSequence(hProfile, xform ->Sequence)) goto Error;
1187 }
1188
1189 // Set the white point
1190 if (deviceClass == cmsSigInputClass) {
1191 if (!cmsWriteTag(hProfile, cmsSigMediaWhitePointTag, &xform ->EntryWhitePoint)) goto Error;
1192 }
1193 else {
1194 if (!cmsWriteTag(hProfile, cmsSigMediaWhitePointTag, &xform ->ExitWhitePoint)) goto Error;
1195 }
1196
1197
1198 // Per 7.2.15 in spec 4.3
1199 cmsSetHeaderRenderingIntent(hProfile, xform ->RenderingIntent);
1200
1201 cmsPipelineFree(LUT);
1202 return hProfile;
1203
1204 Error:
1205 if (LUT != NULL) cmsPipelineFree(LUT);
1206 cmsCloseProfile(hProfile);
1207 return NULL;
1208 }
1209