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