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-2023 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(fOut));
 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 check 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         cmsUInt32Number 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 // returns original flags
 807 cmsUInt32Number CMSEXPORT _cmsGetTransformFlags(struct _cmstransform_struct* CMMcargo)
 808 {
 809     _cmsAssert(CMMcargo != NULL);
 810     return CMMcargo->dwOriginalFlags;
 811 }
 812 
 813 // Returns the worker callback for parallelization plug-ins
 814 _cmsTransform2Fn CMSEXPORT _cmsGetTransformWorker(struct _cmstransform_struct* CMMcargo)
 815 {
 816     _cmsAssert(CMMcargo != NULL);
 817     return CMMcargo->Worker;
 818 }
 819 
 820 // This field holds maximum number of workers or -1 to auto
 821 cmsInt32Number CMSEXPORT _cmsGetTransformMaxWorkers(struct _cmstransform_struct* CMMcargo)
 822 {
 823     _cmsAssert(CMMcargo != NULL);
 824     return CMMcargo->MaxWorkers;
 825 }
 826 
 827 // This field is actually unused and reserved
 828 cmsUInt32Number CMSEXPORT _cmsGetTransformWorkerFlags(struct _cmstransform_struct* CMMcargo)
 829 {
 830     _cmsAssert(CMMcargo != NULL);
 831     return CMMcargo->WorkerFlags;
 832 }
 833 
 834 // In the case there is a parallelization plug-in, let it to do its job
 835 static
 836 void ParalellizeIfSuitable(_cmsTRANSFORM* p)
 837 {
 838     _cmsParallelizationPluginChunkType* ctx = (_cmsParallelizationPluginChunkType*)_cmsContextGetClientChunk(p->ContextID, ParallelizationPlugin);
 839 
 840     _cmsAssert(p != NULL);
 841     if (ctx != NULL && ctx->SchedulerFn != NULL) {
 842 
 843         p->Worker = p->xform;
 844         p->xform = ctx->SchedulerFn;
 845         p->MaxWorkers = ctx->MaxWorkers;
 846         p->WorkerFlags = ctx->WorkerFlags;
 847     }
 848 }
 849 
 850 
 851 /**
 852 * An empty unroll to avoid a check with NULL on cmsDoTransform()
 853 */
 854 static
 855 cmsUInt8Number* UnrollNothing(CMSREGISTER _cmsTRANSFORM* info,
 856                               CMSREGISTER cmsUInt16Number wIn[],
 857                               CMSREGISTER cmsUInt8Number* accum,
 858                               CMSREGISTER cmsUInt32Number Stride)
 859 {
 860     return accum;
 861 
 862     cmsUNUSED_PARAMETER(info);
 863     cmsUNUSED_PARAMETER(wIn);
 864     cmsUNUSED_PARAMETER(Stride);
 865 }
 866 
 867 static
 868 cmsUInt8Number* PackNothing(CMSREGISTER _cmsTRANSFORM* info,
 869                            CMSREGISTER cmsUInt16Number wOut[],
 870                            CMSREGISTER cmsUInt8Number* output,
 871                            CMSREGISTER cmsUInt32Number Stride)
 872 {
 873     return output;
 874 
 875     cmsUNUSED_PARAMETER(info);
 876     cmsUNUSED_PARAMETER(wOut);
 877     cmsUNUSED_PARAMETER(Stride);
 878 }
 879 
 880 // Allocate transform struct and set it to defaults. Ask the optimization plug-in about if those formats are proper
 881 // for separated transforms. If this is the case,
 882 static
 883 _cmsTRANSFORM* AllocEmptyTransform(cmsContext ContextID, cmsPipeline* lut,
 884                                                cmsUInt32Number Intent, cmsUInt32Number* InputFormat, cmsUInt32Number* OutputFormat, cmsUInt32Number* dwFlags)
 885 {
 886      _cmsTransformPluginChunkType* ctx = ( _cmsTransformPluginChunkType*) _cmsContextGetClientChunk(ContextID, TransformPlugin);
 887      _cmsTransformCollection* Plugin;
 888 
 889        // Allocate needed memory
 890        _cmsTRANSFORM* p = (_cmsTRANSFORM*)_cmsMallocZero(ContextID, sizeof(_cmsTRANSFORM));
 891        if (!p) {
 892               cmsPipelineFree(lut);
 893               return NULL;
 894        }
 895 
 896        // Store the proposed pipeline
 897        p->Lut = lut;
 898 
 899        // Let's see if any plug-in want to do the transform by itself
 900        if (p->Lut != NULL) {
 901 
 902            if (!(*dwFlags & cmsFLAGS_NOOPTIMIZE))
 903            {
 904                for (Plugin = ctx->TransformCollection;
 905                    Plugin != NULL;
 906                    Plugin = Plugin->Next) {
 907 
 908                    if (Plugin->Factory(&p->xform, &p->UserData, &p->FreeUserData, &p->Lut, InputFormat, OutputFormat, dwFlags)) {
 909 
 910                        // Last plugin in the declaration order takes control. We just keep
 911                        // the original parameters as a logging.
 912                        // Note that cmsFLAGS_CAN_CHANGE_FORMATTER is not set, so by default
 913                        // an optimized transform is not reusable. The plug-in can, however, change
 914                        // the flags and make it suitable.
 915 
 916                        p->ContextID = ContextID;
 917                        p->InputFormat = *InputFormat;
 918                        p->OutputFormat = *OutputFormat;
 919                        p->dwOriginalFlags = *dwFlags;
 920 
 921                        // Fill the formatters just in case the optimized routine is interested.
 922                        // No error is thrown if the formatter doesn't exist. It is up to the optimization
 923                        // factory to decide what to do in those cases.
 924                        p->FromInput = _cmsGetFormatter(ContextID, *InputFormat, cmsFormatterInput, CMS_PACK_FLAGS_16BITS).Fmt16;
 925                        p->ToOutput = _cmsGetFormatter(ContextID, *OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_16BITS).Fmt16;
 926                        p->FromInputFloat = _cmsGetFormatter(ContextID, *InputFormat, cmsFormatterInput, CMS_PACK_FLAGS_FLOAT).FmtFloat;
 927                        p->ToOutputFloat = _cmsGetFormatter(ContextID, *OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_FLOAT).FmtFloat;
 928 
 929                        // Save the day? (Ignore the warning)
 930                        if (Plugin->OldXform) {
 931                            p->OldXform = (_cmsTransformFn)(void*)p->xform;
 932                            p->xform = _cmsTransform2toTransformAdaptor;
 933                        }
 934 
 935                        ParalellizeIfSuitable(p);
 936                        return p;
 937                    }
 938                }
 939            }
 940 
 941            // Not suitable for the transform plug-in, let's check  the pipeline plug-in
 942            _cmsOptimizePipeline(ContextID, &p->Lut, Intent, InputFormat, OutputFormat, dwFlags);
 943        }
 944 
 945     // Check whatever this is a true floating point transform
 946     if (_cmsFormatterIsFloat(*InputFormat) || _cmsFormatterIsFloat(*OutputFormat)) {
 947 
 948         // Get formatter function always return a valid union, but the contents of this union may be NULL.
 949         p ->FromInputFloat = _cmsGetFormatter(ContextID, *InputFormat,  cmsFormatterInput, CMS_PACK_FLAGS_FLOAT).FmtFloat;
 950         p ->ToOutputFloat  = _cmsGetFormatter(ContextID, *OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_FLOAT).FmtFloat;
 951         *dwFlags |= cmsFLAGS_CAN_CHANGE_FORMATTER;
 952 
 953         if (p ->FromInputFloat == NULL || p ->ToOutputFloat == NULL) {
 954 
 955             cmsSignalError(ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported raster format");
 956             cmsDeleteTransform(p);
 957             return NULL;
 958         }
 959 
 960         if (*dwFlags & cmsFLAGS_NULLTRANSFORM) {
 961 
 962             p ->xform = NullFloatXFORM;
 963         }
 964         else {
 965             // Float transforms don't use cache, always are non-NULL
 966             p ->xform = FloatXFORM;
 967         }
 968 
 969     }
 970     else {
 971 
 972         // Formats are intended to be changed before use
 973         if (*InputFormat == 0 && *OutputFormat == 0) {
 974             p->FromInput = UnrollNothing;
 975             p->ToOutput = PackNothing;
 976             *dwFlags |= cmsFLAGS_CAN_CHANGE_FORMATTER;
 977         }
 978         else {
 979 
 980             cmsUInt32Number BytesPerPixelInput;
 981 
 982             p ->FromInput = _cmsGetFormatter(ContextID, *InputFormat,  cmsFormatterInput, CMS_PACK_FLAGS_16BITS).Fmt16;
 983             p ->ToOutput  = _cmsGetFormatter(ContextID, *OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_16BITS).Fmt16;
 984 
 985             if (p ->FromInput == NULL || p ->ToOutput == NULL) {
 986 
 987                 cmsSignalError(ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported raster format");
 988                 cmsDeleteTransform(p);
 989                 return NULL;
 990             }
 991 
 992             BytesPerPixelInput = T_BYTES(*InputFormat);
 993             if (BytesPerPixelInput == 0 || BytesPerPixelInput >= 2)
 994                    *dwFlags |= cmsFLAGS_CAN_CHANGE_FORMATTER;
 995 
 996         }
 997 
 998         if (*dwFlags & cmsFLAGS_NULLTRANSFORM) {
 999 
1000             p ->xform = NullXFORM;
1001         }
1002         else {
1003             if (*dwFlags & cmsFLAGS_NOCACHE) {
1004 
1005                 if (*dwFlags & cmsFLAGS_GAMUTCHECK)
1006                     p ->xform = PrecalculatedXFORMGamutCheck;  // Gamut check, no cache
1007                 else
1008                     p ->xform = PrecalculatedXFORM;  // No cache, no gamut check
1009             }
1010             else {
1011 
1012                 if (*dwFlags & cmsFLAGS_GAMUTCHECK)
1013                     p ->xform = CachedXFORMGamutCheck;    // Gamut check, cache
1014                 else
1015                     p ->xform = CachedXFORM;  // No gamut check, cache
1016 
1017             }
1018         }
1019     }
1020 
1021     /**
1022     * Check consistency for alpha channel copy
1023     */
1024     if (*dwFlags & cmsFLAGS_COPY_ALPHA)
1025     {
1026         if (T_EXTRA(*InputFormat) != T_EXTRA(*OutputFormat))
1027         {
1028             cmsSignalError(ContextID, cmsERROR_NOT_SUITABLE, "Mismatched alpha channels");
1029             cmsDeleteTransform(p);
1030             return NULL;
1031         }
1032     }
1033 
1034     p ->InputFormat     = *InputFormat;
1035     p ->OutputFormat    = *OutputFormat;
1036     p ->dwOriginalFlags = *dwFlags;
1037     p ->ContextID       = ContextID;
1038     p ->UserData        = NULL;
1039     ParalellizeIfSuitable(p);
1040     return p;
1041 }
1042 
1043 static
1044 cmsBool GetXFormColorSpaces(cmsUInt32Number nProfiles, cmsHPROFILE hProfiles[], cmsColorSpaceSignature* Input, cmsColorSpaceSignature* Output)
1045 {
1046     cmsColorSpaceSignature ColorSpaceIn, ColorSpaceOut;
1047     cmsColorSpaceSignature PostColorSpace;
1048     cmsUInt32Number i;
1049 
1050     if (nProfiles == 0) return FALSE;
1051     if (hProfiles[0] == NULL) return FALSE;
1052 
1053     *Input = PostColorSpace = cmsGetColorSpace(hProfiles[0]);
1054 
1055     for (i=0; i < nProfiles; i++) {
1056 
1057         cmsProfileClassSignature cls;
1058         cmsHPROFILE hProfile = hProfiles[i];
1059 
1060         int lIsInput = (PostColorSpace != cmsSigXYZData) &&
1061                        (PostColorSpace != cmsSigLabData);
1062 
1063         if (hProfile == NULL) return FALSE;
1064 
1065         cls = cmsGetDeviceClass(hProfile);
1066 
1067         if (cls == cmsSigNamedColorClass) {
1068 
1069             ColorSpaceIn    = cmsSig1colorData;
1070             ColorSpaceOut   = (nProfiles > 1) ? cmsGetPCS(hProfile) : cmsGetColorSpace(hProfile);
1071         }
1072         else
1073         if (lIsInput || (cls == cmsSigLinkClass)) {
1074 
1075             ColorSpaceIn    = cmsGetColorSpace(hProfile);
1076             ColorSpaceOut   = cmsGetPCS(hProfile);
1077         }
1078         else
1079         {
1080             ColorSpaceIn    = cmsGetPCS(hProfile);
1081             ColorSpaceOut   = cmsGetColorSpace(hProfile);
1082         }
1083 
1084         if (i==0)
1085             *Input = ColorSpaceIn;
1086 
1087         PostColorSpace = ColorSpaceOut;
1088     }
1089 
1090     *Output = PostColorSpace;
1091 
1092     return TRUE;
1093 }
1094 
1095 // Check colorspace
1096 static
1097 cmsBool  IsProperColorSpace(cmsColorSpaceSignature Check, cmsUInt32Number dwFormat)
1098 {
1099     int Space1 = (int) T_COLORSPACE(dwFormat);
1100     int Space2 = _cmsLCMScolorSpace(Check);
1101 
1102     if (Space1 == PT_ANY) return TRUE;
1103     if (Space1 == Space2) return TRUE;
1104 
1105     if (Space1 == PT_LabV2 && Space2 == PT_Lab) return TRUE;
1106     if (Space1 == PT_Lab   && Space2 == PT_LabV2) return TRUE;
1107 
1108     return FALSE;
1109 }
1110 
1111 // ----------------------------------------------------------------------------------------------------------------
1112 
1113 // Jun-21-2000: Some profiles (those that comes with W2K) comes
1114 // with the media white (media black?) x 100. Add a sanity check
1115 
1116 static
1117 void NormalizeXYZ(cmsCIEXYZ* Dest)
1118 {
1119     while (Dest -> X > 2. &&
1120            Dest -> Y > 2. &&
1121            Dest -> Z > 2.) {
1122 
1123                Dest -> X /= 10.;
1124                Dest -> Y /= 10.;
1125                Dest -> Z /= 10.;
1126        }
1127 }
1128 
1129 static
1130 void SetWhitePoint(cmsCIEXYZ* wtPt, const cmsCIEXYZ* src)
1131 {
1132     if (src == NULL) {
1133         wtPt ->X = cmsD50X;
1134         wtPt ->Y = cmsD50Y;
1135         wtPt ->Z = cmsD50Z;
1136     }
1137     else {
1138         wtPt ->X = src->X;
1139         wtPt ->Y = src->Y;
1140         wtPt ->Z = src->Z;
1141 
1142         NormalizeXYZ(wtPt);
1143     }
1144 
1145 }
1146 
1147 // New to lcms 2.0 -- have all parameters available.
1148 cmsHTRANSFORM CMSEXPORT cmsCreateExtendedTransform(cmsContext ContextID,
1149                                                    cmsUInt32Number nProfiles, cmsHPROFILE hProfiles[],
1150                                                    cmsBool  BPC[],
1151                                                    cmsUInt32Number Intents[],
1152                                                    cmsFloat64Number AdaptationStates[],
1153                                                    cmsHPROFILE hGamutProfile,
1154                                                    cmsUInt32Number nGamutPCSposition,
1155                                                    cmsUInt32Number InputFormat,
1156                                                    cmsUInt32Number OutputFormat,
1157                                                    cmsUInt32Number dwFlags)
1158 {
1159     _cmsTRANSFORM* xform;
1160     cmsColorSpaceSignature EntryColorSpace;
1161     cmsColorSpaceSignature ExitColorSpace;
1162     cmsPipeline* Lut;
1163     cmsUInt32Number LastIntent = Intents[nProfiles-1];
1164 
1165     // If it is a fake transform
1166     if (dwFlags & cmsFLAGS_NULLTRANSFORM)
1167     {
1168         return AllocEmptyTransform(ContextID, NULL, INTENT_PERCEPTUAL, &InputFormat, &OutputFormat, &dwFlags);
1169     }
1170 
1171     // If gamut check is requested, make sure we have a gamut profile
1172     if (dwFlags & cmsFLAGS_GAMUTCHECK) {
1173         if (hGamutProfile == NULL) dwFlags &= ~cmsFLAGS_GAMUTCHECK;
1174     }
1175 
1176     // On floating point transforms, inhibit cache
1177     if (_cmsFormatterIsFloat(InputFormat) || _cmsFormatterIsFloat(OutputFormat))
1178         dwFlags |= cmsFLAGS_NOCACHE;
1179 
1180     // Mark entry/exit spaces
1181     if (!GetXFormColorSpaces(nProfiles, hProfiles, &EntryColorSpace, &ExitColorSpace)) {
1182         cmsSignalError(ContextID, cmsERROR_NULL, "NULL input profiles on transform");
1183         return NULL;
1184     }
1185 
1186     // Check if proper colorspaces
1187     if (!IsProperColorSpace(EntryColorSpace, InputFormat)) {
1188         cmsSignalError(ContextID, cmsERROR_COLORSPACE_CHECK, "Wrong input color space on transform");
1189         return NULL;
1190     }
1191 
1192     if (!IsProperColorSpace(ExitColorSpace, OutputFormat)) {
1193         cmsSignalError(ContextID, cmsERROR_COLORSPACE_CHECK, "Wrong output color space on transform");
1194         return NULL;
1195     }
1196 
1197     // Check whatever the transform is 16 bits and involves linear RGB in first profile. If so, disable optimizations
1198     if (EntryColorSpace == cmsSigRgbData && T_BYTES(InputFormat) == 2 && !(dwFlags & cmsFLAGS_NOOPTIMIZE))
1199     {
1200         cmsFloat64Number gamma = cmsDetectRGBProfileGamma(hProfiles[0], 0.1);
1201 
1202         if (gamma > 0 && gamma < 1.6)
1203             dwFlags |= cmsFLAGS_NOOPTIMIZE;
1204     }
1205 
1206     // Create a pipeline with all transformations
1207     Lut = _cmsLinkProfiles(ContextID, nProfiles, Intents, hProfiles, BPC, AdaptationStates, dwFlags);
1208     if (Lut == NULL) {
1209         cmsSignalError(ContextID, cmsERROR_NOT_SUITABLE, "Couldn't link the profiles");
1210         return NULL;
1211     }
1212 
1213     // Check channel count
1214     if ((cmsChannelsOfColorSpace(EntryColorSpace) != (cmsInt32Number) cmsPipelineInputChannels(Lut)) ||
1215         (cmsChannelsOfColorSpace(ExitColorSpace)  != (cmsInt32Number) cmsPipelineOutputChannels(Lut))) {
1216         cmsPipelineFree(Lut);
1217         cmsSignalError(ContextID, cmsERROR_NOT_SUITABLE, "Channel count doesn't match. Profile is corrupted");
1218         return NULL;
1219     }
1220 
1221 
1222     // All seems ok
1223     xform = AllocEmptyTransform(ContextID, Lut, LastIntent, &InputFormat, &OutputFormat, &dwFlags);
1224     if (xform == NULL) {
1225         return NULL;
1226     }
1227 
1228     // Keep values
1229     xform ->EntryColorSpace = EntryColorSpace;
1230     xform ->ExitColorSpace  = ExitColorSpace;
1231     xform ->RenderingIntent = Intents[nProfiles-1];
1232 
1233     // Take white points
1234     SetWhitePoint(&xform->EntryWhitePoint, (cmsCIEXYZ*) cmsReadTag(hProfiles[0], cmsSigMediaWhitePointTag));
1235     SetWhitePoint(&xform->ExitWhitePoint,  (cmsCIEXYZ*) cmsReadTag(hProfiles[nProfiles-1], cmsSigMediaWhitePointTag));
1236 
1237 
1238     // Create a gamut check LUT if requested
1239     if (hGamutProfile != NULL && (dwFlags & cmsFLAGS_GAMUTCHECK))
1240         xform ->GamutCheck  = _cmsCreateGamutCheckPipeline(ContextID, hProfiles,
1241                                                         BPC, Intents,
1242                                                         AdaptationStates,
1243                                                         nGamutPCSposition,
1244                                                         hGamutProfile);
1245 
1246 
1247     // Try to read input and output colorant table
1248     if (cmsIsTag(hProfiles[0], cmsSigColorantTableTag)) {
1249 
1250         // Input table can only come in this way.
1251         xform ->InputColorant = cmsDupNamedColorList((cmsNAMEDCOLORLIST*) cmsReadTag(hProfiles[0], cmsSigColorantTableTag));
1252     }
1253 
1254     // Output is a little bit more complex.
1255     if (cmsGetDeviceClass(hProfiles[nProfiles-1]) == cmsSigLinkClass) {
1256 
1257         // This tag may exist only on devicelink profiles.
1258         if (cmsIsTag(hProfiles[nProfiles-1], cmsSigColorantTableOutTag)) {
1259 
1260             // It may be NULL if error
1261             xform ->OutputColorant = cmsDupNamedColorList((cmsNAMEDCOLORLIST*) cmsReadTag(hProfiles[nProfiles-1], cmsSigColorantTableOutTag));
1262         }
1263 
1264     } else {
1265 
1266         if (cmsIsTag(hProfiles[nProfiles-1], cmsSigColorantTableTag)) {
1267 
1268             xform -> OutputColorant = cmsDupNamedColorList((cmsNAMEDCOLORLIST*) cmsReadTag(hProfiles[nProfiles-1], cmsSigColorantTableTag));
1269         }
1270     }
1271 
1272     // Store the sequence of profiles
1273     if (dwFlags & cmsFLAGS_KEEP_SEQUENCE) {
1274         xform ->Sequence = _cmsCompileProfileSequence(ContextID, nProfiles, hProfiles);
1275     }
1276     else
1277         xform ->Sequence = NULL;
1278 
1279     // If this is a cached transform, init first value, which is zero (16 bits only)
1280     if (!(dwFlags & cmsFLAGS_NOCACHE)) {
1281 
1282         memset(&xform ->Cache.CacheIn, 0, sizeof(xform ->Cache.CacheIn));
1283 
1284         if (xform ->GamutCheck != NULL) {
1285             TransformOnePixelWithGamutCheck(xform, xform ->Cache.CacheIn, xform->Cache.CacheOut);
1286         }
1287         else {
1288 
1289             xform ->Lut ->Eval16Fn(xform ->Cache.CacheIn, xform->Cache.CacheOut, xform -> Lut->Data);
1290         }
1291 
1292     }
1293 
1294     return (cmsHTRANSFORM) xform;
1295 }
1296 
1297 // Multiprofile transforms: Gamut check is not available here, as it is unclear from which profile the gamut comes.
1298 cmsHTRANSFORM CMSEXPORT cmsCreateMultiprofileTransformTHR(cmsContext ContextID,
1299                                                        cmsHPROFILE hProfiles[],
1300                                                        cmsUInt32Number nProfiles,
1301                                                        cmsUInt32Number InputFormat,
1302                                                        cmsUInt32Number OutputFormat,
1303                                                        cmsUInt32Number Intent,
1304                                                        cmsUInt32Number dwFlags)
1305 {
1306     cmsUInt32Number i;
1307     cmsBool BPC[256];
1308     cmsUInt32Number Intents[256];
1309     cmsFloat64Number AdaptationStates[256];
1310 
1311     if (nProfiles <= 0 || nProfiles > 255) {
1312          cmsSignalError(ContextID, cmsERROR_RANGE, "Wrong number of profiles. 1..255 expected, %d found.", nProfiles);
1313         return NULL;
1314     }
1315 
1316     for (i=0; i < nProfiles; i++) {
1317         BPC[i] = dwFlags & cmsFLAGS_BLACKPOINTCOMPENSATION ? TRUE : FALSE;
1318         Intents[i] = Intent;
1319         AdaptationStates[i] = cmsSetAdaptationStateTHR(ContextID, -1);
1320     }
1321 
1322 
1323     return cmsCreateExtendedTransform(ContextID, nProfiles, hProfiles, BPC, Intents, AdaptationStates, NULL, 0, InputFormat, OutputFormat, dwFlags);
1324 }
1325 
1326 
1327 
1328 cmsHTRANSFORM CMSEXPORT cmsCreateMultiprofileTransform(cmsHPROFILE hProfiles[],
1329                                                   cmsUInt32Number nProfiles,
1330                                                   cmsUInt32Number InputFormat,
1331                                                   cmsUInt32Number OutputFormat,
1332                                                   cmsUInt32Number Intent,
1333                                                   cmsUInt32Number dwFlags)
1334 {
1335 
1336     if (nProfiles <= 0 || nProfiles > 255) {
1337          cmsSignalError(NULL, cmsERROR_RANGE, "Wrong number of profiles. 1..255 expected, %d found.", nProfiles);
1338          return NULL;
1339     }
1340 
1341     return cmsCreateMultiprofileTransformTHR(cmsGetProfileContextID(hProfiles[0]),
1342                                                   hProfiles,
1343                                                   nProfiles,
1344                                                   InputFormat,
1345                                                   OutputFormat,
1346                                                   Intent,
1347                                                   dwFlags);
1348 }
1349 
1350 cmsHTRANSFORM CMSEXPORT cmsCreateTransformTHR(cmsContext ContextID,
1351                                               cmsHPROFILE Input,
1352                                               cmsUInt32Number InputFormat,
1353                                               cmsHPROFILE Output,
1354                                               cmsUInt32Number OutputFormat,
1355                                               cmsUInt32Number Intent,
1356                                               cmsUInt32Number dwFlags)
1357 {
1358 
1359     cmsHPROFILE hArray[2];
1360 
1361     hArray[0] = Input;
1362     hArray[1] = Output;
1363 
1364     return cmsCreateMultiprofileTransformTHR(ContextID, hArray, Output == NULL ? 1U : 2U, InputFormat, OutputFormat, Intent, dwFlags);
1365 }
1366 
1367 CMSAPI cmsHTRANSFORM CMSEXPORT cmsCreateTransform(cmsHPROFILE Input,
1368                                                   cmsUInt32Number InputFormat,
1369                                                   cmsHPROFILE Output,
1370                                                   cmsUInt32Number OutputFormat,
1371                                                   cmsUInt32Number Intent,
1372                                                   cmsUInt32Number dwFlags)
1373 {
1374     return cmsCreateTransformTHR(cmsGetProfileContextID(Input), Input, InputFormat, Output, OutputFormat, Intent, dwFlags);
1375 }
1376 
1377 
1378 cmsHTRANSFORM CMSEXPORT cmsCreateProofingTransformTHR(cmsContext ContextID,
1379                                                    cmsHPROFILE InputProfile,
1380                                                    cmsUInt32Number InputFormat,
1381                                                    cmsHPROFILE OutputProfile,
1382                                                    cmsUInt32Number OutputFormat,
1383                                                    cmsHPROFILE ProofingProfile,
1384                                                    cmsUInt32Number nIntent,
1385                                                    cmsUInt32Number ProofingIntent,
1386                                                    cmsUInt32Number dwFlags)
1387 {
1388     cmsHPROFILE hArray[4];
1389     cmsUInt32Number Intents[4];
1390     cmsBool  BPC[4];
1391     cmsFloat64Number Adaptation[4];
1392     cmsBool  DoBPC = (dwFlags & cmsFLAGS_BLACKPOINTCOMPENSATION) ? TRUE : FALSE;
1393 
1394 
1395     hArray[0]  = InputProfile; hArray[1] = ProofingProfile; hArray[2]  = ProofingProfile;               hArray[3] = OutputProfile;
1396     Intents[0] = nIntent;      Intents[1] = nIntent;        Intents[2] = INTENT_RELATIVE_COLORIMETRIC;  Intents[3] = ProofingIntent;
1397     BPC[0]     = DoBPC;        BPC[1] = DoBPC;              BPC[2] = 0;                                 BPC[3] = 0;
1398 
1399     Adaptation[0] = Adaptation[1] = Adaptation[2] = Adaptation[3] = cmsSetAdaptationStateTHR(ContextID, -1);
1400 
1401     if (!(dwFlags & (cmsFLAGS_SOFTPROOFING|cmsFLAGS_GAMUTCHECK)))
1402         return cmsCreateTransformTHR(ContextID, InputProfile, InputFormat, OutputProfile, OutputFormat, nIntent, dwFlags);
1403 
1404     return cmsCreateExtendedTransform(ContextID, 4, hArray, BPC, Intents, Adaptation,
1405                                         ProofingProfile, 1, InputFormat, OutputFormat, dwFlags);
1406 
1407 }
1408 
1409 
1410 cmsHTRANSFORM CMSEXPORT cmsCreateProofingTransform(cmsHPROFILE InputProfile,
1411                                                    cmsUInt32Number InputFormat,
1412                                                    cmsHPROFILE OutputProfile,
1413                                                    cmsUInt32Number OutputFormat,
1414                                                    cmsHPROFILE ProofingProfile,
1415                                                    cmsUInt32Number nIntent,
1416                                                    cmsUInt32Number ProofingIntent,
1417                                                    cmsUInt32Number dwFlags)
1418 {
1419     return cmsCreateProofingTransformTHR(cmsGetProfileContextID(InputProfile),
1420                                                    InputProfile,
1421                                                    InputFormat,
1422                                                    OutputProfile,
1423                                                    OutputFormat,
1424                                                    ProofingProfile,
1425                                                    nIntent,
1426                                                    ProofingIntent,
1427                                                    dwFlags);
1428 }
1429 
1430 
1431 // Grab the ContextID from an open transform. Returns NULL if a NULL transform is passed
1432 cmsContext CMSEXPORT cmsGetTransformContextID(cmsHTRANSFORM hTransform)
1433 {
1434     _cmsTRANSFORM* xform = (_cmsTRANSFORM*) hTransform;
1435 
1436     if (xform == NULL) return NULL;
1437     return xform -> ContextID;
1438 }
1439 
1440 // Grab the input/output formats
1441 cmsUInt32Number CMSEXPORT cmsGetTransformInputFormat(cmsHTRANSFORM hTransform)
1442 {
1443     _cmsTRANSFORM* xform = (_cmsTRANSFORM*) hTransform;
1444 
1445     if (xform == NULL) return 0;
1446     return xform->InputFormat;
1447 }
1448 
1449 cmsUInt32Number CMSEXPORT cmsGetTransformOutputFormat(cmsHTRANSFORM hTransform)
1450 {
1451     _cmsTRANSFORM* xform = (_cmsTRANSFORM*) hTransform;
1452 
1453     if (xform == NULL) return 0;
1454     return xform->OutputFormat;
1455 }
1456 
1457 // For backwards compatibility
1458 cmsBool CMSEXPORT cmsChangeBuffersFormat(cmsHTRANSFORM hTransform,
1459                                          cmsUInt32Number InputFormat,
1460                                          cmsUInt32Number OutputFormat)
1461 {
1462     _cmsTRANSFORM* xform = (_cmsTRANSFORM*) hTransform;
1463     cmsFormatter16 FromInput, ToOutput;
1464 
1465 
1466     // We only can afford to change formatters if previous transform is at least 16 bits
1467     if (!(xform ->dwOriginalFlags & cmsFLAGS_CAN_CHANGE_FORMATTER)) {
1468 
1469         cmsSignalError(xform ->ContextID, cmsERROR_NOT_SUITABLE, "cmsChangeBuffersFormat works only on transforms created originally with at least 16 bits of precision");
1470         return FALSE;
1471     }
1472 
1473     FromInput = _cmsGetFormatter(xform->ContextID, InputFormat,  cmsFormatterInput, CMS_PACK_FLAGS_16BITS).Fmt16;
1474     ToOutput  = _cmsGetFormatter(xform->ContextID, OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_16BITS).Fmt16;
1475 
1476     if (FromInput == NULL || ToOutput == NULL) {
1477 
1478         cmsSignalError(xform -> ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported raster format");
1479         return FALSE;
1480     }
1481 
1482     xform ->InputFormat  = InputFormat;
1483     xform ->OutputFormat = OutputFormat;
1484     xform ->FromInput    = FromInput;
1485     xform ->ToOutput     = ToOutput;
1486     return TRUE;
1487 }