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