1 //---------------------------------------------------------------------------------
3 // Little Color Management System
4 // Copyright (c) 1998-2023 Marti Maria Saguer
6 // Permission is hereby granted, free of charge, to any person obtaining
7 // a copy of this software and associated documentation files (the "Software"),
8 // to deal in the Software without restriction, including without limitation
9 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 // and/or sell copies of the Software, and to permit persons to whom the Software
11 // is furnished to do so, subject to the following conditions:
13 // The above copyright notice and this permission notice shall be included in
14 // all copies or substantial portions of the Software.
16 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
18 // THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 //---------------------------------------------------------------------------------
27 #include "lcms2_internal.h"
29 // Transformations stuff
30 // -----------------------------------------------------------------------
32 #define DEFAULT_OBSERVER_ADAPTATION_STATE 1.0
34 // The Context0 observer adaptation state.
35 _cmsAdaptationStateChunkType _cmsAdaptationStateChunk = { DEFAULT_OBSERVER_ADAPTATION_STATE };
37 // Init and duplicate observer adaptation state
38 void _cmsAllocAdaptationStateChunk(struct _cmsContext_struct* ctx,
39 const struct _cmsContext_struct* src)
41 static _cmsAdaptationStateChunkType AdaptationStateChunk = { DEFAULT_OBSERVER_ADAPTATION_STATE };
45 from = src ->chunks[AdaptationStateContext];
48 from = &AdaptationStateChunk;
51 ctx ->chunks[AdaptationStateContext] = _cmsSubAllocDup(ctx ->MemPool, from, sizeof(_cmsAdaptationStateChunkType));
55 // Sets adaptation state for absolute colorimetric intent in the given context. Adaptation state applies on all
56 // but cmsCreateExtendedTransformTHR(). Little CMS can handle incomplete adaptation states.
57 cmsFloat64Number CMSEXPORT cmsSetAdaptationStateTHR(cmsContext ContextID, cmsFloat64Number d)
59 cmsFloat64Number prev;
60 _cmsAdaptationStateChunkType* ptr = (_cmsAdaptationStateChunkType*) _cmsContextGetClientChunk(ContextID, AdaptationStateContext);
62 // Get previous value for return
63 prev = ptr ->AdaptationState;
65 // Set the value if d is positive or zero
68 ptr ->AdaptationState = d;
71 // Always return previous value
76 // The adaptation state may be defaulted by this function. If you don't like it, use the extended transform routine
77 cmsFloat64Number CMSEXPORT cmsSetAdaptationState(cmsFloat64Number d)
79 return cmsSetAdaptationStateTHR(NULL, d);
82 // -----------------------------------------------------------------------
84 // Alarm codes for 16-bit transformations, because the fixed range of containers there are
85 // no values left to mark out of gamut.
87 #define DEFAULT_ALARM_CODES_VALUE {0x7F00, 0x7F00, 0x7F00, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
89 _cmsAlarmCodesChunkType _cmsAlarmCodesChunk = { DEFAULT_ALARM_CODES_VALUE };
91 // Sets the codes used to mark out-out-gamut on Proofing transforms for a given context. Values are meant to be
92 // encoded in 16 bits.
93 void CMSEXPORT cmsSetAlarmCodesTHR(cmsContext ContextID, const cmsUInt16Number AlarmCodesP[cmsMAXCHANNELS])
95 _cmsAlarmCodesChunkType* ContextAlarmCodes = (_cmsAlarmCodesChunkType*) _cmsContextGetClientChunk(ContextID, AlarmCodesContext);
97 _cmsAssert(ContextAlarmCodes != NULL); // Can't happen
99 memcpy(ContextAlarmCodes->AlarmCodes, AlarmCodesP, sizeof(ContextAlarmCodes->AlarmCodes));
102 // Gets the current codes used to mark out-out-gamut on Proofing transforms for the given context.
103 // Values are meant to be encoded in 16 bits.
104 void CMSEXPORT cmsGetAlarmCodesTHR(cmsContext ContextID, cmsUInt16Number AlarmCodesP[cmsMAXCHANNELS])
106 _cmsAlarmCodesChunkType* ContextAlarmCodes = (_cmsAlarmCodesChunkType*) _cmsContextGetClientChunk(ContextID, AlarmCodesContext);
108 _cmsAssert(ContextAlarmCodes != NULL); // Can't happen
110 memcpy(AlarmCodesP, ContextAlarmCodes->AlarmCodes, sizeof(ContextAlarmCodes->AlarmCodes));
113 void CMSEXPORT cmsSetAlarmCodes(const cmsUInt16Number NewAlarm[cmsMAXCHANNELS])
115 _cmsAssert(NewAlarm != NULL);
117 cmsSetAlarmCodesTHR(NULL, NewAlarm);
120 void CMSEXPORT cmsGetAlarmCodes(cmsUInt16Number OldAlarm[cmsMAXCHANNELS])
122 _cmsAssert(OldAlarm != NULL);
123 cmsGetAlarmCodesTHR(NULL, OldAlarm);
127 // Init and duplicate alarm codes
128 void _cmsAllocAlarmCodesChunk(struct _cmsContext_struct* ctx,
129 const struct _cmsContext_struct* src)
131 static _cmsAlarmCodesChunkType AlarmCodesChunk = { DEFAULT_ALARM_CODES_VALUE };
135 from = src ->chunks[AlarmCodesContext];
138 from = &AlarmCodesChunk;
141 ctx ->chunks[AlarmCodesContext] = _cmsSubAllocDup(ctx ->MemPool, from, sizeof(_cmsAlarmCodesChunkType));
144 // -----------------------------------------------------------------------
146 // Get rid of transform resources
147 void CMSEXPORT cmsDeleteTransform(cmsHTRANSFORM hTransform)
149 _cmsTRANSFORM* p = (_cmsTRANSFORM*) hTransform;
151 _cmsAssert(p != NULL);
154 cmsPipelineFree(p -> GamutCheck);
157 cmsPipelineFree(p -> Lut);
159 if (p ->InputColorant)
160 cmsFreeNamedColorList(p ->InputColorant);
162 if (p -> OutputColorant)
163 cmsFreeNamedColorList(p ->OutputColorant);
166 cmsFreeProfileSequenceDescription(p ->Sequence);
169 p ->FreeUserData(p ->ContextID, p ->UserData);
171 _cmsFree(p ->ContextID, (void *) p);
176 cmsUInt32Number PixelSize(cmsUInt32Number Format)
178 cmsUInt32Number fmt_bytes = T_BYTES(Format);
180 // For double, the T_BYTES field is zero
182 return sizeof(cmsUInt64Number);
184 // Otherwise, it is already correct for all formats
192 void CMSEXPORT cmsDoTransform(cmsHTRANSFORM Transform,
193 const void* InputBuffer,
195 cmsUInt32Number Size)
198 _cmsTRANSFORM* p = (_cmsTRANSFORM*) Transform;
201 stride.BytesPerLineIn = 0; // Not used
202 stride.BytesPerLineOut = 0;
203 stride.BytesPerPlaneIn = Size * PixelSize(p->InputFormat);
204 stride.BytesPerPlaneOut = Size * PixelSize(p->OutputFormat);
206 p -> xform(p, InputBuffer, OutputBuffer, Size, 1, &stride);
210 // This is a legacy stride for planar
211 void CMSEXPORT cmsDoTransformStride(cmsHTRANSFORM Transform,
212 const void* InputBuffer,
214 cmsUInt32Number Size, cmsUInt32Number Stride)
217 _cmsTRANSFORM* p = (_cmsTRANSFORM*) Transform;
220 stride.BytesPerLineIn = 0;
221 stride.BytesPerLineOut = 0;
222 stride.BytesPerPlaneIn = Stride;
223 stride.BytesPerPlaneOut = Stride;
225 p -> xform(p, InputBuffer, OutputBuffer, Size, 1, &stride);
228 // This is the "fast" function for plugins
229 void CMSEXPORT cmsDoTransformLineStride(cmsHTRANSFORM Transform,
230 const void* InputBuffer,
232 cmsUInt32Number PixelsPerLine,
233 cmsUInt32Number LineCount,
234 cmsUInt32Number BytesPerLineIn,
235 cmsUInt32Number BytesPerLineOut,
236 cmsUInt32Number BytesPerPlaneIn,
237 cmsUInt32Number BytesPerPlaneOut)
240 _cmsTRANSFORM* p = (_cmsTRANSFORM*) Transform;
243 stride.BytesPerLineIn = BytesPerLineIn;
244 stride.BytesPerLineOut = BytesPerLineOut;
245 stride.BytesPerPlaneIn = BytesPerPlaneIn;
246 stride.BytesPerPlaneOut = BytesPerPlaneOut;
248 p->xform(p, InputBuffer, OutputBuffer, PixelsPerLine, LineCount, &stride);
253 // Transform routines ----------------------------------------------------------------------------------------------------------
255 // Float xform converts floats. Since there are no performance issues, one routine does all job, including gamut check.
256 // Note that because extended range, we can use a -1.0 value for out of gamut in this case.
258 void FloatXFORM(_cmsTRANSFORM* p,
261 cmsUInt32Number PixelsPerLine,
262 cmsUInt32Number LineCount,
263 const cmsStride* Stride)
265 cmsUInt8Number* accum;
266 cmsUInt8Number* output;
267 cmsFloat32Number fIn[cmsMAXCHANNELS], fOut[cmsMAXCHANNELS];
268 cmsFloat32Number OutOfGamut;
269 cmsUInt32Number i, j, c, strideIn, strideOut;
271 _cmsHandleExtraChannels(p, in, out, PixelsPerLine, LineCount, Stride);
275 memset(fIn, 0, sizeof(fIn));
276 memset(fOut, 0, sizeof(fOut));
278 for (i = 0; i < LineCount; i++) {
280 accum = (cmsUInt8Number*)in + strideIn;
281 output = (cmsUInt8Number*)out + strideOut;
283 for (j = 0; j < PixelsPerLine; j++) {
285 accum = p->FromInputFloat(p, fIn, accum, Stride->BytesPerPlaneIn);
287 // Any gamut check to do?
288 if (p->GamutCheck != NULL) {
290 // Evaluate gamut marker.
291 cmsPipelineEvalFloat(fIn, &OutOfGamut, p->GamutCheck);
293 // Is current color out of gamut?
294 if (OutOfGamut > 0.0) {
296 // Certainly, out of gamut
297 for (c = 0; c < cmsMAXCHANNELS; c++)
302 // No, proceed normally
303 cmsPipelineEvalFloat(fIn, fOut, p->Lut);
308 // No gamut check at all
309 cmsPipelineEvalFloat(fIn, fOut, p->Lut);
313 output = p->ToOutputFloat(p, fOut, output, Stride->BytesPerPlaneOut);
316 strideIn += Stride->BytesPerLineIn;
317 strideOut += Stride->BytesPerLineOut;
324 void NullFloatXFORM(_cmsTRANSFORM* p,
327 cmsUInt32Number PixelsPerLine,
328 cmsUInt32Number LineCount,
329 const cmsStride* Stride)
332 cmsUInt8Number* accum;
333 cmsUInt8Number* output;
334 cmsFloat32Number fIn[cmsMAXCHANNELS];
335 cmsUInt32Number i, j, strideIn, strideOut;
337 _cmsHandleExtraChannels(p, in, out, PixelsPerLine, LineCount, Stride);
341 memset(fIn, 0, sizeof(fIn));
343 for (i = 0; i < LineCount; i++) {
345 accum = (cmsUInt8Number*) in + strideIn;
346 output = (cmsUInt8Number*) out + strideOut;
348 for (j = 0; j < PixelsPerLine; j++) {
350 accum = p->FromInputFloat(p, fIn, accum, Stride ->BytesPerPlaneIn);
351 output = p->ToOutputFloat(p, fIn, output, Stride->BytesPerPlaneOut);
354 strideIn += Stride->BytesPerLineIn;
355 strideOut += Stride->BytesPerLineOut;
359 // 16 bit precision -----------------------------------------------------------------------------------------------------------
361 // Null transformation, only applies formatters. No cache
363 void NullXFORM(_cmsTRANSFORM* p,
366 cmsUInt32Number PixelsPerLine,
367 cmsUInt32Number LineCount,
368 const cmsStride* Stride)
370 cmsUInt8Number* accum;
371 cmsUInt8Number* output;
372 cmsUInt16Number wIn[cmsMAXCHANNELS];
373 cmsUInt32Number i, j, strideIn, strideOut;
375 _cmsHandleExtraChannels(p, in, out, PixelsPerLine, LineCount, Stride);
379 memset(wIn, 0, sizeof(wIn));
381 for (i = 0; i < LineCount; i++) {
383 accum = (cmsUInt8Number*)in + strideIn;
384 output = (cmsUInt8Number*)out + strideOut;
386 for (j = 0; j < PixelsPerLine; j++) {
388 accum = p->FromInput(p, wIn, accum, Stride->BytesPerPlaneIn);
389 output = p->ToOutput(p, wIn, output, Stride->BytesPerPlaneOut);
392 strideIn += Stride->BytesPerLineIn;
393 strideOut += Stride->BytesPerLineOut;
399 // No gamut check, no cache, 16 bits
401 void PrecalculatedXFORM(_cmsTRANSFORM* p,
404 cmsUInt32Number PixelsPerLine,
405 cmsUInt32Number LineCount,
406 const cmsStride* Stride)
408 CMSREGISTER cmsUInt8Number* accum;
409 CMSREGISTER cmsUInt8Number* output;
410 cmsUInt16Number wIn[cmsMAXCHANNELS], wOut[cmsMAXCHANNELS];
411 cmsUInt32Number i, j, strideIn, strideOut;
413 _cmsHandleExtraChannels(p, in, out, PixelsPerLine, LineCount, Stride);
417 memset(wIn, 0, sizeof(wIn));
418 memset(wOut, 0, sizeof(wOut));
420 for (i = 0; i < LineCount; i++) {
422 accum = (cmsUInt8Number*)in + strideIn;
423 output = (cmsUInt8Number*)out + strideOut;
425 for (j = 0; j < PixelsPerLine; j++) {
427 accum = p->FromInput(p, wIn, accum, Stride->BytesPerPlaneIn);
428 p->Lut->Eval16Fn(wIn, wOut, p->Lut->Data);
429 output = p->ToOutput(p, wOut, output, Stride->BytesPerPlaneOut);
432 strideIn += Stride->BytesPerLineIn;
433 strideOut += Stride->BytesPerLineOut;
439 // Auxiliary: Handle precalculated gamut check. The retrieval of context may be alittle bit slow, but this function is not critical.
441 void TransformOnePixelWithGamutCheck(_cmsTRANSFORM* p,
442 const cmsUInt16Number wIn[],
443 cmsUInt16Number wOut[])
445 cmsUInt16Number wOutOfGamut;
447 p ->GamutCheck ->Eval16Fn(wIn, &wOutOfGamut, p ->GamutCheck ->Data);
448 if (wOutOfGamut >= 1) {
451 _cmsAlarmCodesChunkType* ContextAlarmCodes = (_cmsAlarmCodesChunkType*) _cmsContextGetClientChunk(p->ContextID, AlarmCodesContext);
453 for (i=0; i < p ->Lut->OutputChannels; i++) {
455 wOut[i] = ContextAlarmCodes ->AlarmCodes[i];
459 p ->Lut ->Eval16Fn(wIn, wOut, p -> Lut->Data);
462 // Gamut check, No cache, 16 bits.
464 void PrecalculatedXFORMGamutCheck(_cmsTRANSFORM* p,
467 cmsUInt32Number PixelsPerLine,
468 cmsUInt32Number LineCount,
469 const cmsStride* Stride)
471 cmsUInt8Number* accum;
472 cmsUInt8Number* output;
473 cmsUInt16Number wIn[cmsMAXCHANNELS], wOut[cmsMAXCHANNELS];
474 cmsUInt32Number i, j, strideIn, strideOut;
476 _cmsHandleExtraChannels(p, in, out, PixelsPerLine, LineCount, Stride);
480 memset(wIn, 0, sizeof(wIn));
481 memset(wOut, 0, sizeof(wOut));
483 for (i = 0; i < LineCount; i++) {
485 accum = (cmsUInt8Number*)in + strideIn;
486 output = (cmsUInt8Number*)out + strideOut;
488 for (j = 0; j < PixelsPerLine; j++) {
490 accum = p->FromInput(p, wIn, accum, Stride->BytesPerPlaneIn);
491 TransformOnePixelWithGamutCheck(p, wIn, wOut);
492 output = p->ToOutput(p, wOut, output, Stride->BytesPerPlaneOut);
495 strideIn += Stride->BytesPerLineIn;
496 strideOut += Stride->BytesPerLineOut;
501 // No gamut check, Cache, 16 bits,
503 void CachedXFORM(_cmsTRANSFORM* p,
506 cmsUInt32Number PixelsPerLine,
507 cmsUInt32Number LineCount,
508 const cmsStride* Stride)
510 cmsUInt8Number* accum;
511 cmsUInt8Number* output;
512 cmsUInt16Number wIn[cmsMAXCHANNELS], wOut[cmsMAXCHANNELS];
514 cmsUInt32Number i, j, strideIn, strideOut;
516 _cmsHandleExtraChannels(p, in, out, PixelsPerLine, LineCount, Stride);
518 // Empty buffers for quick memcmp
519 memset(wIn, 0, sizeof(wIn));
520 memset(wOut, 0, sizeof(wOut));
522 // Get copy of zero cache
523 memcpy(&Cache, &p->Cache, sizeof(Cache));
528 for (i = 0; i < LineCount; i++) {
530 accum = (cmsUInt8Number*)in + strideIn;
531 output = (cmsUInt8Number*)out + strideOut;
533 for (j = 0; j < PixelsPerLine; j++) {
535 accum = p->FromInput(p, wIn, accum, Stride->BytesPerPlaneIn);
537 if (memcmp(wIn, Cache.CacheIn, sizeof(Cache.CacheIn)) == 0) {
539 memcpy(wOut, Cache.CacheOut, sizeof(Cache.CacheOut));
542 p->Lut->Eval16Fn(wIn, wOut, p->Lut->Data);
544 memcpy(Cache.CacheIn, wIn, sizeof(Cache.CacheIn));
545 memcpy(Cache.CacheOut, wOut, sizeof(Cache.CacheOut));
548 output = p->ToOutput(p, wOut, output, Stride->BytesPerPlaneOut);
551 strideIn += Stride->BytesPerLineIn;
552 strideOut += Stride->BytesPerLineOut;
556 // All those nice features together
558 void CachedXFORMGamutCheck(_cmsTRANSFORM* p,
561 cmsUInt32Number PixelsPerLine,
562 cmsUInt32Number LineCount,
563 const cmsStride* Stride)
565 cmsUInt8Number* accum;
566 cmsUInt8Number* output;
567 cmsUInt16Number wIn[cmsMAXCHANNELS], wOut[cmsMAXCHANNELS];
569 cmsUInt32Number i, j, strideIn, strideOut;
571 _cmsHandleExtraChannels(p, in, out, PixelsPerLine, LineCount, Stride);
573 // Empty buffers for quick memcmp
574 memset(wIn, 0, sizeof(wIn));
575 memset(wOut, 0, sizeof(wOut));
577 // Get copy of zero cache
578 memcpy(&Cache, &p->Cache, sizeof(Cache));
583 for (i = 0; i < LineCount; i++) {
585 accum = (cmsUInt8Number*)in + strideIn;
586 output = (cmsUInt8Number*)out + strideOut;
588 for (j = 0; j < PixelsPerLine; j++) {
590 accum = p->FromInput(p, wIn, accum, Stride->BytesPerPlaneIn);
592 if (memcmp(wIn, Cache.CacheIn, sizeof(Cache.CacheIn)) == 0) {
594 memcpy(wOut, Cache.CacheOut, sizeof(Cache.CacheOut));
597 TransformOnePixelWithGamutCheck(p, wIn, wOut);
599 memcpy(Cache.CacheIn, wIn, sizeof(Cache.CacheIn));
600 memcpy(Cache.CacheOut, wOut, sizeof(Cache.CacheOut));
603 output = p->ToOutput(p, wOut, output, Stride->BytesPerPlaneOut);
606 strideIn += Stride->BytesPerLineIn;
607 strideOut += Stride->BytesPerLineOut;
611 // Transform plug-ins ----------------------------------------------------------------------------------------------------
613 // List of used-defined transform factories
614 typedef struct _cmsTransformCollection_st {
616 _cmsTransform2Factory Factory;
617 cmsBool OldXform; // Factory returns xform function in the old style
619 struct _cmsTransformCollection_st *Next;
621 } _cmsTransformCollection;
623 // The linked list head
624 _cmsTransformPluginChunkType _cmsTransformPluginChunk = { NULL };
627 // Duplicates the zone of memory used by the plug-in in the new context
629 void DupPluginTransformList(struct _cmsContext_struct* ctx,
630 const struct _cmsContext_struct* src)
632 _cmsTransformPluginChunkType newHead = { NULL };
633 _cmsTransformCollection* entry;
634 _cmsTransformCollection* Anterior = NULL;
635 _cmsTransformPluginChunkType* head = (_cmsTransformPluginChunkType*) src->chunks[TransformPlugin];
637 // Walk the list copying all nodes
638 for (entry = head->TransformCollection;
640 entry = entry ->Next) {
642 _cmsTransformCollection *newEntry = ( _cmsTransformCollection *) _cmsSubAllocDup(ctx ->MemPool, entry, sizeof(_cmsTransformCollection));
644 if (newEntry == NULL)
647 // We want to keep the linked list order, so this is a little bit tricky
648 newEntry -> Next = NULL;
650 Anterior -> Next = newEntry;
654 if (newHead.TransformCollection == NULL)
655 newHead.TransformCollection = newEntry;
658 ctx ->chunks[TransformPlugin] = _cmsSubAllocDup(ctx->MemPool, &newHead, sizeof(_cmsTransformPluginChunkType));
661 // Allocates memory for transform plugin factory
662 void _cmsAllocTransformPluginChunk(struct _cmsContext_struct* ctx,
663 const struct _cmsContext_struct* src)
667 // Copy all linked list
668 DupPluginTransformList(ctx, src);
671 static _cmsTransformPluginChunkType TransformPluginChunkType = { NULL };
672 ctx ->chunks[TransformPlugin] = _cmsSubAllocDup(ctx ->MemPool, &TransformPluginChunkType, sizeof(_cmsTransformPluginChunkType));
676 // Adaptor for old versions of plug-in
678 void _cmsTransform2toTransformAdaptor(struct _cmstransform_struct *CMMcargo,
679 const void* InputBuffer,
681 cmsUInt32Number PixelsPerLine,
682 cmsUInt32Number LineCount,
683 const cmsStride* Stride)
686 cmsUInt32Number i, strideIn, strideOut;
688 _cmsHandleExtraChannels(CMMcargo, InputBuffer, OutputBuffer, PixelsPerLine, LineCount, Stride);
693 for (i = 0; i < LineCount; i++) {
695 void *accum = (cmsUInt8Number*)InputBuffer + strideIn;
696 void *output = (cmsUInt8Number*)OutputBuffer + strideOut;
698 CMMcargo->OldXform(CMMcargo, accum, output, PixelsPerLine, Stride->BytesPerPlaneIn);
700 strideIn += Stride->BytesPerLineIn;
701 strideOut += Stride->BytesPerLineOut;
707 // Register new ways to transform
708 cmsBool _cmsRegisterTransformPlugin(cmsContext ContextID, cmsPluginBase* Data)
710 cmsPluginTransform* Plugin = (cmsPluginTransform*) Data;
711 _cmsTransformCollection* fl;
712 _cmsTransformPluginChunkType* ctx = ( _cmsTransformPluginChunkType*) _cmsContextGetClientChunk(ContextID,TransformPlugin);
716 // Free the chain. Memory is safely freed at exit
717 ctx->TransformCollection = NULL;
721 // Factory callback is required
722 if (Plugin->factories.xform == NULL) return FALSE;
725 fl = (_cmsTransformCollection*) _cmsPluginMalloc(ContextID, sizeof(_cmsTransformCollection));
726 if (fl == NULL) return FALSE;
728 // Check for full xform plug-ins previous to 2.8, we would need an adapter in that case
729 if (Plugin->base.ExpectedVersion < 2080) {
734 fl->OldXform = FALSE;
736 // Copy the parameters
737 fl->Factory = Plugin->factories.xform;
740 fl ->Next = ctx->TransformCollection;
741 ctx->TransformCollection = fl;
748 void CMSEXPORT _cmsSetTransformUserData(struct _cmstransform_struct *CMMcargo, void* ptr, _cmsFreeUserDataFn FreePrivateDataFn)
750 _cmsAssert(CMMcargo != NULL);
751 CMMcargo ->UserData = ptr;
752 CMMcargo ->FreeUserData = FreePrivateDataFn;
755 // returns the pointer defined by the plug-in to store private data
756 void * CMSEXPORT _cmsGetTransformUserData(struct _cmstransform_struct *CMMcargo)
758 _cmsAssert(CMMcargo != NULL);
759 return CMMcargo ->UserData;
762 // returns the current formatters
763 void CMSEXPORT _cmsGetTransformFormatters16(struct _cmstransform_struct *CMMcargo, cmsFormatter16* FromInput, cmsFormatter16* ToOutput)
765 _cmsAssert(CMMcargo != NULL);
766 if (FromInput) *FromInput = CMMcargo ->FromInput;
767 if (ToOutput) *ToOutput = CMMcargo ->ToOutput;
770 void CMSEXPORT _cmsGetTransformFormattersFloat(struct _cmstransform_struct *CMMcargo, cmsFormatterFloat* FromInput, cmsFormatterFloat* ToOutput)
772 _cmsAssert(CMMcargo != NULL);
773 if (FromInput) *FromInput = CMMcargo ->FromInputFloat;
774 if (ToOutput) *ToOutput = CMMcargo ->ToOutputFloat;
777 // returns original flags
778 cmsUInt32Number CMSEXPORT _cmsGetTransformFlags(struct _cmstransform_struct* CMMcargo)
780 _cmsAssert(CMMcargo != NULL);
781 return CMMcargo->dwOriginalFlags;
784 // Returns the worker callback for parallelization plug-ins
785 _cmsTransform2Fn CMSEXPORT _cmsGetTransformWorker(struct _cmstransform_struct* CMMcargo)
787 _cmsAssert(CMMcargo != NULL);
788 return CMMcargo->Worker;
791 // This field holds maximum number of workers or -1 to auto
792 cmsInt32Number CMSEXPORT _cmsGetTransformMaxWorkers(struct _cmstransform_struct* CMMcargo)
794 _cmsAssert(CMMcargo != NULL);
795 return CMMcargo->MaxWorkers;
798 // This field is actually unused and reserved
799 cmsUInt32Number CMSEXPORT _cmsGetTransformWorkerFlags(struct _cmstransform_struct* CMMcargo)
801 _cmsAssert(CMMcargo != NULL);
802 return CMMcargo->WorkerFlags;
805 // In the case there is a parallelization plug-in, let it to do its job
807 void ParalellizeIfSuitable(_cmsTRANSFORM* p)
809 _cmsParallelizationPluginChunkType* ctx = (_cmsParallelizationPluginChunkType*)_cmsContextGetClientChunk(p->ContextID, ParallelizationPlugin);
811 _cmsAssert(p != NULL);
812 if (ctx != NULL && ctx->SchedulerFn != NULL) {
814 p->Worker = p->xform;
815 p->xform = ctx->SchedulerFn;
816 p->MaxWorkers = ctx->MaxWorkers;
817 p->WorkerFlags = ctx->WorkerFlags;
823 * An empty unroll to avoid a check with NULL on cmsDoTransform()
826 cmsUInt8Number* UnrollNothing(CMSREGISTER _cmsTRANSFORM* info,
827 CMSREGISTER cmsUInt16Number wIn[],
828 CMSREGISTER cmsUInt8Number* accum,
829 CMSREGISTER cmsUInt32Number Stride)
833 cmsUNUSED_PARAMETER(info);
834 cmsUNUSED_PARAMETER(wIn);
835 cmsUNUSED_PARAMETER(Stride);
839 cmsUInt8Number* PackNothing(CMSREGISTER _cmsTRANSFORM* info,
840 CMSREGISTER cmsUInt16Number wOut[],
841 CMSREGISTER cmsUInt8Number* output,
842 CMSREGISTER cmsUInt32Number Stride)
846 cmsUNUSED_PARAMETER(info);
847 cmsUNUSED_PARAMETER(wOut);
848 cmsUNUSED_PARAMETER(Stride);
851 // Allocate transform struct and set it to defaults. Ask the optimization plug-in about if those formats are proper
852 // for separated transforms. If this is the case,
854 _cmsTRANSFORM* AllocEmptyTransform(cmsContext ContextID, cmsPipeline* lut,
855 cmsUInt32Number Intent, cmsUInt32Number* InputFormat, cmsUInt32Number* OutputFormat, cmsUInt32Number* dwFlags)
857 _cmsTransformPluginChunkType* ctx = ( _cmsTransformPluginChunkType*) _cmsContextGetClientChunk(ContextID, TransformPlugin);
858 _cmsTransformCollection* Plugin;
860 // Allocate needed memory
861 _cmsTRANSFORM* p = (_cmsTRANSFORM*)_cmsMallocZero(ContextID, sizeof(_cmsTRANSFORM));
863 cmsPipelineFree(lut);
867 // Store the proposed pipeline
870 // Let's see if any plug-in want to do the transform by itself
871 if (p->Lut != NULL) {
873 if (!(*dwFlags & cmsFLAGS_NOOPTIMIZE))
875 for (Plugin = ctx->TransformCollection;
877 Plugin = Plugin->Next) {
879 if (Plugin->Factory(&p->xform, &p->UserData, &p->FreeUserData, &p->Lut, InputFormat, OutputFormat, dwFlags)) {
881 // Last plugin in the declaration order takes control. We just keep
882 // the original parameters as a logging.
883 // Note that cmsFLAGS_CAN_CHANGE_FORMATTER is not set, so by default
884 // an optimized transform is not reusable. The plug-in can, however, change
885 // the flags and make it suitable.
887 p->ContextID = ContextID;
888 p->InputFormat = *InputFormat;
889 p->OutputFormat = *OutputFormat;
890 p->dwOriginalFlags = *dwFlags;
892 // Fill the formatters just in case the optimized routine is interested.
893 // No error is thrown if the formatter doesn't exist. It is up to the optimization
894 // factory to decide what to do in those cases.
895 p->FromInput = _cmsGetFormatter(ContextID, *InputFormat, cmsFormatterInput, CMS_PACK_FLAGS_16BITS).Fmt16;
896 p->ToOutput = _cmsGetFormatter(ContextID, *OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_16BITS).Fmt16;
897 p->FromInputFloat = _cmsGetFormatter(ContextID, *InputFormat, cmsFormatterInput, CMS_PACK_FLAGS_FLOAT).FmtFloat;
898 p->ToOutputFloat = _cmsGetFormatter(ContextID, *OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_FLOAT).FmtFloat;
900 // Save the day? (Ignore the warning)
901 if (Plugin->OldXform) {
902 p->OldXform = (_cmsTransformFn)(void*)p->xform;
903 p->xform = _cmsTransform2toTransformAdaptor;
906 ParalellizeIfSuitable(p);
912 // Not suitable for the transform plug-in, let's check the pipeline plug-in
913 _cmsOptimizePipeline(ContextID, &p->Lut, Intent, InputFormat, OutputFormat, dwFlags);
916 // Check whatever this is a true floating point transform
917 if (_cmsFormatterIsFloat(*InputFormat) || _cmsFormatterIsFloat(*OutputFormat)) {
919 // Get formatter function always return a valid union, but the contents of this union may be NULL.
920 p ->FromInputFloat = _cmsGetFormatter(ContextID, *InputFormat, cmsFormatterInput, CMS_PACK_FLAGS_FLOAT).FmtFloat;
921 p ->ToOutputFloat = _cmsGetFormatter(ContextID, *OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_FLOAT).FmtFloat;
922 *dwFlags |= cmsFLAGS_CAN_CHANGE_FORMATTER;
924 if (p ->FromInputFloat == NULL || p ->ToOutputFloat == NULL) {
926 cmsSignalError(ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported raster format");
927 cmsDeleteTransform(p);
931 if (*dwFlags & cmsFLAGS_NULLTRANSFORM) {
933 p ->xform = NullFloatXFORM;
936 // Float transforms don't use cache, always are non-NULL
937 p ->xform = FloatXFORM;
943 // Formats are intended to be changed before use
944 if (*InputFormat == 0 && *OutputFormat == 0) {
945 p->FromInput = UnrollNothing;
946 p->ToOutput = PackNothing;
947 *dwFlags |= cmsFLAGS_CAN_CHANGE_FORMATTER;
951 cmsUInt32Number BytesPerPixelInput;
953 p ->FromInput = _cmsGetFormatter(ContextID, *InputFormat, cmsFormatterInput, CMS_PACK_FLAGS_16BITS).Fmt16;
954 p ->ToOutput = _cmsGetFormatter(ContextID, *OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_16BITS).Fmt16;
956 if (p ->FromInput == NULL || p ->ToOutput == NULL) {
958 cmsSignalError(ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported raster format");
959 cmsDeleteTransform(p);
963 BytesPerPixelInput = T_BYTES(*InputFormat);
964 if (BytesPerPixelInput == 0 || BytesPerPixelInput >= 2)
965 *dwFlags |= cmsFLAGS_CAN_CHANGE_FORMATTER;
969 if (*dwFlags & cmsFLAGS_NULLTRANSFORM) {
971 p ->xform = NullXFORM;
974 if (*dwFlags & cmsFLAGS_NOCACHE) {
976 if (*dwFlags & cmsFLAGS_GAMUTCHECK)
977 p ->xform = PrecalculatedXFORMGamutCheck; // Gamut check, no cache
979 p ->xform = PrecalculatedXFORM; // No cache, no gamut check
983 if (*dwFlags & cmsFLAGS_GAMUTCHECK)
984 p ->xform = CachedXFORMGamutCheck; // Gamut check, cache
986 p ->xform = CachedXFORM; // No gamut check, cache
993 * Check consistency for alpha channel copy
995 if (*dwFlags & cmsFLAGS_COPY_ALPHA)
997 if (T_EXTRA(*InputFormat) != T_EXTRA(*OutputFormat))
999 cmsSignalError(ContextID, cmsERROR_NOT_SUITABLE, "Mismatched alpha channels");
1000 cmsDeleteTransform(p);
1005 p ->InputFormat = *InputFormat;
1006 p ->OutputFormat = *OutputFormat;
1007 p ->dwOriginalFlags = *dwFlags;
1008 p ->ContextID = ContextID;
1009 p ->UserData = NULL;
1010 ParalellizeIfSuitable(p);
1015 cmsBool GetXFormColorSpaces(cmsUInt32Number nProfiles, cmsHPROFILE hProfiles[], cmsColorSpaceSignature* Input, cmsColorSpaceSignature* Output)
1017 cmsColorSpaceSignature ColorSpaceIn, ColorSpaceOut;
1018 cmsColorSpaceSignature PostColorSpace;
1021 if (nProfiles == 0) return FALSE;
1022 if (hProfiles[0] == NULL) return FALSE;
1024 *Input = PostColorSpace = cmsGetColorSpace(hProfiles[0]);
1026 for (i=0; i < nProfiles; i++) {
1028 cmsProfileClassSignature cls;
1029 cmsHPROFILE hProfile = hProfiles[i];
1031 int lIsInput = (PostColorSpace != cmsSigXYZData) &&
1032 (PostColorSpace != cmsSigLabData);
1034 if (hProfile == NULL) return FALSE;
1036 cls = cmsGetDeviceClass(hProfile);
1038 if (cls == cmsSigNamedColorClass) {
1040 ColorSpaceIn = cmsSig1colorData;
1041 ColorSpaceOut = (nProfiles > 1) ? cmsGetPCS(hProfile) : cmsGetColorSpace(hProfile);
1044 if (lIsInput || (cls == cmsSigLinkClass)) {
1046 ColorSpaceIn = cmsGetColorSpace(hProfile);
1047 ColorSpaceOut = cmsGetPCS(hProfile);
1051 ColorSpaceIn = cmsGetPCS(hProfile);
1052 ColorSpaceOut = cmsGetColorSpace(hProfile);
1056 *Input = ColorSpaceIn;
1058 PostColorSpace = ColorSpaceOut;
1061 *Output = PostColorSpace;
1068 cmsBool IsProperColorSpace(cmsColorSpaceSignature Check, cmsUInt32Number dwFormat)
1070 int Space1 = (int) T_COLORSPACE(dwFormat);
1071 int Space2 = _cmsLCMScolorSpace(Check);
1073 if (Space1 == PT_ANY) return TRUE;
1074 if (Space1 == Space2) return TRUE;
1076 if (Space1 == PT_LabV2 && Space2 == PT_Lab) return TRUE;
1077 if (Space1 == PT_Lab && Space2 == PT_LabV2) return TRUE;
1082 // ----------------------------------------------------------------------------------------------------------------
1084 // Jun-21-2000: Some profiles (those that comes with W2K) comes
1085 // with the media white (media black?) x 100. Add a sanity check
1088 void NormalizeXYZ(cmsCIEXYZ* Dest)
1090 while (Dest -> X > 2. &&
1101 void SetWhitePoint(cmsCIEXYZ* wtPt, const cmsCIEXYZ* src)
1118 // New to lcms 2.0 -- have all parameters available.
1119 cmsHTRANSFORM CMSEXPORT cmsCreateExtendedTransform(cmsContext ContextID,
1120 cmsUInt32Number nProfiles, cmsHPROFILE hProfiles[],
1122 cmsUInt32Number Intents[],
1123 cmsFloat64Number AdaptationStates[],
1124 cmsHPROFILE hGamutProfile,
1125 cmsUInt32Number nGamutPCSposition,
1126 cmsUInt32Number InputFormat,
1127 cmsUInt32Number OutputFormat,
1128 cmsUInt32Number dwFlags)
1130 _cmsTRANSFORM* xform;
1131 cmsColorSpaceSignature EntryColorSpace;
1132 cmsColorSpaceSignature ExitColorSpace;
1134 cmsUInt32Number LastIntent = Intents[nProfiles-1];
1136 // If it is a fake transform
1137 if (dwFlags & cmsFLAGS_NULLTRANSFORM)
1139 return AllocEmptyTransform(ContextID, NULL, INTENT_PERCEPTUAL, &InputFormat, &OutputFormat, &dwFlags);
1142 // If gamut check is requested, make sure we have a gamut profile
1143 if (dwFlags & cmsFLAGS_GAMUTCHECK) {
1144 if (hGamutProfile == NULL) dwFlags &= ~cmsFLAGS_GAMUTCHECK;
1147 // On floating point transforms, inhibit cache
1148 if (_cmsFormatterIsFloat(InputFormat) || _cmsFormatterIsFloat(OutputFormat))
1149 dwFlags |= cmsFLAGS_NOCACHE;
1151 // Mark entry/exit spaces
1152 if (!GetXFormColorSpaces(nProfiles, hProfiles, &EntryColorSpace, &ExitColorSpace)) {
1153 cmsSignalError(ContextID, cmsERROR_NULL, "NULL input profiles on transform");
1157 // Check if proper colorspaces
1158 if (!IsProperColorSpace(EntryColorSpace, InputFormat)) {
1159 cmsSignalError(ContextID, cmsERROR_COLORSPACE_CHECK, "Wrong input color space on transform");
1163 if (!IsProperColorSpace(ExitColorSpace, OutputFormat)) {
1164 cmsSignalError(ContextID, cmsERROR_COLORSPACE_CHECK, "Wrong output color space on transform");
1168 // Check whatever the transform is 16 bits and involves linear RGB in first profile. If so, disable optimizations
1169 if (EntryColorSpace == cmsSigRgbData && T_BYTES(InputFormat) == 2 && !(dwFlags & cmsFLAGS_NOOPTIMIZE))
1171 cmsFloat64Number gamma = cmsDetectRGBProfileGamma(hProfiles[0], 0.1);
1173 if (gamma > 0 && gamma < 1.6)
1174 dwFlags |= cmsFLAGS_NOOPTIMIZE;
1177 // Create a pipeline with all transformations
1178 Lut = _cmsLinkProfiles(ContextID, nProfiles, Intents, hProfiles, BPC, AdaptationStates, dwFlags);
1180 cmsSignalError(ContextID, cmsERROR_NOT_SUITABLE, "Couldn't link the profiles");
1184 // Check channel count
1185 if ((cmsChannelsOfColorSpace(EntryColorSpace) != (cmsInt32Number) cmsPipelineInputChannels(Lut)) ||
1186 (cmsChannelsOfColorSpace(ExitColorSpace) != (cmsInt32Number) cmsPipelineOutputChannels(Lut))) {
1187 cmsPipelineFree(Lut);
1188 cmsSignalError(ContextID, cmsERROR_NOT_SUITABLE, "Channel count doesn't match. Profile is corrupted");
1194 xform = AllocEmptyTransform(ContextID, Lut, LastIntent, &InputFormat, &OutputFormat, &dwFlags);
1195 if (xform == NULL) {
1200 xform ->EntryColorSpace = EntryColorSpace;
1201 xform ->ExitColorSpace = ExitColorSpace;
1202 xform ->RenderingIntent = Intents[nProfiles-1];
1204 // Take white points
1205 SetWhitePoint(&xform->EntryWhitePoint, (cmsCIEXYZ*) cmsReadTag(hProfiles[0], cmsSigMediaWhitePointTag));
1206 SetWhitePoint(&xform->ExitWhitePoint, (cmsCIEXYZ*) cmsReadTag(hProfiles[nProfiles-1], cmsSigMediaWhitePointTag));
1209 // Create a gamut check LUT if requested
1210 if (hGamutProfile != NULL && (dwFlags & cmsFLAGS_GAMUTCHECK))
1211 xform ->GamutCheck = _cmsCreateGamutCheckPipeline(ContextID, hProfiles,
1218 // Try to read input and output colorant table
1219 if (cmsIsTag(hProfiles[0], cmsSigColorantTableTag)) {
1221 // Input table can only come in this way.
1222 xform ->InputColorant = cmsDupNamedColorList((cmsNAMEDCOLORLIST*) cmsReadTag(hProfiles[0], cmsSigColorantTableTag));
1225 // Output is a little bit more complex.
1226 if (cmsGetDeviceClass(hProfiles[nProfiles-1]) == cmsSigLinkClass) {
1228 // This tag may exist only on devicelink profiles.
1229 if (cmsIsTag(hProfiles[nProfiles-1], cmsSigColorantTableOutTag)) {
1231 // It may be NULL if error
1232 xform ->OutputColorant = cmsDupNamedColorList((cmsNAMEDCOLORLIST*) cmsReadTag(hProfiles[nProfiles-1], cmsSigColorantTableOutTag));
1237 if (cmsIsTag(hProfiles[nProfiles-1], cmsSigColorantTableTag)) {
1239 xform -> OutputColorant = cmsDupNamedColorList((cmsNAMEDCOLORLIST*) cmsReadTag(hProfiles[nProfiles-1], cmsSigColorantTableTag));
1243 // Store the sequence of profiles
1244 if (dwFlags & cmsFLAGS_KEEP_SEQUENCE) {
1245 xform ->Sequence = _cmsCompileProfileSequence(ContextID, nProfiles, hProfiles);
1248 xform ->Sequence = NULL;
1250 // If this is a cached transform, init first value, which is zero (16 bits only)
1251 if (!(dwFlags & cmsFLAGS_NOCACHE)) {
1253 memset(&xform ->Cache.CacheIn, 0, sizeof(xform ->Cache.CacheIn));
1255 if (xform ->GamutCheck != NULL) {
1256 TransformOnePixelWithGamutCheck(xform, xform ->Cache.CacheIn, xform->Cache.CacheOut);
1260 xform ->Lut ->Eval16Fn(xform ->Cache.CacheIn, xform->Cache.CacheOut, xform -> Lut->Data);
1265 return (cmsHTRANSFORM) xform;
1268 // Multiprofile transforms: Gamut check is not available here, as it is unclear from which profile the gamut comes.
1269 cmsHTRANSFORM CMSEXPORT cmsCreateMultiprofileTransformTHR(cmsContext ContextID,
1270 cmsHPROFILE hProfiles[],
1271 cmsUInt32Number nProfiles,
1272 cmsUInt32Number InputFormat,
1273 cmsUInt32Number OutputFormat,
1274 cmsUInt32Number Intent,
1275 cmsUInt32Number dwFlags)
1279 cmsUInt32Number Intents[256];
1280 cmsFloat64Number AdaptationStates[256];
1282 if (nProfiles <= 0 || nProfiles > 255) {
1283 cmsSignalError(ContextID, cmsERROR_RANGE, "Wrong number of profiles. 1..255 expected, %d found.", nProfiles);
1287 for (i=0; i < nProfiles; i++) {
1288 BPC[i] = dwFlags & cmsFLAGS_BLACKPOINTCOMPENSATION ? TRUE : FALSE;
1289 Intents[i] = Intent;
1290 AdaptationStates[i] = cmsSetAdaptationStateTHR(ContextID, -1);
1294 return cmsCreateExtendedTransform(ContextID, nProfiles, hProfiles, BPC, Intents, AdaptationStates, NULL, 0, InputFormat, OutputFormat, dwFlags);
1299 cmsHTRANSFORM CMSEXPORT cmsCreateMultiprofileTransform(cmsHPROFILE hProfiles[],
1300 cmsUInt32Number nProfiles,
1301 cmsUInt32Number InputFormat,
1302 cmsUInt32Number OutputFormat,
1303 cmsUInt32Number Intent,
1304 cmsUInt32Number dwFlags)
1307 if (nProfiles <= 0 || nProfiles > 255) {
1308 cmsSignalError(NULL, cmsERROR_RANGE, "Wrong number of profiles. 1..255 expected, %d found.", nProfiles);
1312 return cmsCreateMultiprofileTransformTHR(cmsGetProfileContextID(hProfiles[0]),
1321 cmsHTRANSFORM CMSEXPORT cmsCreateTransformTHR(cmsContext ContextID,
1323 cmsUInt32Number InputFormat,
1325 cmsUInt32Number OutputFormat,
1326 cmsUInt32Number Intent,
1327 cmsUInt32Number dwFlags)
1330 cmsHPROFILE hArray[2];
1335 return cmsCreateMultiprofileTransformTHR(ContextID, hArray, Output == NULL ? 1U : 2U, InputFormat, OutputFormat, Intent, dwFlags);
1338 CMSAPI cmsHTRANSFORM CMSEXPORT cmsCreateTransform(cmsHPROFILE Input,
1339 cmsUInt32Number InputFormat,
1341 cmsUInt32Number OutputFormat,
1342 cmsUInt32Number Intent,
1343 cmsUInt32Number dwFlags)
1345 return cmsCreateTransformTHR(cmsGetProfileContextID(Input), Input, InputFormat, Output, OutputFormat, Intent, dwFlags);
1349 cmsHTRANSFORM CMSEXPORT cmsCreateProofingTransformTHR(cmsContext ContextID,
1350 cmsHPROFILE InputProfile,
1351 cmsUInt32Number InputFormat,
1352 cmsHPROFILE OutputProfile,
1353 cmsUInt32Number OutputFormat,
1354 cmsHPROFILE ProofingProfile,
1355 cmsUInt32Number nIntent,
1356 cmsUInt32Number ProofingIntent,
1357 cmsUInt32Number dwFlags)
1359 cmsHPROFILE hArray[4];
1360 cmsUInt32Number Intents[4];
1362 cmsFloat64Number Adaptation[4];
1363 cmsBool DoBPC = (dwFlags & cmsFLAGS_BLACKPOINTCOMPENSATION) ? TRUE : FALSE;
1366 hArray[0] = InputProfile; hArray[1] = ProofingProfile; hArray[2] = ProofingProfile; hArray[3] = OutputProfile;
1367 Intents[0] = nIntent; Intents[1] = nIntent; Intents[2] = INTENT_RELATIVE_COLORIMETRIC; Intents[3] = ProofingIntent;
1368 BPC[0] = DoBPC; BPC[1] = DoBPC; BPC[2] = 0; BPC[3] = 0;
1370 Adaptation[0] = Adaptation[1] = Adaptation[2] = Adaptation[3] = cmsSetAdaptationStateTHR(ContextID, -1);
1372 if (!(dwFlags & (cmsFLAGS_SOFTPROOFING|cmsFLAGS_GAMUTCHECK)))
1373 return cmsCreateTransformTHR(ContextID, InputProfile, InputFormat, OutputProfile, OutputFormat, nIntent, dwFlags);
1375 return cmsCreateExtendedTransform(ContextID, 4, hArray, BPC, Intents, Adaptation,
1376 ProofingProfile, 1, InputFormat, OutputFormat, dwFlags);
1381 cmsHTRANSFORM CMSEXPORT cmsCreateProofingTransform(cmsHPROFILE InputProfile,
1382 cmsUInt32Number InputFormat,
1383 cmsHPROFILE OutputProfile,
1384 cmsUInt32Number OutputFormat,
1385 cmsHPROFILE ProofingProfile,
1386 cmsUInt32Number nIntent,
1387 cmsUInt32Number ProofingIntent,
1388 cmsUInt32Number dwFlags)
1390 return cmsCreateProofingTransformTHR(cmsGetProfileContextID(InputProfile),
1402 // Grab the ContextID from an open transform. Returns NULL if a NULL transform is passed
1403 cmsContext CMSEXPORT cmsGetTransformContextID(cmsHTRANSFORM hTransform)
1405 _cmsTRANSFORM* xform = (_cmsTRANSFORM*) hTransform;
1407 if (xform == NULL) return NULL;
1408 return xform -> ContextID;
1411 // Grab the input/output formats
1412 cmsUInt32Number CMSEXPORT cmsGetTransformInputFormat(cmsHTRANSFORM hTransform)
1414 _cmsTRANSFORM* xform = (_cmsTRANSFORM*) hTransform;
1416 if (xform == NULL) return 0;
1417 return xform->InputFormat;
1420 cmsUInt32Number CMSEXPORT cmsGetTransformOutputFormat(cmsHTRANSFORM hTransform)
1422 _cmsTRANSFORM* xform = (_cmsTRANSFORM*) hTransform;
1424 if (xform == NULL) return 0;
1425 return xform->OutputFormat;
1428 // For backwards compatibility
1429 cmsBool CMSEXPORT cmsChangeBuffersFormat(cmsHTRANSFORM hTransform,
1430 cmsUInt32Number InputFormat,
1431 cmsUInt32Number OutputFormat)
1433 _cmsTRANSFORM* xform = (_cmsTRANSFORM*) hTransform;
1434 cmsFormatter16 FromInput, ToOutput;
1437 // We only can afford to change formatters if previous transform is at least 16 bits
1438 if (!(xform ->dwOriginalFlags & cmsFLAGS_CAN_CHANGE_FORMATTER)) {
1440 cmsSignalError(xform ->ContextID, cmsERROR_NOT_SUITABLE, "cmsChangeBuffersFormat works only on transforms created originally with at least 16 bits of precision");
1444 FromInput = _cmsGetFormatter(xform->ContextID, InputFormat, cmsFormatterInput, CMS_PACK_FLAGS_16BITS).Fmt16;
1445 ToOutput = _cmsGetFormatter(xform->ContextID, OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_16BITS).Fmt16;
1447 if (FromInput == NULL || ToOutput == NULL) {
1449 cmsSignalError(xform -> ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported raster format");
1453 xform ->InputFormat = InputFormat;
1454 xform ->OutputFormat = OutputFormat;
1455 xform ->FromInput = FromInput;
1456 xform ->ToOutput = ToOutput;