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