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