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