1//---------------------------------------------------------------------------------
2//
3// Little Color Management System
4// Copyright (c) 1998-2017 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:
12//
13// The above copyright notice and this permission notice shall be included in
14// all copies or substantial portions of the Software.
15//
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.
23//
24//---------------------------------------------------------------------------------
25//
26
27#include "lcms2_internal.h"
28
29// Transformations stuff
30// -----------------------------------------------------------------------
31
32#define DEFAULT_OBSERVER_ADAPTATION_STATE 1.0
33
34// The Context0 observer adaptation state.
35_cmsAdaptationStateChunkType _cmsAdaptationStateChunk = { DEFAULT_OBSERVER_ADAPTATION_STATE };
36
37// Init and duplicate observer adaptation state
38void _cmsAllocAdaptationStateChunk(struct _cmsContext_struct* ctx,
39 const struct _cmsContext_struct* src)
40{
41 static _cmsAdaptationStateChunkType AdaptationStateChunk = { DEFAULT_OBSERVER_ADAPTATION_STATE };
42 void* from;
43
44 if (src != NULL) {
45 from = src ->chunks[AdaptationStateContext];
46 }
47 else {
48 from = &AdaptationStateChunk;
49 }
50
51 ctx ->chunks[AdaptationStateContext] = _cmsSubAllocDup(ctx ->MemPool, from, sizeof(_cmsAdaptationStateChunkType));
52}
53
54
55// Sets adaptation state for absolute colorimetric intent in the given context. Adaptation state applies on all
56// but cmsCreateExtendedTransform(). Little CMS can handle incomplete adaptation states.
57// The adaptation state may be defaulted by this function. If you don't like it, use the extended transform routine
58cmsFloat64Number CMSEXPORT cmsSetAdaptationState(cmsContext ContextID, cmsFloat64Number d)
59{
60 cmsFloat64Number prev;
61 _cmsAdaptationStateChunkType* ptr = (_cmsAdaptationStateChunkType*) _cmsContextGetClientChunk(ContextID, AdaptationStateContext);
62
63 // Get previous value for return
64 prev = ptr ->AdaptationState;
65
66 // Set the value if d is positive or zero
67 if (d >= 0.0) {
68
69 ptr ->AdaptationState = d;
70 }
71
72 // Always return previous value
73 return prev;
74}
75
76
77// -----------------------------------------------------------------------
78
79// Alarm codes for 16-bit transformations, because the fixed range of containers there are
80// no values left to mark out of gamut.
81
82#define DEFAULT_ALARM_CODES_VALUE {0x7F00, 0x7F00, 0x7F00, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
83
84_cmsAlarmCodesChunkType _cmsAlarmCodesChunk = { DEFAULT_ALARM_CODES_VALUE };
85
86// Sets the codes used to mark out-out-gamut on Proofing transforms for a given context. Values are meant to be
87// encoded in 16 bits.
88void CMSEXPORT cmsSetAlarmCodes(cmsContext ContextID, const cmsUInt16Number AlarmCodesP[cmsMAXCHANNELS])
89{
90 _cmsAlarmCodesChunkType* ContextAlarmCodes = (_cmsAlarmCodesChunkType*) _cmsContextGetClientChunk(ContextID, AlarmCodesContext);
91
92 _cmsAssert(ContextAlarmCodes != NULL); // Can't happen
93
94 memcpy(ContextAlarmCodes->AlarmCodes, AlarmCodesP, sizeof(ContextAlarmCodes->AlarmCodes));
95}
96
97// Gets the current codes used to mark out-out-gamut on Proofing transforms for the given context.
98// Values are meant to be encoded in 16 bits.
99void CMSEXPORT cmsGetAlarmCodes(cmsContext ContextID, cmsUInt16Number AlarmCodesP[cmsMAXCHANNELS])
100{
101 _cmsAlarmCodesChunkType* ContextAlarmCodes = (_cmsAlarmCodesChunkType*) _cmsContextGetClientChunk(ContextID, AlarmCodesContext);
102
103 _cmsAssert(ContextAlarmCodes != NULL); // Can't happen
104
105 memcpy(AlarmCodesP, ContextAlarmCodes->AlarmCodes, sizeof(ContextAlarmCodes->AlarmCodes));
106}
107
108
109// Init and duplicate alarm codes
110void _cmsAllocAlarmCodesChunk(struct _cmsContext_struct* ctx,
111 const struct _cmsContext_struct* src)
112{
113 static _cmsAlarmCodesChunkType AlarmCodesChunk = { DEFAULT_ALARM_CODES_VALUE };
114 void* from;
115
116 if (src != NULL) {
117 from = src ->chunks[AlarmCodesContext];
118 }
119 else {
120 from = &AlarmCodesChunk;
121 }
122
123 ctx ->chunks[AlarmCodesContext] = _cmsSubAllocDup(ctx ->MemPool, from, sizeof(_cmsAlarmCodesChunkType));
124}
125
126// -----------------------------------------------------------------------
127
128// Get rid of transform resources
129void CMSEXPORT cmsDeleteTransform(cmsContext ContextID, cmsHTRANSFORM hTransform)
130{
131 _cmsTRANSFORM* p = (_cmsTRANSFORM*) hTransform;
132 _cmsTRANSFORMCORE *core;
133 cmsUInt32Number refs;
134
135 if (p == NULL)
136 return;
137
138 core = p->core;
139
140 _cmsAssert(core != NULL);
141
142 refs = _cmsAdjustReferenceCount(&core->refs, -1);
143 _cmsFree(ContextID, (void *) p);
144
145 if (refs != 0)
146 return;
147
148 if (core->GamutCheck)
149 cmsPipelineFree(ContextID, core->GamutCheck);
150
151 if (core->Lut)
152 cmsPipelineFree(ContextID, core->Lut);
153
154 if (core->InputColorant)
155 cmsFreeNamedColorList(ContextID, core->InputColorant);
156
157 if (core->OutputColorant)
158 cmsFreeNamedColorList(ContextID, core->OutputColorant);
159
160 if (core->Sequence)
161 cmsFreeProfileSequenceDescription(ContextID, core->Sequence);
162
163 if (core->UserData)
164 core->FreeUserData(ContextID, core->UserData);
165
166 _cmsFree(ContextID, (void *)core);
167}
168
169// Apply transform.
170void CMSEXPORT cmsDoTransform(cmsContext ContextID, cmsHTRANSFORM Transform,
171 const void* InputBuffer,
172 void* OutputBuffer,
173 cmsUInt32Number Size)
174
175{
176 _cmsTRANSFORM* p = (_cmsTRANSFORM*) Transform;
177 cmsStride stride;
178
179 stride.BytesPerLineIn = 0; // Not used
180 stride.BytesPerLineOut = 0;
181 stride.BytesPerPlaneIn = Size;
182 stride.BytesPerPlaneOut = Size;
183
184 p -> xform(ContextID, p, InputBuffer, OutputBuffer, Size, 1, &stride);
185}
186
187
188// This is a legacy stride for planar
189void CMSEXPORT cmsDoTransformStride(cmsContext ContextID, cmsHTRANSFORM Transform,
190 const void* InputBuffer,
191 void* OutputBuffer,
192 cmsUInt32Number Size, cmsUInt32Number Stride)
193
194{
195 _cmsTRANSFORM* p = (_cmsTRANSFORM*) Transform;
196 cmsStride stride;
197
198 stride.BytesPerLineIn = 0;
199 stride.BytesPerLineOut = 0;
200 stride.BytesPerPlaneIn = Stride;
201 stride.BytesPerPlaneOut = Stride;
202
203 p -> xform(ContextID, p, InputBuffer, OutputBuffer, Size, 1, &stride);
204}
205
206// This is the "fast" function for plugins
207void CMSEXPORT cmsDoTransformLineStride(cmsContext ContextID, cmsHTRANSFORM Transform,
208 const void* InputBuffer,
209 void* OutputBuffer,
210 cmsUInt32Number PixelsPerLine,
211 cmsUInt32Number LineCount,
212 cmsUInt32Number BytesPerLineIn,
213 cmsUInt32Number BytesPerLineOut,
214 cmsUInt32Number BytesPerPlaneIn,
215 cmsUInt32Number BytesPerPlaneOut)
216
217{
218 _cmsTRANSFORM* p = (_cmsTRANSFORM*) Transform;
219 cmsStride stride;
220
221 stride.BytesPerLineIn = BytesPerLineIn;
222 stride.BytesPerLineOut = BytesPerLineOut;
223 stride.BytesPerPlaneIn = BytesPerPlaneIn;
224 stride.BytesPerPlaneOut = BytesPerPlaneOut;
225
226 p->xform(ContextID, p, InputBuffer, OutputBuffer, PixelsPerLine, LineCount, &stride);
227}
228
229
230
231// Transform routines ----------------------------------------------------------------------------------------------------------
232
233// Float xform converts floats. Since there are no performance issues, one routine does all job, including gamut check.
234// Note that because extended range, we can use a -1.0 value for out of gamut in this case.
235static
236void FloatXFORM(cmsContext ContextID, _cmsTRANSFORM* p,
237 const void* in,
238 void* out,
239 cmsUInt32Number PixelsPerLine,
240 cmsUInt32Number LineCount,
241 const cmsStride* Stride)
242{
243 cmsUInt8Number* accum;
244 cmsUInt8Number* output;
245 cmsFloat32Number fIn[cmsMAXCHANNELS], fOut[cmsMAXCHANNELS];
246 cmsFloat32Number OutOfGamut;
247 cmsUInt32Number i, j, c, strideIn, strideOut;
248 _cmsTRANSFORMCORE *core = p->core;
249
250 _cmsHandleExtraChannels(ContextID, p, in, out, PixelsPerLine, LineCount, Stride);
251
252 strideIn = 0;
253 strideOut = 0;
254 memset(fIn, 0, sizeof(fIn));
255 memset(fOut, 0, sizeof(fIn));
256
257 for (i = 0; i < LineCount; i++) {
258
259 accum = (cmsUInt8Number*)in + strideIn;
260 output = (cmsUInt8Number*)out + strideOut;
261
262 for (j = 0; j < PixelsPerLine; j++) {
263
264 accum = p->FromInputFloat(ContextID, p, fIn, accum, Stride->BytesPerPlaneIn);
265
266 // Any gamut chack to do?
267 if (core->GamutCheck != NULL) {
268
269 // Evaluate gamut marker.
270 cmsPipelineEvalFloat(ContextID, fIn, &OutOfGamut, core->GamutCheck);
271
272 // Is current color out of gamut?
273 if (OutOfGamut > 0.0) {
274
275 // Certainly, out of gamut
276 for (c = 0; c < cmsMAXCHANNELS; c++)
277 fOut[c] = -1.0;
278
279 }
280 else {
281 // No, proceed normally
282 cmsPipelineEvalFloat(ContextID, fIn, fOut, core->Lut);
283 }
284 }
285 else {
286
287 // No gamut check at all
288 cmsPipelineEvalFloat(ContextID, fIn, fOut, core->Lut);
289 }
290
291
292 output = p->ToOutputFloat(ContextID, p, fOut, output, Stride->BytesPerPlaneOut);
293 }
294
295 strideIn += Stride->BytesPerLineIn;
296 strideOut += Stride->BytesPerLineOut;
297 }
298
299}
300
301
302static
303void NullFloatXFORM(cmsContext ContextID, _cmsTRANSFORM* p,
304 const void* in,
305 void* out,
306 cmsUInt32Number PixelsPerLine,
307 cmsUInt32Number LineCount,
308 const cmsStride* Stride)
309
310{
311 cmsUInt8Number* accum;
312 cmsUInt8Number* output;
313 cmsFloat32Number fIn[cmsMAXCHANNELS];
314 cmsUInt32Number i, j, strideIn, strideOut;
315
316 _cmsHandleExtraChannels(ContextID, p, in, out, PixelsPerLine, LineCount, Stride);
317
318 strideIn = 0;
319 strideOut = 0;
320 memset(fIn, 0, sizeof(fIn));
321
322 for (i = 0; i < LineCount; i++) {
323
324 accum = (cmsUInt8Number*) in + strideIn;
325 output = (cmsUInt8Number*) out + strideOut;
326
327 for (j = 0; j < PixelsPerLine; j++) {
328
329 accum = p->FromInputFloat(ContextID, p, fIn, accum, Stride ->BytesPerPlaneIn);
330 output = p->ToOutputFloat(ContextID, p, fIn, output, Stride->BytesPerPlaneOut);
331 }
332
333 strideIn += Stride->BytesPerLineIn;
334 strideOut += Stride->BytesPerLineOut;
335 }
336}
337
338// 16 bit precision -----------------------------------------------------------------------------------------------------------
339
340// Null transformation, only applies formatters. No cache
341static
342void NullXFORM(cmsContext ContextID,
343 _cmsTRANSFORM* p,
344 const void* in,
345 void* out,
346 cmsUInt32Number PixelsPerLine,
347 cmsUInt32Number LineCount,
348 const cmsStride* Stride)
349{
350 cmsUInt8Number* accum;
351 cmsUInt8Number* output;
352 cmsUInt16Number wIn[cmsMAXCHANNELS];
353 cmsUInt32Number i, j, strideIn, strideOut;
354
355 _cmsHandleExtraChannels(ContextID, p, in, out, PixelsPerLine, LineCount, Stride);
356
357 strideIn = 0;
358 strideOut = 0;
359 memset(wIn, 0, sizeof(wIn));
360
361 for (i = 0; i < LineCount; i++) {
362
363 accum = (cmsUInt8Number*)in + strideIn;
364 output = (cmsUInt8Number*)out + strideOut;
365
366 for (j = 0; j < PixelsPerLine; j++) {
367
368 accum = p->FromInput(ContextID, p, wIn, accum, Stride->BytesPerPlaneIn);
369 output = p->ToOutput(ContextID, p, wIn, output, Stride->BytesPerPlaneOut);
370 }
371
372 strideIn += Stride->BytesPerLineIn;
373 strideOut += Stride->BytesPerLineOut;
374 }
375
376}
377
378
379// No gamut check, no cache, 16 bits
380#define FUNCTION_NAME PrecalculatedXFORM
381#include "extra_xform.h"
382
383// No gamut check, no cache, Identity transform, including pack/unpack
384static
385void PrecalculatedXFORMIdentity(cmsContext ContextID,
386 _cmsTRANSFORM* p,
387 const void* in,
388 void* out,
389 cmsUInt32Number PixelsPerLine,
390 cmsUInt32Number LineCount,
391 const cmsStride* Stride)
392{
393 cmsUInt32Number bpli = Stride->BytesPerLineIn;
394 cmsUInt32Number bplo = Stride->BytesPerLineOut;
395 int bpp;
396 cmsUNUSED_PARAMETER(ContextID);
397
398 /* Silence some warnings */
399 (void)bpli;
400 (void)bplo;
401
402 if ((in == out && bpli == bplo) || PixelsPerLine == 0)
403 return;
404
405 bpp = T_BYTES(p->InputFormat);
406 if (bpp == 0)
407 bpp = sizeof(double);
408 bpp *= T_CHANNELS(p->InputFormat) + T_EXTRA(p->InputFormat);
409 PixelsPerLine *= bpp; /* Convert to BytesPerLine */
410 while (LineCount-- > 0)
411 {
412 memmove(out, in, PixelsPerLine);
413 in = (void *)((cmsUInt8Number *)in + bpli);
414 out = (void *)((cmsUInt8Number *)out + bplo);
415 }
416}
417
418static
419void PrecalculatedXFORMIdentityPlanar(cmsContext ContextID,
420 _cmsTRANSFORM* p,
421 const void* in,
422 void* out,
423 cmsUInt32Number PixelsPerLine,
424 cmsUInt32Number LineCount,
425 const cmsStride* Stride)
426{
427 cmsUInt32Number bpli = Stride->BytesPerLineIn;
428 cmsUInt32Number bplo = Stride->BytesPerLineOut;
429 cmsUInt32Number bppi = Stride->BytesPerPlaneIn;
430 cmsUInt32Number bppo = Stride->BytesPerPlaneOut;
431 int bpp;
432 int planes;
433 const void *plane_in;
434 void *plane_out;
435 cmsUNUSED_PARAMETER(ContextID);
436
437 /* Silence some warnings */
438 (void)bpli;
439 (void)bplo;
440 (void)bppi;
441 (void)bppo;
442
443 if ((in == out && bpli == bplo && bppi == bppo) || PixelsPerLine == 0)
444 return;
445
446 bpp = T_BYTES(p->InputFormat);
447 if (bpp == 0)
448 bpp = sizeof(double);
449 PixelsPerLine *= bpp; /* Convert to BytesPerLine */
450 planes = T_CHANNELS(p->InputFormat) + T_EXTRA(p->InputFormat);
451 while (planes-- > 0)
452 {
453 plane_in = in;
454 plane_out = out;
455 while (LineCount-- > 0)
456 {
457 memmove(plane_out, plane_in, PixelsPerLine);
458 plane_in = (void *)((cmsUInt8Number *)plane_in + bpli);
459 plane_out = (void *)((cmsUInt8Number *)plane_out + bplo);
460 }
461 in = (void *)((cmsUInt8Number *)in + bppi);
462 out = (void *)((cmsUInt8Number *)out + bppo);
463 }
464}
465
466// Auxiliary: Handle precalculated gamut check. The retrieval of context may be alittle bit slow, but this function is not critical.
467static
468void TransformOnePixelWithGamutCheck(cmsContext ContextID, _cmsTRANSFORM* p,
469 const cmsUInt16Number wIn[],
470 cmsUInt16Number wOut[])
471{
472 cmsUInt16Number wOutOfGamut;
473 _cmsTRANSFORMCORE *core = p->core;
474
475 core->GamutCheck->Eval16Fn(ContextID, wIn, &wOutOfGamut, core->GamutCheck->Data);
476 if (wOutOfGamut >= 1) {
477
478 cmsUInt32Number i;
479 cmsUInt32Number n = core->Lut->OutputChannels;
480 _cmsAlarmCodesChunkType* ContextAlarmCodes = (_cmsAlarmCodesChunkType*) _cmsContextGetClientChunk(ContextID, AlarmCodesContext);
481
482 for (i=0; i < n; i++) {
483
484 wOut[i] = ContextAlarmCodes ->AlarmCodes[i];
485 }
486 }
487 else
488 core->Lut->Eval16Fn(ContextID, wIn, wOut, core->Lut->Data);
489}
490
491// Gamut check, No cache, 16 bits.
492#define FUNCTION_NAME PrecalculatedXFORMGamutCheck
493#define GAMUTCHECK
494#include "extra_xform.h"
495
496// No gamut check, Cache, 16 bits,
497#define FUNCTION_NAME CachedXFORM
498#define CACHED
499#include "extra_xform.h"
500
501// All those nice features together
502#define FUNCTION_NAME CachedXFORMGamutCheck
503#define CACHED
504#define GAMUTCHECK
505#include "extra_xform.h"
506
507// No gamut check, Cache, 16 bits, <= 4 bytes
508#define FUNCTION_NAME CachedXFORM4
509#define CACHED
510#define INBYTES 4
511#define EXTRABYTES 0
512#include "extra_xform.h"
513
514// No gamut check, Cache, 16 bits, <= 8 bytes total
515#define FUNCTION_NAME CachedXFORM8
516#define CACHED
517#define INBYTES 8
518#define EXTRABYTES 0
519#include "extra_xform.h"
520
521// Special ones for common cases.
522#define FUNCTION_NAME CachedXFORM1to1
523#define CACHED
524#define INBYTES 2
525#define EXTRABYTES 0
526#define UNPACK(CTX,T,D,S,Z) \
527do { \
528 (D)[0] = FROM_8_TO_16(*(S)); (S)++; \
529} while (0)
530#define PACK(CTX,T,S,D,Z) \
531do { \
532 *(D)++ = FROM_16_TO_8((S)[0]); \
533} while (0)
534#include "extra_xform.h"
535
536#define FUNCTION_NAME CachedXFORM1x2to1x2
537#define CACHED
538#define INBYTES 2
539#define EXTRABYTES 0
540#define UNPACK(CTX,T,D,S,Z) \
541do { \
542 (D)[0] = *(cmsUInt16Number *)(S); (S) += 2; \
543} while (0)
544#define PACK(CTX,T,S,D,Z) \
545do { \
546 *(cmsUInt16Number *)(D) = (S)[0]; (D) += 2; \
547} while (0)
548#include "extra_xform.h"
549
550#define FUNCTION_NAME CachedXFORM1to3
551#define CACHED
552#define INBYTES 2
553#define EXTRABYTES 0
554#define UNPACK(CTX,T,D,S,Z) \
555do { \
556 (D)[0] = FROM_8_TO_16(*(S)); (S)++; \
557} while (0)
558#define PACK(CTX,T,S,D,Z) \
559do { \
560 *(D)++ = FROM_16_TO_8((S)[0]); \
561 *(D)++ = FROM_16_TO_8((S)[1]); \
562 *(D)++ = FROM_16_TO_8((S)[2]); \
563} while (0)
564#include "extra_xform.h"
565
566#define FUNCTION_NAME CachedXFORM1x2to3x2
567#define CACHED
568#define INBYTES 2
569#define EXTRABYTES 0
570#define UNPACK(CTX,T,D,S,Z) \
571do { \
572 (D)[0] = *(cmsUInt16Number *)(S); (S) += 2; \
573} while (0)
574#define PACK(CTX,T,S,D,Z) \
575do { \
576 *(cmsUInt16Number *)(D) = (S)[0]; (D) += 2; \
577 *(cmsUInt16Number *)(D) = (S)[1]; (D) += 2; \
578 *(cmsUInt16Number *)(D) = (S)[2]; (D) += 2; \
579} while (0)
580#include "extra_xform.h"
581
582#define FUNCTION_NAME CachedXFORM1to4
583#define CACHED
584#define INBYTES 2
585#define EXTRABYTES 0
586#define UNPACK(CTX,T,D,S,Z) \
587do { \
588 (D)[0] = FROM_8_TO_16(*(S)); (S)++; \
589} while (0)
590#define PACK(CTX,T,S,D,Z) \
591do { \
592 *(D)++ = FROM_16_TO_8((S)[0]); \
593 *(D)++ = FROM_16_TO_8((S)[1]); \
594 *(D)++ = FROM_16_TO_8((S)[2]); \
595 *(D)++ = FROM_16_TO_8((S)[3]); \
596} while (0)
597#include "extra_xform.h"
598
599#define FUNCTION_NAME CachedXFORM1x2to4x2
600#define CACHED
601#define INBYTES 2
602#define EXTRABYTES 0
603#define UNPACK(CTX,T,D,S,Z) \
604do { \
605 (D)[0] = *(cmsUInt16Number *)(S); (S) += 2; \
606} while (0)
607#define PACK(CTX,T,S,D,Z) \
608do { \
609 *(cmsUInt16Number *)(D) = (S)[0]; (D) += 2; \
610 *(cmsUInt16Number *)(D) = (S)[1]; (D) += 2; \
611 *(cmsUInt16Number *)(D) = (S)[2]; (D) += 2; \
612 *(cmsUInt16Number *)(D) = (S)[3]; (D) += 2; \
613} while (0)
614#include "extra_xform.h"
615
616#define FUNCTION_NAME CachedXFORM3to1
617#define CACHED
618#define INBYTES 6
619#define EXTRABYTES 0
620#define UNPACK(CTX,T,D,S,Z) \
621do { \
622 (D)[0] = FROM_8_TO_16(*(S)); (S)++; \
623 (D)[1] = FROM_8_TO_16(*(S)); (S)++; \
624 (D)[2] = FROM_8_TO_16(*(S)); (S)++; \
625} while (0)
626#define PACK(CTX,T,S,D,Z) \
627do { \
628 *(D)++ = FROM_16_TO_8((S)[0]); \
629} while (0)
630#include "extra_xform.h"
631
632#define FUNCTION_NAME CachedXFORM3x2to1x2
633#define CACHED
634#define INBYTES 6
635#define EXTRABYTES 0
636#define UNPACK(CTX,T,D,S,Z) \
637do { \
638 (D)[0] = *(cmsUInt16Number *)(S); (S) += 2; \
639 (D)[1] = *(cmsUInt16Number *)(S); (S) += 2; \
640 (D)[2] = *(cmsUInt16Number *)(S); (S) += 2; \
641} while (0)
642#define PACK(CTX,T,S,D,Z) \
643do { \
644 *(cmsUInt16Number *)(D) = (S)[0]; (D) += 2; \
645} while (0)
646#include "extra_xform.h"
647
648#define FUNCTION_NAME CachedXFORM3to3
649#define CACHED
650#define INBYTES 6
651#define EXTRABYTES 0
652#define UNPACK(CTX,T,D,S,Z) \
653do { \
654 (D)[0] = FROM_8_TO_16(*(S)); (S)++; \
655 (D)[1] = FROM_8_TO_16(*(S)); (S)++; \
656 (D)[2] = FROM_8_TO_16(*(S)); (S)++; \
657} while (0)
658#define PACK(CTX,T,S,D,Z) \
659do { \
660 *(D)++ = FROM_16_TO_8((S)[0]); \
661 *(D)++ = FROM_16_TO_8((S)[1]); \
662 *(D)++ = FROM_16_TO_8((S)[2]); \
663} while (0)
664#include "extra_xform.h"
665
666#define FUNCTION_NAME CachedXFORM3x2to3x2
667#define CACHED
668#define INBYTES 6
669#define EXTRABYTES 0
670#define UNPACK(CTX,T,D,S,Z) \
671do { \
672 (D)[0] = *(cmsUInt16Number *)(S); (S) += 2; \
673 (D)[1] = *(cmsUInt16Number *)(S); (S) += 2; \
674 (D)[2] = *(cmsUInt16Number *)(S); (S) += 2; \
675} while (0)
676#define PACK(CTX,T,S,D,Z) \
677do { \
678 *(cmsUInt16Number *)(D) = (S)[0]; (D) += 2; \
679 *(cmsUInt16Number *)(D) = (S)[1]; (D) += 2; \
680 *(cmsUInt16Number *)(D) = (S)[2]; (D) += 2; \
681} while (0)
682#include "extra_xform.h"
683
684#define FUNCTION_NAME CachedXFORM3to4
685#define CACHED
686#define INBYTES 6
687#define EXTRABYTES 0
688#define UNPACK(CTX,T,D,S,Z) \
689do { \
690 (D)[0] = FROM_8_TO_16(*(S)); (S)++; \
691 (D)[1] = FROM_8_TO_16(*(S)); (S)++; \
692 (D)[2] = FROM_8_TO_16(*(S)); (S)++; \
693} while (0)
694#define PACK(CTX,T,S,D,Z) \
695do { \
696 *(D)++ = FROM_16_TO_8((S)[0]); \
697 *(D)++ = FROM_16_TO_8((S)[1]); \
698 *(D)++ = FROM_16_TO_8((S)[2]); \
699 *(D)++ = FROM_16_TO_8((S)[3]); \
700} while (0)
701#include "extra_xform.h"
702
703#define FUNCTION_NAME CachedXFORM3x2to4x2
704#define CACHED
705#define INBYTES 6
706#define EXTRABYTES 0
707#define UNPACK(CTX,T,D,S,Z) \
708do { \
709 (D)[0] = *(cmsUInt16Number *)(S); (S) += 2; \
710 (D)[1] = *(cmsUInt16Number *)(S); (S) += 2; \
711 (D)[2] = *(cmsUInt16Number *)(S); (S) += 2; \
712} while (0)
713#define PACK(CTX,T,S,D,Z) \
714do { \
715 *(cmsUInt16Number *)(D) = (S)[0]; (D) += 2; \
716 *(cmsUInt16Number *)(D) = (S)[1]; (D) += 2; \
717 *(cmsUInt16Number *)(D) = (S)[2]; (D) += 2; \
718 *(cmsUInt16Number *)(D) = (S)[3]; (D) += 2; \
719} while (0)
720#include "extra_xform.h"
721
722#define FUNCTION_NAME CachedXFORM4to1
723#define CACHED
724#define INBYTES 8
725#define EXTRABYTES 0
726#define UNPACK(CTX,T,D,S,Z) \
727do { \
728 (D)[0] = FROM_8_TO_16(*(S)); (S)++; \
729 (D)[1] = FROM_8_TO_16(*(S)); (S)++; \
730 (D)[2] = FROM_8_TO_16(*(S)); (S)++; \
731 (D)[3] = FROM_8_TO_16(*(S)); (S)++; \
732} while (0)
733#define PACK(CTX,T,S,D,Z) \
734do { \
735 *(D)++ = FROM_16_TO_8((S)[0]); \
736} while (0)
737#include "extra_xform.h"
738
739#define FUNCTION_NAME CachedXFORM4x2to1x2
740#define CACHED
741#define INBYTES 8
742#define EXTRABYTES 0
743#define UNPACK(CTX,T,D,S,Z) \
744do { \
745 (D)[0] = *(cmsUInt16Number *)(S); (S) += 2; \
746 (D)[1] = *(cmsUInt16Number *)(S); (S) += 2; \
747 (D)[2] = *(cmsUInt16Number *)(S); (S) += 2; \
748 (D)[3] = *(cmsUInt16Number *)(S); (S) += 2; \
749} while (0)
750#define PACK(CTX,T,S,D,Z) \
751do { \
752 *(cmsUInt16Number *)(D) = (S)[0]; (D) += 2; \
753} while (0)
754#include "extra_xform.h"
755
756#define FUNCTION_NAME CachedXFORM4to3
757#define CACHED
758#define INBYTES 8
759#define EXTRABYTES 0
760#define UNPACK(CTX,T,D,S,Z) \
761do { \
762 (D)[0] = FROM_8_TO_16(*(S)); (S)++; \
763 (D)[1] = FROM_8_TO_16(*(S)); (S)++; \
764 (D)[2] = FROM_8_TO_16(*(S)); (S)++; \
765 (D)[3] = FROM_8_TO_16(*(S)); (S)++; \
766} while (0)
767#define PACK(CTX,T,S,D,Z) \
768do { \
769 *(D)++ = FROM_16_TO_8((S)[0]); \
770 *(D)++ = FROM_16_TO_8((S)[1]); \
771 *(D)++ = FROM_16_TO_8((S)[2]); \
772} while (0)
773#include "extra_xform.h"
774
775#define FUNCTION_NAME CachedXFORM4x2to3x2
776#define CACHED
777#define INBYTES 8
778#define EXTRABYTES 0
779#define UNPACK(CTX,T,D,S,Z) \
780do { \
781 (D)[0] = *(cmsUInt16Number *)(S); (S) += 2; \
782 (D)[1] = *(cmsUInt16Number *)(S); (S) += 2; \
783 (D)[2] = *(cmsUInt16Number *)(S); (S) += 2; \
784 (D)[3] = *(cmsUInt16Number *)(S); (S) += 2; \
785} while (0)
786#define PACK(CTX,T,S,D,Z) \
787do { \
788 *(cmsUInt16Number *)(D) = (S)[0]; (D) += 2; \
789 *(cmsUInt16Number *)(D) = (S)[1]; (D) += 2; \
790 *(cmsUInt16Number *)(D) = (S)[2]; (D) += 2; \
791} while (0)
792#include "extra_xform.h"
793
794#define FUNCTION_NAME CachedXFORM4to4
795#define CACHED
796#define INBYTES 8
797#define EXTRABYTES 0
798#define UNPACK(CTX,T,D,S,Z) \
799do { \
800 (D)[0] = FROM_8_TO_16(*(S)); (S)++; \
801 (D)[1] = FROM_8_TO_16(*(S)); (S)++; \
802 (D)[2] = FROM_8_TO_16(*(S)); (S)++; \
803 (D)[3] = FROM_8_TO_16(*(S)); (S)++; \
804} while (0)
805#define PACK(CTX,T,S,D,Z) \
806do { \
807 *(D)++ = FROM_16_TO_8((S)[0]); \
808 *(D)++ = FROM_16_TO_8((S)[1]); \
809 *(D)++ = FROM_16_TO_8((S)[2]); \
810 *(D)++ = FROM_16_TO_8((S)[3]); \
811} while (0)
812#include "extra_xform.h"
813
814#define FUNCTION_NAME CachedXFORM4x2to4x2
815#define CACHED
816#define INBYTES 8
817#define EXTRABYTES 0
818#define UNPACK(CTX,T,D,S,Z) \
819do { \
820 (D)[0] = *(cmsUInt16Number *)(S); (S) += 2; \
821 (D)[1] = *(cmsUInt16Number *)(S); (S) += 2; \
822 (D)[2] = *(cmsUInt16Number *)(S); (S) += 2; \
823 (D)[3] = *(cmsUInt16Number *)(S); (S) += 2; \
824} while (0)
825#define PACK(CTX,T,S,D,Z) \
826do { \
827 *(cmsUInt16Number *)(D) = (S)[0]; (D) += 2; \
828 *(cmsUInt16Number *)(D) = (S)[1]; (D) += 2; \
829 *(cmsUInt16Number *)(D) = (S)[2]; (D) += 2; \
830 *(cmsUInt16Number *)(D) = (S)[3]; (D) += 2; \
831} while (0)
832#include "extra_xform.h"
833
834// Transform plug-ins ----------------------------------------------------------------------------------------------------
835
836// List of used-defined transform factories
837typedef struct _cmsTransformCollection_st {
838
839 _cmsTransform2Factory Factory;
840 cmsBool OldXform; // Factory returns xform function in the old style
841
842 struct _cmsTransformCollection_st *Next;
843
844} _cmsTransformCollection;
845
846// The linked list head
847_cmsTransformPluginChunkType _cmsTransformPluginChunk = { NULL };
848
849
850// Duplicates the zone of memory used by the plug-in in the new context
851static
852void DupPluginTransformList(struct _cmsContext_struct* ctx,
853 const struct _cmsContext_struct* src)
854{
855 _cmsTransformPluginChunkType newHead = { NULL };
856 _cmsTransformCollection* entry;
857 _cmsTransformCollection* Anterior = NULL;
858 _cmsTransformPluginChunkType* head = (_cmsTransformPluginChunkType*) src->chunks[TransformPlugin];
859
860 // Walk the list copying all nodes
861 for (entry = head->TransformCollection;
862 entry != NULL;
863 entry = entry ->Next) {
864
865 _cmsTransformCollection *newEntry = ( _cmsTransformCollection *) _cmsSubAllocDup(ctx ->MemPool, entry, sizeof(_cmsTransformCollection));
866
867 if (newEntry == NULL)
868 return;
869
870 // We want to keep the linked list order, so this is a little bit tricky
871 newEntry -> Next = NULL;
872 if (Anterior)
873 Anterior -> Next = newEntry;
874
875 Anterior = newEntry;
876
877 if (newHead.TransformCollection == NULL)
878 newHead.TransformCollection = newEntry;
879 }
880
881 ctx ->chunks[TransformPlugin] = _cmsSubAllocDup(ctx->MemPool, &newHead, sizeof(_cmsTransformPluginChunkType));
882}
883
884// Allocates memory for transform plugin factory
885void _cmsAllocTransformPluginChunk(struct _cmsContext_struct* ctx,
886 const struct _cmsContext_struct* src)
887{
888 if (src != NULL) {
889
890 // Copy all linked list
891 DupPluginTransformList(ctx, src);
892 }
893 else {
894 static _cmsTransformPluginChunkType TransformPluginChunkType = { NULL };
895 ctx ->chunks[TransformPlugin] = _cmsSubAllocDup(ctx ->MemPool, &TransformPluginChunkType, sizeof(_cmsTransformPluginChunkType));
896 }
897}
898
899// Adaptor for old versions of plug-in
900static
901void _cmsTransform2toTransformAdaptor(cmsContext ContextID, struct _cmstransform_struct *CMMcargo,
902 const void* InputBuffer,
903 void* OutputBuffer,
904 cmsUInt32Number PixelsPerLine,
905 cmsUInt32Number LineCount,
906 const cmsStride* Stride)
907{
908
909 cmsUInt32Number i, strideIn, strideOut;
910
911 _cmsHandleExtraChannels(ContextID, CMMcargo, InputBuffer, OutputBuffer, PixelsPerLine, LineCount, Stride);
912
913 strideIn = 0;
914 strideOut = 0;
915
916 for (i = 0; i < LineCount; i++) {
917
918 void *accum = (cmsUInt8Number*)InputBuffer + strideIn;
919 void *output = (cmsUInt8Number*)OutputBuffer + strideOut;
920
921 CMMcargo->OldXform(ContextID, CMMcargo, accum, output, PixelsPerLine, Stride->BytesPerPlaneIn);
922
923 strideIn += Stride->BytesPerLineIn;
924 strideOut += Stride->BytesPerLineOut;
925 }
926}
927
928
929
930// Register new ways to transform
931cmsBool _cmsRegisterTransformPlugin(cmsContext ContextID, cmsPluginBase* Data)
932{
933 cmsPluginTransform* Plugin = (cmsPluginTransform*) Data;
934 _cmsTransformCollection* fl;
935 _cmsTransformPluginChunkType* ctx = ( _cmsTransformPluginChunkType*) _cmsContextGetClientChunk(ContextID,TransformPlugin);
936
937 if (Data == NULL) {
938
939 // Free the chain. Memory is safely freed at exit
940 ctx->TransformCollection = NULL;
941 return TRUE;
942 }
943
944 // Factory callback is required
945 if (Plugin->factories.xform == NULL) return FALSE;
946
947
948 fl = (_cmsTransformCollection*) _cmsPluginMalloc(ContextID, sizeof(_cmsTransformCollection));
949 if (fl == NULL) return FALSE;
950
951 // Check for full xform plug-ins previous to 2.8, we would need an adapter in that case
952 if (Plugin->base.ExpectedVersion < 2080) {
953
954 fl->OldXform = TRUE;
955 }
956 else
957 fl->OldXform = FALSE;
958
959 // Copy the parameters
960 fl->Factory = Plugin->factories.xform;
961
962 // Keep linked list
963 fl ->Next = ctx->TransformCollection;
964 ctx->TransformCollection = fl;
965
966 // All is ok
967 return TRUE;
968}
969
970
971void CMSEXPORT _cmsSetTransformUserData(struct _cmstransform_struct *CMMcargo, void* ptr, _cmsFreeUserDataFn FreePrivateDataFn)
972{
973 _cmsAssert(CMMcargo != NULL && CMMcargo->core != NULL);
974 CMMcargo->core->UserData = ptr;
975 CMMcargo->core->FreeUserData = FreePrivateDataFn;
976}
977
978// returns the pointer defined by the plug-in to store private data
979void * CMSEXPORT _cmsGetTransformUserData(struct _cmstransform_struct *CMMcargo)
980{
981 _cmsAssert(CMMcargo != NULL && CMMcargo->core != NULL);
982 return CMMcargo->core->UserData;
983}
984
985// returns the current formatters
986void CMSEXPORT _cmsGetTransformFormatters16(struct _cmstransform_struct *CMMcargo, cmsFormatter16* FromInput, cmsFormatter16* ToOutput)
987{
988 _cmsAssert(CMMcargo != NULL);
989 if (FromInput) *FromInput = CMMcargo ->FromInput;
990 if (ToOutput) *ToOutput = CMMcargo ->ToOutput;
991}
992
993void CMSEXPORT _cmsGetTransformFormattersFloat(struct _cmstransform_struct *CMMcargo, cmsFormatterFloat* FromInput, cmsFormatterFloat* ToOutput)
994{
995 _cmsAssert(CMMcargo != NULL);
996 if (FromInput) *FromInput = CMMcargo ->FromInputFloat;
997 if (ToOutput) *ToOutput = CMMcargo ->ToOutputFloat;
998}
999
1000
1001void
1002_cmsFindFormatter(_cmsTRANSFORM* p, cmsUInt32Number InputFormat, cmsUInt32Number OutputFormat, cmsUInt32Number dwFlags)
1003{
1004 if (dwFlags & cmsFLAGS_NULLTRANSFORM) {
1005 p ->xform = NullXFORM;
1006 return;
1007 }
1008 if (dwFlags & cmsFLAGS_NOCACHE) {
1009 if (dwFlags & cmsFLAGS_GAMUTCHECK)
1010 p ->xform = PrecalculatedXFORMGamutCheck; // Gamut check, no cache
1011 else if ((InputFormat & ~COLORSPACE_SH(31)) == (OutputFormat & ~COLORSPACE_SH(31)) &&
1012 _cmsLutIsIdentity(p->core->Lut)) {
1013 if (T_PLANAR(InputFormat))
1014 p ->xform = PrecalculatedXFORMIdentityPlanar;
1015 else
1016 p ->xform = PrecalculatedXFORMIdentity;
1017 } else
1018 p ->xform = PrecalculatedXFORM; // No cache, no gamut check
1019 return;
1020 }
1021 if (dwFlags & cmsFLAGS_GAMUTCHECK) {
1022 p ->xform = CachedXFORMGamutCheck; // Gamut check, cache
1023 return;
1024 }
1025 if ((InputFormat & ~COLORSPACE_SH(31)) == (OutputFormat & ~COLORSPACE_SH(31)) &&
1026 _cmsLutIsIdentity(p->core->Lut)) {
1027 /* No point in a cache here! */
1028 if (T_PLANAR(InputFormat))
1029 p ->xform = PrecalculatedXFORMIdentityPlanar;
1030 else
1031 p ->xform = PrecalculatedXFORMIdentity;
1032 return;
1033 }
1034 if (T_EXTRA(InputFormat) != 0) {
1035 p ->xform = CachedXFORM; // No gamut check, cache
1036 return;
1037 }
1038 if ((InputFormat & ~(COLORSPACE_SH(31)|CHANNELS_SH(7)|BYTES_SH(3))) == 0 &&
1039 (OutputFormat & ~(COLORSPACE_SH(31)|CHANNELS_SH(7)|BYTES_SH(3))) == 0) {
1040 switch ((InputFormat & (CHANNELS_SH(7)|BYTES_SH(3)))|
1041 ((OutputFormat & (CHANNELS_SH(7)|BYTES_SH(3)))<<6)) {
1042 case CHANNELS_SH(1) | BYTES_SH(1) | ((CHANNELS_SH(1) | BYTES_SH(1))<<6):
1043 p->xform = CachedXFORM1to1;
1044 return;
1045 case CHANNELS_SH(1) | BYTES_SH(2) | ((CHANNELS_SH(1) | BYTES_SH(2))<<6):
1046 p->xform = CachedXFORM1x2to1x2;
1047 return;
1048 case CHANNELS_SH(1) | BYTES_SH(1) | ((CHANNELS_SH(3) | BYTES_SH(1))<<6):
1049 p->xform = CachedXFORM1to3;
1050 return;
1051 case CHANNELS_SH(1) | BYTES_SH(2) | ((CHANNELS_SH(3) | BYTES_SH(2))<<6):
1052 p->xform = CachedXFORM1x2to3x2;
1053 return;
1054 case CHANNELS_SH(1) | BYTES_SH(1) | ((CHANNELS_SH(4) | BYTES_SH(1))<<6):
1055 p->xform = CachedXFORM1to4;
1056 return;
1057 case CHANNELS_SH(1) | BYTES_SH(2) | ((CHANNELS_SH(4) | BYTES_SH(2))<<6):
1058 p->xform = CachedXFORM1x2to4x2;
1059 return;
1060 case CHANNELS_SH(3) | BYTES_SH(1) | ((CHANNELS_SH(1) | BYTES_SH(1))<<6):
1061 p ->xform = CachedXFORM3to1;
1062 return;
1063 case CHANNELS_SH(3) | BYTES_SH(2) | ((CHANNELS_SH(1) | BYTES_SH(2))<<6):
1064 p ->xform = CachedXFORM3x2to1x2;
1065 return;
1066 case CHANNELS_SH(3) | BYTES_SH(1) | ((CHANNELS_SH(3) | BYTES_SH(1))<<6):
1067 p->xform = CachedXFORM3to3;
1068 return;
1069 case CHANNELS_SH(3) | BYTES_SH(2) | ((CHANNELS_SH(3) | BYTES_SH(2))<<6):
1070 p->xform = CachedXFORM3x2to3x2;
1071 return;
1072 case CHANNELS_SH(3) | BYTES_SH(1) | ((CHANNELS_SH(4) | BYTES_SH(1))<<6):
1073 p->xform = CachedXFORM3to4;
1074 return;
1075 case CHANNELS_SH(3) | BYTES_SH(2) | ((CHANNELS_SH(4) | BYTES_SH(2))<<6):
1076 p->xform = CachedXFORM3x2to4x2;
1077 return;
1078 case CHANNELS_SH(4) | BYTES_SH(1) | ((CHANNELS_SH(1) | BYTES_SH(1))<<6):
1079 p->xform = CachedXFORM4to1;
1080 return;
1081 case CHANNELS_SH(4) | BYTES_SH(2) | ((CHANNELS_SH(1) | BYTES_SH(2))<<6):
1082 p->xform = CachedXFORM4x2to1x2;
1083 return;
1084 case CHANNELS_SH(4) | BYTES_SH(1) | ((CHANNELS_SH(3) | BYTES_SH(1))<<6):
1085 p->xform = CachedXFORM4to3;
1086 return;
1087 case CHANNELS_SH(4) | BYTES_SH(2) | ((CHANNELS_SH(3) | BYTES_SH(2))<<6):
1088 p->xform = CachedXFORM4x2to3x2;
1089 return;
1090 case CHANNELS_SH(4) | BYTES_SH(1) | ((CHANNELS_SH(4) | BYTES_SH(1))<<6):
1091 p->xform = CachedXFORM4to4;
1092 return;
1093 case CHANNELS_SH(4) | BYTES_SH(2) | ((CHANNELS_SH(4) | BYTES_SH(2))<<6):
1094 p->xform = CachedXFORM4x2to4x2;
1095 return;
1096 }
1097 }
1098 {
1099 int inwords = T_CHANNELS(InputFormat);
1100 if (inwords <= 2)
1101 p ->xform = CachedXFORM4;
1102 else if (inwords <= 4)
1103 p ->xform = CachedXFORM8;
1104 else
1105 p ->xform = CachedXFORM; // No gamut check, cache
1106 }
1107}
1108
1109// Allocate transform struct and set it to defaults. Ask the optimization plug-in about if those formats are proper
1110// for separated transforms. If this is the case,
1111static
1112_cmsTRANSFORM* AllocEmptyTransform(cmsContext ContextID, cmsPipeline* lut,
1113 cmsUInt32Number Intent, cmsUInt32Number* InputFormat, cmsUInt32Number* OutputFormat, cmsUInt32Number* dwFlags)
1114{
1115 _cmsTransformPluginChunkType* ctx = ( _cmsTransformPluginChunkType*) _cmsContextGetClientChunk(ContextID, TransformPlugin);
1116 _cmsTransformCollection* Plugin;
1117 _cmsTRANSFORMCORE *core;
1118
1119 // Allocate needed memory
1120 _cmsTRANSFORM* p = (_cmsTRANSFORM*)_cmsMallocZero(ContextID, sizeof(_cmsTRANSFORM));
1121 if (!p) {
1122 cmsPipelineFree(ContextID, lut);
1123 return NULL;
1124 }
1125
1126 core = (_cmsTRANSFORMCORE*)_cmsMallocZero(ContextID, sizeof(*core));
1127 if (!core) {
1128 _cmsFree(ContextID, p);
1129 cmsPipelineFree(ContextID, lut);
1130 return NULL;
1131 }
1132
1133 p->core = core;
1134 core->refs = 1;
1135 // Store the proposed pipeline
1136 p->core->Lut = lut;
1137
1138 // Let's see if any plug-in want to do the transform by itself
1139 if (core->Lut != NULL) {
1140
1141 for (Plugin = ctx->TransformCollection;
1142 Plugin != NULL;
1143 Plugin = Plugin->Next) {
1144
1145 if (Plugin->Factory(ContextID, &p->xform, &core->UserData, &core->FreeUserData, &core->Lut, InputFormat, OutputFormat, dwFlags)) {
1146
1147 // Last plugin in the declaration order takes control. We just keep
1148 // the original parameters as a logging.
1149 // Note that cmsFLAGS_CAN_CHANGE_FORMATTER is not set, so by default
1150 // an optimized transform is not reusable. The plug-in can, however, change
1151 // the flags and make it suitable.
1152
1153 p->InputFormat = *InputFormat;
1154 p->OutputFormat = *OutputFormat;
1155 core->dwOriginalFlags = *dwFlags;
1156
1157 // Fill the formatters just in case the optimized routine is interested.
1158 // No error is thrown if the formatter doesn't exist. It is up to the optimization
1159 // factory to decide what to do in those cases.
1160 p->FromInput = _cmsGetFormatter(ContextID, *InputFormat, cmsFormatterInput, CMS_PACK_FLAGS_16BITS).Fmt16;
1161 p->ToOutput = _cmsGetFormatter(ContextID, *OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_16BITS).Fmt16;
1162 p->FromInputFloat = _cmsGetFormatter(ContextID, *InputFormat, cmsFormatterInput, CMS_PACK_FLAGS_FLOAT).FmtFloat;
1163 p->ToOutputFloat = _cmsGetFormatter(ContextID, *OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_FLOAT).FmtFloat;
1164
1165 // Save the day? (Ignore the warning)
1166 if (Plugin->OldXform) {
1167 p->OldXform = (_cmsTransformFn)(void*) p->xform;
1168 p->xform = _cmsTransform2toTransformAdaptor;
1169 }
1170 return p;
1171 }
1172 }
1173
1174 // Not suitable for the transform plug-in, let's check the pipeline plug-in
1175 _cmsOptimizePipeline(ContextID, &core->Lut, Intent, InputFormat, OutputFormat, dwFlags);
1176 }
1177
1178 // Check whatever this is a true floating point transform
1179 if (_cmsFormatterIsFloat(*InputFormat) && _cmsFormatterIsFloat(*OutputFormat)) {
1180
1181 // Get formatter function always return a valid union, but the contents of this union may be NULL.
1182 p ->FromInputFloat = _cmsGetFormatter(ContextID, *InputFormat, cmsFormatterInput, CMS_PACK_FLAGS_FLOAT).FmtFloat;
1183 p ->ToOutputFloat = _cmsGetFormatter(ContextID, *OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_FLOAT).FmtFloat;
1184 *dwFlags |= cmsFLAGS_CAN_CHANGE_FORMATTER;
1185
1186 if (p ->FromInputFloat == NULL || p ->ToOutputFloat == NULL) {
1187
1188 cmsSignalError(ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported raster format");
1189 cmsDeleteTransform(ContextID, p);
1190 return NULL;
1191 }
1192
1193 if (*dwFlags & cmsFLAGS_NULLTRANSFORM) {
1194
1195 p ->xform = NullFloatXFORM;
1196 }
1197 else {
1198 // Float transforms don't use cache, always are non-NULL
1199 p ->xform = FloatXFORM;
1200 }
1201
1202 }
1203 else {
1204
1205 if (*InputFormat == 0 && *OutputFormat == 0) {
1206 p ->FromInput = p ->ToOutput = NULL;
1207 *dwFlags |= cmsFLAGS_CAN_CHANGE_FORMATTER;
1208 }
1209 else {
1210
1211 cmsUInt32Number BytesPerPixelInput;
1212
1213 p ->FromInput = _cmsGetFormatter(ContextID, *InputFormat, cmsFormatterInput, CMS_PACK_FLAGS_16BITS).Fmt16;
1214 p ->ToOutput = _cmsGetFormatter(ContextID, *OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_16BITS).Fmt16;
1215
1216 if (p ->FromInput == NULL || p ->ToOutput == NULL) {
1217
1218 cmsSignalError(ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported raster format");
1219 cmsDeleteTransform(ContextID, p);
1220 return NULL;
1221 }
1222
1223 BytesPerPixelInput = T_BYTES(p ->InputFormat);
1224 if (BytesPerPixelInput == 0 || BytesPerPixelInput >= 2)
1225 *dwFlags |= cmsFLAGS_CAN_CHANGE_FORMATTER;
1226
1227 }
1228
1229 _cmsFindFormatter(p, *InputFormat, *OutputFormat, *dwFlags);
1230 }
1231
1232 p ->InputFormat = *InputFormat;
1233 p ->OutputFormat = *OutputFormat;
1234 core->dwOriginalFlags = *dwFlags;
1235 core->UserData = NULL;
1236 return p;
1237}
1238
1239static
1240cmsBool GetXFormColorSpaces(cmsContext ContextID, cmsUInt32Number nProfiles, cmsHPROFILE hProfiles[], cmsColorSpaceSignature* Input, cmsColorSpaceSignature* Output)
1241{
1242 cmsColorSpaceSignature ColorSpaceIn, ColorSpaceOut;
1243 cmsColorSpaceSignature PostColorSpace;
1244 cmsUInt32Number i;
1245
1246 if (nProfiles == 0) return FALSE;
1247 if (hProfiles[0] == NULL) return FALSE;
1248
1249 *Input = PostColorSpace = cmsGetColorSpace(ContextID, hProfiles[0]);
1250
1251 for (i=0; i < nProfiles; i++) {
1252
1253 cmsProfileClassSignature cls;
1254 cmsHPROFILE hProfile = hProfiles[i];
1255
1256 int lIsInput = (PostColorSpace != cmsSigXYZData) &&
1257 (PostColorSpace != cmsSigLabData);
1258
1259 if (hProfile == NULL) return FALSE;
1260
1261 cls = cmsGetDeviceClass(ContextID, hProfile);
1262
1263 if (cls == cmsSigNamedColorClass) {
1264
1265 ColorSpaceIn = cmsSig1colorData;
1266 ColorSpaceOut = (nProfiles > 1) ? cmsGetPCS(ContextID, hProfile) : cmsGetColorSpace(ContextID, hProfile);
1267 }
1268 else
1269 if (lIsInput || (cls == cmsSigLinkClass)) {
1270
1271 ColorSpaceIn = cmsGetColorSpace(ContextID, hProfile);
1272 ColorSpaceOut = cmsGetPCS(ContextID, hProfile);
1273 }
1274 else
1275 {
1276 ColorSpaceIn = cmsGetPCS(ContextID, hProfile);
1277 ColorSpaceOut = cmsGetColorSpace(ContextID, hProfile);
1278 }
1279
1280 if (i==0)
1281 *Input = ColorSpaceIn;
1282
1283 PostColorSpace = ColorSpaceOut;
1284 }
1285
1286 *Output = PostColorSpace;
1287
1288 return TRUE;
1289}
1290
1291// Check colorspace
1292static
1293cmsBool IsProperColorSpace(cmsContext ContextID, cmsColorSpaceSignature Check, cmsUInt32Number dwFormat)
1294{
1295 int Space1 = (int) T_COLORSPACE(dwFormat);
1296 int Space2 = _cmsLCMScolorSpace(ContextID, Check);
1297
1298 if (Space1 == PT_ANY) return TRUE;
1299 if (Space1 == Space2) return TRUE;
1300
1301 if (Space1 == PT_LabV2 && Space2 == PT_Lab) return TRUE;
1302 if (Space1 == PT_Lab && Space2 == PT_LabV2) return TRUE;
1303
1304 return FALSE;
1305}
1306
1307// ----------------------------------------------------------------------------------------------------------------
1308
1309// Jun-21-2000: Some profiles (those that comes with W2K) comes
1310// with the media white (media black?) x 100. Add a sanity check
1311
1312static
1313void NormalizeXYZ(cmsCIEXYZ* Dest)
1314{
1315 while (Dest -> X > 2. &&
1316 Dest -> Y > 2. &&
1317 Dest -> Z > 2.) {
1318
1319 Dest -> X /= 10.;
1320 Dest -> Y /= 10.;
1321 Dest -> Z /= 10.;
1322 }
1323}
1324
1325static
1326void SetWhitePoint(cmsCIEXYZ* wtPt, const cmsCIEXYZ* src)
1327{
1328 if (src == NULL) {
1329 wtPt ->X = cmsD50X;
1330 wtPt ->Y = cmsD50Y;
1331 wtPt ->Z = cmsD50Z;
1332 }
1333 else {
1334 wtPt ->X = src->X;
1335 wtPt ->Y = src->Y;
1336 wtPt ->Z = src->Z;
1337
1338 NormalizeXYZ(wtPt);
1339 }
1340
1341}
1342
1343// New to lcms 2.0 -- have all parameters available.
1344cmsHTRANSFORM CMSEXPORT cmsCreateExtendedTransform(cmsContext ContextID,
1345 cmsUInt32Number nProfiles, cmsHPROFILE hProfiles[],
1346 cmsBool BPC[],
1347 cmsUInt32Number Intents[],
1348 cmsFloat64Number AdaptationStates[],
1349 cmsHPROFILE hGamutProfile,
1350 cmsUInt32Number nGamutPCSposition,
1351 cmsUInt32Number InputFormat,
1352 cmsUInt32Number OutputFormat,
1353 cmsUInt32Number dwFlags)
1354{
1355 _cmsTRANSFORM* xform;
1356 cmsColorSpaceSignature EntryColorSpace;
1357 cmsColorSpaceSignature ExitColorSpace;
1358 cmsPipeline* Lut;
1359 cmsUInt32Number LastIntent = Intents[nProfiles-1];
1360
1361 // If it is a fake transform
1362 if (dwFlags & cmsFLAGS_NULLTRANSFORM)
1363 {
1364 return AllocEmptyTransform(ContextID, NULL, INTENT_PERCEPTUAL, &InputFormat, &OutputFormat, &dwFlags);
1365 }
1366
1367 // If gamut check is requested, make sure we have a gamut profile
1368 if (dwFlags & cmsFLAGS_GAMUTCHECK) {
1369 if (hGamutProfile == NULL) dwFlags &= ~cmsFLAGS_GAMUTCHECK;
1370 }
1371
1372 // On floating point transforms, inhibit cache
1373 if (_cmsFormatterIsFloat(InputFormat) || _cmsFormatterIsFloat(OutputFormat))
1374 dwFlags |= cmsFLAGS_NOCACHE;
1375
1376 // Mark entry/exit spaces
1377 if (!GetXFormColorSpaces(ContextID, nProfiles, hProfiles, &EntryColorSpace, &ExitColorSpace)) {
1378 cmsSignalError(ContextID, cmsERROR_NULL, "NULL input profiles on transform");
1379 return NULL;
1380 }
1381
1382 // Check if proper colorspaces
1383 if (!IsProperColorSpace(ContextID, EntryColorSpace, InputFormat)) {
1384 cmsSignalError(ContextID, cmsERROR_COLORSPACE_CHECK, "Wrong input color space on transform");
1385 return NULL;
1386 }
1387
1388 if (!IsProperColorSpace(ContextID, ExitColorSpace, OutputFormat)) {
1389 cmsSignalError(ContextID, cmsERROR_COLORSPACE_CHECK, "Wrong output color space on transform");
1390 return NULL;
1391 }
1392
1393 // Create a pipeline with all transformations
1394 Lut = _cmsLinkProfiles(ContextID, nProfiles, Intents, hProfiles, BPC, AdaptationStates, dwFlags);
1395 if (Lut == NULL) {
1396 cmsSignalError(ContextID, cmsERROR_NOT_SUITABLE, "Couldn't link the profiles");
1397 return NULL;
1398 }
1399
1400 // Check channel count
1401 if ((cmsChannelsOf(ContextID, EntryColorSpace) != cmsPipelineInputChannels(ContextID, Lut)) ||
1402 (cmsChannelsOf(ContextID, ExitColorSpace) != cmsPipelineOutputChannels(ContextID, Lut))) {
1403 cmsPipelineFree(ContextID, Lut);
1404 cmsSignalError(ContextID, cmsERROR_NOT_SUITABLE, "Channel count doesn't match. Profile is corrupted");
1405 return NULL;
1406 }
1407
1408
1409 // All seems ok
1410 xform = AllocEmptyTransform(ContextID, Lut, LastIntent, &InputFormat, &OutputFormat, &dwFlags);
1411 if (xform == NULL) {
1412 return NULL;
1413 }
1414
1415 // Keep values
1416 xform->core->EntryColorSpace = EntryColorSpace;
1417 xform->core->ExitColorSpace = ExitColorSpace;
1418 xform->core->RenderingIntent = Intents[nProfiles-1];
1419
1420 // Take white points
1421 SetWhitePoint(&xform->core->EntryWhitePoint, (cmsCIEXYZ*) cmsReadTag(ContextID, hProfiles[0], cmsSigMediaWhitePointTag));
1422 SetWhitePoint(&xform->core->ExitWhitePoint, (cmsCIEXYZ*) cmsReadTag(ContextID, hProfiles[nProfiles-1], cmsSigMediaWhitePointTag));
1423
1424
1425 // Create a gamut check LUT if requested
1426 if (hGamutProfile != NULL && (dwFlags & cmsFLAGS_GAMUTCHECK))
1427 xform->core->GamutCheck = _cmsCreateGamutCheckPipeline(ContextID, hProfiles,
1428 BPC, Intents,
1429 AdaptationStates,
1430 nGamutPCSposition,
1431 hGamutProfile);
1432
1433
1434 // Try to read input and output colorant table
1435 if (cmsIsTag(ContextID, hProfiles[0], cmsSigColorantTableTag)) {
1436
1437 // Input table can only come in this way.
1438 xform->core->InputColorant = cmsDupNamedColorList(ContextID, (cmsNAMEDCOLORLIST*) cmsReadTag(ContextID, hProfiles[0], cmsSigColorantTableTag));
1439 }
1440
1441 // Output is a little bit more complex.
1442 if (cmsGetDeviceClass(ContextID, hProfiles[nProfiles-1]) == cmsSigLinkClass) {
1443
1444 // This tag may exist only on devicelink profiles.
1445 if (cmsIsTag(ContextID, hProfiles[nProfiles-1], cmsSigColorantTableOutTag)) {
1446
1447 // It may be NULL if error
1448 xform->core->OutputColorant = cmsDupNamedColorList(ContextID, (cmsNAMEDCOLORLIST*) cmsReadTag(ContextID, hProfiles[nProfiles-1], cmsSigColorantTableOutTag));
1449 }
1450
1451 } else {
1452
1453 if (cmsIsTag(ContextID, hProfiles[nProfiles-1], cmsSigColorantTableTag)) {
1454
1455 xform->core->OutputColorant = cmsDupNamedColorList(ContextID, (cmsNAMEDCOLORLIST*) cmsReadTag(ContextID, hProfiles[nProfiles-1], cmsSigColorantTableTag));
1456 }
1457 }
1458
1459 // Store the sequence of profiles
1460 if (dwFlags & cmsFLAGS_KEEP_SEQUENCE) {
1461 xform->core->Sequence = _cmsCompileProfileSequence(ContextID, nProfiles, hProfiles);
1462 }
1463 else
1464 xform->core->Sequence = NULL;
1465
1466 // If this is a cached transform, init first value, which is zero (16 bits only)
1467 if (!(dwFlags & cmsFLAGS_NOCACHE)) {
1468
1469 memset(&xform ->Cache.CacheIn, 0, sizeof(xform ->Cache.CacheIn));
1470
1471 if (xform->core->GamutCheck != NULL) {
1472 TransformOnePixelWithGamutCheck(ContextID, xform, xform->Cache.CacheIn, xform->Cache.CacheOut);
1473 }
1474 else {
1475
1476 xform->core->Lut->Eval16Fn(ContextID, xform ->Cache.CacheIn, xform->Cache.CacheOut, xform->core->Lut->Data);
1477 }
1478
1479 }
1480
1481 return (cmsHTRANSFORM) xform;
1482}
1483
1484// Multiprofile transforms: Gamut check is not available here, as it is unclear from which profile the gamut comes.
1485cmsHTRANSFORM CMSEXPORT cmsCreateMultiprofileTransform(cmsContext ContextID,
1486 cmsHPROFILE hProfiles[],
1487 cmsUInt32Number nProfiles,
1488 cmsUInt32Number InputFormat,
1489 cmsUInt32Number OutputFormat,
1490 cmsUInt32Number Intent,
1491 cmsUInt32Number dwFlags)
1492{
1493 cmsUInt32Number i;
1494 cmsBool BPC[256];
1495 cmsUInt32Number Intents[256];
1496 cmsFloat64Number AdaptationStates[256];
1497
1498 if (nProfiles <= 0 || nProfiles > 255) {
1499 cmsSignalError(ContextID, cmsERROR_RANGE, "Wrong number of profiles. 1..255 expected, %d found.", nProfiles);
1500 return NULL;
1501 }
1502
1503 for (i=0; i < nProfiles; i++) {
1504 BPC[i] = dwFlags & cmsFLAGS_BLACKPOINTCOMPENSATION ? TRUE : FALSE;
1505 Intents[i] = Intent;
1506 AdaptationStates[i] = cmsSetAdaptationState(ContextID, -1);
1507 }
1508
1509
1510 return cmsCreateExtendedTransform(ContextID, nProfiles, hProfiles, BPC, Intents, AdaptationStates, NULL, 0, InputFormat, OutputFormat, dwFlags);
1511}
1512
1513
1514
1515cmsHTRANSFORM CMSEXPORT cmsCreateTransform(cmsContext ContextID,
1516 cmsHPROFILE Input,
1517 cmsUInt32Number InputFormat,
1518 cmsHPROFILE Output,
1519 cmsUInt32Number OutputFormat,
1520 cmsUInt32Number Intent,
1521 cmsUInt32Number dwFlags)
1522{
1523
1524 cmsHPROFILE hArray[2];
1525
1526 hArray[0] = Input;
1527 hArray[1] = Output;
1528
1529 return cmsCreateMultiprofileTransform(ContextID, hArray, Output == NULL ? 1U : 2U, InputFormat, OutputFormat, Intent, dwFlags);
1530}
1531
1532
1533cmsHTRANSFORM CMSEXPORT cmsCreateProofingTransform(cmsContext ContextID,
1534 cmsHPROFILE InputProfile,
1535 cmsUInt32Number InputFormat,
1536 cmsHPROFILE OutputProfile,
1537 cmsUInt32Number OutputFormat,
1538 cmsHPROFILE ProofingProfile,
1539 cmsUInt32Number nIntent,
1540 cmsUInt32Number ProofingIntent,
1541 cmsUInt32Number dwFlags)
1542{
1543 cmsHPROFILE hArray[4];
1544 cmsUInt32Number Intents[4];
1545 cmsBool BPC[4];
1546 cmsFloat64Number Adaptation[4];
1547 cmsBool DoBPC = (dwFlags & cmsFLAGS_BLACKPOINTCOMPENSATION) ? TRUE : FALSE;
1548
1549
1550 hArray[0] = InputProfile; hArray[1] = ProofingProfile; hArray[2] = ProofingProfile; hArray[3] = OutputProfile;
1551 Intents[0] = nIntent; Intents[1] = nIntent; Intents[2] = INTENT_RELATIVE_COLORIMETRIC; Intents[3] = ProofingIntent;
1552 BPC[0] = DoBPC; BPC[1] = DoBPC; BPC[2] = 0; BPC[3] = 0;
1553
1554 Adaptation[0] = Adaptation[1] = Adaptation[2] = Adaptation[3] = cmsSetAdaptationState(ContextID, -1);
1555
1556 if (!(dwFlags & (cmsFLAGS_SOFTPROOFING|cmsFLAGS_GAMUTCHECK)))
1557 return cmsCreateTransform(ContextID, InputProfile, InputFormat, OutputProfile, OutputFormat, nIntent, dwFlags);
1558
1559 return cmsCreateExtendedTransform(ContextID, 4, hArray, BPC, Intents, Adaptation,
1560 ProofingProfile, 1, InputFormat, OutputFormat, dwFlags);
1561
1562}
1563
1564
1565
1566// Grab the input/output formats
1567cmsUInt32Number CMSEXPORT cmsGetTransformInputFormat(cmsContext ContextID, cmsHTRANSFORM hTransform)
1568{
1569 _cmsTRANSFORM* xform = (_cmsTRANSFORM*) hTransform;
1570 cmsUNUSED_PARAMETER(ContextID);
1571
1572 if (xform == NULL) return 0;
1573 return xform->InputFormat;
1574}
1575
1576cmsUInt32Number CMSEXPORT cmsGetTransformOutputFormat(cmsContext ContextID, cmsHTRANSFORM hTransform)
1577{
1578 _cmsTRANSFORM* xform = (_cmsTRANSFORM*) hTransform;
1579 cmsUNUSED_PARAMETER(ContextID);
1580
1581 if (xform == NULL) return 0;
1582 return xform->OutputFormat;
1583}
1584
1585cmsHTRANSFORM cmsCloneTransformChangingFormats(cmsContext ContextID,
1586 const cmsHTRANSFORM hTransform,
1587 cmsUInt32Number InputFormat,
1588 cmsUInt32Number OutputFormat)
1589{
1590 const _cmsTRANSFORM *oldXform = (const _cmsTRANSFORM *)hTransform;
1591 _cmsTRANSFORM *xform;
1592 cmsFormatter16 FromInput, ToOutput;
1593
1594 _cmsAssert(oldXform != NULL && oldXform->core != NULL);
1595
1596 // We only can afford to change formatters if previous transform is at least 16 bits
1597 if (!(oldXform->core->dwOriginalFlags & cmsFLAGS_CAN_CHANGE_FORMATTER)) {
1598 cmsSignalError(ContextID, cmsERROR_NOT_SUITABLE, "cmsCloneTransformChangingFormats works only on transforms created originally with at least 16 bits of precision");
1599 return NULL;
1600 }
1601
1602 xform = _cmsMalloc(ContextID, sizeof(*xform));
1603 if (xform == NULL)
1604 return NULL;
1605
1606 memcpy(xform, oldXform, sizeof(*xform));
1607
1608 FromInput = _cmsGetFormatter(ContextID, InputFormat, cmsFormatterInput, CMS_PACK_FLAGS_16BITS).Fmt16;
1609 ToOutput = _cmsGetFormatter(ContextID, OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_16BITS).Fmt16;
1610
1611 if (FromInput == NULL || ToOutput == NULL) {
1612
1613 cmsSignalError(ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported raster format");
1614 return NULL;
1615 }
1616
1617 xform ->InputFormat = InputFormat;
1618 xform ->OutputFormat = OutputFormat;
1619 xform ->FromInput = FromInput;
1620 xform ->ToOutput = ToOutput;
1621 _cmsFindFormatter(xform, InputFormat, OutputFormat, xform->core->dwOriginalFlags);
1622
1623 (void)_cmsAdjustReferenceCount(&xform->core->refs, 1);
1624
1625 return xform;
1626}
1627