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