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