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