1 /* 2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 3 * 4 * This code is free software; you can redistribute it and/or modify it 5 * under the terms of the GNU General Public License version 2 only, as 6 * published by the Free Software Foundation. Oracle designates this 7 * particular file as subject to the "Classpath" exception as provided 8 * by Oracle in the LICENSE file that accompanied this code. 9 * 10 * This code is distributed in the hope that it will be useful, but WITHOUT 11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 13 * version 2 for more details (a copy is included in the LICENSE file that 14 * accompanied this code). 15 * 16 * You should have received a copy of the GNU General Public License version 17 * 2 along with this work; if not, write to the Free Software Foundation, 18 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 19 * 20 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 21 * or visit www.oracle.com if you need additional information or have any 22 * questions. 23 */ 24 25 // This file is available under and governed by the GNU General Public 26 // License version 2 only, as published by the Free Software Foundation. 27 // However, the following notice accompanied the original version of this 28 // file: 29 // 30 //--------------------------------------------------------------------------------- 31 // 32 // Little Color Management System 33 // Copyright (c) 1998-2020 Marti Maria Saguer 34 // 35 // Permission is hereby granted, free of charge, to any person obtaining 36 // a copy of this software and associated documentation files (the "Software"), 37 // to deal in the Software without restriction, including without limitation 38 // the rights to use, copy, modify, merge, publish, distribute, sublicense, 39 // and/or sell copies of the Software, and to permit persons to whom the Software 40 // is furnished to do so, subject to the following conditions: 41 // 42 // The above copyright notice and this permission notice shall be included in 43 // all copies or substantial portions of the Software. 44 // 45 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 46 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO 47 // THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 48 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 49 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 50 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 51 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 52 // 53 //--------------------------------------------------------------------------------- 54 // 55 56 #include "lcms2_internal.h" 57 58 // Transformations stuff 59 // ----------------------------------------------------------------------- 60 61 #define DEFAULT_OBSERVER_ADAPTATION_STATE 1.0 62 63 // The Context0 observer adaptation state. 64 _cmsAdaptationStateChunkType _cmsAdaptationStateChunk = { DEFAULT_OBSERVER_ADAPTATION_STATE }; 65 66 // Init and duplicate observer adaptation state 67 void _cmsAllocAdaptationStateChunk(struct _cmsContext_struct* ctx, 68 const struct _cmsContext_struct* src) 69 { 70 static _cmsAdaptationStateChunkType AdaptationStateChunk = { DEFAULT_OBSERVER_ADAPTATION_STATE }; 71 void* from; 72 73 if (src != NULL) { 74 from = src ->chunks[AdaptationStateContext]; 75 } 76 else { 77 from = &AdaptationStateChunk; 78 } 79 80 ctx ->chunks[AdaptationStateContext] = _cmsSubAllocDup(ctx ->MemPool, from, sizeof(_cmsAdaptationStateChunkType)); 81 } 82 83 84 // Sets adaptation state for absolute colorimetric intent in the given context. Adaptation state applies on all 85 // but cmsCreateExtendedTransformTHR(). Little CMS can handle incomplete adaptation states. 86 cmsFloat64Number CMSEXPORT cmsSetAdaptationStateTHR(cmsContext ContextID, cmsFloat64Number d) 87 { 88 cmsFloat64Number prev; 89 _cmsAdaptationStateChunkType* ptr = (_cmsAdaptationStateChunkType*) _cmsContextGetClientChunk(ContextID, AdaptationStateContext); 90 91 // Get previous value for return 92 prev = ptr ->AdaptationState; 93 94 // Set the value if d is positive or zero 95 if (d >= 0.0) { 96 97 ptr ->AdaptationState = d; 98 } 99 100 // Always return previous value 101 return prev; 102 } 103 104 105 // The adaptation state may be defaulted by this function. If you don't like it, use the extended transform routine 106 cmsFloat64Number CMSEXPORT cmsSetAdaptationState(cmsFloat64Number d) 107 { 108 return cmsSetAdaptationStateTHR(NULL, d); 109 } 110 111 // ----------------------------------------------------------------------- 112 113 // Alarm codes for 16-bit transformations, because the fixed range of containers there are 114 // no values left to mark out of gamut. 115 116 #define DEFAULT_ALARM_CODES_VALUE {0x7F00, 0x7F00, 0x7F00, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} 117 118 _cmsAlarmCodesChunkType _cmsAlarmCodesChunk = { DEFAULT_ALARM_CODES_VALUE }; 119 120 // Sets the codes used to mark out-out-gamut on Proofing transforms for a given context. Values are meant to be 121 // encoded in 16 bits. 122 void CMSEXPORT cmsSetAlarmCodesTHR(cmsContext ContextID, const cmsUInt16Number AlarmCodesP[cmsMAXCHANNELS]) 123 { 124 _cmsAlarmCodesChunkType* ContextAlarmCodes = (_cmsAlarmCodesChunkType*) _cmsContextGetClientChunk(ContextID, AlarmCodesContext); 125 126 _cmsAssert(ContextAlarmCodes != NULL); // Can't happen 127 128 memcpy(ContextAlarmCodes->AlarmCodes, AlarmCodesP, sizeof(ContextAlarmCodes->AlarmCodes)); 129 } 130 131 // Gets the current codes used to mark out-out-gamut on Proofing transforms for the given context. 132 // Values are meant to be encoded in 16 bits. 133 void CMSEXPORT cmsGetAlarmCodesTHR(cmsContext ContextID, cmsUInt16Number AlarmCodesP[cmsMAXCHANNELS]) 134 { 135 _cmsAlarmCodesChunkType* ContextAlarmCodes = (_cmsAlarmCodesChunkType*) _cmsContextGetClientChunk(ContextID, AlarmCodesContext); 136 137 _cmsAssert(ContextAlarmCodes != NULL); // Can't happen 138 139 memcpy(AlarmCodesP, ContextAlarmCodes->AlarmCodes, sizeof(ContextAlarmCodes->AlarmCodes)); 140 } 141 142 void CMSEXPORT cmsSetAlarmCodes(const cmsUInt16Number NewAlarm[cmsMAXCHANNELS]) 143 { 144 _cmsAssert(NewAlarm != NULL); 145 146 cmsSetAlarmCodesTHR(NULL, NewAlarm); 147 } 148 149 void CMSEXPORT cmsGetAlarmCodes(cmsUInt16Number OldAlarm[cmsMAXCHANNELS]) 150 { 151 _cmsAssert(OldAlarm != NULL); 152 cmsGetAlarmCodesTHR(NULL, OldAlarm); 153 } 154 155 156 // Init and duplicate alarm codes 157 void _cmsAllocAlarmCodesChunk(struct _cmsContext_struct* ctx, 158 const struct _cmsContext_struct* src) 159 { 160 static _cmsAlarmCodesChunkType AlarmCodesChunk = { DEFAULT_ALARM_CODES_VALUE }; 161 void* from; 162 163 if (src != NULL) { 164 from = src ->chunks[AlarmCodesContext]; 165 } 166 else { 167 from = &AlarmCodesChunk; 168 } 169 170 ctx ->chunks[AlarmCodesContext] = _cmsSubAllocDup(ctx ->MemPool, from, sizeof(_cmsAlarmCodesChunkType)); 171 } 172 173 // ----------------------------------------------------------------------- 174 175 // Get rid of transform resources 176 void CMSEXPORT cmsDeleteTransform(cmsHTRANSFORM hTransform) 177 { 178 _cmsTRANSFORM* p = (_cmsTRANSFORM*) hTransform; 179 180 _cmsAssert(p != NULL); 181 182 if (p -> GamutCheck) 183 cmsPipelineFree(p -> GamutCheck); 184 185 if (p -> Lut) 186 cmsPipelineFree(p -> Lut); 187 188 if (p ->InputColorant) 189 cmsFreeNamedColorList(p ->InputColorant); 190 191 if (p -> OutputColorant) 192 cmsFreeNamedColorList(p ->OutputColorant); 193 194 if (p ->Sequence) 195 cmsFreeProfileSequenceDescription(p ->Sequence); 196 197 if (p ->UserData) 198 p ->FreeUserData(p ->ContextID, p ->UserData); 199 200 _cmsFree(p ->ContextID, (void *) p); 201 } 202 203 204 static 205 cmsUInt32Number PixelSize(cmsUInt32Number Format) 206 { 207 cmsUInt32Number fmt_bytes = T_BYTES(Format); 208 209 // For double, the T_BYTES field is zero 210 if (fmt_bytes == 0) 211 return sizeof(cmsUInt64Number); 212 213 // Otherwise, it is already correct for all formats 214 return fmt_bytes; 215 } 216 217 218 219 220 // Apply transform. 221 void CMSEXPORT cmsDoTransform(cmsHTRANSFORM Transform, 222 const void* InputBuffer, 223 void* OutputBuffer, 224 cmsUInt32Number Size) 225 226 { 227 _cmsTRANSFORM* p = (_cmsTRANSFORM*) Transform; 228 cmsStride stride; 229 230 stride.BytesPerLineIn = 0; // Not used 231 stride.BytesPerLineOut = 0; 232 stride.BytesPerPlaneIn = Size * PixelSize(p->InputFormat); 233 stride.BytesPerPlaneOut = Size * PixelSize(p->OutputFormat); 234 235 p -> xform(p, InputBuffer, OutputBuffer, Size, 1, &stride); 236 } 237 238 239 // This is a legacy stride for planar 240 void CMSEXPORT cmsDoTransformStride(cmsHTRANSFORM Transform, 241 const void* InputBuffer, 242 void* OutputBuffer, 243 cmsUInt32Number Size, cmsUInt32Number Stride) 244 245 { 246 _cmsTRANSFORM* p = (_cmsTRANSFORM*) Transform; 247 cmsStride stride; 248 249 stride.BytesPerLineIn = 0; 250 stride.BytesPerLineOut = 0; 251 stride.BytesPerPlaneIn = Stride; 252 stride.BytesPerPlaneOut = Stride; 253 254 p -> xform(p, InputBuffer, OutputBuffer, Size, 1, &stride); 255 } 256 257 // This is the "fast" function for plugins 258 void CMSEXPORT cmsDoTransformLineStride(cmsHTRANSFORM Transform, 259 const void* InputBuffer, 260 void* OutputBuffer, 261 cmsUInt32Number PixelsPerLine, 262 cmsUInt32Number LineCount, 263 cmsUInt32Number BytesPerLineIn, 264 cmsUInt32Number BytesPerLineOut, 265 cmsUInt32Number BytesPerPlaneIn, 266 cmsUInt32Number BytesPerPlaneOut) 267 268 { 269 _cmsTRANSFORM* p = (_cmsTRANSFORM*) Transform; 270 cmsStride stride; 271 272 stride.BytesPerLineIn = BytesPerLineIn; 273 stride.BytesPerLineOut = BytesPerLineOut; 274 stride.BytesPerPlaneIn = BytesPerPlaneIn; 275 stride.BytesPerPlaneOut = BytesPerPlaneOut; 276 277 p->xform(p, InputBuffer, OutputBuffer, PixelsPerLine, LineCount, &stride); 278 } 279 280 281 282 // Transform routines ---------------------------------------------------------------------------------------------------------- 283 284 // Float xform converts floats. Since there are no performance issues, one routine does all job, including gamut check. 285 // Note that because extended range, we can use a -1.0 value for out of gamut in this case. 286 static 287 void FloatXFORM(_cmsTRANSFORM* p, 288 const void* in, 289 void* out, 290 cmsUInt32Number PixelsPerLine, 291 cmsUInt32Number LineCount, 292 const cmsStride* Stride) 293 { 294 cmsUInt8Number* accum; 295 cmsUInt8Number* output; 296 cmsFloat32Number fIn[cmsMAXCHANNELS], fOut[cmsMAXCHANNELS]; 297 cmsFloat32Number OutOfGamut; 298 cmsUInt32Number i, j, c, strideIn, strideOut; 299 300 _cmsHandleExtraChannels(p, in, out, PixelsPerLine, LineCount, Stride); 301 302 strideIn = 0; 303 strideOut = 0; 304 memset(fIn, 0, sizeof(fIn)); 305 memset(fOut, 0, sizeof(fIn)); 306 307 for (i = 0; i < LineCount; i++) { 308 309 accum = (cmsUInt8Number*)in + strideIn; 310 output = (cmsUInt8Number*)out + strideOut; 311 312 for (j = 0; j < PixelsPerLine; j++) { 313 314 accum = p->FromInputFloat(p, fIn, accum, Stride->BytesPerPlaneIn); 315 316 // Any gamut chack to do? 317 if (p->GamutCheck != NULL) { 318 319 // Evaluate gamut marker. 320 cmsPipelineEvalFloat(fIn, &OutOfGamut, p->GamutCheck); 321 322 // Is current color out of gamut? 323 if (OutOfGamut > 0.0) { 324 325 // Certainly, out of gamut 326 for (c = 0; c < cmsMAXCHANNELS; c++) 327 fOut[c] = -1.0; 328 329 } 330 else { 331 // No, proceed normally 332 cmsPipelineEvalFloat(fIn, fOut, p->Lut); 333 } 334 } 335 else { 336 337 // No gamut check at all 338 cmsPipelineEvalFloat(fIn, fOut, p->Lut); 339 } 340 341 342 output = p->ToOutputFloat(p, fOut, output, Stride->BytesPerPlaneOut); 343 } 344 345 strideIn += Stride->BytesPerLineIn; 346 strideOut += Stride->BytesPerLineOut; 347 } 348 349 } 350 351 352 static 353 void NullFloatXFORM(_cmsTRANSFORM* p, 354 const void* in, 355 void* out, 356 cmsUInt32Number PixelsPerLine, 357 cmsUInt32Number LineCount, 358 const cmsStride* Stride) 359 360 { 361 cmsUInt8Number* accum; 362 cmsUInt8Number* output; 363 cmsFloat32Number fIn[cmsMAXCHANNELS]; 364 cmsUInt32Number i, j, strideIn, strideOut; 365 366 _cmsHandleExtraChannels(p, in, out, PixelsPerLine, LineCount, Stride); 367 368 strideIn = 0; 369 strideOut = 0; 370 memset(fIn, 0, sizeof(fIn)); 371 372 for (i = 0; i < LineCount; i++) { 373 374 accum = (cmsUInt8Number*) in + strideIn; 375 output = (cmsUInt8Number*) out + strideOut; 376 377 for (j = 0; j < PixelsPerLine; j++) { 378 379 accum = p->FromInputFloat(p, fIn, accum, Stride ->BytesPerPlaneIn); 380 output = p->ToOutputFloat(p, fIn, output, Stride->BytesPerPlaneOut); 381 } 382 383 strideIn += Stride->BytesPerLineIn; 384 strideOut += Stride->BytesPerLineOut; 385 } 386 } 387 388 // 16 bit precision ----------------------------------------------------------------------------------------------------------- 389 390 // Null transformation, only applies formatters. No cache 391 static 392 void NullXFORM(_cmsTRANSFORM* p, 393 const void* in, 394 void* out, 395 cmsUInt32Number PixelsPerLine, 396 cmsUInt32Number LineCount, 397 const cmsStride* Stride) 398 { 399 cmsUInt8Number* accum; 400 cmsUInt8Number* output; 401 cmsUInt16Number wIn[cmsMAXCHANNELS]; 402 cmsUInt32Number i, j, strideIn, strideOut; 403 404 _cmsHandleExtraChannels(p, in, out, PixelsPerLine, LineCount, Stride); 405 406 strideIn = 0; 407 strideOut = 0; 408 memset(wIn, 0, sizeof(wIn)); 409 410 for (i = 0; i < LineCount; i++) { 411 412 accum = (cmsUInt8Number*)in + strideIn; 413 output = (cmsUInt8Number*)out + strideOut; 414 415 for (j = 0; j < PixelsPerLine; j++) { 416 417 accum = p->FromInput(p, wIn, accum, Stride->BytesPerPlaneIn); 418 output = p->ToOutput(p, wIn, output, Stride->BytesPerPlaneOut); 419 } 420 421 strideIn += Stride->BytesPerLineIn; 422 strideOut += Stride->BytesPerLineOut; 423 } 424 425 } 426 427 428 // No gamut check, no cache, 16 bits 429 static 430 void PrecalculatedXFORM(_cmsTRANSFORM* p, 431 const void* in, 432 void* out, 433 cmsUInt32Number PixelsPerLine, 434 cmsUInt32Number LineCount, 435 const cmsStride* Stride) 436 { 437 CMSREGISTER cmsUInt8Number* accum; 438 CMSREGISTER cmsUInt8Number* output; 439 cmsUInt16Number wIn[cmsMAXCHANNELS], wOut[cmsMAXCHANNELS]; 440 cmsUInt32Number i, j, strideIn, strideOut; 441 442 _cmsHandleExtraChannels(p, in, out, PixelsPerLine, LineCount, Stride); 443 444 strideIn = 0; 445 strideOut = 0; 446 memset(wIn, 0, sizeof(wIn)); 447 memset(wOut, 0, sizeof(wOut)); 448 449 for (i = 0; i < LineCount; i++) { 450 451 accum = (cmsUInt8Number*)in + strideIn; 452 output = (cmsUInt8Number*)out + strideOut; 453 454 for (j = 0; j < PixelsPerLine; j++) { 455 456 accum = p->FromInput(p, wIn, accum, Stride->BytesPerPlaneIn); 457 p->Lut->Eval16Fn(wIn, wOut, p->Lut->Data); 458 output = p->ToOutput(p, wOut, output, Stride->BytesPerPlaneOut); 459 } 460 461 strideIn += Stride->BytesPerLineIn; 462 strideOut += Stride->BytesPerLineOut; 463 } 464 465 } 466 467 468 // Auxiliary: Handle precalculated gamut check. The retrieval of context may be alittle bit slow, but this function is not critical. 469 static 470 void TransformOnePixelWithGamutCheck(_cmsTRANSFORM* p, 471 const cmsUInt16Number wIn[], 472 cmsUInt16Number wOut[]) 473 { 474 cmsUInt16Number wOutOfGamut; 475 476 p ->GamutCheck ->Eval16Fn(wIn, &wOutOfGamut, p ->GamutCheck ->Data); 477 if (wOutOfGamut >= 1) { 478 479 cmsUInt16Number i; 480 _cmsAlarmCodesChunkType* ContextAlarmCodes = (_cmsAlarmCodesChunkType*) _cmsContextGetClientChunk(p->ContextID, AlarmCodesContext); 481 482 for (i=0; i < p ->Lut->OutputChannels; i++) { 483 484 wOut[i] = ContextAlarmCodes ->AlarmCodes[i]; 485 } 486 } 487 else 488 p ->Lut ->Eval16Fn(wIn, wOut, p -> Lut->Data); 489 } 490 491 // Gamut check, No cache, 16 bits. 492 static 493 void PrecalculatedXFORMGamutCheck(_cmsTRANSFORM* p, 494 const void* in, 495 void* out, 496 cmsUInt32Number PixelsPerLine, 497 cmsUInt32Number LineCount, 498 const cmsStride* Stride) 499 { 500 cmsUInt8Number* accum; 501 cmsUInt8Number* output; 502 cmsUInt16Number wIn[cmsMAXCHANNELS], wOut[cmsMAXCHANNELS]; 503 cmsUInt32Number i, j, strideIn, strideOut; 504 505 _cmsHandleExtraChannels(p, in, out, PixelsPerLine, LineCount, Stride); 506 507 strideIn = 0; 508 strideOut = 0; 509 memset(wIn, 0, sizeof(wIn)); 510 memset(wOut, 0, sizeof(wOut)); 511 512 for (i = 0; i < LineCount; i++) { 513 514 accum = (cmsUInt8Number*)in + strideIn; 515 output = (cmsUInt8Number*)out + strideOut; 516 517 for (j = 0; j < PixelsPerLine; j++) { 518 519 accum = p->FromInput(p, wIn, accum, Stride->BytesPerPlaneIn); 520 TransformOnePixelWithGamutCheck(p, wIn, wOut); 521 output = p->ToOutput(p, wOut, output, Stride->BytesPerPlaneOut); 522 } 523 524 strideIn += Stride->BytesPerLineIn; 525 strideOut += Stride->BytesPerLineOut; 526 } 527 } 528 529 530 // No gamut check, Cache, 16 bits, 531 static 532 void CachedXFORM(_cmsTRANSFORM* p, 533 const void* in, 534 void* out, 535 cmsUInt32Number PixelsPerLine, 536 cmsUInt32Number LineCount, 537 const cmsStride* Stride) 538 { 539 cmsUInt8Number* accum; 540 cmsUInt8Number* output; 541 cmsUInt16Number wIn[cmsMAXCHANNELS], wOut[cmsMAXCHANNELS]; 542 _cmsCACHE Cache; 543 cmsUInt32Number i, j, strideIn, strideOut; 544 545 _cmsHandleExtraChannels(p, in, out, PixelsPerLine, LineCount, Stride); 546 547 // Empty buffers for quick memcmp 548 memset(wIn, 0, sizeof(wIn)); 549 memset(wOut, 0, sizeof(wOut)); 550 551 // Get copy of zero cache 552 memcpy(&Cache, &p->Cache, sizeof(Cache)); 553 554 strideIn = 0; 555 strideOut = 0; 556 557 for (i = 0; i < LineCount; i++) { 558 559 accum = (cmsUInt8Number*)in + strideIn; 560 output = (cmsUInt8Number*)out + strideOut; 561 562 for (j = 0; j < PixelsPerLine; j++) { 563 564 accum = p->FromInput(p, wIn, accum, Stride->BytesPerPlaneIn); 565 566 if (memcmp(wIn, Cache.CacheIn, sizeof(Cache.CacheIn)) == 0) { 567 568 memcpy(wOut, Cache.CacheOut, sizeof(Cache.CacheOut)); 569 } 570 else { 571 p->Lut->Eval16Fn(wIn, wOut, p->Lut->Data); 572 573 memcpy(Cache.CacheIn, wIn, sizeof(Cache.CacheIn)); 574 memcpy(Cache.CacheOut, wOut, sizeof(Cache.CacheOut)); 575 } 576 577 output = p->ToOutput(p, wOut, output, Stride->BytesPerPlaneOut); 578 } 579 580 strideIn += Stride->BytesPerLineIn; 581 strideOut += Stride->BytesPerLineOut; 582 } 583 } 584 585 // All those nice features together 586 static 587 void CachedXFORMGamutCheck(_cmsTRANSFORM* p, 588 const void* in, 589 void* out, 590 cmsUInt32Number PixelsPerLine, 591 cmsUInt32Number LineCount, 592 const cmsStride* Stride) 593 { 594 cmsUInt8Number* accum; 595 cmsUInt8Number* output; 596 cmsUInt16Number wIn[cmsMAXCHANNELS], wOut[cmsMAXCHANNELS]; 597 _cmsCACHE Cache; 598 cmsUInt32Number i, j, strideIn, strideOut; 599 600 _cmsHandleExtraChannels(p, in, out, PixelsPerLine, LineCount, Stride); 601 602 // Empty buffers for quick memcmp 603 memset(wIn, 0, sizeof(wIn)); 604 memset(wOut, 0, sizeof(wOut)); 605 606 // Get copy of zero cache 607 memcpy(&Cache, &p->Cache, sizeof(Cache)); 608 609 strideIn = 0; 610 strideOut = 0; 611 612 for (i = 0; i < LineCount; i++) { 613 614 accum = (cmsUInt8Number*)in + strideIn; 615 output = (cmsUInt8Number*)out + strideOut; 616 617 for (j = 0; j < PixelsPerLine; j++) { 618 619 accum = p->FromInput(p, wIn, accum, Stride->BytesPerPlaneIn); 620 621 if (memcmp(wIn, Cache.CacheIn, sizeof(Cache.CacheIn)) == 0) { 622 623 memcpy(wOut, Cache.CacheOut, sizeof(Cache.CacheOut)); 624 } 625 else { 626 TransformOnePixelWithGamutCheck(p, wIn, wOut); 627 628 memcpy(Cache.CacheIn, wIn, sizeof(Cache.CacheIn)); 629 memcpy(Cache.CacheOut, wOut, sizeof(Cache.CacheOut)); 630 } 631 632 output = p->ToOutput(p, wOut, output, Stride->BytesPerPlaneOut); 633 } 634 635 strideIn += Stride->BytesPerLineIn; 636 strideOut += Stride->BytesPerLineOut; 637 } 638 } 639 640 // Transform plug-ins ---------------------------------------------------------------------------------------------------- 641 642 // List of used-defined transform factories 643 typedef struct _cmsTransformCollection_st { 644 645 _cmsTransform2Factory Factory; 646 cmsBool OldXform; // Factory returns xform function in the old style 647 648 struct _cmsTransformCollection_st *Next; 649 650 } _cmsTransformCollection; 651 652 // The linked list head 653 _cmsTransformPluginChunkType _cmsTransformPluginChunk = { NULL }; 654 655 656 // Duplicates the zone of memory used by the plug-in in the new context 657 static 658 void DupPluginTransformList(struct _cmsContext_struct* ctx, 659 const struct _cmsContext_struct* src) 660 { 661 _cmsTransformPluginChunkType newHead = { NULL }; 662 _cmsTransformCollection* entry; 663 _cmsTransformCollection* Anterior = NULL; 664 _cmsTransformPluginChunkType* head = (_cmsTransformPluginChunkType*) src->chunks[TransformPlugin]; 665 666 // Walk the list copying all nodes 667 for (entry = head->TransformCollection; 668 entry != NULL; 669 entry = entry ->Next) { 670 671 _cmsTransformCollection *newEntry = ( _cmsTransformCollection *) _cmsSubAllocDup(ctx ->MemPool, entry, sizeof(_cmsTransformCollection)); 672 673 if (newEntry == NULL) 674 return; 675 676 // We want to keep the linked list order, so this is a little bit tricky 677 newEntry -> Next = NULL; 678 if (Anterior) 679 Anterior -> Next = newEntry; 680 681 Anterior = newEntry; 682 683 if (newHead.TransformCollection == NULL) 684 newHead.TransformCollection = newEntry; 685 } 686 687 ctx ->chunks[TransformPlugin] = _cmsSubAllocDup(ctx->MemPool, &newHead, sizeof(_cmsTransformPluginChunkType)); 688 } 689 690 // Allocates memory for transform plugin factory 691 void _cmsAllocTransformPluginChunk(struct _cmsContext_struct* ctx, 692 const struct _cmsContext_struct* src) 693 { 694 if (src != NULL) { 695 696 // Copy all linked list 697 DupPluginTransformList(ctx, src); 698 } 699 else { 700 static _cmsTransformPluginChunkType TransformPluginChunkType = { NULL }; 701 ctx ->chunks[TransformPlugin] = _cmsSubAllocDup(ctx ->MemPool, &TransformPluginChunkType, sizeof(_cmsTransformPluginChunkType)); 702 } 703 } 704 705 // Adaptor for old versions of plug-in 706 static 707 void _cmsTransform2toTransformAdaptor(struct _cmstransform_struct *CMMcargo, 708 const void* InputBuffer, 709 void* OutputBuffer, 710 cmsUInt32Number PixelsPerLine, 711 cmsUInt32Number LineCount, 712 const cmsStride* Stride) 713 { 714 715 cmsUInt32Number i, strideIn, strideOut; 716 717 _cmsHandleExtraChannels(CMMcargo, InputBuffer, OutputBuffer, PixelsPerLine, LineCount, Stride); 718 719 strideIn = 0; 720 strideOut = 0; 721 722 for (i = 0; i < LineCount; i++) { 723 724 void *accum = (cmsUInt8Number*)InputBuffer + strideIn; 725 void *output = (cmsUInt8Number*)OutputBuffer + strideOut; 726 727 CMMcargo->OldXform(CMMcargo, accum, output, PixelsPerLine, Stride->BytesPerPlaneIn); 728 729 strideIn += Stride->BytesPerLineIn; 730 strideOut += Stride->BytesPerLineOut; 731 } 732 } 733 734 735 736 // Register new ways to transform 737 cmsBool _cmsRegisterTransformPlugin(cmsContext ContextID, cmsPluginBase* Data) 738 { 739 cmsPluginTransform* Plugin = (cmsPluginTransform*) Data; 740 _cmsTransformCollection* fl; 741 _cmsTransformPluginChunkType* ctx = ( _cmsTransformPluginChunkType*) _cmsContextGetClientChunk(ContextID,TransformPlugin); 742 743 if (Data == NULL) { 744 745 // Free the chain. Memory is safely freed at exit 746 ctx->TransformCollection = NULL; 747 return TRUE; 748 } 749 750 // Factory callback is required 751 if (Plugin->factories.xform == NULL) return FALSE; 752 753 754 fl = (_cmsTransformCollection*) _cmsPluginMalloc(ContextID, sizeof(_cmsTransformCollection)); 755 if (fl == NULL) return FALSE; 756 757 // Check for full xform plug-ins previous to 2.8, we would need an adapter in that case 758 if (Plugin->base.ExpectedVersion < 2080) { 759 760 fl->OldXform = TRUE; 761 } 762 else 763 fl->OldXform = FALSE; 764 765 // Copy the parameters 766 fl->Factory = Plugin->factories.xform; 767 768 // Keep linked list 769 fl ->Next = ctx->TransformCollection; 770 ctx->TransformCollection = fl; 771 772 // All is ok 773 return TRUE; 774 } 775 776 777 void CMSEXPORT _cmsSetTransformUserData(struct _cmstransform_struct *CMMcargo, void* ptr, _cmsFreeUserDataFn FreePrivateDataFn) 778 { 779 _cmsAssert(CMMcargo != NULL); 780 CMMcargo ->UserData = ptr; 781 CMMcargo ->FreeUserData = FreePrivateDataFn; 782 } 783 784 // returns the pointer defined by the plug-in to store private data 785 void * CMSEXPORT _cmsGetTransformUserData(struct _cmstransform_struct *CMMcargo) 786 { 787 _cmsAssert(CMMcargo != NULL); 788 return CMMcargo ->UserData; 789 } 790 791 // returns the current formatters 792 void CMSEXPORT _cmsGetTransformFormatters16(struct _cmstransform_struct *CMMcargo, cmsFormatter16* FromInput, cmsFormatter16* ToOutput) 793 { 794 _cmsAssert(CMMcargo != NULL); 795 if (FromInput) *FromInput = CMMcargo ->FromInput; 796 if (ToOutput) *ToOutput = CMMcargo ->ToOutput; 797 } 798 799 void CMSEXPORT _cmsGetTransformFormattersFloat(struct _cmstransform_struct *CMMcargo, cmsFormatterFloat* FromInput, cmsFormatterFloat* ToOutput) 800 { 801 _cmsAssert(CMMcargo != NULL); 802 if (FromInput) *FromInput = CMMcargo ->FromInputFloat; 803 if (ToOutput) *ToOutput = CMMcargo ->ToOutputFloat; 804 } 805 806 807 // Allocate transform struct and set it to defaults. Ask the optimization plug-in about if those formats are proper 808 // for separated transforms. If this is the case, 809 static 810 _cmsTRANSFORM* AllocEmptyTransform(cmsContext ContextID, cmsPipeline* lut, 811 cmsUInt32Number Intent, cmsUInt32Number* InputFormat, cmsUInt32Number* OutputFormat, cmsUInt32Number* dwFlags) 812 { 813 _cmsTransformPluginChunkType* ctx = ( _cmsTransformPluginChunkType*) _cmsContextGetClientChunk(ContextID, TransformPlugin); 814 _cmsTransformCollection* Plugin; 815 816 // Allocate needed memory 817 _cmsTRANSFORM* p = (_cmsTRANSFORM*)_cmsMallocZero(ContextID, sizeof(_cmsTRANSFORM)); 818 if (!p) { 819 cmsPipelineFree(lut); 820 return NULL; 821 } 822 823 // Store the proposed pipeline 824 p->Lut = lut; 825 826 // Let's see if any plug-in want to do the transform by itself 827 if (p->Lut != NULL) { 828 829 if (!(*dwFlags & cmsFLAGS_NOOPTIMIZE)) 830 { 831 for (Plugin = ctx->TransformCollection; 832 Plugin != NULL; 833 Plugin = Plugin->Next) { 834 835 if (Plugin->Factory(&p->xform, &p->UserData, &p->FreeUserData, &p->Lut, InputFormat, OutputFormat, dwFlags)) { 836 837 // Last plugin in the declaration order takes control. We just keep 838 // the original parameters as a logging. 839 // Note that cmsFLAGS_CAN_CHANGE_FORMATTER is not set, so by default 840 // an optimized transform is not reusable. The plug-in can, however, change 841 // the flags and make it suitable. 842 843 p->ContextID = ContextID; 844 p->InputFormat = *InputFormat; 845 p->OutputFormat = *OutputFormat; 846 p->dwOriginalFlags = *dwFlags; 847 848 // Fill the formatters just in case the optimized routine is interested. 849 // No error is thrown if the formatter doesn't exist. It is up to the optimization 850 // factory to decide what to do in those cases. 851 p->FromInput = _cmsGetFormatter(ContextID, *InputFormat, cmsFormatterInput, CMS_PACK_FLAGS_16BITS).Fmt16; 852 p->ToOutput = _cmsGetFormatter(ContextID, *OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_16BITS).Fmt16; 853 p->FromInputFloat = _cmsGetFormatter(ContextID, *InputFormat, cmsFormatterInput, CMS_PACK_FLAGS_FLOAT).FmtFloat; 854 p->ToOutputFloat = _cmsGetFormatter(ContextID, *OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_FLOAT).FmtFloat; 855 856 // Save the day? (Ignore the warning) 857 if (Plugin->OldXform) { 858 p->OldXform = (_cmsTransformFn)(void*)p->xform; 859 p->xform = _cmsTransform2toTransformAdaptor; 860 } 861 862 return p; 863 } 864 } 865 } 866 867 // Not suitable for the transform plug-in, let's check the pipeline plug-in 868 _cmsOptimizePipeline(ContextID, &p->Lut, Intent, InputFormat, OutputFormat, dwFlags); 869 } 870 871 // Check whatever this is a true floating point transform 872 if (_cmsFormatterIsFloat(*InputFormat) && _cmsFormatterIsFloat(*OutputFormat)) { 873 874 // Get formatter function always return a valid union, but the contents of this union may be NULL. 875 p ->FromInputFloat = _cmsGetFormatter(ContextID, *InputFormat, cmsFormatterInput, CMS_PACK_FLAGS_FLOAT).FmtFloat; 876 p ->ToOutputFloat = _cmsGetFormatter(ContextID, *OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_FLOAT).FmtFloat; 877 *dwFlags |= cmsFLAGS_CAN_CHANGE_FORMATTER; 878 879 if (p ->FromInputFloat == NULL || p ->ToOutputFloat == NULL) { 880 881 cmsSignalError(ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported raster format"); 882 cmsDeleteTransform(p); 883 return NULL; 884 } 885 886 if (*dwFlags & cmsFLAGS_NULLTRANSFORM) { 887 888 p ->xform = NullFloatXFORM; 889 } 890 else { 891 // Float transforms don't use cache, always are non-NULL 892 p ->xform = FloatXFORM; 893 } 894 895 } 896 else { 897 898 if (*InputFormat == 0 && *OutputFormat == 0) { 899 p ->FromInput = p ->ToOutput = NULL; 900 *dwFlags |= cmsFLAGS_CAN_CHANGE_FORMATTER; 901 } 902 else { 903 904 cmsUInt32Number BytesPerPixelInput; 905 906 p ->FromInput = _cmsGetFormatter(ContextID, *InputFormat, cmsFormatterInput, CMS_PACK_FLAGS_16BITS).Fmt16; 907 p ->ToOutput = _cmsGetFormatter(ContextID, *OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_16BITS).Fmt16; 908 909 if (p ->FromInput == NULL || p ->ToOutput == NULL) { 910 911 cmsSignalError(ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported raster format"); 912 cmsDeleteTransform(p); 913 return NULL; 914 } 915 916 BytesPerPixelInput = T_BYTES(p ->InputFormat); 917 if (BytesPerPixelInput == 0 || BytesPerPixelInput >= 2) 918 *dwFlags |= cmsFLAGS_CAN_CHANGE_FORMATTER; 919 920 } 921 922 if (*dwFlags & cmsFLAGS_NULLTRANSFORM) { 923 924 p ->xform = NullXFORM; 925 } 926 else { 927 if (*dwFlags & cmsFLAGS_NOCACHE) { 928 929 if (*dwFlags & cmsFLAGS_GAMUTCHECK) 930 p ->xform = PrecalculatedXFORMGamutCheck; // Gamut check, no cache 931 else 932 p ->xform = PrecalculatedXFORM; // No cache, no gamut check 933 } 934 else { 935 936 if (*dwFlags & cmsFLAGS_GAMUTCHECK) 937 p ->xform = CachedXFORMGamutCheck; // Gamut check, cache 938 else 939 p ->xform = CachedXFORM; // No gamut check, cache 940 941 } 942 } 943 } 944 945 p ->InputFormat = *InputFormat; 946 p ->OutputFormat = *OutputFormat; 947 p ->dwOriginalFlags = *dwFlags; 948 p ->ContextID = ContextID; 949 p ->UserData = NULL; 950 return p; 951 } 952 953 static 954 cmsBool GetXFormColorSpaces(cmsUInt32Number nProfiles, cmsHPROFILE hProfiles[], cmsColorSpaceSignature* Input, cmsColorSpaceSignature* Output) 955 { 956 cmsColorSpaceSignature ColorSpaceIn, ColorSpaceOut; 957 cmsColorSpaceSignature PostColorSpace; 958 cmsUInt32Number i; 959 960 if (nProfiles == 0) return FALSE; 961 if (hProfiles[0] == NULL) return FALSE; 962 963 *Input = PostColorSpace = cmsGetColorSpace(hProfiles[0]); 964 965 for (i=0; i < nProfiles; i++) { 966 967 cmsProfileClassSignature cls; 968 cmsHPROFILE hProfile = hProfiles[i]; 969 970 int lIsInput = (PostColorSpace != cmsSigXYZData) && 971 (PostColorSpace != cmsSigLabData); 972 973 if (hProfile == NULL) return FALSE; 974 975 cls = cmsGetDeviceClass(hProfile); 976 977 if (cls == cmsSigNamedColorClass) { 978 979 ColorSpaceIn = cmsSig1colorData; 980 ColorSpaceOut = (nProfiles > 1) ? cmsGetPCS(hProfile) : cmsGetColorSpace(hProfile); 981 } 982 else 983 if (lIsInput || (cls == cmsSigLinkClass)) { 984 985 ColorSpaceIn = cmsGetColorSpace(hProfile); 986 ColorSpaceOut = cmsGetPCS(hProfile); 987 } 988 else 989 { 990 ColorSpaceIn = cmsGetPCS(hProfile); 991 ColorSpaceOut = cmsGetColorSpace(hProfile); 992 } 993 994 if (i==0) 995 *Input = ColorSpaceIn; 996 997 PostColorSpace = ColorSpaceOut; 998 } 999 1000 *Output = PostColorSpace; 1001 1002 return TRUE; 1003 } 1004 1005 // Check colorspace 1006 static 1007 cmsBool IsProperColorSpace(cmsColorSpaceSignature Check, cmsUInt32Number dwFormat) 1008 { 1009 int Space1 = (int) T_COLORSPACE(dwFormat); 1010 int Space2 = _cmsLCMScolorSpace(Check); 1011 1012 if (Space1 == PT_ANY) return TRUE; 1013 if (Space1 == Space2) return TRUE; 1014 1015 if (Space1 == PT_LabV2 && Space2 == PT_Lab) return TRUE; 1016 if (Space1 == PT_Lab && Space2 == PT_LabV2) return TRUE; 1017 1018 return FALSE; 1019 } 1020 1021 // ---------------------------------------------------------------------------------------------------------------- 1022 1023 // Jun-21-2000: Some profiles (those that comes with W2K) comes 1024 // with the media white (media black?) x 100. Add a sanity check 1025 1026 static 1027 void NormalizeXYZ(cmsCIEXYZ* Dest) 1028 { 1029 while (Dest -> X > 2. && 1030 Dest -> Y > 2. && 1031 Dest -> Z > 2.) { 1032 1033 Dest -> X /= 10.; 1034 Dest -> Y /= 10.; 1035 Dest -> Z /= 10.; 1036 } 1037 } 1038 1039 static 1040 void SetWhitePoint(cmsCIEXYZ* wtPt, const cmsCIEXYZ* src) 1041 { 1042 if (src == NULL) { 1043 wtPt ->X = cmsD50X; 1044 wtPt ->Y = cmsD50Y; 1045 wtPt ->Z = cmsD50Z; 1046 } 1047 else { 1048 wtPt ->X = src->X; 1049 wtPt ->Y = src->Y; 1050 wtPt ->Z = src->Z; 1051 1052 NormalizeXYZ(wtPt); 1053 } 1054 1055 } 1056 1057 // New to lcms 2.0 -- have all parameters available. 1058 cmsHTRANSFORM CMSEXPORT cmsCreateExtendedTransform(cmsContext ContextID, 1059 cmsUInt32Number nProfiles, cmsHPROFILE hProfiles[], 1060 cmsBool BPC[], 1061 cmsUInt32Number Intents[], 1062 cmsFloat64Number AdaptationStates[], 1063 cmsHPROFILE hGamutProfile, 1064 cmsUInt32Number nGamutPCSposition, 1065 cmsUInt32Number InputFormat, 1066 cmsUInt32Number OutputFormat, 1067 cmsUInt32Number dwFlags) 1068 { 1069 _cmsTRANSFORM* xform; 1070 cmsColorSpaceSignature EntryColorSpace; 1071 cmsColorSpaceSignature ExitColorSpace; 1072 cmsPipeline* Lut; 1073 cmsUInt32Number LastIntent = Intents[nProfiles-1]; 1074 1075 // If it is a fake transform 1076 if (dwFlags & cmsFLAGS_NULLTRANSFORM) 1077 { 1078 return AllocEmptyTransform(ContextID, NULL, INTENT_PERCEPTUAL, &InputFormat, &OutputFormat, &dwFlags); 1079 } 1080 1081 // If gamut check is requested, make sure we have a gamut profile 1082 if (dwFlags & cmsFLAGS_GAMUTCHECK) { 1083 if (hGamutProfile == NULL) dwFlags &= ~cmsFLAGS_GAMUTCHECK; 1084 } 1085 1086 // On floating point transforms, inhibit cache 1087 if (_cmsFormatterIsFloat(InputFormat) || _cmsFormatterIsFloat(OutputFormat)) 1088 dwFlags |= cmsFLAGS_NOCACHE; 1089 1090 // Mark entry/exit spaces 1091 if (!GetXFormColorSpaces(nProfiles, hProfiles, &EntryColorSpace, &ExitColorSpace)) { 1092 cmsSignalError(ContextID, cmsERROR_NULL, "NULL input profiles on transform"); 1093 return NULL; 1094 } 1095 1096 // Check if proper colorspaces 1097 if (!IsProperColorSpace(EntryColorSpace, InputFormat)) { 1098 cmsSignalError(ContextID, cmsERROR_COLORSPACE_CHECK, "Wrong input color space on transform"); 1099 return NULL; 1100 } 1101 1102 if (!IsProperColorSpace(ExitColorSpace, OutputFormat)) { 1103 cmsSignalError(ContextID, cmsERROR_COLORSPACE_CHECK, "Wrong output color space on transform"); 1104 return NULL; 1105 } 1106 1107 // Create a pipeline with all transformations 1108 Lut = _cmsLinkProfiles(ContextID, nProfiles, Intents, hProfiles, BPC, AdaptationStates, dwFlags); 1109 if (Lut == NULL) { 1110 cmsSignalError(ContextID, cmsERROR_NOT_SUITABLE, "Couldn't link the profiles"); 1111 return NULL; 1112 } 1113 1114 // Check channel count 1115 if ((cmsChannelsOf(EntryColorSpace) != cmsPipelineInputChannels(Lut)) || 1116 (cmsChannelsOf(ExitColorSpace) != cmsPipelineOutputChannels(Lut))) { 1117 cmsPipelineFree(Lut); 1118 cmsSignalError(ContextID, cmsERROR_NOT_SUITABLE, "Channel count doesn't match. Profile is corrupted"); 1119 return NULL; 1120 } 1121 1122 1123 // All seems ok 1124 xform = AllocEmptyTransform(ContextID, Lut, LastIntent, &InputFormat, &OutputFormat, &dwFlags); 1125 if (xform == NULL) { 1126 return NULL; 1127 } 1128 1129 // Keep values 1130 xform ->EntryColorSpace = EntryColorSpace; 1131 xform ->ExitColorSpace = ExitColorSpace; 1132 xform ->RenderingIntent = Intents[nProfiles-1]; 1133 1134 // Take white points 1135 SetWhitePoint(&xform->EntryWhitePoint, (cmsCIEXYZ*) cmsReadTag(hProfiles[0], cmsSigMediaWhitePointTag)); 1136 SetWhitePoint(&xform->ExitWhitePoint, (cmsCIEXYZ*) cmsReadTag(hProfiles[nProfiles-1], cmsSigMediaWhitePointTag)); 1137 1138 1139 // Create a gamut check LUT if requested 1140 if (hGamutProfile != NULL && (dwFlags & cmsFLAGS_GAMUTCHECK)) 1141 xform ->GamutCheck = _cmsCreateGamutCheckPipeline(ContextID, hProfiles, 1142 BPC, Intents, 1143 AdaptationStates, 1144 nGamutPCSposition, 1145 hGamutProfile); 1146 1147 1148 // Try to read input and output colorant table 1149 if (cmsIsTag(hProfiles[0], cmsSigColorantTableTag)) { 1150 1151 // Input table can only come in this way. 1152 xform ->InputColorant = cmsDupNamedColorList((cmsNAMEDCOLORLIST*) cmsReadTag(hProfiles[0], cmsSigColorantTableTag)); 1153 } 1154 1155 // Output is a little bit more complex. 1156 if (cmsGetDeviceClass(hProfiles[nProfiles-1]) == cmsSigLinkClass) { 1157 1158 // This tag may exist only on devicelink profiles. 1159 if (cmsIsTag(hProfiles[nProfiles-1], cmsSigColorantTableOutTag)) { 1160 1161 // It may be NULL if error 1162 xform ->OutputColorant = cmsDupNamedColorList((cmsNAMEDCOLORLIST*) cmsReadTag(hProfiles[nProfiles-1], cmsSigColorantTableOutTag)); 1163 } 1164 1165 } else { 1166 1167 if (cmsIsTag(hProfiles[nProfiles-1], cmsSigColorantTableTag)) { 1168 1169 xform -> OutputColorant = cmsDupNamedColorList((cmsNAMEDCOLORLIST*) cmsReadTag(hProfiles[nProfiles-1], cmsSigColorantTableTag)); 1170 } 1171 } 1172 1173 // Store the sequence of profiles 1174 if (dwFlags & cmsFLAGS_KEEP_SEQUENCE) { 1175 xform ->Sequence = _cmsCompileProfileSequence(ContextID, nProfiles, hProfiles); 1176 } 1177 else 1178 xform ->Sequence = NULL; 1179 1180 // If this is a cached transform, init first value, which is zero (16 bits only) 1181 if (!(dwFlags & cmsFLAGS_NOCACHE)) { 1182 1183 memset(&xform ->Cache.CacheIn, 0, sizeof(xform ->Cache.CacheIn)); 1184 1185 if (xform ->GamutCheck != NULL) { 1186 TransformOnePixelWithGamutCheck(xform, xform ->Cache.CacheIn, xform->Cache.CacheOut); 1187 } 1188 else { 1189 1190 xform ->Lut ->Eval16Fn(xform ->Cache.CacheIn, xform->Cache.CacheOut, xform -> Lut->Data); 1191 } 1192 1193 } 1194 1195 return (cmsHTRANSFORM) xform; 1196 } 1197 1198 // Multiprofile transforms: Gamut check is not available here, as it is unclear from which profile the gamut comes. 1199 cmsHTRANSFORM CMSEXPORT cmsCreateMultiprofileTransformTHR(cmsContext ContextID, 1200 cmsHPROFILE hProfiles[], 1201 cmsUInt32Number nProfiles, 1202 cmsUInt32Number InputFormat, 1203 cmsUInt32Number OutputFormat, 1204 cmsUInt32Number Intent, 1205 cmsUInt32Number dwFlags) 1206 { 1207 cmsUInt32Number i; 1208 cmsBool BPC[256]; 1209 cmsUInt32Number Intents[256]; 1210 cmsFloat64Number AdaptationStates[256]; 1211 1212 if (nProfiles <= 0 || nProfiles > 255) { 1213 cmsSignalError(ContextID, cmsERROR_RANGE, "Wrong number of profiles. 1..255 expected, %d found.", nProfiles); 1214 return NULL; 1215 } 1216 1217 for (i=0; i < nProfiles; i++) { 1218 BPC[i] = dwFlags & cmsFLAGS_BLACKPOINTCOMPENSATION ? TRUE : FALSE; 1219 Intents[i] = Intent; 1220 AdaptationStates[i] = cmsSetAdaptationStateTHR(ContextID, -1); 1221 } 1222 1223 1224 return cmsCreateExtendedTransform(ContextID, nProfiles, hProfiles, BPC, Intents, AdaptationStates, NULL, 0, InputFormat, OutputFormat, dwFlags); 1225 } 1226 1227 1228 1229 cmsHTRANSFORM CMSEXPORT cmsCreateMultiprofileTransform(cmsHPROFILE hProfiles[], 1230 cmsUInt32Number nProfiles, 1231 cmsUInt32Number InputFormat, 1232 cmsUInt32Number OutputFormat, 1233 cmsUInt32Number Intent, 1234 cmsUInt32Number dwFlags) 1235 { 1236 1237 if (nProfiles <= 0 || nProfiles > 255) { 1238 cmsSignalError(NULL, cmsERROR_RANGE, "Wrong number of profiles. 1..255 expected, %d found.", nProfiles); 1239 return NULL; 1240 } 1241 1242 return cmsCreateMultiprofileTransformTHR(cmsGetProfileContextID(hProfiles[0]), 1243 hProfiles, 1244 nProfiles, 1245 InputFormat, 1246 OutputFormat, 1247 Intent, 1248 dwFlags); 1249 } 1250 1251 cmsHTRANSFORM CMSEXPORT cmsCreateTransformTHR(cmsContext ContextID, 1252 cmsHPROFILE Input, 1253 cmsUInt32Number InputFormat, 1254 cmsHPROFILE Output, 1255 cmsUInt32Number OutputFormat, 1256 cmsUInt32Number Intent, 1257 cmsUInt32Number dwFlags) 1258 { 1259 1260 cmsHPROFILE hArray[2]; 1261 1262 hArray[0] = Input; 1263 hArray[1] = Output; 1264 1265 return cmsCreateMultiprofileTransformTHR(ContextID, hArray, Output == NULL ? 1U : 2U, InputFormat, OutputFormat, Intent, dwFlags); 1266 } 1267 1268 CMSAPI cmsHTRANSFORM CMSEXPORT cmsCreateTransform(cmsHPROFILE Input, 1269 cmsUInt32Number InputFormat, 1270 cmsHPROFILE Output, 1271 cmsUInt32Number OutputFormat, 1272 cmsUInt32Number Intent, 1273 cmsUInt32Number dwFlags) 1274 { 1275 return cmsCreateTransformTHR(cmsGetProfileContextID(Input), Input, InputFormat, Output, OutputFormat, Intent, dwFlags); 1276 } 1277 1278 1279 cmsHTRANSFORM CMSEXPORT cmsCreateProofingTransformTHR(cmsContext ContextID, 1280 cmsHPROFILE InputProfile, 1281 cmsUInt32Number InputFormat, 1282 cmsHPROFILE OutputProfile, 1283 cmsUInt32Number OutputFormat, 1284 cmsHPROFILE ProofingProfile, 1285 cmsUInt32Number nIntent, 1286 cmsUInt32Number ProofingIntent, 1287 cmsUInt32Number dwFlags) 1288 { 1289 cmsHPROFILE hArray[4]; 1290 cmsUInt32Number Intents[4]; 1291 cmsBool BPC[4]; 1292 cmsFloat64Number Adaptation[4]; 1293 cmsBool DoBPC = (dwFlags & cmsFLAGS_BLACKPOINTCOMPENSATION) ? TRUE : FALSE; 1294 1295 1296 hArray[0] = InputProfile; hArray[1] = ProofingProfile; hArray[2] = ProofingProfile; hArray[3] = OutputProfile; 1297 Intents[0] = nIntent; Intents[1] = nIntent; Intents[2] = INTENT_RELATIVE_COLORIMETRIC; Intents[3] = ProofingIntent; 1298 BPC[0] = DoBPC; BPC[1] = DoBPC; BPC[2] = 0; BPC[3] = 0; 1299 1300 Adaptation[0] = Adaptation[1] = Adaptation[2] = Adaptation[3] = cmsSetAdaptationStateTHR(ContextID, -1); 1301 1302 if (!(dwFlags & (cmsFLAGS_SOFTPROOFING|cmsFLAGS_GAMUTCHECK))) 1303 return cmsCreateTransformTHR(ContextID, InputProfile, InputFormat, OutputProfile, OutputFormat, nIntent, dwFlags); 1304 1305 return cmsCreateExtendedTransform(ContextID, 4, hArray, BPC, Intents, Adaptation, 1306 ProofingProfile, 1, InputFormat, OutputFormat, dwFlags); 1307 1308 } 1309 1310 1311 cmsHTRANSFORM CMSEXPORT cmsCreateProofingTransform(cmsHPROFILE InputProfile, 1312 cmsUInt32Number InputFormat, 1313 cmsHPROFILE OutputProfile, 1314 cmsUInt32Number OutputFormat, 1315 cmsHPROFILE ProofingProfile, 1316 cmsUInt32Number nIntent, 1317 cmsUInt32Number ProofingIntent, 1318 cmsUInt32Number dwFlags) 1319 { 1320 return cmsCreateProofingTransformTHR(cmsGetProfileContextID(InputProfile), 1321 InputProfile, 1322 InputFormat, 1323 OutputProfile, 1324 OutputFormat, 1325 ProofingProfile, 1326 nIntent, 1327 ProofingIntent, 1328 dwFlags); 1329 } 1330 1331 1332 // Grab the ContextID from an open transform. Returns NULL if a NULL transform is passed 1333 cmsContext CMSEXPORT cmsGetTransformContextID(cmsHTRANSFORM hTransform) 1334 { 1335 _cmsTRANSFORM* xform = (_cmsTRANSFORM*) hTransform; 1336 1337 if (xform == NULL) return NULL; 1338 return xform -> ContextID; 1339 } 1340 1341 // Grab the input/output formats 1342 cmsUInt32Number CMSEXPORT cmsGetTransformInputFormat(cmsHTRANSFORM hTransform) 1343 { 1344 _cmsTRANSFORM* xform = (_cmsTRANSFORM*) hTransform; 1345 1346 if (xform == NULL) return 0; 1347 return xform->InputFormat; 1348 } 1349 1350 cmsUInt32Number CMSEXPORT cmsGetTransformOutputFormat(cmsHTRANSFORM hTransform) 1351 { 1352 _cmsTRANSFORM* xform = (_cmsTRANSFORM*) hTransform; 1353 1354 if (xform == NULL) return 0; 1355 return xform->OutputFormat; 1356 } 1357 1358 // For backwards compatibility 1359 cmsBool CMSEXPORT cmsChangeBuffersFormat(cmsHTRANSFORM hTransform, 1360 cmsUInt32Number InputFormat, 1361 cmsUInt32Number OutputFormat) 1362 { 1363 _cmsTRANSFORM* xform = (_cmsTRANSFORM*) hTransform; 1364 cmsFormatter16 FromInput, ToOutput; 1365 1366 1367 // We only can afford to change formatters if previous transform is at least 16 bits 1368 if (!(xform ->dwOriginalFlags & cmsFLAGS_CAN_CHANGE_FORMATTER)) { 1369 1370 cmsSignalError(xform ->ContextID, cmsERROR_NOT_SUITABLE, "cmsChangeBuffersFormat works only on transforms created originally with at least 16 bits of precision"); 1371 return FALSE; 1372 } 1373 1374 FromInput = _cmsGetFormatter(xform->ContextID, InputFormat, cmsFormatterInput, CMS_PACK_FLAGS_16BITS).Fmt16; 1375 ToOutput = _cmsGetFormatter(xform->ContextID, OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_16BITS).Fmt16; 1376 1377 if (FromInput == NULL || ToOutput == NULL) { 1378 1379 cmsSignalError(xform -> ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported raster format"); 1380 return FALSE; 1381 } 1382 1383 xform ->InputFormat = InputFormat; 1384 xform ->OutputFormat = OutputFormat; 1385 xform ->FromInput = FromInput; 1386 xform ->ToOutput = ToOutput; 1387 return TRUE; 1388 }