msdasql: Check rowset pointer before assignment in ICommandText::Execute.
[wine.git] / libs / lcms2 / src / cmsxform.c
blob1372b19b5646412ac29c294dc20d007a1f029b19
1 //---------------------------------------------------------------------------------
2 //
3 // Little Color Management System
4 // Copyright (c) 1998-2023 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 #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 };
42 void* from;
44 if (src != NULL) {
45 from = src ->chunks[AdaptationStateContext];
47 else {
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
66 if (d >= 0.0) {
68 ptr ->AdaptationState = d;
71 // Always return previous value
72 return prev;
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 };
132 void* from;
134 if (src != NULL) {
135 from = src ->chunks[AlarmCodesContext];
137 else {
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);
153 if (p -> GamutCheck)
154 cmsPipelineFree(p -> GamutCheck);
156 if (p -> Lut)
157 cmsPipelineFree(p -> Lut);
159 if (p ->InputColorant)
160 cmsFreeNamedColorList(p ->InputColorant);
162 if (p -> OutputColorant)
163 cmsFreeNamedColorList(p ->OutputColorant);
165 if (p ->Sequence)
166 cmsFreeProfileSequenceDescription(p ->Sequence);
168 if (p ->UserData)
169 p ->FreeUserData(p ->ContextID, p ->UserData);
171 _cmsFree(p ->ContextID, (void *) p);
175 static
176 cmsUInt32Number PixelSize(cmsUInt32Number Format)
178 cmsUInt32Number fmt_bytes = T_BYTES(Format);
180 // For double, the T_BYTES field is zero
181 if (fmt_bytes == 0)
182 return sizeof(cmsUInt64Number);
184 // Otherwise, it is already correct for all formats
185 return fmt_bytes;
191 // Apply transform.
192 void CMSEXPORT cmsDoTransform(cmsHTRANSFORM Transform,
193 const void* InputBuffer,
194 void* OutputBuffer,
195 cmsUInt32Number Size)
198 _cmsTRANSFORM* p = (_cmsTRANSFORM*) Transform;
199 cmsStride stride;
201 stride.BytesPerLineIn = 0; // Not used
202 stride.BytesPerLineOut = 0;
203 stride.BytesPerPlaneIn = Size * PixelSize(p->InputFormat);
204 stride.BytesPerPlaneOut = Size * PixelSize(p->OutputFormat);
206 p -> xform(p, InputBuffer, OutputBuffer, Size, 1, &stride);
210 // This is a legacy stride for planar
211 void CMSEXPORT cmsDoTransformStride(cmsHTRANSFORM Transform,
212 const void* InputBuffer,
213 void* OutputBuffer,
214 cmsUInt32Number Size, cmsUInt32Number Stride)
217 _cmsTRANSFORM* p = (_cmsTRANSFORM*) Transform;
218 cmsStride stride;
220 stride.BytesPerLineIn = 0;
221 stride.BytesPerLineOut = 0;
222 stride.BytesPerPlaneIn = Stride;
223 stride.BytesPerPlaneOut = Stride;
225 p -> xform(p, InputBuffer, OutputBuffer, Size, 1, &stride);
228 // This is the "fast" function for plugins
229 void CMSEXPORT cmsDoTransformLineStride(cmsHTRANSFORM Transform,
230 const void* InputBuffer,
231 void* OutputBuffer,
232 cmsUInt32Number PixelsPerLine,
233 cmsUInt32Number LineCount,
234 cmsUInt32Number BytesPerLineIn,
235 cmsUInt32Number BytesPerLineOut,
236 cmsUInt32Number BytesPerPlaneIn,
237 cmsUInt32Number BytesPerPlaneOut)
240 _cmsTRANSFORM* p = (_cmsTRANSFORM*) Transform;
241 cmsStride stride;
243 stride.BytesPerLineIn = BytesPerLineIn;
244 stride.BytesPerLineOut = BytesPerLineOut;
245 stride.BytesPerPlaneIn = BytesPerPlaneIn;
246 stride.BytesPerPlaneOut = BytesPerPlaneOut;
248 p->xform(p, InputBuffer, OutputBuffer, PixelsPerLine, LineCount, &stride);
253 // Transform routines ----------------------------------------------------------------------------------------------------------
255 // Float xform converts floats. Since there are no performance issues, one routine does all job, including gamut check.
256 // Note that because extended range, we can use a -1.0 value for out of gamut in this case.
257 static
258 void FloatXFORM(_cmsTRANSFORM* p,
259 const void* in,
260 void* out,
261 cmsUInt32Number PixelsPerLine,
262 cmsUInt32Number LineCount,
263 const cmsStride* Stride)
265 cmsUInt8Number* accum;
266 cmsUInt8Number* output;
267 cmsFloat32Number fIn[cmsMAXCHANNELS], fOut[cmsMAXCHANNELS];
268 cmsFloat32Number OutOfGamut;
269 cmsUInt32Number i, j, c, strideIn, strideOut;
271 _cmsHandleExtraChannels(p, in, out, PixelsPerLine, LineCount, Stride);
273 strideIn = 0;
274 strideOut = 0;
275 memset(fIn, 0, sizeof(fIn));
276 memset(fOut, 0, sizeof(fOut));
278 for (i = 0; i < LineCount; i++) {
280 accum = (cmsUInt8Number*)in + strideIn;
281 output = (cmsUInt8Number*)out + strideOut;
283 for (j = 0; j < PixelsPerLine; j++) {
285 accum = p->FromInputFloat(p, fIn, accum, Stride->BytesPerPlaneIn);
287 // Any gamut check to do?
288 if (p->GamutCheck != NULL) {
290 // Evaluate gamut marker.
291 cmsPipelineEvalFloat(fIn, &OutOfGamut, p->GamutCheck);
293 // Is current color out of gamut?
294 if (OutOfGamut > 0.0) {
296 // Certainly, out of gamut
297 for (c = 0; c < cmsMAXCHANNELS; c++)
298 fOut[c] = -1.0;
301 else {
302 // No, proceed normally
303 cmsPipelineEvalFloat(fIn, fOut, p->Lut);
306 else {
308 // No gamut check at all
309 cmsPipelineEvalFloat(fIn, fOut, p->Lut);
313 output = p->ToOutputFloat(p, fOut, output, Stride->BytesPerPlaneOut);
316 strideIn += Stride->BytesPerLineIn;
317 strideOut += Stride->BytesPerLineOut;
323 static
324 void NullFloatXFORM(_cmsTRANSFORM* p,
325 const void* in,
326 void* out,
327 cmsUInt32Number PixelsPerLine,
328 cmsUInt32Number LineCount,
329 const cmsStride* Stride)
332 cmsUInt8Number* accum;
333 cmsUInt8Number* output;
334 cmsFloat32Number fIn[cmsMAXCHANNELS];
335 cmsUInt32Number i, j, strideIn, strideOut;
337 _cmsHandleExtraChannels(p, in, out, PixelsPerLine, LineCount, Stride);
339 strideIn = 0;
340 strideOut = 0;
341 memset(fIn, 0, sizeof(fIn));
343 for (i = 0; i < LineCount; i++) {
345 accum = (cmsUInt8Number*) in + strideIn;
346 output = (cmsUInt8Number*) out + strideOut;
348 for (j = 0; j < PixelsPerLine; j++) {
350 accum = p->FromInputFloat(p, fIn, accum, Stride ->BytesPerPlaneIn);
351 output = p->ToOutputFloat(p, fIn, output, Stride->BytesPerPlaneOut);
354 strideIn += Stride->BytesPerLineIn;
355 strideOut += Stride->BytesPerLineOut;
359 // 16 bit precision -----------------------------------------------------------------------------------------------------------
361 // Null transformation, only applies formatters. No cache
362 static
363 void NullXFORM(_cmsTRANSFORM* p,
364 const void* in,
365 void* out,
366 cmsUInt32Number PixelsPerLine,
367 cmsUInt32Number LineCount,
368 const cmsStride* Stride)
370 cmsUInt8Number* accum;
371 cmsUInt8Number* output;
372 cmsUInt16Number wIn[cmsMAXCHANNELS];
373 cmsUInt32Number i, j, strideIn, strideOut;
375 _cmsHandleExtraChannels(p, in, out, PixelsPerLine, LineCount, Stride);
377 strideIn = 0;
378 strideOut = 0;
379 memset(wIn, 0, sizeof(wIn));
381 for (i = 0; i < LineCount; i++) {
383 accum = (cmsUInt8Number*)in + strideIn;
384 output = (cmsUInt8Number*)out + strideOut;
386 for (j = 0; j < PixelsPerLine; j++) {
388 accum = p->FromInput(p, wIn, accum, Stride->BytesPerPlaneIn);
389 output = p->ToOutput(p, wIn, output, Stride->BytesPerPlaneOut);
392 strideIn += Stride->BytesPerLineIn;
393 strideOut += Stride->BytesPerLineOut;
399 // No gamut check, no cache, 16 bits
400 static
401 void PrecalculatedXFORM(_cmsTRANSFORM* p,
402 const void* in,
403 void* out,
404 cmsUInt32Number PixelsPerLine,
405 cmsUInt32Number LineCount,
406 const cmsStride* Stride)
408 CMSREGISTER cmsUInt8Number* accum;
409 CMSREGISTER cmsUInt8Number* output;
410 cmsUInt16Number wIn[cmsMAXCHANNELS], wOut[cmsMAXCHANNELS];
411 cmsUInt32Number i, j, strideIn, strideOut;
413 _cmsHandleExtraChannels(p, in, out, PixelsPerLine, LineCount, Stride);
415 strideIn = 0;
416 strideOut = 0;
417 memset(wIn, 0, sizeof(wIn));
418 memset(wOut, 0, sizeof(wOut));
420 for (i = 0; i < LineCount; i++) {
422 accum = (cmsUInt8Number*)in + strideIn;
423 output = (cmsUInt8Number*)out + strideOut;
425 for (j = 0; j < PixelsPerLine; j++) {
427 accum = p->FromInput(p, wIn, accum, Stride->BytesPerPlaneIn);
428 p->Lut->Eval16Fn(wIn, wOut, p->Lut->Data);
429 output = p->ToOutput(p, wOut, output, Stride->BytesPerPlaneOut);
432 strideIn += Stride->BytesPerLineIn;
433 strideOut += Stride->BytesPerLineOut;
439 // Auxiliary: Handle precalculated gamut check. The retrieval of context may be alittle bit slow, but this function is not critical.
440 static
441 void TransformOnePixelWithGamutCheck(_cmsTRANSFORM* p,
442 const cmsUInt16Number wIn[],
443 cmsUInt16Number wOut[])
445 cmsUInt16Number wOutOfGamut;
447 p ->GamutCheck ->Eval16Fn(wIn, &wOutOfGamut, p ->GamutCheck ->Data);
448 if (wOutOfGamut >= 1) {
450 cmsUInt32Number i;
451 _cmsAlarmCodesChunkType* ContextAlarmCodes = (_cmsAlarmCodesChunkType*) _cmsContextGetClientChunk(p->ContextID, AlarmCodesContext);
453 for (i=0; i < p ->Lut->OutputChannels; i++) {
455 wOut[i] = ContextAlarmCodes ->AlarmCodes[i];
458 else
459 p ->Lut ->Eval16Fn(wIn, wOut, p -> Lut->Data);
462 // Gamut check, No cache, 16 bits.
463 static
464 void PrecalculatedXFORMGamutCheck(_cmsTRANSFORM* p,
465 const void* in,
466 void* out,
467 cmsUInt32Number PixelsPerLine,
468 cmsUInt32Number LineCount,
469 const cmsStride* Stride)
471 cmsUInt8Number* accum;
472 cmsUInt8Number* output;
473 cmsUInt16Number wIn[cmsMAXCHANNELS], wOut[cmsMAXCHANNELS];
474 cmsUInt32Number i, j, strideIn, strideOut;
476 _cmsHandleExtraChannels(p, in, out, PixelsPerLine, LineCount, Stride);
478 strideIn = 0;
479 strideOut = 0;
480 memset(wIn, 0, sizeof(wIn));
481 memset(wOut, 0, sizeof(wOut));
483 for (i = 0; i < LineCount; i++) {
485 accum = (cmsUInt8Number*)in + strideIn;
486 output = (cmsUInt8Number*)out + strideOut;
488 for (j = 0; j < PixelsPerLine; j++) {
490 accum = p->FromInput(p, wIn, accum, Stride->BytesPerPlaneIn);
491 TransformOnePixelWithGamutCheck(p, wIn, wOut);
492 output = p->ToOutput(p, wOut, output, Stride->BytesPerPlaneOut);
495 strideIn += Stride->BytesPerLineIn;
496 strideOut += Stride->BytesPerLineOut;
501 // No gamut check, Cache, 16 bits,
502 static
503 void CachedXFORM(_cmsTRANSFORM* p,
504 const void* in,
505 void* out,
506 cmsUInt32Number PixelsPerLine,
507 cmsUInt32Number LineCount,
508 const cmsStride* Stride)
510 cmsUInt8Number* accum;
511 cmsUInt8Number* output;
512 cmsUInt16Number wIn[cmsMAXCHANNELS], wOut[cmsMAXCHANNELS];
513 _cmsCACHE Cache;
514 cmsUInt32Number i, j, strideIn, strideOut;
516 _cmsHandleExtraChannels(p, in, out, PixelsPerLine, LineCount, Stride);
518 // Empty buffers for quick memcmp
519 memset(wIn, 0, sizeof(wIn));
520 memset(wOut, 0, sizeof(wOut));
522 // Get copy of zero cache
523 memcpy(&Cache, &p->Cache, sizeof(Cache));
525 strideIn = 0;
526 strideOut = 0;
528 for (i = 0; i < LineCount; i++) {
530 accum = (cmsUInt8Number*)in + strideIn;
531 output = (cmsUInt8Number*)out + strideOut;
533 for (j = 0; j < PixelsPerLine; j++) {
535 accum = p->FromInput(p, wIn, accum, Stride->BytesPerPlaneIn);
537 if (memcmp(wIn, Cache.CacheIn, sizeof(Cache.CacheIn)) == 0) {
539 memcpy(wOut, Cache.CacheOut, sizeof(Cache.CacheOut));
541 else {
542 p->Lut->Eval16Fn(wIn, wOut, p->Lut->Data);
544 memcpy(Cache.CacheIn, wIn, sizeof(Cache.CacheIn));
545 memcpy(Cache.CacheOut, wOut, sizeof(Cache.CacheOut));
548 output = p->ToOutput(p, wOut, output, Stride->BytesPerPlaneOut);
551 strideIn += Stride->BytesPerLineIn;
552 strideOut += Stride->BytesPerLineOut;
556 // All those nice features together
557 static
558 void CachedXFORMGamutCheck(_cmsTRANSFORM* p,
559 const void* in,
560 void* out,
561 cmsUInt32Number PixelsPerLine,
562 cmsUInt32Number LineCount,
563 const cmsStride* Stride)
565 cmsUInt8Number* accum;
566 cmsUInt8Number* output;
567 cmsUInt16Number wIn[cmsMAXCHANNELS], wOut[cmsMAXCHANNELS];
568 _cmsCACHE Cache;
569 cmsUInt32Number i, j, strideIn, strideOut;
571 _cmsHandleExtraChannels(p, in, out, PixelsPerLine, LineCount, Stride);
573 // Empty buffers for quick memcmp
574 memset(wIn, 0, sizeof(wIn));
575 memset(wOut, 0, sizeof(wOut));
577 // Get copy of zero cache
578 memcpy(&Cache, &p->Cache, sizeof(Cache));
580 strideIn = 0;
581 strideOut = 0;
583 for (i = 0; i < LineCount; i++) {
585 accum = (cmsUInt8Number*)in + strideIn;
586 output = (cmsUInt8Number*)out + strideOut;
588 for (j = 0; j < PixelsPerLine; j++) {
590 accum = p->FromInput(p, wIn, accum, Stride->BytesPerPlaneIn);
592 if (memcmp(wIn, Cache.CacheIn, sizeof(Cache.CacheIn)) == 0) {
594 memcpy(wOut, Cache.CacheOut, sizeof(Cache.CacheOut));
596 else {
597 TransformOnePixelWithGamutCheck(p, wIn, wOut);
599 memcpy(Cache.CacheIn, wIn, sizeof(Cache.CacheIn));
600 memcpy(Cache.CacheOut, wOut, sizeof(Cache.CacheOut));
603 output = p->ToOutput(p, wOut, output, Stride->BytesPerPlaneOut);
606 strideIn += Stride->BytesPerLineIn;
607 strideOut += Stride->BytesPerLineOut;
611 // Transform plug-ins ----------------------------------------------------------------------------------------------------
613 // List of used-defined transform factories
614 typedef struct _cmsTransformCollection_st {
616 _cmsTransform2Factory Factory;
617 cmsBool OldXform; // Factory returns xform function in the old style
619 struct _cmsTransformCollection_st *Next;
621 } _cmsTransformCollection;
623 // The linked list head
624 _cmsTransformPluginChunkType _cmsTransformPluginChunk = { NULL };
627 // Duplicates the zone of memory used by the plug-in in the new context
628 static
629 void DupPluginTransformList(struct _cmsContext_struct* ctx,
630 const struct _cmsContext_struct* src)
632 _cmsTransformPluginChunkType newHead = { NULL };
633 _cmsTransformCollection* entry;
634 _cmsTransformCollection* Anterior = NULL;
635 _cmsTransformPluginChunkType* head = (_cmsTransformPluginChunkType*) src->chunks[TransformPlugin];
637 // Walk the list copying all nodes
638 for (entry = head->TransformCollection;
639 entry != NULL;
640 entry = entry ->Next) {
642 _cmsTransformCollection *newEntry = ( _cmsTransformCollection *) _cmsSubAllocDup(ctx ->MemPool, entry, sizeof(_cmsTransformCollection));
644 if (newEntry == NULL)
645 return;
647 // We want to keep the linked list order, so this is a little bit tricky
648 newEntry -> Next = NULL;
649 if (Anterior)
650 Anterior -> Next = newEntry;
652 Anterior = newEntry;
654 if (newHead.TransformCollection == NULL)
655 newHead.TransformCollection = newEntry;
658 ctx ->chunks[TransformPlugin] = _cmsSubAllocDup(ctx->MemPool, &newHead, sizeof(_cmsTransformPluginChunkType));
661 // Allocates memory for transform plugin factory
662 void _cmsAllocTransformPluginChunk(struct _cmsContext_struct* ctx,
663 const struct _cmsContext_struct* src)
665 if (src != NULL) {
667 // Copy all linked list
668 DupPluginTransformList(ctx, src);
670 else {
671 static _cmsTransformPluginChunkType TransformPluginChunkType = { NULL };
672 ctx ->chunks[TransformPlugin] = _cmsSubAllocDup(ctx ->MemPool, &TransformPluginChunkType, sizeof(_cmsTransformPluginChunkType));
676 // Adaptor for old versions of plug-in
677 static
678 void _cmsTransform2toTransformAdaptor(struct _cmstransform_struct *CMMcargo,
679 const void* InputBuffer,
680 void* OutputBuffer,
681 cmsUInt32Number PixelsPerLine,
682 cmsUInt32Number LineCount,
683 const cmsStride* Stride)
686 cmsUInt32Number i, strideIn, strideOut;
688 _cmsHandleExtraChannels(CMMcargo, InputBuffer, OutputBuffer, PixelsPerLine, LineCount, Stride);
690 strideIn = 0;
691 strideOut = 0;
693 for (i = 0; i < LineCount; i++) {
695 void *accum = (cmsUInt8Number*)InputBuffer + strideIn;
696 void *output = (cmsUInt8Number*)OutputBuffer + strideOut;
698 CMMcargo->OldXform(CMMcargo, accum, output, PixelsPerLine, Stride->BytesPerPlaneIn);
700 strideIn += Stride->BytesPerLineIn;
701 strideOut += Stride->BytesPerLineOut;
707 // Register new ways to transform
708 cmsBool _cmsRegisterTransformPlugin(cmsContext ContextID, cmsPluginBase* Data)
710 cmsPluginTransform* Plugin = (cmsPluginTransform*) Data;
711 _cmsTransformCollection* fl;
712 _cmsTransformPluginChunkType* ctx = ( _cmsTransformPluginChunkType*) _cmsContextGetClientChunk(ContextID,TransformPlugin);
714 if (Data == NULL) {
716 // Free the chain. Memory is safely freed at exit
717 ctx->TransformCollection = NULL;
718 return TRUE;
721 // Factory callback is required
722 if (Plugin->factories.xform == NULL) return FALSE;
725 fl = (_cmsTransformCollection*) _cmsPluginMalloc(ContextID, sizeof(_cmsTransformCollection));
726 if (fl == NULL) return FALSE;
728 // Check for full xform plug-ins previous to 2.8, we would need an adapter in that case
729 if (Plugin->base.ExpectedVersion < 2080) {
731 fl->OldXform = TRUE;
733 else
734 fl->OldXform = FALSE;
736 // Copy the parameters
737 fl->Factory = Plugin->factories.xform;
739 // Keep linked list
740 fl ->Next = ctx->TransformCollection;
741 ctx->TransformCollection = fl;
743 // All is ok
744 return TRUE;
748 void CMSEXPORT _cmsSetTransformUserData(struct _cmstransform_struct *CMMcargo, void* ptr, _cmsFreeUserDataFn FreePrivateDataFn)
750 _cmsAssert(CMMcargo != NULL);
751 CMMcargo ->UserData = ptr;
752 CMMcargo ->FreeUserData = FreePrivateDataFn;
755 // returns the pointer defined by the plug-in to store private data
756 void * CMSEXPORT _cmsGetTransformUserData(struct _cmstransform_struct *CMMcargo)
758 _cmsAssert(CMMcargo != NULL);
759 return CMMcargo ->UserData;
762 // returns the current formatters
763 void CMSEXPORT _cmsGetTransformFormatters16(struct _cmstransform_struct *CMMcargo, cmsFormatter16* FromInput, cmsFormatter16* ToOutput)
765 _cmsAssert(CMMcargo != NULL);
766 if (FromInput) *FromInput = CMMcargo ->FromInput;
767 if (ToOutput) *ToOutput = CMMcargo ->ToOutput;
770 void CMSEXPORT _cmsGetTransformFormattersFloat(struct _cmstransform_struct *CMMcargo, cmsFormatterFloat* FromInput, cmsFormatterFloat* ToOutput)
772 _cmsAssert(CMMcargo != NULL);
773 if (FromInput) *FromInput = CMMcargo ->FromInputFloat;
774 if (ToOutput) *ToOutput = CMMcargo ->ToOutputFloat;
777 // returns original flags
778 cmsUInt32Number CMSEXPORT _cmsGetTransformFlags(struct _cmstransform_struct* CMMcargo)
780 _cmsAssert(CMMcargo != NULL);
781 return CMMcargo->dwOriginalFlags;
784 // Returns the worker callback for parallelization plug-ins
785 _cmsTransform2Fn CMSEXPORT _cmsGetTransformWorker(struct _cmstransform_struct* CMMcargo)
787 _cmsAssert(CMMcargo != NULL);
788 return CMMcargo->Worker;
791 // This field holds maximum number of workers or -1 to auto
792 cmsInt32Number CMSEXPORT _cmsGetTransformMaxWorkers(struct _cmstransform_struct* CMMcargo)
794 _cmsAssert(CMMcargo != NULL);
795 return CMMcargo->MaxWorkers;
798 // This field is actually unused and reserved
799 cmsUInt32Number CMSEXPORT _cmsGetTransformWorkerFlags(struct _cmstransform_struct* CMMcargo)
801 _cmsAssert(CMMcargo != NULL);
802 return CMMcargo->WorkerFlags;
805 // In the case there is a parallelization plug-in, let it to do its job
806 static
807 void ParalellizeIfSuitable(_cmsTRANSFORM* p)
809 _cmsParallelizationPluginChunkType* ctx = (_cmsParallelizationPluginChunkType*)_cmsContextGetClientChunk(p->ContextID, ParallelizationPlugin);
811 _cmsAssert(p != NULL);
812 if (ctx != NULL && ctx->SchedulerFn != NULL) {
814 p->Worker = p->xform;
815 p->xform = ctx->SchedulerFn;
816 p->MaxWorkers = ctx->MaxWorkers;
817 p->WorkerFlags = ctx->WorkerFlags;
823 * An empty unroll to avoid a check with NULL on cmsDoTransform()
825 static
826 cmsUInt8Number* UnrollNothing(CMSREGISTER _cmsTRANSFORM* info,
827 CMSREGISTER cmsUInt16Number wIn[],
828 CMSREGISTER cmsUInt8Number* accum,
829 CMSREGISTER cmsUInt32Number Stride)
831 return accum;
833 cmsUNUSED_PARAMETER(info);
834 cmsUNUSED_PARAMETER(wIn);
835 cmsUNUSED_PARAMETER(Stride);
838 static
839 cmsUInt8Number* PackNothing(CMSREGISTER _cmsTRANSFORM* info,
840 CMSREGISTER cmsUInt16Number wOut[],
841 CMSREGISTER cmsUInt8Number* output,
842 CMSREGISTER cmsUInt32Number Stride)
844 return output;
846 cmsUNUSED_PARAMETER(info);
847 cmsUNUSED_PARAMETER(wOut);
848 cmsUNUSED_PARAMETER(Stride);
851 // Allocate transform struct and set it to defaults. Ask the optimization plug-in about if those formats are proper
852 // for separated transforms. If this is the case,
853 static
854 _cmsTRANSFORM* AllocEmptyTransform(cmsContext ContextID, cmsPipeline* lut,
855 cmsUInt32Number Intent, cmsUInt32Number* InputFormat, cmsUInt32Number* OutputFormat, cmsUInt32Number* dwFlags)
857 _cmsTransformPluginChunkType* ctx = ( _cmsTransformPluginChunkType*) _cmsContextGetClientChunk(ContextID, TransformPlugin);
858 _cmsTransformCollection* Plugin;
860 // Allocate needed memory
861 _cmsTRANSFORM* p = (_cmsTRANSFORM*)_cmsMallocZero(ContextID, sizeof(_cmsTRANSFORM));
862 if (!p) {
863 cmsPipelineFree(lut);
864 return NULL;
867 // Store the proposed pipeline
868 p->Lut = lut;
870 // Let's see if any plug-in want to do the transform by itself
871 if (p->Lut != NULL) {
873 if (!(*dwFlags & cmsFLAGS_NOOPTIMIZE))
875 for (Plugin = ctx->TransformCollection;
876 Plugin != NULL;
877 Plugin = Plugin->Next) {
879 if (Plugin->Factory(&p->xform, &p->UserData, &p->FreeUserData, &p->Lut, InputFormat, OutputFormat, dwFlags)) {
881 // Last plugin in the declaration order takes control. We just keep
882 // the original parameters as a logging.
883 // Note that cmsFLAGS_CAN_CHANGE_FORMATTER is not set, so by default
884 // an optimized transform is not reusable. The plug-in can, however, change
885 // the flags and make it suitable.
887 p->ContextID = ContextID;
888 p->InputFormat = *InputFormat;
889 p->OutputFormat = *OutputFormat;
890 p->dwOriginalFlags = *dwFlags;
892 // Fill the formatters just in case the optimized routine is interested.
893 // No error is thrown if the formatter doesn't exist. It is up to the optimization
894 // factory to decide what to do in those cases.
895 p->FromInput = _cmsGetFormatter(ContextID, *InputFormat, cmsFormatterInput, CMS_PACK_FLAGS_16BITS).Fmt16;
896 p->ToOutput = _cmsGetFormatter(ContextID, *OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_16BITS).Fmt16;
897 p->FromInputFloat = _cmsGetFormatter(ContextID, *InputFormat, cmsFormatterInput, CMS_PACK_FLAGS_FLOAT).FmtFloat;
898 p->ToOutputFloat = _cmsGetFormatter(ContextID, *OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_FLOAT).FmtFloat;
900 // Save the day? (Ignore the warning)
901 if (Plugin->OldXform) {
902 p->OldXform = (_cmsTransformFn)(void*)p->xform;
903 p->xform = _cmsTransform2toTransformAdaptor;
906 ParalellizeIfSuitable(p);
907 return p;
912 // Not suitable for the transform plug-in, let's check the pipeline plug-in
913 _cmsOptimizePipeline(ContextID, &p->Lut, Intent, InputFormat, OutputFormat, dwFlags);
916 // Check whatever this is a true floating point transform
917 if (_cmsFormatterIsFloat(*OutputFormat)) {
919 // Get formatter function always return a valid union, but the contents of this union may be NULL.
920 p ->FromInputFloat = _cmsGetFormatter(ContextID, *InputFormat, cmsFormatterInput, CMS_PACK_FLAGS_FLOAT).FmtFloat;
921 p ->ToOutputFloat = _cmsGetFormatter(ContextID, *OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_FLOAT).FmtFloat;
922 *dwFlags |= cmsFLAGS_CAN_CHANGE_FORMATTER;
924 if (p ->FromInputFloat == NULL || p ->ToOutputFloat == NULL) {
926 cmsSignalError(ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported raster format");
927 cmsDeleteTransform(p);
928 return NULL;
931 if (*dwFlags & cmsFLAGS_NULLTRANSFORM) {
933 p ->xform = NullFloatXFORM;
935 else {
936 // Float transforms don't use cache, always are non-NULL
937 p ->xform = FloatXFORM;
941 else {
943 // Formats are intended to be changed before use
944 if (*InputFormat == 0 && *OutputFormat == 0) {
945 p->FromInput = UnrollNothing;
946 p->ToOutput = PackNothing;
947 *dwFlags |= cmsFLAGS_CAN_CHANGE_FORMATTER;
949 else {
951 cmsUInt32Number BytesPerPixelInput;
953 p ->FromInput = _cmsGetFormatter(ContextID, *InputFormat, cmsFormatterInput, CMS_PACK_FLAGS_16BITS).Fmt16;
954 p ->ToOutput = _cmsGetFormatter(ContextID, *OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_16BITS).Fmt16;
956 if (p ->FromInput == NULL || p ->ToOutput == NULL) {
958 cmsSignalError(ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported raster format");
959 cmsDeleteTransform(p);
960 return NULL;
963 BytesPerPixelInput = T_BYTES(*InputFormat);
964 if (BytesPerPixelInput == 0 || BytesPerPixelInput >= 2)
965 *dwFlags |= cmsFLAGS_CAN_CHANGE_FORMATTER;
969 if (*dwFlags & cmsFLAGS_NULLTRANSFORM) {
971 p ->xform = NullXFORM;
973 else {
974 if (*dwFlags & cmsFLAGS_NOCACHE) {
976 if (*dwFlags & cmsFLAGS_GAMUTCHECK)
977 p ->xform = PrecalculatedXFORMGamutCheck; // Gamut check, no cache
978 else
979 p ->xform = PrecalculatedXFORM; // No cache, no gamut check
981 else {
983 if (*dwFlags & cmsFLAGS_GAMUTCHECK)
984 p ->xform = CachedXFORMGamutCheck; // Gamut check, cache
985 else
986 p ->xform = CachedXFORM; // No gamut check, cache
992 p ->InputFormat = *InputFormat;
993 p ->OutputFormat = *OutputFormat;
994 p ->dwOriginalFlags = *dwFlags;
995 p ->ContextID = ContextID;
996 p ->UserData = NULL;
997 ParalellizeIfSuitable(p);
998 return p;
1001 static
1002 cmsBool GetXFormColorSpaces(cmsUInt32Number nProfiles, cmsHPROFILE hProfiles[], cmsColorSpaceSignature* Input, cmsColorSpaceSignature* Output)
1004 cmsColorSpaceSignature ColorSpaceIn, ColorSpaceOut;
1005 cmsColorSpaceSignature PostColorSpace;
1006 cmsUInt32Number i;
1008 if (nProfiles == 0) return FALSE;
1009 if (hProfiles[0] == NULL) return FALSE;
1011 *Input = PostColorSpace = cmsGetColorSpace(hProfiles[0]);
1013 for (i=0; i < nProfiles; i++) {
1015 cmsProfileClassSignature cls;
1016 cmsHPROFILE hProfile = hProfiles[i];
1018 int lIsInput = (PostColorSpace != cmsSigXYZData) &&
1019 (PostColorSpace != cmsSigLabData);
1021 if (hProfile == NULL) return FALSE;
1023 cls = cmsGetDeviceClass(hProfile);
1025 if (cls == cmsSigNamedColorClass) {
1027 ColorSpaceIn = cmsSig1colorData;
1028 ColorSpaceOut = (nProfiles > 1) ? cmsGetPCS(hProfile) : cmsGetColorSpace(hProfile);
1030 else
1031 if (lIsInput || (cls == cmsSigLinkClass)) {
1033 ColorSpaceIn = cmsGetColorSpace(hProfile);
1034 ColorSpaceOut = cmsGetPCS(hProfile);
1036 else
1038 ColorSpaceIn = cmsGetPCS(hProfile);
1039 ColorSpaceOut = cmsGetColorSpace(hProfile);
1042 if (i==0)
1043 *Input = ColorSpaceIn;
1045 PostColorSpace = ColorSpaceOut;
1048 *Output = PostColorSpace;
1050 return TRUE;
1053 // Check colorspace
1054 static
1055 cmsBool IsProperColorSpace(cmsColorSpaceSignature Check, cmsUInt32Number dwFormat)
1057 int Space1 = (int) T_COLORSPACE(dwFormat);
1058 int Space2 = _cmsLCMScolorSpace(Check);
1060 if (Space1 == PT_ANY) return TRUE;
1061 if (Space1 == Space2) return TRUE;
1063 if (Space1 == PT_LabV2 && Space2 == PT_Lab) return TRUE;
1064 if (Space1 == PT_Lab && Space2 == PT_LabV2) return TRUE;
1066 return FALSE;
1069 // ----------------------------------------------------------------------------------------------------------------
1071 // Jun-21-2000: Some profiles (those that comes with W2K) comes
1072 // with the media white (media black?) x 100. Add a sanity check
1074 static
1075 void NormalizeXYZ(cmsCIEXYZ* Dest)
1077 while (Dest -> X > 2. &&
1078 Dest -> Y > 2. &&
1079 Dest -> Z > 2.) {
1081 Dest -> X /= 10.;
1082 Dest -> Y /= 10.;
1083 Dest -> Z /= 10.;
1087 static
1088 void SetWhitePoint(cmsCIEXYZ* wtPt, const cmsCIEXYZ* src)
1090 if (src == NULL) {
1091 wtPt ->X = cmsD50X;
1092 wtPt ->Y = cmsD50Y;
1093 wtPt ->Z = cmsD50Z;
1095 else {
1096 wtPt ->X = src->X;
1097 wtPt ->Y = src->Y;
1098 wtPt ->Z = src->Z;
1100 NormalizeXYZ(wtPt);
1105 // New to lcms 2.0 -- have all parameters available.
1106 cmsHTRANSFORM CMSEXPORT cmsCreateExtendedTransform(cmsContext ContextID,
1107 cmsUInt32Number nProfiles, cmsHPROFILE hProfiles[],
1108 cmsBool BPC[],
1109 cmsUInt32Number Intents[],
1110 cmsFloat64Number AdaptationStates[],
1111 cmsHPROFILE hGamutProfile,
1112 cmsUInt32Number nGamutPCSposition,
1113 cmsUInt32Number InputFormat,
1114 cmsUInt32Number OutputFormat,
1115 cmsUInt32Number dwFlags)
1117 _cmsTRANSFORM* xform;
1118 cmsColorSpaceSignature EntryColorSpace;
1119 cmsColorSpaceSignature ExitColorSpace;
1120 cmsPipeline* Lut;
1121 cmsUInt32Number LastIntent = Intents[nProfiles-1];
1123 // If it is a fake transform
1124 if (dwFlags & cmsFLAGS_NULLTRANSFORM)
1126 return AllocEmptyTransform(ContextID, NULL, INTENT_PERCEPTUAL, &InputFormat, &OutputFormat, &dwFlags);
1129 // If gamut check is requested, make sure we have a gamut profile
1130 if (dwFlags & cmsFLAGS_GAMUTCHECK) {
1131 if (hGamutProfile == NULL) dwFlags &= ~cmsFLAGS_GAMUTCHECK;
1134 // On floating point transforms, inhibit cache
1135 if (_cmsFormatterIsFloat(InputFormat) || _cmsFormatterIsFloat(OutputFormat))
1136 dwFlags |= cmsFLAGS_NOCACHE;
1138 // Mark entry/exit spaces
1139 if (!GetXFormColorSpaces(nProfiles, hProfiles, &EntryColorSpace, &ExitColorSpace)) {
1140 cmsSignalError(ContextID, cmsERROR_NULL, "NULL input profiles on transform");
1141 return NULL;
1144 // Check if proper colorspaces
1145 if (!IsProperColorSpace(EntryColorSpace, InputFormat)) {
1146 cmsSignalError(ContextID, cmsERROR_COLORSPACE_CHECK, "Wrong input color space on transform");
1147 return NULL;
1150 if (!IsProperColorSpace(ExitColorSpace, OutputFormat)) {
1151 cmsSignalError(ContextID, cmsERROR_COLORSPACE_CHECK, "Wrong output color space on transform");
1152 return NULL;
1155 // Check whatever the transform is 16 bits and involves linear RGB in first profile. If so, disable optimizations
1156 if (EntryColorSpace == cmsSigRgbData && T_BYTES(InputFormat) == 2 && !(dwFlags & cmsFLAGS_NOOPTIMIZE))
1158 cmsFloat64Number gamma = cmsDetectRGBProfileGamma(hProfiles[0], 0.1);
1160 if (gamma > 0 && gamma < 1.6)
1161 dwFlags |= cmsFLAGS_NOOPTIMIZE;
1164 // Create a pipeline with all transformations
1165 Lut = _cmsLinkProfiles(ContextID, nProfiles, Intents, hProfiles, BPC, AdaptationStates, dwFlags);
1166 if (Lut == NULL) {
1167 cmsSignalError(ContextID, cmsERROR_NOT_SUITABLE, "Couldn't link the profiles");
1168 return NULL;
1171 // Check channel count
1172 if ((cmsChannelsOfColorSpace(EntryColorSpace) != (cmsInt32Number) cmsPipelineInputChannels(Lut)) ||
1173 (cmsChannelsOfColorSpace(ExitColorSpace) != (cmsInt32Number) cmsPipelineOutputChannels(Lut))) {
1174 cmsPipelineFree(Lut);
1175 cmsSignalError(ContextID, cmsERROR_NOT_SUITABLE, "Channel count doesn't match. Profile is corrupted");
1176 return NULL;
1180 // All seems ok
1181 xform = AllocEmptyTransform(ContextID, Lut, LastIntent, &InputFormat, &OutputFormat, &dwFlags);
1182 if (xform == NULL) {
1183 return NULL;
1186 // Keep values
1187 xform ->EntryColorSpace = EntryColorSpace;
1188 xform ->ExitColorSpace = ExitColorSpace;
1189 xform ->RenderingIntent = Intents[nProfiles-1];
1191 // Take white points
1192 SetWhitePoint(&xform->EntryWhitePoint, (cmsCIEXYZ*) cmsReadTag(hProfiles[0], cmsSigMediaWhitePointTag));
1193 SetWhitePoint(&xform->ExitWhitePoint, (cmsCIEXYZ*) cmsReadTag(hProfiles[nProfiles-1], cmsSigMediaWhitePointTag));
1196 // Create a gamut check LUT if requested
1197 if (hGamutProfile != NULL && (dwFlags & cmsFLAGS_GAMUTCHECK))
1198 xform ->GamutCheck = _cmsCreateGamutCheckPipeline(ContextID, hProfiles,
1199 BPC, Intents,
1200 AdaptationStates,
1201 nGamutPCSposition,
1202 hGamutProfile);
1205 // Try to read input and output colorant table
1206 if (cmsIsTag(hProfiles[0], cmsSigColorantTableTag)) {
1208 // Input table can only come in this way.
1209 xform ->InputColorant = cmsDupNamedColorList((cmsNAMEDCOLORLIST*) cmsReadTag(hProfiles[0], cmsSigColorantTableTag));
1212 // Output is a little bit more complex.
1213 if (cmsGetDeviceClass(hProfiles[nProfiles-1]) == cmsSigLinkClass) {
1215 // This tag may exist only on devicelink profiles.
1216 if (cmsIsTag(hProfiles[nProfiles-1], cmsSigColorantTableOutTag)) {
1218 // It may be NULL if error
1219 xform ->OutputColorant = cmsDupNamedColorList((cmsNAMEDCOLORLIST*) cmsReadTag(hProfiles[nProfiles-1], cmsSigColorantTableOutTag));
1222 } else {
1224 if (cmsIsTag(hProfiles[nProfiles-1], cmsSigColorantTableTag)) {
1226 xform -> OutputColorant = cmsDupNamedColorList((cmsNAMEDCOLORLIST*) cmsReadTag(hProfiles[nProfiles-1], cmsSigColorantTableTag));
1230 // Store the sequence of profiles
1231 if (dwFlags & cmsFLAGS_KEEP_SEQUENCE) {
1232 xform ->Sequence = _cmsCompileProfileSequence(ContextID, nProfiles, hProfiles);
1234 else
1235 xform ->Sequence = NULL;
1237 // If this is a cached transform, init first value, which is zero (16 bits only)
1238 if (!(dwFlags & cmsFLAGS_NOCACHE)) {
1240 memset(&xform ->Cache.CacheIn, 0, sizeof(xform ->Cache.CacheIn));
1242 if (xform ->GamutCheck != NULL) {
1243 TransformOnePixelWithGamutCheck(xform, xform ->Cache.CacheIn, xform->Cache.CacheOut);
1245 else {
1247 xform ->Lut ->Eval16Fn(xform ->Cache.CacheIn, xform->Cache.CacheOut, xform -> Lut->Data);
1252 return (cmsHTRANSFORM) xform;
1255 // Multiprofile transforms: Gamut check is not available here, as it is unclear from which profile the gamut comes.
1256 cmsHTRANSFORM CMSEXPORT cmsCreateMultiprofileTransformTHR(cmsContext ContextID,
1257 cmsHPROFILE hProfiles[],
1258 cmsUInt32Number nProfiles,
1259 cmsUInt32Number InputFormat,
1260 cmsUInt32Number OutputFormat,
1261 cmsUInt32Number Intent,
1262 cmsUInt32Number dwFlags)
1264 cmsUInt32Number i;
1265 cmsBool BPC[256];
1266 cmsUInt32Number Intents[256];
1267 cmsFloat64Number AdaptationStates[256];
1269 if (nProfiles <= 0 || nProfiles > 255) {
1270 cmsSignalError(ContextID, cmsERROR_RANGE, "Wrong number of profiles. 1..255 expected, %d found.", nProfiles);
1271 return NULL;
1274 for (i=0; i < nProfiles; i++) {
1275 BPC[i] = dwFlags & cmsFLAGS_BLACKPOINTCOMPENSATION ? TRUE : FALSE;
1276 Intents[i] = Intent;
1277 AdaptationStates[i] = cmsSetAdaptationStateTHR(ContextID, -1);
1281 return cmsCreateExtendedTransform(ContextID, nProfiles, hProfiles, BPC, Intents, AdaptationStates, NULL, 0, InputFormat, OutputFormat, dwFlags);
1286 cmsHTRANSFORM CMSEXPORT cmsCreateMultiprofileTransform(cmsHPROFILE hProfiles[],
1287 cmsUInt32Number nProfiles,
1288 cmsUInt32Number InputFormat,
1289 cmsUInt32Number OutputFormat,
1290 cmsUInt32Number Intent,
1291 cmsUInt32Number dwFlags)
1294 if (nProfiles <= 0 || nProfiles > 255) {
1295 cmsSignalError(NULL, cmsERROR_RANGE, "Wrong number of profiles. 1..255 expected, %d found.", nProfiles);
1296 return NULL;
1299 return cmsCreateMultiprofileTransformTHR(cmsGetProfileContextID(hProfiles[0]),
1300 hProfiles,
1301 nProfiles,
1302 InputFormat,
1303 OutputFormat,
1304 Intent,
1305 dwFlags);
1308 cmsHTRANSFORM CMSEXPORT cmsCreateTransformTHR(cmsContext ContextID,
1309 cmsHPROFILE Input,
1310 cmsUInt32Number InputFormat,
1311 cmsHPROFILE Output,
1312 cmsUInt32Number OutputFormat,
1313 cmsUInt32Number Intent,
1314 cmsUInt32Number dwFlags)
1317 cmsHPROFILE hArray[2];
1319 hArray[0] = Input;
1320 hArray[1] = Output;
1322 return cmsCreateMultiprofileTransformTHR(ContextID, hArray, Output == NULL ? 1U : 2U, InputFormat, OutputFormat, Intent, dwFlags);
1325 CMSAPI cmsHTRANSFORM CMSEXPORT cmsCreateTransform(cmsHPROFILE Input,
1326 cmsUInt32Number InputFormat,
1327 cmsHPROFILE Output,
1328 cmsUInt32Number OutputFormat,
1329 cmsUInt32Number Intent,
1330 cmsUInt32Number dwFlags)
1332 return cmsCreateTransformTHR(cmsGetProfileContextID(Input), Input, InputFormat, Output, OutputFormat, Intent, dwFlags);
1336 cmsHTRANSFORM CMSEXPORT cmsCreateProofingTransformTHR(cmsContext ContextID,
1337 cmsHPROFILE InputProfile,
1338 cmsUInt32Number InputFormat,
1339 cmsHPROFILE OutputProfile,
1340 cmsUInt32Number OutputFormat,
1341 cmsHPROFILE ProofingProfile,
1342 cmsUInt32Number nIntent,
1343 cmsUInt32Number ProofingIntent,
1344 cmsUInt32Number dwFlags)
1346 cmsHPROFILE hArray[4];
1347 cmsUInt32Number Intents[4];
1348 cmsBool BPC[4];
1349 cmsFloat64Number Adaptation[4];
1350 cmsBool DoBPC = (dwFlags & cmsFLAGS_BLACKPOINTCOMPENSATION) ? TRUE : FALSE;
1353 hArray[0] = InputProfile; hArray[1] = ProofingProfile; hArray[2] = ProofingProfile; hArray[3] = OutputProfile;
1354 Intents[0] = nIntent; Intents[1] = nIntent; Intents[2] = INTENT_RELATIVE_COLORIMETRIC; Intents[3] = ProofingIntent;
1355 BPC[0] = DoBPC; BPC[1] = DoBPC; BPC[2] = 0; BPC[3] = 0;
1357 Adaptation[0] = Adaptation[1] = Adaptation[2] = Adaptation[3] = cmsSetAdaptationStateTHR(ContextID, -1);
1359 if (!(dwFlags & (cmsFLAGS_SOFTPROOFING|cmsFLAGS_GAMUTCHECK)))
1360 return cmsCreateTransformTHR(ContextID, InputProfile, InputFormat, OutputProfile, OutputFormat, nIntent, dwFlags);
1362 return cmsCreateExtendedTransform(ContextID, 4, hArray, BPC, Intents, Adaptation,
1363 ProofingProfile, 1, InputFormat, OutputFormat, dwFlags);
1368 cmsHTRANSFORM CMSEXPORT cmsCreateProofingTransform(cmsHPROFILE InputProfile,
1369 cmsUInt32Number InputFormat,
1370 cmsHPROFILE OutputProfile,
1371 cmsUInt32Number OutputFormat,
1372 cmsHPROFILE ProofingProfile,
1373 cmsUInt32Number nIntent,
1374 cmsUInt32Number ProofingIntent,
1375 cmsUInt32Number dwFlags)
1377 return cmsCreateProofingTransformTHR(cmsGetProfileContextID(InputProfile),
1378 InputProfile,
1379 InputFormat,
1380 OutputProfile,
1381 OutputFormat,
1382 ProofingProfile,
1383 nIntent,
1384 ProofingIntent,
1385 dwFlags);
1389 // Grab the ContextID from an open transform. Returns NULL if a NULL transform is passed
1390 cmsContext CMSEXPORT cmsGetTransformContextID(cmsHTRANSFORM hTransform)
1392 _cmsTRANSFORM* xform = (_cmsTRANSFORM*) hTransform;
1394 if (xform == NULL) return NULL;
1395 return xform -> ContextID;
1398 // Grab the input/output formats
1399 cmsUInt32Number CMSEXPORT cmsGetTransformInputFormat(cmsHTRANSFORM hTransform)
1401 _cmsTRANSFORM* xform = (_cmsTRANSFORM*) hTransform;
1403 if (xform == NULL) return 0;
1404 return xform->InputFormat;
1407 cmsUInt32Number CMSEXPORT cmsGetTransformOutputFormat(cmsHTRANSFORM hTransform)
1409 _cmsTRANSFORM* xform = (_cmsTRANSFORM*) hTransform;
1411 if (xform == NULL) return 0;
1412 return xform->OutputFormat;
1415 // For backwards compatibility
1416 cmsBool CMSEXPORT cmsChangeBuffersFormat(cmsHTRANSFORM hTransform,
1417 cmsUInt32Number InputFormat,
1418 cmsUInt32Number OutputFormat)
1420 _cmsTRANSFORM* xform = (_cmsTRANSFORM*) hTransform;
1421 cmsFormatter16 FromInput, ToOutput;
1424 // We only can afford to change formatters if previous transform is at least 16 bits
1425 if (!(xform ->dwOriginalFlags & cmsFLAGS_CAN_CHANGE_FORMATTER)) {
1427 cmsSignalError(xform ->ContextID, cmsERROR_NOT_SUITABLE, "cmsChangeBuffersFormat works only on transforms created originally with at least 16 bits of precision");
1428 return FALSE;
1431 FromInput = _cmsGetFormatter(xform->ContextID, InputFormat, cmsFormatterInput, CMS_PACK_FLAGS_16BITS).Fmt16;
1432 ToOutput = _cmsGetFormatter(xform->ContextID, OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_16BITS).Fmt16;
1434 if (FromInput == NULL || ToOutput == NULL) {
1436 cmsSignalError(xform -> ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported raster format");
1437 return FALSE;
1440 xform ->InputFormat = InputFormat;
1441 xform ->OutputFormat = OutputFormat;
1442 xform ->FromInput = FromInput;
1443 xform ->ToOutput = ToOutput;
1444 return TRUE;