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