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