1 //---------------------------------------------------------------------------------
2 //
3 // Little Color Management System
4 // Copyright (c) 1998-2023 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.4);
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.4);
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 cmsInt32Number nChannels;
295
296 hICC = cmsCreateProfilePlaceholder(ContextID);
297 if (!hICC)
298 return NULL;
299
300 cmsSetProfileVersion(hICC, 4.4);
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 = cmsChannelsOfColorSpace(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(CMSREGISTER const cmsUInt16Number In[],CMSREGISTER cmsUInt16Number Out[],CMSREGISTER void * Cargo)364 int InkLimitingSampler(CMSREGISTER const cmsUInt16Number In[], CMSREGISTER cmsUInt16Number Out[], CMSREGISTER void* Cargo)
365 {
366 cmsFloat64Number InkLimit = *(cmsFloat64Number *) Cargo;
367 cmsFloat64Number SumCMY, SumCMYK, Ratio;
368
369 InkLimit = (InkLimit * 655.35);
370
371 SumCMY = (cmsFloat64Number) 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 cmsInt32Number 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.4);
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.4);
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.4);
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(CMSREGISTER const cmsUInt16Number In[],CMSREGISTER cmsUInt16Number Out[],CMSREGISTER void * Cargo)689 int bchswSampler(CMSREGISTER const cmsUInt16Number In[], CMSREGISTER cmsUInt16Number Out[], CMSREGISTER 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,cmsUInt32Number nLUTPoints,cmsFloat64Number Bright,cmsFloat64Number Contrast,cmsFloat64Number Hue,cmsFloat64Number Saturation,cmsUInt32Number TempSrc,cmsUInt32Number TempDest)728 cmsHPROFILE CMSEXPORT cmsCreateBCHSWabstractProfileTHR(cmsContext ContextID,
729 cmsUInt32Number nLUTPoints,
730 cmsFloat64Number Bright,
731 cmsFloat64Number Contrast,
732 cmsFloat64Number Hue,
733 cmsFloat64Number Saturation,
734 cmsUInt32Number TempSrc,
735 cmsUInt32Number TempDest)
736 {
737 cmsHPROFILE hICC;
738 cmsPipeline* Pipeline;
739 BCHSWADJUSTS bchsw;
740 cmsCIExyY WhitePnt;
741 cmsStage* CLUT;
742 cmsUInt32Number Dimensions[MAX_INPUT_DIMENSIONS];
743 cmsUInt32Number 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) goto Error;
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(cmsUInt32Number nLUTPoints,cmsFloat64Number Bright,cmsFloat64Number Contrast,cmsFloat64Number Hue,cmsFloat64Number Saturation,cmsUInt32Number TempSrc,cmsUInt32Number TempDest)814 CMSAPI cmsHPROFILE CMSEXPORT cmsCreateBCHSWabstractProfile(cmsUInt32Number nLUTPoints,
815 cmsFloat64Number Bright,
816 cmsFloat64Number Contrast,
817 cmsFloat64Number Hue,
818 cmsFloat64Number Saturation,
819 cmsUInt32Number TempSrc,
820 cmsUInt32Number 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 cmsStage* OutLin;
834 cmsToneCurve* EmptyTab[3];
835 cmsUInt16Number Zero[2] = { 0, 0 };
836 const cmsFloat64Number PickLstarMatrix[] = { 1, 0, 0 };
837
838 hProfile = cmsCreateProfilePlaceholder(ContextID);
839 if (!hProfile) // can't allocate
840 return NULL;
841
842 cmsSetProfileVersion(hProfile, 4.4);
843
844 if (!SetTextTags(hProfile, L"NULL profile built-in")) goto Error;
845
846
847 cmsSetDeviceClass(hProfile, cmsSigOutputClass);
848 cmsSetColorSpace(hProfile, cmsSigGrayData);
849 cmsSetPCS(hProfile, cmsSigLabData);
850
851 // Create a valid ICC 4 structure
852 LUT = cmsPipelineAlloc(ContextID, 3, 1);
853 if (LUT == NULL) goto Error;
854
855 EmptyTab[0] = EmptyTab[1] = EmptyTab[2] = cmsBuildTabulatedToneCurve16(ContextID, 2, Zero);
856 PostLin = cmsStageAllocToneCurves(ContextID, 3, EmptyTab);
857 OutLin = cmsStageAllocToneCurves(ContextID, 1, EmptyTab);
858 cmsFreeToneCurve(EmptyTab[0]);
859
860 if (!cmsPipelineInsertStage(LUT, cmsAT_END, PostLin))
861 goto Error;
862
863 if (!cmsPipelineInsertStage(LUT, cmsAT_END, cmsStageAllocMatrix(ContextID, 1, 3, PickLstarMatrix, NULL)))
864 goto Error;
865
866 if (!cmsPipelineInsertStage(LUT, cmsAT_END, OutLin))
867 goto Error;
868
869 if (!cmsWriteTag(hProfile, cmsSigBToA0Tag, (void*) LUT)) goto Error;
870 if (!cmsWriteTag(hProfile, cmsSigMediaWhitePointTag, cmsD50_XYZ())) goto Error;
871
872 cmsPipelineFree(LUT);
873 return hProfile;
874
875 Error:
876
877 if (LUT != NULL)
878 cmsPipelineFree(LUT);
879
880 if (hProfile != NULL)
881 cmsCloseProfile(hProfile);
882
883 return NULL;
884 }
885
cmsCreateNULLProfile(void)886 cmsHPROFILE CMSEXPORT cmsCreateNULLProfile(void)
887 {
888 return cmsCreateNULLProfileTHR(NULL);
889 }
890
891
892 static
IsPCS(cmsColorSpaceSignature ColorSpace)893 int IsPCS(cmsColorSpaceSignature ColorSpace)
894 {
895 return (ColorSpace == cmsSigXYZData ||
896 ColorSpace == cmsSigLabData);
897 }
898
899
900 static
FixColorSpaces(cmsHPROFILE hProfile,cmsColorSpaceSignature ColorSpace,cmsColorSpaceSignature PCS,cmsUInt32Number dwFlags)901 void FixColorSpaces(cmsHPROFILE hProfile,
902 cmsColorSpaceSignature ColorSpace,
903 cmsColorSpaceSignature PCS,
904 cmsUInt32Number dwFlags)
905 {
906 if (dwFlags & cmsFLAGS_GUESSDEVICECLASS) {
907
908 if (IsPCS(ColorSpace) && IsPCS(PCS)) {
909
910 cmsSetDeviceClass(hProfile, cmsSigAbstractClass);
911 cmsSetColorSpace(hProfile, ColorSpace);
912 cmsSetPCS(hProfile, PCS);
913 return;
914 }
915
916 if (IsPCS(ColorSpace) && !IsPCS(PCS)) {
917
918 cmsSetDeviceClass(hProfile, cmsSigOutputClass);
919 cmsSetPCS(hProfile, ColorSpace);
920 cmsSetColorSpace(hProfile, PCS);
921 return;
922 }
923
924 if (IsPCS(PCS) && !IsPCS(ColorSpace)) {
925
926 cmsSetDeviceClass(hProfile, cmsSigInputClass);
927 cmsSetColorSpace(hProfile, ColorSpace);
928 cmsSetPCS(hProfile, PCS);
929 return;
930 }
931 }
932
933 cmsSetDeviceClass(hProfile, cmsSigLinkClass);
934 cmsSetColorSpace(hProfile, ColorSpace);
935 cmsSetPCS(hProfile, PCS);
936 }
937
938
939
940 // This function creates a named color profile dumping all the contents of transform to a single profile
941 // In this way, LittleCMS may be used to "group" several named color databases into a single profile.
942 // It has, however, several minor limitations. PCS is always Lab, which is not very critic since this
943 // is the normal PCS for named color profiles.
944 static
CreateNamedColorDevicelink(cmsHTRANSFORM xform)945 cmsHPROFILE CreateNamedColorDevicelink(cmsHTRANSFORM xform)
946 {
947 _cmsTRANSFORM* v = (_cmsTRANSFORM*) xform;
948 cmsHPROFILE hICC = NULL;
949 cmsUInt32Number i, nColors;
950 cmsNAMEDCOLORLIST *nc2 = NULL, *Original = NULL;
951
952 // Create an empty placeholder
953 hICC = cmsCreateProfilePlaceholder(v->ContextID);
954 if (hICC == NULL) return NULL;
955
956 // Critical information
957 cmsSetDeviceClass(hICC, cmsSigNamedColorClass);
958 cmsSetColorSpace(hICC, v ->ExitColorSpace);
959 cmsSetPCS(hICC, cmsSigLabData);
960
961 // Tag profile with information
962 if (!SetTextTags(hICC, L"Named color devicelink")) goto Error;
963
964 Original = cmsGetNamedColorList(xform);
965 if (Original == NULL) goto Error;
966
967 nColors = cmsNamedColorCount(Original);
968 nc2 = cmsDupNamedColorList(Original);
969 if (nc2 == NULL) goto Error;
970
971 // Colorant count now depends on the output space
972 nc2 ->ColorantCount = cmsPipelineOutputChannels(v ->Lut);
973
974 // Make sure we have proper formatters
975 cmsChangeBuffersFormat(xform, TYPE_NAMED_COLOR_INDEX,
976 FLOAT_SH(0) | COLORSPACE_SH(_cmsLCMScolorSpace(v ->ExitColorSpace))
977 | BYTES_SH(2) | CHANNELS_SH(cmsChannelsOfColorSpace(v ->ExitColorSpace)));
978
979 // Apply the transfor to colorants.
980 for (i=0; i < nColors; i++) {
981 cmsDoTransform(xform, &i, nc2 ->List[i].DeviceColorant, 1);
982 }
983
984 if (!cmsWriteTag(hICC, cmsSigNamedColor2Tag, (void*) nc2)) goto Error;
985 cmsFreeNamedColorList(nc2);
986
987 return hICC;
988
989 Error:
990 if (hICC != NULL) cmsCloseProfile(hICC);
991 return NULL;
992 }
993
994
995 // This structure holds information about which MPU can be stored on a profile based on the version
996
997 typedef struct {
998 cmsBool IsV4; // Is a V4 tag?
999 cmsTagSignature RequiredTag; // Set to 0 for both types
1000 cmsTagTypeSignature LutType; // The LUT type
1001 int nTypes; // Number of types (up to 5)
1002 cmsStageSignature MpeTypes[5]; // 5 is the maximum number
1003
1004 } cmsAllowedLUT;
1005
1006 #define cmsSig0 ((cmsTagSignature) 0)
1007
1008 static const cmsAllowedLUT AllowedLUTTypes[] = {
1009
1010 { FALSE, cmsSig0, cmsSigLut16Type, 4, { cmsSigMatrixElemType, cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType } },
1011 { FALSE, cmsSig0, cmsSigLut16Type, 3, { cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType } },
1012 { FALSE, cmsSig0, cmsSigLut16Type, 2, { cmsSigCurveSetElemType, cmsSigCLutElemType } },
1013 { TRUE, cmsSig0, cmsSigLutAtoBType, 1, { cmsSigCurveSetElemType } },
1014 { TRUE , cmsSigAToB0Tag, cmsSigLutAtoBType, 3, { cmsSigCurveSetElemType, cmsSigMatrixElemType, cmsSigCurveSetElemType } },
1015 { TRUE , cmsSigAToB0Tag, cmsSigLutAtoBType, 3, { cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType } },
1016 { TRUE , cmsSigAToB0Tag, cmsSigLutAtoBType, 5, { cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType, cmsSigMatrixElemType, cmsSigCurveSetElemType }},
1017 { TRUE , cmsSigBToA0Tag, cmsSigLutBtoAType, 1, { cmsSigCurveSetElemType }},
1018 { TRUE , cmsSigBToA0Tag, cmsSigLutBtoAType, 3, { cmsSigCurveSetElemType, cmsSigMatrixElemType, cmsSigCurveSetElemType }},
1019 { TRUE , cmsSigBToA0Tag, cmsSigLutBtoAType, 3, { cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType }},
1020 { TRUE , cmsSigBToA0Tag, cmsSigLutBtoAType, 5, { cmsSigCurveSetElemType, cmsSigMatrixElemType, cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType }}
1021 };
1022
1023 #define SIZE_OF_ALLOWED_LUT (sizeof(AllowedLUTTypes)/sizeof(cmsAllowedLUT))
1024
1025 // Check a single entry
1026 static
CheckOne(const cmsAllowedLUT * Tab,const cmsPipeline * Lut)1027 cmsBool CheckOne(const cmsAllowedLUT* Tab, const cmsPipeline* Lut)
1028 {
1029 cmsStage* mpe;
1030 int n;
1031
1032 for (n=0, mpe = Lut ->Elements; mpe != NULL; mpe = mpe ->Next, n++) {
1033
1034 if (n > Tab ->nTypes) return FALSE;
1035 if (cmsStageType(mpe) != Tab ->MpeTypes[n]) return FALSE;
1036 }
1037
1038 return (n == Tab ->nTypes);
1039 }
1040
1041
1042 static
FindCombination(const cmsPipeline * Lut,cmsBool IsV4,cmsTagSignature DestinationTag)1043 const cmsAllowedLUT* FindCombination(const cmsPipeline* Lut, cmsBool IsV4, cmsTagSignature DestinationTag)
1044 {
1045 cmsUInt32Number n;
1046
1047 for (n=0; n < SIZE_OF_ALLOWED_LUT; n++) {
1048
1049 const cmsAllowedLUT* Tab = AllowedLUTTypes + n;
1050
1051 if (IsV4 ^ Tab -> IsV4) continue;
1052 if ((Tab ->RequiredTag != 0) && (Tab ->RequiredTag != DestinationTag)) continue;
1053
1054 if (CheckOne(Tab, Lut)) return Tab;
1055 }
1056
1057 return NULL;
1058 }
1059
1060
1061 // Does convert a transform into a device link profile
cmsTransform2DeviceLink(cmsHTRANSFORM hTransform,cmsFloat64Number Version,cmsUInt32Number dwFlags)1062 cmsHPROFILE CMSEXPORT cmsTransform2DeviceLink(cmsHTRANSFORM hTransform, cmsFloat64Number Version, cmsUInt32Number dwFlags)
1063 {
1064 cmsHPROFILE hProfile = NULL;
1065 cmsUInt32Number FrmIn, FrmOut;
1066 cmsInt32Number ChansIn, ChansOut;
1067 int ColorSpaceBitsIn, ColorSpaceBitsOut;
1068 _cmsTRANSFORM* xform = (_cmsTRANSFORM*) hTransform;
1069 cmsPipeline* LUT = NULL;
1070 cmsStage* mpe;
1071 cmsContext ContextID = cmsGetTransformContextID(hTransform);
1072 const cmsAllowedLUT* AllowedLUT;
1073 cmsTagSignature DestinationTag;
1074 cmsProfileClassSignature deviceClass;
1075
1076 _cmsAssert(hTransform != NULL);
1077
1078 // Get the first mpe to check for named color
1079 mpe = cmsPipelineGetPtrToFirstStage(xform ->Lut);
1080
1081 // Check if is a named color transform
1082 if (mpe != NULL) {
1083
1084 if (cmsStageType(mpe) == cmsSigNamedColorElemType) {
1085 return CreateNamedColorDevicelink(hTransform);
1086 }
1087 }
1088
1089 // First thing to do is to get a copy of the transformation
1090 LUT = cmsPipelineDup(xform ->Lut);
1091 if (LUT == NULL) return NULL;
1092
1093 // Time to fix the Lab2/Lab4 issue.
1094 if ((xform ->EntryColorSpace == cmsSigLabData) && (Version < 4.0)) {
1095
1096 if (!cmsPipelineInsertStage(LUT, cmsAT_BEGIN, _cmsStageAllocLabV2ToV4curves(ContextID)))
1097 goto Error;
1098 }
1099
1100 // On the output side too. Note that due to V2/V4 PCS encoding on lab we cannot fix white misalignments
1101 if ((xform ->ExitColorSpace) == cmsSigLabData && (Version < 4.0)) {
1102
1103 dwFlags |= cmsFLAGS_NOWHITEONWHITEFIXUP;
1104 if (!cmsPipelineInsertStage(LUT, cmsAT_END, _cmsStageAllocLabV4ToV2(ContextID)))
1105 goto Error;
1106 }
1107
1108
1109 hProfile = cmsCreateProfilePlaceholder(ContextID);
1110 if (!hProfile) goto Error; // can't allocate
1111
1112 cmsSetProfileVersion(hProfile, Version);
1113
1114 FixColorSpaces(hProfile, xform -> EntryColorSpace, xform -> ExitColorSpace, dwFlags);
1115
1116 // Optimize the LUT and precalculate a devicelink
1117
1118 ChansIn = cmsChannelsOfColorSpace(xform -> EntryColorSpace);
1119 ChansOut = cmsChannelsOfColorSpace(xform -> ExitColorSpace);
1120
1121 ColorSpaceBitsIn = _cmsLCMScolorSpace(xform -> EntryColorSpace);
1122 ColorSpaceBitsOut = _cmsLCMScolorSpace(xform -> ExitColorSpace);
1123
1124 FrmIn = COLORSPACE_SH(ColorSpaceBitsIn) | CHANNELS_SH(ChansIn)|BYTES_SH(2);
1125 FrmOut = COLORSPACE_SH(ColorSpaceBitsOut) | CHANNELS_SH(ChansOut)|BYTES_SH(2);
1126
1127 deviceClass = cmsGetDeviceClass(hProfile);
1128
1129 if (deviceClass == cmsSigOutputClass)
1130 DestinationTag = cmsSigBToA0Tag;
1131 else
1132 DestinationTag = cmsSigAToB0Tag;
1133
1134 // Check if the profile/version can store the result
1135 if (dwFlags & cmsFLAGS_FORCE_CLUT)
1136 AllowedLUT = NULL;
1137 else
1138 AllowedLUT = FindCombination(LUT, Version >= 4.0, DestinationTag);
1139
1140 if (AllowedLUT == NULL) {
1141
1142 // Try to optimize
1143 _cmsOptimizePipeline(ContextID, &LUT, xform ->RenderingIntent, &FrmIn, &FrmOut, &dwFlags);
1144 AllowedLUT = FindCombination(LUT, Version >= 4.0, DestinationTag);
1145
1146 }
1147
1148 // If no way, then force CLUT that for sure can be written
1149 if (AllowedLUT == NULL) {
1150
1151 cmsStage* FirstStage;
1152 cmsStage* LastStage;
1153
1154 dwFlags |= cmsFLAGS_FORCE_CLUT;
1155 _cmsOptimizePipeline(ContextID, &LUT, xform ->RenderingIntent, &FrmIn, &FrmOut, &dwFlags);
1156
1157 // Put identity curves if needed
1158 FirstStage = cmsPipelineGetPtrToFirstStage(LUT);
1159 if (FirstStage != NULL && FirstStage ->Type != cmsSigCurveSetElemType)
1160 if (!cmsPipelineInsertStage(LUT, cmsAT_BEGIN, _cmsStageAllocIdentityCurves(ContextID, ChansIn)))
1161 goto Error;
1162
1163 LastStage = cmsPipelineGetPtrToLastStage(LUT);
1164 if (LastStage != NULL && LastStage ->Type != cmsSigCurveSetElemType)
1165 if (!cmsPipelineInsertStage(LUT, cmsAT_END, _cmsStageAllocIdentityCurves(ContextID, ChansOut)))
1166 goto Error;
1167
1168 AllowedLUT = FindCombination(LUT, Version >= 4.0, DestinationTag);
1169 }
1170
1171 // Somethings is wrong...
1172 if (AllowedLUT == NULL) {
1173 goto Error;
1174 }
1175
1176
1177 if (dwFlags & cmsFLAGS_8BITS_DEVICELINK)
1178 cmsPipelineSetSaveAs8bitsFlag(LUT, TRUE);
1179
1180 // Tag profile with information
1181 if (!SetTextTags(hProfile, L"devicelink")) goto Error;
1182
1183 // Store result
1184 if (!cmsWriteTag(hProfile, DestinationTag, LUT)) goto Error;
1185
1186
1187 if (xform -> InputColorant != NULL) {
1188 if (!cmsWriteTag(hProfile, cmsSigColorantTableTag, xform->InputColorant)) goto Error;
1189 }
1190
1191 if (xform -> OutputColorant != NULL) {
1192 if (!cmsWriteTag(hProfile, cmsSigColorantTableOutTag, xform->OutputColorant)) goto Error;
1193 }
1194
1195 if ((deviceClass == cmsSigLinkClass) && (xform ->Sequence != NULL)) {
1196 if (!_cmsWriteProfileSequence(hProfile, xform ->Sequence)) goto Error;
1197 }
1198
1199 // Set the white point
1200 if (deviceClass == cmsSigInputClass) {
1201 if (!cmsWriteTag(hProfile, cmsSigMediaWhitePointTag, &xform ->EntryWhitePoint)) goto Error;
1202 }
1203 else {
1204 if (!cmsWriteTag(hProfile, cmsSigMediaWhitePointTag, &xform ->ExitWhitePoint)) goto Error;
1205 }
1206
1207
1208 // Per 7.2.15 in spec 4.3
1209 cmsSetHeaderRenderingIntent(hProfile, xform ->RenderingIntent);
1210
1211 cmsPipelineFree(LUT);
1212 return hProfile;
1213
1214 Error:
1215 if (LUT != NULL) cmsPipelineFree(LUT);
1216 cmsCloseProfile(hProfile);
1217 return NULL;
1218 }
1219