1 //--------------------------------------------------------------------------------- 2 // 3 // Little Color Management System 4 // Copyright (c) 1998-2014 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 183 p -> xform(p, InputBuffer, OutputBuffer, Size, Size); 184 } 185 186 187 // Apply transform. 188 void CMSEXPORT cmsDoTransformStride(cmsHTRANSFORM Transform, 189 const void* InputBuffer, 190 void* OutputBuffer, 191 cmsUInt32Number Size, cmsUInt32Number Stride) 192 193 { 194 _cmsTRANSFORM* p = (_cmsTRANSFORM*) Transform; 195 196 p -> xform(p, InputBuffer, OutputBuffer, Size, Stride); 197 } 198 199 200 // Transform routines ---------------------------------------------------------------------------------------------------------- 201 202 // Float xform converts floats. Since there are no performance issues, one routine does all job, including gamut check. 203 // Note that because extended range, we can use a -1.0 value for out of gamut in this case. 204 static 205 void FloatXFORM(_cmsTRANSFORM* p, 206 const void* in, 207 void* out, cmsUInt32Number Size, cmsUInt32Number Stride) 208 { 209 cmsUInt8Number* accum; 210 cmsUInt8Number* output; 211 cmsFloat32Number fIn[cmsMAXCHANNELS], fOut[cmsMAXCHANNELS]; 212 cmsFloat32Number OutOfGamut; 213 cmsUInt32Number i, j; 214 215 accum = (cmsUInt8Number*) in; 216 output = (cmsUInt8Number*) out; 217 218 for (i=0; i < Size; i++) { 219 220 accum = p -> FromInputFloat(p, fIn, accum, Stride); 221 222 // Any gamut chack to do? 223 if (p ->GamutCheck != NULL) { 224 225 // Evaluate gamut marker. 226 cmsPipelineEvalFloat( fIn, &OutOfGamut, p ->GamutCheck); 227 228 // Is current color out of gamut? 229 if (OutOfGamut > 0.0) { 230 231 // Certainly, out of gamut 232 for (j=0; j < cmsMAXCHANNELS; j++) 233 fOut[j] = -1.0; 234 235 } 236 else { 237 // No, proceed normally 238 cmsPipelineEvalFloat(fIn, fOut, p -> Lut); 239 } 240 } 241 else { 242 243 // No gamut check at all 244 cmsPipelineEvalFloat(fIn, fOut, p -> Lut); 245 } 246 247 // Back to asked representation 248 output = p -> ToOutputFloat(p, fOut, output, Stride); 249 } 250 } 251 252 253 static 254 void NullFloatXFORM(_cmsTRANSFORM* p, 255 const void* in, 256 void* out, 257 cmsUInt32Number Size, 258 cmsUInt32Number Stride) 259 { 260 cmsUInt8Number* accum; 261 cmsUInt8Number* output; 262 cmsFloat32Number fIn[cmsMAXCHANNELS]; 263 cmsUInt32Number i, n; 264 265 accum = (cmsUInt8Number*) in; 266 output = (cmsUInt8Number*) out; 267 n = Size; 268 269 for (i=0; i < n; i++) { 270 271 accum = p -> FromInputFloat(p, fIn, accum, Stride); 272 output = p -> ToOutputFloat(p, fIn, output, Stride); 273 } 274 } 275 276 // 16 bit precision ----------------------------------------------------------------------------------------------------------- 277 278 // Null transformation, only applies formatters. No cach?static 279 void NullXFORM(_cmsTRANSFORM* p, 280 const void* in, 281 void* out, cmsUInt32Number Size, 282 cmsUInt32Number Stride) 283 { 284 cmsUInt8Number* accum; 285 cmsUInt8Number* output; 286 cmsUInt16Number wIn[cmsMAXCHANNELS]; 287 cmsUInt32Number i, n; 288 289 accum = (cmsUInt8Number*) in; 290 output = (cmsUInt8Number*) out; 291 n = Size; // Buffer len 292 293 for (i=0; i < n; i++) { 294 295 accum = p -> FromInput(p, wIn, accum, Stride); 296 output = p -> ToOutput(p, wIn, output, Stride); 297 } 298 } 299 300 301 // No gamut check, no cache, 16 bits 302 static 303 void PrecalculatedXFORM(_cmsTRANSFORM* p, 304 const void* in, 305 void* out, cmsUInt32Number Size, cmsUInt32Number Stride) 306 { 307 register cmsUInt8Number* accum; 308 register cmsUInt8Number* output; 309 cmsUInt16Number wIn[cmsMAXCHANNELS], wOut[cmsMAXCHANNELS]; 310 cmsUInt32Number i, n; 311 312 accum = (cmsUInt8Number*) in; 313 output = (cmsUInt8Number*) out; 314 n = Size; 315 316 for (i=0; i < n; i++) { 317 318 accum = p -> FromInput(p, wIn, accum, Stride); 319 p ->Lut ->Eval16Fn(wIn, wOut, p -> Lut->Data); 320 output = p -> ToOutput(p, wOut, output, Stride); 321 } 322 } 323 324 325 // Auxiliar: Handle precalculated gamut check. The retrieval of context may be alittle bit slow, but this function is not critical. 326 static 327 void TransformOnePixelWithGamutCheck(_cmsTRANSFORM* p, 328 const cmsUInt16Number wIn[], 329 cmsUInt16Number wOut[]) 330 { 331 cmsUInt16Number wOutOfGamut; 332 333 p ->GamutCheck ->Eval16Fn(wIn, &wOutOfGamut, p ->GamutCheck ->Data); 334 if (wOutOfGamut >= 1) { 335 336 cmsUInt16Number i; 337 _cmsAlarmCodesChunkType* ContextAlarmCodes = (_cmsAlarmCodesChunkType*) _cmsContextGetClientChunk(p->ContextID, AlarmCodesContext); 338 339 for (i=0; i < p ->Lut->OutputChannels; i++) { 340 341 wOut[i] = ContextAlarmCodes ->AlarmCodes[i]; 342 } 343 } 344 else 345 p ->Lut ->Eval16Fn(wIn, wOut, p -> Lut->Data); 346 } 347 348 // Gamut check, No cach? 16 bits. 349 static 350 void PrecalculatedXFORMGamutCheck(_cmsTRANSFORM* p, 351 const void* in, 352 void* out, cmsUInt32Number Size, cmsUInt32Number Stride) 353 { 354 cmsUInt8Number* accum; 355 cmsUInt8Number* output; 356 cmsUInt16Number wIn[cmsMAXCHANNELS], wOut[cmsMAXCHANNELS]; 357 cmsUInt32Number i, n; 358 359 accum = (cmsUInt8Number*) in; 360 output = (cmsUInt8Number*) out; 361 n = Size; // Buffer len 362 363 for (i=0; i < n; i++) { 364 365 accum = p -> FromInput(p, wIn, accum, Stride); 366 TransformOnePixelWithGamutCheck(p, wIn, wOut); 367 output = p -> ToOutput(p, wOut, output, Stride); 368 } 369 } 370 371 372 // No gamut check, Cach? 16 bits, 373 static 374 void CachedXFORM(_cmsTRANSFORM* p, 375 const void* in, 376 void* out, cmsUInt32Number Size, cmsUInt32Number Stride) 377 { 378 cmsUInt8Number* accum; 379 cmsUInt8Number* output; 380 cmsUInt16Number wIn[cmsMAXCHANNELS], wOut[cmsMAXCHANNELS]; 381 cmsUInt32Number i, n; 382 _cmsCACHE Cache; 383 384 accum = (cmsUInt8Number*) in; 385 output = (cmsUInt8Number*) out; 386 n = Size; // Buffer len 387 388 // Empty buffers for quick memcmp 389 memset(wIn, 0, sizeof(wIn)); 390 memset(wOut, 0, sizeof(wOut)); 391 392 // Get copy of zero cache 393 memcpy(&Cache, &p ->Cache, sizeof(Cache)); 394 395 for (i=0; i < n; i++) { 396 397 accum = p -> FromInput(p, wIn, accum, Stride); 398 399 if (memcmp(wIn, Cache.CacheIn, sizeof(Cache.CacheIn)) == 0) { 400 401 memcpy(wOut, Cache.CacheOut, sizeof(Cache.CacheOut)); 402 } 403 else { 404 405 p ->Lut ->Eval16Fn(wIn, wOut, p -> Lut->Data); 406 407 memcpy(Cache.CacheIn, wIn, sizeof(Cache.CacheIn)); 408 memcpy(Cache.CacheOut, wOut, sizeof(Cache.CacheOut)); 409 } 410 411 output = p -> ToOutput(p, wOut, output, Stride); 412 } 413 414 } 415 416 417 // All those nice features together 418 static 419 void CachedXFORMGamutCheck(_cmsTRANSFORM* p, 420 const void* in, 421 void* out, cmsUInt32Number Size, cmsUInt32Number Stride) 422 { 423 cmsUInt8Number* accum; 424 cmsUInt8Number* output; 425 cmsUInt16Number wIn[cmsMAXCHANNELS], wOut[cmsMAXCHANNELS]; 426 cmsUInt32Number i, n; 427 _cmsCACHE Cache; 428 429 accum = (cmsUInt8Number*) in; 430 output = (cmsUInt8Number*) out; 431 n = Size; // Buffer len 432 433 // Empty buffers for quick memcmp 434 memset(wIn, 0, sizeof(cmsUInt16Number) * cmsMAXCHANNELS); 435 memset(wOut, 0, sizeof(cmsUInt16Number) * cmsMAXCHANNELS); 436 437 // Get copy of zero cache 438 memcpy(&Cache, &p ->Cache, sizeof(Cache)); 439 440 for (i=0; i < n; i++) { 441 442 accum = p -> FromInput(p, wIn, accum, Stride); 443 444 if (memcmp(wIn, Cache.CacheIn, sizeof(Cache.CacheIn)) == 0) { 445 memcpy(wOut, Cache.CacheOut, sizeof(Cache.CacheOut)); 446 } 447 else { 448 TransformOnePixelWithGamutCheck(p, wIn, wOut); 449 memcpy(Cache.CacheIn, wIn, sizeof(Cache.CacheIn)); 450 memcpy(Cache.CacheOut, wOut, sizeof(Cache.CacheOut)); 451 } 452 453 output = p -> ToOutput(p, wOut, output, Stride); 454 } 455 456 } 457 458 // ------------------------------------------------------------------------------------------------------------- 459 460 // List of used-defined transform factories 461 typedef struct _cmsTransformCollection_st { 462 463 _cmsTransformFactory Factory; 464 struct _cmsTransformCollection_st *Next; 465 466 } _cmsTransformCollection; 467 468 // The linked list head 469 _cmsTransformPluginChunkType _cmsTransformPluginChunk = { NULL }; 470 471 472 // Duplicates the zone of memory used by the plug-in in the new context 473 static 474 void DupPluginTransformList(struct _cmsContext_struct* ctx, 475 const struct _cmsContext_struct* src) 476 { 477 _cmsTransformPluginChunkType newHead = { NULL }; 478 _cmsTransformCollection* entry; 479 _cmsTransformCollection* Anterior = NULL; 480 _cmsTransformPluginChunkType* head = (_cmsTransformPluginChunkType*) src->chunks[TransformPlugin]; 481 482 // Walk the list copying all nodes 483 for (entry = head->TransformCollection; 484 entry != NULL; 485 entry = entry ->Next) { 486 487 _cmsTransformCollection *newEntry = ( _cmsTransformCollection *) _cmsSubAllocDup(ctx ->MemPool, entry, sizeof(_cmsTransformCollection)); 488 489 if (newEntry == NULL) 490 return; 491 492 // We want to keep the linked list order, so this is a little bit tricky 493 newEntry -> Next = NULL; 494 if (Anterior) 495 Anterior -> Next = newEntry; 496 497 Anterior = newEntry; 498 499 if (newHead.TransformCollection == NULL) 500 newHead.TransformCollection = newEntry; 501 } 502 503 ctx ->chunks[TransformPlugin] = _cmsSubAllocDup(ctx->MemPool, &newHead, sizeof(_cmsTransformPluginChunkType)); 504 } 505 506 void _cmsAllocTransformPluginChunk(struct _cmsContext_struct* ctx, 507 const struct _cmsContext_struct* src) 508 { 509 if (src != NULL) { 510 511 // Copy all linked list 512 DupPluginTransformList(ctx, src); 513 } 514 else { 515 static _cmsTransformPluginChunkType TransformPluginChunkType = { NULL }; 516 ctx ->chunks[TransformPlugin] = _cmsSubAllocDup(ctx ->MemPool, &TransformPluginChunkType, sizeof(_cmsTransformPluginChunkType)); 517 } 518 } 519 520 521 522 // Register new ways to transform 523 cmsBool _cmsRegisterTransformPlugin(cmsContext ContextID, cmsPluginBase* Data) 524 { 525 cmsPluginTransform* Plugin = (cmsPluginTransform*) Data; 526 _cmsTransformCollection* fl; 527 _cmsTransformPluginChunkType* ctx = ( _cmsTransformPluginChunkType*) _cmsContextGetClientChunk(ContextID,TransformPlugin); 528 529 if (Data == NULL) { 530 531 // Free the chain. Memory is safely freed at exit 532 ctx->TransformCollection = NULL; 533 return TRUE; 534 } 535 536 // Factory callback is required 537 if (Plugin ->Factory == NULL) return FALSE; 538 539 540 fl = (_cmsTransformCollection*) _cmsPluginMalloc(ContextID, sizeof(_cmsTransformCollection)); 541 if (fl == NULL) return FALSE; 542 543 // Copy the parameters 544 fl ->Factory = Plugin ->Factory; 545 546 // Keep linked list 547 fl ->Next = ctx->TransformCollection; 548 ctx->TransformCollection = fl; 549 550 // All is ok 551 return TRUE; 552 } 553 554 555 void CMSEXPORT _cmsSetTransformUserData(struct _cmstransform_struct *CMMcargo, void* ptr, _cmsFreeUserDataFn FreePrivateDataFn) 556 { 557 _cmsAssert(CMMcargo != NULL); 558 CMMcargo ->UserData = ptr; 559 CMMcargo ->FreeUserData = FreePrivateDataFn; 560 } 561 562 // returns the pointer defined by the plug-in to store private data 563 void * CMSEXPORT _cmsGetTransformUserData(struct _cmstransform_struct *CMMcargo) 564 { 565 _cmsAssert(CMMcargo != NULL); 566 return CMMcargo ->UserData; 567 } 568 569 // returns the current formatters 570 void CMSEXPORT _cmsGetTransformFormatters16(struct _cmstransform_struct *CMMcargo, cmsFormatter16* FromInput, cmsFormatter16* ToOutput) 571 { 572 _cmsAssert(CMMcargo != NULL); 573 if (FromInput) *FromInput = CMMcargo ->FromInput; 574 if (ToOutput) *ToOutput = CMMcargo ->ToOutput; 575 } 576 577 void CMSEXPORT _cmsGetTransformFormattersFloat(struct _cmstransform_struct *CMMcargo, cmsFormatterFloat* FromInput, cmsFormatterFloat* ToOutput) 578 { 579 _cmsAssert(CMMcargo != NULL); 580 if (FromInput) *FromInput = CMMcargo ->FromInputFloat; 581 if (ToOutput) *ToOutput = CMMcargo ->ToOutputFloat; 582 } 583 584 585 // Allocate transform struct and set it to defaults. Ask the optimization plug-in about if those formats are proper 586 // for separated transforms. If this is the case, 587 static 588 _cmsTRANSFORM* AllocEmptyTransform(cmsContext ContextID, cmsPipeline* lut, 589 cmsUInt32Number Intent, cmsUInt32Number* InputFormat, cmsUInt32Number* OutputFormat, cmsUInt32Number* dwFlags) 590 { 591 _cmsTransformPluginChunkType* ctx = ( _cmsTransformPluginChunkType*) _cmsContextGetClientChunk(ContextID, TransformPlugin); 592 _cmsTransformCollection* Plugin; 593 594 // Allocate needed memory 595 _cmsTRANSFORM* p = (_cmsTRANSFORM*) _cmsMallocZero(ContextID, sizeof(_cmsTRANSFORM)); 596 if (!p) return NULL; 597 598 // Store the proposed pipeline 599 p ->Lut = lut; 600 601 // Let's see if any plug-in want to do the transform by itself 602 for (Plugin = ctx ->TransformCollection; 603 Plugin != NULL; 604 Plugin = Plugin ->Next) { 605 606 if (Plugin ->Factory(&p->xform, &p->UserData, &p ->FreeUserData, &p ->Lut, InputFormat, OutputFormat, dwFlags)) { 607 608 // Last plugin in the declaration order takes control. We just keep 609 // the original parameters as a logging. 610 // Note that cmsFLAGS_CAN_CHANGE_FORMATTER is not set, so by default 611 // an optimized transform is not reusable. The plug-in can, however, change 612 // the flags and make it suitable. 613 614 p ->ContextID = ContextID; 615 p ->InputFormat = *InputFormat; 616 p ->OutputFormat = *OutputFormat; 617 p ->dwOriginalFlags = *dwFlags; 618 619 // Fill the formatters just in case the optimized routine is interested. 620 // No error is thrown if the formatter doesn't exist. It is up to the optimization 621 // factory to decide what to do in those cases. 622 p ->FromInput = _cmsGetFormatter(ContextID, *InputFormat, cmsFormatterInput, CMS_PACK_FLAGS_16BITS).Fmt16; 623 p ->ToOutput = _cmsGetFormatter(ContextID, *OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_16BITS).Fmt16; 624 p ->FromInputFloat = _cmsGetFormatter(ContextID, *InputFormat, cmsFormatterInput, CMS_PACK_FLAGS_FLOAT).FmtFloat; 625 p ->ToOutputFloat = _cmsGetFormatter(ContextID, *OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_FLOAT).FmtFloat; 626 627 return p; 628 } 629 } 630 631 // Not suitable for the transform plug-in, let's check the pipeline plug-in 632 if (p ->Lut != NULL) 633 _cmsOptimizePipeline(ContextID, &p->Lut, Intent, InputFormat, OutputFormat, dwFlags); 634 635 // Check whatever this is a true floating point transform 636 if (_cmsFormatterIsFloat(*InputFormat) && _cmsFormatterIsFloat(*OutputFormat)) { 637 638 // Get formatter function always return a valid union, but the contents of this union may be NULL. 639 p ->FromInputFloat = _cmsGetFormatter(ContextID, *InputFormat, cmsFormatterInput, CMS_PACK_FLAGS_FLOAT).FmtFloat; 640 p ->ToOutputFloat = _cmsGetFormatter(ContextID, *OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_FLOAT).FmtFloat; 641 *dwFlags |= cmsFLAGS_CAN_CHANGE_FORMATTER; 642 643 if (p ->FromInputFloat == NULL || p ->ToOutputFloat == NULL) { 644 645 cmsSignalError(ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported raster format"); 646 _cmsFree(ContextID, p); 647 return NULL; 648 } 649 650 if (*dwFlags & cmsFLAGS_NULLTRANSFORM) { 651 652 p ->xform = NullFloatXFORM; 653 } 654 else { 655 // Float transforms don't use cach? always are non-NULL 656 p ->xform = FloatXFORM; 657 } 658 659 } 660 else { 661 662 if (*InputFormat == 0 && *OutputFormat == 0) { 663 p ->FromInput = p ->ToOutput = NULL; 664 *dwFlags |= cmsFLAGS_CAN_CHANGE_FORMATTER; 665 } 666 else { 667 668 int BytesPerPixelInput; 669 670 p ->FromInput = _cmsGetFormatter(ContextID, *InputFormat, cmsFormatterInput, CMS_PACK_FLAGS_16BITS).Fmt16; 671 p ->ToOutput = _cmsGetFormatter(ContextID, *OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_16BITS).Fmt16; 672 673 if (p ->FromInput == NULL || p ->ToOutput == NULL) { 674 675 cmsSignalError(ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported raster format"); 676 _cmsFree(ContextID, p); 677 return NULL; 678 } 679 680 BytesPerPixelInput = T_BYTES(p ->InputFormat); 681 if (BytesPerPixelInput == 0 || BytesPerPixelInput >= 2) 682 *dwFlags |= cmsFLAGS_CAN_CHANGE_FORMATTER; 683 684 } 685 686 if (*dwFlags & cmsFLAGS_NULLTRANSFORM) { 687 688 p ->xform = NullXFORM; 689 } 690 else { 691 if (*dwFlags & cmsFLAGS_NOCACHE) { 692 693 if (*dwFlags & cmsFLAGS_GAMUTCHECK) 694 p ->xform = PrecalculatedXFORMGamutCheck; // Gamut check, no cach? else 695 p ->xform = PrecalculatedXFORM; // No cach? no gamut check 696 } 697 else { 698 699 if (*dwFlags & cmsFLAGS_GAMUTCHECK) 700 p ->xform = CachedXFORMGamutCheck; // Gamut check, cach? else 701 p ->xform = CachedXFORM; // No gamut check, cach? 702 } 703 } 704 } 705 706 p ->InputFormat = *InputFormat; 707 p ->OutputFormat = *OutputFormat; 708 p ->dwOriginalFlags = *dwFlags; 709 p ->ContextID = ContextID; 710 p ->UserData = NULL; 711 return p; 712 } 713 714 static 715 cmsBool GetXFormColorSpaces(int nProfiles, cmsHPROFILE hProfiles[], cmsColorSpaceSignature* Input, cmsColorSpaceSignature* Output) 716 { 717 cmsColorSpaceSignature ColorSpaceIn, ColorSpaceOut; 718 cmsColorSpaceSignature PostColorSpace; 719 int i; 720 721 if (nProfiles <= 0) return FALSE; 722 if (hProfiles[0] == NULL) return FALSE; 723 724 *Input = PostColorSpace = cmsGetColorSpace(hProfiles[0]); 725 726 for (i=0; i < nProfiles; i++) { 727 728 cmsProfileClassSignature cls; 729 cmsHPROFILE hProfile = hProfiles[i]; 730 731 int lIsInput = (PostColorSpace != cmsSigXYZData) && 732 (PostColorSpace != cmsSigLabData); 733 734 if (hProfile == NULL) return FALSE; 735 736 cls = cmsGetDeviceClass(hProfile); 737 738 if (cls == cmsSigNamedColorClass) { 739 740 ColorSpaceIn = cmsSig1colorData; 741 ColorSpaceOut = (nProfiles > 1) ? cmsGetPCS(hProfile) : cmsGetColorSpace(hProfile); 742 } 743 else 744 if (lIsInput || (cls == cmsSigLinkClass)) { 745 746 ColorSpaceIn = cmsGetColorSpace(hProfile); 747 ColorSpaceOut = cmsGetPCS(hProfile); 748 } 749 else 750 { 751 ColorSpaceIn = cmsGetPCS(hProfile); 752 ColorSpaceOut = cmsGetColorSpace(hProfile); 753 } 754 755 if (i==0) 756 *Input = ColorSpaceIn; 757 758 PostColorSpace = ColorSpaceOut; 759 } 760 761 *Output = PostColorSpace; 762 763 return TRUE; 764 } 765 766 // Check colorspace 767 static 768 cmsBool IsProperColorSpace(cmsColorSpaceSignature Check, cmsUInt32Number dwFormat) 769 { 770 int Space1 = T_COLORSPACE(dwFormat); 771 int Space2 = _cmsLCMScolorSpace(Check); 772 773 if (Space1 == PT_ANY) return TRUE; 774 if (Space1 == Space2) return TRUE; 775 776 if (Space1 == PT_LabV2 && Space2 == PT_Lab) return TRUE; 777 if (Space1 == PT_Lab && Space2 == PT_LabV2) return TRUE; 778 779 return FALSE; 780 } 781 782 // ---------------------------------------------------------------------------------------------------------------- 783 784 static 785 void SetWhitePoint(cmsCIEXYZ* wtPt, const cmsCIEXYZ* src) 786 { 787 if (src == NULL) { 788 wtPt ->X = cmsD50X; 789 wtPt ->Y = cmsD50Y; 790 wtPt ->Z = cmsD50Z; 791 } 792 else { 793 wtPt ->X = src->X; 794 wtPt ->Y = src->Y; 795 wtPt ->Z = src->Z; 796 } 797 798 } 799 800 // New to lcms 2.0 -- have all parameters available. 801 cmsHTRANSFORM CMSEXPORT cmsCreateExtendedTransform(cmsContext ContextID, 802 cmsUInt32Number nProfiles, cmsHPROFILE hProfiles[], 803 cmsBool BPC[], 804 cmsUInt32Number Intents[], 805 cmsFloat64Number AdaptationStates[], 806 cmsHPROFILE hGamutProfile, 807 cmsUInt32Number nGamutPCSposition, 808 cmsUInt32Number InputFormat, 809 cmsUInt32Number OutputFormat, 810 cmsUInt32Number dwFlags) 811 { 812 _cmsTRANSFORM* xform; 813 cmsColorSpaceSignature EntryColorSpace; 814 cmsColorSpaceSignature ExitColorSpace; 815 cmsPipeline* Lut; 816 cmsUInt32Number LastIntent = Intents[nProfiles-1]; 817 818 // If it is a fake transform 819 if (dwFlags & cmsFLAGS_NULLTRANSFORM) 820 { 821 return AllocEmptyTransform(ContextID, NULL, INTENT_PERCEPTUAL, &InputFormat, &OutputFormat, &dwFlags); 822 } 823 824 // If gamut check is requested, make sure we have a gamut profile 825 if (dwFlags & cmsFLAGS_GAMUTCHECK) { 826 if (hGamutProfile == NULL) dwFlags &= ~cmsFLAGS_GAMUTCHECK; 827 } 828 829 // On floating point transforms, inhibit cache 830 if (_cmsFormatterIsFloat(InputFormat) || _cmsFormatterIsFloat(OutputFormat)) 831 dwFlags |= cmsFLAGS_NOCACHE; 832 833 // Mark entry/exit spaces 834 if (!GetXFormColorSpaces(nProfiles, hProfiles, &EntryColorSpace, &ExitColorSpace)) { 835 cmsSignalError(ContextID, cmsERROR_NULL, "NULL input profiles on transform"); 836 return NULL; 837 } 838 839 // Check if proper colorspaces 840 if (!IsProperColorSpace(EntryColorSpace, InputFormat)) { 841 cmsSignalError(ContextID, cmsERROR_COLORSPACE_CHECK, "Wrong input color space on transform"); 842 return NULL; 843 } 844 845 if (!IsProperColorSpace(ExitColorSpace, OutputFormat)) { 846 cmsSignalError(ContextID, cmsERROR_COLORSPACE_CHECK, "Wrong output color space on transform"); 847 return NULL; 848 } 849 850 // Create a pipeline with all transformations 851 Lut = _cmsLinkProfiles(ContextID, nProfiles, Intents, hProfiles, BPC, AdaptationStates, dwFlags); 852 if (Lut == NULL) { 853 cmsSignalError(ContextID, cmsERROR_NOT_SUITABLE, "Couldn't link the profiles"); 854 return NULL; 855 } 856 857 // Check channel count 858 if ((cmsChannelsOf(EntryColorSpace) != cmsPipelineInputChannels(Lut)) || 859 (cmsChannelsOf(ExitColorSpace) != cmsPipelineOutputChannels(Lut))) { 860 cmsPipelineFree(Lut); 861 cmsSignalError(ContextID, cmsERROR_NOT_SUITABLE, "Channel count doesn't match. Profile is corrupted"); 862 return NULL; 863 } 864 865 866 // All seems ok 867 xform = AllocEmptyTransform(ContextID, Lut, LastIntent, &InputFormat, &OutputFormat, &dwFlags); 868 if (xform == NULL) { 869 return NULL; 870 } 871 872 // Keep values 873 xform ->EntryColorSpace = EntryColorSpace; 874 xform ->ExitColorSpace = ExitColorSpace; 875 xform ->RenderingIntent = Intents[nProfiles-1]; 876 877 // Take white points 878 SetWhitePoint(&xform->EntryWhitePoint, (cmsCIEXYZ*) cmsReadTag(hProfiles[0], cmsSigMediaWhitePointTag)); 879 SetWhitePoint(&xform->ExitWhitePoint, (cmsCIEXYZ*) cmsReadTag(hProfiles[nProfiles-1], cmsSigMediaWhitePointTag)); 880 881 882 // Create a gamut check LUT if requested 883 if (hGamutProfile != NULL && (dwFlags & cmsFLAGS_GAMUTCHECK)) 884 xform ->GamutCheck = _cmsCreateGamutCheckPipeline(ContextID, hProfiles, 885 BPC, Intents, 886 AdaptationStates, 887 nGamutPCSposition, 888 hGamutProfile); 889 890 891 // Try to read input and output colorant table 892 if (cmsIsTag(hProfiles[0], cmsSigColorantTableTag)) { 893 894 // Input table can only come in this way. 895 xform ->InputColorant = cmsDupNamedColorList((cmsNAMEDCOLORLIST*) cmsReadTag(hProfiles[0], cmsSigColorantTableTag)); 896 } 897 898 // Output is a little bit more complex. 899 if (cmsGetDeviceClass(hProfiles[nProfiles-1]) == cmsSigLinkClass) { 900 901 // This tag may exist only on devicelink profiles. 902 if (cmsIsTag(hProfiles[nProfiles-1], cmsSigColorantTableOutTag)) { 903 904 // It may be NULL if error 905 xform ->OutputColorant = cmsDupNamedColorList((cmsNAMEDCOLORLIST*) cmsReadTag(hProfiles[nProfiles-1], cmsSigColorantTableOutTag)); 906 } 907 908 } else { 909 910 if (cmsIsTag(hProfiles[nProfiles-1], cmsSigColorantTableTag)) { 911 912 xform -> OutputColorant = cmsDupNamedColorList((cmsNAMEDCOLORLIST*) cmsReadTag(hProfiles[nProfiles-1], cmsSigColorantTableTag)); 913 } 914 } 915 916 // Store the sequence of profiles 917 if (dwFlags & cmsFLAGS_KEEP_SEQUENCE) { 918 xform ->Sequence = _cmsCompileProfileSequence(ContextID, nProfiles, hProfiles); 919 } 920 else 921 xform ->Sequence = NULL; 922 923 // If this is a cached transform, init first value, which is zero (16 bits only) 924 if (!(dwFlags & cmsFLAGS_NOCACHE)) { 925 926 memset(&xform ->Cache.CacheIn, 0, sizeof(xform ->Cache.CacheIn)); 927 928 if (xform ->GamutCheck != NULL) { 929 TransformOnePixelWithGamutCheck(xform, xform ->Cache.CacheIn, xform->Cache.CacheOut); 930 } 931 else { 932 933 xform ->Lut ->Eval16Fn(xform ->Cache.CacheIn, xform->Cache.CacheOut, xform -> Lut->Data); 934 } 935 936 } 937 938 return (cmsHTRANSFORM) xform; 939 } 940 941 // Multiprofile transforms: Gamut check is not available here, as it is unclear from which profile the gamut comes. 942 cmsHTRANSFORM CMSEXPORT cmsCreateMultiprofileTransformTHR(cmsContext ContextID, 943 cmsHPROFILE hProfiles[], 944 cmsUInt32Number nProfiles, 945 cmsUInt32Number InputFormat, 946 cmsUInt32Number OutputFormat, 947 cmsUInt32Number Intent, 948 cmsUInt32Number dwFlags) 949 { 950 cmsUInt32Number i; 951 cmsBool BPC[256]; 952 cmsUInt32Number Intents[256]; 953 cmsFloat64Number AdaptationStates[256]; 954 955 if (nProfiles <= 0 || nProfiles > 255) { 956 cmsSignalError(ContextID, cmsERROR_RANGE, "Wrong number of profiles. 1..255 expected, %d found.", nProfiles); 957 return NULL; 958 } 959 960 for (i=0; i < nProfiles; i++) { 961 BPC[i] = dwFlags & cmsFLAGS_BLACKPOINTCOMPENSATION ? TRUE : FALSE; 962 Intents[i] = Intent; 963 AdaptationStates[i] = cmsSetAdaptationStateTHR(ContextID, -1); 964 } 965 966 967 return cmsCreateExtendedTransform(ContextID, nProfiles, hProfiles, BPC, Intents, AdaptationStates, NULL, 0, InputFormat, OutputFormat, dwFlags); 968 } 969 970 971 972 cmsHTRANSFORM CMSEXPORT cmsCreateMultiprofileTransform(cmsHPROFILE hProfiles[], 973 cmsUInt32Number nProfiles, 974 cmsUInt32Number InputFormat, 975 cmsUInt32Number OutputFormat, 976 cmsUInt32Number Intent, 977 cmsUInt32Number dwFlags) 978 { 979 980 if (nProfiles <= 0 || nProfiles > 255) { 981 cmsSignalError(NULL, cmsERROR_RANGE, "Wrong number of profiles. 1..255 expected, %d found.", nProfiles); 982 return NULL; 983 } 984 985 return cmsCreateMultiprofileTransformTHR(cmsGetProfileContextID(hProfiles[0]), 986 hProfiles, 987 nProfiles, 988 InputFormat, 989 OutputFormat, 990 Intent, 991 dwFlags); 992 } 993 994 cmsHTRANSFORM CMSEXPORT cmsCreateTransformTHR(cmsContext ContextID, 995 cmsHPROFILE Input, 996 cmsUInt32Number InputFormat, 997 cmsHPROFILE Output, 998 cmsUInt32Number OutputFormat, 999 cmsUInt32Number Intent, 1000 cmsUInt32Number dwFlags) 1001 { 1002 1003 cmsHPROFILE hArray[2]; 1004 1005 hArray[0] = Input; 1006 hArray[1] = Output; 1007 1008 return cmsCreateMultiprofileTransformTHR(ContextID, hArray, Output == NULL ? 1 : 2, InputFormat, OutputFormat, Intent, dwFlags); 1009 } 1010 1011 CMSAPI cmsHTRANSFORM CMSEXPORT cmsCreateTransform(cmsHPROFILE Input, 1012 cmsUInt32Number InputFormat, 1013 cmsHPROFILE Output, 1014 cmsUInt32Number OutputFormat, 1015 cmsUInt32Number Intent, 1016 cmsUInt32Number dwFlags) 1017 { 1018 return cmsCreateTransformTHR(cmsGetProfileContextID(Input), Input, InputFormat, Output, OutputFormat, Intent, dwFlags); 1019 } 1020 1021 1022 cmsHTRANSFORM CMSEXPORT cmsCreateProofingTransformTHR(cmsContext ContextID, 1023 cmsHPROFILE InputProfile, 1024 cmsUInt32Number InputFormat, 1025 cmsHPROFILE OutputProfile, 1026 cmsUInt32Number OutputFormat, 1027 cmsHPROFILE ProofingProfile, 1028 cmsUInt32Number nIntent, 1029 cmsUInt32Number ProofingIntent, 1030 cmsUInt32Number dwFlags) 1031 { 1032 cmsHPROFILE hArray[4]; 1033 cmsUInt32Number Intents[4]; 1034 cmsBool BPC[4]; 1035 cmsFloat64Number Adaptation[4]; 1036 cmsBool DoBPC = (dwFlags & cmsFLAGS_BLACKPOINTCOMPENSATION) ? TRUE : FALSE; 1037 1038 1039 hArray[0] = InputProfile; hArray[1] = ProofingProfile; hArray[2] = ProofingProfile; hArray[3] = OutputProfile; 1040 Intents[0] = nIntent; Intents[1] = nIntent; Intents[2] = INTENT_RELATIVE_COLORIMETRIC; Intents[3] = ProofingIntent; 1041 BPC[0] = DoBPC; BPC[1] = DoBPC; BPC[2] = 0; BPC[3] = 0; 1042 1043 Adaptation[0] = Adaptation[1] = Adaptation[2] = Adaptation[3] = cmsSetAdaptationStateTHR(ContextID, -1); 1044 1045 if (!(dwFlags & (cmsFLAGS_SOFTPROOFING|cmsFLAGS_GAMUTCHECK))) 1046 return cmsCreateTransformTHR(ContextID, InputProfile, InputFormat, OutputProfile, OutputFormat, nIntent, dwFlags); 1047 1048 return cmsCreateExtendedTransform(ContextID, 4, hArray, BPC, Intents, Adaptation, 1049 ProofingProfile, 1, InputFormat, OutputFormat, dwFlags); 1050 1051 } 1052 1053 1054 cmsHTRANSFORM CMSEXPORT cmsCreateProofingTransform(cmsHPROFILE InputProfile, 1055 cmsUInt32Number InputFormat, 1056 cmsHPROFILE OutputProfile, 1057 cmsUInt32Number OutputFormat, 1058 cmsHPROFILE ProofingProfile, 1059 cmsUInt32Number nIntent, 1060 cmsUInt32Number ProofingIntent, 1061 cmsUInt32Number dwFlags) 1062 { 1063 return cmsCreateProofingTransformTHR(cmsGetProfileContextID(InputProfile), 1064 InputProfile, 1065 InputFormat, 1066 OutputProfile, 1067 OutputFormat, 1068 ProofingProfile, 1069 nIntent, 1070 ProofingIntent, 1071 dwFlags); 1072 } 1073 1074 1075 // Grab the ContextID from an open transform. Returns NULL if a NULL transform is passed 1076 cmsContext CMSEXPORT cmsGetTransformContextID(cmsHTRANSFORM hTransform) 1077 { 1078 _cmsTRANSFORM* xform = (_cmsTRANSFORM*) hTransform; 1079 1080 if (xform == NULL) return NULL; 1081 return xform -> ContextID; 1082 } 1083 1084 // Grab the input/output formats 1085 cmsUInt32Number CMSEXPORT cmsGetTransformInputFormat(cmsHTRANSFORM hTransform) 1086 { 1087 _cmsTRANSFORM* xform = (_cmsTRANSFORM*) hTransform; 1088 1089 if (xform == NULL) return 0; 1090 return xform->InputFormat; 1091 } 1092 1093 cmsUInt32Number CMSEXPORT cmsGetTransformOutputFormat(cmsHTRANSFORM hTransform) 1094 { 1095 _cmsTRANSFORM* xform = (_cmsTRANSFORM*) hTransform; 1096 1097 if (xform == NULL) return 0; 1098 return xform->OutputFormat; 1099 } 1100 1101 // For backwards compatibility 1102 cmsBool CMSEXPORT cmsChangeBuffersFormat(cmsHTRANSFORM hTransform, 1103 cmsUInt32Number InputFormat, 1104 cmsUInt32Number OutputFormat) 1105 { 1106 1107 _cmsTRANSFORM* xform = (_cmsTRANSFORM*) hTransform; 1108 cmsFormatter16 FromInput, ToOutput; 1109 1110 1111 // We only can afford to change formatters if previous transform is at least 16 bits 1112 if (!(xform ->dwOriginalFlags & cmsFLAGS_CAN_CHANGE_FORMATTER)) { 1113 1114 cmsSignalError(xform ->ContextID, cmsERROR_NOT_SUITABLE, "cmsChangeBuffersFormat works only on transforms created originally with at least 16 bits of precision"); 1115 return FALSE; 1116 } 1117 1118 FromInput = _cmsGetFormatter(xform->ContextID, InputFormat, cmsFormatterInput, CMS_PACK_FLAGS_16BITS).Fmt16; 1119 ToOutput = _cmsGetFormatter(xform->ContextID, OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_16BITS).Fmt16; 1120 1121 if (FromInput == NULL || ToOutput == NULL) { 1122 1123 cmsSignalError(xform -> ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported raster format"); 1124 return FALSE; 1125 } 1126 1127 xform ->InputFormat = InputFormat; 1128 xform ->OutputFormat = OutputFormat; 1129 xform ->FromInput = FromInput; 1130 xform ->ToOutput = ToOutput; 1131 return TRUE; 1132 } 1133