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