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