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