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