Home | History | Annotate | Download | only in src
      1 //---------------------------------------------------------------------------------
      2 //
      3 //  Little Color Management System
      4 //  Copyright (c) 1998-2014 Marti Maria Saguer
      5 //
      6 // Permission is hereby granted, free of charge, to any person obtaining
      7 // a copy of this software and associated documentation files (the "Software"),
      8 // to deal in the Software without restriction, including without limitation
      9 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
     10 // and/or sell copies of the Software, and to permit persons to whom the Software
     11 // is furnished to do so, subject to the following conditions:
     12 //
     13 // The above copyright notice and this permission notice shall be included in
     14 // all copies or substantial portions of the Software.
     15 //
     16 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
     17 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
     18 // THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
     19 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
     20 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
     21 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
     22 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
     23 //
     24 //---------------------------------------------------------------------------------
     25 //
     26 
     27 #include "lcms2_internal.h"
     28 
     29 // Transformations stuff
     30 // -----------------------------------------------------------------------
     31 
     32 #define DEFAULT_OBSERVER_ADAPTATION_STATE 1.0
     33 
     34 // The Context0 observer adaptation state.
     35 _cmsAdaptationStateChunkType _cmsAdaptationStateChunk = { DEFAULT_OBSERVER_ADAPTATION_STATE };
     36 
     37 // Init and duplicate observer adaptation state
     38 void _cmsAllocAdaptationStateChunk(struct _cmsContext_struct* ctx,
     39                                    const struct _cmsContext_struct* src)
     40 {
     41     static _cmsAdaptationStateChunkType AdaptationStateChunk = { DEFAULT_OBSERVER_ADAPTATION_STATE };
     42     void* from;
     43 
     44     if (src != NULL) {
     45         from = src ->chunks[AdaptationStateContext];
     46     }
     47     else {
     48        from = &AdaptationStateChunk;
     49     }
     50 
     51     ctx ->chunks[AdaptationStateContext] = _cmsSubAllocDup(ctx ->MemPool, from, sizeof(_cmsAdaptationStateChunkType));
     52 }
     53 
     54 
     55 // Sets adaptation state for absolute colorimetric intent in the given context.  Adaptation state applies on all
     56 // but cmsCreateExtendedTransformTHR().  Little CMS can handle incomplete adaptation states.
     57 cmsFloat64Number CMSEXPORT cmsSetAdaptationStateTHR(cmsContext ContextID, cmsFloat64Number d)
     58 {
     59     cmsFloat64Number prev;
     60     _cmsAdaptationStateChunkType* ptr = (_cmsAdaptationStateChunkType*) _cmsContextGetClientChunk(ContextID, AdaptationStateContext);
     61 
     62     // Get previous value for return
     63     prev = ptr ->AdaptationState;
     64 
     65     // Set the value if d is positive or zero
     66     if (d >= 0.0) {
     67 
     68         ptr ->AdaptationState = d;
     69     }
     70 
     71     // Always return previous value
     72     return prev;
     73 }
     74 
     75 
     76 // The adaptation state may be defaulted by this function. If you don't like it, use the extended transform routine
     77 cmsFloat64Number CMSEXPORT cmsSetAdaptationState(cmsFloat64Number d)
     78 {
     79     return cmsSetAdaptationStateTHR(NULL, d);
     80 }
     81 
     82 // -----------------------------------------------------------------------
     83 
     84 // Alarm codes for 16-bit transformations, because the fixed range of containers there are
     85 // no values left to mark out of gamut.
     86 
     87 #define DEFAULT_ALARM_CODES_VALUE {0x7F00, 0x7F00, 0x7F00, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
     88 
     89 _cmsAlarmCodesChunkType _cmsAlarmCodesChunk = { DEFAULT_ALARM_CODES_VALUE };
     90 
     91 // Sets the codes used to mark out-out-gamut on Proofing transforms for a given context. Values are meant to be
     92 // encoded in 16 bits.
     93 void CMSEXPORT cmsSetAlarmCodesTHR(cmsContext ContextID, const cmsUInt16Number AlarmCodesP[cmsMAXCHANNELS])
     94 {
     95     _cmsAlarmCodesChunkType* ContextAlarmCodes = (_cmsAlarmCodesChunkType*) _cmsContextGetClientChunk(ContextID, AlarmCodesContext);
     96 
     97     _cmsAssert(ContextAlarmCodes != NULL); // Can't happen
     98 
     99     memcpy(ContextAlarmCodes->AlarmCodes, AlarmCodesP, sizeof(ContextAlarmCodes->AlarmCodes));
    100 }
    101 
    102 // Gets the current codes used to mark out-out-gamut on Proofing transforms for the given context.
    103 // Values are meant to be encoded in 16 bits.
    104 void CMSEXPORT cmsGetAlarmCodesTHR(cmsContext ContextID, cmsUInt16Number AlarmCodesP[cmsMAXCHANNELS])
    105 {
    106     _cmsAlarmCodesChunkType* ContextAlarmCodes = (_cmsAlarmCodesChunkType*) _cmsContextGetClientChunk(ContextID, AlarmCodesContext);
    107 
    108     _cmsAssert(ContextAlarmCodes != NULL); // Can't happen
    109 
    110     memcpy(AlarmCodesP, ContextAlarmCodes->AlarmCodes, sizeof(ContextAlarmCodes->AlarmCodes));
    111 }
    112 
    113 void CMSEXPORT cmsSetAlarmCodes(const cmsUInt16Number NewAlarm[cmsMAXCHANNELS])
    114 {
    115     _cmsAssert(NewAlarm != NULL);
    116 
    117     cmsSetAlarmCodesTHR(NULL, NewAlarm);
    118 }
    119 
    120 void CMSEXPORT cmsGetAlarmCodes(cmsUInt16Number OldAlarm[cmsMAXCHANNELS])
    121 {
    122     _cmsAssert(OldAlarm != NULL);
    123     cmsGetAlarmCodesTHR(NULL, OldAlarm);
    124 }
    125 
    126 
    127 // Init and duplicate alarm codes
    128 void _cmsAllocAlarmCodesChunk(struct _cmsContext_struct* ctx,
    129                               const struct _cmsContext_struct* src)
    130 {
    131     static _cmsAlarmCodesChunkType AlarmCodesChunk = { DEFAULT_ALARM_CODES_VALUE };
    132     void* from;
    133 
    134     if (src != NULL) {
    135         from = src ->chunks[AlarmCodesContext];
    136     }
    137     else {
    138        from = &AlarmCodesChunk;
    139     }
    140 
    141     ctx ->chunks[AlarmCodesContext] = _cmsSubAllocDup(ctx ->MemPool, from, sizeof(_cmsAlarmCodesChunkType));
    142 }
    143 
    144 // -----------------------------------------------------------------------
    145 
    146 // Get rid of transform resources
    147 void CMSEXPORT cmsDeleteTransform(cmsHTRANSFORM hTransform)
    148 {
    149     _cmsTRANSFORM* p = (_cmsTRANSFORM*) hTransform;
    150 
    151     _cmsAssert(p != NULL);
    152 
    153     if (p -> GamutCheck)
    154         cmsPipelineFree(p -> GamutCheck);
    155 
    156     if (p -> Lut)
    157         cmsPipelineFree(p -> Lut);
    158 
    159     if (p ->InputColorant)
    160         cmsFreeNamedColorList(p ->InputColorant);
    161 
    162     if (p -> OutputColorant)
    163         cmsFreeNamedColorList(p ->OutputColorant);
    164 
    165     if (p ->Sequence)
    166         cmsFreeProfileSequenceDescription(p ->Sequence);
    167 
    168     if (p ->UserData)
    169         p ->FreeUserData(p ->ContextID, p ->UserData);
    170 
    171     _cmsFree(p ->ContextID, (void *) p);
    172 }
    173 
    174 // Apply transform.
    175 void CMSEXPORT cmsDoTransform(cmsHTRANSFORM  Transform,
    176                               const void* InputBuffer,
    177                               void* OutputBuffer,
    178                               cmsUInt32Number Size)
    179 
    180 {
    181     _cmsTRANSFORM* p = (_cmsTRANSFORM*) Transform;
    182 
    183     p -> xform(p, InputBuffer, OutputBuffer, Size, Size);
    184 }
    185 
    186 
    187 // Apply transform.
    188 void CMSEXPORT cmsDoTransformStride(cmsHTRANSFORM  Transform,
    189                               const void* InputBuffer,
    190                               void* OutputBuffer,
    191                               cmsUInt32Number Size, cmsUInt32Number Stride)
    192 
    193 {
    194     _cmsTRANSFORM* p = (_cmsTRANSFORM*) Transform;
    195 
    196     p -> xform(p, InputBuffer, OutputBuffer, Size, Stride);
    197 }
    198 
    199 
    200 // Transform routines ----------------------------------------------------------------------------------------------------------
    201 
    202 // Float xform converts floats. Since there are no performance issues, one routine does all job, including gamut check.
    203 // Note that because extended range, we can use a -1.0 value for out of gamut in this case.
    204 static
    205 void FloatXFORM(_cmsTRANSFORM* p,
    206                 const void* in,
    207                 void* out, cmsUInt32Number Size, cmsUInt32Number Stride)
    208 {
    209     cmsUInt8Number* accum;
    210     cmsUInt8Number* output;
    211     cmsFloat32Number fIn[cmsMAXCHANNELS], fOut[cmsMAXCHANNELS];
    212     cmsFloat32Number OutOfGamut;
    213     cmsUInt32Number i, j;
    214 
    215     accum  = (cmsUInt8Number*)  in;
    216     output = (cmsUInt8Number*)  out;
    217 
    218     for (i=0; i < Size; i++) {
    219 
    220         accum = p -> FromInputFloat(p, fIn, accum, Stride);
    221 
    222         // Any gamut chack to do?
    223         if (p ->GamutCheck != NULL) {
    224 
    225             // Evaluate gamut marker.
    226             cmsPipelineEvalFloat( fIn, &OutOfGamut, p ->GamutCheck);
    227 
    228             // Is current color out of gamut?
    229             if (OutOfGamut > 0.0) {
    230 
    231                 // Certainly, out of gamut
    232                 for (j=0; j < cmsMAXCHANNELS; j++)
    233                     fOut[j] = -1.0;
    234 
    235             }
    236             else {
    237                 // No, proceed normally
    238                 cmsPipelineEvalFloat(fIn, fOut, p -> Lut);
    239             }
    240         }
    241         else {
    242 
    243             // No gamut check at all
    244             cmsPipelineEvalFloat(fIn, fOut, p -> Lut);
    245         }
    246 
    247         // Back to asked representation
    248         output = p -> ToOutputFloat(p, fOut, output, Stride);
    249     }
    250 }
    251 
    252 
    253 static
    254 void NullFloatXFORM(_cmsTRANSFORM* p,
    255                     const void* in,
    256                     void* out,
    257                     cmsUInt32Number Size,
    258                     cmsUInt32Number Stride)
    259 {
    260     cmsUInt8Number* accum;
    261     cmsUInt8Number* output;
    262     cmsFloat32Number fIn[cmsMAXCHANNELS];
    263     cmsUInt32Number i, n;
    264 
    265     accum  = (cmsUInt8Number*)  in;
    266     output = (cmsUInt8Number*)  out;
    267     n = Size;
    268 
    269     for (i=0; i < n; i++) {
    270 
    271         accum  = p -> FromInputFloat(p, fIn, accum, Stride);
    272         output = p -> ToOutputFloat(p, fIn, output, Stride);
    273     }
    274 }
    275 
    276 // 16 bit precision -----------------------------------------------------------------------------------------------------------
    277 
    278 // Null transformation, only applies formatters. No cach?static
    279 void NullXFORM(_cmsTRANSFORM* p,
    280                const void* in,
    281                void* out, cmsUInt32Number Size,
    282                cmsUInt32Number Stride)
    283 {
    284     cmsUInt8Number* accum;
    285     cmsUInt8Number* output;
    286     cmsUInt16Number wIn[cmsMAXCHANNELS];
    287     cmsUInt32Number i, n;
    288 
    289     accum  = (cmsUInt8Number*)  in;
    290     output = (cmsUInt8Number*)  out;
    291     n = Size;                    // Buffer len
    292 
    293     for (i=0; i < n; i++) {
    294 
    295         accum  = p -> FromInput(p, wIn, accum, Stride);
    296         output = p -> ToOutput(p, wIn, output, Stride);
    297     }
    298 }
    299 
    300 
    301 // No gamut check, no cache, 16 bits
    302 static
    303 void PrecalculatedXFORM(_cmsTRANSFORM* p,
    304                         const void* in,
    305                         void* out, cmsUInt32Number Size, cmsUInt32Number Stride)
    306 {
    307     register cmsUInt8Number* accum;
    308     register cmsUInt8Number* output;
    309     cmsUInt16Number wIn[cmsMAXCHANNELS], wOut[cmsMAXCHANNELS];
    310     cmsUInt32Number i, n;
    311 
    312     accum  = (cmsUInt8Number*)  in;
    313     output = (cmsUInt8Number*)  out;
    314     n = Size;
    315 
    316     for (i=0; i < n; i++) {
    317 
    318         accum = p -> FromInput(p, wIn, accum, Stride);
    319         p ->Lut ->Eval16Fn(wIn, wOut, p -> Lut->Data);
    320         output = p -> ToOutput(p, wOut, output, Stride);
    321     }
    322 }
    323 
    324 
    325 // Auxiliar: Handle precalculated gamut check. The retrieval of context may be alittle bit slow, but this function is not critical.
    326 static
    327 void TransformOnePixelWithGamutCheck(_cmsTRANSFORM* p,
    328                                      const cmsUInt16Number wIn[],
    329                                      cmsUInt16Number wOut[])
    330 {
    331     cmsUInt16Number wOutOfGamut;
    332 
    333     p ->GamutCheck ->Eval16Fn(wIn, &wOutOfGamut, p ->GamutCheck ->Data);
    334     if (wOutOfGamut >= 1) {
    335 
    336         cmsUInt16Number i;
    337         _cmsAlarmCodesChunkType* ContextAlarmCodes = (_cmsAlarmCodesChunkType*) _cmsContextGetClientChunk(p->ContextID, AlarmCodesContext);
    338 
    339         for (i=0; i < p ->Lut->OutputChannels; i++) {
    340 
    341             wOut[i] = ContextAlarmCodes ->AlarmCodes[i];
    342         }
    343     }
    344     else
    345         p ->Lut ->Eval16Fn(wIn, wOut, p -> Lut->Data);
    346 }
    347 
    348 // Gamut check, No cach? 16 bits.
    349 static
    350 void PrecalculatedXFORMGamutCheck(_cmsTRANSFORM* p,
    351                                   const void* in,
    352                                   void* out, cmsUInt32Number Size, cmsUInt32Number Stride)
    353 {
    354     cmsUInt8Number* accum;
    355     cmsUInt8Number* output;
    356     cmsUInt16Number wIn[cmsMAXCHANNELS], wOut[cmsMAXCHANNELS];
    357     cmsUInt32Number i, n;
    358 
    359     accum  = (cmsUInt8Number*)  in;
    360     output = (cmsUInt8Number*)  out;
    361     n = Size;                    // Buffer len
    362 
    363     for (i=0; i < n; i++) {
    364 
    365         accum = p -> FromInput(p, wIn, accum, Stride);
    366         TransformOnePixelWithGamutCheck(p, wIn, wOut);
    367         output = p -> ToOutput(p, wOut, output, Stride);
    368     }
    369 }
    370 
    371 
    372 // No gamut check, Cach? 16 bits,
    373 static
    374 void CachedXFORM(_cmsTRANSFORM* p,
    375                  const void* in,
    376                  void* out, cmsUInt32Number Size, cmsUInt32Number Stride)
    377 {
    378     cmsUInt8Number* accum;
    379     cmsUInt8Number* output;
    380     cmsUInt16Number wIn[cmsMAXCHANNELS], wOut[cmsMAXCHANNELS];
    381     cmsUInt32Number i, n;
    382     _cmsCACHE Cache;
    383 
    384     accum  = (cmsUInt8Number*)  in;
    385     output = (cmsUInt8Number*)  out;
    386     n = Size;                    // Buffer len
    387 
    388     // Empty buffers for quick memcmp
    389     memset(wIn,  0, sizeof(wIn));
    390     memset(wOut, 0, sizeof(wOut));
    391 
    392     // Get copy of zero cache
    393     memcpy(&Cache, &p ->Cache, sizeof(Cache));
    394 
    395     for (i=0; i < n; i++) {
    396 
    397         accum = p -> FromInput(p, wIn, accum, Stride);
    398 
    399         if (memcmp(wIn, Cache.CacheIn, sizeof(Cache.CacheIn)) == 0) {
    400 
    401             memcpy(wOut, Cache.CacheOut, sizeof(Cache.CacheOut));
    402         }
    403         else {
    404 
    405             p ->Lut ->Eval16Fn(wIn, wOut, p -> Lut->Data);
    406 
    407             memcpy(Cache.CacheIn,  wIn,  sizeof(Cache.CacheIn));
    408             memcpy(Cache.CacheOut, wOut, sizeof(Cache.CacheOut));
    409         }
    410 
    411         output = p -> ToOutput(p, wOut, output, Stride);
    412     }
    413 
    414 }
    415 
    416 
    417 // All those nice features together
    418 static
    419 void CachedXFORMGamutCheck(_cmsTRANSFORM* p,
    420                            const void* in,
    421                            void* out, cmsUInt32Number Size, cmsUInt32Number Stride)
    422 {
    423        cmsUInt8Number* accum;
    424        cmsUInt8Number* output;
    425        cmsUInt16Number wIn[cmsMAXCHANNELS], wOut[cmsMAXCHANNELS];
    426        cmsUInt32Number i, n;
    427        _cmsCACHE Cache;
    428 
    429        accum  = (cmsUInt8Number*)  in;
    430        output = (cmsUInt8Number*)  out;
    431        n = Size;                    // Buffer len
    432 
    433        // Empty buffers for quick memcmp
    434        memset(wIn,  0, sizeof(cmsUInt16Number) * cmsMAXCHANNELS);
    435        memset(wOut, 0, sizeof(cmsUInt16Number) * cmsMAXCHANNELS);
    436 
    437        // Get copy of zero cache
    438        memcpy(&Cache, &p ->Cache, sizeof(Cache));
    439 
    440        for (i=0; i < n; i++) {
    441 
    442             accum = p -> FromInput(p, wIn, accum, Stride);
    443 
    444             if (memcmp(wIn, Cache.CacheIn, sizeof(Cache.CacheIn)) == 0) {
    445                     memcpy(wOut, Cache.CacheOut, sizeof(Cache.CacheOut));
    446             }
    447             else {
    448                     TransformOnePixelWithGamutCheck(p, wIn, wOut);
    449                     memcpy(Cache.CacheIn, wIn, sizeof(Cache.CacheIn));
    450                     memcpy(Cache.CacheOut, wOut, sizeof(Cache.CacheOut));
    451             }
    452 
    453             output = p -> ToOutput(p, wOut, output, Stride);
    454        }
    455 
    456 }
    457 
    458 // -------------------------------------------------------------------------------------------------------------
    459 
    460 // List of used-defined transform factories
    461 typedef struct _cmsTransformCollection_st {
    462 
    463     _cmsTransformFactory  Factory;
    464     struct _cmsTransformCollection_st *Next;
    465 
    466 } _cmsTransformCollection;
    467 
    468 // The linked list head
    469 _cmsTransformPluginChunkType _cmsTransformPluginChunk = { NULL };
    470 
    471 
    472 // Duplicates the zone of memory used by the plug-in in the new context
    473 static
    474 void DupPluginTransformList(struct _cmsContext_struct* ctx,
    475                                                const struct _cmsContext_struct* src)
    476 {
    477    _cmsTransformPluginChunkType newHead = { NULL };
    478    _cmsTransformCollection*  entry;
    479    _cmsTransformCollection*  Anterior = NULL;
    480    _cmsTransformPluginChunkType* head = (_cmsTransformPluginChunkType*) src->chunks[TransformPlugin];
    481 
    482     // Walk the list copying all nodes
    483    for (entry = head->TransformCollection;
    484         entry != NULL;
    485         entry = entry ->Next) {
    486 
    487             _cmsTransformCollection *newEntry = ( _cmsTransformCollection *) _cmsSubAllocDup(ctx ->MemPool, entry, sizeof(_cmsTransformCollection));
    488 
    489             if (newEntry == NULL)
    490                 return;
    491 
    492             // We want to keep the linked list order, so this is a little bit tricky
    493             newEntry -> Next = NULL;
    494             if (Anterior)
    495                 Anterior -> Next = newEntry;
    496 
    497             Anterior = newEntry;
    498 
    499             if (newHead.TransformCollection == NULL)
    500                 newHead.TransformCollection = newEntry;
    501     }
    502 
    503   ctx ->chunks[TransformPlugin] = _cmsSubAllocDup(ctx->MemPool, &newHead, sizeof(_cmsTransformPluginChunkType));
    504 }
    505 
    506 void _cmsAllocTransformPluginChunk(struct _cmsContext_struct* ctx,
    507                                         const struct _cmsContext_struct* src)
    508 {
    509     if (src != NULL) {
    510 
    511         // Copy all linked list
    512         DupPluginTransformList(ctx, src);
    513     }
    514     else {
    515         static _cmsTransformPluginChunkType TransformPluginChunkType = { NULL };
    516         ctx ->chunks[TransformPlugin] = _cmsSubAllocDup(ctx ->MemPool, &TransformPluginChunkType, sizeof(_cmsTransformPluginChunkType));
    517     }
    518 }
    519 
    520 
    521 
    522 // Register new ways to transform
    523 cmsBool  _cmsRegisterTransformPlugin(cmsContext ContextID, cmsPluginBase* Data)
    524 {
    525     cmsPluginTransform* Plugin = (cmsPluginTransform*) Data;
    526     _cmsTransformCollection* fl;
    527     _cmsTransformPluginChunkType* ctx = ( _cmsTransformPluginChunkType*) _cmsContextGetClientChunk(ContextID,TransformPlugin);
    528 
    529     if (Data == NULL) {
    530 
    531         // Free the chain. Memory is safely freed at exit
    532         ctx->TransformCollection = NULL;
    533         return TRUE;
    534     }
    535 
    536     // Factory callback is required
    537     if (Plugin ->Factory == NULL) return FALSE;
    538 
    539 
    540     fl = (_cmsTransformCollection*) _cmsPluginMalloc(ContextID, sizeof(_cmsTransformCollection));
    541     if (fl == NULL) return FALSE;
    542 
    543     // Copy the parameters
    544     fl ->Factory = Plugin ->Factory;
    545 
    546     // Keep linked list
    547     fl ->Next = ctx->TransformCollection;
    548     ctx->TransformCollection = fl;
    549 
    550     // All is ok
    551     return TRUE;
    552 }
    553 
    554 
    555 void CMSEXPORT _cmsSetTransformUserData(struct _cmstransform_struct *CMMcargo, void* ptr, _cmsFreeUserDataFn FreePrivateDataFn)
    556 {
    557     _cmsAssert(CMMcargo != NULL);
    558     CMMcargo ->UserData = ptr;
    559     CMMcargo ->FreeUserData = FreePrivateDataFn;
    560 }
    561 
    562 // returns the pointer defined by the plug-in to store private data
    563 void * CMSEXPORT _cmsGetTransformUserData(struct _cmstransform_struct *CMMcargo)
    564 {
    565     _cmsAssert(CMMcargo != NULL);
    566     return CMMcargo ->UserData;
    567 }
    568 
    569 // returns the current formatters
    570 void CMSEXPORT _cmsGetTransformFormatters16(struct _cmstransform_struct *CMMcargo, cmsFormatter16* FromInput, cmsFormatter16* ToOutput)
    571 {
    572      _cmsAssert(CMMcargo != NULL);
    573      if (FromInput) *FromInput = CMMcargo ->FromInput;
    574      if (ToOutput)  *ToOutput  = CMMcargo ->ToOutput;
    575 }
    576 
    577 void CMSEXPORT _cmsGetTransformFormattersFloat(struct _cmstransform_struct *CMMcargo, cmsFormatterFloat* FromInput, cmsFormatterFloat* ToOutput)
    578 {
    579      _cmsAssert(CMMcargo != NULL);
    580      if (FromInput) *FromInput = CMMcargo ->FromInputFloat;
    581      if (ToOutput)  *ToOutput  = CMMcargo ->ToOutputFloat;
    582 }
    583 
    584 
    585 // Allocate transform struct and set it to defaults. Ask the optimization plug-in about if those formats are proper
    586 // for separated transforms. If this is the case,
    587 static
    588 _cmsTRANSFORM* AllocEmptyTransform(cmsContext ContextID, cmsPipeline* lut,
    589                                                cmsUInt32Number Intent, cmsUInt32Number* InputFormat, cmsUInt32Number* OutputFormat, cmsUInt32Number* dwFlags)
    590 {
    591      _cmsTransformPluginChunkType* ctx = ( _cmsTransformPluginChunkType*) _cmsContextGetClientChunk(ContextID, TransformPlugin);
    592      _cmsTransformCollection* Plugin;
    593 
    594     // Allocate needed memory
    595     _cmsTRANSFORM* p = (_cmsTRANSFORM*) _cmsMallocZero(ContextID, sizeof(_cmsTRANSFORM));
    596     if (!p) return NULL;
    597 
    598     // Store the proposed pipeline
    599     p ->Lut = lut;
    600 
    601     // Let's see if any plug-in want to do the transform by itself
    602     for (Plugin = ctx ->TransformCollection;
    603         Plugin != NULL;
    604         Plugin = Plugin ->Next) {
    605 
    606             if (Plugin ->Factory(&p->xform, &p->UserData, &p ->FreeUserData, &p ->Lut, InputFormat, OutputFormat, dwFlags)) {
    607 
    608                 // Last plugin in the declaration order takes control. We just keep
    609                 // the original parameters as a logging.
    610                 // Note that cmsFLAGS_CAN_CHANGE_FORMATTER is not set, so by default
    611                 // an optimized transform is not reusable. The plug-in can, however, change
    612                 // the flags and make it suitable.
    613 
    614                 p ->ContextID       = ContextID;
    615                 p ->InputFormat     = *InputFormat;
    616                 p ->OutputFormat    = *OutputFormat;
    617                 p ->dwOriginalFlags = *dwFlags;
    618 
    619                 // Fill the formatters just in case the optimized routine is interested.
    620                 // No error is thrown if the formatter doesn't exist. It is up to the optimization
    621                 // factory to decide what to do in those cases.
    622                 p ->FromInput      = _cmsGetFormatter(ContextID, *InputFormat,  cmsFormatterInput, CMS_PACK_FLAGS_16BITS).Fmt16;
    623                 p ->ToOutput       = _cmsGetFormatter(ContextID, *OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_16BITS).Fmt16;
    624                 p ->FromInputFloat = _cmsGetFormatter(ContextID, *InputFormat,  cmsFormatterInput, CMS_PACK_FLAGS_FLOAT).FmtFloat;
    625                 p ->ToOutputFloat  = _cmsGetFormatter(ContextID, *OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_FLOAT).FmtFloat;
    626 
    627                 return p;
    628             }
    629     }
    630 
    631     // Not suitable for the transform plug-in, let's check  the pipeline plug-in
    632     if (p ->Lut != NULL)
    633         _cmsOptimizePipeline(ContextID, &p->Lut, Intent, InputFormat, OutputFormat, dwFlags);
    634 
    635     // Check whatever this is a true floating point transform
    636     if (_cmsFormatterIsFloat(*InputFormat) && _cmsFormatterIsFloat(*OutputFormat)) {
    637 
    638         // Get formatter function always return a valid union, but the contents of this union may be NULL.
    639         p ->FromInputFloat = _cmsGetFormatter(ContextID, *InputFormat,  cmsFormatterInput, CMS_PACK_FLAGS_FLOAT).FmtFloat;
    640         p ->ToOutputFloat  = _cmsGetFormatter(ContextID, *OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_FLOAT).FmtFloat;
    641         *dwFlags |= cmsFLAGS_CAN_CHANGE_FORMATTER;
    642 
    643         if (p ->FromInputFloat == NULL || p ->ToOutputFloat == NULL) {
    644 
    645             cmsSignalError(ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported raster format");
    646             _cmsFree(ContextID, p);
    647             return NULL;
    648         }
    649 
    650         if (*dwFlags & cmsFLAGS_NULLTRANSFORM) {
    651 
    652             p ->xform = NullFloatXFORM;
    653         }
    654         else {
    655             // Float transforms don't use cach? always are non-NULL
    656             p ->xform = FloatXFORM;
    657         }
    658 
    659     }
    660     else {
    661 
    662         if (*InputFormat == 0 && *OutputFormat == 0) {
    663             p ->FromInput = p ->ToOutput = NULL;
    664             *dwFlags |= cmsFLAGS_CAN_CHANGE_FORMATTER;
    665         }
    666         else {
    667 
    668             int BytesPerPixelInput;
    669 
    670             p ->FromInput = _cmsGetFormatter(ContextID, *InputFormat,  cmsFormatterInput, CMS_PACK_FLAGS_16BITS).Fmt16;
    671             p ->ToOutput  = _cmsGetFormatter(ContextID, *OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_16BITS).Fmt16;
    672 
    673             if (p ->FromInput == NULL || p ->ToOutput == NULL) {
    674 
    675                 cmsSignalError(ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported raster format");
    676                 _cmsFree(ContextID, p);
    677                 return NULL;
    678             }
    679 
    680             BytesPerPixelInput = T_BYTES(p ->InputFormat);
    681             if (BytesPerPixelInput == 0 || BytesPerPixelInput >= 2)
    682                    *dwFlags |= cmsFLAGS_CAN_CHANGE_FORMATTER;
    683 
    684         }
    685 
    686         if (*dwFlags & cmsFLAGS_NULLTRANSFORM) {
    687 
    688             p ->xform = NullXFORM;
    689         }
    690         else {
    691             if (*dwFlags & cmsFLAGS_NOCACHE) {
    692 
    693                 if (*dwFlags & cmsFLAGS_GAMUTCHECK)
    694                     p ->xform = PrecalculatedXFORMGamutCheck;  // Gamut check, no cach?                else
    695                     p ->xform = PrecalculatedXFORM;  // No cach? no gamut check
    696             }
    697             else {
    698 
    699                 if (*dwFlags & cmsFLAGS_GAMUTCHECK)
    700                     p ->xform = CachedXFORMGamutCheck;    // Gamut check, cach?                else
    701                     p ->xform = CachedXFORM;  // No gamut check, cach?
    702             }
    703         }
    704     }
    705 
    706     p ->InputFormat     = *InputFormat;
    707     p ->OutputFormat    = *OutputFormat;
    708     p ->dwOriginalFlags = *dwFlags;
    709     p ->ContextID       = ContextID;
    710     p ->UserData        = NULL;
    711     return p;
    712 }
    713 
    714 static
    715 cmsBool GetXFormColorSpaces(int nProfiles, cmsHPROFILE hProfiles[], cmsColorSpaceSignature* Input, cmsColorSpaceSignature* Output)
    716 {
    717     cmsColorSpaceSignature ColorSpaceIn, ColorSpaceOut;
    718     cmsColorSpaceSignature PostColorSpace;
    719     int i;
    720 
    721     if (nProfiles <= 0) return FALSE;
    722     if (hProfiles[0] == NULL) return FALSE;
    723 
    724     *Input = PostColorSpace = cmsGetColorSpace(hProfiles[0]);
    725 
    726     for (i=0; i < nProfiles; i++) {
    727 
    728         cmsProfileClassSignature cls;
    729         cmsHPROFILE hProfile = hProfiles[i];
    730 
    731         int lIsInput = (PostColorSpace != cmsSigXYZData) &&
    732                        (PostColorSpace != cmsSigLabData);
    733 
    734         if (hProfile == NULL) return FALSE;
    735 
    736         cls = cmsGetDeviceClass(hProfile);
    737 
    738         if (cls == cmsSigNamedColorClass) {
    739 
    740             ColorSpaceIn    = cmsSig1colorData;
    741             ColorSpaceOut   = (nProfiles > 1) ? cmsGetPCS(hProfile) : cmsGetColorSpace(hProfile);
    742         }
    743         else
    744         if (lIsInput || (cls == cmsSigLinkClass)) {
    745 
    746             ColorSpaceIn    = cmsGetColorSpace(hProfile);
    747             ColorSpaceOut   = cmsGetPCS(hProfile);
    748         }
    749         else
    750         {
    751             ColorSpaceIn    = cmsGetPCS(hProfile);
    752             ColorSpaceOut   = cmsGetColorSpace(hProfile);
    753         }
    754 
    755         if (i==0)
    756             *Input = ColorSpaceIn;
    757 
    758         PostColorSpace = ColorSpaceOut;
    759     }
    760 
    761     *Output = PostColorSpace;
    762 
    763     return TRUE;
    764 }
    765 
    766 // Check colorspace
    767 static
    768 cmsBool  IsProperColorSpace(cmsColorSpaceSignature Check, cmsUInt32Number dwFormat)
    769 {
    770     int Space1 = T_COLORSPACE(dwFormat);
    771     int Space2 = _cmsLCMScolorSpace(Check);
    772 
    773     if (Space1 == PT_ANY) return TRUE;
    774     if (Space1 == Space2) return TRUE;
    775 
    776     if (Space1 == PT_LabV2 && Space2 == PT_Lab) return TRUE;
    777     if (Space1 == PT_Lab   && Space2 == PT_LabV2) return TRUE;
    778 
    779     return FALSE;
    780 }
    781 
    782 // ----------------------------------------------------------------------------------------------------------------
    783 
    784 static
    785 void SetWhitePoint(cmsCIEXYZ* wtPt, const cmsCIEXYZ* src)
    786 {
    787     if (src == NULL) {
    788         wtPt ->X = cmsD50X;
    789         wtPt ->Y = cmsD50Y;
    790         wtPt ->Z = cmsD50Z;
    791     }
    792     else {
    793         wtPt ->X = src->X;
    794         wtPt ->Y = src->Y;
    795         wtPt ->Z = src->Z;
    796     }
    797 
    798 }
    799 
    800 // New to lcms 2.0 -- have all parameters available.
    801 cmsHTRANSFORM CMSEXPORT cmsCreateExtendedTransform(cmsContext ContextID,
    802                                                    cmsUInt32Number nProfiles, cmsHPROFILE hProfiles[],
    803                                                    cmsBool  BPC[],
    804                                                    cmsUInt32Number Intents[],
    805                                                    cmsFloat64Number AdaptationStates[],
    806                                                    cmsHPROFILE hGamutProfile,
    807                                                    cmsUInt32Number nGamutPCSposition,
    808                                                    cmsUInt32Number InputFormat,
    809                                                    cmsUInt32Number OutputFormat,
    810                                                    cmsUInt32Number dwFlags)
    811 {
    812     _cmsTRANSFORM* xform;
    813     cmsColorSpaceSignature EntryColorSpace;
    814     cmsColorSpaceSignature ExitColorSpace;
    815     cmsPipeline* Lut;
    816     cmsUInt32Number LastIntent = Intents[nProfiles-1];
    817 
    818     // If it is a fake transform
    819     if (dwFlags & cmsFLAGS_NULLTRANSFORM)
    820     {
    821         return AllocEmptyTransform(ContextID, NULL, INTENT_PERCEPTUAL, &InputFormat, &OutputFormat, &dwFlags);
    822     }
    823 
    824     // If gamut check is requested, make sure we have a gamut profile
    825     if (dwFlags & cmsFLAGS_GAMUTCHECK) {
    826         if (hGamutProfile == NULL) dwFlags &= ~cmsFLAGS_GAMUTCHECK;
    827     }
    828 
    829     // On floating point transforms, inhibit cache
    830     if (_cmsFormatterIsFloat(InputFormat) || _cmsFormatterIsFloat(OutputFormat))
    831         dwFlags |= cmsFLAGS_NOCACHE;
    832 
    833     // Mark entry/exit spaces
    834     if (!GetXFormColorSpaces(nProfiles, hProfiles, &EntryColorSpace, &ExitColorSpace)) {
    835         cmsSignalError(ContextID, cmsERROR_NULL, "NULL input profiles on transform");
    836         return NULL;
    837     }
    838 
    839     // Check if proper colorspaces
    840     if (!IsProperColorSpace(EntryColorSpace, InputFormat)) {
    841         cmsSignalError(ContextID, cmsERROR_COLORSPACE_CHECK, "Wrong input color space on transform");
    842         return NULL;
    843     }
    844 
    845     if (!IsProperColorSpace(ExitColorSpace, OutputFormat)) {
    846         cmsSignalError(ContextID, cmsERROR_COLORSPACE_CHECK, "Wrong output color space on transform");
    847         return NULL;
    848     }
    849 
    850     // Create a pipeline with all transformations
    851     Lut = _cmsLinkProfiles(ContextID, nProfiles, Intents, hProfiles, BPC, AdaptationStates, dwFlags);
    852     if (Lut == NULL) {
    853         cmsSignalError(ContextID, cmsERROR_NOT_SUITABLE, "Couldn't link the profiles");
    854         return NULL;
    855     }
    856 
    857     // Check channel count
    858     if ((cmsChannelsOf(EntryColorSpace) != cmsPipelineInputChannels(Lut)) ||
    859         (cmsChannelsOf(ExitColorSpace)  != cmsPipelineOutputChannels(Lut))) {
    860         cmsPipelineFree(Lut);
    861         cmsSignalError(ContextID, cmsERROR_NOT_SUITABLE, "Channel count doesn't match. Profile is corrupted");
    862         return NULL;
    863     }
    864 
    865 
    866     // All seems ok
    867     xform = AllocEmptyTransform(ContextID, Lut, LastIntent, &InputFormat, &OutputFormat, &dwFlags);
    868     if (xform == NULL) {
    869         return NULL;
    870     }
    871 
    872     // Keep values
    873     xform ->EntryColorSpace = EntryColorSpace;
    874     xform ->ExitColorSpace  = ExitColorSpace;
    875     xform ->RenderingIntent = Intents[nProfiles-1];
    876 
    877     // Take white points
    878     SetWhitePoint(&xform->EntryWhitePoint, (cmsCIEXYZ*) cmsReadTag(hProfiles[0], cmsSigMediaWhitePointTag));
    879     SetWhitePoint(&xform->ExitWhitePoint,  (cmsCIEXYZ*) cmsReadTag(hProfiles[nProfiles-1], cmsSigMediaWhitePointTag));
    880 
    881 
    882     // Create a gamut check LUT if requested
    883     if (hGamutProfile != NULL && (dwFlags & cmsFLAGS_GAMUTCHECK))
    884         xform ->GamutCheck  = _cmsCreateGamutCheckPipeline(ContextID, hProfiles,
    885                                                         BPC, Intents,
    886                                                         AdaptationStates,
    887                                                         nGamutPCSposition,
    888                                                         hGamutProfile);
    889 
    890 
    891     // Try to read input and output colorant table
    892     if (cmsIsTag(hProfiles[0], cmsSigColorantTableTag)) {
    893 
    894         // Input table can only come in this way.
    895         xform ->InputColorant = cmsDupNamedColorList((cmsNAMEDCOLORLIST*) cmsReadTag(hProfiles[0], cmsSigColorantTableTag));
    896     }
    897 
    898     // Output is a little bit more complex.
    899     if (cmsGetDeviceClass(hProfiles[nProfiles-1]) == cmsSigLinkClass) {
    900 
    901         // This tag may exist only on devicelink profiles.
    902         if (cmsIsTag(hProfiles[nProfiles-1], cmsSigColorantTableOutTag)) {
    903 
    904             // It may be NULL if error
    905             xform ->OutputColorant = cmsDupNamedColorList((cmsNAMEDCOLORLIST*) cmsReadTag(hProfiles[nProfiles-1], cmsSigColorantTableOutTag));
    906         }
    907 
    908     } else {
    909 
    910         if (cmsIsTag(hProfiles[nProfiles-1], cmsSigColorantTableTag)) {
    911 
    912             xform -> OutputColorant = cmsDupNamedColorList((cmsNAMEDCOLORLIST*) cmsReadTag(hProfiles[nProfiles-1], cmsSigColorantTableTag));
    913         }
    914     }
    915 
    916     // Store the sequence of profiles
    917     if (dwFlags & cmsFLAGS_KEEP_SEQUENCE) {
    918         xform ->Sequence = _cmsCompileProfileSequence(ContextID, nProfiles, hProfiles);
    919     }
    920     else
    921         xform ->Sequence = NULL;
    922 
    923     // If this is a cached transform, init first value, which is zero (16 bits only)
    924     if (!(dwFlags & cmsFLAGS_NOCACHE)) {
    925 
    926         memset(&xform ->Cache.CacheIn, 0, sizeof(xform ->Cache.CacheIn));
    927 
    928         if (xform ->GamutCheck != NULL) {
    929             TransformOnePixelWithGamutCheck(xform, xform ->Cache.CacheIn, xform->Cache.CacheOut);
    930         }
    931         else {
    932 
    933             xform ->Lut ->Eval16Fn(xform ->Cache.CacheIn, xform->Cache.CacheOut, xform -> Lut->Data);
    934         }
    935 
    936     }
    937 
    938     return (cmsHTRANSFORM) xform;
    939 }
    940 
    941 // Multiprofile transforms: Gamut check is not available here, as it is unclear from which profile the gamut comes.
    942 cmsHTRANSFORM CMSEXPORT cmsCreateMultiprofileTransformTHR(cmsContext ContextID,
    943                                                        cmsHPROFILE hProfiles[],
    944                                                        cmsUInt32Number nProfiles,
    945                                                        cmsUInt32Number InputFormat,
    946                                                        cmsUInt32Number OutputFormat,
    947                                                        cmsUInt32Number Intent,
    948                                                        cmsUInt32Number dwFlags)
    949 {
    950     cmsUInt32Number i;
    951     cmsBool BPC[256];
    952     cmsUInt32Number Intents[256];
    953     cmsFloat64Number AdaptationStates[256];
    954 
    955     if (nProfiles <= 0 || nProfiles > 255) {
    956          cmsSignalError(ContextID, cmsERROR_RANGE, "Wrong number of profiles. 1..255 expected, %d found.", nProfiles);
    957         return NULL;
    958     }
    959 
    960     for (i=0; i < nProfiles; i++) {
    961         BPC[i] = dwFlags & cmsFLAGS_BLACKPOINTCOMPENSATION ? TRUE : FALSE;
    962         Intents[i] = Intent;
    963         AdaptationStates[i] = cmsSetAdaptationStateTHR(ContextID, -1);
    964     }
    965 
    966 
    967     return cmsCreateExtendedTransform(ContextID, nProfiles, hProfiles, BPC, Intents, AdaptationStates, NULL, 0, InputFormat, OutputFormat, dwFlags);
    968 }
    969 
    970 
    971 
    972 cmsHTRANSFORM CMSEXPORT cmsCreateMultiprofileTransform(cmsHPROFILE hProfiles[],
    973                                                   cmsUInt32Number nProfiles,
    974                                                   cmsUInt32Number InputFormat,
    975                                                   cmsUInt32Number OutputFormat,
    976                                                   cmsUInt32Number Intent,
    977                                                   cmsUInt32Number dwFlags)
    978 {
    979 
    980     if (nProfiles <= 0 || nProfiles > 255) {
    981          cmsSignalError(NULL, cmsERROR_RANGE, "Wrong number of profiles. 1..255 expected, %d found.", nProfiles);
    982          return NULL;
    983     }
    984 
    985     return cmsCreateMultiprofileTransformTHR(cmsGetProfileContextID(hProfiles[0]),
    986                                                   hProfiles,
    987                                                   nProfiles,
    988                                                   InputFormat,
    989                                                   OutputFormat,
    990                                                   Intent,
    991                                                   dwFlags);
    992 }
    993 
    994 cmsHTRANSFORM CMSEXPORT cmsCreateTransformTHR(cmsContext ContextID,
    995                                               cmsHPROFILE Input,
    996                                               cmsUInt32Number InputFormat,
    997                                               cmsHPROFILE Output,
    998                                               cmsUInt32Number OutputFormat,
    999                                               cmsUInt32Number Intent,
   1000                                               cmsUInt32Number dwFlags)
   1001 {
   1002 
   1003     cmsHPROFILE hArray[2];
   1004 
   1005     hArray[0] = Input;
   1006     hArray[1] = Output;
   1007 
   1008     return cmsCreateMultiprofileTransformTHR(ContextID, hArray, Output == NULL ? 1 : 2, InputFormat, OutputFormat, Intent, dwFlags);
   1009 }
   1010 
   1011 CMSAPI cmsHTRANSFORM CMSEXPORT cmsCreateTransform(cmsHPROFILE Input,
   1012                                                   cmsUInt32Number InputFormat,
   1013                                                   cmsHPROFILE Output,
   1014                                                   cmsUInt32Number OutputFormat,
   1015                                                   cmsUInt32Number Intent,
   1016                                                   cmsUInt32Number dwFlags)
   1017 {
   1018     return cmsCreateTransformTHR(cmsGetProfileContextID(Input), Input, InputFormat, Output, OutputFormat, Intent, dwFlags);
   1019 }
   1020 
   1021 
   1022 cmsHTRANSFORM CMSEXPORT cmsCreateProofingTransformTHR(cmsContext ContextID,
   1023                                                    cmsHPROFILE InputProfile,
   1024                                                    cmsUInt32Number InputFormat,
   1025                                                    cmsHPROFILE OutputProfile,
   1026                                                    cmsUInt32Number OutputFormat,
   1027                                                    cmsHPROFILE ProofingProfile,
   1028                                                    cmsUInt32Number nIntent,
   1029                                                    cmsUInt32Number ProofingIntent,
   1030                                                    cmsUInt32Number dwFlags)
   1031 {
   1032     cmsHPROFILE hArray[4];
   1033     cmsUInt32Number Intents[4];
   1034     cmsBool  BPC[4];
   1035     cmsFloat64Number Adaptation[4];
   1036     cmsBool  DoBPC = (dwFlags & cmsFLAGS_BLACKPOINTCOMPENSATION) ? TRUE : FALSE;
   1037 
   1038 
   1039     hArray[0]  = InputProfile; hArray[1] = ProofingProfile; hArray[2]  = ProofingProfile;               hArray[3] = OutputProfile;
   1040     Intents[0] = nIntent;      Intents[1] = nIntent;        Intents[2] = INTENT_RELATIVE_COLORIMETRIC;  Intents[3] = ProofingIntent;
   1041     BPC[0]     = DoBPC;        BPC[1] = DoBPC;              BPC[2] = 0;                                 BPC[3] = 0;
   1042 
   1043     Adaptation[0] = Adaptation[1] = Adaptation[2] = Adaptation[3] = cmsSetAdaptationStateTHR(ContextID, -1);
   1044 
   1045     if (!(dwFlags & (cmsFLAGS_SOFTPROOFING|cmsFLAGS_GAMUTCHECK)))
   1046         return cmsCreateTransformTHR(ContextID, InputProfile, InputFormat, OutputProfile, OutputFormat, nIntent, dwFlags);
   1047 
   1048     return cmsCreateExtendedTransform(ContextID, 4, hArray, BPC, Intents, Adaptation,
   1049                                         ProofingProfile, 1, InputFormat, OutputFormat, dwFlags);
   1050 
   1051 }
   1052 
   1053 
   1054 cmsHTRANSFORM CMSEXPORT cmsCreateProofingTransform(cmsHPROFILE InputProfile,
   1055                                                    cmsUInt32Number InputFormat,
   1056                                                    cmsHPROFILE OutputProfile,
   1057                                                    cmsUInt32Number OutputFormat,
   1058                                                    cmsHPROFILE ProofingProfile,
   1059                                                    cmsUInt32Number nIntent,
   1060                                                    cmsUInt32Number ProofingIntent,
   1061                                                    cmsUInt32Number dwFlags)
   1062 {
   1063     return cmsCreateProofingTransformTHR(cmsGetProfileContextID(InputProfile),
   1064                                                    InputProfile,
   1065                                                    InputFormat,
   1066                                                    OutputProfile,
   1067                                                    OutputFormat,
   1068                                                    ProofingProfile,
   1069                                                    nIntent,
   1070                                                    ProofingIntent,
   1071                                                    dwFlags);
   1072 }
   1073 
   1074 
   1075 // Grab the ContextID from an open transform. Returns NULL if a NULL transform is passed
   1076 cmsContext CMSEXPORT cmsGetTransformContextID(cmsHTRANSFORM hTransform)
   1077 {
   1078     _cmsTRANSFORM* xform = (_cmsTRANSFORM*) hTransform;
   1079 
   1080     if (xform == NULL) return NULL;
   1081     return xform -> ContextID;
   1082 }
   1083 
   1084 // Grab the input/output formats
   1085 cmsUInt32Number CMSEXPORT cmsGetTransformInputFormat(cmsHTRANSFORM hTransform)
   1086 {
   1087     _cmsTRANSFORM* xform = (_cmsTRANSFORM*) hTransform;
   1088 
   1089     if (xform == NULL) return 0;
   1090     return xform->InputFormat;
   1091 }
   1092 
   1093 cmsUInt32Number CMSEXPORT cmsGetTransformOutputFormat(cmsHTRANSFORM hTransform)
   1094 {
   1095     _cmsTRANSFORM* xform = (_cmsTRANSFORM*) hTransform;
   1096 
   1097     if (xform == NULL) return 0;
   1098     return xform->OutputFormat;
   1099 }
   1100 
   1101 // For backwards compatibility
   1102 cmsBool CMSEXPORT cmsChangeBuffersFormat(cmsHTRANSFORM hTransform,
   1103                                          cmsUInt32Number InputFormat,
   1104                                          cmsUInt32Number OutputFormat)
   1105 {
   1106 
   1107     _cmsTRANSFORM* xform = (_cmsTRANSFORM*) hTransform;
   1108     cmsFormatter16 FromInput, ToOutput;
   1109 
   1110 
   1111     // We only can afford to change formatters if previous transform is at least 16 bits
   1112     if (!(xform ->dwOriginalFlags & cmsFLAGS_CAN_CHANGE_FORMATTER)) {
   1113 
   1114         cmsSignalError(xform ->ContextID, cmsERROR_NOT_SUITABLE, "cmsChangeBuffersFormat works only on transforms created originally with at least 16 bits of precision");
   1115         return FALSE;
   1116     }
   1117 
   1118     FromInput = _cmsGetFormatter(xform->ContextID, InputFormat,  cmsFormatterInput, CMS_PACK_FLAGS_16BITS).Fmt16;
   1119     ToOutput  = _cmsGetFormatter(xform->ContextID, OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_16BITS).Fmt16;
   1120 
   1121     if (FromInput == NULL || ToOutput == NULL) {
   1122 
   1123         cmsSignalError(xform -> ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported raster format");
   1124         return FALSE;
   1125     }
   1126 
   1127     xform ->InputFormat  = InputFormat;
   1128     xform ->OutputFormat = OutputFormat;
   1129     xform ->FromInput    = FromInput;
   1130     xform ->ToOutput     = ToOutput;
   1131     return TRUE;
   1132 }
   1133