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