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 }