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