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