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 if (p->Lut != NULL) { 633 634 for (Plugin = ctx->TransformCollection; 635 Plugin != NULL; 636 Plugin = Plugin->Next) { 637 638 if (Plugin->Factory(&p->xform, &p->UserData, &p->FreeUserData, &p->Lut, InputFormat, OutputFormat, dwFlags)) { 639 640 // Last plugin in the declaration order takes control. We just keep 641 // the original parameters as a logging. 642 // Note that cmsFLAGS_CAN_CHANGE_FORMATTER is not set, so by default 643 // an optimized transform is not reusable. The plug-in can, however, change 644 // the flags and make it suitable. 645 646 p->ContextID = ContextID; 647 p->InputFormat = *InputFormat; 648 p->OutputFormat = *OutputFormat; 649 p->dwOriginalFlags = *dwFlags; 650 651 // Fill the formatters just in case the optimized routine is interested. 652 // No error is thrown if the formatter doesn't exist. It is up to the optimization 653 // factory to decide what to do in those cases. 654 p->FromInput = _cmsGetFormatter(ContextID, *InputFormat, cmsFormatterInput, CMS_PACK_FLAGS_16BITS).Fmt16; 655 p->ToOutput = _cmsGetFormatter(ContextID, *OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_16BITS).Fmt16; 656 p->FromInputFloat = _cmsGetFormatter(ContextID, *InputFormat, cmsFormatterInput, CMS_PACK_FLAGS_FLOAT).FmtFloat; 657 p->ToOutputFloat = _cmsGetFormatter(ContextID, *OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_FLOAT).FmtFloat; 658 659 return p; 660 } 661 } 662 663 // Not suitable for the transform plug-in, let's check the pipeline plug-in 664 _cmsOptimizePipeline(ContextID, &p->Lut, Intent, InputFormat, OutputFormat, dwFlags); 665 } 666 667 // Check whatever this is a true floating point transform 668 if (_cmsFormatterIsFloat(*InputFormat) && _cmsFormatterIsFloat(*OutputFormat)) { 669 670 // Get formatter function always return a valid union, but the contents of this union may be NULL. 671 p ->FromInputFloat = _cmsGetFormatter(ContextID, *InputFormat, cmsFormatterInput, CMS_PACK_FLAGS_FLOAT).FmtFloat; 672 p ->ToOutputFloat = _cmsGetFormatter(ContextID, *OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_FLOAT).FmtFloat; 673 *dwFlags |= cmsFLAGS_CAN_CHANGE_FORMATTER; 674 675 if (p ->FromInputFloat == NULL || p ->ToOutputFloat == NULL) { 676 677 cmsSignalError(ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported raster format"); 678 _cmsFree(ContextID, p); 679 return NULL; 680 } 681 682 if (*dwFlags & cmsFLAGS_NULLTRANSFORM) { 683 684 p ->xform = NullFloatXFORM; 685 } 686 else { 687 // Float transforms don't use caché, always are non-NULL 688 p ->xform = FloatXFORM; 689 } 690 691 } 692 else { 693 694 if (*InputFormat == 0 && *OutputFormat == 0) { 695 p ->FromInput = p ->ToOutput = NULL; 696 *dwFlags |= cmsFLAGS_CAN_CHANGE_FORMATTER; 697 } 698 else { 699 700 int BytesPerPixelInput; 701 702 p ->FromInput = _cmsGetFormatter(ContextID, *InputFormat, cmsFormatterInput, CMS_PACK_FLAGS_16BITS).Fmt16; 703 p ->ToOutput = _cmsGetFormatter(ContextID, *OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_16BITS).Fmt16; 704 705 if (p ->FromInput == NULL || p ->ToOutput == NULL) { 706 707 cmsSignalError(ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported raster format"); 708 _cmsFree(ContextID, p); 709 return NULL; 710 } 711 712 BytesPerPixelInput = T_BYTES(p ->InputFormat); 713 if (BytesPerPixelInput == 0 || BytesPerPixelInput >= 2) 714 *dwFlags |= cmsFLAGS_CAN_CHANGE_FORMATTER; 715 716 } 717 718 if (*dwFlags & cmsFLAGS_NULLTRANSFORM) { 719 720 p ->xform = NullXFORM; 721 } 722 else { 723 if (*dwFlags & cmsFLAGS_NOCACHE) { 724 725 if (*dwFlags & cmsFLAGS_GAMUTCHECK) 726 p ->xform = PrecalculatedXFORMGamutCheck; // Gamut check, no caché 727 else 728 p ->xform = PrecalculatedXFORM; // No caché, no gamut check 729 } 730 else { 731 732 if (*dwFlags & cmsFLAGS_GAMUTCHECK) 733 p ->xform = CachedXFORMGamutCheck; // Gamut check, caché 734 else 735 p ->xform = CachedXFORM; // No gamut check, caché 736 737 } 738 } 739 } 740 741 p ->InputFormat = *InputFormat; 742 p ->OutputFormat = *OutputFormat; 743 p ->dwOriginalFlags = *dwFlags; 744 p ->ContextID = ContextID; 745 p ->UserData = NULL; 746 return p; 747 } 748 749 static 750 cmsBool GetXFormColorSpaces(int nProfiles, cmsHPROFILE hProfiles[], cmsColorSpaceSignature* Input, cmsColorSpaceSignature* Output) 751 { 752 cmsColorSpaceSignature ColorSpaceIn, ColorSpaceOut; 753 cmsColorSpaceSignature PostColorSpace; 754 int i; 755 756 if (nProfiles <= 0) return FALSE; 757 if (hProfiles[0] == NULL) return FALSE; 758 759 *Input = PostColorSpace = cmsGetColorSpace(hProfiles[0]); 760 761 for (i=0; i < nProfiles; i++) { 762 763 cmsProfileClassSignature cls; 764 cmsHPROFILE hProfile = hProfiles[i]; 765 766 int lIsInput = (PostColorSpace != cmsSigXYZData) && 767 (PostColorSpace != cmsSigLabData); 768 769 if (hProfile == NULL) return FALSE; 770 771 cls = cmsGetDeviceClass(hProfile); 772 773 if (cls == cmsSigNamedColorClass) { 774 775 ColorSpaceIn = cmsSig1colorData; 776 ColorSpaceOut = (nProfiles > 1) ? cmsGetPCS(hProfile) : cmsGetColorSpace(hProfile); 777 } 778 else 779 if (lIsInput || (cls == cmsSigLinkClass)) { 780 781 ColorSpaceIn = cmsGetColorSpace(hProfile); 782 ColorSpaceOut = cmsGetPCS(hProfile); 783 } 784 else 785 { 786 ColorSpaceIn = cmsGetPCS(hProfile); 787 ColorSpaceOut = cmsGetColorSpace(hProfile); 788 } 789 790 if (i==0) 791 *Input = ColorSpaceIn; 792 793 PostColorSpace = ColorSpaceOut; 794 } 795 796 *Output = PostColorSpace; 797 798 return TRUE; 799 } 800 801 // Check colorspace 802 static 803 cmsBool IsProperColorSpace(cmsColorSpaceSignature Check, cmsUInt32Number dwFormat) 804 { 805 int Space1 = T_COLORSPACE(dwFormat); 806 int Space2 = _cmsLCMScolorSpace(Check); 807 808 if (Space1 == PT_ANY) return TRUE; 809 if (Space1 == Space2) return TRUE; 810 811 if (Space1 == PT_LabV2 && Space2 == PT_Lab) return TRUE; 812 if (Space1 == PT_Lab && Space2 == PT_LabV2) return TRUE; 813 814 return FALSE; 815 } 816 817 // ---------------------------------------------------------------------------------------------------------------- 818 819 static 820 void SetWhitePoint(cmsCIEXYZ* wtPt, const cmsCIEXYZ* src) 821 { 822 if (src == NULL) { 823 wtPt ->X = cmsD50X; 824 wtPt ->Y = cmsD50Y; 825 wtPt ->Z = cmsD50Z; 826 } 827 else { 828 wtPt ->X = src->X; 829 wtPt ->Y = src->Y; 830 wtPt ->Z = src->Z; 831 } 832 833 } 834 835 // New to lcms 2.0 -- have all parameters available. 836 cmsHTRANSFORM CMSEXPORT cmsCreateExtendedTransform(cmsContext ContextID, 837 cmsUInt32Number nProfiles, cmsHPROFILE hProfiles[], 838 cmsBool BPC[], 839 cmsUInt32Number Intents[], 840 cmsFloat64Number AdaptationStates[], 841 cmsHPROFILE hGamutProfile, 842 cmsUInt32Number nGamutPCSposition, 843 cmsUInt32Number InputFormat, 844 cmsUInt32Number OutputFormat, 845 cmsUInt32Number dwFlags) 846 { 847 _cmsTRANSFORM* xform; 848 cmsColorSpaceSignature EntryColorSpace; 849 cmsColorSpaceSignature ExitColorSpace; 850 cmsPipeline* Lut; 851 cmsUInt32Number LastIntent = Intents[nProfiles-1]; 852 853 // If it is a fake transform 854 if (dwFlags & cmsFLAGS_NULLTRANSFORM) 855 { 856 return AllocEmptyTransform(ContextID, NULL, INTENT_PERCEPTUAL, &InputFormat, &OutputFormat, &dwFlags); 857 } 858 859 // If gamut check is requested, make sure we have a gamut profile 860 if (dwFlags & cmsFLAGS_GAMUTCHECK) { 861 if (hGamutProfile == NULL) dwFlags &= ~cmsFLAGS_GAMUTCHECK; 862 } 863 864 // On floating point transforms, inhibit cache 865 if (_cmsFormatterIsFloat(InputFormat) || _cmsFormatterIsFloat(OutputFormat)) 866 dwFlags |= cmsFLAGS_NOCACHE; 867 868 // Mark entry/exit spaces 869 if (!GetXFormColorSpaces(nProfiles, hProfiles, &EntryColorSpace, &ExitColorSpace)) { 870 cmsSignalError(ContextID, cmsERROR_NULL, "NULL input profiles on transform"); 871 return NULL; 872 } 873 874 // Check if proper colorspaces 875 if (!IsProperColorSpace(EntryColorSpace, InputFormat)) { 876 cmsSignalError(ContextID, cmsERROR_COLORSPACE_CHECK, "Wrong input color space on transform"); 877 return NULL; 878 } 879 880 if (!IsProperColorSpace(ExitColorSpace, OutputFormat)) { 881 cmsSignalError(ContextID, cmsERROR_COLORSPACE_CHECK, "Wrong output color space on transform"); 882 return NULL; 883 } 884 885 // Create a pipeline with all transformations 886 Lut = _cmsLinkProfiles(ContextID, nProfiles, Intents, hProfiles, BPC, AdaptationStates, dwFlags); 887 if (Lut == NULL) { 888 cmsSignalError(ContextID, cmsERROR_NOT_SUITABLE, "Couldn't link the profiles"); 889 return NULL; 890 } 891 892 // Check channel count 893 if ((cmsChannelsOf(EntryColorSpace) != cmsPipelineInputChannels(Lut)) || 894 (cmsChannelsOf(ExitColorSpace) != cmsPipelineOutputChannels(Lut))) { 895 cmsPipelineFree(Lut); 896 cmsSignalError(ContextID, cmsERROR_NOT_SUITABLE, "Channel count doesn't match. Profile is corrupted"); 897 return NULL; 898 } 899 900 901 // All seems ok 902 xform = AllocEmptyTransform(ContextID, Lut, LastIntent, &InputFormat, &OutputFormat, &dwFlags); 903 if (xform == NULL) { 904 return NULL; 905 } 906 907 // Keep values 908 xform ->EntryColorSpace = EntryColorSpace; 909 xform ->ExitColorSpace = ExitColorSpace; 910 xform ->RenderingIntent = Intents[nProfiles-1]; 911 912 // Take white points 913 SetWhitePoint(&xform->EntryWhitePoint, (cmsCIEXYZ*) cmsReadTag(hProfiles[0], cmsSigMediaWhitePointTag)); 914 SetWhitePoint(&xform->ExitWhitePoint, (cmsCIEXYZ*) cmsReadTag(hProfiles[nProfiles-1], cmsSigMediaWhitePointTag)); 915 916 917 // Create a gamut check LUT if requested 918 if (hGamutProfile != NULL && (dwFlags & cmsFLAGS_GAMUTCHECK)) 919 xform ->GamutCheck = _cmsCreateGamutCheckPipeline(ContextID, hProfiles, 920 BPC, Intents, 921 AdaptationStates, 922 nGamutPCSposition, 923 hGamutProfile); 924 925 926 // Try to read input and output colorant table 927 if (cmsIsTag(hProfiles[0], cmsSigColorantTableTag)) { 928 929 // Input table can only come in this way. 930 xform ->InputColorant = cmsDupNamedColorList((cmsNAMEDCOLORLIST*) cmsReadTag(hProfiles[0], cmsSigColorantTableTag)); 931 } 932 933 // Output is a little bit more complex. 934 if (cmsGetDeviceClass(hProfiles[nProfiles-1]) == cmsSigLinkClass) { 935 936 // This tag may exist only on devicelink profiles. 937 if (cmsIsTag(hProfiles[nProfiles-1], cmsSigColorantTableOutTag)) { 938 939 // It may be NULL if error 940 xform ->OutputColorant = cmsDupNamedColorList((cmsNAMEDCOLORLIST*) cmsReadTag(hProfiles[nProfiles-1], cmsSigColorantTableOutTag)); 941 } 942 943 } else { 944 945 if (cmsIsTag(hProfiles[nProfiles-1], cmsSigColorantTableTag)) { 946 947 xform -> OutputColorant = cmsDupNamedColorList((cmsNAMEDCOLORLIST*) cmsReadTag(hProfiles[nProfiles-1], cmsSigColorantTableTag)); 948 } 949 } 950 951 // Store the sequence of profiles 952 if (dwFlags & cmsFLAGS_KEEP_SEQUENCE) { 953 xform ->Sequence = _cmsCompileProfileSequence(ContextID, nProfiles, hProfiles); 954 } 955 else 956 xform ->Sequence = NULL; 957 958 // If this is a cached transform, init first value, which is zero (16 bits only) 959 if (!(dwFlags & cmsFLAGS_NOCACHE)) { 960 961 memset(&xform ->Cache.CacheIn, 0, sizeof(xform ->Cache.CacheIn)); 962 963 if (xform ->GamutCheck != NULL) { 964 TransformOnePixelWithGamutCheck(xform, xform ->Cache.CacheIn, xform->Cache.CacheOut); 965 } 966 else { 967 968 xform ->Lut ->Eval16Fn(xform ->Cache.CacheIn, xform->Cache.CacheOut, xform -> Lut->Data); 969 } 970 971 } 972 973 return (cmsHTRANSFORM) xform; 974 } 975 976 // Multiprofile transforms: Gamut check is not available here, as it is unclear from which profile the gamut comes. 977 cmsHTRANSFORM CMSEXPORT cmsCreateMultiprofileTransformTHR(cmsContext ContextID, 978 cmsHPROFILE hProfiles[], 979 cmsUInt32Number nProfiles, 980 cmsUInt32Number InputFormat, 981 cmsUInt32Number OutputFormat, 982 cmsUInt32Number Intent, 983 cmsUInt32Number dwFlags) 984 { 985 cmsUInt32Number i; 986 cmsBool BPC[256]; 987 cmsUInt32Number Intents[256]; 988 cmsFloat64Number AdaptationStates[256]; 989 990 if (nProfiles <= 0 || nProfiles > 255) { 991 cmsSignalError(ContextID, cmsERROR_RANGE, "Wrong number of profiles. 1..255 expected, %d found.", nProfiles); 992 return NULL; 993 } 994 995 for (i=0; i < nProfiles; i++) { 996 BPC[i] = dwFlags & cmsFLAGS_BLACKPOINTCOMPENSATION ? TRUE : FALSE; 997 Intents[i] = Intent; 998 AdaptationStates[i] = cmsSetAdaptationStateTHR(ContextID, -1); 999 } 1000 1001 1002 return cmsCreateExtendedTransform(ContextID, nProfiles, hProfiles, BPC, Intents, AdaptationStates, NULL, 0, InputFormat, OutputFormat, dwFlags); 1003 } 1004 1005 1006 1007 cmsHTRANSFORM CMSEXPORT cmsCreateMultiprofileTransform(cmsHPROFILE hProfiles[], 1008 cmsUInt32Number nProfiles, 1009 cmsUInt32Number InputFormat, 1010 cmsUInt32Number OutputFormat, 1011 cmsUInt32Number Intent, 1012 cmsUInt32Number dwFlags) 1013 { 1014 1015 if (nProfiles <= 0 || nProfiles > 255) { 1016 cmsSignalError(NULL, cmsERROR_RANGE, "Wrong number of profiles. 1..255 expected, %d found.", nProfiles); 1017 return NULL; 1018 } 1019 1020 return cmsCreateMultiprofileTransformTHR(cmsGetProfileContextID(hProfiles[0]), 1021 hProfiles, 1022 nProfiles, 1023 InputFormat, 1024 OutputFormat, 1025 Intent, 1026 dwFlags); 1027 } 1028 1029 cmsHTRANSFORM CMSEXPORT cmsCreateTransformTHR(cmsContext ContextID, 1030 cmsHPROFILE Input, 1031 cmsUInt32Number InputFormat, 1032 cmsHPROFILE Output, 1033 cmsUInt32Number OutputFormat, 1034 cmsUInt32Number Intent, 1035 cmsUInt32Number dwFlags) 1036 { 1037 1038 cmsHPROFILE hArray[2]; 1039 1040 hArray[0] = Input; 1041 hArray[1] = Output; 1042 1043 return cmsCreateMultiprofileTransformTHR(ContextID, hArray, Output == NULL ? 1 : 2, InputFormat, OutputFormat, Intent, dwFlags); 1044 } 1045 1046 CMSAPI cmsHTRANSFORM CMSEXPORT cmsCreateTransform(cmsHPROFILE Input, 1047 cmsUInt32Number InputFormat, 1048 cmsHPROFILE Output, 1049 cmsUInt32Number OutputFormat, 1050 cmsUInt32Number Intent, 1051 cmsUInt32Number dwFlags) 1052 { 1053 return cmsCreateTransformTHR(cmsGetProfileContextID(Input), Input, InputFormat, Output, OutputFormat, Intent, dwFlags); 1054 } 1055 1056 1057 cmsHTRANSFORM CMSEXPORT cmsCreateProofingTransformTHR(cmsContext ContextID, 1058 cmsHPROFILE InputProfile, 1059 cmsUInt32Number InputFormat, 1060 cmsHPROFILE OutputProfile, 1061 cmsUInt32Number OutputFormat, 1062 cmsHPROFILE ProofingProfile, 1063 cmsUInt32Number nIntent, 1064 cmsUInt32Number ProofingIntent, 1065 cmsUInt32Number dwFlags) 1066 { 1067 cmsHPROFILE hArray[4]; 1068 cmsUInt32Number Intents[4]; 1069 cmsBool BPC[4]; 1070 cmsFloat64Number Adaptation[4]; 1071 cmsBool DoBPC = (dwFlags & cmsFLAGS_BLACKPOINTCOMPENSATION) ? TRUE : FALSE; 1072 1073 1074 hArray[0] = InputProfile; hArray[1] = ProofingProfile; hArray[2] = ProofingProfile; hArray[3] = OutputProfile; 1075 Intents[0] = nIntent; Intents[1] = nIntent; Intents[2] = INTENT_RELATIVE_COLORIMETRIC; Intents[3] = ProofingIntent; 1076 BPC[0] = DoBPC; BPC[1] = DoBPC; BPC[2] = 0; BPC[3] = 0; 1077 1078 Adaptation[0] = Adaptation[1] = Adaptation[2] = Adaptation[3] = cmsSetAdaptationStateTHR(ContextID, -1); 1079 1080 if (!(dwFlags & (cmsFLAGS_SOFTPROOFING|cmsFLAGS_GAMUTCHECK))) 1081 return cmsCreateTransformTHR(ContextID, InputProfile, InputFormat, OutputProfile, OutputFormat, nIntent, dwFlags); 1082 1083 return cmsCreateExtendedTransform(ContextID, 4, hArray, BPC, Intents, Adaptation, 1084 ProofingProfile, 1, InputFormat, OutputFormat, dwFlags); 1085 1086 } 1087 1088 1089 cmsHTRANSFORM CMSEXPORT cmsCreateProofingTransform(cmsHPROFILE InputProfile, 1090 cmsUInt32Number InputFormat, 1091 cmsHPROFILE OutputProfile, 1092 cmsUInt32Number OutputFormat, 1093 cmsHPROFILE ProofingProfile, 1094 cmsUInt32Number nIntent, 1095 cmsUInt32Number ProofingIntent, 1096 cmsUInt32Number dwFlags) 1097 { 1098 return cmsCreateProofingTransformTHR(cmsGetProfileContextID(InputProfile), 1099 InputProfile, 1100 InputFormat, 1101 OutputProfile, 1102 OutputFormat, 1103 ProofingProfile, 1104 nIntent, 1105 ProofingIntent, 1106 dwFlags); 1107 } 1108 1109 1110 // Grab the ContextID from an open transform. Returns NULL if a NULL transform is passed 1111 cmsContext CMSEXPORT cmsGetTransformContextID(cmsHTRANSFORM hTransform) 1112 { 1113 _cmsTRANSFORM* xform = (_cmsTRANSFORM*) hTransform; 1114 1115 if (xform == NULL) return NULL; 1116 return xform -> ContextID; 1117 } 1118 1119 // Grab the input/output formats 1120 cmsUInt32Number CMSEXPORT cmsGetTransformInputFormat(cmsHTRANSFORM hTransform) 1121 { 1122 _cmsTRANSFORM* xform = (_cmsTRANSFORM*) hTransform; 1123 1124 if (xform == NULL) return 0; 1125 return xform->InputFormat; 1126 } 1127 1128 cmsUInt32Number CMSEXPORT cmsGetTransformOutputFormat(cmsHTRANSFORM hTransform) 1129 { 1130 _cmsTRANSFORM* xform = (_cmsTRANSFORM*) hTransform; 1131 1132 if (xform == NULL) return 0; 1133 return xform->OutputFormat; 1134 } 1135 1136 // For backwards compatibility 1137 cmsBool CMSEXPORT cmsChangeBuffersFormat(cmsHTRANSFORM hTransform, 1138 cmsUInt32Number InputFormat, 1139 cmsUInt32Number OutputFormat) 1140 { 1141 1142 _cmsTRANSFORM* xform = (_cmsTRANSFORM*) hTransform; 1143 cmsFormatter16 FromInput, ToOutput; 1144 1145 1146 // We only can afford to change formatters if previous transform is at least 16 bits 1147 if (!(xform ->dwOriginalFlags & cmsFLAGS_CAN_CHANGE_FORMATTER)) { 1148 1149 cmsSignalError(xform ->ContextID, cmsERROR_NOT_SUITABLE, "cmsChangeBuffersFormat works only on transforms created originally with at least 16 bits of precision"); 1150 return FALSE; 1151 } 1152 1153 FromInput = _cmsGetFormatter(xform->ContextID, InputFormat, cmsFormatterInput, CMS_PACK_FLAGS_16BITS).Fmt16; 1154 ToOutput = _cmsGetFormatter(xform->ContextID, OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_16BITS).Fmt16; 1155 1156 if (FromInput == NULL || ToOutput == NULL) { 1157 1158 cmsSignalError(xform -> ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported raster format"); 1159 return FALSE; 1160 } 1161 1162 xform ->InputFormat = InputFormat; 1163 xform ->OutputFormat = OutputFormat; 1164 xform ->FromInput = FromInput; 1165 xform ->ToOutput = ToOutput; 1166 return TRUE; 1167 }