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-2020 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 cmsUInt32Number 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(CMSREGISTER const cmsUInt16Number In[], CMSREGISTER cmsUInt16Number Out[], CMSREGISTER 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 cmsUInt32Number 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(CMSREGISTER const cmsUInt16Number In[], CMSREGISTER cmsUInt16Number Out[], CMSREGISTER 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 cmsUInt32Number nLUTPoints, 759 cmsFloat64Number Bright, 760 cmsFloat64Number Contrast, 761 cmsFloat64Number Hue, 762 cmsFloat64Number Saturation, 763 cmsUInt32Number TempSrc, 764 cmsUInt32Number TempDest) 765 { 766 cmsHPROFILE hICC; 767 cmsPipeline* Pipeline; 768 BCHSWADJUSTS bchsw; 769 cmsCIExyY WhitePnt; 770 cmsStage* CLUT; 771 cmsUInt32Number Dimensions[MAX_INPUT_DIMENSIONS]; 772 cmsUInt32Number 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) goto Error; 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(cmsUInt32Number nLUTPoints, 844 cmsFloat64Number Bright, 845 cmsFloat64Number Contrast, 846 cmsFloat64Number Hue, 847 cmsFloat64Number Saturation, 848 cmsUInt32Number TempSrc, 849 cmsUInt32Number 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 cmsStage* OutLin; 863 cmsToneCurve* EmptyTab[3]; 864 cmsUInt16Number Zero[2] = { 0, 0 }; 865 const cmsFloat64Number PickLstarMatrix[] = { 1, 0, 0 }; 866 867 hProfile = cmsCreateProfilePlaceholder(ContextID); 868 if (!hProfile) // can't allocate 869 return NULL; 870 871 cmsSetProfileVersion(hProfile, 4.3); 872 873 if (!SetTextTags(hProfile, L"NULL profile built-in")) goto Error; 874 875 876 cmsSetDeviceClass(hProfile, cmsSigOutputClass); 877 cmsSetColorSpace(hProfile, cmsSigGrayData); 878 cmsSetPCS(hProfile, cmsSigLabData); 879 880 // Create a valid ICC 4 structure 881 LUT = cmsPipelineAlloc(ContextID, 3, 1); 882 if (LUT == NULL) goto Error; 883 884 EmptyTab[0] = EmptyTab[1] = EmptyTab[2] = cmsBuildTabulatedToneCurve16(ContextID, 2, Zero); 885 PostLin = cmsStageAllocToneCurves(ContextID, 3, EmptyTab); 886 OutLin = cmsStageAllocToneCurves(ContextID, 1, EmptyTab); 887 cmsFreeToneCurve(EmptyTab[0]); 888 889 if (!cmsPipelineInsertStage(LUT, cmsAT_END, PostLin)) 890 goto Error; 891 892 if (!cmsPipelineInsertStage(LUT, cmsAT_END, cmsStageAllocMatrix(ContextID, 1, 3, PickLstarMatrix, NULL))) 893 goto Error; 894 895 if (!cmsPipelineInsertStage(LUT, cmsAT_END, OutLin)) 896 goto Error; 897 898 if (!cmsWriteTag(hProfile, cmsSigBToA0Tag, (void*) LUT)) goto Error; 899 if (!cmsWriteTag(hProfile, cmsSigMediaWhitePointTag, cmsD50_XYZ())) goto Error; 900 901 cmsPipelineFree(LUT); 902 return hProfile; 903 904 Error: 905 906 if (LUT != NULL) 907 cmsPipelineFree(LUT); 908 909 if (hProfile != NULL) 910 cmsCloseProfile(hProfile); 911 912 return NULL; 913 } 914 915 cmsHPROFILE CMSEXPORT cmsCreateNULLProfile(void) 916 { 917 return cmsCreateNULLProfileTHR(NULL); 918 } 919 920 921 static 922 int IsPCS(cmsColorSpaceSignature ColorSpace) 923 { 924 return (ColorSpace == cmsSigXYZData || 925 ColorSpace == cmsSigLabData); 926 } 927 928 929 static 930 void FixColorSpaces(cmsHPROFILE hProfile, 931 cmsColorSpaceSignature ColorSpace, 932 cmsColorSpaceSignature PCS, 933 cmsUInt32Number dwFlags) 934 { 935 if (dwFlags & cmsFLAGS_GUESSDEVICECLASS) { 936 937 if (IsPCS(ColorSpace) && IsPCS(PCS)) { 938 939 cmsSetDeviceClass(hProfile, cmsSigAbstractClass); 940 cmsSetColorSpace(hProfile, ColorSpace); 941 cmsSetPCS(hProfile, PCS); 942 return; 943 } 944 945 if (IsPCS(ColorSpace) && !IsPCS(PCS)) { 946 947 cmsSetDeviceClass(hProfile, cmsSigOutputClass); 948 cmsSetPCS(hProfile, ColorSpace); 949 cmsSetColorSpace(hProfile, PCS); 950 return; 951 } 952 953 if (IsPCS(PCS) && !IsPCS(ColorSpace)) { 954 955 cmsSetDeviceClass(hProfile, cmsSigInputClass); 956 cmsSetColorSpace(hProfile, ColorSpace); 957 cmsSetPCS(hProfile, PCS); 958 return; 959 } 960 } 961 962 cmsSetDeviceClass(hProfile, cmsSigLinkClass); 963 cmsSetColorSpace(hProfile, ColorSpace); 964 cmsSetPCS(hProfile, PCS); 965 } 966 967 968 969 // This function creates a named color profile dumping all the contents of transform to a single profile 970 // In this way, LittleCMS may be used to "group" several named color databases into a single profile. 971 // It has, however, several minor limitations. PCS is always Lab, which is not very critic since this 972 // is the normal PCS for named color profiles. 973 static 974 cmsHPROFILE CreateNamedColorDevicelink(cmsHTRANSFORM xform) 975 { 976 _cmsTRANSFORM* v = (_cmsTRANSFORM*) xform; 977 cmsHPROFILE hICC = NULL; 978 cmsUInt32Number i, nColors; 979 cmsNAMEDCOLORLIST *nc2 = NULL, *Original = NULL; 980 981 // Create an empty placeholder 982 hICC = cmsCreateProfilePlaceholder(v->ContextID); 983 if (hICC == NULL) return NULL; 984 985 // Critical information 986 cmsSetDeviceClass(hICC, cmsSigNamedColorClass); 987 cmsSetColorSpace(hICC, v ->ExitColorSpace); 988 cmsSetPCS(hICC, cmsSigLabData); 989 990 // Tag profile with information 991 if (!SetTextTags(hICC, L"Named color devicelink")) goto Error; 992 993 Original = cmsGetNamedColorList(xform); 994 if (Original == NULL) goto Error; 995 996 nColors = cmsNamedColorCount(Original); 997 nc2 = cmsDupNamedColorList(Original); 998 if (nc2 == NULL) goto Error; 999 1000 // Colorant count now depends on the output space 1001 nc2 ->ColorantCount = cmsPipelineOutputChannels(v ->Lut); 1002 1003 // Make sure we have proper formatters 1004 cmsChangeBuffersFormat(xform, TYPE_NAMED_COLOR_INDEX, 1005 FLOAT_SH(0) | COLORSPACE_SH(_cmsLCMScolorSpace(v ->ExitColorSpace)) 1006 | BYTES_SH(2) | CHANNELS_SH(cmsChannelsOf(v ->ExitColorSpace))); 1007 1008 // Apply the transfor to colorants. 1009 for (i=0; i < nColors; i++) { 1010 cmsDoTransform(xform, &i, nc2 ->List[i].DeviceColorant, 1); 1011 } 1012 1013 if (!cmsWriteTag(hICC, cmsSigNamedColor2Tag, (void*) nc2)) goto Error; 1014 cmsFreeNamedColorList(nc2); 1015 1016 return hICC; 1017 1018 Error: 1019 if (hICC != NULL) cmsCloseProfile(hICC); 1020 return NULL; 1021 } 1022 1023 1024 // This structure holds information about which MPU can be stored on a profile based on the version 1025 1026 typedef struct { 1027 cmsBool IsV4; // Is a V4 tag? 1028 cmsTagSignature RequiredTag; // Set to 0 for both types 1029 cmsTagTypeSignature LutType; // The LUT type 1030 int nTypes; // Number of types (up to 5) 1031 cmsStageSignature MpeTypes[5]; // 5 is the maximum number 1032 1033 } cmsAllowedLUT; 1034 1035 #define cmsSig0 ((cmsTagSignature) 0) 1036 1037 static const cmsAllowedLUT AllowedLUTTypes[] = { 1038 1039 { FALSE, cmsSig0, cmsSigLut16Type, 4, { cmsSigMatrixElemType, cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType } }, 1040 { FALSE, cmsSig0, cmsSigLut16Type, 3, { cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType } }, 1041 { FALSE, cmsSig0, cmsSigLut16Type, 2, { cmsSigCurveSetElemType, cmsSigCLutElemType } }, 1042 { TRUE, cmsSig0, cmsSigLutAtoBType, 1, { cmsSigCurveSetElemType } }, 1043 { TRUE , cmsSigAToB0Tag, cmsSigLutAtoBType, 3, { cmsSigCurveSetElemType, cmsSigMatrixElemType, cmsSigCurveSetElemType } }, 1044 { TRUE , cmsSigAToB0Tag, cmsSigLutAtoBType, 3, { cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType } }, 1045 { TRUE , cmsSigAToB0Tag, cmsSigLutAtoBType, 5, { cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType, cmsSigMatrixElemType, cmsSigCurveSetElemType }}, 1046 { TRUE , cmsSigBToA0Tag, cmsSigLutBtoAType, 1, { cmsSigCurveSetElemType }}, 1047 { TRUE , cmsSigBToA0Tag, cmsSigLutBtoAType, 3, { cmsSigCurveSetElemType, cmsSigMatrixElemType, cmsSigCurveSetElemType }}, 1048 { TRUE , cmsSigBToA0Tag, cmsSigLutBtoAType, 3, { cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType }}, 1049 { TRUE , cmsSigBToA0Tag, cmsSigLutBtoAType, 5, { cmsSigCurveSetElemType, cmsSigMatrixElemType, cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType }} 1050 }; 1051 1052 #define SIZE_OF_ALLOWED_LUT (sizeof(AllowedLUTTypes)/sizeof(cmsAllowedLUT)) 1053 1054 // Check a single entry 1055 static 1056 cmsBool CheckOne(const cmsAllowedLUT* Tab, const cmsPipeline* Lut) 1057 { 1058 cmsStage* mpe; 1059 int n; 1060 1061 for (n=0, mpe = Lut ->Elements; mpe != NULL; mpe = mpe ->Next, n++) { 1062 1063 if (n > Tab ->nTypes) return FALSE; 1064 if (cmsStageType(mpe) != Tab ->MpeTypes[n]) return FALSE; 1065 } 1066 1067 return (n == Tab ->nTypes); 1068 } 1069 1070 1071 static 1072 const cmsAllowedLUT* FindCombination(const cmsPipeline* Lut, cmsBool IsV4, cmsTagSignature DestinationTag) 1073 { 1074 cmsUInt32Number n; 1075 1076 for (n=0; n < SIZE_OF_ALLOWED_LUT; n++) { 1077 1078 const cmsAllowedLUT* Tab = AllowedLUTTypes + n; 1079 1080 if (IsV4 ^ Tab -> IsV4) continue; 1081 if ((Tab ->RequiredTag != 0) && (Tab ->RequiredTag != DestinationTag)) continue; 1082 1083 if (CheckOne(Tab, Lut)) return Tab; 1084 } 1085 1086 return NULL; 1087 } 1088 1089 1090 // Does convert a transform into a device link profile 1091 cmsHPROFILE CMSEXPORT cmsTransform2DeviceLink(cmsHTRANSFORM hTransform, cmsFloat64Number Version, cmsUInt32Number dwFlags) 1092 { 1093 cmsHPROFILE hProfile = NULL; 1094 cmsUInt32Number FrmIn, FrmOut, ChansIn, ChansOut; 1095 int ColorSpaceBitsIn, ColorSpaceBitsOut; 1096 _cmsTRANSFORM* xform = (_cmsTRANSFORM*) hTransform; 1097 cmsPipeline* LUT = NULL; 1098 cmsStage* mpe; 1099 cmsContext ContextID = cmsGetTransformContextID(hTransform); 1100 const cmsAllowedLUT* AllowedLUT; 1101 cmsTagSignature DestinationTag; 1102 cmsProfileClassSignature deviceClass; 1103 1104 _cmsAssert(hTransform != NULL); 1105 1106 // Get the first mpe to check for named color 1107 mpe = cmsPipelineGetPtrToFirstStage(xform ->Lut); 1108 1109 // Check if is a named color transform 1110 if (mpe != NULL) { 1111 1112 if (cmsStageType(mpe) == cmsSigNamedColorElemType) { 1113 return CreateNamedColorDevicelink(hTransform); 1114 } 1115 } 1116 1117 // First thing to do is to get a copy of the transformation 1118 LUT = cmsPipelineDup(xform ->Lut); 1119 if (LUT == NULL) return NULL; 1120 1121 // Time to fix the Lab2/Lab4 issue. 1122 if ((xform ->EntryColorSpace == cmsSigLabData) && (Version < 4.0)) { 1123 1124 if (!cmsPipelineInsertStage(LUT, cmsAT_BEGIN, _cmsStageAllocLabV2ToV4curves(ContextID))) 1125 goto Error; 1126 } 1127 1128 // On the output side too. Note that due to V2/V4 PCS encoding on lab we cannot fix white misalignments 1129 if ((xform ->ExitColorSpace) == cmsSigLabData && (Version < 4.0)) { 1130 1131 dwFlags |= cmsFLAGS_NOWHITEONWHITEFIXUP; 1132 if (!cmsPipelineInsertStage(LUT, cmsAT_END, _cmsStageAllocLabV4ToV2(ContextID))) 1133 goto Error; 1134 } 1135 1136 1137 hProfile = cmsCreateProfilePlaceholder(ContextID); 1138 if (!hProfile) goto Error; // can't allocate 1139 1140 cmsSetProfileVersion(hProfile, Version); 1141 1142 FixColorSpaces(hProfile, xform -> EntryColorSpace, xform -> ExitColorSpace, dwFlags); 1143 1144 // Optimize the LUT and precalculate a devicelink 1145 1146 ChansIn = cmsChannelsOf(xform -> EntryColorSpace); 1147 ChansOut = cmsChannelsOf(xform -> ExitColorSpace); 1148 1149 ColorSpaceBitsIn = _cmsLCMScolorSpace(xform -> EntryColorSpace); 1150 ColorSpaceBitsOut = _cmsLCMScolorSpace(xform -> ExitColorSpace); 1151 1152 FrmIn = COLORSPACE_SH(ColorSpaceBitsIn) | CHANNELS_SH(ChansIn)|BYTES_SH(2); 1153 FrmOut = COLORSPACE_SH(ColorSpaceBitsOut) | CHANNELS_SH(ChansOut)|BYTES_SH(2); 1154 1155 deviceClass = cmsGetDeviceClass(hProfile); 1156 1157 if (deviceClass == cmsSigOutputClass) 1158 DestinationTag = cmsSigBToA0Tag; 1159 else 1160 DestinationTag = cmsSigAToB0Tag; 1161 1162 // Check if the profile/version can store the result 1163 if (dwFlags & cmsFLAGS_FORCE_CLUT) 1164 AllowedLUT = NULL; 1165 else 1166 AllowedLUT = FindCombination(LUT, Version >= 4.0, DestinationTag); 1167 1168 if (AllowedLUT == NULL) { 1169 1170 // Try to optimize 1171 _cmsOptimizePipeline(ContextID, &LUT, xform ->RenderingIntent, &FrmIn, &FrmOut, &dwFlags); 1172 AllowedLUT = FindCombination(LUT, Version >= 4.0, DestinationTag); 1173 1174 } 1175 1176 // If no way, then force CLUT that for sure can be written 1177 if (AllowedLUT == NULL) { 1178 1179 cmsStage* FirstStage; 1180 cmsStage* LastStage; 1181 1182 dwFlags |= cmsFLAGS_FORCE_CLUT; 1183 _cmsOptimizePipeline(ContextID, &LUT, xform ->RenderingIntent, &FrmIn, &FrmOut, &dwFlags); 1184 1185 // Put identity curves if needed 1186 FirstStage = cmsPipelineGetPtrToFirstStage(LUT); 1187 if (FirstStage != NULL && FirstStage ->Type != cmsSigCurveSetElemType) 1188 if (!cmsPipelineInsertStage(LUT, cmsAT_BEGIN, _cmsStageAllocIdentityCurves(ContextID, ChansIn))) 1189 goto Error; 1190 1191 LastStage = cmsPipelineGetPtrToLastStage(LUT); 1192 if (LastStage != NULL && LastStage ->Type != cmsSigCurveSetElemType) 1193 if (!cmsPipelineInsertStage(LUT, cmsAT_END, _cmsStageAllocIdentityCurves(ContextID, ChansOut))) 1194 goto Error; 1195 1196 AllowedLUT = FindCombination(LUT, Version >= 4.0, DestinationTag); 1197 } 1198 1199 // Somethings is wrong... 1200 if (AllowedLUT == NULL) { 1201 goto Error; 1202 } 1203 1204 1205 if (dwFlags & cmsFLAGS_8BITS_DEVICELINK) 1206 cmsPipelineSetSaveAs8bitsFlag(LUT, TRUE); 1207 1208 // Tag profile with information 1209 if (!SetTextTags(hProfile, L"devicelink")) goto Error; 1210 1211 // Store result 1212 if (!cmsWriteTag(hProfile, DestinationTag, LUT)) goto Error; 1213 1214 1215 if (xform -> InputColorant != NULL) { 1216 if (!cmsWriteTag(hProfile, cmsSigColorantTableTag, xform->InputColorant)) goto Error; 1217 } 1218 1219 if (xform -> OutputColorant != NULL) { 1220 if (!cmsWriteTag(hProfile, cmsSigColorantTableOutTag, xform->OutputColorant)) goto Error; 1221 } 1222 1223 if ((deviceClass == cmsSigLinkClass) && (xform ->Sequence != NULL)) { 1224 if (!_cmsWriteProfileSequence(hProfile, xform ->Sequence)) goto Error; 1225 } 1226 1227 // Set the white point 1228 if (deviceClass == cmsSigInputClass) { 1229 if (!cmsWriteTag(hProfile, cmsSigMediaWhitePointTag, &xform ->EntryWhitePoint)) goto Error; 1230 } 1231 else { 1232 if (!cmsWriteTag(hProfile, cmsSigMediaWhitePointTag, &xform ->ExitWhitePoint)) goto Error; 1233 } 1234 1235 1236 // Per 7.2.15 in spec 4.3 1237 cmsSetHeaderRenderingIntent(hProfile, xform ->RenderingIntent); 1238 1239 cmsPipelineFree(LUT); 1240 return hProfile; 1241 1242 Error: 1243 if (LUT != NULL) cmsPipelineFree(LUT); 1244 cmsCloseProfile(hProfile); 1245 return NULL; 1246 }