1ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov//---------------------------------------------------------------------------------
2ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov//
3ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov//  Little Color Management System
4ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov//  Copyright (c) 1998-2012 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// Read tags using low-level functions, provides necessary glue code to adapt versions, etc.
30ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
31ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// LUT tags
32ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganovstatic const cmsTagSignature Device2PCS16[]   =  {cmsSigAToB0Tag,     // Perceptual
33ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov                                                  cmsSigAToB1Tag,     // Relative colorimetric
34ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov                                                  cmsSigAToB2Tag,     // Saturation
35ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov                                                  cmsSigAToB1Tag };   // Absolute colorimetric
36ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
37ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganovstatic const cmsTagSignature Device2PCSFloat[] = {cmsSigDToB0Tag,     // Perceptual
38ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov                                                  cmsSigDToB1Tag,     // Relative colorimetric
39ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov                                                  cmsSigDToB2Tag,     // Saturation
40ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov                                                  cmsSigDToB3Tag };   // Absolute colorimetric
41ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
42ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganovstatic const cmsTagSignature PCS2Device16[]    = {cmsSigBToA0Tag,     // Perceptual
43ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov                                                  cmsSigBToA1Tag,     // Relative colorimetric
44ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov                                                  cmsSigBToA2Tag,     // Saturation
45ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov                                                  cmsSigBToA1Tag };   // Absolute colorimetric
46ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
47ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganovstatic const cmsTagSignature PCS2DeviceFloat[] = {cmsSigBToD0Tag,     // Perceptual
48ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov                                                  cmsSigBToD1Tag,     // Relative colorimetric
49ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov                                                  cmsSigBToD2Tag,     // Saturation
50ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov                                                  cmsSigBToD3Tag };   // Absolute colorimetric
51ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
52ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
53ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// Factors to convert from 1.15 fixed point to 0..1.0 range and vice-versa
54ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov#define InpAdj   (1.0/MAX_ENCODEABLE_XYZ)     // (65536.0/(65535.0*2.0))
55ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov#define OutpAdj  (MAX_ENCODEABLE_XYZ)         // ((2.0*65535.0)/65536.0)
56ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
57ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// Several resources for gray conversions.
58ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganovstatic const cmsFloat64Number GrayInputMatrix[] = { (InpAdj*cmsD50X),  (InpAdj*cmsD50Y),  (InpAdj*cmsD50Z) };
59ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganovstatic const cmsFloat64Number OneToThreeInputMatrix[] = { 1, 1, 1 };
60ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganovstatic const cmsFloat64Number PickYMatrix[] = { 0, (OutpAdj*cmsD50Y), 0 };
61ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganovstatic const cmsFloat64Number PickLstarMatrix[] = { 1, 0, 0 };
62ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
63ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// Get a media white point fixing some issues found in certain old profiles
64ee451cb395940862dad63c85adfe8f2fd55e864cSvet GanovcmsBool  _cmsReadMediaWhitePoint(cmsCIEXYZ* Dest, cmsHPROFILE hProfile)
65ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov{
66ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    cmsCIEXYZ* Tag;
67ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
68ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    _cmsAssert(Dest != NULL);
69ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
70ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    Tag = (cmsCIEXYZ*) cmsReadTag(hProfile, cmsSigMediaWhitePointTag);
71ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
72ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    // If no wp, take D50
73ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    if (Tag == NULL) {
74ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        *Dest = *cmsD50_XYZ();
75ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        return TRUE;
76ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    }
77ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
78ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    // V2 display profiles should give D50
79ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    if (cmsGetEncodedICCversion(hProfile) < 0x4000000) {
80ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
81ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        if (cmsGetDeviceClass(hProfile) == cmsSigDisplayClass) {
82ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            *Dest = *cmsD50_XYZ();
83ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            return TRUE;
84ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        }
85ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    }
86ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
87ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    // All seems ok
88ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    *Dest = *Tag;
89ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    return TRUE;
90ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov}
91ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
92ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
93ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// Chromatic adaptation matrix. Fix some issues as well
94ee451cb395940862dad63c85adfe8f2fd55e864cSvet GanovcmsBool  _cmsReadCHAD(cmsMAT3* Dest, cmsHPROFILE hProfile)
95ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov{
96ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    cmsMAT3* Tag;
97ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
98ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    _cmsAssert(Dest != NULL);
99ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
100ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    Tag = (cmsMAT3*) cmsReadTag(hProfile, cmsSigChromaticAdaptationTag);
101ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
102ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    if (Tag != NULL) {
103ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        *Dest = *Tag;
104ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        return TRUE;
105ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    }
106ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
107ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    // No CHAD available, default it to identity
108ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    _cmsMAT3identity(Dest);
109ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
110ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    // V2 display profiles should give D50
111ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    if (cmsGetEncodedICCversion(hProfile) < 0x4000000) {
112ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
113ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        if (cmsGetDeviceClass(hProfile) == cmsSigDisplayClass) {
114ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
115ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            cmsCIEXYZ* White = (cmsCIEXYZ*) cmsReadTag(hProfile, cmsSigMediaWhitePointTag);
116ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
117ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            if (White == NULL) {
118ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
119ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov                _cmsMAT3identity(Dest);
120ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov                return TRUE;
121ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            }
122ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
123ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            return _cmsAdaptationMatrix(Dest, NULL, White, cmsD50_XYZ());
124ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        }
125ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    }
126ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
127ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    return TRUE;
128ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov}
129ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
130ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
131ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// Auxiliar, read colorants as a MAT3 structure. Used by any function that needs a matrix-shaper
132ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganovstatic
133ee451cb395940862dad63c85adfe8f2fd55e864cSvet GanovcmsBool ReadICCMatrixRGB2XYZ(cmsMAT3* r, cmsHPROFILE hProfile)
134ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov{
135ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    cmsCIEXYZ *PtrRed, *PtrGreen, *PtrBlue;
136ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
137ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    _cmsAssert(r != NULL);
138ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
139ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    PtrRed   = (cmsCIEXYZ *) cmsReadTag(hProfile, cmsSigRedColorantTag);
140ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    PtrGreen = (cmsCIEXYZ *) cmsReadTag(hProfile, cmsSigGreenColorantTag);
141ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    PtrBlue  = (cmsCIEXYZ *) cmsReadTag(hProfile, cmsSigBlueColorantTag);
142ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
143ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    if (PtrRed == NULL || PtrGreen == NULL || PtrBlue == NULL)
144ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        return FALSE;
145ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
146ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    _cmsVEC3init(&r -> v[0], PtrRed -> X, PtrGreen -> X,  PtrBlue -> X);
147ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    _cmsVEC3init(&r -> v[1], PtrRed -> Y, PtrGreen -> Y,  PtrBlue -> Y);
148ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    _cmsVEC3init(&r -> v[2], PtrRed -> Z, PtrGreen -> Z,  PtrBlue -> Z);
149ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
150ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    return TRUE;
151ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov}
152ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
153ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
154ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// Gray input pipeline
155ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganovstatic
156ee451cb395940862dad63c85adfe8f2fd55e864cSvet GanovcmsPipeline* BuildGrayInputMatrixPipeline(cmsHPROFILE hProfile)
157ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov{
158ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    cmsToneCurve *GrayTRC;
159ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    cmsPipeline* Lut;
160ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    cmsContext ContextID = cmsGetProfileContextID(hProfile);
161ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
162ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    GrayTRC = (cmsToneCurve *) cmsReadTag(hProfile, cmsSigGrayTRCTag);
163ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    if (GrayTRC == NULL) return NULL;
164ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
165ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    Lut = cmsPipelineAlloc(ContextID, 1, 3);
166ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    if (Lut == NULL)
167ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        goto Error;
168ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
169ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    if (cmsGetPCS(hProfile) == cmsSigLabData) {
170ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
171ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        // In this case we implement the profile as an  identity matrix plus 3 tone curves
172ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        cmsUInt16Number Zero[2] = { 0x8080, 0x8080 };
173ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        cmsToneCurve* EmptyTab;
174ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        cmsToneCurve* LabCurves[3];
175ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
176ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        EmptyTab = cmsBuildTabulatedToneCurve16(ContextID, 2, Zero);
177ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
178ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        if (EmptyTab == NULL)
179ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            goto Error;
180ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
181ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        LabCurves[0] = GrayTRC;
182ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        LabCurves[1] = EmptyTab;
183ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        LabCurves[2] = EmptyTab;
184ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
185ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        if (!cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocMatrix(ContextID, 3,  1, OneToThreeInputMatrix, NULL)) ||
186ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            !cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocToneCurves(ContextID, 3, LabCurves))) {
187ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov                cmsFreeToneCurve(EmptyTab);
188ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov                goto Error;
189ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        }
190ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
191ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        cmsFreeToneCurve(EmptyTab);
192ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
193ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    }
194ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    else  {
195ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
196ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        if (!cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocToneCurves(ContextID, 1, &GrayTRC)) ||
197ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            !cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocMatrix(ContextID, 3,  1, GrayInputMatrix, NULL)))
198ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            goto Error;
199ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    }
200ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
201ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    return Lut;
202ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
203ee451cb395940862dad63c85adfe8f2fd55e864cSvet GanovError:
204ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov	// memory pointed by GrayTRC is not a new malloc memory, so don't free it here,
205ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov	// memory pointed by GrayTRC will be freed when hProfile is closed.
206ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov	// test file :0047776_Pocket Medicine_ The Massachusetts General Hospital Handbook of Internal Medicine-2.pdf
207ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov	// Xiaochuan Liu, 20140421
208ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    //cmsFreeToneCurve(GrayTRC);
209ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    cmsPipelineFree(Lut);
210ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    return NULL;
211ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov}
212ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
213ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// RGB Matrix shaper
214ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganovstatic
215ee451cb395940862dad63c85adfe8f2fd55e864cSvet GanovcmsPipeline* BuildRGBInputMatrixShaper(cmsHPROFILE hProfile)
216ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov{
217ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    cmsPipeline* Lut;
218ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    cmsMAT3 Mat;
219ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    cmsToneCurve *Shapes[3];
220ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    cmsContext ContextID = cmsGetProfileContextID(hProfile);
221ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    int i, j;
222ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
223ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    if (!ReadICCMatrixRGB2XYZ(&Mat, hProfile)) return NULL;
224ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
225ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    // XYZ PCS in encoded in 1.15 format, and the matrix output comes in 0..0xffff range, so
226ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    // we need to adjust the output by a factor of (0x10000/0xffff) to put data in
227ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    // a 1.16 range, and then a >> 1 to obtain 1.15. The total factor is (65536.0)/(65535.0*2)
228ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
229ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    for (i=0; i < 3; i++)
230ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        for (j=0; j < 3; j++)
231ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            Mat.v[i].n[j] *= InpAdj;
232ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
233ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
234ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    Shapes[0] = (cmsToneCurve *) cmsReadTag(hProfile, cmsSigRedTRCTag);
235ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    Shapes[1] = (cmsToneCurve *) cmsReadTag(hProfile, cmsSigGreenTRCTag);
236ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    Shapes[2] = (cmsToneCurve *) cmsReadTag(hProfile, cmsSigBlueTRCTag);
237ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
238ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    if (!Shapes[0] || !Shapes[1] || !Shapes[2])
239ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        return NULL;
240ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
241ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    Lut = cmsPipelineAlloc(ContextID, 3, 3);
242ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    if (Lut != NULL) {
243ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
244ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        if (!cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocToneCurves(ContextID, 3, Shapes)) ||
245ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            !cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocMatrix(ContextID, 3, 3, (cmsFloat64Number*) &Mat, NULL)))
246ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            goto Error;
247ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
248ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        // Note that it is certainly possible a single profile would have a LUT based
249ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        // tag for output working in lab and a matrix-shaper for the fallback cases.
250ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        // This is not allowed by the spec, but this code is tolerant to those cases
251ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        if (cmsGetPCS(hProfile) == cmsSigLabData) {
252ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
253ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            if (!cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageAllocXYZ2Lab(ContextID)))
254ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov                goto Error;
255ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        }
256ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
257ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    }
258ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
259ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    return Lut;
260ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
261ee451cb395940862dad63c85adfe8f2fd55e864cSvet GanovError:
262ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    cmsPipelineFree(Lut);
263ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    return NULL;
264ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov}
265ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
266ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
267ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
268ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// Read the DToAX tag, adjusting the encoding of Lab or XYZ if neded
269ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganovstatic
270ee451cb395940862dad63c85adfe8f2fd55e864cSvet GanovcmsPipeline* _cmsReadFloatInputTag(cmsHPROFILE hProfile, cmsTagSignature tagFloat)
271ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov{
272ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    cmsContext ContextID       = cmsGetProfileContextID(hProfile);
273ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    cmsPipeline* Lut           = cmsPipelineDup((cmsPipeline*) cmsReadTag(hProfile, tagFloat));
274ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    cmsColorSpaceSignature spc = cmsGetColorSpace(hProfile);
275ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    cmsColorSpaceSignature PCS = cmsGetPCS(hProfile);
276ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
277ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    if (Lut == NULL) return NULL;
278ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
279ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    // input and output of transform are in lcms 0..1 encoding.  If XYZ or Lab spaces are used,
280ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    //  these need to be normalized into the appropriate ranges (Lab = 100,0,0, XYZ=1.0,1.0,1.0)
281ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    if ( spc == cmsSigLabData)
282ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    {
283ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        if (!cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageNormalizeToLabFloat(ContextID)))
284ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            goto Error;
285ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    }
286ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    else if (spc == cmsSigXYZData)
287ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    {
288ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        if (!cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageNormalizeToXyzFloat(ContextID)))
289ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            goto Error;
290ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    }
291ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
292ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    if ( PCS == cmsSigLabData)
293ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    {
294ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        if (!cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageNormalizeFromLabFloat(ContextID)))
295ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            goto Error;
296ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    }
297ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    else if( PCS == cmsSigXYZData)
298ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    {
299ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        if (!cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageNormalizeFromXyzFloat(ContextID)))
300ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            goto Error;
301ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    }
302ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
303ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    return Lut;
304ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
305ee451cb395940862dad63c85adfe8f2fd55e864cSvet GanovError:
306ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    cmsPipelineFree(Lut);
307ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    return NULL;
308ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov}
309ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
310ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
311ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// Read and create a BRAND NEW MPE LUT from a given profile. All stuff dependent of version, etc
312ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// is adjusted here in order to create a LUT that takes care of all those details.
313ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// We add intent = -1 as a way to read matrix shaper always, no matter of other LUT
314ee451cb395940862dad63c85adfe8f2fd55e864cSvet GanovcmsPipeline* _cmsReadInputLUT(cmsHPROFILE hProfile, int Intent)
315ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov{
316ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    cmsTagTypeSignature OriginalType;
317ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    cmsTagSignature tag16    = Device2PCS16[Intent];
318ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    cmsTagSignature tagFloat = Device2PCSFloat[Intent];
319ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    cmsContext ContextID = cmsGetProfileContextID(hProfile);
320ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
321ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    // On named color, take the appropiate tag
322ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    if (cmsGetDeviceClass(hProfile) == cmsSigNamedColorClass) {
323ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
324ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        cmsPipeline* Lut;
325ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        cmsNAMEDCOLORLIST* nc = (cmsNAMEDCOLORLIST*) cmsReadTag(hProfile, cmsSigNamedColor2Tag);
326ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
327ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        if (nc == NULL) return NULL;
328ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
329ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        Lut = cmsPipelineAlloc(ContextID, 0, 0);
330ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        if (Lut == NULL) {
331ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            cmsFreeNamedColorList(nc);
332ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            return NULL;
333ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        }
334ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
335ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        if (!cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageAllocNamedColor(nc, TRUE)) ||
336ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            !cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageAllocLabV2ToV4(ContextID))) {
337ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            cmsPipelineFree(Lut);
338ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            return NULL;
339ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        }
340ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        return Lut;
341ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    }
342ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
343ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    // This is an attempt to reuse this funtion to retrieve the matrix-shaper as pipeline no
344ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    // matter other LUT are present and have precedence. Intent = -1 means just this.
345ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    if (Intent != -1) {
346ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
347ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        if (cmsIsTag(hProfile, tagFloat)) {  // Float tag takes precedence
348ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
349ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            // Floating point LUT are always V4, but the encoding range is no
350ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            // longer 0..1.0, so we need to add an stage depending on the color space
351ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            return _cmsReadFloatInputTag(hProfile, tagFloat);
352ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        }
353ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
354ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        // Revert to perceptual if no tag is found
355ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        if (!cmsIsTag(hProfile, tag16)) {
356ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            tag16 = Device2PCS16[0];
357ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        }
358ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
359ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        if (cmsIsTag(hProfile, tag16)) { // Is there any LUT-Based table?
360ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
361ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            // Check profile version and LUT type. Do the necessary adjustments if needed
362ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
363ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            // First read the tag
364ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            cmsPipeline* Lut = (cmsPipeline*) cmsReadTag(hProfile, tag16);
365ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            if (Lut == NULL) return NULL;
366ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
367ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            // After reading it, we have now info about the original type
368ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            OriginalType =  _cmsGetTagTrueType(hProfile, tag16);
369ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
370ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            // The profile owns the Lut, so we need to copy it
371ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            Lut = cmsPipelineDup(Lut);
372ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
373ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            // We need to adjust data only for Lab16 on output
374ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            if (OriginalType != cmsSigLut16Type || cmsGetPCS(hProfile) != cmsSigLabData)
375ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov                return Lut;
376ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
377ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            // If the input is Lab, add also a conversion at the begin
378ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            if (cmsGetColorSpace(hProfile) == cmsSigLabData &&
379ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov                !cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageAllocLabV4ToV2(ContextID)))
380ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov                goto Error;
381ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
382ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            // Add a matrix for conversion V2 to V4 Lab PCS
383ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            if (!cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageAllocLabV2ToV4(ContextID)))
384ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov                goto Error;
385ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
386ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            return Lut;
387ee451cb395940862dad63c85adfe8f2fd55e864cSvet GanovError:
388ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            cmsPipelineFree(Lut);
389ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            return NULL;
390ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        }
391ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    }
392ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
393ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    // Lut was not found, try to create a matrix-shaper
394ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
395ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    // Check if this is a grayscale profile.
396ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    if (cmsGetColorSpace(hProfile) == cmsSigGrayData) {
397ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
398ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        // if so, build appropiate conversion tables.
399ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        // The tables are the PCS iluminant, scaled across GrayTRC
400ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        return BuildGrayInputMatrixPipeline(hProfile);
401ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    }
402ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
403ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    // Not gray, create a normal matrix-shaper
404ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    return BuildRGBInputMatrixShaper(hProfile);
405ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov}
406ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
407ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// ---------------------------------------------------------------------------------------------------------------
408ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
409ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// Gray output pipeline.
410ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// XYZ -> Gray or Lab -> Gray. Since we only know the GrayTRC, we need to do some assumptions. Gray component will be
411ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// given by Y on XYZ PCS and by L* on Lab PCS, Both across inverse TRC curve.
412ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// The complete pipeline on XYZ is Matrix[3:1] -> Tone curve and in Lab Matrix[3:1] -> Tone Curve as well.
413ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
414ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganovstatic
415ee451cb395940862dad63c85adfe8f2fd55e864cSvet GanovcmsPipeline* BuildGrayOutputPipeline(cmsHPROFILE hProfile)
416ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov{
417ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    cmsToneCurve *GrayTRC, *RevGrayTRC;
418ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    cmsPipeline* Lut;
419ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    cmsContext ContextID = cmsGetProfileContextID(hProfile);
420ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
421ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    GrayTRC = (cmsToneCurve *) cmsReadTag(hProfile, cmsSigGrayTRCTag);
422ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    if (GrayTRC == NULL) return NULL;
423ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
424ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    RevGrayTRC = cmsReverseToneCurve(GrayTRC);
425ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    if (RevGrayTRC == NULL) return NULL;
426ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
427ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    Lut = cmsPipelineAlloc(ContextID, 3, 1);
428ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    if (Lut == NULL) {
429ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        cmsFreeToneCurve(RevGrayTRC);
430ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        return NULL;
431ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    }
432ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
433ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    if (cmsGetPCS(hProfile) == cmsSigLabData) {
434ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
435ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        if (!cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocMatrix(ContextID, 1,  3, PickLstarMatrix, NULL)))
436ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            goto Error;
437ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    }
438ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    else  {
439ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        if (!cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocMatrix(ContextID, 1,  3, PickYMatrix, NULL)))
440ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            goto Error;
441ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    }
442ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
443ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    if (!cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocToneCurves(ContextID, 1, &RevGrayTRC)))
444ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        goto Error;
445ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
446ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    cmsFreeToneCurve(RevGrayTRC);
447ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    return Lut;
448ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
449ee451cb395940862dad63c85adfe8f2fd55e864cSvet GanovError:
450ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    cmsFreeToneCurve(RevGrayTRC);
451ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    cmsPipelineFree(Lut);
452ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    return NULL;
453ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov}
454ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
455ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
456ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganovstatic
457ee451cb395940862dad63c85adfe8f2fd55e864cSvet GanovcmsPipeline* BuildRGBOutputMatrixShaper(cmsHPROFILE hProfile)
458ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov{
459ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    cmsPipeline* Lut;
460ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    cmsToneCurve *Shapes[3], *InvShapes[3];
461ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    cmsMAT3 Mat, Inv;
462ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    int i, j;
463ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    cmsContext ContextID = cmsGetProfileContextID(hProfile);
464ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
465ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    if (!ReadICCMatrixRGB2XYZ(&Mat, hProfile))
466ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        return NULL;
467ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
468ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    if (!_cmsMAT3inverse(&Mat, &Inv))
469ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        return NULL;
470ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
471ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    // XYZ PCS in encoded in 1.15 format, and the matrix input should come in 0..0xffff range, so
472ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    // we need to adjust the input by a << 1 to obtain a 1.16 fixed and then by a factor of
473ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    // (0xffff/0x10000) to put data in 0..0xffff range. Total factor is (2.0*65535.0)/65536.0;
474ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
475ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    for (i=0; i < 3; i++)
476ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        for (j=0; j < 3; j++)
477ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            Inv.v[i].n[j] *= OutpAdj;
478ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
479ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    Shapes[0] = (cmsToneCurve *) cmsReadTag(hProfile, cmsSigRedTRCTag);
480ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    Shapes[1] = (cmsToneCurve *) cmsReadTag(hProfile, cmsSigGreenTRCTag);
481ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    Shapes[2] = (cmsToneCurve *) cmsReadTag(hProfile, cmsSigBlueTRCTag);
482ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
483ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    if (!Shapes[0] || !Shapes[1] || !Shapes[2])
484ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        return NULL;
485ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
486ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    InvShapes[0] = cmsReverseToneCurve(Shapes[0]);
487ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    InvShapes[1] = cmsReverseToneCurve(Shapes[1]);
488ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    InvShapes[2] = cmsReverseToneCurve(Shapes[2]);
489ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
490ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    if (!InvShapes[0] || !InvShapes[1] || !InvShapes[2]) {
491ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        return NULL;
492ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    }
493ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
494ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    Lut = cmsPipelineAlloc(ContextID, 3, 3);
495ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    if (Lut != NULL) {
496ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
497ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        // Note that it is certainly possible a single profile would have a LUT based
498ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        // tag for output working in lab and a matrix-shaper for the fallback cases.
499ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        // This is not allowed by the spec, but this code is tolerant to those cases
500ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        if (cmsGetPCS(hProfile) == cmsSigLabData) {
501ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
502ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            if (!cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageAllocLab2XYZ(ContextID)))
503ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov                goto Error;
504ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        }
505ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
506ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        if (!cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocMatrix(ContextID, 3, 3, (cmsFloat64Number*) &Inv, NULL)) ||
507ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            !cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocToneCurves(ContextID, 3, InvShapes)))
508ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            goto Error;
509ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    }
510ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
511ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    cmsFreeToneCurveTriple(InvShapes);
512ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    return Lut;
513ee451cb395940862dad63c85adfe8f2fd55e864cSvet GanovError:
514ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    cmsFreeToneCurveTriple(InvShapes);
515ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    cmsPipelineFree(Lut);
516ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    return NULL;
517ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov}
518ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
519ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
520ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// Change CLUT interpolation to trilinear
521ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganovstatic
522ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganovvoid ChangeInterpolationToTrilinear(cmsPipeline* Lut)
523ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov{
524ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    cmsStage* Stage;
525ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
526ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    for (Stage = cmsPipelineGetPtrToFirstStage(Lut);
527ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        Stage != NULL;
528ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        Stage = cmsStageNext(Stage)) {
529ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
530ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            if (cmsStageType(Stage) == cmsSigCLutElemType) {
531ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
532ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov                _cmsStageCLutData* CLUT = (_cmsStageCLutData*) Stage ->Data;
533ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
534ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov                CLUT ->Params->dwFlags |= CMS_LERP_FLAGS_TRILINEAR;
535ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov                _cmsSetInterpolationRoutine(Lut->ContextID, CLUT ->Params);
536ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            }
537ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    }
538ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov}
539ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
540ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
541ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// Read the DToAX tag, adjusting the encoding of Lab or XYZ if neded
542ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganovstatic
543ee451cb395940862dad63c85adfe8f2fd55e864cSvet GanovcmsPipeline* _cmsReadFloatOutputTag(cmsHPROFILE hProfile, cmsTagSignature tagFloat)
544ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov{
545ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    cmsContext ContextID       = cmsGetProfileContextID(hProfile);
546ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    cmsPipeline* Lut           = cmsPipelineDup((cmsPipeline*) cmsReadTag(hProfile, tagFloat));
547ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    cmsColorSpaceSignature PCS = cmsGetPCS(hProfile);
548ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    cmsColorSpaceSignature dataSpace = cmsGetColorSpace(hProfile);
549ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
550ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    if (Lut == NULL) return NULL;
551ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
552ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    // If PCS is Lab or XYZ, the floating point tag is accepting data in the space encoding,
553ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    // and since the formatter has already accomodated to 0..1.0, we should undo this change
554ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    if ( PCS == cmsSigLabData)
555ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    {
556ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        if (!cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageNormalizeToLabFloat(ContextID)))
557ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            goto Error;
558ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    }
559ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    else
560ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        if (PCS == cmsSigXYZData)
561ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        {
562ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            if (!cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageNormalizeToXyzFloat(ContextID)))
563ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov                goto Error;
564ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        }
565ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
566ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    // the output can be Lab or XYZ, in which case normalisation is needed on the end of the pipeline
567ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    if ( dataSpace == cmsSigLabData)
568ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    {
569ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        if (!cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageNormalizeFromLabFloat(ContextID)))
570ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            goto Error;
571ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    }
572ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    else if (dataSpace == cmsSigXYZData)
573ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    {
574ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        if (!cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageNormalizeFromXyzFloat(ContextID)))
575ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            goto Error;
576ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    }
577ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
578ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    return Lut;
579ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
580ee451cb395940862dad63c85adfe8f2fd55e864cSvet GanovError:
581ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    cmsPipelineFree(Lut);
582ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    return NULL;
583ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov}
584ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
585ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// Create an output MPE LUT from agiven profile. Version mismatches are handled here
586ee451cb395940862dad63c85adfe8f2fd55e864cSvet GanovcmsPipeline* _cmsReadOutputLUT(cmsHPROFILE hProfile, int Intent)
587ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov{
588ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    cmsTagTypeSignature OriginalType;
589ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    cmsTagSignature tag16    = PCS2Device16[Intent];
590ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    cmsTagSignature tagFloat = PCS2DeviceFloat[Intent];
591ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    cmsContext ContextID     = cmsGetProfileContextID(hProfile);
592ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
593ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
594ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    if (Intent != -1) {
595ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
596ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        if (cmsIsTag(hProfile, tagFloat)) {  // Float tag takes precedence
597ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
598ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            // Floating point LUT are always V4
599ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            return _cmsReadFloatOutputTag(hProfile, tagFloat);
600ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        }
601ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
602ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        // Revert to perceptual if no tag is found
603ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        if (!cmsIsTag(hProfile, tag16)) {
604ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            tag16 = PCS2Device16[0];
605ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        }
606ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
607ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        if (cmsIsTag(hProfile, tag16)) { // Is there any LUT-Based table?
608ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
609ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            // Check profile version and LUT type. Do the necessary adjustments if needed
610ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
611ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            // First read the tag
612ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            cmsPipeline* Lut = (cmsPipeline*) cmsReadTag(hProfile, tag16);
613ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            if (Lut == NULL) return NULL;
614ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
615ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            // After reading it, we have info about the original type
616ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            OriginalType =  _cmsGetTagTrueType(hProfile, tag16);
617ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
618ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            // The profile owns the Lut, so we need to copy it
619ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            Lut = cmsPipelineDup(Lut);
620ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            if (Lut == NULL) return NULL;
621ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
622ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            // Now it is time for a controversial stuff. I found that for 3D LUTS using
623ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            // Lab used as indexer space,  trilinear interpolation should be used
624ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            if (cmsGetPCS(hProfile) == cmsSigLabData)
625ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov                ChangeInterpolationToTrilinear(Lut);
626ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
627ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            // We need to adjust data only for Lab and Lut16 type
628ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            if (OriginalType != cmsSigLut16Type || cmsGetPCS(hProfile) != cmsSigLabData)
629ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov                return Lut;
630ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
631ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            // Add a matrix for conversion V4 to V2 Lab PCS
632ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            if (!cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageAllocLabV4ToV2(ContextID)))
633ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov                goto Error;
634ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
635ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            // If the output is Lab, add also a conversion at the end
636ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            if (cmsGetColorSpace(hProfile) == cmsSigLabData)
637ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov                if (!cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageAllocLabV2ToV4(ContextID)))
638ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov                    goto Error;
639ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
640ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            return Lut;
641ee451cb395940862dad63c85adfe8f2fd55e864cSvet GanovError:
642ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            cmsPipelineFree(Lut);
643ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            return NULL;
644ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        }
645ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    }
646ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
647ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    // Lut not found, try to create a matrix-shaper
648ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
649ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    // Check if this is a grayscale profile.
650ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    if (cmsGetColorSpace(hProfile) == cmsSigGrayData) {
651ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
652ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        // if so, build appropiate conversion tables.
653ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        // The tables are the PCS iluminant, scaled across GrayTRC
654ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        return BuildGrayOutputPipeline(hProfile);
655ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    }
656ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
657ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    // Not gray, create a normal matrix-shaper, which only operates in XYZ space
658ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    return BuildRGBOutputMatrixShaper(hProfile);
659ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov}
660ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
661ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// ---------------------------------------------------------------------------------------------------------------
662ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
663ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// Read the AToD0 tag, adjusting the encoding of Lab or XYZ if neded
664ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganovstatic
665ee451cb395940862dad63c85adfe8f2fd55e864cSvet GanovcmsPipeline* _cmsReadFloatDevicelinkTag(cmsHPROFILE hProfile, cmsTagSignature tagFloat)
666ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov{
667ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    cmsContext ContextID       = cmsGetProfileContextID(hProfile);
668ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    cmsPipeline* Lut           = cmsPipelineDup((cmsPipeline*) cmsReadTag(hProfile, tagFloat));
669ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    cmsColorSpaceSignature PCS = cmsGetPCS(hProfile);
670ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    cmsColorSpaceSignature spc = cmsGetColorSpace(hProfile);
671ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
672ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    if (Lut == NULL) return NULL;
673ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
674ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    if (spc == cmsSigLabData)
675ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    {
676ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        if (!cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageNormalizeToLabFloat(ContextID)))
677ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            goto Error;
678ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    }
679ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    else
680ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        if (spc == cmsSigXYZData)
681ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        {
682ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            if (!cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageNormalizeToXyzFloat(ContextID)))
683ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov                goto Error;
684ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        }
685ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
686ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        if (PCS == cmsSigLabData)
687ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        {
688ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            if (!cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageNormalizeFromLabFloat(ContextID)))
689ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov                goto Error;
690ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        }
691ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        else
692ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            if (PCS == cmsSigXYZData)
693ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            {
694ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov                if (!cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageNormalizeFromXyzFloat(ContextID)))
695ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov                    goto Error;
696ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            }
697ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
698ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    return Lut;
699ee451cb395940862dad63c85adfe8f2fd55e864cSvet GanovError:
700ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    cmsPipelineFree(Lut);
701ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    return NULL;
702ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov}
703ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
704ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// This one includes abstract profiles as well. Matrix-shaper cannot be obtained on that device class. The
705ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// tag name here may default to AToB0
706ee451cb395940862dad63c85adfe8f2fd55e864cSvet GanovcmsPipeline* _cmsReadDevicelinkLUT(cmsHPROFILE hProfile, int Intent)
707ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov{
708ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    cmsPipeline* Lut;
709ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    cmsTagTypeSignature OriginalType;
710ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    cmsTagSignature tag16    = Device2PCS16[Intent];
711ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    cmsTagSignature tagFloat = Device2PCSFloat[Intent];
712ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    cmsContext ContextID = cmsGetProfileContextID(hProfile);
713ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
714ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
715ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    // On named color, take the appropiate tag
716ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    if (cmsGetDeviceClass(hProfile) == cmsSigNamedColorClass) {
717ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
718ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        cmsNAMEDCOLORLIST* nc = (cmsNAMEDCOLORLIST*) cmsReadTag(hProfile, cmsSigNamedColor2Tag);
719ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
720ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        if (nc == NULL) return NULL;
721ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
722ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        Lut = cmsPipelineAlloc(ContextID, 0, 0);
723ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        if (Lut == NULL)
724ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            goto Error;
725ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
726ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        if (!cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageAllocNamedColor(nc, FALSE)))
727ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            goto Error;
728ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
729ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        if (cmsGetColorSpace(hProfile) == cmsSigLabData)
730ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            if (!cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageAllocLabV2ToV4(ContextID)))
731ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov                goto Error;
732ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
733ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        return Lut;
734ee451cb395940862dad63c85adfe8f2fd55e864cSvet GanovError:
735ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        cmsPipelineFree(Lut);
736ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        cmsFreeNamedColorList(nc);
737ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        return NULL;
738ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    }
739ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
740ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    if (cmsIsTag(hProfile, tagFloat)) {  // Float tag takes precedence
741ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
742ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        // Floating point LUT are always V
743ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        return _cmsReadFloatDevicelinkTag(hProfile, tagFloat);
744ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    }
745ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
746ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    tagFloat = Device2PCSFloat[0];
747ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    if (cmsIsTag(hProfile, tagFloat)) {
748ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
749ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        return cmsPipelineDup((cmsPipeline*) cmsReadTag(hProfile, tagFloat));
750ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    }
751ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
752ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    if (!cmsIsTag(hProfile, tag16)) {  // Is there any LUT-Based table?
753ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
754ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        tag16    = Device2PCS16[0];
755ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        if (!cmsIsTag(hProfile, tag16)) return NULL;
756ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    }
757ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
758ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    // Check profile version and LUT type. Do the necessary adjustments if needed
759ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
760ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    // Read the tag
761ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    Lut = (cmsPipeline*) cmsReadTag(hProfile, tag16);
762ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    if (Lut == NULL) return NULL;
763ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
764ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    // The profile owns the Lut, so we need to copy it
765ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    Lut = cmsPipelineDup(Lut);
766ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    if (Lut == NULL) return NULL;
767ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
768ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    // Now it is time for a controversial stuff. I found that for 3D LUTS using
769ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    // Lab used as indexer space,  trilinear interpolation should be used
770ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    if (cmsGetPCS(hProfile) == cmsSigLabData)
771ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        ChangeInterpolationToTrilinear(Lut);
772ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
773ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    // After reading it, we have info about the original type
774ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    OriginalType =  _cmsGetTagTrueType(hProfile, tag16);
775ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
776ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    // We need to adjust data for Lab16 on output
777ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    if (OriginalType != cmsSigLut16Type) return Lut;
778ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
779ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    // Here it is possible to get Lab on both sides
780ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
781ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    if (cmsGetColorSpace(hProfile) == cmsSigLabData) {
782ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        if(!cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageAllocLabV4ToV2(ContextID)))
783ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            goto Error2;
784ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    }
785ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
786ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    if (cmsGetPCS(hProfile) == cmsSigLabData) {
787ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        if(!cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageAllocLabV2ToV4(ContextID)))
788ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            goto Error2;
789ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    }
790ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
791ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    return Lut;
792ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
793ee451cb395940862dad63c85adfe8f2fd55e864cSvet GanovError2:
794ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    cmsPipelineFree(Lut);
795ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    return NULL;
796ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov}
797ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
798ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// ---------------------------------------------------------------------------------------------------------------
799ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
800ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// Returns TRUE if the profile is implemented as matrix-shaper
801ee451cb395940862dad63c85adfe8f2fd55e864cSvet GanovcmsBool  CMSEXPORT cmsIsMatrixShaper(cmsHPROFILE hProfile)
802ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov{
803ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    switch (cmsGetColorSpace(hProfile)) {
804ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
805ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    case cmsSigGrayData:
806ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
807ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        return cmsIsTag(hProfile, cmsSigGrayTRCTag);
808ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
809ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    case cmsSigRgbData:
810ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
811ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        return (cmsIsTag(hProfile, cmsSigRedColorantTag) &&
812ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov                cmsIsTag(hProfile, cmsSigGreenColorantTag) &&
813ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov                cmsIsTag(hProfile, cmsSigBlueColorantTag) &&
814ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov                cmsIsTag(hProfile, cmsSigRedTRCTag) &&
815ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov                cmsIsTag(hProfile, cmsSigGreenTRCTag) &&
816ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov                cmsIsTag(hProfile, cmsSigBlueTRCTag));
817ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
818ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    default:
819ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
820ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        return FALSE;
821ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    }
822ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov}
823ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
824ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// Returns TRUE if the intent is implemented as CLUT
825ee451cb395940862dad63c85adfe8f2fd55e864cSvet GanovcmsBool  CMSEXPORT cmsIsCLUT(cmsHPROFILE hProfile, cmsUInt32Number Intent, cmsUInt32Number UsedDirection)
826ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov{
827ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    const cmsTagSignature* TagTable;
828ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
829ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    // For devicelinks, the supported intent is that one stated in the header
830ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    if (cmsGetDeviceClass(hProfile) == cmsSigLinkClass) {
831ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            return (cmsGetHeaderRenderingIntent(hProfile) == Intent);
832ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    }
833ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
834ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    switch (UsedDirection) {
835ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
836ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov       case LCMS_USED_AS_INPUT: TagTable = Device2PCS16; break;
837ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov       case LCMS_USED_AS_OUTPUT:TagTable = PCS2Device16; break;
838ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
839ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov       // For proofing, we need rel. colorimetric in output. Let's do some recursion
840ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov       case LCMS_USED_AS_PROOF:
841ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov           return cmsIsIntentSupported(hProfile, Intent, LCMS_USED_AS_INPUT) &&
842ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov                  cmsIsIntentSupported(hProfile, INTENT_RELATIVE_COLORIMETRIC, LCMS_USED_AS_OUTPUT);
843ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
844ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov       default:
845ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov           cmsSignalError(cmsGetProfileContextID(hProfile), cmsERROR_RANGE, "Unexpected direction (%d)", UsedDirection);
846ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov           return FALSE;
847ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    }
848ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
849ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    return cmsIsTag(hProfile, TagTable[Intent]);
850ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
851ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov}
852ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
853ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
854ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// Return info about supported intents
855ee451cb395940862dad63c85adfe8f2fd55e864cSvet GanovcmsBool  CMSEXPORT cmsIsIntentSupported(cmsHPROFILE hProfile,
856ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov                                        cmsUInt32Number Intent, cmsUInt32Number UsedDirection)
857ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov{
858ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
859ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    if (cmsIsCLUT(hProfile, Intent, UsedDirection)) return TRUE;
860ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
861ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    // Is there any matrix-shaper? If so, the intent is supported. This is a bit odd, since V2 matrix shaper
862ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    // does not fully support relative colorimetric because they cannot deal with non-zero black points, but
863ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    // many profiles claims that, and this is certainly not true for V4 profiles. Lets answer "yes" no matter
864ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    // the accuracy would be less than optimal in rel.col and v2 case.
865ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
866ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    return cmsIsMatrixShaper(hProfile);
867ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov}
868ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
869ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
870ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// ---------------------------------------------------------------------------------------------------------------
871ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
872ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// Read both, profile sequence description and profile sequence id if present. Then combine both to
873ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// create qa unique structure holding both. Shame on ICC to store things in such complicated way.
874ee451cb395940862dad63c85adfe8f2fd55e864cSvet GanovcmsSEQ* _cmsReadProfileSequence(cmsHPROFILE hProfile)
875ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov{
876ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    cmsSEQ* ProfileSeq;
877ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    cmsSEQ* ProfileId;
878ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    cmsSEQ* NewSeq;
879ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    cmsUInt32Number i;
880ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
881ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    // Take profile sequence description first
882ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    ProfileSeq = (cmsSEQ*) cmsReadTag(hProfile, cmsSigProfileSequenceDescTag);
883ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
884ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    // Take profile sequence ID
885ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    ProfileId  = (cmsSEQ*) cmsReadTag(hProfile, cmsSigProfileSequenceIdTag);
886ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
887ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    if (ProfileSeq == NULL && ProfileId == NULL) return NULL;
888ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
889ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    if (ProfileSeq == NULL) return cmsDupProfileSequenceDescription(ProfileId);
890ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    if (ProfileId  == NULL) return cmsDupProfileSequenceDescription(ProfileSeq);
891ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
892ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    // We have to mix both together. For that they must agree
893ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    if (ProfileSeq ->n != ProfileId ->n) return cmsDupProfileSequenceDescription(ProfileSeq);
894ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
895ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    NewSeq = cmsDupProfileSequenceDescription(ProfileSeq);
896ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
897ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    // Ok, proceed to the mixing
898ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    if (NewSeq != NULL) {
899ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        for (i=0; i < ProfileSeq ->n; i++) {
900ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
901ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            memmove(&NewSeq ->seq[i].ProfileID, &ProfileId ->seq[i].ProfileID, sizeof(cmsProfileID));
902ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            NewSeq ->seq[i].Description = cmsMLUdup(ProfileId ->seq[i].Description);
903ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        }
904ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    }
905ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    return NewSeq;
906ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov}
907ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
908ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// Dump the contents of profile sequence in both tags (if v4 available)
909ee451cb395940862dad63c85adfe8f2fd55e864cSvet GanovcmsBool _cmsWriteProfileSequence(cmsHPROFILE hProfile, const cmsSEQ* seq)
910ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov{
911ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    if (!cmsWriteTag(hProfile, cmsSigProfileSequenceDescTag, seq)) return FALSE;
912ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
913ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    if (cmsGetProfileVersion(hProfile) >= 4.0) {
914ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
915ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            if (!cmsWriteTag(hProfile, cmsSigProfileSequenceIdTag, seq)) return FALSE;
916ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    }
917ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
918ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    return TRUE;
919ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov}
920ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
921ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
922ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// Auxiliar, read and duplicate a MLU if found.
923ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganovstatic
924ee451cb395940862dad63c85adfe8f2fd55e864cSvet GanovcmsMLU* GetMLUFromProfile(cmsHPROFILE h, cmsTagSignature sig)
925ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov{
926ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    cmsMLU* mlu = (cmsMLU*) cmsReadTag(h, sig);
927ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    if (mlu == NULL) return NULL;
928ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
929ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    return cmsMLUdup(mlu);
930ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov}
931ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
932ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// Create a sequence description out of an array of profiles
933ee451cb395940862dad63c85adfe8f2fd55e864cSvet GanovcmsSEQ* _cmsCompileProfileSequence(cmsContext ContextID, cmsUInt32Number nProfiles, cmsHPROFILE hProfiles[])
934ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov{
935ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    cmsUInt32Number i;
936ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    cmsSEQ* seq = cmsAllocProfileSequenceDescription(ContextID, nProfiles);
937ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
938ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    if (seq == NULL) return NULL;
939ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
940ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    for (i=0; i < nProfiles; i++) {
941ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
942ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        cmsPSEQDESC* ps = &seq ->seq[i];
943ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        cmsHPROFILE h = hProfiles[i];
944ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        cmsTechnologySignature* techpt;
945ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
946ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        cmsGetHeaderAttributes(h, &ps ->attributes);
947ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        cmsGetHeaderProfileID(h, ps ->ProfileID.ID8);
948ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        ps ->deviceMfg   = cmsGetHeaderManufacturer(h);
949ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        ps ->deviceModel = cmsGetHeaderModel(h);
950ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
951ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        techpt = (cmsTechnologySignature*) cmsReadTag(h, cmsSigTechnologyTag);
952ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        if (techpt == NULL)
953ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            ps ->technology   =  (cmsTechnologySignature) 0;
954ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        else
955ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            ps ->technology   = *techpt;
956ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
957ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        ps ->Manufacturer = GetMLUFromProfile(h,  cmsSigDeviceMfgDescTag);
958ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        ps ->Model        = GetMLUFromProfile(h,  cmsSigDeviceModelDescTag);
959ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        ps ->Description  = GetMLUFromProfile(h, cmsSigProfileDescriptionTag);
960ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
961ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    }
962ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
963ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    return seq;
964ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov}
965ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
966ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// -------------------------------------------------------------------------------------------------------------------
967ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
968ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
969ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganovstatic
970ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganovconst cmsMLU* GetInfo(cmsHPROFILE hProfile, cmsInfoType Info)
971ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov{
972ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    cmsTagSignature sig;
973ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
974ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    switch (Info) {
975ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
976ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    case cmsInfoDescription:
977ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        sig = cmsSigProfileDescriptionTag;
978ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        break;
979ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
980ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    case cmsInfoManufacturer:
981ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        sig = cmsSigDeviceMfgDescTag;
982ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        break;
983ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
984ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    case cmsInfoModel:
985ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        sig = cmsSigDeviceModelDescTag;
986ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov         break;
987ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
988ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    case cmsInfoCopyright:
989ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        sig = cmsSigCopyrightTag;
990ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        break;
991ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
992ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    default: return NULL;
993ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    }
994ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
995ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
996ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    return (cmsMLU*) cmsReadTag(hProfile, sig);
997ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov}
998ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
999ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
1000ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
1001ee451cb395940862dad63c85adfe8f2fd55e864cSvet GanovcmsUInt32Number CMSEXPORT cmsGetProfileInfo(cmsHPROFILE hProfile, cmsInfoType Info,
1002ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov                                            const char LanguageCode[3], const char CountryCode[3],
1003ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov                                            wchar_t* Buffer, cmsUInt32Number BufferSize)
1004ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov{
1005ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    const cmsMLU* mlu = GetInfo(hProfile, Info);
1006ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    if (mlu == NULL) return 0;
1007ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
1008ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    return cmsMLUgetWide(mlu, LanguageCode, CountryCode, Buffer, BufferSize);
1009ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov}
1010ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
1011ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
1012ee451cb395940862dad63c85adfe8f2fd55e864cSvet GanovcmsUInt32Number  CMSEXPORT cmsGetProfileInfoASCII(cmsHPROFILE hProfile, cmsInfoType Info,
1013ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov                                                          const char LanguageCode[3], const char CountryCode[3],
1014ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov                                                          char* Buffer, cmsUInt32Number BufferSize)
1015ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov{
1016ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    const cmsMLU* mlu = GetInfo(hProfile, Info);
1017ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    if (mlu == NULL) return 0;
1018ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
1019ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    return cmsMLUgetASCII(mlu, LanguageCode, CountryCode, Buffer, BufferSize);
1020ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov}
1021