1 //---------------------------------------------------------------------------------
3 // Little Color Management System
4 // Copyright (c) 1998-2011 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 // Alarm codes for 16-bit transformations, because the fixed range of containers there are
33 // no values left to mark out of gamut. volatile is C99 per 6.2.5
34 static volatile cmsUInt16Number Alarm
[cmsMAXCHANNELS
] = { 0x7F00, 0x7F00, 0x7F00, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
35 static volatile cmsFloat64Number GlobalAdaptationState
= 1;
37 // The adaptation state may be defaulted by this function. If you don't like it, use the extended transform routine
38 cmsFloat64Number CMSEXPORT
cmsSetAdaptationState(cmsFloat64Number d
)
40 cmsFloat64Number OldVal
= GlobalAdaptationState
;
43 GlobalAdaptationState
= d
;
48 // Alarm codes are always global
49 void CMSEXPORT
cmsSetAlarmCodes(cmsUInt16Number NewAlarm
[cmsMAXCHANNELS
])
53 _cmsAssert(NewAlarm
!= NULL
);
55 for (i
=0; i
< cmsMAXCHANNELS
; i
++)
56 Alarm
[i
] = NewAlarm
[i
];
59 // You can get the codes cas well
60 void CMSEXPORT
cmsGetAlarmCodes(cmsUInt16Number OldAlarm
[cmsMAXCHANNELS
])
64 _cmsAssert(OldAlarm
!= NULL
);
66 for (i
=0; i
< cmsMAXCHANNELS
; i
++)
67 OldAlarm
[i
] = Alarm
[i
];
70 // Get rid of transform resources
71 void CMSEXPORT
cmsDeleteTransform(cmsHTRANSFORM hTransform
)
73 _cmsTRANSFORM
* p
= (_cmsTRANSFORM
*) hTransform
;
75 _cmsAssert(p
!= NULL
);
78 cmsPipelineFree(p
-> GamutCheck
);
81 cmsPipelineFree(p
-> Lut
);
83 if (p
->InputColorant
)
84 cmsFreeNamedColorList(p
->InputColorant
);
86 if (p
-> OutputColorant
)
87 cmsFreeNamedColorList(p
->OutputColorant
);
90 cmsFreeProfileSequenceDescription(p
->Sequence
);
93 p
->FreeUserData(p
->ContextID
, p
->UserData
);
95 _cmsFree(p
->ContextID
, (void *) p
);
99 void CMSEXPORT
cmsDoTransform(cmsHTRANSFORM Transform
,
100 const void* InputBuffer
,
102 cmsUInt32Number Size
)
105 _cmsTRANSFORM
* p
= (_cmsTRANSFORM
*) Transform
;
107 p
-> xform(p
, InputBuffer
, OutputBuffer
, Size
, Size
);
112 void CMSEXPORT
cmsDoTransformStride(cmsHTRANSFORM Transform
,
113 const void* InputBuffer
,
115 cmsUInt32Number Size
, cmsUInt32Number Stride
)
118 _cmsTRANSFORM
* p
= (_cmsTRANSFORM
*) Transform
;
120 p
-> xform(p
, InputBuffer
, OutputBuffer
, Size
, Stride
);
124 // Transform routines ----------------------------------------------------------------------------------------------------------
126 // Float xform converts floats. Since there are no performance issues, one routine does all job, including gamut check.
127 // Note that because extended range, we can use a -1.0 value for out of gamut in this case.
129 void FloatXFORM(_cmsTRANSFORM
* p
,
131 void* out
, cmsUInt32Number Size
, cmsUInt32Number Stride
)
133 cmsUInt8Number
* accum
;
134 cmsUInt8Number
* output
;
135 cmsFloat32Number fIn
[cmsMAXCHANNELS
], fOut
[cmsMAXCHANNELS
];
136 cmsFloat32Number OutOfGamut
;
137 cmsUInt32Number i
, j
;
139 accum
= (cmsUInt8Number
*) in
;
140 output
= (cmsUInt8Number
*) out
;
142 for (i
=0; i
< Size
; i
++) {
144 accum
= p
-> FromInputFloat(p
, fIn
, accum
, Stride
);
146 // Any gamut chack to do?
147 if (p
->GamutCheck
!= NULL
) {
149 // Evaluate gamut marker.
150 cmsPipelineEvalFloat( fIn
, &OutOfGamut
, p
->GamutCheck
);
152 // Is current color out of gamut?
153 if (OutOfGamut
> 0.0) {
155 // Certainly, out of gamut
156 for (j
=0; j
< cmsMAXCHANNELS
; j
++)
161 // No, proceed normally
162 cmsPipelineEvalFloat(fIn
, fOut
, p
-> Lut
);
167 // No gamut check at all
168 cmsPipelineEvalFloat(fIn
, fOut
, p
-> Lut
);
171 // Back to asked representation
172 output
= p
-> ToOutputFloat(p
, fOut
, output
, Stride
);
176 // 16 bit precision -----------------------------------------------------------------------------------------------------------
178 // Null transformation, only applies formatters. No caché
180 void NullXFORM(_cmsTRANSFORM
* p
,
182 void* out
, cmsUInt32Number Size
,
183 cmsUInt32Number Stride
)
185 cmsUInt8Number
* accum
;
186 cmsUInt8Number
* output
;
187 cmsUInt16Number wIn
[cmsMAXCHANNELS
];
188 cmsUInt32Number i
, n
;
190 accum
= (cmsUInt8Number
*) in
;
191 output
= (cmsUInt8Number
*) out
;
192 n
= Size
; // Buffer len
194 for (i
=0; i
< n
; i
++) {
196 accum
= p
-> FromInput(p
, wIn
, accum
, Stride
);
197 output
= p
-> ToOutput(p
, wIn
, output
, Stride
);
202 // No gamut check, no cache, 16 bits
204 void PrecalculatedXFORM(_cmsTRANSFORM
* p
,
206 void* out
, cmsUInt32Number Size
, cmsUInt32Number Stride
)
208 register cmsUInt8Number
* accum
;
209 register cmsUInt8Number
* output
;
210 cmsUInt16Number wIn
[cmsMAXCHANNELS
], wOut
[cmsMAXCHANNELS
];
211 cmsUInt32Number i
, n
;
213 accum
= (cmsUInt8Number
*) in
;
214 output
= (cmsUInt8Number
*) out
;
217 for (i
=0; i
< n
; i
++) {
219 accum
= p
-> FromInput(p
, wIn
, accum
, Stride
);
220 p
->Lut
->Eval16Fn(wIn
, wOut
, p
-> Lut
->Data
);
221 output
= p
-> ToOutput(p
, wOut
, output
, Stride
);
226 // Auxiliar: Handle precalculated gamut check
228 void TransformOnePixelWithGamutCheck(_cmsTRANSFORM
* p
,
229 const cmsUInt16Number wIn
[],
230 cmsUInt16Number wOut
[])
232 cmsUInt16Number wOutOfGamut
;
234 p
->GamutCheck
->Eval16Fn(wIn
, &wOutOfGamut
, p
->GamutCheck
->Data
);
235 if (wOutOfGamut
>= 1) {
239 for (i
=0; i
< p
->Lut
->OutputChannels
; i
++)
243 p
->Lut
->Eval16Fn(wIn
, wOut
, p
-> Lut
->Data
);
246 // Gamut check, No caché, 16 bits.
248 void PrecalculatedXFORMGamutCheck(_cmsTRANSFORM
* p
,
250 void* out
, cmsUInt32Number Size
, cmsUInt32Number Stride
)
252 cmsUInt8Number
* accum
;
253 cmsUInt8Number
* output
;
254 cmsUInt16Number wIn
[cmsMAXCHANNELS
], wOut
[cmsMAXCHANNELS
];
255 cmsUInt32Number i
, n
;
257 accum
= (cmsUInt8Number
*) in
;
258 output
= (cmsUInt8Number
*) out
;
259 n
= Size
; // Buffer len
261 for (i
=0; i
< n
; i
++) {
263 accum
= p
-> FromInput(p
, wIn
, accum
, Stride
);
264 TransformOnePixelWithGamutCheck(p
, wIn
, wOut
);
265 output
= p
-> ToOutput(p
, wOut
, output
, Stride
);
270 // No gamut check, Caché, 16 bits,
272 void CachedXFORM(_cmsTRANSFORM
* p
,
274 void* out
, cmsUInt32Number Size
, cmsUInt32Number Stride
)
276 cmsUInt8Number
* accum
;
277 cmsUInt8Number
* output
;
278 cmsUInt16Number wIn
[cmsMAXCHANNELS
], wOut
[cmsMAXCHANNELS
];
279 cmsUInt32Number i
, n
;
282 accum
= (cmsUInt8Number
*) in
;
283 output
= (cmsUInt8Number
*) out
;
284 n
= Size
; // Buffer len
286 // Empty buffers for quick memcmp
287 memset(wIn
, 0, sizeof(wIn
));
288 memset(wOut
, 0, sizeof(wOut
));
290 // Get copy of zero cache
291 memcpy(&Cache
, &p
->Cache
, sizeof(Cache
));
293 for (i
=0; i
< n
; i
++) {
295 accum
= p
-> FromInput(p
, wIn
, accum
, Stride
);
297 if (memcmp(wIn
, Cache
.CacheIn
, sizeof(Cache
.CacheIn
)) == 0) {
299 memcpy(wOut
, Cache
.CacheOut
, sizeof(Cache
.CacheOut
));
303 p
->Lut
->Eval16Fn(wIn
, wOut
, p
-> Lut
->Data
);
305 memcpy(Cache
.CacheIn
, wIn
, sizeof(Cache
.CacheIn
));
306 memcpy(Cache
.CacheOut
, wOut
, sizeof(Cache
.CacheOut
));
309 output
= p
-> ToOutput(p
, wOut
, output
, Stride
);
315 // All those nice features together
317 void CachedXFORMGamutCheck(_cmsTRANSFORM
* p
,
319 void* out
, cmsUInt32Number Size
, cmsUInt32Number Stride
)
321 cmsUInt8Number
* accum
;
322 cmsUInt8Number
* output
;
323 cmsUInt16Number wIn
[cmsMAXCHANNELS
], wOut
[cmsMAXCHANNELS
];
324 cmsUInt32Number i
, n
;
327 accum
= (cmsUInt8Number
*) in
;
328 output
= (cmsUInt8Number
*) out
;
329 n
= Size
; // Buffer len
331 // Empty buffers for quick memcmp
332 memset(wIn
, 0, sizeof(cmsUInt16Number
) * cmsMAXCHANNELS
);
333 memset(wOut
, 0, sizeof(cmsUInt16Number
) * cmsMAXCHANNELS
);
335 // Get copy of zero cache
336 memcpy(&Cache
, &p
->Cache
, sizeof(Cache
));
338 for (i
=0; i
< n
; i
++) {
340 accum
= p
-> FromInput(p
, wIn
, accum
, Stride
);
342 if (memcmp(wIn
, Cache
.CacheIn
, sizeof(Cache
.CacheIn
)) == 0) {
343 memcpy(wOut
, Cache
.CacheOut
, sizeof(Cache
.CacheOut
));
346 TransformOnePixelWithGamutCheck(p
, wIn
, wOut
);
347 memcpy(Cache
.CacheIn
, wIn
, sizeof(Cache
.CacheIn
));
348 memcpy(Cache
.CacheOut
, wOut
, sizeof(Cache
.CacheOut
));
351 output
= p
-> ToOutput(p
, wOut
, output
, Stride
);
356 // -------------------------------------------------------------------------------------------------------------
358 // List of used-defined transform factories
359 typedef struct _cmsTransformCollection_st
{
361 _cmsTransformFactory Factory
;
362 struct _cmsTransformCollection_st
*Next
;
364 } _cmsTransformCollection
;
366 // The linked list head
367 static _cmsTransformCollection
* TransformCollection
= NULL
;
369 // Register new ways to transform
370 cmsBool
_cmsRegisterTransformPlugin(cmsContext id
, cmsPluginBase
* Data
)
372 cmsPluginTransform
* Plugin
= (cmsPluginTransform
*) Data
;
373 _cmsTransformCollection
* fl
;
377 // Free the chain. Memory is safely freed at exit
378 TransformCollection
= NULL
;
382 // Factory callback is required
383 if (Plugin
->Factory
== NULL
) return FALSE
;
386 fl
= (_cmsTransformCollection
*) _cmsPluginMalloc(id
, sizeof(_cmsTransformCollection
));
387 if (fl
== NULL
) return FALSE
;
389 // Copy the parameters
390 fl
->Factory
= Plugin
->Factory
;
393 fl
->Next
= TransformCollection
;
394 TransformCollection
= fl
;
401 void CMSEXPORT
_cmsSetTransformUserData(struct _cmstransform_struct
*CMMcargo
, void* ptr
, _cmsFreeUserDataFn FreePrivateDataFn
)
403 _cmsAssert(CMMcargo
!= NULL
);
404 CMMcargo
->UserData
= ptr
;
405 CMMcargo
->FreeUserData
= FreePrivateDataFn
;
408 // returns the pointer defined by the plug-in to store private data
409 void * CMSEXPORT
_cmsGetTransformUserData(struct _cmstransform_struct
*CMMcargo
)
411 _cmsAssert(CMMcargo
!= NULL
);
412 return CMMcargo
->UserData
;
415 // returns the current formatters
416 void CMSEXPORT
_cmsGetTransformFormatters16(struct _cmstransform_struct
*CMMcargo
, cmsFormatter16
* FromInput
, cmsFormatter16
* ToOutput
)
418 _cmsAssert(CMMcargo
!= NULL
);
419 if (FromInput
) *FromInput
= CMMcargo
->FromInput
;
420 if (ToOutput
) *ToOutput
= CMMcargo
->ToOutput
;
423 void CMSEXPORT
_cmsGetTransformFormattersFloat(struct _cmstransform_struct
*CMMcargo
, cmsFormatterFloat
* FromInput
, cmsFormatterFloat
* ToOutput
)
425 _cmsAssert(CMMcargo
!= NULL
);
426 if (FromInput
) *FromInput
= CMMcargo
->FromInputFloat
;
427 if (ToOutput
) *ToOutput
= CMMcargo
->ToOutputFloat
;
431 // Allocate transform struct and set it to defaults. Ask the optimization plug-in about if those formats are proper
432 // for separated transforms. If this is the case,
434 _cmsTRANSFORM
* AllocEmptyTransform(cmsContext ContextID
, cmsPipeline
* lut
,
435 cmsUInt32Number Intent
, cmsUInt32Number
* InputFormat
, cmsUInt32Number
* OutputFormat
, cmsUInt32Number
* dwFlags
)
437 _cmsTransformCollection
* Plugin
;
439 // Allocate needed memory
440 _cmsTRANSFORM
* p
= (_cmsTRANSFORM
*) _cmsMallocZero(ContextID
, sizeof(_cmsTRANSFORM
));
443 // Store the proposed pipeline
446 // Let's see if any plug-in want to do the transform by itself
447 for (Plugin
= TransformCollection
;
449 Plugin
= Plugin
->Next
) {
451 if (Plugin
->Factory(&p
->xform
, &p
->UserData
, &p
->FreeUserData
, &p
->Lut
, InputFormat
, OutputFormat
, dwFlags
)) {
453 // Last plugin in the declaration order takes control. We just keep
454 // the original parameters as a logging.
455 // Note that cmsFLAGS_CAN_CHANGE_FORMATTER is not set, so by default
456 // an optimized transform is not reusable. The plug-in can, however, change
457 // the flags and make it suitable.
459 p
->ContextID
= ContextID
;
460 p
->InputFormat
= *InputFormat
;
461 p
->OutputFormat
= *OutputFormat
;
462 p
->dwOriginalFlags
= *dwFlags
;
464 // Fill the formatters just in case the optimized routine is interested.
465 // No error is thrown if the formatter doesn't exist. It is up to the optimization
466 // factory to decide what to do in those cases.
467 p
->FromInput
= _cmsGetFormatter(*InputFormat
, cmsFormatterInput
, CMS_PACK_FLAGS_16BITS
).Fmt16
;
468 p
->ToOutput
= _cmsGetFormatter(*OutputFormat
, cmsFormatterOutput
, CMS_PACK_FLAGS_16BITS
).Fmt16
;
469 p
->FromInputFloat
= _cmsGetFormatter(*InputFormat
, cmsFormatterInput
, CMS_PACK_FLAGS_FLOAT
).FmtFloat
;
470 p
->ToOutputFloat
= _cmsGetFormatter(*OutputFormat
, cmsFormatterOutput
, CMS_PACK_FLAGS_FLOAT
).FmtFloat
;
476 // Not suitable for the transform plug-in, let's check the pipeline plug-in
478 _cmsOptimizePipeline(&p
->Lut
, Intent
, InputFormat
, OutputFormat
, dwFlags
);
480 // Check whatever this is a true floating point transform
481 if (_cmsFormatterIsFloat(*InputFormat
) && _cmsFormatterIsFloat(*OutputFormat
)) {
483 // Get formatter function always return a valid union, but the contents of this union may be NULL.
484 p
->FromInputFloat
= _cmsGetFormatter(*InputFormat
, cmsFormatterInput
, CMS_PACK_FLAGS_FLOAT
).FmtFloat
;
485 p
->ToOutputFloat
= _cmsGetFormatter(*OutputFormat
, cmsFormatterOutput
, CMS_PACK_FLAGS_FLOAT
).FmtFloat
;
486 *dwFlags
|= cmsFLAGS_CAN_CHANGE_FORMATTER
;
488 if (p
->FromInputFloat
== NULL
|| p
->ToOutputFloat
== NULL
) {
490 cmsSignalError(ContextID
, cmsERROR_UNKNOWN_EXTENSION
, "Unsupported raster format");
491 _cmsFree(ContextID
, p
);
495 // Float transforms don't use caché, always are non-NULL
496 p
->xform
= FloatXFORM
;
500 if (*InputFormat
== 0 && *OutputFormat
== 0) {
501 p
->FromInput
= p
->ToOutput
= NULL
;
502 *dwFlags
|= cmsFLAGS_CAN_CHANGE_FORMATTER
;
506 int BytesPerPixelInput
;
508 p
->FromInput
= _cmsGetFormatter(*InputFormat
, cmsFormatterInput
, CMS_PACK_FLAGS_16BITS
).Fmt16
;
509 p
->ToOutput
= _cmsGetFormatter(*OutputFormat
, cmsFormatterOutput
, CMS_PACK_FLAGS_16BITS
).Fmt16
;
511 if (p
->FromInput
== NULL
|| p
->ToOutput
== NULL
) {
513 cmsSignalError(ContextID
, cmsERROR_UNKNOWN_EXTENSION
, "Unsupported raster format");
514 _cmsFree(ContextID
, p
);
518 BytesPerPixelInput
= T_BYTES(p
->InputFormat
);
519 if (BytesPerPixelInput
== 0 || BytesPerPixelInput
>= 2)
520 *dwFlags
|= cmsFLAGS_CAN_CHANGE_FORMATTER
;
524 if (*dwFlags
& cmsFLAGS_NULLTRANSFORM
) {
526 p
->xform
= NullXFORM
;
529 if (*dwFlags
& cmsFLAGS_NOCACHE
) {
531 if (*dwFlags
& cmsFLAGS_GAMUTCHECK
)
532 p
->xform
= PrecalculatedXFORMGamutCheck
; // Gamut check, no caché
534 p
->xform
= PrecalculatedXFORM
; // No caché, no gamut check
538 if (*dwFlags
& cmsFLAGS_GAMUTCHECK
)
539 p
->xform
= CachedXFORMGamutCheck
; // Gamut check, caché
541 p
->xform
= CachedXFORM
; // No gamut check, caché
547 p
->InputFormat
= *InputFormat
;
548 p
->OutputFormat
= *OutputFormat
;
549 p
->dwOriginalFlags
= *dwFlags
;
550 p
->ContextID
= ContextID
;
556 cmsBool
GetXFormColorSpaces(int nProfiles
, cmsHPROFILE hProfiles
[], cmsColorSpaceSignature
* Input
, cmsColorSpaceSignature
* Output
)
558 cmsColorSpaceSignature ColorSpaceIn
, ColorSpaceOut
;
559 cmsColorSpaceSignature PostColorSpace
;
562 if (nProfiles
<= 0) return FALSE
;
563 if (hProfiles
[0] == NULL
) return FALSE
;
565 *Input
= PostColorSpace
= cmsGetColorSpace(hProfiles
[0]);
567 for (i
=0; i
< nProfiles
; i
++) {
569 cmsProfileClassSignature cls
;
570 cmsHPROFILE hProfile
= hProfiles
[i
];
572 int lIsInput
= (PostColorSpace
!= cmsSigXYZData
) &&
573 (PostColorSpace
!= cmsSigLabData
);
575 if (hProfile
== NULL
) return FALSE
;
577 cls
= cmsGetDeviceClass(hProfile
);
579 if (cls
== cmsSigNamedColorClass
) {
581 ColorSpaceIn
= cmsSig1colorData
;
582 ColorSpaceOut
= (nProfiles
> 1) ? cmsGetPCS(hProfile
) : cmsGetColorSpace(hProfile
);
585 if (lIsInput
|| (cls
== cmsSigLinkClass
)) {
587 ColorSpaceIn
= cmsGetColorSpace(hProfile
);
588 ColorSpaceOut
= cmsGetPCS(hProfile
);
592 ColorSpaceIn
= cmsGetPCS(hProfile
);
593 ColorSpaceOut
= cmsGetColorSpace(hProfile
);
597 *Input
= ColorSpaceIn
;
599 PostColorSpace
= ColorSpaceOut
;
602 *Output
= PostColorSpace
;
609 cmsBool
IsProperColorSpace(cmsColorSpaceSignature Check
, cmsUInt32Number dwFormat
)
611 int Space1
= T_COLORSPACE(dwFormat
);
612 int Space2
= _cmsLCMScolorSpace(Check
);
614 if (Space1
== PT_ANY
) return TRUE
;
615 if (Space1
== Space2
) return TRUE
;
617 if (Space1
== PT_LabV2
&& Space2
== PT_Lab
) return TRUE
;
618 if (Space1
== PT_Lab
&& Space2
== PT_LabV2
) return TRUE
;
623 // ----------------------------------------------------------------------------------------------------------------
626 void SetWhitePoint(cmsCIEXYZ
* wtPt
, const cmsCIEXYZ
* src
)
641 // New to lcms 2.0 -- have all parameters available.
642 cmsHTRANSFORM CMSEXPORT
cmsCreateExtendedTransform(cmsContext ContextID
,
643 cmsUInt32Number nProfiles
, cmsHPROFILE hProfiles
[],
645 cmsUInt32Number Intents
[],
646 cmsFloat64Number AdaptationStates
[],
647 cmsHPROFILE hGamutProfile
,
648 cmsUInt32Number nGamutPCSposition
,
649 cmsUInt32Number InputFormat
,
650 cmsUInt32Number OutputFormat
,
651 cmsUInt32Number dwFlags
)
653 _cmsTRANSFORM
* xform
;
654 cmsColorSpaceSignature EntryColorSpace
;
655 cmsColorSpaceSignature ExitColorSpace
;
657 cmsUInt32Number LastIntent
= Intents
[nProfiles
-1];
659 // If it is a fake transform
660 if (dwFlags
& cmsFLAGS_NULLTRANSFORM
)
662 return AllocEmptyTransform(ContextID
, NULL
, INTENT_PERCEPTUAL
, &InputFormat
, &OutputFormat
, &dwFlags
);
665 // If gamut check is requested, make sure we have a gamut profile
666 if (dwFlags
& cmsFLAGS_GAMUTCHECK
) {
667 if (hGamutProfile
== NULL
) dwFlags
&= ~cmsFLAGS_GAMUTCHECK
;
670 // On floating point transforms, inhibit cache
671 if (_cmsFormatterIsFloat(InputFormat
) || _cmsFormatterIsFloat(OutputFormat
))
672 dwFlags
|= cmsFLAGS_NOCACHE
;
674 // Mark entry/exit spaces
675 if (!GetXFormColorSpaces(nProfiles
, hProfiles
, &EntryColorSpace
, &ExitColorSpace
)) {
676 cmsSignalError(ContextID
, cmsERROR_NULL
, "NULL input profiles on transform");
680 // Check if proper colorspaces
681 if (!IsProperColorSpace(EntryColorSpace
, InputFormat
)) {
682 cmsSignalError(ContextID
, cmsERROR_COLORSPACE_CHECK
, "Wrong input color space on transform");
686 if (!IsProperColorSpace(ExitColorSpace
, OutputFormat
)) {
687 cmsSignalError(ContextID
, cmsERROR_COLORSPACE_CHECK
, "Wrong output color space on transform");
691 // Create a pipeline with all transformations
692 Lut
= _cmsLinkProfiles(ContextID
, nProfiles
, Intents
, hProfiles
, BPC
, AdaptationStates
, dwFlags
);
694 cmsSignalError(ContextID
, cmsERROR_NOT_SUITABLE
, "Couldn't link the profiles");
698 // Check channel count
699 if ((cmsChannelsOf(EntryColorSpace
) != cmsPipelineInputChannels(Lut
)) ||
700 (cmsChannelsOf(ExitColorSpace
) != cmsPipelineOutputChannels(Lut
))) {
701 cmsSignalError(ContextID
, cmsERROR_NOT_SUITABLE
, "Channel count doesn't match. Profile is corrupted");
707 xform
= AllocEmptyTransform(ContextID
, Lut
, LastIntent
, &InputFormat
, &OutputFormat
, &dwFlags
);
713 xform
->EntryColorSpace
= EntryColorSpace
;
714 xform
->ExitColorSpace
= ExitColorSpace
;
715 xform
->RenderingIntent
= Intents
[nProfiles
-1];
718 SetWhitePoint(&xform
->EntryWhitePoint
, (cmsCIEXYZ
*) cmsReadTag(hProfiles
[0], cmsSigMediaWhitePointTag
));
719 SetWhitePoint(&xform
->ExitWhitePoint
, (cmsCIEXYZ
*) cmsReadTag(hProfiles
[nProfiles
-1], cmsSigMediaWhitePointTag
));
722 // Create a gamut check LUT if requested
723 if (hGamutProfile
!= NULL
&& (dwFlags
& cmsFLAGS_GAMUTCHECK
))
724 xform
->GamutCheck
= _cmsCreateGamutCheckPipeline(ContextID
, hProfiles
,
731 // Try to read input and output colorant table
732 if (cmsIsTag(hProfiles
[0], cmsSigColorantTableTag
)) {
734 // Input table can only come in this way.
735 xform
->InputColorant
= cmsDupNamedColorList((cmsNAMEDCOLORLIST
*) cmsReadTag(hProfiles
[0], cmsSigColorantTableTag
));
738 // Output is a little bit more complex.
739 if (cmsGetDeviceClass(hProfiles
[nProfiles
-1]) == cmsSigLinkClass
) {
741 // This tag may exist only on devicelink profiles.
742 if (cmsIsTag(hProfiles
[nProfiles
-1], cmsSigColorantTableOutTag
)) {
744 // It may be NULL if error
745 xform
->OutputColorant
= cmsDupNamedColorList((cmsNAMEDCOLORLIST
*) cmsReadTag(hProfiles
[nProfiles
-1], cmsSigColorantTableOutTag
));
750 if (cmsIsTag(hProfiles
[nProfiles
-1], cmsSigColorantTableTag
)) {
752 xform
-> OutputColorant
= cmsDupNamedColorList((cmsNAMEDCOLORLIST
*) cmsReadTag(hProfiles
[nProfiles
-1], cmsSigColorantTableTag
));
756 // Store the sequence of profiles
757 if (dwFlags
& cmsFLAGS_KEEP_SEQUENCE
) {
758 xform
->Sequence
= _cmsCompileProfileSequence(ContextID
, nProfiles
, hProfiles
);
761 xform
->Sequence
= NULL
;
763 // If this is a cached transform, init first value, which is zero (16 bits only)
764 if (!(dwFlags
& cmsFLAGS_NOCACHE
)) {
766 memset(&xform
->Cache
.CacheIn
, 0, sizeof(xform
->Cache
.CacheIn
));
768 if (xform
->GamutCheck
!= NULL
) {
769 TransformOnePixelWithGamutCheck(xform
, xform
->Cache
.CacheIn
, xform
->Cache
.CacheOut
);
773 xform
->Lut
->Eval16Fn(xform
->Cache
.CacheIn
, xform
->Cache
.CacheOut
, xform
-> Lut
->Data
);
778 return (cmsHTRANSFORM
) xform
;
781 // Multiprofile transforms: Gamut check is not available here, as it is unclear from which profile the gamut comes.
782 cmsHTRANSFORM CMSEXPORT
cmsCreateMultiprofileTransformTHR(cmsContext ContextID
,
783 cmsHPROFILE hProfiles
[],
784 cmsUInt32Number nProfiles
,
785 cmsUInt32Number InputFormat
,
786 cmsUInt32Number OutputFormat
,
787 cmsUInt32Number Intent
,
788 cmsUInt32Number dwFlags
)
792 cmsUInt32Number Intents
[256];
793 cmsFloat64Number AdaptationStates
[256];
795 if (nProfiles
<= 0 || nProfiles
> 255) {
796 cmsSignalError(ContextID
, cmsERROR_RANGE
, "Wrong number of profiles. 1..255 expected, %d found.", nProfiles
);
800 for (i
=0; i
< nProfiles
; i
++) {
801 BPC
[i
] = dwFlags
& cmsFLAGS_BLACKPOINTCOMPENSATION
? TRUE
: FALSE
;
803 AdaptationStates
[i
] = GlobalAdaptationState
;
807 return cmsCreateExtendedTransform(ContextID
, nProfiles
, hProfiles
, BPC
, Intents
, AdaptationStates
, NULL
, 0, InputFormat
, OutputFormat
, dwFlags
);
812 cmsHTRANSFORM CMSEXPORT
cmsCreateMultiprofileTransform(cmsHPROFILE hProfiles
[],
813 cmsUInt32Number nProfiles
,
814 cmsUInt32Number InputFormat
,
815 cmsUInt32Number OutputFormat
,
816 cmsUInt32Number Intent
,
817 cmsUInt32Number dwFlags
)
820 if (nProfiles
<= 0 || nProfiles
> 255) {
821 cmsSignalError(NULL
, cmsERROR_RANGE
, "Wrong number of profiles. 1..255 expected, %d found.", nProfiles
);
825 return cmsCreateMultiprofileTransformTHR(cmsGetProfileContextID(hProfiles
[0]),
834 cmsHTRANSFORM CMSEXPORT
cmsCreateTransformTHR(cmsContext ContextID
,
836 cmsUInt32Number InputFormat
,
838 cmsUInt32Number OutputFormat
,
839 cmsUInt32Number Intent
,
840 cmsUInt32Number dwFlags
)
843 cmsHPROFILE hArray
[2];
848 return cmsCreateMultiprofileTransformTHR(ContextID
, hArray
, Output
== NULL
? 1 : 2, InputFormat
, OutputFormat
, Intent
, dwFlags
);
851 CMSAPI cmsHTRANSFORM CMSEXPORT
cmsCreateTransform(cmsHPROFILE Input
,
852 cmsUInt32Number InputFormat
,
854 cmsUInt32Number OutputFormat
,
855 cmsUInt32Number Intent
,
856 cmsUInt32Number dwFlags
)
858 return cmsCreateTransformTHR(cmsGetProfileContextID(Input
), Input
, InputFormat
, Output
, OutputFormat
, Intent
, dwFlags
);
862 cmsHTRANSFORM CMSEXPORT
cmsCreateProofingTransformTHR(cmsContext ContextID
,
863 cmsHPROFILE InputProfile
,
864 cmsUInt32Number InputFormat
,
865 cmsHPROFILE OutputProfile
,
866 cmsUInt32Number OutputFormat
,
867 cmsHPROFILE ProofingProfile
,
868 cmsUInt32Number nIntent
,
869 cmsUInt32Number ProofingIntent
,
870 cmsUInt32Number dwFlags
)
872 cmsHPROFILE hArray
[4];
873 cmsUInt32Number Intents
[4];
875 cmsFloat64Number Adaptation
[4];
876 cmsBool DoBPC
= (dwFlags
& cmsFLAGS_BLACKPOINTCOMPENSATION
) ? TRUE
: FALSE
;
879 hArray
[0] = InputProfile
; hArray
[1] = ProofingProfile
; hArray
[2] = ProofingProfile
; hArray
[3] = OutputProfile
;
880 Intents
[0] = nIntent
; Intents
[1] = nIntent
; Intents
[2] = INTENT_RELATIVE_COLORIMETRIC
; Intents
[3] = ProofingIntent
;
881 BPC
[0] = DoBPC
; BPC
[1] = DoBPC
; BPC
[2] = 0; BPC
[3] = 0;
883 Adaptation
[0] = Adaptation
[1] = Adaptation
[2] = Adaptation
[3] = GlobalAdaptationState
;
885 if (!(dwFlags
& (cmsFLAGS_SOFTPROOFING
|cmsFLAGS_GAMUTCHECK
)))
886 return cmsCreateTransformTHR(ContextID
, InputProfile
, InputFormat
, OutputProfile
, OutputFormat
, nIntent
, dwFlags
);
888 return cmsCreateExtendedTransform(ContextID
, 4, hArray
, BPC
, Intents
, Adaptation
,
889 ProofingProfile
, 1, InputFormat
, OutputFormat
, dwFlags
);
894 cmsHTRANSFORM CMSEXPORT
cmsCreateProofingTransform(cmsHPROFILE InputProfile
,
895 cmsUInt32Number InputFormat
,
896 cmsHPROFILE OutputProfile
,
897 cmsUInt32Number OutputFormat
,
898 cmsHPROFILE ProofingProfile
,
899 cmsUInt32Number nIntent
,
900 cmsUInt32Number ProofingIntent
,
901 cmsUInt32Number dwFlags
)
903 return cmsCreateProofingTransformTHR(cmsGetProfileContextID(InputProfile
),
915 // Grab the ContextID from an open transform. Returns NULL if a NULL transform is passed
916 cmsContext CMSEXPORT
cmsGetTransformContextID(cmsHTRANSFORM hTransform
)
918 _cmsTRANSFORM
* xform
= (_cmsTRANSFORM
*) hTransform
;
920 if (xform
== NULL
) return NULL
;
921 return xform
-> ContextID
;
924 // Grab the input/output formats
925 cmsUInt32Number CMSEXPORT
cmsGetTransformInputFormat(cmsHTRANSFORM hTransform
)
927 _cmsTRANSFORM
* xform
= (_cmsTRANSFORM
*) hTransform
;
929 if (xform
== NULL
) return 0;
930 return xform
->InputFormat
;
933 cmsUInt32Number CMSEXPORT
cmsGetTransformOutputFormat(cmsHTRANSFORM hTransform
)
935 _cmsTRANSFORM
* xform
= (_cmsTRANSFORM
*) hTransform
;
937 if (xform
== NULL
) return 0;
938 return xform
->OutputFormat
;
941 // For backwards compatibility
942 cmsBool CMSEXPORT
cmsChangeBuffersFormat(cmsHTRANSFORM hTransform
,
943 cmsUInt32Number InputFormat
,
944 cmsUInt32Number OutputFormat
)
947 _cmsTRANSFORM
* xform
= (_cmsTRANSFORM
*) hTransform
;
948 cmsFormatter16 FromInput
, ToOutput
;
951 // We only can afford to change formatters if previous transform is at least 16 bits
952 if (!(xform
->dwOriginalFlags
& cmsFLAGS_CAN_CHANGE_FORMATTER
)) {
954 cmsSignalError(xform
->ContextID
, cmsERROR_NOT_SUITABLE
, "cmsChangeBuffersFormat works only on transforms created originally with at least 16 bits of precision");
958 FromInput
= _cmsGetFormatter(InputFormat
, cmsFormatterInput
, CMS_PACK_FLAGS_16BITS
).Fmt16
;
959 ToOutput
= _cmsGetFormatter(OutputFormat
, cmsFormatterOutput
, CMS_PACK_FLAGS_16BITS
).Fmt16
;
961 if (FromInput
== NULL
|| ToOutput
== NULL
) {
963 cmsSignalError(xform
-> ContextID
, cmsERROR_UNKNOWN_EXTENSION
, "Unsupported raster format");
967 xform
->InputFormat
= InputFormat
;
968 xform
->OutputFormat
= OutputFormat
;
969 xform
->FromInput
= FromInput
;
970 xform
->ToOutput
= ToOutput
;