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