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