grub2: bring back build of aros-side grub2 tools
[AROS.git] / workbench / libs / lcms2 / src / cmsxform.c
blob37a21b3a2c41f5304c5ce0709c7a439c406f4ced
1 //---------------------------------------------------------------------------------
2 //
3 // Little Color Management System
4 // Copyright (c) 1998-2011 Marti Maria Saguer
5 //
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;
42 if (d >= 0)
43 GlobalAdaptationState = d;
45 return OldVal;
48 // Alarm codes are always global
49 void CMSEXPORT cmsSetAlarmCodes(cmsUInt16Number NewAlarm[cmsMAXCHANNELS])
51 int i;
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])
62 int i;
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);
77 if (p -> GamutCheck)
78 cmsPipelineFree(p -> GamutCheck);
80 if (p -> Lut)
81 cmsPipelineFree(p -> Lut);
83 if (p ->InputColorant)
84 cmsFreeNamedColorList(p ->InputColorant);
86 if (p -> OutputColorant)
87 cmsFreeNamedColorList(p ->OutputColorant);
89 if (p ->Sequence)
90 cmsFreeProfileSequenceDescription(p ->Sequence);
92 if (p ->UserData)
93 p ->FreeUserData(p ->ContextID, p ->UserData);
95 _cmsFree(p ->ContextID, (void *) p);
98 // Apply transform.
99 void CMSEXPORT cmsDoTransform(cmsHTRANSFORM Transform,
100 const void* InputBuffer,
101 void* OutputBuffer,
102 cmsUInt32Number Size)
105 _cmsTRANSFORM* p = (_cmsTRANSFORM*) Transform;
107 p -> xform(p, InputBuffer, OutputBuffer, Size, Size);
111 // Apply transform.
112 void CMSEXPORT cmsDoTransformStride(cmsHTRANSFORM Transform,
113 const void* InputBuffer,
114 void* OutputBuffer,
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.
128 static
129 void FloatXFORM(_cmsTRANSFORM* p,
130 const void* in,
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++)
157 fOut[j] = -1.0;
160 else {
161 // No, proceed normally
162 cmsPipelineEvalFloat(fIn, fOut, p -> Lut);
165 else {
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é
179 static
180 void NullXFORM(_cmsTRANSFORM* p,
181 const void* in,
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
203 static
204 void PrecalculatedXFORM(_cmsTRANSFORM* p,
205 const void* in,
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;
215 n = Size;
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
227 static
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) {
237 cmsUInt16Number i;
239 for (i=0; i < p ->Lut->OutputChannels; i++)
240 wOut[i] = Alarm[i];
242 else
243 p ->Lut ->Eval16Fn(wIn, wOut, p -> Lut->Data);
246 // Gamut check, No caché, 16 bits.
247 static
248 void PrecalculatedXFORMGamutCheck(_cmsTRANSFORM* p,
249 const void* in,
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,
271 static
272 void CachedXFORM(_cmsTRANSFORM* p,
273 const void* in,
274 void* out, cmsUInt32Number Size, cmsUInt32Number Stride)
276 cmsUInt8Number* accum;
277 cmsUInt8Number* output;
278 cmsUInt16Number wIn[cmsMAXCHANNELS], wOut[cmsMAXCHANNELS];
279 cmsUInt32Number i, n;
280 _cmsCACHE Cache;
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));
301 else {
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
316 static
317 void CachedXFORMGamutCheck(_cmsTRANSFORM* p,
318 const void* in,
319 void* out, cmsUInt32Number Size, cmsUInt32Number Stride)
321 cmsUInt8Number* accum;
322 cmsUInt8Number* output;
323 cmsUInt16Number wIn[cmsMAXCHANNELS], wOut[cmsMAXCHANNELS];
324 cmsUInt32Number i, n;
325 _cmsCACHE Cache;
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));
345 else {
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;
375 if (Data == NULL) {
377 // Free the chain. Memory is safely freed at exit
378 TransformCollection = NULL;
379 return TRUE;
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;
392 // Keep linked list
393 fl ->Next = TransformCollection;
394 TransformCollection = fl;
396 // All is ok
397 return TRUE;
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,
433 static
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));
441 if (!p) return NULL;
443 // Store the proposed pipeline
444 p ->Lut = lut;
446 // Let's see if any plug-in want to do the transform by itself
447 for (Plugin = TransformCollection;
448 Plugin != NULL;
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;
472 return p;
476 // Not suitable for the transform plug-in, let's check the pipeline plug-in
477 if (p ->Lut != NULL)
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);
492 return NULL;
495 // Float transforms don't use caché, always are non-NULL
496 p ->xform = FloatXFORM;
498 else {
500 if (*InputFormat == 0 && *OutputFormat == 0) {
501 p ->FromInput = p ->ToOutput = NULL;
502 *dwFlags |= cmsFLAGS_CAN_CHANGE_FORMATTER;
504 else {
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);
515 return NULL;
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;
528 else {
529 if (*dwFlags & cmsFLAGS_NOCACHE) {
531 if (*dwFlags & cmsFLAGS_GAMUTCHECK)
532 p ->xform = PrecalculatedXFORMGamutCheck; // Gamut check, no caché
533 else
534 p ->xform = PrecalculatedXFORM; // No caché, no gamut check
536 else {
538 if (*dwFlags & cmsFLAGS_GAMUTCHECK)
539 p ->xform = CachedXFORMGamutCheck; // Gamut check, caché
540 else
541 p ->xform = CachedXFORM; // No gamut check, caché
547 p ->InputFormat = *InputFormat;
548 p ->OutputFormat = *OutputFormat;
549 p ->dwOriginalFlags = *dwFlags;
550 p ->ContextID = ContextID;
551 p ->UserData = NULL;
552 return p;
555 static
556 cmsBool GetXFormColorSpaces(int nProfiles, cmsHPROFILE hProfiles[], cmsColorSpaceSignature* Input, cmsColorSpaceSignature* Output)
558 cmsColorSpaceSignature ColorSpaceIn, ColorSpaceOut;
559 cmsColorSpaceSignature PostColorSpace;
560 int i;
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);
584 else
585 if (lIsInput || (cls == cmsSigLinkClass)) {
587 ColorSpaceIn = cmsGetColorSpace(hProfile);
588 ColorSpaceOut = cmsGetPCS(hProfile);
590 else
592 ColorSpaceIn = cmsGetPCS(hProfile);
593 ColorSpaceOut = cmsGetColorSpace(hProfile);
596 if (i==0)
597 *Input = ColorSpaceIn;
599 PostColorSpace = ColorSpaceOut;
602 *Output = PostColorSpace;
604 return TRUE;
607 // Check colorspace
608 static
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;
620 return FALSE;
623 // ----------------------------------------------------------------------------------------------------------------
625 static
626 void SetWhitePoint(cmsCIEXYZ* wtPt, const cmsCIEXYZ* src)
628 if (src == NULL) {
629 wtPt ->X = cmsD50X;
630 wtPt ->Y = cmsD50Y;
631 wtPt ->Z = cmsD50Z;
633 else {
634 wtPt ->X = src->X;
635 wtPt ->Y = src->Y;
636 wtPt ->Z = src->Z;
641 // New to lcms 2.0 -- have all parameters available.
642 cmsHTRANSFORM CMSEXPORT cmsCreateExtendedTransform(cmsContext ContextID,
643 cmsUInt32Number nProfiles, cmsHPROFILE hProfiles[],
644 cmsBool BPC[],
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;
656 cmsPipeline* Lut;
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");
677 return NULL;
680 // Check if proper colorspaces
681 if (!IsProperColorSpace(EntryColorSpace, InputFormat)) {
682 cmsSignalError(ContextID, cmsERROR_COLORSPACE_CHECK, "Wrong input color space on transform");
683 return NULL;
686 if (!IsProperColorSpace(ExitColorSpace, OutputFormat)) {
687 cmsSignalError(ContextID, cmsERROR_COLORSPACE_CHECK, "Wrong output color space on transform");
688 return NULL;
691 // Create a pipeline with all transformations
692 Lut = _cmsLinkProfiles(ContextID, nProfiles, Intents, hProfiles, BPC, AdaptationStates, dwFlags);
693 if (Lut == NULL) {
694 cmsSignalError(ContextID, cmsERROR_NOT_SUITABLE, "Couldn't link the profiles");
695 return NULL;
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");
702 return NULL;
706 // All seems ok
707 xform = AllocEmptyTransform(ContextID, Lut, LastIntent, &InputFormat, &OutputFormat, &dwFlags);
708 if (xform == NULL) {
709 return NULL;
712 // Keep values
713 xform ->EntryColorSpace = EntryColorSpace;
714 xform ->ExitColorSpace = ExitColorSpace;
715 xform ->RenderingIntent = Intents[nProfiles-1];
717 // Take white points
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,
725 BPC, Intents,
726 AdaptationStates,
727 nGamutPCSposition,
728 hGamutProfile);
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));
748 } else {
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);
760 else
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);
771 else {
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)
790 cmsUInt32Number i;
791 cmsBool BPC[256];
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);
797 return NULL;
800 for (i=0; i < nProfiles; i++) {
801 BPC[i] = dwFlags & cmsFLAGS_BLACKPOINTCOMPENSATION ? TRUE : FALSE;
802 Intents[i] = Intent;
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);
822 return NULL;
825 return cmsCreateMultiprofileTransformTHR(cmsGetProfileContextID(hProfiles[0]),
826 hProfiles,
827 nProfiles,
828 InputFormat,
829 OutputFormat,
830 Intent,
831 dwFlags);
834 cmsHTRANSFORM CMSEXPORT cmsCreateTransformTHR(cmsContext ContextID,
835 cmsHPROFILE Input,
836 cmsUInt32Number InputFormat,
837 cmsHPROFILE Output,
838 cmsUInt32Number OutputFormat,
839 cmsUInt32Number Intent,
840 cmsUInt32Number dwFlags)
843 cmsHPROFILE hArray[2];
845 hArray[0] = Input;
846 hArray[1] = Output;
848 return cmsCreateMultiprofileTransformTHR(ContextID, hArray, Output == NULL ? 1 : 2, InputFormat, OutputFormat, Intent, dwFlags);
851 CMSAPI cmsHTRANSFORM CMSEXPORT cmsCreateTransform(cmsHPROFILE Input,
852 cmsUInt32Number InputFormat,
853 cmsHPROFILE Output,
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];
874 cmsBool BPC[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),
904 InputProfile,
905 InputFormat,
906 OutputProfile,
907 OutputFormat,
908 ProofingProfile,
909 nIntent,
910 ProofingIntent,
911 dwFlags);
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");
955 return FALSE;
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");
964 return FALSE;
967 xform ->InputFormat = InputFormat;
968 xform ->OutputFormat = OutputFormat;
969 xform ->FromInput = FromInput;
970 xform ->ToOutput = ToOutput;
971 return TRUE;