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-2014 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         // Tries to minimize space. Thanks to Richard Hughes for this nice idea
 212         if (!cmsWriteTag(hICC, cmsSigRedTRCTag,   (void*) TransferFunction[0])) goto Error;
 213 
 214         if (TransferFunction[1] == TransferFunction[0]) {
 215 
 216             if (!cmsLinkTag (hICC, cmsSigGreenTRCTag, cmsSigRedTRCTag)) goto Error;
 217 
 218         } else {
 219 
 220             if (!cmsWriteTag(hICC, cmsSigGreenTRCTag, (void*) TransferFunction[1])) goto Error;
 221         }
 222 
 223         if (TransferFunction[2] == TransferFunction[0]) {
 224 
 225             if (!cmsLinkTag (hICC, cmsSigBlueTRCTag, cmsSigRedTRCTag)) goto Error;
 226 
 227         } else {
 228 
 229             if (!cmsWriteTag(hICC, cmsSigBlueTRCTag, (void*) TransferFunction[2])) goto Error;
 230         }
 231     }
 232 
 233     if (Primaries) {
 234         if (!cmsWriteTag(hICC, cmsSigChromaticityTag, (void*) Primaries)) goto Error;
 235     }
 236 
 237 
 238     return hICC;
 239 
 240 Error:
 241     if (hICC)
 242         cmsCloseProfile(hICC);
 243     return NULL;
 244 }
 245 
 246 cmsHPROFILE CMSEXPORT cmsCreateRGBProfile(const cmsCIExyY* WhitePoint,
 247                                           const cmsCIExyYTRIPLE* Primaries,
 248                                           cmsToneCurve* const TransferFunction[3])
 249 {
 250     return cmsCreateRGBProfileTHR(NULL, WhitePoint, Primaries, TransferFunction);
 251 }
 252 
 253 
 254 
 255 // This function creates a profile based on White point and transfer function.
 256 cmsHPROFILE CMSEXPORT cmsCreateGrayProfileTHR(cmsContext ContextID,
 257                                            const cmsCIExyY* WhitePoint,
 258                                            const cmsToneCurve* TransferFunction)
 259 {
 260     cmsHPROFILE hICC;
 261     cmsCIEXYZ tmp;
 262 
 263     hICC = cmsCreateProfilePlaceholder(ContextID);
 264     if (!hICC)                          // can't allocate
 265         return NULL;
 266 
 267     cmsSetProfileVersion(hICC, 4.3);
 268 
 269     cmsSetDeviceClass(hICC,      cmsSigDisplayClass);
 270     cmsSetColorSpace(hICC,       cmsSigGrayData);
 271     cmsSetPCS(hICC,              cmsSigXYZData);
 272     cmsSetHeaderRenderingIntent(hICC,  INTENT_PERCEPTUAL);
 273 
 274 
 275     // Implement profile using following tags:
 276     //
 277     //  1 cmsSigProfileDescriptionTag
 278     //  2 cmsSigMediaWhitePointTag
 279     //  3 cmsSigGrayTRCTag
 280 
 281     // This conforms a standard Gray DisplayProfile
 282 
 283     // Fill-in the tags
 284 
 285     if (!SetTextTags(hICC, L"gray built-in")) goto Error;
 286 
 287 
 288     if (WhitePoint) {
 289 
 290         cmsxyY2XYZ(&tmp, WhitePoint);
 291         if (!cmsWriteTag(hICC, cmsSigMediaWhitePointTag, (void*) &tmp)) goto Error;
 292     }
 293 
 294     if (TransferFunction) {
 295 
 296         if (!cmsWriteTag(hICC, cmsSigGrayTRCTag, (void*) TransferFunction)) goto Error;
 297     }
 298 
 299     return hICC;
 300 
 301 Error:
 302     if (hICC)
 303         cmsCloseProfile(hICC);
 304     return NULL;
 305 }
 306 
 307 
 308 
 309 cmsHPROFILE CMSEXPORT cmsCreateGrayProfile(const cmsCIExyY* WhitePoint,
 310                                                     const cmsToneCurve* TransferFunction)
 311 {
 312     return cmsCreateGrayProfileTHR(NULL, WhitePoint, TransferFunction);
 313 }
 314 
 315 // This is a devicelink operating in the target colorspace with as many transfer functions as components
 316 
 317 cmsHPROFILE CMSEXPORT cmsCreateLinearizationDeviceLinkTHR(cmsContext ContextID,
 318                                                           cmsColorSpaceSignature ColorSpace,
 319                                                           cmsToneCurve* const TransferFunctions[])
 320 {
 321     cmsHPROFILE hICC;
 322     cmsPipeline* Pipeline;
 323     int nChannels;
 324 
 325     hICC = cmsCreateProfilePlaceholder(ContextID);
 326     if (!hICC)
 327         return NULL;
 328 
 329     cmsSetProfileVersion(hICC, 4.3);
 330 
 331     cmsSetDeviceClass(hICC,      cmsSigLinkClass);
 332     cmsSetColorSpace(hICC,       ColorSpace);
 333     cmsSetPCS(hICC,              ColorSpace);
 334 
 335     cmsSetHeaderRenderingIntent(hICC,  INTENT_PERCEPTUAL);
 336 
 337     // Set up channels
 338     nChannels = cmsChannelsOf(ColorSpace);
 339 
 340     // Creates a Pipeline with prelinearization step only
 341     Pipeline = cmsPipelineAlloc(ContextID, nChannels, nChannels);
 342     if (Pipeline == NULL) goto Error;
 343 
 344 
 345     // Copy tables to Pipeline
 346     if (!cmsPipelineInsertStage(Pipeline, cmsAT_BEGIN, cmsStageAllocToneCurves(ContextID, nChannels, TransferFunctions)))
 347         goto Error;
 348 
 349     // Create tags
 350     if (!SetTextTags(hICC, L"Linearization built-in")) goto Error;
 351     if (!cmsWriteTag(hICC, cmsSigAToB0Tag, (void*) Pipeline)) goto Error;
 352     if (!SetSeqDescTag(hICC, "Linearization built-in")) goto Error;
 353 
 354     // Pipeline is already on virtual profile
 355     cmsPipelineFree(Pipeline);
 356 
 357     // Ok, done
 358     return hICC;
 359 
 360 Error:
 361     cmsPipelineFree(Pipeline);
 362     if (hICC)
 363         cmsCloseProfile(hICC);
 364 
 365 
 366     return NULL;
 367 }
 368 
 369 cmsHPROFILE CMSEXPORT cmsCreateLinearizationDeviceLink(cmsColorSpaceSignature ColorSpace,
 370                                                                  cmsToneCurve* const TransferFunctions[])
 371 {
 372     return cmsCreateLinearizationDeviceLinkTHR(NULL, ColorSpace, TransferFunctions);
 373 }
 374 
 375 // Ink-limiting algorithm
 376 //
 377 //  Sum = C + M + Y + K
 378 //  If Sum > InkLimit
 379 //        Ratio= 1 - (Sum - InkLimit) / (C + M + Y)
 380 //        if Ratio <0
 381 //              Ratio=0
 382 //        endif
 383 //     Else
 384 //         Ratio=1
 385 //     endif
 386 //
 387 //     C = Ratio * C
 388 //     M = Ratio * M
 389 //     Y = Ratio * Y
 390 //     K: Does not change
 391 
 392 static
 393 int InkLimitingSampler(register const cmsUInt16Number In[], register cmsUInt16Number Out[], register void* Cargo)
 394 {
 395     cmsFloat64Number InkLimit = *(cmsFloat64Number *) Cargo;
 396     cmsFloat64Number SumCMY, SumCMYK, Ratio;
 397 
 398     InkLimit = (InkLimit * 655.35);
 399 
 400     SumCMY   = In[0]  + In[1] + In[2];
 401     SumCMYK  = SumCMY + In[3];
 402 
 403     if (SumCMYK > InkLimit) {
 404 
 405         Ratio = 1 - ((SumCMYK - InkLimit) / SumCMY);
 406         if (Ratio < 0)
 407             Ratio = 0;
 408     }
 409     else Ratio = 1;
 410 
 411     Out[0] = _cmsQuickSaturateWord(In[0] * Ratio);     // C
 412     Out[1] = _cmsQuickSaturateWord(In[1] * Ratio);     // M
 413     Out[2] = _cmsQuickSaturateWord(In[2] * Ratio);     // Y
 414 
 415     Out[3] = In[3];                                 // K (untouched)
 416 
 417     return TRUE;
 418 }
 419 
 420 // This is a devicelink operating in CMYK for ink-limiting
 421 
 422 cmsHPROFILE CMSEXPORT cmsCreateInkLimitingDeviceLinkTHR(cmsContext ContextID,
 423                                                      cmsColorSpaceSignature ColorSpace,
 424                                                      cmsFloat64Number Limit)
 425 {
 426     cmsHPROFILE hICC;
 427     cmsPipeline* LUT;
 428     cmsStage* CLUT;
 429     int nChannels;
 430 
 431     if (ColorSpace != cmsSigCmykData) {
 432         cmsSignalError(ContextID, cmsERROR_COLORSPACE_CHECK, "InkLimiting: Only CMYK currently supported");
 433         return NULL;
 434     }
 435 
 436     if (Limit < 0.0 || Limit > 400) {
 437 
 438         cmsSignalError(ContextID, cmsERROR_RANGE, "InkLimiting: Limit should be between 0..400");
 439         if (Limit < 0) Limit = 0;
 440         if (Limit > 400) Limit = 400;
 441 
 442     }
 443 
 444     hICC = cmsCreateProfilePlaceholder(ContextID);
 445     if (!hICC)                          // can't allocate
 446         return NULL;
 447 
 448     cmsSetProfileVersion(hICC, 4.3);
 449 
 450     cmsSetDeviceClass(hICC,      cmsSigLinkClass);
 451     cmsSetColorSpace(hICC,       ColorSpace);
 452     cmsSetPCS(hICC,              ColorSpace);
 453 
 454     cmsSetHeaderRenderingIntent(hICC,  INTENT_PERCEPTUAL);
 455 
 456 
 457     // Creates a Pipeline with 3D grid only
 458     LUT = cmsPipelineAlloc(ContextID, 4, 4);
 459     if (LUT == NULL) goto Error;
 460 
 461 
 462     nChannels = cmsChannelsOf(ColorSpace);
 463 
 464     CLUT = cmsStageAllocCLut16bit(ContextID, 17, nChannels, nChannels, NULL);
 465     if (CLUT == NULL) goto Error;
 466 
 467     if (!cmsStageSampleCLut16bit(CLUT, InkLimitingSampler, (void*) &Limit, 0)) goto Error;
 468 
 469     if (!cmsPipelineInsertStage(LUT, cmsAT_BEGIN, _cmsStageAllocIdentityCurves(ContextID, nChannels)) ||
 470         !cmsPipelineInsertStage(LUT, cmsAT_END, CLUT) ||
 471         !cmsPipelineInsertStage(LUT, cmsAT_END, _cmsStageAllocIdentityCurves(ContextID, nChannels)))
 472         goto Error;
 473 
 474     // Create tags
 475     if (!SetTextTags(hICC, L"ink-limiting built-in")) goto Error;
 476 
 477     if (!cmsWriteTag(hICC, cmsSigAToB0Tag, (void*) LUT))  goto Error;
 478     if (!SetSeqDescTag(hICC, "ink-limiting built-in")) goto Error;
 479 
 480     // cmsPipeline is already on virtual profile
 481     cmsPipelineFree(LUT);
 482 
 483     // Ok, done
 484     return hICC;
 485 
 486 Error:
 487     if (LUT != NULL)
 488         cmsPipelineFree(LUT);
 489 
 490     if (hICC != NULL)
 491         cmsCloseProfile(hICC);
 492 
 493     return NULL;
 494 }
 495 
 496 cmsHPROFILE CMSEXPORT cmsCreateInkLimitingDeviceLink(cmsColorSpaceSignature ColorSpace, cmsFloat64Number Limit)
 497 {
 498     return cmsCreateInkLimitingDeviceLinkTHR(NULL, ColorSpace, Limit);
 499 }
 500 
 501 
 502 // Creates a fake Lab identity.
 503 cmsHPROFILE CMSEXPORT cmsCreateLab2ProfileTHR(cmsContext ContextID, const cmsCIExyY* WhitePoint)
 504 {
 505     cmsHPROFILE hProfile;
 506     cmsPipeline* LUT = NULL;
 507 
 508     hProfile = cmsCreateRGBProfileTHR(ContextID, WhitePoint == NULL ? cmsD50_xyY() : WhitePoint, NULL, NULL);
 509     if (hProfile == NULL) return NULL;
 510 
 511     cmsSetProfileVersion(hProfile, 2.1);
 512 
 513     cmsSetDeviceClass(hProfile, cmsSigAbstractClass);
 514     cmsSetColorSpace(hProfile,  cmsSigLabData);
 515     cmsSetPCS(hProfile,         cmsSigLabData);
 516 
 517     if (!SetTextTags(hProfile, L"Lab identity built-in")) return NULL;
 518 
 519     // An identity LUT is all we need
 520     LUT = cmsPipelineAlloc(ContextID, 3, 3);
 521     if (LUT == NULL) goto Error;
 522 
 523     if (!cmsPipelineInsertStage(LUT, cmsAT_BEGIN, _cmsStageAllocIdentityCLut(ContextID, 3)))
 524         goto Error;
 525 
 526     if (!cmsWriteTag(hProfile, cmsSigAToB0Tag, LUT)) goto Error;
 527     cmsPipelineFree(LUT);
 528 
 529     return hProfile;
 530 
 531 Error:
 532 
 533     if (LUT != NULL)
 534         cmsPipelineFree(LUT);
 535 
 536     if (hProfile != NULL)
 537         cmsCloseProfile(hProfile);
 538 
 539     return NULL;
 540 }
 541 
 542 
 543 cmsHPROFILE CMSEXPORT cmsCreateLab2Profile(const cmsCIExyY* WhitePoint)
 544 {
 545     return cmsCreateLab2ProfileTHR(NULL, WhitePoint);
 546 }
 547 
 548 
 549 // Creates a fake Lab V4 identity.
 550 cmsHPROFILE CMSEXPORT cmsCreateLab4ProfileTHR(cmsContext ContextID, const cmsCIExyY* WhitePoint)
 551 {
 552     cmsHPROFILE hProfile;
 553     cmsPipeline* LUT = NULL;
 554 
 555     hProfile = cmsCreateRGBProfileTHR(ContextID, WhitePoint == NULL ? cmsD50_xyY() : WhitePoint, NULL, NULL);
 556     if (hProfile == NULL) return NULL;
 557 
 558     cmsSetProfileVersion(hProfile, 4.3);
 559 
 560     cmsSetDeviceClass(hProfile, cmsSigAbstractClass);
 561     cmsSetColorSpace(hProfile,  cmsSigLabData);
 562     cmsSetPCS(hProfile,         cmsSigLabData);
 563 
 564     if (!SetTextTags(hProfile, L"Lab identity built-in")) goto Error;
 565 
 566     // An empty LUTs is all we need
 567     LUT = cmsPipelineAlloc(ContextID, 3, 3);
 568     if (LUT == NULL) goto Error;
 569 
 570     if (!cmsPipelineInsertStage(LUT, cmsAT_BEGIN, _cmsStageAllocIdentityCurves(ContextID, 3)))
 571         goto Error;
 572 
 573     if (!cmsWriteTag(hProfile, cmsSigAToB0Tag, LUT)) goto Error;
 574     cmsPipelineFree(LUT);
 575 
 576     return hProfile;
 577 
 578 Error:
 579 
 580     if (LUT != NULL)
 581         cmsPipelineFree(LUT);
 582 
 583     if (hProfile != NULL)
 584         cmsCloseProfile(hProfile);
 585 
 586     return NULL;
 587 }
 588 
 589 cmsHPROFILE CMSEXPORT cmsCreateLab4Profile(const cmsCIExyY* WhitePoint)
 590 {
 591     return cmsCreateLab4ProfileTHR(NULL, WhitePoint);
 592 }
 593 
 594 
 595 // Creates a fake XYZ identity
 596 cmsHPROFILE CMSEXPORT cmsCreateXYZProfileTHR(cmsContext ContextID)
 597 {
 598     cmsHPROFILE hProfile;
 599     cmsPipeline* LUT = NULL;
 600 
 601     hProfile = cmsCreateRGBProfileTHR(ContextID, cmsD50_xyY(), NULL, NULL);
 602     if (hProfile == NULL) return NULL;
 603 
 604     cmsSetProfileVersion(hProfile, 4.3);
 605 
 606     cmsSetDeviceClass(hProfile, cmsSigAbstractClass);
 607     cmsSetColorSpace(hProfile,  cmsSigXYZData);
 608     cmsSetPCS(hProfile,         cmsSigXYZData);
 609 
 610     if (!SetTextTags(hProfile, L"XYZ identity built-in")) goto Error;
 611 
 612     // An identity LUT is all we need
 613     LUT = cmsPipelineAlloc(ContextID, 3, 3);
 614     if (LUT == NULL) goto Error;
 615 
 616     if (!cmsPipelineInsertStage(LUT, cmsAT_BEGIN, _cmsStageAllocIdentityCurves(ContextID, 3)))
 617         goto Error;
 618 
 619     if (!cmsWriteTag(hProfile, cmsSigAToB0Tag, LUT)) goto Error;
 620     cmsPipelineFree(LUT);
 621 
 622     return hProfile;
 623 
 624 Error:
 625 
 626     if (LUT != NULL)
 627         cmsPipelineFree(LUT);
 628 
 629     if (hProfile != NULL)
 630         cmsCloseProfile(hProfile);
 631 
 632     return NULL;
 633 }
 634 
 635 
 636 cmsHPROFILE CMSEXPORT cmsCreateXYZProfile(void)
 637 {
 638     return cmsCreateXYZProfileTHR(NULL);
 639 }
 640 
 641 
 642 //sRGB Curves are defined by:
 643 //
 644 //If  R’sRGB,G’sRGB, B’sRGB < 0.04045
 645 //
 646 //    R =  R’sRGB / 12.92
 647 //    G =  G’sRGB / 12.92
 648 //    B =  B’sRGB / 12.92
 649 //
 650 //
 651 //else if  R’sRGB,G’sRGB, B’sRGB >= 0.04045
 652 //
 653 //    R = ((R’sRGB + 0.055) / 1.055)^2.4
 654 //    G = ((G’sRGB + 0.055) / 1.055)^2.4
 655 //    B = ((B’sRGB + 0.055) / 1.055)^2.4
 656 
 657 static
 658 cmsToneCurve* Build_sRGBGamma(cmsContext ContextID)
 659 {
 660     cmsFloat64Number Parameters[5];
 661 
 662     Parameters[0] = 2.4;
 663     Parameters[1] = 1. / 1.055;
 664     Parameters[2] = 0.055 / 1.055;
 665     Parameters[3] = 1. / 12.92;
 666     Parameters[4] = 0.04045;
 667 
 668     return cmsBuildParametricToneCurve(ContextID, 4, Parameters);
 669 }
 670 
 671 // Create the ICC virtual profile for sRGB space
 672 cmsHPROFILE CMSEXPORT cmsCreate_sRGBProfileTHR(cmsContext ContextID)
 673 {
 674        cmsCIExyY       D65 = { 0.3127, 0.3290, 1.0 };
 675        cmsCIExyYTRIPLE Rec709Primaries = {
 676                                    {0.6400, 0.3300, 1.0},
 677                                    {0.3000, 0.6000, 1.0},
 678                                    {0.1500, 0.0600, 1.0}
 679                                    };
 680        cmsToneCurve* Gamma22[3];
 681        cmsHPROFILE  hsRGB;
 682 
 683       // cmsWhitePointFromTemp(&D65, 6504);
 684        Gamma22[0] = Gamma22[1] = Gamma22[2] = Build_sRGBGamma(ContextID);
 685        if (Gamma22[0] == NULL) return NULL;
 686 
 687        hsRGB = cmsCreateRGBProfileTHR(ContextID, &D65, &Rec709Primaries, Gamma22);
 688        cmsFreeToneCurve(Gamma22[0]);
 689        if (hsRGB == NULL) return NULL;
 690 
 691        if (!SetTextTags(hsRGB, L"sRGB built-in")) {
 692            cmsCloseProfile(hsRGB);
 693            return NULL;
 694        }
 695 
 696        return hsRGB;
 697 }
 698 
 699 cmsHPROFILE CMSEXPORT cmsCreate_sRGBProfile(void)
 700 {
 701     return cmsCreate_sRGBProfileTHR(NULL);
 702 }
 703 
 704 
 705 
 706 typedef struct {
 707                 cmsFloat64Number Brightness;
 708                 cmsFloat64Number Contrast;
 709                 cmsFloat64Number Hue;
 710                 cmsFloat64Number Saturation;
 711                 cmsBool          lAdjustWP;
 712                 cmsCIEXYZ WPsrc, WPdest;
 713 
 714 } BCHSWADJUSTS, *LPBCHSWADJUSTS;
 715 
 716 
 717 static
 718 int bchswSampler(register const cmsUInt16Number In[], register cmsUInt16Number Out[], register void* Cargo)
 719 {
 720     cmsCIELab LabIn, LabOut;
 721     cmsCIELCh LChIn, LChOut;
 722     cmsCIEXYZ XYZ;
 723     LPBCHSWADJUSTS bchsw = (LPBCHSWADJUSTS) Cargo;
 724 
 725 
 726     cmsLabEncoded2Float(&LabIn, In);
 727 
 728 
 729     cmsLab2LCh(&LChIn, &LabIn);
 730 
 731     // Do some adjusts on LCh
 732 
 733     LChOut.L = LChIn.L * bchsw ->Contrast + bchsw ->Brightness;
 734     LChOut.C = LChIn.C + bchsw -> Saturation;
 735     LChOut.h = LChIn.h + bchsw -> Hue;
 736 
 737 
 738     cmsLCh2Lab(&LabOut, &LChOut);
 739 
 740     // Move white point in Lab
 741     if (bchsw->lAdjustWP) {
 742            cmsLab2XYZ(&bchsw->WPsrc, &XYZ, &LabOut);
 743            cmsXYZ2Lab(&bchsw->WPdest, &LabOut, &XYZ);
 744     }
 745 
 746     // Back to encoded
 747 
 748     cmsFloat2LabEncoded(Out, &LabOut);
 749 
 750     return TRUE;
 751 }
 752 
 753 
 754 // Creates an abstract profile operating in Lab space for Brightness,
 755 // contrast, Saturation and white point displacement
 756 
 757 cmsHPROFILE CMSEXPORT cmsCreateBCHSWabstractProfileTHR(cmsContext ContextID,
 758     int nLUTPoints,
 759     cmsFloat64Number Bright,
 760     cmsFloat64Number Contrast,
 761     cmsFloat64Number Hue,
 762     cmsFloat64Number Saturation,
 763     int TempSrc,
 764     int TempDest)
 765 {
 766     cmsHPROFILE hICC;
 767     cmsPipeline* Pipeline;
 768     BCHSWADJUSTS bchsw;
 769     cmsCIExyY WhitePnt;
 770     cmsStage* CLUT;
 771     cmsUInt32Number Dimensions[MAX_INPUT_DIMENSIONS];
 772     int i;
 773 
 774     bchsw.Brightness = Bright;
 775     bchsw.Contrast   = Contrast;
 776     bchsw.Hue        = Hue;
 777     bchsw.Saturation = Saturation;
 778     if (TempSrc == TempDest) {
 779 
 780            bchsw.lAdjustWP = FALSE;
 781     }
 782     else {
 783            bchsw.lAdjustWP = TRUE;
 784            cmsWhitePointFromTemp(&WhitePnt, TempSrc);
 785            cmsxyY2XYZ(&bchsw.WPsrc, &WhitePnt);
 786            cmsWhitePointFromTemp(&WhitePnt, TempDest);
 787            cmsxyY2XYZ(&bchsw.WPdest, &WhitePnt);
 788 
 789     }
 790 
 791     hICC = cmsCreateProfilePlaceholder(ContextID);
 792     if (!hICC)                          // can't allocate
 793         return NULL;
 794 
 795     cmsSetDeviceClass(hICC,      cmsSigAbstractClass);
 796     cmsSetColorSpace(hICC,       cmsSigLabData);
 797     cmsSetPCS(hICC,              cmsSigLabData);
 798 
 799     cmsSetHeaderRenderingIntent(hICC,  INTENT_PERCEPTUAL);
 800 
 801     // Creates a Pipeline with 3D grid only
 802     Pipeline = cmsPipelineAlloc(ContextID, 3, 3);
 803     if (Pipeline == NULL) {
 804         cmsCloseProfile(hICC);
 805         return NULL;
 806     }
 807 
 808     for (i=0; i < MAX_INPUT_DIMENSIONS; i++) Dimensions[i] = nLUTPoints;
 809     CLUT = cmsStageAllocCLut16bitGranular(ContextID, Dimensions, 3, 3, NULL);
 810     if (CLUT == NULL) return NULL;
 811 
 812 
 813     if (!cmsStageSampleCLut16bit(CLUT, bchswSampler, (void*) &bchsw, 0)) {
 814 
 815         // Shouldn't reach here
 816         goto Error;
 817     }
 818 
 819     if (!cmsPipelineInsertStage(Pipeline, cmsAT_END, CLUT)) {
 820         goto Error;
 821     }
 822 
 823     // Create tags
 824     if (!SetTextTags(hICC, L"BCHS built-in")) return NULL;
 825 
 826     cmsWriteTag(hICC, cmsSigMediaWhitePointTag, (void*) cmsD50_XYZ());
 827 
 828     cmsWriteTag(hICC, cmsSigAToB0Tag, (void*) Pipeline);
 829 
 830     // Pipeline is already on virtual profile
 831     cmsPipelineFree(Pipeline);
 832 
 833     // Ok, done
 834     return hICC;
 835 
 836 Error:
 837     cmsPipelineFree(Pipeline);
 838     cmsCloseProfile(hICC);
 839     return NULL;
 840 }
 841 
 842 
 843 CMSAPI cmsHPROFILE   CMSEXPORT cmsCreateBCHSWabstractProfile(int nLUTPoints,
 844                                                              cmsFloat64Number Bright,
 845                                                              cmsFloat64Number Contrast,
 846                                                              cmsFloat64Number Hue,
 847                                                              cmsFloat64Number Saturation,
 848                                                              int TempSrc,
 849                                                              int TempDest)
 850 {
 851     return cmsCreateBCHSWabstractProfileTHR(NULL, nLUTPoints, Bright, Contrast, Hue, Saturation, TempSrc, TempDest);
 852 }
 853 
 854 
 855 // Creates a fake NULL profile. This profile return 1 channel as always 0.
 856 // Is useful only for gamut checking tricks
 857 cmsHPROFILE CMSEXPORT cmsCreateNULLProfileTHR(cmsContext ContextID)
 858 {
 859     cmsHPROFILE hProfile;
 860     cmsPipeline* LUT = NULL;
 861     cmsStage* PostLin;
 862     cmsToneCurve* EmptyTab;
 863     cmsUInt16Number Zero[2] = { 0, 0 };
 864 
 865     hProfile = cmsCreateProfilePlaceholder(ContextID);
 866     if (!hProfile)                          // can't allocate
 867         return NULL;
 868 
 869     cmsSetProfileVersion(hProfile, 4.3);
 870 
 871     if (!SetTextTags(hProfile, L"NULL profile built-in")) goto Error;
 872 
 873 
 874 
 875     cmsSetDeviceClass(hProfile, cmsSigOutputClass);
 876     cmsSetColorSpace(hProfile,  cmsSigGrayData);
 877     cmsSetPCS(hProfile,         cmsSigLabData);
 878 
 879     // An empty LUTs is all we need
 880     LUT = cmsPipelineAlloc(ContextID, 1, 1);
 881     if (LUT == NULL) goto Error;
 882 
 883     EmptyTab = cmsBuildTabulatedToneCurve16(ContextID, 2, Zero);
 884     PostLin = cmsStageAllocToneCurves(ContextID, 1, &EmptyTab);
 885     cmsFreeToneCurve(EmptyTab);
 886 
 887     if (!cmsPipelineInsertStage(LUT, cmsAT_END, PostLin))
 888         goto Error;
 889 
 890     if (!cmsWriteTag(hProfile, cmsSigBToA0Tag, (void*) LUT)) goto Error;
 891     if (!cmsWriteTag(hProfile, cmsSigMediaWhitePointTag, cmsD50_XYZ())) goto Error;
 892 
 893     cmsPipelineFree(LUT);
 894     return hProfile;
 895 
 896 Error:
 897 
 898     if (LUT != NULL)
 899         cmsPipelineFree(LUT);
 900 
 901     if (hProfile != NULL)
 902         cmsCloseProfile(hProfile);
 903 
 904     return NULL;
 905 }
 906 
 907 cmsHPROFILE CMSEXPORT cmsCreateNULLProfile(void)
 908 {
 909     return cmsCreateNULLProfileTHR(NULL);
 910 }
 911 
 912 
 913 static
 914 int IsPCS(cmsColorSpaceSignature ColorSpace)
 915 {
 916     return (ColorSpace == cmsSigXYZData ||
 917             ColorSpace == cmsSigLabData);
 918 }
 919 
 920 
 921 static
 922 void FixColorSpaces(cmsHPROFILE hProfile,
 923                               cmsColorSpaceSignature ColorSpace,
 924                               cmsColorSpaceSignature PCS,
 925                               cmsUInt32Number dwFlags)
 926 {
 927     if (dwFlags & cmsFLAGS_GUESSDEVICECLASS) {
 928 
 929             if (IsPCS(ColorSpace) && IsPCS(PCS)) {
 930 
 931                     cmsSetDeviceClass(hProfile,      cmsSigAbstractClass);
 932                     cmsSetColorSpace(hProfile,       ColorSpace);
 933                     cmsSetPCS(hProfile,              PCS);
 934                     return;
 935             }
 936 
 937             if (IsPCS(ColorSpace) && !IsPCS(PCS)) {
 938 
 939                     cmsSetDeviceClass(hProfile, cmsSigOutputClass);
 940                     cmsSetPCS(hProfile,         ColorSpace);
 941                     cmsSetColorSpace(hProfile,  PCS);
 942                     return;
 943             }
 944 
 945             if (IsPCS(PCS) && !IsPCS(ColorSpace)) {
 946 
 947                    cmsSetDeviceClass(hProfile,  cmsSigInputClass);
 948                    cmsSetColorSpace(hProfile,   ColorSpace);
 949                    cmsSetPCS(hProfile,          PCS);
 950                    return;
 951             }
 952     }
 953 
 954     cmsSetDeviceClass(hProfile,      cmsSigLinkClass);
 955     cmsSetColorSpace(hProfile,       ColorSpace);
 956     cmsSetPCS(hProfile,              PCS);
 957 }
 958 
 959 
 960 
 961 // This function creates a named color profile dumping all the contents of transform to a single profile
 962 // In this way, LittleCMS may be used to "group" several named color databases into a single profile.
 963 // It has, however, several minor limitations. PCS is always Lab, which is not very critic since this
 964 // is the normal PCS for named color profiles.
 965 static
 966 cmsHPROFILE CreateNamedColorDevicelink(cmsHTRANSFORM xform)
 967 {
 968     _cmsTRANSFORM* v = (_cmsTRANSFORM*) xform;
 969     cmsHPROFILE hICC = NULL;
 970     int i, nColors;
 971     cmsNAMEDCOLORLIST *nc2 = NULL, *Original = NULL;
 972 
 973     // Create an empty placeholder
 974     hICC = cmsCreateProfilePlaceholder(v->ContextID);
 975     if (hICC == NULL) return NULL;
 976 
 977     // Critical information
 978     cmsSetDeviceClass(hICC, cmsSigNamedColorClass);
 979     cmsSetColorSpace(hICC, v ->ExitColorSpace);
 980     cmsSetPCS(hICC, cmsSigLabData);
 981 
 982     // Tag profile with information
 983     if (!SetTextTags(hICC, L"Named color devicelink")) goto Error;
 984 
 985     Original = cmsGetNamedColorList(xform);
 986     if (Original == NULL) goto Error;
 987 
 988     nColors = cmsNamedColorCount(Original);
 989     nc2     = cmsDupNamedColorList(Original);
 990     if (nc2 == NULL) goto Error;
 991 
 992     // Colorant count now depends on the output space
 993     nc2 ->ColorantCount = cmsPipelineOutputChannels(v ->Lut);
 994 
 995     // Make sure we have proper formatters
 996     cmsChangeBuffersFormat(xform, TYPE_NAMED_COLOR_INDEX,
 997         FLOAT_SH(0) | COLORSPACE_SH(_cmsLCMScolorSpace(v ->ExitColorSpace))
 998         | BYTES_SH(2) | CHANNELS_SH(cmsChannelsOf(v ->ExitColorSpace)));
 999 
1000     // Apply the transfor to colorants.
1001     for (i=0; i < nColors; i++) {
1002         cmsDoTransform(xform, &i, nc2 ->List[i].DeviceColorant, 1);
1003     }
1004 
1005     if (!cmsWriteTag(hICC, cmsSigNamedColor2Tag, (void*) nc2)) goto Error;
1006     cmsFreeNamedColorList(nc2);
1007 
1008     return hICC;
1009 
1010 Error:
1011     if (hICC != NULL) cmsCloseProfile(hICC);
1012     return NULL;
1013 }
1014 
1015 
1016 // This structure holds information about which MPU can be stored on a profile based on the version
1017 
1018 typedef struct {
1019     cmsBool              IsV4;             // Is a V4 tag?
1020     cmsTagSignature      RequiredTag;      // Set to 0 for both types
1021     cmsTagTypeSignature  LutType;          // The LUT type
1022     int                  nTypes;           // Number of types (up to 5)
1023     cmsStageSignature    MpeTypes[5];      // 5 is the maximum number
1024 
1025 } cmsAllowedLUT;
1026 
1027 #define cmsSig0 ((cmsTagSignature) 0)
1028 
1029 static const cmsAllowedLUT AllowedLUTTypes[] = {
1030 
1031     { FALSE, cmsSig0,        cmsSigLut16Type, 4, { cmsSigMatrixElemType, cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType } },
1032     { FALSE, cmsSig0,        cmsSigLut16Type, 3, { cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType } },
1033     { FALSE, cmsSig0,        cmsSigLut16Type, 2, { cmsSigCurveSetElemType, cmsSigCLutElemType } },
1034     { TRUE,  cmsSig0,        cmsSigLutAtoBType, 1, { cmsSigCurveSetElemType } },
1035     { TRUE , cmsSigAToB0Tag, cmsSigLutAtoBType,  3,  { cmsSigCurveSetElemType, cmsSigMatrixElemType, cmsSigCurveSetElemType } },
1036     { TRUE , cmsSigAToB0Tag, cmsSigLutAtoBType,  3,  { cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType   } },
1037     { TRUE , cmsSigAToB0Tag, cmsSigLutAtoBType,  5,  { cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType, cmsSigMatrixElemType, cmsSigCurveSetElemType }},
1038     { TRUE , cmsSigBToA0Tag, cmsSigLutBtoAType,  1,  { cmsSigCurveSetElemType }},
1039     { TRUE , cmsSigBToA0Tag, cmsSigLutBtoAType,  3,  { cmsSigCurveSetElemType, cmsSigMatrixElemType, cmsSigCurveSetElemType }},
1040     { TRUE , cmsSigBToA0Tag, cmsSigLutBtoAType,  3,  { cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType }},
1041     { TRUE , cmsSigBToA0Tag, cmsSigLutBtoAType,  5,  { cmsSigCurveSetElemType, cmsSigMatrixElemType, cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType }}
1042 };
1043 
1044 #define SIZE_OF_ALLOWED_LUT (sizeof(AllowedLUTTypes)/sizeof(cmsAllowedLUT))
1045 
1046 // Check a single entry
1047 static
1048 cmsBool CheckOne(const cmsAllowedLUT* Tab, const cmsPipeline* Lut)
1049 {
1050     cmsStage* mpe;
1051     int n;
1052 
1053     for (n=0, mpe = Lut ->Elements; mpe != NULL; mpe = mpe ->Next, n++) {
1054 
1055         if (n > Tab ->nTypes) return FALSE;
1056         if (cmsStageType(mpe) != Tab ->MpeTypes[n]) return FALSE;
1057     }
1058 
1059     return (n == Tab ->nTypes);
1060 }
1061 
1062 
1063 static
1064 const cmsAllowedLUT* FindCombination(const cmsPipeline* Lut, cmsBool IsV4, cmsTagSignature DestinationTag)
1065 {
1066     cmsUInt32Number n;
1067 
1068     for (n=0; n < SIZE_OF_ALLOWED_LUT; n++) {
1069 
1070         const cmsAllowedLUT* Tab = AllowedLUTTypes + n;
1071 
1072         if (IsV4 ^ Tab -> IsV4) continue;
1073         if ((Tab ->RequiredTag != 0) && (Tab ->RequiredTag != DestinationTag)) continue;
1074 
1075         if (CheckOne(Tab, Lut)) return Tab;
1076     }
1077 
1078     return NULL;
1079 }
1080 
1081 
1082 // Does convert a transform into a device link profile
1083 cmsHPROFILE CMSEXPORT cmsTransform2DeviceLink(cmsHTRANSFORM hTransform, cmsFloat64Number Version, cmsUInt32Number dwFlags)
1084 {
1085     cmsHPROFILE hProfile = NULL;
1086     cmsUInt32Number FrmIn, FrmOut, ChansIn, ChansOut;
1087     cmsUInt32Number ColorSpaceBitsIn, ColorSpaceBitsOut;
1088     _cmsTRANSFORM* xform = (_cmsTRANSFORM*) hTransform;
1089     cmsPipeline* LUT = NULL;
1090     cmsStage* mpe;
1091     cmsContext ContextID = cmsGetTransformContextID(hTransform);
1092     const cmsAllowedLUT* AllowedLUT;
1093     cmsTagSignature DestinationTag;
1094     cmsProfileClassSignature deviceClass;
1095 
1096     _cmsAssert(hTransform != NULL);
1097 
1098     // Get the first mpe to check for named color
1099     mpe = cmsPipelineGetPtrToFirstStage(xform ->Lut);
1100 
1101     // Check if is a named color transform
1102     if (mpe != NULL) {
1103 
1104         if (cmsStageType(mpe) == cmsSigNamedColorElemType) {
1105             return CreateNamedColorDevicelink(hTransform);
1106         }
1107     }
1108 
1109     // First thing to do is to get a copy of the transformation
1110     LUT = cmsPipelineDup(xform ->Lut);
1111     if (LUT == NULL) return NULL;
1112 
1113     // Time to fix the Lab2/Lab4 issue.
1114     if ((xform ->EntryColorSpace == cmsSigLabData) && (Version < 4.0)) {
1115 
1116         if (!cmsPipelineInsertStage(LUT, cmsAT_BEGIN, _cmsStageAllocLabV2ToV4curves(ContextID)))
1117             goto Error;
1118     }
1119 
1120     // On the output side too
1121     if ((xform ->ExitColorSpace) == cmsSigLabData && (Version < 4.0)) {
1122 
1123         if (!cmsPipelineInsertStage(LUT, cmsAT_END, _cmsStageAllocLabV4ToV2(ContextID)))
1124             goto Error;
1125     }
1126 
1127 
1128     hProfile = cmsCreateProfilePlaceholder(ContextID);
1129     if (!hProfile) goto Error;                    // can't allocate
1130 
1131     cmsSetProfileVersion(hProfile, Version);
1132 
1133     FixColorSpaces(hProfile, xform -> EntryColorSpace, xform -> ExitColorSpace, dwFlags);
1134 
1135     // Optimize the LUT and precalculate a devicelink
1136 
1137     ChansIn  = cmsChannelsOf(xform -> EntryColorSpace);
1138     ChansOut = cmsChannelsOf(xform -> ExitColorSpace);
1139 
1140     ColorSpaceBitsIn  = _cmsLCMScolorSpace(xform -> EntryColorSpace);
1141     ColorSpaceBitsOut = _cmsLCMScolorSpace(xform -> ExitColorSpace);
1142 
1143     FrmIn  = COLORSPACE_SH(ColorSpaceBitsIn) | CHANNELS_SH(ChansIn)|BYTES_SH(2);
1144     FrmOut = COLORSPACE_SH(ColorSpaceBitsOut) | CHANNELS_SH(ChansOut)|BYTES_SH(2);
1145 
1146     deviceClass = cmsGetDeviceClass(hProfile);
1147 
1148      if (deviceClass == cmsSigOutputClass)
1149          DestinationTag = cmsSigBToA0Tag;
1150      else
1151          DestinationTag = cmsSigAToB0Tag;
1152 
1153     // Check if the profile/version can store the result
1154     if (dwFlags & cmsFLAGS_FORCE_CLUT)
1155         AllowedLUT = NULL;
1156     else
1157         AllowedLUT = FindCombination(LUT, Version >= 4.0, DestinationTag);
1158 
1159     if (AllowedLUT == NULL) {
1160 
1161         // Try to optimize
1162         _cmsOptimizePipeline(ContextID, &LUT, xform ->RenderingIntent, &FrmIn, &FrmOut, &dwFlags);
1163         AllowedLUT = FindCombination(LUT, Version >= 4.0, DestinationTag);
1164 
1165     }
1166 
1167     // If no way, then force CLUT that for sure can be written
1168     if (AllowedLUT == NULL) {
1169 
1170         dwFlags |= cmsFLAGS_FORCE_CLUT;
1171         _cmsOptimizePipeline(ContextID, &LUT, xform ->RenderingIntent, &FrmIn, &FrmOut, &dwFlags);
1172 
1173         // Put identity curves if needed
1174         if (cmsPipelineGetPtrToFirstStage(LUT) ->Type != cmsSigCurveSetElemType)
1175              if (!cmsPipelineInsertStage(LUT, cmsAT_BEGIN, _cmsStageAllocIdentityCurves(ContextID, ChansIn)))
1176                  goto Error;
1177 
1178         if (cmsPipelineGetPtrToLastStage(LUT) ->Type != cmsSigCurveSetElemType)
1179              if (!cmsPipelineInsertStage(LUT, cmsAT_END,   _cmsStageAllocIdentityCurves(ContextID, ChansOut)))
1180                  goto Error;
1181 
1182         AllowedLUT = FindCombination(LUT, Version >= 4.0, DestinationTag);
1183     }
1184 
1185     // Somethings is wrong...
1186     if (AllowedLUT == NULL) {
1187         goto Error;
1188     }
1189 
1190 
1191     if (dwFlags & cmsFLAGS_8BITS_DEVICELINK)
1192                      cmsPipelineSetSaveAs8bitsFlag(LUT, TRUE);
1193 
1194     // Tag profile with information
1195     if (!SetTextTags(hProfile, L"devicelink")) goto Error;
1196 
1197     // Store result
1198     if (!cmsWriteTag(hProfile, DestinationTag, LUT)) goto Error;
1199 
1200 
1201     if (xform -> InputColorant != NULL) {
1202            if (!cmsWriteTag(hProfile, cmsSigColorantTableTag, xform->InputColorant)) goto Error;
1203     }
1204 
1205     if (xform -> OutputColorant != NULL) {
1206            if (!cmsWriteTag(hProfile, cmsSigColorantTableOutTag, xform->OutputColorant)) goto Error;
1207     }
1208 
1209     if ((deviceClass == cmsSigLinkClass) && (xform ->Sequence != NULL)) {
1210         if (!_cmsWriteProfileSequence(hProfile, xform ->Sequence)) goto Error;
1211     }
1212 
1213     // Set the white point
1214     if (deviceClass == cmsSigInputClass) {
1215         if (!cmsWriteTag(hProfile, cmsSigMediaWhitePointTag, &xform ->EntryWhitePoint)) goto Error;
1216     }
1217     else {
1218          if (!cmsWriteTag(hProfile, cmsSigMediaWhitePointTag, &xform ->ExitWhitePoint)) goto Error;
1219     }
1220 
1221 
1222     // Per 7.2.15 in spec 4.3
1223     cmsSetHeaderRenderingIntent(hProfile, xform ->RenderingIntent);
1224 
1225     cmsPipelineFree(LUT);
1226     return hProfile;
1227 
1228 Error:
1229     if (LUT != NULL) cmsPipelineFree(LUT);
1230     cmsCloseProfile(hProfile);
1231     return NULL;
1232 }