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(*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
992 p
->InputFormat
= *InputFormat
;
993 p
->OutputFormat
= *OutputFormat
;
994 p
->dwOriginalFlags
= *dwFlags
;
995 p
->ContextID
= ContextID
;
997 ParalellizeIfSuitable(p
);
1002 cmsBool
GetXFormColorSpaces(cmsUInt32Number nProfiles
, cmsHPROFILE hProfiles
[], cmsColorSpaceSignature
* Input
, cmsColorSpaceSignature
* Output
)
1004 cmsColorSpaceSignature ColorSpaceIn
, ColorSpaceOut
;
1005 cmsColorSpaceSignature PostColorSpace
;
1008 if (nProfiles
== 0) return FALSE
;
1009 if (hProfiles
[0] == NULL
) return FALSE
;
1011 *Input
= PostColorSpace
= cmsGetColorSpace(hProfiles
[0]);
1013 for (i
=0; i
< nProfiles
; i
++) {
1015 cmsProfileClassSignature cls
;
1016 cmsHPROFILE hProfile
= hProfiles
[i
];
1018 int lIsInput
= (PostColorSpace
!= cmsSigXYZData
) &&
1019 (PostColorSpace
!= cmsSigLabData
);
1021 if (hProfile
== NULL
) return FALSE
;
1023 cls
= cmsGetDeviceClass(hProfile
);
1025 if (cls
== cmsSigNamedColorClass
) {
1027 ColorSpaceIn
= cmsSig1colorData
;
1028 ColorSpaceOut
= (nProfiles
> 1) ? cmsGetPCS(hProfile
) : cmsGetColorSpace(hProfile
);
1031 if (lIsInput
|| (cls
== cmsSigLinkClass
)) {
1033 ColorSpaceIn
= cmsGetColorSpace(hProfile
);
1034 ColorSpaceOut
= cmsGetPCS(hProfile
);
1038 ColorSpaceIn
= cmsGetPCS(hProfile
);
1039 ColorSpaceOut
= cmsGetColorSpace(hProfile
);
1043 *Input
= ColorSpaceIn
;
1045 PostColorSpace
= ColorSpaceOut
;
1048 *Output
= PostColorSpace
;
1055 cmsBool
IsProperColorSpace(cmsColorSpaceSignature Check
, cmsUInt32Number dwFormat
)
1057 int Space1
= (int) T_COLORSPACE(dwFormat
);
1058 int Space2
= _cmsLCMScolorSpace(Check
);
1060 if (Space1
== PT_ANY
) return TRUE
;
1061 if (Space1
== Space2
) return TRUE
;
1063 if (Space1
== PT_LabV2
&& Space2
== PT_Lab
) return TRUE
;
1064 if (Space1
== PT_Lab
&& Space2
== PT_LabV2
) return TRUE
;
1069 // ----------------------------------------------------------------------------------------------------------------
1071 // Jun-21-2000: Some profiles (those that comes with W2K) comes
1072 // with the media white (media black?) x 100. Add a sanity check
1075 void NormalizeXYZ(cmsCIEXYZ
* Dest
)
1077 while (Dest
-> X
> 2. &&
1088 void SetWhitePoint(cmsCIEXYZ
* wtPt
, const cmsCIEXYZ
* src
)
1105 // New to lcms 2.0 -- have all parameters available.
1106 cmsHTRANSFORM CMSEXPORT
cmsCreateExtendedTransform(cmsContext ContextID
,
1107 cmsUInt32Number nProfiles
, cmsHPROFILE hProfiles
[],
1109 cmsUInt32Number Intents
[],
1110 cmsFloat64Number AdaptationStates
[],
1111 cmsHPROFILE hGamutProfile
,
1112 cmsUInt32Number nGamutPCSposition
,
1113 cmsUInt32Number InputFormat
,
1114 cmsUInt32Number OutputFormat
,
1115 cmsUInt32Number dwFlags
)
1117 _cmsTRANSFORM
* xform
;
1118 cmsColorSpaceSignature EntryColorSpace
;
1119 cmsColorSpaceSignature ExitColorSpace
;
1121 cmsUInt32Number LastIntent
= Intents
[nProfiles
-1];
1123 // If it is a fake transform
1124 if (dwFlags
& cmsFLAGS_NULLTRANSFORM
)
1126 return AllocEmptyTransform(ContextID
, NULL
, INTENT_PERCEPTUAL
, &InputFormat
, &OutputFormat
, &dwFlags
);
1129 // If gamut check is requested, make sure we have a gamut profile
1130 if (dwFlags
& cmsFLAGS_GAMUTCHECK
) {
1131 if (hGamutProfile
== NULL
) dwFlags
&= ~cmsFLAGS_GAMUTCHECK
;
1134 // On floating point transforms, inhibit cache
1135 if (_cmsFormatterIsFloat(InputFormat
) || _cmsFormatterIsFloat(OutputFormat
))
1136 dwFlags
|= cmsFLAGS_NOCACHE
;
1138 // Mark entry/exit spaces
1139 if (!GetXFormColorSpaces(nProfiles
, hProfiles
, &EntryColorSpace
, &ExitColorSpace
)) {
1140 cmsSignalError(ContextID
, cmsERROR_NULL
, "NULL input profiles on transform");
1144 // Check if proper colorspaces
1145 if (!IsProperColorSpace(EntryColorSpace
, InputFormat
)) {
1146 cmsSignalError(ContextID
, cmsERROR_COLORSPACE_CHECK
, "Wrong input color space on transform");
1150 if (!IsProperColorSpace(ExitColorSpace
, OutputFormat
)) {
1151 cmsSignalError(ContextID
, cmsERROR_COLORSPACE_CHECK
, "Wrong output color space on transform");
1155 // Check whatever the transform is 16 bits and involves linear RGB in first profile. If so, disable optimizations
1156 if (EntryColorSpace
== cmsSigRgbData
&& T_BYTES(InputFormat
) == 2 && !(dwFlags
& cmsFLAGS_NOOPTIMIZE
))
1158 cmsFloat64Number gamma
= cmsDetectRGBProfileGamma(hProfiles
[0], 0.1);
1160 if (gamma
> 0 && gamma
< 1.6)
1161 dwFlags
|= cmsFLAGS_NOOPTIMIZE
;
1164 // Create a pipeline with all transformations
1165 Lut
= _cmsLinkProfiles(ContextID
, nProfiles
, Intents
, hProfiles
, BPC
, AdaptationStates
, dwFlags
);
1167 cmsSignalError(ContextID
, cmsERROR_NOT_SUITABLE
, "Couldn't link the profiles");
1171 // Check channel count
1172 if ((cmsChannelsOfColorSpace(EntryColorSpace
) != (cmsInt32Number
) cmsPipelineInputChannels(Lut
)) ||
1173 (cmsChannelsOfColorSpace(ExitColorSpace
) != (cmsInt32Number
) cmsPipelineOutputChannels(Lut
))) {
1174 cmsPipelineFree(Lut
);
1175 cmsSignalError(ContextID
, cmsERROR_NOT_SUITABLE
, "Channel count doesn't match. Profile is corrupted");
1181 xform
= AllocEmptyTransform(ContextID
, Lut
, LastIntent
, &InputFormat
, &OutputFormat
, &dwFlags
);
1182 if (xform
== NULL
) {
1187 xform
->EntryColorSpace
= EntryColorSpace
;
1188 xform
->ExitColorSpace
= ExitColorSpace
;
1189 xform
->RenderingIntent
= Intents
[nProfiles
-1];
1191 // Take white points
1192 SetWhitePoint(&xform
->EntryWhitePoint
, (cmsCIEXYZ
*) cmsReadTag(hProfiles
[0], cmsSigMediaWhitePointTag
));
1193 SetWhitePoint(&xform
->ExitWhitePoint
, (cmsCIEXYZ
*) cmsReadTag(hProfiles
[nProfiles
-1], cmsSigMediaWhitePointTag
));
1196 // Create a gamut check LUT if requested
1197 if (hGamutProfile
!= NULL
&& (dwFlags
& cmsFLAGS_GAMUTCHECK
))
1198 xform
->GamutCheck
= _cmsCreateGamutCheckPipeline(ContextID
, hProfiles
,
1205 // Try to read input and output colorant table
1206 if (cmsIsTag(hProfiles
[0], cmsSigColorantTableTag
)) {
1208 // Input table can only come in this way.
1209 xform
->InputColorant
= cmsDupNamedColorList((cmsNAMEDCOLORLIST
*) cmsReadTag(hProfiles
[0], cmsSigColorantTableTag
));
1212 // Output is a little bit more complex.
1213 if (cmsGetDeviceClass(hProfiles
[nProfiles
-1]) == cmsSigLinkClass
) {
1215 // This tag may exist only on devicelink profiles.
1216 if (cmsIsTag(hProfiles
[nProfiles
-1], cmsSigColorantTableOutTag
)) {
1218 // It may be NULL if error
1219 xform
->OutputColorant
= cmsDupNamedColorList((cmsNAMEDCOLORLIST
*) cmsReadTag(hProfiles
[nProfiles
-1], cmsSigColorantTableOutTag
));
1224 if (cmsIsTag(hProfiles
[nProfiles
-1], cmsSigColorantTableTag
)) {
1226 xform
-> OutputColorant
= cmsDupNamedColorList((cmsNAMEDCOLORLIST
*) cmsReadTag(hProfiles
[nProfiles
-1], cmsSigColorantTableTag
));
1230 // Store the sequence of profiles
1231 if (dwFlags
& cmsFLAGS_KEEP_SEQUENCE
) {
1232 xform
->Sequence
= _cmsCompileProfileSequence(ContextID
, nProfiles
, hProfiles
);
1235 xform
->Sequence
= NULL
;
1237 // If this is a cached transform, init first value, which is zero (16 bits only)
1238 if (!(dwFlags
& cmsFLAGS_NOCACHE
)) {
1240 memset(&xform
->Cache
.CacheIn
, 0, sizeof(xform
->Cache
.CacheIn
));
1242 if (xform
->GamutCheck
!= NULL
) {
1243 TransformOnePixelWithGamutCheck(xform
, xform
->Cache
.CacheIn
, xform
->Cache
.CacheOut
);
1247 xform
->Lut
->Eval16Fn(xform
->Cache
.CacheIn
, xform
->Cache
.CacheOut
, xform
-> Lut
->Data
);
1252 return (cmsHTRANSFORM
) xform
;
1255 // Multiprofile transforms: Gamut check is not available here, as it is unclear from which profile the gamut comes.
1256 cmsHTRANSFORM CMSEXPORT
cmsCreateMultiprofileTransformTHR(cmsContext ContextID
,
1257 cmsHPROFILE hProfiles
[],
1258 cmsUInt32Number nProfiles
,
1259 cmsUInt32Number InputFormat
,
1260 cmsUInt32Number OutputFormat
,
1261 cmsUInt32Number Intent
,
1262 cmsUInt32Number dwFlags
)
1266 cmsUInt32Number Intents
[256];
1267 cmsFloat64Number AdaptationStates
[256];
1269 if (nProfiles
<= 0 || nProfiles
> 255) {
1270 cmsSignalError(ContextID
, cmsERROR_RANGE
, "Wrong number of profiles. 1..255 expected, %d found.", nProfiles
);
1274 for (i
=0; i
< nProfiles
; i
++) {
1275 BPC
[i
] = dwFlags
& cmsFLAGS_BLACKPOINTCOMPENSATION
? TRUE
: FALSE
;
1276 Intents
[i
] = Intent
;
1277 AdaptationStates
[i
] = cmsSetAdaptationStateTHR(ContextID
, -1);
1281 return cmsCreateExtendedTransform(ContextID
, nProfiles
, hProfiles
, BPC
, Intents
, AdaptationStates
, NULL
, 0, InputFormat
, OutputFormat
, dwFlags
);
1286 cmsHTRANSFORM CMSEXPORT
cmsCreateMultiprofileTransform(cmsHPROFILE hProfiles
[],
1287 cmsUInt32Number nProfiles
,
1288 cmsUInt32Number InputFormat
,
1289 cmsUInt32Number OutputFormat
,
1290 cmsUInt32Number Intent
,
1291 cmsUInt32Number dwFlags
)
1294 if (nProfiles
<= 0 || nProfiles
> 255) {
1295 cmsSignalError(NULL
, cmsERROR_RANGE
, "Wrong number of profiles. 1..255 expected, %d found.", nProfiles
);
1299 return cmsCreateMultiprofileTransformTHR(cmsGetProfileContextID(hProfiles
[0]),
1308 cmsHTRANSFORM CMSEXPORT
cmsCreateTransformTHR(cmsContext ContextID
,
1310 cmsUInt32Number InputFormat
,
1312 cmsUInt32Number OutputFormat
,
1313 cmsUInt32Number Intent
,
1314 cmsUInt32Number dwFlags
)
1317 cmsHPROFILE hArray
[2];
1322 return cmsCreateMultiprofileTransformTHR(ContextID
, hArray
, Output
== NULL
? 1U : 2U, InputFormat
, OutputFormat
, Intent
, dwFlags
);
1325 CMSAPI cmsHTRANSFORM CMSEXPORT
cmsCreateTransform(cmsHPROFILE Input
,
1326 cmsUInt32Number InputFormat
,
1328 cmsUInt32Number OutputFormat
,
1329 cmsUInt32Number Intent
,
1330 cmsUInt32Number dwFlags
)
1332 return cmsCreateTransformTHR(cmsGetProfileContextID(Input
), Input
, InputFormat
, Output
, OutputFormat
, Intent
, dwFlags
);
1336 cmsHTRANSFORM CMSEXPORT
cmsCreateProofingTransformTHR(cmsContext ContextID
,
1337 cmsHPROFILE InputProfile
,
1338 cmsUInt32Number InputFormat
,
1339 cmsHPROFILE OutputProfile
,
1340 cmsUInt32Number OutputFormat
,
1341 cmsHPROFILE ProofingProfile
,
1342 cmsUInt32Number nIntent
,
1343 cmsUInt32Number ProofingIntent
,
1344 cmsUInt32Number dwFlags
)
1346 cmsHPROFILE hArray
[4];
1347 cmsUInt32Number Intents
[4];
1349 cmsFloat64Number Adaptation
[4];
1350 cmsBool DoBPC
= (dwFlags
& cmsFLAGS_BLACKPOINTCOMPENSATION
) ? TRUE
: FALSE
;
1353 hArray
[0] = InputProfile
; hArray
[1] = ProofingProfile
; hArray
[2] = ProofingProfile
; hArray
[3] = OutputProfile
;
1354 Intents
[0] = nIntent
; Intents
[1] = nIntent
; Intents
[2] = INTENT_RELATIVE_COLORIMETRIC
; Intents
[3] = ProofingIntent
;
1355 BPC
[0] = DoBPC
; BPC
[1] = DoBPC
; BPC
[2] = 0; BPC
[3] = 0;
1357 Adaptation
[0] = Adaptation
[1] = Adaptation
[2] = Adaptation
[3] = cmsSetAdaptationStateTHR(ContextID
, -1);
1359 if (!(dwFlags
& (cmsFLAGS_SOFTPROOFING
|cmsFLAGS_GAMUTCHECK
)))
1360 return cmsCreateTransformTHR(ContextID
, InputProfile
, InputFormat
, OutputProfile
, OutputFormat
, nIntent
, dwFlags
);
1362 return cmsCreateExtendedTransform(ContextID
, 4, hArray
, BPC
, Intents
, Adaptation
,
1363 ProofingProfile
, 1, InputFormat
, OutputFormat
, dwFlags
);
1368 cmsHTRANSFORM CMSEXPORT
cmsCreateProofingTransform(cmsHPROFILE InputProfile
,
1369 cmsUInt32Number InputFormat
,
1370 cmsHPROFILE OutputProfile
,
1371 cmsUInt32Number OutputFormat
,
1372 cmsHPROFILE ProofingProfile
,
1373 cmsUInt32Number nIntent
,
1374 cmsUInt32Number ProofingIntent
,
1375 cmsUInt32Number dwFlags
)
1377 return cmsCreateProofingTransformTHR(cmsGetProfileContextID(InputProfile
),
1389 // Grab the ContextID from an open transform. Returns NULL if a NULL transform is passed
1390 cmsContext CMSEXPORT
cmsGetTransformContextID(cmsHTRANSFORM hTransform
)
1392 _cmsTRANSFORM
* xform
= (_cmsTRANSFORM
*) hTransform
;
1394 if (xform
== NULL
) return NULL
;
1395 return xform
-> ContextID
;
1398 // Grab the input/output formats
1399 cmsUInt32Number CMSEXPORT
cmsGetTransformInputFormat(cmsHTRANSFORM hTransform
)
1401 _cmsTRANSFORM
* xform
= (_cmsTRANSFORM
*) hTransform
;
1403 if (xform
== NULL
) return 0;
1404 return xform
->InputFormat
;
1407 cmsUInt32Number CMSEXPORT
cmsGetTransformOutputFormat(cmsHTRANSFORM hTransform
)
1409 _cmsTRANSFORM
* xform
= (_cmsTRANSFORM
*) hTransform
;
1411 if (xform
== NULL
) return 0;
1412 return xform
->OutputFormat
;
1415 // For backwards compatibility
1416 cmsBool CMSEXPORT
cmsChangeBuffersFormat(cmsHTRANSFORM hTransform
,
1417 cmsUInt32Number InputFormat
,
1418 cmsUInt32Number OutputFormat
)
1420 _cmsTRANSFORM
* xform
= (_cmsTRANSFORM
*) hTransform
;
1421 cmsFormatter16 FromInput
, ToOutput
;
1424 // We only can afford to change formatters if previous transform is at least 16 bits
1425 if (!(xform
->dwOriginalFlags
& cmsFLAGS_CAN_CHANGE_FORMATTER
)) {
1427 cmsSignalError(xform
->ContextID
, cmsERROR_NOT_SUITABLE
, "cmsChangeBuffersFormat works only on transforms created originally with at least 16 bits of precision");
1431 FromInput
= _cmsGetFormatter(xform
->ContextID
, InputFormat
, cmsFormatterInput
, CMS_PACK_FLAGS_16BITS
).Fmt16
;
1432 ToOutput
= _cmsGetFormatter(xform
->ContextID
, OutputFormat
, cmsFormatterOutput
, CMS_PACK_FLAGS_16BITS
).Fmt16
;
1434 if (FromInput
== NULL
|| ToOutput
== NULL
) {
1436 cmsSignalError(xform
-> ContextID
, cmsERROR_UNKNOWN_EXTENSION
, "Unsupported raster format");
1440 xform
->InputFormat
= InputFormat
;
1441 xform
->OutputFormat
= OutputFormat
;
1442 xform
->FromInput
= FromInput
;
1443 xform
->ToOutput
= ToOutput
;