1 //---------------------------------------------------------------------------------
2 //
3 //  Little Color Management System
4 //  Copyright (c) 1998-2017 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 cmsCreateExtendedTransform().  Little CMS can handle incomplete adaptation states.
57 // The adaptation state may be defaulted by this function. If you don't like it, use the extended transform routine
cmsSetAdaptationState(cmsContext ContextID,cmsFloat64Number d)58 cmsFloat64Number CMSEXPORT cmsSetAdaptationState(cmsContext ContextID, cmsFloat64Number d)
59 {
60     cmsFloat64Number prev;
61     _cmsAdaptationStateChunkType* ptr = (_cmsAdaptationStateChunkType*) _cmsContextGetClientChunk(ContextID, AdaptationStateContext);
62 
63     // Get previous value for return
64     prev = ptr ->AdaptationState;
65 
66     // Set the value if d is positive or zero
67     if (d >= 0.0) {
68 
69         ptr ->AdaptationState = d;
70     }
71 
72     // Always return previous value
73     return prev;
74 }
75 
76 
77 // -----------------------------------------------------------------------
78 
79 // Alarm codes for 16-bit transformations, because the fixed range of containers there are
80 // no values left to mark out of gamut.
81 
82 #define DEFAULT_ALARM_CODES_VALUE {0x7F00, 0x7F00, 0x7F00, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
83 
84 _cmsAlarmCodesChunkType _cmsAlarmCodesChunk = { DEFAULT_ALARM_CODES_VALUE };
85 
86 // Sets the codes used to mark out-out-gamut on Proofing transforms for a given context. Values are meant to be
87 // encoded in 16 bits.
cmsSetAlarmCodes(cmsContext ContextID,const cmsUInt16Number AlarmCodesP[cmsMAXCHANNELS])88 void CMSEXPORT cmsSetAlarmCodes(cmsContext ContextID, const cmsUInt16Number AlarmCodesP[cmsMAXCHANNELS])
89 {
90     _cmsAlarmCodesChunkType* ContextAlarmCodes = (_cmsAlarmCodesChunkType*) _cmsContextGetClientChunk(ContextID, AlarmCodesContext);
91 
92     _cmsAssert(ContextAlarmCodes != NULL); // Can't happen
93 
94     memcpy(ContextAlarmCodes->AlarmCodes, AlarmCodesP, sizeof(ContextAlarmCodes->AlarmCodes));
95 }
96 
97 // Gets the current codes used to mark out-out-gamut on Proofing transforms for the given context.
98 // Values are meant to be encoded in 16 bits.
cmsGetAlarmCodes(cmsContext ContextID,cmsUInt16Number AlarmCodesP[cmsMAXCHANNELS])99 void CMSEXPORT cmsGetAlarmCodes(cmsContext ContextID, cmsUInt16Number AlarmCodesP[cmsMAXCHANNELS])
100 {
101     _cmsAlarmCodesChunkType* ContextAlarmCodes = (_cmsAlarmCodesChunkType*) _cmsContextGetClientChunk(ContextID, AlarmCodesContext);
102 
103     _cmsAssert(ContextAlarmCodes != NULL); // Can't happen
104 
105     memcpy(AlarmCodesP, ContextAlarmCodes->AlarmCodes, sizeof(ContextAlarmCodes->AlarmCodes));
106 }
107 
108 
109 // Init and duplicate alarm codes
_cmsAllocAlarmCodesChunk(struct _cmsContext_struct * ctx,const struct _cmsContext_struct * src)110 void _cmsAllocAlarmCodesChunk(struct _cmsContext_struct* ctx,
111                               const struct _cmsContext_struct* src)
112 {
113     static _cmsAlarmCodesChunkType AlarmCodesChunk = { DEFAULT_ALARM_CODES_VALUE };
114     void* from;
115 
116     if (src != NULL) {
117         from = src ->chunks[AlarmCodesContext];
118     }
119     else {
120        from = &AlarmCodesChunk;
121     }
122 
123     ctx ->chunks[AlarmCodesContext] = _cmsSubAllocDup(ctx ->MemPool, from, sizeof(_cmsAlarmCodesChunkType));
124 }
125 
126 // -----------------------------------------------------------------------
127 
128 // Get rid of transform resources
cmsDeleteTransform(cmsContext ContextID,cmsHTRANSFORM hTransform)129 void CMSEXPORT cmsDeleteTransform(cmsContext ContextID, cmsHTRANSFORM hTransform)
130 {
131     _cmsTRANSFORM* p = (_cmsTRANSFORM*) hTransform;
132     _cmsTRANSFORMCORE *core;
133     cmsUInt32Number refs;
134 
135     if (p == NULL)
136         return;
137 
138     core = p->core;
139 
140     _cmsAssert(core != NULL);
141 
142     refs = _cmsAdjustReferenceCount(&core->refs, -1);
143     _cmsFree(ContextID, (void *) p);
144 
145     if (refs != 0)
146         return;
147 
148     if (core->GamutCheck)
149         cmsPipelineFree(ContextID, core->GamutCheck);
150 
151     if (core->Lut)
152         cmsPipelineFree(ContextID, core->Lut);
153 
154     if (core->InputColorant)
155         cmsFreeNamedColorList(ContextID, core->InputColorant);
156 
157     if (core->OutputColorant)
158         cmsFreeNamedColorList(ContextID, core->OutputColorant);
159 
160     if (core->Sequence)
161         cmsFreeProfileSequenceDescription(ContextID, core->Sequence);
162 
163     if (core->UserData)
164         core->FreeUserData(ContextID, core->UserData);
165 
166     _cmsFree(ContextID, (void *)core);
167 }
168 
169 // Apply transform.
cmsDoTransform(cmsContext ContextID,cmsHTRANSFORM Transform,const void * InputBuffer,void * OutputBuffer,cmsUInt32Number Size)170 void CMSEXPORT cmsDoTransform(cmsContext ContextID, cmsHTRANSFORM  Transform,
171                               const void* InputBuffer,
172                               void* OutputBuffer,
173                               cmsUInt32Number Size)
174 
175 {
176     _cmsTRANSFORM* p = (_cmsTRANSFORM*) Transform;
177     cmsStride stride;
178 
179     stride.BytesPerLineIn = 0;  // Not used
180     stride.BytesPerLineOut = 0;
181     stride.BytesPerPlaneIn = Size;
182     stride.BytesPerPlaneOut = Size;
183 
184     p -> xform(ContextID, p, InputBuffer, OutputBuffer, Size, 1, &stride);
185 }
186 
187 
188 // This is a legacy stride for planar
cmsDoTransformStride(cmsContext ContextID,cmsHTRANSFORM Transform,const void * InputBuffer,void * OutputBuffer,cmsUInt32Number Size,cmsUInt32Number Stride)189 void CMSEXPORT cmsDoTransformStride(cmsContext ContextID, cmsHTRANSFORM  Transform,
190                               const void* InputBuffer,
191                               void* OutputBuffer,
192                               cmsUInt32Number Size, cmsUInt32Number Stride)
193 
194 {
195     _cmsTRANSFORM* p = (_cmsTRANSFORM*) Transform;
196     cmsStride stride;
197 
198     stride.BytesPerLineIn = 0;
199     stride.BytesPerLineOut = 0;
200     stride.BytesPerPlaneIn = Stride;
201     stride.BytesPerPlaneOut = Stride;
202 
203     p -> xform(ContextID, p, InputBuffer, OutputBuffer, Size, 1, &stride);
204 }
205 
206 // This is the "fast" function for plugins
cmsDoTransformLineStride(cmsContext ContextID,cmsHTRANSFORM Transform,const void * InputBuffer,void * OutputBuffer,cmsUInt32Number PixelsPerLine,cmsUInt32Number LineCount,cmsUInt32Number BytesPerLineIn,cmsUInt32Number BytesPerLineOut,cmsUInt32Number BytesPerPlaneIn,cmsUInt32Number BytesPerPlaneOut)207 void CMSEXPORT cmsDoTransformLineStride(cmsContext ContextID, cmsHTRANSFORM  Transform,
208                               const void* InputBuffer,
209                               void* OutputBuffer,
210                               cmsUInt32Number PixelsPerLine,
211                               cmsUInt32Number LineCount,
212                               cmsUInt32Number BytesPerLineIn,
213                               cmsUInt32Number BytesPerLineOut,
214                               cmsUInt32Number BytesPerPlaneIn,
215                               cmsUInt32Number BytesPerPlaneOut)
216 
217 {
218     _cmsTRANSFORM* p = (_cmsTRANSFORM*) Transform;
219     cmsStride stride;
220 
221     stride.BytesPerLineIn = BytesPerLineIn;
222     stride.BytesPerLineOut = BytesPerLineOut;
223     stride.BytesPerPlaneIn = BytesPerPlaneIn;
224     stride.BytesPerPlaneOut = BytesPerPlaneOut;
225 
226     p->xform(ContextID, p, InputBuffer, OutputBuffer, PixelsPerLine, LineCount, &stride);
227 }
228 
229 
230 
231 // Transform routines ----------------------------------------------------------------------------------------------------------
232 
233 // Float xform converts floats. Since there are no performance issues, one routine does all job, including gamut check.
234 // Note that because extended range, we can use a -1.0 value for out of gamut in this case.
235 static
FloatXFORM(cmsContext ContextID,_cmsTRANSFORM * p,const void * in,void * out,cmsUInt32Number PixelsPerLine,cmsUInt32Number LineCount,const cmsStride * Stride)236 void FloatXFORM(cmsContext ContextID, _cmsTRANSFORM* p,
237                 const void* in,
238                 void* out,
239                 cmsUInt32Number PixelsPerLine,
240                 cmsUInt32Number LineCount,
241                 const cmsStride* Stride)
242 {
243     cmsUInt8Number* accum;
244     cmsUInt8Number* output;
245     cmsFloat32Number fIn[cmsMAXCHANNELS], fOut[cmsMAXCHANNELS];
246     cmsFloat32Number OutOfGamut;
247     cmsUInt32Number i, j, c, strideIn, strideOut;
248     _cmsTRANSFORMCORE *core = p->core;
249 
250     _cmsHandleExtraChannels(ContextID, p, in, out, PixelsPerLine, LineCount, Stride);
251 
252     strideIn = 0;
253     strideOut = 0;
254     memset(fIn, 0, sizeof(fIn));
255     memset(fOut, 0, sizeof(fIn));
256 
257     for (i = 0; i < LineCount; i++) {
258 
259         accum = (cmsUInt8Number*)in + strideIn;
260         output = (cmsUInt8Number*)out + strideOut;
261 
262         for (j = 0; j < PixelsPerLine; j++) {
263 
264             accum = p->FromInputFloat(ContextID, p, fIn, accum, Stride->BytesPerPlaneIn);
265 
266             // Any gamut chack to do?
267             if (core->GamutCheck != NULL) {
268 
269                 // Evaluate gamut marker.
270                 cmsPipelineEvalFloat(ContextID, fIn, &OutOfGamut, core->GamutCheck);
271 
272                 // Is current color out of gamut?
273                 if (OutOfGamut > 0.0) {
274 
275                     // Certainly, out of gamut
276                     for (c = 0; c < cmsMAXCHANNELS; c++)
277                         fOut[c] = -1.0;
278 
279                 }
280                 else {
281                     // No, proceed normally
282                     cmsPipelineEvalFloat(ContextID, fIn, fOut, core->Lut);
283                 }
284             }
285             else {
286 
287                 // No gamut check at all
288                 cmsPipelineEvalFloat(ContextID, fIn, fOut, core->Lut);
289             }
290 
291 
292             output = p->ToOutputFloat(ContextID, p, fOut, output, Stride->BytesPerPlaneOut);
293         }
294 
295         strideIn += Stride->BytesPerLineIn;
296         strideOut += Stride->BytesPerLineOut;
297     }
298 
299 }
300 
301 
302 static
NullFloatXFORM(cmsContext ContextID,_cmsTRANSFORM * p,const void * in,void * out,cmsUInt32Number PixelsPerLine,cmsUInt32Number LineCount,const cmsStride * Stride)303 void NullFloatXFORM(cmsContext ContextID, _cmsTRANSFORM* p,
304                     const void* in,
305                     void* out,
306                     cmsUInt32Number PixelsPerLine,
307                     cmsUInt32Number LineCount,
308                     const cmsStride* Stride)
309 
310 {
311     cmsUInt8Number* accum;
312     cmsUInt8Number* output;
313     cmsFloat32Number fIn[cmsMAXCHANNELS];
314     cmsUInt32Number i, j, strideIn, strideOut;
315 
316     _cmsHandleExtraChannels(ContextID, p, in, out, PixelsPerLine, LineCount, Stride);
317 
318     strideIn = 0;
319     strideOut = 0;
320     memset(fIn, 0, sizeof(fIn));
321 
322     for (i = 0; i < LineCount; i++) {
323 
324            accum = (cmsUInt8Number*) in + strideIn;
325            output = (cmsUInt8Number*) out + strideOut;
326 
327            for (j = 0; j < PixelsPerLine; j++) {
328 
329                   accum = p->FromInputFloat(ContextID, p, fIn, accum, Stride ->BytesPerPlaneIn);
330                   output = p->ToOutputFloat(ContextID, p, fIn, output, Stride->BytesPerPlaneOut);
331            }
332 
333            strideIn += Stride->BytesPerLineIn;
334            strideOut += Stride->BytesPerLineOut;
335     }
336 }
337 
338 // 16 bit precision -----------------------------------------------------------------------------------------------------------
339 
340 // Null transformation, only applies formatters. No cache
341 static
NullXFORM(cmsContext ContextID,_cmsTRANSFORM * p,const void * in,void * out,cmsUInt32Number PixelsPerLine,cmsUInt32Number LineCount,const cmsStride * Stride)342 void NullXFORM(cmsContext ContextID,
343                _cmsTRANSFORM* p,
344                const void* in,
345                void* out,
346                cmsUInt32Number PixelsPerLine,
347                cmsUInt32Number LineCount,
348                const cmsStride* Stride)
349 {
350     cmsUInt8Number* accum;
351     cmsUInt8Number* output;
352     cmsUInt16Number wIn[cmsMAXCHANNELS];
353     cmsUInt32Number i, j, strideIn, strideOut;
354 
355     _cmsHandleExtraChannels(ContextID, p, in, out, PixelsPerLine, LineCount, Stride);
356 
357     strideIn = 0;
358     strideOut = 0;
359     memset(wIn, 0, sizeof(wIn));
360 
361     for (i = 0; i < LineCount; i++) {
362 
363            accum = (cmsUInt8Number*)in + strideIn;
364            output = (cmsUInt8Number*)out + strideOut;
365 
366            for (j = 0; j < PixelsPerLine; j++) {
367 
368                   accum = p->FromInput(ContextID, p, wIn, accum, Stride->BytesPerPlaneIn);
369                   output = p->ToOutput(ContextID, p, wIn, output, Stride->BytesPerPlaneOut);
370     }
371 
372            strideIn += Stride->BytesPerLineIn;
373            strideOut += Stride->BytesPerLineOut;
374     }
375 
376 }
377 
378 
379 // No gamut check, no cache, 16 bits
380 #define FUNCTION_NAME PrecalculatedXFORM
381 #include "extra_xform.h"
382 
383 // No gamut check, no cache, Identity transform, including pack/unpack
384 static
PrecalculatedXFORMIdentity(cmsContext ContextID,_cmsTRANSFORM * p,const void * in,void * out,cmsUInt32Number PixelsPerLine,cmsUInt32Number LineCount,const cmsStride * Stride)385 void PrecalculatedXFORMIdentity(cmsContext ContextID,
386                                 _cmsTRANSFORM* p,
387                                 const void* in,
388                                 void* out,
389                                 cmsUInt32Number PixelsPerLine,
390                                 cmsUInt32Number LineCount,
391                                 const cmsStride* Stride)
392 {
393     cmsUInt32Number bpli = Stride->BytesPerLineIn;
394     cmsUInt32Number bplo = Stride->BytesPerLineOut;
395     int bpp;
396     cmsUNUSED_PARAMETER(ContextID);
397 
398     /* Silence some warnings */
399     (void)bpli;
400     (void)bplo;
401 
402     if ((in == out && bpli == bplo) || PixelsPerLine == 0)
403         return;
404 
405     bpp = T_BYTES(p->InputFormat);
406     if (bpp == 0)
407         bpp = sizeof(double);
408     bpp *= T_CHANNELS(p->InputFormat) + T_EXTRA(p->InputFormat);
409     PixelsPerLine *= bpp; /* Convert to BytesPerLine */
410     while (LineCount-- > 0)
411     {
412         memmove(out, in, PixelsPerLine);
413         in = (void *)((cmsUInt8Number *)in + bpli);
414         out = (void *)((cmsUInt8Number *)out + bplo);
415     }
416 }
417 
418 static
PrecalculatedXFORMIdentityPlanar(cmsContext ContextID,_cmsTRANSFORM * p,const void * in,void * out,cmsUInt32Number PixelsPerLine,cmsUInt32Number LineCount,const cmsStride * Stride)419 void PrecalculatedXFORMIdentityPlanar(cmsContext ContextID,
420                                       _cmsTRANSFORM* p,
421                                       const void* in,
422                                       void* out,
423                                       cmsUInt32Number PixelsPerLine,
424                                       cmsUInt32Number LineCount,
425                                       const cmsStride* Stride)
426 {
427     cmsUInt32Number bpli = Stride->BytesPerLineIn;
428     cmsUInt32Number bplo = Stride->BytesPerLineOut;
429     cmsUInt32Number bppi = Stride->BytesPerPlaneIn;
430     cmsUInt32Number bppo = Stride->BytesPerPlaneOut;
431     int bpp;
432     int planes;
433     const void *plane_in;
434     void *plane_out;
435     cmsUNUSED_PARAMETER(ContextID);
436 
437     /* Silence some warnings */
438     (void)bpli;
439     (void)bplo;
440     (void)bppi;
441     (void)bppo;
442 
443     if ((in == out && bpli == bplo && bppi == bppo) || PixelsPerLine == 0)
444         return;
445 
446     bpp = T_BYTES(p->InputFormat);
447     if (bpp == 0)
448         bpp = sizeof(double);
449     PixelsPerLine *= bpp; /* Convert to BytesPerLine */
450     planes = T_CHANNELS(p->InputFormat) + T_EXTRA(p->InputFormat);
451     while (planes-- > 0)
452     {
453         plane_in = in;
454         plane_out = out;
455         while (LineCount-- > 0)
456         {
457             memmove(plane_out, plane_in, PixelsPerLine);
458             plane_in = (void *)((cmsUInt8Number *)plane_in + bpli);
459             plane_out = (void *)((cmsUInt8Number *)plane_out + bplo);
460         }
461         in = (void *)((cmsUInt8Number *)in + bppi);
462         out = (void *)((cmsUInt8Number *)out + bppo);
463     }
464 }
465 
466 // Auxiliary: Handle precalculated gamut check. The retrieval of context may be alittle bit slow, but this function is not critical.
467 static
TransformOnePixelWithGamutCheck(cmsContext ContextID,_cmsTRANSFORM * p,const cmsUInt16Number wIn[],cmsUInt16Number wOut[])468 void TransformOnePixelWithGamutCheck(cmsContext ContextID, _cmsTRANSFORM* p,
469                                      const cmsUInt16Number wIn[],
470                                      cmsUInt16Number wOut[])
471 {
472     cmsUInt16Number wOutOfGamut;
473     _cmsTRANSFORMCORE *core = p->core;
474 
475     core->GamutCheck->Eval16Fn(ContextID, wIn, &wOutOfGamut, core->GamutCheck->Data);
476     if (wOutOfGamut >= 1) {
477 
478         cmsUInt32Number i;
479         cmsUInt32Number n = core->Lut->OutputChannels;
480         _cmsAlarmCodesChunkType* ContextAlarmCodes = (_cmsAlarmCodesChunkType*) _cmsContextGetClientChunk(ContextID, AlarmCodesContext);
481 
482         for (i=0; i < n; i++) {
483 
484             wOut[i] = ContextAlarmCodes ->AlarmCodes[i];
485         }
486     }
487     else
488         core->Lut->Eval16Fn(ContextID, wIn, wOut, core->Lut->Data);
489 }
490 
491 // Gamut check, No cache, 16 bits.
492 #define FUNCTION_NAME PrecalculatedXFORMGamutCheck
493 #define GAMUTCHECK
494 #include "extra_xform.h"
495 
496 // No gamut check, Cache, 16 bits,
497 #define FUNCTION_NAME CachedXFORM
498 #define CACHED
499 #include "extra_xform.h"
500 
501 // All those nice features together
502 #define FUNCTION_NAME CachedXFORMGamutCheck
503 #define CACHED
504 #define GAMUTCHECK
505 #include "extra_xform.h"
506 
507 // No gamut check, Cache, 16 bits, <= 4 bytes
508 #define FUNCTION_NAME CachedXFORM4
509 #define CACHED
510 #define INBYTES 4
511 #define EXTRABYTES 0
512 #include "extra_xform.h"
513 
514 // No gamut check, Cache, 16 bits, <= 8 bytes total
515 #define FUNCTION_NAME CachedXFORM8
516 #define CACHED
517 #define INBYTES 8
518 #define EXTRABYTES 0
519 #include "extra_xform.h"
520 
521 // Special ones for common cases.
522 #define FUNCTION_NAME CachedXFORM1to1
523 #define CACHED
524 #define INBYTES 2
525 #define EXTRABYTES 0
526 #define UNPACK(CTX,T,D,S,Z)                \
527 do {                                       \
528        (D)[0] = FROM_8_TO_16(*(S)); (S)++; \
529 } while (0)
530 #define PACK(CTX,T,S,D,Z)          \
531 do {                               \
532     *(D)++ = FROM_16_TO_8((S)[0]); \
533 } while (0)
534 #include "extra_xform.h"
535 
536 #define FUNCTION_NAME CachedXFORM1x2to1x2
537 #define CACHED
538 #define INBYTES 2
539 #define EXTRABYTES 0
540 #define UNPACK(CTX,T,D,S,Z)                        \
541 do {                                               \
542        (D)[0] = *(cmsUInt16Number *)(S); (S) += 2; \
543 } while (0)
544 #define PACK(CTX,T,S,D,Z)                       \
545 do {                                            \
546     *(cmsUInt16Number *)(D) = (S)[0]; (D) += 2; \
547 } while (0)
548 #include "extra_xform.h"
549 
550 #define FUNCTION_NAME CachedXFORM1to3
551 #define CACHED
552 #define INBYTES 2
553 #define EXTRABYTES 0
554 #define UNPACK(CTX,T,D,S,Z)                \
555 do {                                       \
556        (D)[0] = FROM_8_TO_16(*(S)); (S)++; \
557 } while (0)
558 #define PACK(CTX,T,S,D,Z)          \
559 do {                               \
560     *(D)++ = FROM_16_TO_8((S)[0]); \
561     *(D)++ = FROM_16_TO_8((S)[1]); \
562     *(D)++ = FROM_16_TO_8((S)[2]); \
563 } while (0)
564 #include "extra_xform.h"
565 
566 #define FUNCTION_NAME CachedXFORM1x2to3x2
567 #define CACHED
568 #define INBYTES 2
569 #define EXTRABYTES 0
570 #define UNPACK(CTX,T,D,S,Z)                        \
571 do {                                               \
572        (D)[0] = *(cmsUInt16Number *)(S); (S) += 2; \
573 } while (0)
574 #define PACK(CTX,T,S,D,Z)                       \
575 do {                                            \
576     *(cmsUInt16Number *)(D) = (S)[0]; (D) += 2; \
577     *(cmsUInt16Number *)(D) = (S)[1]; (D) += 2; \
578     *(cmsUInt16Number *)(D) = (S)[2]; (D) += 2; \
579 } while (0)
580 #include "extra_xform.h"
581 
582 #define FUNCTION_NAME CachedXFORM1to4
583 #define CACHED
584 #define INBYTES 2
585 #define EXTRABYTES 0
586 #define UNPACK(CTX,T,D,S,Z)                \
587 do {                                       \
588        (D)[0] = FROM_8_TO_16(*(S)); (S)++; \
589 } while (0)
590 #define PACK(CTX,T,S,D,Z)          \
591 do {                               \
592     *(D)++ = FROM_16_TO_8((S)[0]); \
593     *(D)++ = FROM_16_TO_8((S)[1]); \
594     *(D)++ = FROM_16_TO_8((S)[2]); \
595     *(D)++ = FROM_16_TO_8((S)[3]); \
596 } while (0)
597 #include "extra_xform.h"
598 
599 #define FUNCTION_NAME CachedXFORM1x2to4x2
600 #define CACHED
601 #define INBYTES 2
602 #define EXTRABYTES 0
603 #define UNPACK(CTX,T,D,S,Z)                        \
604 do {                                               \
605        (D)[0] = *(cmsUInt16Number *)(S); (S) += 2; \
606 } while (0)
607 #define PACK(CTX,T,S,D,Z)                       \
608 do {                                            \
609     *(cmsUInt16Number *)(D) = (S)[0]; (D) += 2; \
610     *(cmsUInt16Number *)(D) = (S)[1]; (D) += 2; \
611     *(cmsUInt16Number *)(D) = (S)[2]; (D) += 2; \
612     *(cmsUInt16Number *)(D) = (S)[3]; (D) += 2; \
613 } while (0)
614 #include "extra_xform.h"
615 
616 #define FUNCTION_NAME CachedXFORM3to1
617 #define CACHED
618 #define INBYTES 6
619 #define EXTRABYTES 0
620 #define UNPACK(CTX,T,D,S,Z)                 \
621 do {                                        \
622         (D)[0] = FROM_8_TO_16(*(S)); (S)++; \
623         (D)[1] = FROM_8_TO_16(*(S)); (S)++; \
624         (D)[2] = FROM_8_TO_16(*(S)); (S)++; \
625 } while (0)
626 #define PACK(CTX,T,S,D,Z)          \
627 do {                               \
628     *(D)++ = FROM_16_TO_8((S)[0]); \
629 } while (0)
630 #include "extra_xform.h"
631 
632 #define FUNCTION_NAME CachedXFORM3x2to1x2
633 #define CACHED
634 #define INBYTES 6
635 #define EXTRABYTES 0
636 #define UNPACK(CTX,T,D,S,Z)                         \
637 do {                                                \
638         (D)[0] = *(cmsUInt16Number *)(S); (S) += 2; \
639         (D)[1] = *(cmsUInt16Number *)(S); (S) += 2; \
640         (D)[2] = *(cmsUInt16Number *)(S); (S) += 2; \
641 } while (0)
642 #define PACK(CTX,T,S,D,Z)                       \
643 do {                                            \
644     *(cmsUInt16Number *)(D) = (S)[0]; (D) += 2; \
645 } while (0)
646 #include "extra_xform.h"
647 
648 #define FUNCTION_NAME CachedXFORM3to3
649 #define CACHED
650 #define INBYTES 6
651 #define EXTRABYTES 0
652 #define UNPACK(CTX,T,D,S,Z)                \
653 do {                                       \
654        (D)[0] = FROM_8_TO_16(*(S)); (S)++; \
655        (D)[1] = FROM_8_TO_16(*(S)); (S)++; \
656        (D)[2] = FROM_8_TO_16(*(S)); (S)++; \
657 } while (0)
658 #define PACK(CTX,T,S,D,Z)          \
659 do {                               \
660     *(D)++ = FROM_16_TO_8((S)[0]); \
661     *(D)++ = FROM_16_TO_8((S)[1]); \
662     *(D)++ = FROM_16_TO_8((S)[2]); \
663 } while (0)
664 #include "extra_xform.h"
665 
666 #define FUNCTION_NAME CachedXFORM3x2to3x2
667 #define CACHED
668 #define INBYTES 6
669 #define EXTRABYTES 0
670 #define UNPACK(CTX,T,D,S,Z)                        \
671 do {                                               \
672        (D)[0] = *(cmsUInt16Number *)(S); (S) += 2; \
673        (D)[1] = *(cmsUInt16Number *)(S); (S) += 2; \
674        (D)[2] = *(cmsUInt16Number *)(S); (S) += 2; \
675 } while (0)
676 #define PACK(CTX,T,S,D,Z)                       \
677 do {                                            \
678     *(cmsUInt16Number *)(D) = (S)[0]; (D) += 2; \
679     *(cmsUInt16Number *)(D) = (S)[1]; (D) += 2; \
680     *(cmsUInt16Number *)(D) = (S)[2]; (D) += 2; \
681 } while (0)
682 #include "extra_xform.h"
683 
684 #define FUNCTION_NAME CachedXFORM3to4
685 #define CACHED
686 #define INBYTES 6
687 #define EXTRABYTES 0
688 #define UNPACK(CTX,T,D,S,Z)                \
689 do {                                       \
690        (D)[0] = FROM_8_TO_16(*(S)); (S)++; \
691        (D)[1] = FROM_8_TO_16(*(S)); (S)++; \
692        (D)[2] = FROM_8_TO_16(*(S)); (S)++; \
693 } while (0)
694 #define PACK(CTX,T,S,D,Z)          \
695 do {                               \
696     *(D)++ = FROM_16_TO_8((S)[0]); \
697     *(D)++ = FROM_16_TO_8((S)[1]); \
698     *(D)++ = FROM_16_TO_8((S)[2]); \
699     *(D)++ = FROM_16_TO_8((S)[3]); \
700 } while (0)
701 #include "extra_xform.h"
702 
703 #define FUNCTION_NAME CachedXFORM3x2to4x2
704 #define CACHED
705 #define INBYTES 6
706 #define EXTRABYTES 0
707 #define UNPACK(CTX,T,D,S,Z)                        \
708 do {                                               \
709        (D)[0] = *(cmsUInt16Number *)(S); (S) += 2; \
710        (D)[1] = *(cmsUInt16Number *)(S); (S) += 2; \
711        (D)[2] = *(cmsUInt16Number *)(S); (S) += 2; \
712 } while (0)
713 #define PACK(CTX,T,S,D,Z)                       \
714 do {                                            \
715     *(cmsUInt16Number *)(D) = (S)[0]; (D) += 2; \
716     *(cmsUInt16Number *)(D) = (S)[1]; (D) += 2; \
717     *(cmsUInt16Number *)(D) = (S)[2]; (D) += 2; \
718     *(cmsUInt16Number *)(D) = (S)[3]; (D) += 2; \
719 } while (0)
720 #include "extra_xform.h"
721 
722 #define FUNCTION_NAME CachedXFORM4to1
723 #define CACHED
724 #define INBYTES 8
725 #define EXTRABYTES 0
726 #define UNPACK(CTX,T,D,S,Z)                \
727 do {                                       \
728        (D)[0] = FROM_8_TO_16(*(S)); (S)++; \
729        (D)[1] = FROM_8_TO_16(*(S)); (S)++; \
730        (D)[2] = FROM_8_TO_16(*(S)); (S)++; \
731        (D)[3] = FROM_8_TO_16(*(S)); (S)++; \
732 } while (0)
733 #define PACK(CTX,T,S,D,Z)          \
734 do {                               \
735     *(D)++ = FROM_16_TO_8((S)[0]); \
736 } while (0)
737 #include "extra_xform.h"
738 
739 #define FUNCTION_NAME CachedXFORM4x2to1x2
740 #define CACHED
741 #define INBYTES 8
742 #define EXTRABYTES 0
743 #define UNPACK(CTX,T,D,S,Z)                        \
744 do {                                               \
745        (D)[0] = *(cmsUInt16Number *)(S); (S) += 2; \
746        (D)[1] = *(cmsUInt16Number *)(S); (S) += 2; \
747        (D)[2] = *(cmsUInt16Number *)(S); (S) += 2; \
748        (D)[3] = *(cmsUInt16Number *)(S); (S) += 2; \
749 } while (0)
750 #define PACK(CTX,T,S,D,Z)                       \
751 do {                                            \
752     *(cmsUInt16Number *)(D) = (S)[0]; (D) += 2; \
753 } while (0)
754 #include "extra_xform.h"
755 
756 #define FUNCTION_NAME CachedXFORM4to3
757 #define CACHED
758 #define INBYTES 8
759 #define EXTRABYTES 0
760 #define UNPACK(CTX,T,D,S,Z)                \
761 do {                                       \
762        (D)[0] = FROM_8_TO_16(*(S)); (S)++; \
763        (D)[1] = FROM_8_TO_16(*(S)); (S)++; \
764        (D)[2] = FROM_8_TO_16(*(S)); (S)++; \
765        (D)[3] = FROM_8_TO_16(*(S)); (S)++; \
766 } while (0)
767 #define PACK(CTX,T,S,D,Z)          \
768 do {                               \
769     *(D)++ = FROM_16_TO_8((S)[0]); \
770     *(D)++ = FROM_16_TO_8((S)[1]); \
771     *(D)++ = FROM_16_TO_8((S)[2]); \
772 } while (0)
773 #include "extra_xform.h"
774 
775 #define FUNCTION_NAME CachedXFORM4x2to3x2
776 #define CACHED
777 #define INBYTES 8
778 #define EXTRABYTES 0
779 #define UNPACK(CTX,T,D,S,Z)                        \
780 do {                                               \
781        (D)[0] = *(cmsUInt16Number *)(S); (S) += 2; \
782        (D)[1] = *(cmsUInt16Number *)(S); (S) += 2; \
783        (D)[2] = *(cmsUInt16Number *)(S); (S) += 2; \
784        (D)[3] = *(cmsUInt16Number *)(S); (S) += 2; \
785 } while (0)
786 #define PACK(CTX,T,S,D,Z)                       \
787 do {                                            \
788     *(cmsUInt16Number *)(D) = (S)[0]; (D) += 2; \
789     *(cmsUInt16Number *)(D) = (S)[1]; (D) += 2; \
790     *(cmsUInt16Number *)(D) = (S)[2]; (D) += 2; \
791 } while (0)
792 #include "extra_xform.h"
793 
794 #define FUNCTION_NAME CachedXFORM4to4
795 #define CACHED
796 #define INBYTES 8
797 #define EXTRABYTES 0
798 #define UNPACK(CTX,T,D,S,Z)                \
799 do {                                       \
800        (D)[0] = FROM_8_TO_16(*(S)); (S)++; \
801        (D)[1] = FROM_8_TO_16(*(S)); (S)++; \
802        (D)[2] = FROM_8_TO_16(*(S)); (S)++; \
803        (D)[3] = FROM_8_TO_16(*(S)); (S)++; \
804 } while (0)
805 #define PACK(CTX,T,S,D,Z)          \
806 do {                               \
807     *(D)++ = FROM_16_TO_8((S)[0]); \
808     *(D)++ = FROM_16_TO_8((S)[1]); \
809     *(D)++ = FROM_16_TO_8((S)[2]); \
810     *(D)++ = FROM_16_TO_8((S)[3]); \
811 } while (0)
812 #include "extra_xform.h"
813 
814 #define FUNCTION_NAME CachedXFORM4x2to4x2
815 #define CACHED
816 #define INBYTES 8
817 #define EXTRABYTES 0
818 #define UNPACK(CTX,T,D,S,Z)                        \
819 do {                                               \
820        (D)[0] = *(cmsUInt16Number *)(S); (S) += 2; \
821        (D)[1] = *(cmsUInt16Number *)(S); (S) += 2; \
822        (D)[2] = *(cmsUInt16Number *)(S); (S) += 2; \
823        (D)[3] = *(cmsUInt16Number *)(S); (S) += 2; \
824 } while (0)
825 #define PACK(CTX,T,S,D,Z)                       \
826 do {                                            \
827     *(cmsUInt16Number *)(D) = (S)[0]; (D) += 2; \
828     *(cmsUInt16Number *)(D) = (S)[1]; (D) += 2; \
829     *(cmsUInt16Number *)(D) = (S)[2]; (D) += 2; \
830     *(cmsUInt16Number *)(D) = (S)[3]; (D) += 2; \
831 } while (0)
832 #include "extra_xform.h"
833 
834 // Transform plug-ins ----------------------------------------------------------------------------------------------------
835 
836 // List of used-defined transform factories
837 typedef struct _cmsTransformCollection_st {
838 
839     _cmsTransform2Factory  Factory;
840     cmsBool                OldXform;   // Factory returns xform function in the old style
841 
842     struct _cmsTransformCollection_st *Next;
843 
844 } _cmsTransformCollection;
845 
846 // The linked list head
847 _cmsTransformPluginChunkType _cmsTransformPluginChunk = { NULL };
848 
849 
850 // Duplicates the zone of memory used by the plug-in in the new context
851 static
DupPluginTransformList(struct _cmsContext_struct * ctx,const struct _cmsContext_struct * src)852 void DupPluginTransformList(struct _cmsContext_struct* ctx,
853                                                const struct _cmsContext_struct* src)
854 {
855    _cmsTransformPluginChunkType newHead = { NULL };
856    _cmsTransformCollection*  entry;
857    _cmsTransformCollection*  Anterior = NULL;
858    _cmsTransformPluginChunkType* head = (_cmsTransformPluginChunkType*) src->chunks[TransformPlugin];
859 
860     // Walk the list copying all nodes
861    for (entry = head->TransformCollection;
862         entry != NULL;
863         entry = entry ->Next) {
864 
865             _cmsTransformCollection *newEntry = ( _cmsTransformCollection *) _cmsSubAllocDup(ctx ->MemPool, entry, sizeof(_cmsTransformCollection));
866 
867             if (newEntry == NULL)
868                 return;
869 
870             // We want to keep the linked list order, so this is a little bit tricky
871             newEntry -> Next = NULL;
872             if (Anterior)
873                 Anterior -> Next = newEntry;
874 
875             Anterior = newEntry;
876 
877             if (newHead.TransformCollection == NULL)
878                 newHead.TransformCollection = newEntry;
879     }
880 
881   ctx ->chunks[TransformPlugin] = _cmsSubAllocDup(ctx->MemPool, &newHead, sizeof(_cmsTransformPluginChunkType));
882 }
883 
884 // Allocates memory for transform plugin factory
_cmsAllocTransformPluginChunk(struct _cmsContext_struct * ctx,const struct _cmsContext_struct * src)885 void _cmsAllocTransformPluginChunk(struct _cmsContext_struct* ctx,
886                                         const struct _cmsContext_struct* src)
887 {
888     if (src != NULL) {
889 
890         // Copy all linked list
891         DupPluginTransformList(ctx, src);
892     }
893     else {
894         static _cmsTransformPluginChunkType TransformPluginChunkType = { NULL };
895         ctx ->chunks[TransformPlugin] = _cmsSubAllocDup(ctx ->MemPool, &TransformPluginChunkType, sizeof(_cmsTransformPluginChunkType));
896     }
897 }
898 
899 // Adaptor for old versions of plug-in
900 static
_cmsTransform2toTransformAdaptor(cmsContext ContextID,struct _cmstransform_struct * CMMcargo,const void * InputBuffer,void * OutputBuffer,cmsUInt32Number PixelsPerLine,cmsUInt32Number LineCount,const cmsStride * Stride)901 void _cmsTransform2toTransformAdaptor(cmsContext ContextID, struct _cmstransform_struct *CMMcargo,
902                                       const void* InputBuffer,
903                                       void* OutputBuffer,
904                                       cmsUInt32Number PixelsPerLine,
905                                       cmsUInt32Number LineCount,
906                                       const cmsStride* Stride)
907 {
908 
909        cmsUInt32Number i, strideIn, strideOut;
910 
911        _cmsHandleExtraChannels(ContextID, CMMcargo, InputBuffer, OutputBuffer, PixelsPerLine, LineCount, Stride);
912 
913        strideIn = 0;
914        strideOut = 0;
915 
916        for (i = 0; i < LineCount; i++) {
917 
918               void *accum = (cmsUInt8Number*)InputBuffer + strideIn;
919               void *output = (cmsUInt8Number*)OutputBuffer + strideOut;
920 
921               CMMcargo->OldXform(ContextID, CMMcargo, accum, output, PixelsPerLine, Stride->BytesPerPlaneIn);
922 
923               strideIn += Stride->BytesPerLineIn;
924               strideOut += Stride->BytesPerLineOut;
925        }
926 }
927 
928 
929 
930 // Register new ways to transform
_cmsRegisterTransformPlugin(cmsContext ContextID,cmsPluginBase * Data)931 cmsBool  _cmsRegisterTransformPlugin(cmsContext ContextID, cmsPluginBase* Data)
932 {
933     cmsPluginTransform* Plugin = (cmsPluginTransform*) Data;
934     _cmsTransformCollection* fl;
935     _cmsTransformPluginChunkType* ctx = ( _cmsTransformPluginChunkType*) _cmsContextGetClientChunk(ContextID,TransformPlugin);
936 
937     if (Data == NULL) {
938 
939         // Free the chain. Memory is safely freed at exit
940         ctx->TransformCollection = NULL;
941         return TRUE;
942     }
943 
944     // Factory callback is required
945     if (Plugin->factories.xform == NULL) return FALSE;
946 
947 
948     fl = (_cmsTransformCollection*) _cmsPluginMalloc(ContextID, sizeof(_cmsTransformCollection));
949     if (fl == NULL) return FALSE;
950 
951     // Check for full xform plug-ins previous to 2.8, we would need an adapter in that case
952     if (Plugin->base.ExpectedVersion < 80) {
953 
954            fl->OldXform = TRUE;
955     }
956     else
957            fl->OldXform = FALSE;
958 
959     // Copy the parameters
960     fl->Factory = Plugin->factories.xform;
961 
962     // Keep linked list
963     fl ->Next = ctx->TransformCollection;
964     ctx->TransformCollection = fl;
965 
966     // All is ok
967     return TRUE;
968 }
969 
970 
_cmsSetTransformUserData(struct _cmstransform_struct * CMMcargo,void * ptr,_cmsFreeUserDataFn FreePrivateDataFn)971 void CMSEXPORT _cmsSetTransformUserData(struct _cmstransform_struct *CMMcargo, void* ptr, _cmsFreeUserDataFn FreePrivateDataFn)
972 {
973     _cmsAssert(CMMcargo != NULL && CMMcargo->core != NULL);
974     CMMcargo->core->UserData = ptr;
975     CMMcargo->core->FreeUserData = FreePrivateDataFn;
976 }
977 
978 // returns the pointer defined by the plug-in to store private data
_cmsGetTransformUserData(struct _cmstransform_struct * CMMcargo)979 void * CMSEXPORT _cmsGetTransformUserData(struct _cmstransform_struct *CMMcargo)
980 {
981     _cmsAssert(CMMcargo != NULL && CMMcargo->core != NULL);
982     return CMMcargo->core->UserData;
983 }
984 
985 // returns the current formatters
_cmsGetTransformFormatters16(struct _cmstransform_struct * CMMcargo,cmsFormatter16 * FromInput,cmsFormatter16 * ToOutput)986 void CMSEXPORT _cmsGetTransformFormatters16(struct _cmstransform_struct *CMMcargo, cmsFormatter16* FromInput, cmsFormatter16* ToOutput)
987 {
988      _cmsAssert(CMMcargo != NULL);
989      if (FromInput) *FromInput = CMMcargo ->FromInput;
990      if (ToOutput)  *ToOutput  = CMMcargo ->ToOutput;
991 }
992 
_cmsGetTransformFormattersFloat(struct _cmstransform_struct * CMMcargo,cmsFormatterFloat * FromInput,cmsFormatterFloat * ToOutput)993 void CMSEXPORT _cmsGetTransformFormattersFloat(struct _cmstransform_struct *CMMcargo, cmsFormatterFloat* FromInput, cmsFormatterFloat* ToOutput)
994 {
995      _cmsAssert(CMMcargo != NULL);
996      if (FromInput) *FromInput = CMMcargo ->FromInputFloat;
997      if (ToOutput)  *ToOutput  = CMMcargo ->ToOutputFloat;
998 }
999 
1000 
1001 void
_cmsFindFormatter(_cmsTRANSFORM * p,cmsUInt32Number InputFormat,cmsUInt32Number OutputFormat,cmsUInt32Number dwFlags)1002 _cmsFindFormatter(_cmsTRANSFORM* p, cmsUInt32Number InputFormat, cmsUInt32Number OutputFormat, cmsUInt32Number dwFlags)
1003 {
1004     if (dwFlags & cmsFLAGS_NULLTRANSFORM) {
1005         p ->xform = NullXFORM;
1006         return;
1007     }
1008     if (dwFlags & cmsFLAGS_NOCACHE) {
1009         if (dwFlags & cmsFLAGS_GAMUTCHECK)
1010             p ->xform = PrecalculatedXFORMGamutCheck;  // Gamut check, no cache
1011         else if ((InputFormat & ~COLORSPACE_SH(31)) == (OutputFormat & ~COLORSPACE_SH(31)) &&
1012                  _cmsLutIsIdentity(p->core->Lut)) {
1013             if (T_PLANAR(InputFormat))
1014                 p ->xform = PrecalculatedXFORMIdentityPlanar;
1015             else
1016                 p ->xform = PrecalculatedXFORMIdentity;
1017         } else
1018             p ->xform = PrecalculatedXFORM;  // No cache, no gamut check
1019 	return;
1020     }
1021     if (dwFlags & cmsFLAGS_GAMUTCHECK) {
1022         p ->xform = CachedXFORMGamutCheck;    // Gamut check, cache
1023 	return;
1024     }
1025     if ((InputFormat & ~COLORSPACE_SH(31)) == (OutputFormat & ~COLORSPACE_SH(31)) &&
1026         _cmsLutIsIdentity(p->core->Lut)) {
1027         /* No point in a cache here! */
1028         if (T_PLANAR(InputFormat))
1029             p ->xform = PrecalculatedXFORMIdentityPlanar;
1030         else
1031             p ->xform = PrecalculatedXFORMIdentity;
1032         return;
1033     }
1034     if (T_EXTRA(InputFormat) != 0) {
1035         p ->xform = CachedXFORM;  // No gamut check, cache
1036         return;
1037     }
1038     if ((InputFormat & ~(COLORSPACE_SH(31)|CHANNELS_SH(7)|BYTES_SH(3))) == 0 &&
1039         (OutputFormat & ~(COLORSPACE_SH(31)|CHANNELS_SH(7)|BYTES_SH(3))) == 0) {
1040         switch ((InputFormat & (CHANNELS_SH(7)|BYTES_SH(3)))|
1041                 ((OutputFormat & (CHANNELS_SH(7)|BYTES_SH(3)))<<6)) {
1042             case CHANNELS_SH(1) | BYTES_SH(1) | ((CHANNELS_SH(1) | BYTES_SH(1))<<6):
1043                 p->xform = CachedXFORM1to1;
1044                 return;
1045             case CHANNELS_SH(1) | BYTES_SH(2) | ((CHANNELS_SH(1) | BYTES_SH(2))<<6):
1046                 p->xform = CachedXFORM1x2to1x2;
1047                 return;
1048             case CHANNELS_SH(1) | BYTES_SH(1) | ((CHANNELS_SH(3) | BYTES_SH(1))<<6):
1049                 p->xform = CachedXFORM1to3;
1050                 return;
1051             case CHANNELS_SH(1) | BYTES_SH(2) | ((CHANNELS_SH(3) | BYTES_SH(2))<<6):
1052                 p->xform = CachedXFORM1x2to3x2;
1053                 return;
1054             case CHANNELS_SH(1) | BYTES_SH(1) | ((CHANNELS_SH(4) | BYTES_SH(1))<<6):
1055                 p->xform = CachedXFORM1to4;
1056                 return;
1057             case CHANNELS_SH(1) | BYTES_SH(2) | ((CHANNELS_SH(4) | BYTES_SH(2))<<6):
1058                 p->xform = CachedXFORM1x2to4x2;
1059                 return;
1060             case CHANNELS_SH(3) | BYTES_SH(1) | ((CHANNELS_SH(1) | BYTES_SH(1))<<6):
1061                 p ->xform = CachedXFORM3to1;
1062                 return;
1063             case CHANNELS_SH(3) | BYTES_SH(2) | ((CHANNELS_SH(1) | BYTES_SH(2))<<6):
1064                 p ->xform = CachedXFORM3x2to1x2;
1065                 return;
1066             case CHANNELS_SH(3) | BYTES_SH(1) | ((CHANNELS_SH(3) | BYTES_SH(1))<<6):
1067                 p->xform = CachedXFORM3to3;
1068                 return;
1069             case CHANNELS_SH(3) | BYTES_SH(2) | ((CHANNELS_SH(3) | BYTES_SH(2))<<6):
1070                 p->xform = CachedXFORM3x2to3x2;
1071                 return;
1072             case CHANNELS_SH(3) | BYTES_SH(1) | ((CHANNELS_SH(4) | BYTES_SH(1))<<6):
1073                 p->xform = CachedXFORM3to4;
1074                 return;
1075             case CHANNELS_SH(3) | BYTES_SH(2) | ((CHANNELS_SH(4) | BYTES_SH(2))<<6):
1076                 p->xform = CachedXFORM3x2to4x2;
1077                 return;
1078             case CHANNELS_SH(4) | BYTES_SH(1) | ((CHANNELS_SH(1) | BYTES_SH(1))<<6):
1079                 p->xform = CachedXFORM4to1;
1080                 return;
1081             case CHANNELS_SH(4) | BYTES_SH(2) | ((CHANNELS_SH(1) | BYTES_SH(2))<<6):
1082                 p->xform = CachedXFORM4x2to1x2;
1083                 return;
1084             case CHANNELS_SH(4) | BYTES_SH(1) | ((CHANNELS_SH(3) | BYTES_SH(1))<<6):
1085                 p->xform = CachedXFORM4to3;
1086                 return;
1087             case CHANNELS_SH(4) | BYTES_SH(2) | ((CHANNELS_SH(3) | BYTES_SH(2))<<6):
1088                 p->xform = CachedXFORM4x2to3x2;
1089                 return;
1090             case CHANNELS_SH(4) | BYTES_SH(1) | ((CHANNELS_SH(4) | BYTES_SH(1))<<6):
1091                 p->xform = CachedXFORM4to4;
1092                 return;
1093             case CHANNELS_SH(4) | BYTES_SH(2) | ((CHANNELS_SH(4) | BYTES_SH(2))<<6):
1094                 p->xform = CachedXFORM4x2to4x2;
1095                 return;
1096         }
1097     }
1098     {
1099         int inwords = T_CHANNELS(InputFormat);
1100         if (inwords <= 2)
1101             p ->xform = CachedXFORM4;
1102         else if (inwords <= 4)
1103             p ->xform = CachedXFORM8;
1104         else
1105             p ->xform = CachedXFORM;  // No gamut check, cache
1106     }
1107 }
1108 
1109 // Allocate transform struct and set it to defaults. Ask the optimization plug-in about if those formats are proper
1110 // for separated transforms. If this is the case,
1111 static
AllocEmptyTransform(cmsContext ContextID,cmsPipeline * lut,cmsUInt32Number Intent,cmsUInt32Number * InputFormat,cmsUInt32Number * OutputFormat,cmsUInt32Number * dwFlags)1112 _cmsTRANSFORM* AllocEmptyTransform(cmsContext ContextID, cmsPipeline* lut,
1113                                                cmsUInt32Number Intent, cmsUInt32Number* InputFormat, cmsUInt32Number* OutputFormat, cmsUInt32Number* dwFlags)
1114 {
1115     _cmsTransformPluginChunkType* ctx = ( _cmsTransformPluginChunkType*) _cmsContextGetClientChunk(ContextID, TransformPlugin);
1116     _cmsTransformCollection* Plugin;
1117     _cmsTRANSFORMCORE *core;
1118 
1119     // Allocate needed memory
1120     _cmsTRANSFORM* p = (_cmsTRANSFORM*)_cmsMallocZero(ContextID, sizeof(_cmsTRANSFORM));
1121     if (!p) {
1122         cmsPipelineFree(ContextID, lut);
1123         return NULL;
1124     }
1125 
1126     core = (_cmsTRANSFORMCORE*)_cmsMallocZero(ContextID, sizeof(*core));
1127     if (!core) {
1128         _cmsFree(ContextID, p);
1129         cmsPipelineFree(ContextID, lut);
1130         return NULL;
1131     }
1132 
1133     p->core = core;
1134     core->refs = 1;
1135     // Store the proposed pipeline
1136     p->core->Lut = lut;
1137 
1138        // Let's see if any plug-in want to do the transform by itself
1139        if (core->Lut != NULL) {
1140 
1141            // First, optimise the pipeline. This may cause us to recognise that the Luts are
1142            // identity.
1143            _cmsOptimizePipeline(ContextID, &core->Lut, Intent, InputFormat, OutputFormat, dwFlags);
1144 
1145            if (_cmsLutIsIdentity(core->Lut) == FALSE) {
1146               for (Plugin = ctx->TransformCollection;
1147                      Plugin != NULL;
1148                      Plugin = Plugin->Next) {
1149 
1150                      if (Plugin->Factory(ContextID, &p->xform, &core->UserData, &core->FreeUserData, &core->Lut, InputFormat, OutputFormat, dwFlags)) {
1151 
1152                             // Last plugin in the declaration order takes control. We just keep
1153                             // the original parameters as a logging.
1154                             // Note that cmsFLAGS_CAN_CHANGE_FORMATTER is not set, so by default
1155                             // an optimized transform is not reusable. The plug-in can, however, change
1156                             // the flags and make it suitable.
1157 
1158                             p->InputFormat = *InputFormat;
1159                             p->OutputFormat = *OutputFormat;
1160                             core->dwOriginalFlags = *dwFlags;
1161 
1162                             // Fill the formatters just in case the optimized routine is interested.
1163                             // No error is thrown if the formatter doesn't exist. It is up to the optimization
1164                             // factory to decide what to do in those cases.
1165                             p->FromInput = _cmsGetFormatter(ContextID, *InputFormat, cmsFormatterInput, CMS_PACK_FLAGS_16BITS).Fmt16;
1166                             p->ToOutput = _cmsGetFormatter(ContextID, *OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_16BITS).Fmt16;
1167                             p->FromInputFloat = _cmsGetFormatter(ContextID, *InputFormat, cmsFormatterInput, CMS_PACK_FLAGS_FLOAT).FmtFloat;
1168                             p->ToOutputFloat = _cmsGetFormatter(ContextID, *OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_FLOAT).FmtFloat;
1169 
1170                             // Save the day? (Ignore the warning)
1171                             if (Plugin->OldXform) {
1172                                    p->OldXform = (_cmsTransformFn)(void*) p->xform;
1173                                    p->xform = _cmsTransform2toTransformAdaptor;
1174                             }
1175                             return p;
1176                      }
1177               }
1178           }
1179 
1180        }
1181 
1182     // Check whatever this is a true floating point transform
1183     if (_cmsFormatterIsFloat(*InputFormat) && _cmsFormatterIsFloat(*OutputFormat)) {
1184 
1185         // Get formatter function always return a valid union, but the contents of this union may be NULL.
1186         p ->FromInputFloat = _cmsGetFormatter(ContextID, *InputFormat,  cmsFormatterInput, CMS_PACK_FLAGS_FLOAT).FmtFloat;
1187         p ->ToOutputFloat  = _cmsGetFormatter(ContextID, *OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_FLOAT).FmtFloat;
1188         *dwFlags |= cmsFLAGS_CAN_CHANGE_FORMATTER;
1189 
1190         if (p ->FromInputFloat == NULL || p ->ToOutputFloat == NULL) {
1191 
1192             cmsSignalError(ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported raster format");
1193             cmsDeleteTransform(ContextID, p);
1194             return NULL;
1195         }
1196 
1197         if (*dwFlags & cmsFLAGS_NULLTRANSFORM) {
1198 
1199             p ->xform = NullFloatXFORM;
1200         }
1201         else {
1202             // Float transforms don't use cache, always are non-NULL
1203             p ->xform = FloatXFORM;
1204         }
1205 
1206     }
1207     else {
1208 
1209         if (*InputFormat == 0 && *OutputFormat == 0) {
1210             p ->FromInput = p ->ToOutput = NULL;
1211             *dwFlags |= cmsFLAGS_CAN_CHANGE_FORMATTER;
1212         }
1213         else {
1214 
1215             cmsUInt32Number BytesPerPixelInput;
1216 
1217             p ->FromInput = _cmsGetFormatter(ContextID, *InputFormat,  cmsFormatterInput, CMS_PACK_FLAGS_16BITS).Fmt16;
1218             p ->ToOutput  = _cmsGetFormatter(ContextID, *OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_16BITS).Fmt16;
1219 
1220             if (p ->FromInput == NULL || p ->ToOutput == NULL) {
1221 
1222                 cmsSignalError(ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported raster format");
1223                 cmsDeleteTransform(ContextID, p);
1224                 return NULL;
1225             }
1226 
1227             BytesPerPixelInput = T_BYTES(p ->InputFormat);
1228             if (BytesPerPixelInput == 0 || BytesPerPixelInput >= 2)
1229                    *dwFlags |= cmsFLAGS_CAN_CHANGE_FORMATTER;
1230 
1231         }
1232 
1233         _cmsFindFormatter(p, *InputFormat, *OutputFormat, *dwFlags);
1234     }
1235 
1236     p ->InputFormat     = *InputFormat;
1237     p ->OutputFormat    = *OutputFormat;
1238     core->dwOriginalFlags = *dwFlags;
1239     core->UserData        = NULL;
1240     return p;
1241 }
1242 
1243 static
GetXFormColorSpaces(cmsContext ContextID,cmsUInt32Number nProfiles,cmsHPROFILE hProfiles[],cmsColorSpaceSignature * Input,cmsColorSpaceSignature * Output)1244 cmsBool GetXFormColorSpaces(cmsContext ContextID, cmsUInt32Number nProfiles, cmsHPROFILE hProfiles[], cmsColorSpaceSignature* Input, cmsColorSpaceSignature* Output)
1245 {
1246     cmsColorSpaceSignature ColorSpaceIn, ColorSpaceOut;
1247     cmsColorSpaceSignature PostColorSpace;
1248     cmsUInt32Number i;
1249 
1250     if (nProfiles == 0) return FALSE;
1251     if (hProfiles[0] == NULL) return FALSE;
1252 
1253     *Input = PostColorSpace = cmsGetColorSpace(ContextID, hProfiles[0]);
1254 
1255     for (i=0; i < nProfiles; i++) {
1256 
1257         cmsProfileClassSignature cls;
1258         cmsHPROFILE hProfile = hProfiles[i];
1259 
1260         int lIsInput = (PostColorSpace != cmsSigXYZData) &&
1261                        (PostColorSpace != cmsSigLabData);
1262 
1263         if (hProfile == NULL) return FALSE;
1264 
1265         cls = cmsGetDeviceClass(ContextID, hProfile);
1266 
1267         if (cls == cmsSigNamedColorClass) {
1268 
1269             ColorSpaceIn    = cmsSig1colorData;
1270             ColorSpaceOut   = (nProfiles > 1) ? cmsGetPCS(ContextID, hProfile) : cmsGetColorSpace(ContextID, hProfile);
1271         }
1272         else
1273         if (lIsInput || (cls == cmsSigLinkClass)) {
1274 
1275             ColorSpaceIn    = cmsGetColorSpace(ContextID, hProfile);
1276             ColorSpaceOut   = cmsGetPCS(ContextID, hProfile);
1277         }
1278         else
1279         {
1280             ColorSpaceIn    = cmsGetPCS(ContextID, hProfile);
1281             ColorSpaceOut   = cmsGetColorSpace(ContextID, hProfile);
1282         }
1283 
1284         if (i==0)
1285             *Input = ColorSpaceIn;
1286 
1287         PostColorSpace = ColorSpaceOut;
1288     }
1289 
1290     *Output = PostColorSpace;
1291 
1292     return TRUE;
1293 }
1294 
1295 // Check colorspace
1296 static
IsProperColorSpace(cmsContext ContextID,cmsColorSpaceSignature Check,cmsUInt32Number dwFormat)1297 cmsBool  IsProperColorSpace(cmsContext ContextID, cmsColorSpaceSignature Check, cmsUInt32Number dwFormat)
1298 {
1299     int Space1 = (int) T_COLORSPACE(dwFormat);
1300     int Space2 = _cmsLCMScolorSpace(ContextID, Check);
1301 
1302     if (Space1 == PT_ANY) return TRUE;
1303     if (Space1 == Space2) return TRUE;
1304 
1305     if (Space1 == PT_LabV2 && Space2 == PT_Lab) return TRUE;
1306     if (Space1 == PT_Lab   && Space2 == PT_LabV2) return TRUE;
1307 
1308     return FALSE;
1309 }
1310 
1311 // ----------------------------------------------------------------------------------------------------------------
1312 
1313 // Jun-21-2000: Some profiles (those that comes with W2K) comes
1314 // with the media white (media black?) x 100. Add a sanity check
1315 
1316 static
NormalizeXYZ(cmsCIEXYZ * Dest)1317 void NormalizeXYZ(cmsCIEXYZ* Dest)
1318 {
1319     while (Dest -> X > 2. &&
1320            Dest -> Y > 2. &&
1321            Dest -> Z > 2.) {
1322 
1323                Dest -> X /= 10.;
1324                Dest -> Y /= 10.;
1325                Dest -> Z /= 10.;
1326        }
1327 }
1328 
1329 static
SetWhitePoint(cmsCIEXYZ * wtPt,const cmsCIEXYZ * src)1330 void SetWhitePoint(cmsCIEXYZ* wtPt, const cmsCIEXYZ* src)
1331 {
1332     if (src == NULL) {
1333         wtPt ->X = cmsD50X;
1334         wtPt ->Y = cmsD50Y;
1335         wtPt ->Z = cmsD50Z;
1336     }
1337     else {
1338         wtPt ->X = src->X;
1339         wtPt ->Y = src->Y;
1340         wtPt ->Z = src->Z;
1341 
1342         NormalizeXYZ(wtPt);
1343     }
1344 
1345 }
1346 
1347 // 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)1348 cmsHTRANSFORM CMSEXPORT cmsCreateExtendedTransform(cmsContext ContextID,
1349                                                    cmsUInt32Number nProfiles, cmsHPROFILE hProfiles[],
1350                                                    cmsBool  BPC[],
1351                                                    cmsUInt32Number Intents[],
1352                                                    cmsFloat64Number AdaptationStates[],
1353                                                    cmsHPROFILE hGamutProfile,
1354                                                    cmsUInt32Number nGamutPCSposition,
1355                                                    cmsUInt32Number InputFormat,
1356                                                    cmsUInt32Number OutputFormat,
1357                                                    cmsUInt32Number dwFlags)
1358 {
1359     _cmsTRANSFORM* xform;
1360     cmsColorSpaceSignature EntryColorSpace;
1361     cmsColorSpaceSignature ExitColorSpace;
1362     cmsPipeline* Lut;
1363     cmsUInt32Number LastIntent = Intents[nProfiles-1];
1364 
1365     // If it is a fake transform
1366     if (dwFlags & cmsFLAGS_NULLTRANSFORM)
1367     {
1368         return AllocEmptyTransform(ContextID, NULL, INTENT_PERCEPTUAL, &InputFormat, &OutputFormat, &dwFlags);
1369     }
1370 
1371     // If gamut check is requested, make sure we have a gamut profile
1372     if (dwFlags & cmsFLAGS_GAMUTCHECK) {
1373         if (hGamutProfile == NULL) dwFlags &= ~cmsFLAGS_GAMUTCHECK;
1374     }
1375 
1376     // On floating point transforms, inhibit cache
1377     if (_cmsFormatterIsFloat(InputFormat) || _cmsFormatterIsFloat(OutputFormat))
1378         dwFlags |= cmsFLAGS_NOCACHE;
1379 
1380     // Mark entry/exit spaces
1381     if (!GetXFormColorSpaces(ContextID, nProfiles, hProfiles, &EntryColorSpace, &ExitColorSpace)) {
1382         cmsSignalError(ContextID, cmsERROR_NULL, "NULL input profiles on transform");
1383         return NULL;
1384     }
1385 
1386     // Check if proper colorspaces
1387     if (!IsProperColorSpace(ContextID, EntryColorSpace, InputFormat)) {
1388         cmsSignalError(ContextID, cmsERROR_COLORSPACE_CHECK, "Wrong input color space on transform");
1389         return NULL;
1390     }
1391 
1392     if (!IsProperColorSpace(ContextID, ExitColorSpace, OutputFormat)) {
1393         cmsSignalError(ContextID, cmsERROR_COLORSPACE_CHECK, "Wrong output color space on transform");
1394         return NULL;
1395     }
1396 
1397     // Create a pipeline with all transformations
1398     Lut = _cmsLinkProfiles(ContextID, nProfiles, Intents, hProfiles, BPC, AdaptationStates, dwFlags);
1399     if (Lut == NULL) {
1400         cmsSignalError(ContextID, cmsERROR_NOT_SUITABLE, "Couldn't link the profiles");
1401         return NULL;
1402     }
1403 
1404     // Check channel count
1405     if ((cmsChannelsOf(ContextID, EntryColorSpace) != cmsPipelineInputChannels(ContextID, Lut)) ||
1406         (cmsChannelsOf(ContextID, ExitColorSpace)  != cmsPipelineOutputChannels(ContextID, Lut))) {
1407         cmsPipelineFree(ContextID, Lut);
1408         cmsSignalError(ContextID, cmsERROR_NOT_SUITABLE, "Channel count doesn't match. Profile is corrupted");
1409         return NULL;
1410     }
1411 
1412 
1413     // All seems ok
1414     xform = AllocEmptyTransform(ContextID, Lut, LastIntent, &InputFormat, &OutputFormat, &dwFlags);
1415     if (xform == NULL) {
1416         return NULL;
1417     }
1418 
1419     // Keep values
1420     xform->core->EntryColorSpace = EntryColorSpace;
1421     xform->core->ExitColorSpace  = ExitColorSpace;
1422     xform->core->RenderingIntent = Intents[nProfiles-1];
1423 
1424     // Take white points
1425     SetWhitePoint(&xform->core->EntryWhitePoint, (cmsCIEXYZ*) cmsReadTag(ContextID, hProfiles[0], cmsSigMediaWhitePointTag));
1426     SetWhitePoint(&xform->core->ExitWhitePoint,  (cmsCIEXYZ*) cmsReadTag(ContextID, hProfiles[nProfiles-1], cmsSigMediaWhitePointTag));
1427 
1428 
1429     // Create a gamut check LUT if requested
1430     if (hGamutProfile != NULL && (dwFlags & cmsFLAGS_GAMUTCHECK))
1431         xform->core->GamutCheck  = _cmsCreateGamutCheckPipeline(ContextID, hProfiles,
1432                                                         BPC, Intents,
1433                                                         AdaptationStates,
1434                                                         nGamutPCSposition,
1435                                                         hGamutProfile);
1436 
1437 
1438     // Try to read input and output colorant table
1439     if (cmsIsTag(ContextID, hProfiles[0], cmsSigColorantTableTag)) {
1440 
1441         // Input table can only come in this way.
1442         xform->core->InputColorant = cmsDupNamedColorList(ContextID, (cmsNAMEDCOLORLIST*) cmsReadTag(ContextID, hProfiles[0], cmsSigColorantTableTag));
1443     }
1444 
1445     // Output is a little bit more complex.
1446     if (cmsGetDeviceClass(ContextID, hProfiles[nProfiles-1]) == cmsSigLinkClass) {
1447 
1448         // This tag may exist only on devicelink profiles.
1449         if (cmsIsTag(ContextID, hProfiles[nProfiles-1], cmsSigColorantTableOutTag)) {
1450 
1451             // It may be NULL if error
1452             xform->core->OutputColorant = cmsDupNamedColorList(ContextID, (cmsNAMEDCOLORLIST*) cmsReadTag(ContextID, hProfiles[nProfiles-1], cmsSigColorantTableOutTag));
1453         }
1454 
1455     } else {
1456 
1457         if (cmsIsTag(ContextID, hProfiles[nProfiles-1], cmsSigColorantTableTag)) {
1458 
1459             xform->core->OutputColorant = cmsDupNamedColorList(ContextID, (cmsNAMEDCOLORLIST*) cmsReadTag(ContextID, hProfiles[nProfiles-1], cmsSigColorantTableTag));
1460         }
1461     }
1462 
1463     // Store the sequence of profiles
1464     if (dwFlags & cmsFLAGS_KEEP_SEQUENCE) {
1465         xform->core->Sequence = _cmsCompileProfileSequence(ContextID, nProfiles, hProfiles);
1466     }
1467     else
1468         xform->core->Sequence = NULL;
1469 
1470     // If this is a cached transform, init first value, which is zero (16 bits only)
1471     if (!(dwFlags & cmsFLAGS_NOCACHE)) {
1472 
1473         memset(&xform ->Cache.CacheIn, 0, sizeof(xform ->Cache.CacheIn));
1474 
1475         if (xform->core->GamutCheck != NULL) {
1476             TransformOnePixelWithGamutCheck(ContextID, xform, xform->Cache.CacheIn, xform->Cache.CacheOut);
1477         }
1478         else {
1479 
1480             xform->core->Lut->Eval16Fn(ContextID, xform ->Cache.CacheIn, xform->Cache.CacheOut, xform->core->Lut->Data);
1481         }
1482 
1483     }
1484 
1485     return (cmsHTRANSFORM) xform;
1486 }
1487 
1488 // Multiprofile transforms: Gamut check is not available here, as it is unclear from which profile the gamut comes.
cmsCreateMultiprofileTransform(cmsContext ContextID,cmsHPROFILE hProfiles[],cmsUInt32Number nProfiles,cmsUInt32Number InputFormat,cmsUInt32Number OutputFormat,cmsUInt32Number Intent,cmsUInt32Number dwFlags)1489 cmsHTRANSFORM CMSEXPORT cmsCreateMultiprofileTransform(cmsContext ContextID,
1490                                                        cmsHPROFILE hProfiles[],
1491                                                        cmsUInt32Number nProfiles,
1492                                                        cmsUInt32Number InputFormat,
1493                                                        cmsUInt32Number OutputFormat,
1494                                                        cmsUInt32Number Intent,
1495                                                        cmsUInt32Number dwFlags)
1496 {
1497     cmsUInt32Number i;
1498     cmsBool BPC[256];
1499     cmsUInt32Number Intents[256];
1500     cmsFloat64Number AdaptationStates[256];
1501 
1502     if (nProfiles <= 0 || nProfiles > 255) {
1503          cmsSignalError(ContextID, cmsERROR_RANGE, "Wrong number of profiles. 1..255 expected, %d found.", nProfiles);
1504         return NULL;
1505     }
1506 
1507     for (i=0; i < nProfiles; i++) {
1508         BPC[i] = dwFlags & cmsFLAGS_BLACKPOINTCOMPENSATION ? TRUE : FALSE;
1509         Intents[i] = Intent;
1510         AdaptationStates[i] = cmsSetAdaptationState(ContextID, -1);
1511     }
1512 
1513 
1514     return cmsCreateExtendedTransform(ContextID, nProfiles, hProfiles, BPC, Intents, AdaptationStates, NULL, 0, InputFormat, OutputFormat, dwFlags);
1515 }
1516 
1517 
1518 
cmsCreateTransform(cmsContext ContextID,cmsHPROFILE Input,cmsUInt32Number InputFormat,cmsHPROFILE Output,cmsUInt32Number OutputFormat,cmsUInt32Number Intent,cmsUInt32Number dwFlags)1519 cmsHTRANSFORM CMSEXPORT cmsCreateTransform(cmsContext ContextID,
1520                                            cmsHPROFILE Input,
1521                                            cmsUInt32Number InputFormat,
1522                                            cmsHPROFILE Output,
1523                                            cmsUInt32Number OutputFormat,
1524                                            cmsUInt32Number Intent,
1525                                            cmsUInt32Number dwFlags)
1526 {
1527 
1528     cmsHPROFILE hArray[2];
1529 
1530     hArray[0] = Input;
1531     hArray[1] = Output;
1532 
1533     return cmsCreateMultiprofileTransform(ContextID, hArray, Output == NULL ? 1U : 2U, InputFormat, OutputFormat, Intent, dwFlags);
1534 }
1535 
1536 
cmsCreateProofingTransform(cmsContext ContextID,cmsHPROFILE InputProfile,cmsUInt32Number InputFormat,cmsHPROFILE OutputProfile,cmsUInt32Number OutputFormat,cmsHPROFILE ProofingProfile,cmsUInt32Number nIntent,cmsUInt32Number ProofingIntent,cmsUInt32Number dwFlags)1537 cmsHTRANSFORM CMSEXPORT cmsCreateProofingTransform(cmsContext ContextID,
1538                                                    cmsHPROFILE InputProfile,
1539                                                    cmsUInt32Number InputFormat,
1540                                                    cmsHPROFILE OutputProfile,
1541                                                    cmsUInt32Number OutputFormat,
1542                                                    cmsHPROFILE ProofingProfile,
1543                                                    cmsUInt32Number nIntent,
1544                                                    cmsUInt32Number ProofingIntent,
1545                                                    cmsUInt32Number dwFlags)
1546 {
1547     cmsHPROFILE hArray[4];
1548     cmsUInt32Number Intents[4];
1549     cmsBool  BPC[4];
1550     cmsFloat64Number Adaptation[4];
1551     cmsBool  DoBPC = (dwFlags & cmsFLAGS_BLACKPOINTCOMPENSATION) ? TRUE : FALSE;
1552 
1553 
1554     hArray[0]  = InputProfile; hArray[1] = ProofingProfile; hArray[2]  = ProofingProfile;               hArray[3] = OutputProfile;
1555     Intents[0] = nIntent;      Intents[1] = nIntent;        Intents[2] = INTENT_RELATIVE_COLORIMETRIC;  Intents[3] = ProofingIntent;
1556     BPC[0]     = DoBPC;        BPC[1] = DoBPC;              BPC[2] = 0;                                 BPC[3] = 0;
1557 
1558     Adaptation[0] = Adaptation[1] = Adaptation[2] = Adaptation[3] = cmsSetAdaptationState(ContextID, -1);
1559 
1560     if (!(dwFlags & (cmsFLAGS_SOFTPROOFING|cmsFLAGS_GAMUTCHECK)))
1561         return cmsCreateTransform(ContextID, InputProfile, InputFormat, OutputProfile, OutputFormat, nIntent, dwFlags);
1562 
1563     return cmsCreateExtendedTransform(ContextID, 4, hArray, BPC, Intents, Adaptation,
1564                                         ProofingProfile, 1, InputFormat, OutputFormat, dwFlags);
1565 
1566 }
1567 
1568 
1569 
1570 // Grab the input/output formats
cmsGetTransformInputFormat(cmsContext ContextID,cmsHTRANSFORM hTransform)1571 cmsUInt32Number CMSEXPORT cmsGetTransformInputFormat(cmsContext ContextID, cmsHTRANSFORM hTransform)
1572 {
1573     _cmsTRANSFORM* xform = (_cmsTRANSFORM*) hTransform;
1574     cmsUNUSED_PARAMETER(ContextID);
1575 
1576     if (xform == NULL) return 0;
1577     return xform->InputFormat;
1578 }
1579 
cmsGetTransformOutputFormat(cmsContext ContextID,cmsHTRANSFORM hTransform)1580 cmsUInt32Number CMSEXPORT cmsGetTransformOutputFormat(cmsContext ContextID, cmsHTRANSFORM hTransform)
1581 {
1582     _cmsTRANSFORM* xform = (_cmsTRANSFORM*) hTransform;
1583     cmsUNUSED_PARAMETER(ContextID);
1584 
1585     if (xform == NULL) return 0;
1586     return xform->OutputFormat;
1587 }
1588 
cmsCloneTransformChangingFormats(cmsContext ContextID,const cmsHTRANSFORM hTransform,cmsUInt32Number InputFormat,cmsUInt32Number OutputFormat)1589 cmsHTRANSFORM cmsCloneTransformChangingFormats(cmsContext ContextID,
1590                                                const cmsHTRANSFORM hTransform,
1591                                                cmsUInt32Number InputFormat,
1592                                                cmsUInt32Number OutputFormat)
1593 {
1594     const _cmsTRANSFORM *oldXform = (const _cmsTRANSFORM *)hTransform;
1595     _cmsTRANSFORM *xform;
1596     cmsFormatter16 FromInput, ToOutput;
1597     _cmsTransformPluginChunkType* ctx = (_cmsTransformPluginChunkType*)_cmsContextGetClientChunk(ContextID, TransformPlugin);
1598     _cmsTransformCollection* Plugin;
1599     _cmsTRANSFORMCORE *core;
1600 
1601     _cmsAssert(oldXform != NULL && oldXform->core != NULL);
1602 
1603     // We only can afford to change formatters if previous transform is at least 16 bits
1604     if (!(oldXform->core->dwOriginalFlags & cmsFLAGS_CAN_CHANGE_FORMATTER)) {
1605         cmsSignalError(ContextID, cmsERROR_NOT_SUITABLE, "cmsCloneTransformChangingFormats works only on transforms created originally with at least 16 bits of precision");
1606         return NULL;
1607     }
1608 
1609     xform = _cmsMalloc(ContextID, sizeof(*xform));
1610     if (xform == NULL)
1611         return NULL;
1612 
1613     memcpy(xform, oldXform, sizeof(*xform));
1614 
1615     FromInput = _cmsGetFormatter(ContextID, InputFormat,  cmsFormatterInput, CMS_PACK_FLAGS_16BITS).Fmt16;
1616     ToOutput  = _cmsGetFormatter(ContextID, OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_16BITS).Fmt16;
1617 
1618     if (FromInput == NULL || ToOutput == NULL) {
1619 
1620         cmsSignalError(ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported raster format");
1621         return NULL;
1622     }
1623 
1624     xform ->InputFormat  = InputFormat;
1625     xform ->OutputFormat = OutputFormat;
1626     xform ->FromInput    = FromInput;
1627     xform ->ToOutput     = ToOutput;
1628 
1629     /* Transformation plug-in support needed here but only if lcms has determined
1630        that this lut is not the identity transform */
1631     if (oldXform->core->Lut != NULL && _cmsLutIsIdentity(oldXform->core->Lut) == FALSE) {
1632         for (Plugin = ctx->TransformCollection; Plugin != NULL; Plugin = Plugin->Next) {
1633             core = xform->core;
1634             if (Plugin->Factory(ContextID, &xform->xform, &core->UserData,
1635                 &core->FreeUserData, &core->Lut, &InputFormat, &OutputFormat, NULL)) {
1636                 (void)_cmsAdjustReferenceCount(&xform->core->refs, 1);
1637                 return xform;
1638             }
1639         }
1640     }
1641 
1642     _cmsFindFormatter(xform, InputFormat, OutputFormat, xform->core->dwOriginalFlags);
1643 
1644     (void)_cmsAdjustReferenceCount(&xform->core->refs, 1);
1645 
1646     return xform;
1647 }
1648