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(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(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 // ----------------------------------------------------------------------------------------------------------------
625 // New to lcms 2.0 -- have all parameters available.
626 cmsHTRANSFORM CMSEXPORT cmsCreateExtendedTransform(cmsContext ContextID,
627 cmsUInt32Number nProfiles, cmsHPROFILE hProfiles[],
629 cmsUInt32Number Intents[],
630 cmsFloat64Number AdaptationStates[],
631 cmsHPROFILE hGamutProfile,
632 cmsUInt32Number nGamutPCSposition,
633 cmsUInt32Number InputFormat,
634 cmsUInt32Number OutputFormat,
635 cmsUInt32Number dwFlags)
637 _cmsTRANSFORM* xform;
638 cmsBool FloatTransform;
639 cmsColorSpaceSignature EntryColorSpace;
640 cmsColorSpaceSignature ExitColorSpace;
642 cmsUInt32Number LastIntent = Intents[nProfiles-1];
644 // If it is a fake transform
645 if (dwFlags & cmsFLAGS_NULLTRANSFORM)
647 return AllocEmptyTransform(ContextID, NULL, INTENT_PERCEPTUAL, &InputFormat, &OutputFormat, &dwFlags);
650 // If gamut check is requested, make sure we have a gamut profile
651 if (dwFlags & cmsFLAGS_GAMUTCHECK) {
652 if (hGamutProfile == NULL) dwFlags &= ~cmsFLAGS_GAMUTCHECK;
655 // On floating point transforms, inhibit optimizations
656 FloatTransform = (_cmsFormatterIsFloat(InputFormat) && _cmsFormatterIsFloat(OutputFormat));
658 if (_cmsFormatterIsFloat(InputFormat) || _cmsFormatterIsFloat(OutputFormat))
659 dwFlags |= cmsFLAGS_NOCACHE;
661 // Mark entry/exit spaces
662 if (!GetXFormColorSpaces(nProfiles, hProfiles, &EntryColorSpace, &ExitColorSpace)) {
663 cmsSignalError(ContextID, cmsERROR_NULL, "NULL input profiles on transform");
667 // Check if proper colorspaces
668 if (!IsProperColorSpace(EntryColorSpace, InputFormat)) {
669 cmsSignalError(ContextID, cmsERROR_COLORSPACE_CHECK, "Wrong input color space on transform");
673 if (!IsProperColorSpace(ExitColorSpace, OutputFormat)) {
674 cmsSignalError(ContextID, cmsERROR_COLORSPACE_CHECK, "Wrong output color space on transform");
678 // Create a pipeline with all transformations
679 Lut = _cmsLinkProfiles(ContextID, nProfiles, Intents, hProfiles, BPC, AdaptationStates, dwFlags);
681 cmsSignalError(ContextID, cmsERROR_NOT_SUITABLE, "Couldn't link the profiles");
685 // Check channel count
686 if ((cmsChannelsOf(EntryColorSpace) != cmsPipelineInputChannels(Lut)) ||
687 (cmsChannelsOf(ExitColorSpace) != cmsPipelineOutputChannels(Lut))) {
688 cmsSignalError(ContextID, cmsERROR_NOT_SUITABLE, "Channel count doesn't match. Profile is corrupted");
694 xform = AllocEmptyTransform(ContextID, Lut, LastIntent, &InputFormat, &OutputFormat, &dwFlags);
700 xform ->EntryColorSpace = EntryColorSpace;
701 xform ->ExitColorSpace = ExitColorSpace;
702 xform ->RenderingIntent = Intents[nProfiles-1];
705 // Create a gamut check LUT if requested
706 if (hGamutProfile != NULL && (dwFlags & cmsFLAGS_GAMUTCHECK))
707 xform ->GamutCheck = _cmsCreateGamutCheckPipeline(ContextID, hProfiles,
714 // Try to read input and output colorant table
715 if (cmsIsTag(hProfiles[0], cmsSigColorantTableTag)) {
717 // Input table can only come in this way.
718 xform ->InputColorant = cmsDupNamedColorList((cmsNAMEDCOLORLIST*) cmsReadTag(hProfiles[0], cmsSigColorantTableTag));
721 // Output is a little bit more complex.
722 if (cmsGetDeviceClass(hProfiles[nProfiles-1]) == cmsSigLinkClass) {
724 // This tag may exist only on devicelink profiles.
725 if (cmsIsTag(hProfiles[nProfiles-1], cmsSigColorantTableOutTag)) {
727 // It may be NULL if error
728 xform ->OutputColorant = cmsDupNamedColorList((cmsNAMEDCOLORLIST*) cmsReadTag(hProfiles[nProfiles-1], cmsSigColorantTableOutTag));
733 if (cmsIsTag(hProfiles[nProfiles-1], cmsSigColorantTableTag)) {
735 xform -> OutputColorant = cmsDupNamedColorList((cmsNAMEDCOLORLIST*) cmsReadTag(hProfiles[nProfiles-1], cmsSigColorantTableTag));
739 // Store the sequence of profiles
740 if (dwFlags & cmsFLAGS_KEEP_SEQUENCE) {
741 xform ->Sequence = _cmsCompileProfileSequence(ContextID, nProfiles, hProfiles);
744 xform ->Sequence = NULL;
746 // If this is a cached transform, init first value, which is zero (16 bits only)
747 if (!(dwFlags & cmsFLAGS_NOCACHE)) {
749 memset(&xform ->Cache.CacheIn, 0, sizeof(xform ->Cache.CacheIn));
751 if (xform ->GamutCheck != NULL) {
752 TransformOnePixelWithGamutCheck(xform, xform ->Cache.CacheIn, xform->Cache.CacheOut);
756 xform ->Lut ->Eval16Fn(xform ->Cache.CacheIn, xform->Cache.CacheOut, xform -> Lut->Data);
761 return (cmsHTRANSFORM) xform;
764 // Multiprofile transforms: Gamut check is not available here, as it is unclear from which profile the gamut comes.
765 cmsHTRANSFORM CMSEXPORT cmsCreateMultiprofileTransformTHR(cmsContext ContextID,
766 cmsHPROFILE hProfiles[],
767 cmsUInt32Number nProfiles,
768 cmsUInt32Number InputFormat,
769 cmsUInt32Number OutputFormat,
770 cmsUInt32Number Intent,
771 cmsUInt32Number dwFlags)
775 cmsUInt32Number Intents[256];
776 cmsFloat64Number AdaptationStates[256];
778 if (nProfiles <= 0 || nProfiles > 255) {
779 cmsSignalError(ContextID, cmsERROR_RANGE, "Wrong number of profiles. 1..255 expected, %d found.", nProfiles);
783 for (i=0; i < nProfiles; i++) {
784 BPC[i] = dwFlags & cmsFLAGS_BLACKPOINTCOMPENSATION ? TRUE : FALSE;
786 AdaptationStates[i] = GlobalAdaptationState;
790 return cmsCreateExtendedTransform(ContextID, nProfiles, hProfiles, BPC, Intents, AdaptationStates, NULL, 0, InputFormat, OutputFormat, dwFlags);
795 cmsHTRANSFORM CMSEXPORT cmsCreateMultiprofileTransform(cmsHPROFILE hProfiles[],
796 cmsUInt32Number nProfiles,
797 cmsUInt32Number InputFormat,
798 cmsUInt32Number OutputFormat,
799 cmsUInt32Number Intent,
800 cmsUInt32Number dwFlags)
803 if (nProfiles <= 0 || nProfiles > 255) {
804 cmsSignalError(NULL, cmsERROR_RANGE, "Wrong number of profiles. 1..255 expected, %d found.", nProfiles);
808 return cmsCreateMultiprofileTransformTHR(cmsGetProfileContextID(hProfiles[0]),
817 cmsHTRANSFORM CMSEXPORT cmsCreateTransformTHR(cmsContext ContextID,
819 cmsUInt32Number InputFormat,
821 cmsUInt32Number OutputFormat,
822 cmsUInt32Number Intent,
823 cmsUInt32Number dwFlags)
826 cmsHPROFILE hArray[2];
831 return cmsCreateMultiprofileTransformTHR(ContextID, hArray, Output == NULL ? 1 : 2, InputFormat, OutputFormat, Intent, dwFlags);
834 CMSAPI cmsHTRANSFORM CMSEXPORT cmsCreateTransform(cmsHPROFILE Input,
835 cmsUInt32Number InputFormat,
837 cmsUInt32Number OutputFormat,
838 cmsUInt32Number Intent,
839 cmsUInt32Number dwFlags)
841 return cmsCreateTransformTHR(cmsGetProfileContextID(Input), Input, InputFormat, Output, OutputFormat, Intent, dwFlags);
845 cmsHTRANSFORM CMSEXPORT cmsCreateProofingTransformTHR(cmsContext ContextID,
846 cmsHPROFILE InputProfile,
847 cmsUInt32Number InputFormat,
848 cmsHPROFILE OutputProfile,
849 cmsUInt32Number OutputFormat,
850 cmsHPROFILE ProofingProfile,
851 cmsUInt32Number nIntent,
852 cmsUInt32Number ProofingIntent,
853 cmsUInt32Number dwFlags)
855 cmsHPROFILE hArray[4];
856 cmsUInt32Number Intents[4];
858 cmsFloat64Number Adaptation[4];
859 cmsBool DoBPC = (dwFlags & cmsFLAGS_BLACKPOINTCOMPENSATION) ? TRUE : FALSE;
862 hArray[0] = InputProfile; hArray[1] = ProofingProfile; hArray[2] = ProofingProfile; hArray[3] = OutputProfile;
863 Intents[0] = nIntent; Intents[1] = nIntent; Intents[2] = INTENT_RELATIVE_COLORIMETRIC; Intents[3] = ProofingIntent;
864 BPC[0] = DoBPC; BPC[1] = DoBPC; BPC[2] = 0; BPC[3] = 0;
866 Adaptation[0] = Adaptation[1] = Adaptation[2] = Adaptation[3] = GlobalAdaptationState;
868 if (!(dwFlags & (cmsFLAGS_SOFTPROOFING|cmsFLAGS_GAMUTCHECK)))
869 return cmsCreateTransformTHR(ContextID, InputProfile, InputFormat, OutputProfile, OutputFormat, nIntent, dwFlags);
871 return cmsCreateExtendedTransform(ContextID, 4, hArray, BPC, Intents, Adaptation,
872 ProofingProfile, 1, InputFormat, OutputFormat, dwFlags);
877 cmsHTRANSFORM CMSEXPORT cmsCreateProofingTransform(cmsHPROFILE InputProfile,
878 cmsUInt32Number InputFormat,
879 cmsHPROFILE OutputProfile,
880 cmsUInt32Number OutputFormat,
881 cmsHPROFILE ProofingProfile,
882 cmsUInt32Number nIntent,
883 cmsUInt32Number ProofingIntent,
884 cmsUInt32Number dwFlags)
886 return cmsCreateProofingTransformTHR(cmsGetProfileContextID(InputProfile),
898 // Grab the ContextID from an open transform. Returns NULL if a NULL transform is passed
899 cmsContext CMSEXPORT cmsGetTransformContextID(cmsHTRANSFORM hTransform)
901 _cmsTRANSFORM* xform = (_cmsTRANSFORM*) hTransform;
903 if (xform == NULL) return NULL;
904 return xform -> ContextID;
907 // Grab the input/output formats
908 cmsUInt32Number CMSEXPORT cmsGetTransformInputFormat(cmsHTRANSFORM hTransform)
910 _cmsTRANSFORM* xform = (_cmsTRANSFORM*) hTransform;
912 if (xform == NULL) return 0;
913 return xform->InputFormat;
916 cmsUInt32Number CMSEXPORT cmsGetTransformOutputFormat(cmsHTRANSFORM hTransform)
918 _cmsTRANSFORM* xform = (_cmsTRANSFORM*) hTransform;
920 if (xform == NULL) return 0;
921 return xform->OutputFormat;
924 // For backwards compatibility
925 cmsBool CMSEXPORT cmsChangeBuffersFormat(cmsHTRANSFORM hTransform,
926 cmsUInt32Number InputFormat,
927 cmsUInt32Number OutputFormat)
930 _cmsTRANSFORM* xform = (_cmsTRANSFORM*) hTransform;
931 cmsFormatter16 FromInput, ToOutput;
934 // We only can afford to change formatters if previous transform is at least 16 bits
935 if (!(xform ->dwOriginalFlags & cmsFLAGS_CAN_CHANGE_FORMATTER)) {
937 cmsSignalError(xform ->ContextID, cmsERROR_NOT_SUITABLE, "cmsChangeBuffersFormat works only on transforms created originally with at least 16 bits of precision");
941 FromInput = _cmsGetFormatter(InputFormat, cmsFormatterInput, CMS_PACK_FLAGS_16BITS).Fmt16;
942 ToOutput = _cmsGetFormatter(OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_16BITS).Fmt16;
944 if (FromInput == NULL || ToOutput == NULL) {
946 cmsSignalError(xform -> ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported raster format");
950 xform ->InputFormat = InputFormat;
951 xform ->OutputFormat = OutputFormat;
952 xform ->FromInput = FromInput;
953 xform ->ToOutput = ToOutput;