1 //---------------------------------------------------------------------------------
3 // Little Color Management System
4 // Copyright (c) 1998-2014 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);
175 void CMSEXPORT cmsDoTransform(cmsHTRANSFORM Transform,
176 const void* InputBuffer,
178 cmsUInt32Number Size)
181 _cmsTRANSFORM* p = (_cmsTRANSFORM*) Transform;
183 p -> xform(p, InputBuffer, OutputBuffer, Size, Size);
188 void CMSEXPORT cmsDoTransformStride(cmsHTRANSFORM Transform,
189 const void* InputBuffer,
191 cmsUInt32Number Size, cmsUInt32Number Stride)
194 _cmsTRANSFORM* p = (_cmsTRANSFORM*) Transform;
196 p -> xform(p, InputBuffer, OutputBuffer, Size, Stride);
200 // Transform routines ----------------------------------------------------------------------------------------------------------
202 // Float xform converts floats. Since there are no performance issues, one routine does all job, including gamut check.
203 // Note that because extended range, we can use a -1.0 value for out of gamut in this case.
205 void FloatXFORM(_cmsTRANSFORM* p,
207 void* out, cmsUInt32Number Size, cmsUInt32Number Stride)
209 cmsUInt8Number* accum;
210 cmsUInt8Number* output;
211 cmsFloat32Number fIn[cmsMAXCHANNELS], fOut[cmsMAXCHANNELS];
212 cmsFloat32Number OutOfGamut;
213 cmsUInt32Number i, j;
215 accum = (cmsUInt8Number*) in;
216 output = (cmsUInt8Number*) out;
218 for (i=0; i < Size; i++) {
220 accum = p -> FromInputFloat(p, fIn, accum, Stride);
222 // Any gamut chack to do?
223 if (p ->GamutCheck != NULL) {
225 // Evaluate gamut marker.
226 cmsPipelineEvalFloat( fIn, &OutOfGamut, p ->GamutCheck);
228 // Is current color out of gamut?
229 if (OutOfGamut > 0.0) {
231 // Certainly, out of gamut
232 for (j=0; j < cmsMAXCHANNELS; j++)
237 // No, proceed normally
238 cmsPipelineEvalFloat(fIn, fOut, p -> Lut);
243 // No gamut check at all
244 cmsPipelineEvalFloat(fIn, fOut, p -> Lut);
247 // Back to asked representation
248 output = p -> ToOutputFloat(p, fOut, output, Stride);
254 void NullFloatXFORM(_cmsTRANSFORM* p,
257 cmsUInt32Number Size,
258 cmsUInt32Number Stride)
260 cmsUInt8Number* accum;
261 cmsUInt8Number* output;
262 cmsFloat32Number fIn[cmsMAXCHANNELS];
263 cmsUInt32Number i, n;
265 accum = (cmsUInt8Number*) in;
266 output = (cmsUInt8Number*) out;
269 for (i=0; i < n; i++) {
271 accum = p -> FromInputFloat(p, fIn, accum, Stride);
272 output = p -> ToOutputFloat(p, fIn, output, Stride);
276 // 16 bit precision -----------------------------------------------------------------------------------------------------------
278 // Null transformation, only applies formatters. No cach�
280 void NullXFORM(_cmsTRANSFORM* p,
282 void* out, cmsUInt32Number Size,
283 cmsUInt32Number Stride)
285 cmsUInt8Number* accum;
286 cmsUInt8Number* output;
287 cmsUInt16Number wIn[cmsMAXCHANNELS];
288 cmsUInt32Number i, n;
290 accum = (cmsUInt8Number*) in;
291 output = (cmsUInt8Number*) out;
292 n = Size; // Buffer len
294 for (i=0; i < n; i++) {
296 accum = p -> FromInput(p, wIn, accum, Stride);
297 output = p -> ToOutput(p, wIn, output, Stride);
302 // No gamut check, no cache, 16 bits
304 void PrecalculatedXFORM(_cmsTRANSFORM* p,
306 void* out, cmsUInt32Number Size, cmsUInt32Number Stride)
308 register cmsUInt8Number* accum;
309 register cmsUInt8Number* output;
310 cmsUInt16Number wIn[cmsMAXCHANNELS], wOut[cmsMAXCHANNELS];
311 cmsUInt32Number i, n;
313 accum = (cmsUInt8Number*) in;
314 output = (cmsUInt8Number*) out;
317 for (i=0; i < n; i++) {
319 accum = p -> FromInput(p, wIn, accum, Stride);
320 p ->Lut ->Eval16Fn(wIn, wOut, p -> Lut->Data);
321 output = p -> ToOutput(p, wOut, output, Stride);
326 // Auxiliar: Handle precalculated gamut check. The retrieval of context may be alittle bit slow, but this function is not critical.
328 void TransformOnePixelWithGamutCheck(_cmsTRANSFORM* p,
329 const cmsUInt16Number wIn[],
330 cmsUInt16Number wOut[])
332 cmsUInt16Number wOutOfGamut;
334 p ->GamutCheck ->Eval16Fn(wIn, &wOutOfGamut, p ->GamutCheck ->Data);
335 if (wOutOfGamut >= 1) {
338 _cmsAlarmCodesChunkType* ContextAlarmCodes = (_cmsAlarmCodesChunkType*) _cmsContextGetClientChunk(p->ContextID, AlarmCodesContext);
340 for (i=0; i < p ->Lut->OutputChannels; i++) {
342 wOut[i] = ContextAlarmCodes ->AlarmCodes[i];
346 p ->Lut ->Eval16Fn(wIn, wOut, p -> Lut->Data);
349 // Gamut check, No cach�, 16 bits.
351 void PrecalculatedXFORMGamutCheck(_cmsTRANSFORM* p,
353 void* out, cmsUInt32Number Size, cmsUInt32Number Stride)
355 cmsUInt8Number* accum;
356 cmsUInt8Number* output;
357 cmsUInt16Number wIn[cmsMAXCHANNELS], wOut[cmsMAXCHANNELS];
358 cmsUInt32Number i, n;
360 accum = (cmsUInt8Number*) in;
361 output = (cmsUInt8Number*) out;
362 n = Size; // Buffer len
364 for (i=0; i < n; i++) {
366 accum = p -> FromInput(p, wIn, accum, Stride);
367 TransformOnePixelWithGamutCheck(p, wIn, wOut);
368 output = p -> ToOutput(p, wOut, output, Stride);
373 // No gamut check, Cach�, 16 bits,
375 void CachedXFORM(_cmsTRANSFORM* p,
377 void* out, cmsUInt32Number Size, cmsUInt32Number Stride)
379 cmsUInt8Number* accum;
380 cmsUInt8Number* output;
381 cmsUInt16Number wIn[cmsMAXCHANNELS], wOut[cmsMAXCHANNELS];
382 cmsUInt32Number i, n;
385 accum = (cmsUInt8Number*) in;
386 output = (cmsUInt8Number*) out;
387 n = Size; // Buffer len
389 // Empty buffers for quick memcmp
390 memset(wIn, 0, sizeof(wIn));
391 memset(wOut, 0, sizeof(wOut));
393 // Get copy of zero cache
394 memcpy(&Cache, &p ->Cache, sizeof(Cache));
396 for (i=0; i < n; i++) {
398 accum = p -> FromInput(p, wIn, accum, Stride);
400 if (memcmp(wIn, Cache.CacheIn, sizeof(Cache.CacheIn)) == 0) {
402 memcpy(wOut, Cache.CacheOut, sizeof(Cache.CacheOut));
406 p ->Lut ->Eval16Fn(wIn, wOut, p -> Lut->Data);
408 memcpy(Cache.CacheIn, wIn, sizeof(Cache.CacheIn));
409 memcpy(Cache.CacheOut, wOut, sizeof(Cache.CacheOut));
412 output = p -> ToOutput(p, wOut, output, Stride);
418 // All those nice features together
420 void CachedXFORMGamutCheck(_cmsTRANSFORM* p,
422 void* out, cmsUInt32Number Size, cmsUInt32Number Stride)
424 cmsUInt8Number* accum;
425 cmsUInt8Number* output;
426 cmsUInt16Number wIn[cmsMAXCHANNELS], wOut[cmsMAXCHANNELS];
427 cmsUInt32Number i, n;
430 accum = (cmsUInt8Number*) in;
431 output = (cmsUInt8Number*) out;
432 n = Size; // Buffer len
434 // Empty buffers for quick memcmp
435 memset(wIn, 0, sizeof(cmsUInt16Number) * cmsMAXCHANNELS);
436 memset(wOut, 0, sizeof(cmsUInt16Number) * cmsMAXCHANNELS);
438 // Get copy of zero cache
439 memcpy(&Cache, &p ->Cache, sizeof(Cache));
441 for (i=0; i < n; i++) {
443 accum = p -> FromInput(p, wIn, accum, Stride);
445 if (memcmp(wIn, Cache.CacheIn, sizeof(Cache.CacheIn)) == 0) {
446 memcpy(wOut, Cache.CacheOut, sizeof(Cache.CacheOut));
449 TransformOnePixelWithGamutCheck(p, wIn, wOut);
450 memcpy(Cache.CacheIn, wIn, sizeof(Cache.CacheIn));
451 memcpy(Cache.CacheOut, wOut, sizeof(Cache.CacheOut));
454 output = p -> ToOutput(p, wOut, output, Stride);
459 // -------------------------------------------------------------------------------------------------------------
461 // List of used-defined transform factories
462 typedef struct _cmsTransformCollection_st {
464 _cmsTransformFactory Factory;
465 struct _cmsTransformCollection_st *Next;
467 } _cmsTransformCollection;
469 // The linked list head
470 _cmsTransformPluginChunkType _cmsTransformPluginChunk = { NULL };
473 // Duplicates the zone of memory used by the plug-in in the new context
475 void DupPluginTransformList(struct _cmsContext_struct* ctx,
476 const struct _cmsContext_struct* src)
478 _cmsTransformPluginChunkType newHead = { NULL };
479 _cmsTransformCollection* entry;
480 _cmsTransformCollection* Anterior = NULL;
481 _cmsTransformPluginChunkType* head = (_cmsTransformPluginChunkType*) src->chunks[TransformPlugin];
483 // Walk the list copying all nodes
484 for (entry = head->TransformCollection;
486 entry = entry ->Next) {
488 _cmsTransformCollection *newEntry = ( _cmsTransformCollection *) _cmsSubAllocDup(ctx ->MemPool, entry, sizeof(_cmsTransformCollection));
490 if (newEntry == NULL)
493 // We want to keep the linked list order, so this is a little bit tricky
494 newEntry -> Next = NULL;
496 Anterior -> Next = newEntry;
500 if (newHead.TransformCollection == NULL)
501 newHead.TransformCollection = newEntry;
504 ctx ->chunks[TransformPlugin] = _cmsSubAllocDup(ctx->MemPool, &newHead, sizeof(_cmsTransformPluginChunkType));
507 void _cmsAllocTransformPluginChunk(struct _cmsContext_struct* ctx,
508 const struct _cmsContext_struct* src)
512 // Copy all linked list
513 DupPluginTransformList(ctx, src);
516 static _cmsTransformPluginChunkType TransformPluginChunkType = { NULL };
517 ctx ->chunks[TransformPlugin] = _cmsSubAllocDup(ctx ->MemPool, &TransformPluginChunkType, sizeof(_cmsTransformPluginChunkType));
523 // Register new ways to transform
524 cmsBool _cmsRegisterTransformPlugin(cmsContext ContextID, cmsPluginBase* Data)
526 cmsPluginTransform* Plugin = (cmsPluginTransform*) Data;
527 _cmsTransformCollection* fl;
528 _cmsTransformPluginChunkType* ctx = ( _cmsTransformPluginChunkType*) _cmsContextGetClientChunk(ContextID,TransformPlugin);
532 // Free the chain. Memory is safely freed at exit
533 ctx->TransformCollection = NULL;
537 // Factory callback is required
538 if (Plugin ->Factory == NULL) return FALSE;
541 fl = (_cmsTransformCollection*) _cmsPluginMalloc(ContextID, sizeof(_cmsTransformCollection));
542 if (fl == NULL) return FALSE;
544 // Copy the parameters
545 fl ->Factory = Plugin ->Factory;
548 fl ->Next = ctx->TransformCollection;
549 ctx->TransformCollection = fl;
556 void CMSEXPORT _cmsSetTransformUserData(struct _cmstransform_struct *CMMcargo, void* ptr, _cmsFreeUserDataFn FreePrivateDataFn)
558 _cmsAssert(CMMcargo != NULL);
559 CMMcargo ->UserData = ptr;
560 CMMcargo ->FreeUserData = FreePrivateDataFn;
563 // returns the pointer defined by the plug-in to store private data
564 void * CMSEXPORT _cmsGetTransformUserData(struct _cmstransform_struct *CMMcargo)
566 _cmsAssert(CMMcargo != NULL);
567 return CMMcargo ->UserData;
570 // returns the current formatters
571 void CMSEXPORT _cmsGetTransformFormatters16(struct _cmstransform_struct *CMMcargo, cmsFormatter16* FromInput, cmsFormatter16* ToOutput)
573 _cmsAssert(CMMcargo != NULL);
574 if (FromInput) *FromInput = CMMcargo ->FromInput;
575 if (ToOutput) *ToOutput = CMMcargo ->ToOutput;
578 void CMSEXPORT _cmsGetTransformFormattersFloat(struct _cmstransform_struct *CMMcargo, cmsFormatterFloat* FromInput, cmsFormatterFloat* ToOutput)
580 _cmsAssert(CMMcargo != NULL);
581 if (FromInput) *FromInput = CMMcargo ->FromInputFloat;
582 if (ToOutput) *ToOutput = CMMcargo ->ToOutputFloat;
586 // Allocate transform struct and set it to defaults. Ask the optimization plug-in about if those formats are proper
587 // for separated transforms. If this is the case,
589 _cmsTRANSFORM* AllocEmptyTransform(cmsContext ContextID, cmsPipeline* lut,
590 cmsUInt32Number Intent, cmsUInt32Number* InputFormat, cmsUInt32Number* OutputFormat, cmsUInt32Number* dwFlags)
592 _cmsTransformPluginChunkType* ctx = ( _cmsTransformPluginChunkType*) _cmsContextGetClientChunk(ContextID, TransformPlugin);
593 _cmsTransformCollection* Plugin;
595 // Allocate needed memory
596 _cmsTRANSFORM* p = (_cmsTRANSFORM*) _cmsMallocZero(ContextID, sizeof(_cmsTRANSFORM));
599 // Store the proposed pipeline
602 // Let's see if any plug-in want to do the transform by itself
603 for (Plugin = ctx ->TransformCollection;
605 Plugin = Plugin ->Next) {
607 if (Plugin ->Factory(&p->xform, &p->UserData, &p ->FreeUserData, &p ->Lut, InputFormat, OutputFormat, dwFlags)) {
609 // Last plugin in the declaration order takes control. We just keep
610 // the original parameters as a logging.
611 // Note that cmsFLAGS_CAN_CHANGE_FORMATTER is not set, so by default
612 // an optimized transform is not reusable. The plug-in can, however, change
613 // the flags and make it suitable.
615 p ->ContextID = ContextID;
616 p ->InputFormat = *InputFormat;
617 p ->OutputFormat = *OutputFormat;
618 p ->dwOriginalFlags = *dwFlags;
620 // Fill the formatters just in case the optimized routine is interested.
621 // No error is thrown if the formatter doesn't exist. It is up to the optimization
622 // factory to decide what to do in those cases.
623 p ->FromInput = _cmsGetFormatter(ContextID, *InputFormat, cmsFormatterInput, CMS_PACK_FLAGS_16BITS).Fmt16;
624 p ->ToOutput = _cmsGetFormatter(ContextID, *OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_16BITS).Fmt16;
625 p ->FromInputFloat = _cmsGetFormatter(ContextID, *InputFormat, cmsFormatterInput, CMS_PACK_FLAGS_FLOAT).FmtFloat;
626 p ->ToOutputFloat = _cmsGetFormatter(ContextID, *OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_FLOAT).FmtFloat;
632 // Not suitable for the transform plug-in, let's check the pipeline plug-in
634 _cmsOptimizePipeline(ContextID, &p->Lut, Intent, InputFormat, OutputFormat, dwFlags);
636 // Check whatever this is a true floating point transform
637 if (_cmsFormatterIsFloat(*InputFormat) && _cmsFormatterIsFloat(*OutputFormat)) {
639 // Get formatter function always return a valid union, but the contents of this union may be NULL.
640 p ->FromInputFloat = _cmsGetFormatter(ContextID, *InputFormat, cmsFormatterInput, CMS_PACK_FLAGS_FLOAT).FmtFloat;
641 p ->ToOutputFloat = _cmsGetFormatter(ContextID, *OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_FLOAT).FmtFloat;
642 *dwFlags |= cmsFLAGS_CAN_CHANGE_FORMATTER;
644 if (p ->FromInputFloat == NULL || p ->ToOutputFloat == NULL) {
646 cmsSignalError(ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported raster format");
647 _cmsFree(ContextID, p);
651 if (*dwFlags & cmsFLAGS_NULLTRANSFORM) {
653 p ->xform = NullFloatXFORM;
656 // Float transforms don't use cach�, always are non-NULL
657 p ->xform = FloatXFORM;
663 if (*InputFormat == 0 && *OutputFormat == 0) {
664 p ->FromInput = p ->ToOutput = NULL;
665 *dwFlags |= cmsFLAGS_CAN_CHANGE_FORMATTER;
669 int BytesPerPixelInput;
671 p ->FromInput = _cmsGetFormatter(ContextID, *InputFormat, cmsFormatterInput, CMS_PACK_FLAGS_16BITS).Fmt16;
672 p ->ToOutput = _cmsGetFormatter(ContextID, *OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_16BITS).Fmt16;
674 if (p ->FromInput == NULL || p ->ToOutput == NULL) {
676 cmsSignalError(ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported raster format");
677 _cmsFree(ContextID, p);
681 BytesPerPixelInput = T_BYTES(p ->InputFormat);
682 if (BytesPerPixelInput == 0 || BytesPerPixelInput >= 2)
683 *dwFlags |= cmsFLAGS_CAN_CHANGE_FORMATTER;
687 if (*dwFlags & cmsFLAGS_NULLTRANSFORM) {
689 p ->xform = NullXFORM;
692 if (*dwFlags & cmsFLAGS_NOCACHE) {
694 if (*dwFlags & cmsFLAGS_GAMUTCHECK)
695 p ->xform = PrecalculatedXFORMGamutCheck; // Gamut check, no cach�
697 p ->xform = PrecalculatedXFORM; // No cach�, no gamut check
701 if (*dwFlags & cmsFLAGS_GAMUTCHECK)
702 p ->xform = CachedXFORMGamutCheck; // Gamut check, cach�
704 p ->xform = CachedXFORM; // No gamut check, cach�
710 p ->InputFormat = *InputFormat;
711 p ->OutputFormat = *OutputFormat;
712 p ->dwOriginalFlags = *dwFlags;
713 p ->ContextID = ContextID;
719 cmsBool GetXFormColorSpaces(int nProfiles, cmsHPROFILE hProfiles[], cmsColorSpaceSignature* Input, cmsColorSpaceSignature* Output)
721 cmsColorSpaceSignature ColorSpaceIn, ColorSpaceOut;
722 cmsColorSpaceSignature PostColorSpace;
725 if (nProfiles <= 0) return FALSE;
726 if (hProfiles[0] == NULL) return FALSE;
728 *Input = PostColorSpace = cmsGetColorSpace(hProfiles[0]);
730 for (i=0; i < nProfiles; i++) {
732 cmsProfileClassSignature cls;
733 cmsHPROFILE hProfile = hProfiles[i];
735 int lIsInput = (PostColorSpace != cmsSigXYZData) &&
736 (PostColorSpace != cmsSigLabData);
738 if (hProfile == NULL) return FALSE;
740 cls = cmsGetDeviceClass(hProfile);
742 if (cls == cmsSigNamedColorClass) {
744 ColorSpaceIn = cmsSig1colorData;
745 ColorSpaceOut = (nProfiles > 1) ? cmsGetPCS(hProfile) : cmsGetColorSpace(hProfile);
748 if (lIsInput || (cls == cmsSigLinkClass)) {
750 ColorSpaceIn = cmsGetColorSpace(hProfile);
751 ColorSpaceOut = cmsGetPCS(hProfile);
755 ColorSpaceIn = cmsGetPCS(hProfile);
756 ColorSpaceOut = cmsGetColorSpace(hProfile);
760 *Input = ColorSpaceIn;
762 PostColorSpace = ColorSpaceOut;
765 *Output = PostColorSpace;
772 cmsBool IsProperColorSpace(cmsColorSpaceSignature Check, cmsUInt32Number dwFormat)
774 int Space1 = T_COLORSPACE(dwFormat);
775 int Space2 = _cmsLCMScolorSpace(Check);
777 if (Space1 == PT_ANY) return TRUE;
778 if (Space1 == Space2) return TRUE;
780 if (Space1 == PT_LabV2 && Space2 == PT_Lab) return TRUE;
781 if (Space1 == PT_Lab && Space2 == PT_LabV2) return TRUE;
786 // ----------------------------------------------------------------------------------------------------------------
789 void SetWhitePoint(cmsCIEXYZ* wtPt, const cmsCIEXYZ* src)
804 // New to lcms 2.0 -- have all parameters available.
805 cmsHTRANSFORM CMSEXPORT cmsCreateExtendedTransform(cmsContext ContextID,
806 cmsUInt32Number nProfiles, cmsHPROFILE hProfiles[],
808 cmsUInt32Number Intents[],
809 cmsFloat64Number AdaptationStates[],
810 cmsHPROFILE hGamutProfile,
811 cmsUInt32Number nGamutPCSposition,
812 cmsUInt32Number InputFormat,
813 cmsUInt32Number OutputFormat,
814 cmsUInt32Number dwFlags)
816 _cmsTRANSFORM* xform;
817 cmsColorSpaceSignature EntryColorSpace;
818 cmsColorSpaceSignature ExitColorSpace;
820 cmsUInt32Number LastIntent = Intents[nProfiles-1];
822 // If it is a fake transform
823 if (dwFlags & cmsFLAGS_NULLTRANSFORM)
825 return AllocEmptyTransform(ContextID, NULL, INTENT_PERCEPTUAL, &InputFormat, &OutputFormat, &dwFlags);
828 // If gamut check is requested, make sure we have a gamut profile
829 if (dwFlags & cmsFLAGS_GAMUTCHECK) {
830 if (hGamutProfile == NULL) dwFlags &= ~cmsFLAGS_GAMUTCHECK;
833 // On floating point transforms, inhibit cache
834 if (_cmsFormatterIsFloat(InputFormat) || _cmsFormatterIsFloat(OutputFormat))
835 dwFlags |= cmsFLAGS_NOCACHE;
837 // Mark entry/exit spaces
838 if (!GetXFormColorSpaces(nProfiles, hProfiles, &EntryColorSpace, &ExitColorSpace)) {
839 cmsSignalError(ContextID, cmsERROR_NULL, "NULL input profiles on transform");
843 // Check if proper colorspaces
844 if (!IsProperColorSpace(EntryColorSpace, InputFormat)) {
845 cmsSignalError(ContextID, cmsERROR_COLORSPACE_CHECK, "Wrong input color space on transform");
849 if (!IsProperColorSpace(ExitColorSpace, OutputFormat)) {
850 cmsSignalError(ContextID, cmsERROR_COLORSPACE_CHECK, "Wrong output color space on transform");
854 // Create a pipeline with all transformations
855 Lut = _cmsLinkProfiles(ContextID, nProfiles, Intents, hProfiles, BPC, AdaptationStates, dwFlags);
857 cmsSignalError(ContextID, cmsERROR_NOT_SUITABLE, "Couldn't link the profiles");
861 // Check channel count
862 if ((cmsChannelsOf(EntryColorSpace) != cmsPipelineInputChannels(Lut)) ||
863 (cmsChannelsOf(ExitColorSpace) != cmsPipelineOutputChannels(Lut))) {
864 cmsPipelineFree(Lut);
865 cmsSignalError(ContextID, cmsERROR_NOT_SUITABLE, "Channel count doesn't match. Profile is corrupted");
871 xform = AllocEmptyTransform(ContextID, Lut, LastIntent, &InputFormat, &OutputFormat, &dwFlags);
877 xform ->EntryColorSpace = EntryColorSpace;
878 xform ->ExitColorSpace = ExitColorSpace;
879 xform ->RenderingIntent = Intents[nProfiles-1];
882 SetWhitePoint(&xform->EntryWhitePoint, (cmsCIEXYZ*) cmsReadTag(hProfiles[0], cmsSigMediaWhitePointTag));
883 SetWhitePoint(&xform->ExitWhitePoint, (cmsCIEXYZ*) cmsReadTag(hProfiles[nProfiles-1], cmsSigMediaWhitePointTag));
886 // Create a gamut check LUT if requested
887 if (hGamutProfile != NULL && (dwFlags & cmsFLAGS_GAMUTCHECK))
888 xform ->GamutCheck = _cmsCreateGamutCheckPipeline(ContextID, hProfiles,
895 // Try to read input and output colorant table
896 if (cmsIsTag(hProfiles[0], cmsSigColorantTableTag)) {
898 // Input table can only come in this way.
899 xform ->InputColorant = cmsDupNamedColorList((cmsNAMEDCOLORLIST*) cmsReadTag(hProfiles[0], cmsSigColorantTableTag));
902 // Output is a little bit more complex.
903 if (cmsGetDeviceClass(hProfiles[nProfiles-1]) == cmsSigLinkClass) {
905 // This tag may exist only on devicelink profiles.
906 if (cmsIsTag(hProfiles[nProfiles-1], cmsSigColorantTableOutTag)) {
908 // It may be NULL if error
909 xform ->OutputColorant = cmsDupNamedColorList((cmsNAMEDCOLORLIST*) cmsReadTag(hProfiles[nProfiles-1], cmsSigColorantTableOutTag));
914 if (cmsIsTag(hProfiles[nProfiles-1], cmsSigColorantTableTag)) {
916 xform -> OutputColorant = cmsDupNamedColorList((cmsNAMEDCOLORLIST*) cmsReadTag(hProfiles[nProfiles-1], cmsSigColorantTableTag));
920 // Store the sequence of profiles
921 if (dwFlags & cmsFLAGS_KEEP_SEQUENCE) {
922 xform ->Sequence = _cmsCompileProfileSequence(ContextID, nProfiles, hProfiles);
925 xform ->Sequence = NULL;
927 // If this is a cached transform, init first value, which is zero (16 bits only)
928 if (!(dwFlags & cmsFLAGS_NOCACHE)) {
930 memset(&xform ->Cache.CacheIn, 0, sizeof(xform ->Cache.CacheIn));
932 if (xform ->GamutCheck != NULL) {
933 TransformOnePixelWithGamutCheck(xform, xform ->Cache.CacheIn, xform->Cache.CacheOut);
937 xform ->Lut ->Eval16Fn(xform ->Cache.CacheIn, xform->Cache.CacheOut, xform -> Lut->Data);
942 return (cmsHTRANSFORM) xform;
945 // Multiprofile transforms: Gamut check is not available here, as it is unclear from which profile the gamut comes.
946 cmsHTRANSFORM CMSEXPORT cmsCreateMultiprofileTransformTHR(cmsContext ContextID,
947 cmsHPROFILE hProfiles[],
948 cmsUInt32Number nProfiles,
949 cmsUInt32Number InputFormat,
950 cmsUInt32Number OutputFormat,
951 cmsUInt32Number Intent,
952 cmsUInt32Number dwFlags)
956 cmsUInt32Number Intents[256];
957 cmsFloat64Number AdaptationStates[256];
959 if (nProfiles <= 0 || nProfiles > 255) {
960 cmsSignalError(ContextID, cmsERROR_RANGE, "Wrong number of profiles. 1..255 expected, %d found.", nProfiles);
964 for (i=0; i < nProfiles; i++) {
965 BPC[i] = dwFlags & cmsFLAGS_BLACKPOINTCOMPENSATION ? TRUE : FALSE;
967 AdaptationStates[i] = cmsSetAdaptationStateTHR(ContextID, -1);
971 return cmsCreateExtendedTransform(ContextID, nProfiles, hProfiles, BPC, Intents, AdaptationStates, NULL, 0, InputFormat, OutputFormat, dwFlags);
976 cmsHTRANSFORM CMSEXPORT cmsCreateMultiprofileTransform(cmsHPROFILE hProfiles[],
977 cmsUInt32Number nProfiles,
978 cmsUInt32Number InputFormat,
979 cmsUInt32Number OutputFormat,
980 cmsUInt32Number Intent,
981 cmsUInt32Number dwFlags)
984 if (nProfiles <= 0 || nProfiles > 255) {
985 cmsSignalError(NULL, cmsERROR_RANGE, "Wrong number of profiles. 1..255 expected, %d found.", nProfiles);
989 return cmsCreateMultiprofileTransformTHR(cmsGetProfileContextID(hProfiles[0]),
998 cmsHTRANSFORM CMSEXPORT cmsCreateTransformTHR(cmsContext ContextID,
1000 cmsUInt32Number InputFormat,
1002 cmsUInt32Number OutputFormat,
1003 cmsUInt32Number Intent,
1004 cmsUInt32Number dwFlags)
1007 cmsHPROFILE hArray[2];
1012 return cmsCreateMultiprofileTransformTHR(ContextID, hArray, Output == NULL ? 1 : 2, InputFormat, OutputFormat, Intent, dwFlags);
1015 CMSAPI cmsHTRANSFORM CMSEXPORT cmsCreateTransform(cmsHPROFILE Input,
1016 cmsUInt32Number InputFormat,
1018 cmsUInt32Number OutputFormat,
1019 cmsUInt32Number Intent,
1020 cmsUInt32Number dwFlags)
1022 return cmsCreateTransformTHR(cmsGetProfileContextID(Input), Input, InputFormat, Output, OutputFormat, Intent, dwFlags);
1026 cmsHTRANSFORM CMSEXPORT cmsCreateProofingTransformTHR(cmsContext ContextID,
1027 cmsHPROFILE InputProfile,
1028 cmsUInt32Number InputFormat,
1029 cmsHPROFILE OutputProfile,
1030 cmsUInt32Number OutputFormat,
1031 cmsHPROFILE ProofingProfile,
1032 cmsUInt32Number nIntent,
1033 cmsUInt32Number ProofingIntent,
1034 cmsUInt32Number dwFlags)
1036 cmsHPROFILE hArray[4];
1037 cmsUInt32Number Intents[4];
1039 cmsFloat64Number Adaptation[4];
1040 cmsBool DoBPC = (dwFlags & cmsFLAGS_BLACKPOINTCOMPENSATION) ? TRUE : FALSE;
1043 hArray[0] = InputProfile; hArray[1] = ProofingProfile; hArray[2] = ProofingProfile; hArray[3] = OutputProfile;
1044 Intents[0] = nIntent; Intents[1] = nIntent; Intents[2] = INTENT_RELATIVE_COLORIMETRIC; Intents[3] = ProofingIntent;
1045 BPC[0] = DoBPC; BPC[1] = DoBPC; BPC[2] = 0; BPC[3] = 0;
1047 Adaptation[0] = Adaptation[1] = Adaptation[2] = Adaptation[3] = cmsSetAdaptationStateTHR(ContextID, -1);
1049 if (!(dwFlags & (cmsFLAGS_SOFTPROOFING|cmsFLAGS_GAMUTCHECK)))
1050 return cmsCreateTransformTHR(ContextID, InputProfile, InputFormat, OutputProfile, OutputFormat, nIntent, dwFlags);
1052 return cmsCreateExtendedTransform(ContextID, 4, hArray, BPC, Intents, Adaptation,
1053 ProofingProfile, 1, InputFormat, OutputFormat, dwFlags);
1058 cmsHTRANSFORM CMSEXPORT cmsCreateProofingTransform(cmsHPROFILE InputProfile,
1059 cmsUInt32Number InputFormat,
1060 cmsHPROFILE OutputProfile,
1061 cmsUInt32Number OutputFormat,
1062 cmsHPROFILE ProofingProfile,
1063 cmsUInt32Number nIntent,
1064 cmsUInt32Number ProofingIntent,
1065 cmsUInt32Number dwFlags)
1067 return cmsCreateProofingTransformTHR(cmsGetProfileContextID(InputProfile),
1079 // Grab the ContextID from an open transform. Returns NULL if a NULL transform is passed
1080 cmsContext CMSEXPORT cmsGetTransformContextID(cmsHTRANSFORM hTransform)
1082 _cmsTRANSFORM* xform = (_cmsTRANSFORM*) hTransform;
1084 if (xform == NULL) return NULL;
1085 return xform -> ContextID;
1088 // Grab the input/output formats
1089 cmsUInt32Number CMSEXPORT cmsGetTransformInputFormat(cmsHTRANSFORM hTransform)
1091 _cmsTRANSFORM* xform = (_cmsTRANSFORM*) hTransform;
1093 if (xform == NULL) return 0;
1094 return xform->InputFormat;
1097 cmsUInt32Number CMSEXPORT cmsGetTransformOutputFormat(cmsHTRANSFORM hTransform)
1099 _cmsTRANSFORM* xform = (_cmsTRANSFORM*) hTransform;
1101 if (xform == NULL) return 0;
1102 return xform->OutputFormat;
1105 // For backwards compatibility
1106 cmsBool CMSEXPORT cmsChangeBuffersFormat(cmsHTRANSFORM hTransform,
1107 cmsUInt32Number InputFormat,
1108 cmsUInt32Number OutputFormat)
1111 _cmsTRANSFORM* xform = (_cmsTRANSFORM*) hTransform;
1112 cmsFormatter16 FromInput, ToOutput;
1115 // We only can afford to change formatters if previous transform is at least 16 bits
1116 if (!(xform ->dwOriginalFlags & cmsFLAGS_CAN_CHANGE_FORMATTER)) {
1118 cmsSignalError(xform ->ContextID, cmsERROR_NOT_SUITABLE, "cmsChangeBuffersFormat works only on transforms created originally with at least 16 bits of precision");
1122 FromInput = _cmsGetFormatter(xform->ContextID, InputFormat, cmsFormatterInput, CMS_PACK_FLAGS_16BITS).Fmt16;
1123 ToOutput = _cmsGetFormatter(xform->ContextID, OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_16BITS).Fmt16;
1125 if (FromInput == NULL || ToOutput == NULL) {
1127 cmsSignalError(xform -> ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported raster format");
1131 xform ->InputFormat = InputFormat;
1132 xform ->OutputFormat = OutputFormat;
1133 xform ->FromInput = FromInput;
1134 xform ->ToOutput = ToOutput;