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