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 }