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
_cmsAllocAdaptationStateChunk(struct _cmsContext_struct * ctx,const struct _cmsContext_struct * src)38 void _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
cmsSetAdaptationState(cmsContext ContextID,cmsFloat64Number d)58 cmsFloat64Number 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.
cmsSetAlarmCodes(cmsContext ContextID,const cmsUInt16Number AlarmCodesP[cmsMAXCHANNELS])88 void 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.
cmsGetAlarmCodes(cmsContext ContextID,cmsUInt16Number AlarmCodesP[cmsMAXCHANNELS])99 void 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
_cmsAllocAlarmCodesChunk(struct _cmsContext_struct * ctx,const struct _cmsContext_struct * src)110 void _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
cmsDeleteTransform(cmsContext ContextID,cmsHTRANSFORM hTransform)129 void 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.
cmsDoTransform(cmsContext ContextID,cmsHTRANSFORM Transform,const void * InputBuffer,void * OutputBuffer,cmsUInt32Number Size)170 void 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
cmsDoTransformStride(cmsContext ContextID,cmsHTRANSFORM Transform,const void * InputBuffer,void * OutputBuffer,cmsUInt32Number Size,cmsUInt32Number Stride)189 void 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
cmsDoTransformLineStride(cmsContext ContextID,cmsHTRANSFORM Transform,const void * InputBuffer,void * OutputBuffer,cmsUInt32Number PixelsPerLine,cmsUInt32Number LineCount,cmsUInt32Number BytesPerLineIn,cmsUInt32Number BytesPerLineOut,cmsUInt32Number BytesPerPlaneIn,cmsUInt32Number BytesPerPlaneOut)207 void 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.
235 static
FloatXFORM(cmsContext ContextID,_cmsTRANSFORM * p,const void * in,void * out,cmsUInt32Number PixelsPerLine,cmsUInt32Number LineCount,const cmsStride * Stride)236 void 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
302 static
NullFloatXFORM(cmsContext ContextID,_cmsTRANSFORM * p,const void * in,void * out,cmsUInt32Number PixelsPerLine,cmsUInt32Number LineCount,const cmsStride * Stride)303 void 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
341 static
NullXFORM(cmsContext ContextID,_cmsTRANSFORM * p,const void * in,void * out,cmsUInt32Number PixelsPerLine,cmsUInt32Number LineCount,const cmsStride * Stride)342 void 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
384 static
PrecalculatedXFORMIdentity(cmsContext ContextID,_cmsTRANSFORM * p,const void * in,void * out,cmsUInt32Number PixelsPerLine,cmsUInt32Number LineCount,const cmsStride * Stride)385 void 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
418 static
PrecalculatedXFORMIdentityPlanar(cmsContext ContextID,_cmsTRANSFORM * p,const void * in,void * out,cmsUInt32Number PixelsPerLine,cmsUInt32Number LineCount,const cmsStride * Stride)419 void 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.
467 static
TransformOnePixelWithGamutCheck(cmsContext ContextID,_cmsTRANSFORM * p,const cmsUInt16Number wIn[],cmsUInt16Number wOut[])468 void 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) \
527 do { \
528 (D)[0] = FROM_8_TO_16(*(S)); (S)++; \
529 } while (0)
530 #define PACK(CTX,T,S,D,Z) \
531 do { \
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) \
541 do { \
542 (D)[0] = *(cmsUInt16Number *)(S); (S) += 2; \
543 } while (0)
544 #define PACK(CTX,T,S,D,Z) \
545 do { \
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) \
555 do { \
556 (D)[0] = FROM_8_TO_16(*(S)); (S)++; \
557 } while (0)
558 #define PACK(CTX,T,S,D,Z) \
559 do { \
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) \
571 do { \
572 (D)[0] = *(cmsUInt16Number *)(S); (S) += 2; \
573 } while (0)
574 #define PACK(CTX,T,S,D,Z) \
575 do { \
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) \
587 do { \
588 (D)[0] = FROM_8_TO_16(*(S)); (S)++; \
589 } while (0)
590 #define PACK(CTX,T,S,D,Z) \
591 do { \
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) \
604 do { \
605 (D)[0] = *(cmsUInt16Number *)(S); (S) += 2; \
606 } while (0)
607 #define PACK(CTX,T,S,D,Z) \
608 do { \
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) \
621 do { \
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) \
627 do { \
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) \
637 do { \
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) \
643 do { \
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) \
653 do { \
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) \
659 do { \
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) \
671 do { \
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) \
677 do { \
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) \
689 do { \
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) \
695 do { \
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) \
708 do { \
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) \
714 do { \
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) \
727 do { \
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) \
734 do { \
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) \
744 do { \
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) \
751 do { \
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) \
761 do { \
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) \
768 do { \
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) \
780 do { \
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) \
787 do { \
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) \
799 do { \
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) \
806 do { \
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) \
819 do { \
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) \
826 do { \
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
837 typedef 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
851 static
DupPluginTransformList(struct _cmsContext_struct * ctx,const struct _cmsContext_struct * src)852 void 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
_cmsAllocTransformPluginChunk(struct _cmsContext_struct * ctx,const struct _cmsContext_struct * src)885 void _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
900 static
_cmsTransform2toTransformAdaptor(cmsContext ContextID,struct _cmstransform_struct * CMMcargo,const void * InputBuffer,void * OutputBuffer,cmsUInt32Number PixelsPerLine,cmsUInt32Number LineCount,const cmsStride * Stride)901 void _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
_cmsRegisterTransformPlugin(cmsContext ContextID,cmsPluginBase * Data)931 cmsBool _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 < 80) {
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
_cmsSetTransformUserData(struct _cmstransform_struct * CMMcargo,void * ptr,_cmsFreeUserDataFn FreePrivateDataFn)971 void 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
_cmsGetTransformUserData(struct _cmstransform_struct * CMMcargo)979 void * 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
_cmsGetTransformFormatters16(struct _cmstransform_struct * CMMcargo,cmsFormatter16 * FromInput,cmsFormatter16 * ToOutput)986 void 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
_cmsGetTransformFormattersFloat(struct _cmstransform_struct * CMMcargo,cmsFormatterFloat * FromInput,cmsFormatterFloat * ToOutput)993 void 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
1001 void
_cmsFindFormatter(_cmsTRANSFORM * p,cmsUInt32Number InputFormat,cmsUInt32Number OutputFormat,cmsUInt32Number dwFlags)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,
1111 static
AllocEmptyTransform(cmsContext ContextID,cmsPipeline * lut,cmsUInt32Number Intent,cmsUInt32Number * InputFormat,cmsUInt32Number * OutputFormat,cmsUInt32Number * dwFlags)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 // First, optimise the pipeline. This may cause us to recognise that the Luts are
1142 // identity.
1143 _cmsOptimizePipeline(ContextID, &core->Lut, Intent, InputFormat, OutputFormat, dwFlags);
1144
1145 if (_cmsLutIsIdentity(core->Lut) == FALSE) {
1146 for (Plugin = ctx->TransformCollection;
1147 Plugin != NULL;
1148 Plugin = Plugin->Next) {
1149
1150 if (Plugin->Factory(ContextID, &p->xform, &core->UserData, &core->FreeUserData, &core->Lut, InputFormat, OutputFormat, dwFlags)) {
1151
1152 // Last plugin in the declaration order takes control. We just keep
1153 // the original parameters as a logging.
1154 // Note that cmsFLAGS_CAN_CHANGE_FORMATTER is not set, so by default
1155 // an optimized transform is not reusable. The plug-in can, however, change
1156 // the flags and make it suitable.
1157
1158 p->InputFormat = *InputFormat;
1159 p->OutputFormat = *OutputFormat;
1160 core->dwOriginalFlags = *dwFlags;
1161
1162 // Fill the formatters just in case the optimized routine is interested.
1163 // No error is thrown if the formatter doesn't exist. It is up to the optimization
1164 // factory to decide what to do in those cases.
1165 p->FromInput = _cmsGetFormatter(ContextID, *InputFormat, cmsFormatterInput, CMS_PACK_FLAGS_16BITS).Fmt16;
1166 p->ToOutput = _cmsGetFormatter(ContextID, *OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_16BITS).Fmt16;
1167 p->FromInputFloat = _cmsGetFormatter(ContextID, *InputFormat, cmsFormatterInput, CMS_PACK_FLAGS_FLOAT).FmtFloat;
1168 p->ToOutputFloat = _cmsGetFormatter(ContextID, *OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_FLOAT).FmtFloat;
1169
1170 // Save the day? (Ignore the warning)
1171 if (Plugin->OldXform) {
1172 p->OldXform = (_cmsTransformFn)(void*) p->xform;
1173 p->xform = _cmsTransform2toTransformAdaptor;
1174 }
1175 return p;
1176 }
1177 }
1178 }
1179
1180 }
1181
1182 // Check whatever this is a true floating point transform
1183 if (_cmsFormatterIsFloat(*InputFormat) && _cmsFormatterIsFloat(*OutputFormat)) {
1184
1185 // Get formatter function always return a valid union, but the contents of this union may be NULL.
1186 p ->FromInputFloat = _cmsGetFormatter(ContextID, *InputFormat, cmsFormatterInput, CMS_PACK_FLAGS_FLOAT).FmtFloat;
1187 p ->ToOutputFloat = _cmsGetFormatter(ContextID, *OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_FLOAT).FmtFloat;
1188 *dwFlags |= cmsFLAGS_CAN_CHANGE_FORMATTER;
1189
1190 if (p ->FromInputFloat == NULL || p ->ToOutputFloat == NULL) {
1191
1192 cmsSignalError(ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported raster format");
1193 cmsDeleteTransform(ContextID, p);
1194 return NULL;
1195 }
1196
1197 if (*dwFlags & cmsFLAGS_NULLTRANSFORM) {
1198
1199 p ->xform = NullFloatXFORM;
1200 }
1201 else {
1202 // Float transforms don't use cache, always are non-NULL
1203 p ->xform = FloatXFORM;
1204 }
1205
1206 }
1207 else {
1208
1209 if (*InputFormat == 0 && *OutputFormat == 0) {
1210 p ->FromInput = p ->ToOutput = NULL;
1211 *dwFlags |= cmsFLAGS_CAN_CHANGE_FORMATTER;
1212 }
1213 else {
1214
1215 cmsUInt32Number BytesPerPixelInput;
1216
1217 p ->FromInput = _cmsGetFormatter(ContextID, *InputFormat, cmsFormatterInput, CMS_PACK_FLAGS_16BITS).Fmt16;
1218 p ->ToOutput = _cmsGetFormatter(ContextID, *OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_16BITS).Fmt16;
1219
1220 if (p ->FromInput == NULL || p ->ToOutput == NULL) {
1221
1222 cmsSignalError(ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported raster format");
1223 cmsDeleteTransform(ContextID, p);
1224 return NULL;
1225 }
1226
1227 BytesPerPixelInput = T_BYTES(p ->InputFormat);
1228 if (BytesPerPixelInput == 0 || BytesPerPixelInput >= 2)
1229 *dwFlags |= cmsFLAGS_CAN_CHANGE_FORMATTER;
1230
1231 }
1232
1233 _cmsFindFormatter(p, *InputFormat, *OutputFormat, *dwFlags);
1234 }
1235
1236 p ->InputFormat = *InputFormat;
1237 p ->OutputFormat = *OutputFormat;
1238 core->dwOriginalFlags = *dwFlags;
1239 core->UserData = NULL;
1240 return p;
1241 }
1242
1243 static
GetXFormColorSpaces(cmsContext ContextID,cmsUInt32Number nProfiles,cmsHPROFILE hProfiles[],cmsColorSpaceSignature * Input,cmsColorSpaceSignature * Output)1244 cmsBool GetXFormColorSpaces(cmsContext ContextID, cmsUInt32Number nProfiles, cmsHPROFILE hProfiles[], cmsColorSpaceSignature* Input, cmsColorSpaceSignature* Output)
1245 {
1246 cmsColorSpaceSignature ColorSpaceIn, ColorSpaceOut;
1247 cmsColorSpaceSignature PostColorSpace;
1248 cmsUInt32Number i;
1249
1250 if (nProfiles == 0) return FALSE;
1251 if (hProfiles[0] == NULL) return FALSE;
1252
1253 *Input = PostColorSpace = cmsGetColorSpace(ContextID, hProfiles[0]);
1254
1255 for (i=0; i < nProfiles; i++) {
1256
1257 cmsProfileClassSignature cls;
1258 cmsHPROFILE hProfile = hProfiles[i];
1259
1260 int lIsInput = (PostColorSpace != cmsSigXYZData) &&
1261 (PostColorSpace != cmsSigLabData);
1262
1263 if (hProfile == NULL) return FALSE;
1264
1265 cls = cmsGetDeviceClass(ContextID, hProfile);
1266
1267 if (cls == cmsSigNamedColorClass) {
1268
1269 ColorSpaceIn = cmsSig1colorData;
1270 ColorSpaceOut = (nProfiles > 1) ? cmsGetPCS(ContextID, hProfile) : cmsGetColorSpace(ContextID, hProfile);
1271 }
1272 else
1273 if (lIsInput || (cls == cmsSigLinkClass)) {
1274
1275 ColorSpaceIn = cmsGetColorSpace(ContextID, hProfile);
1276 ColorSpaceOut = cmsGetPCS(ContextID, hProfile);
1277 }
1278 else
1279 {
1280 ColorSpaceIn = cmsGetPCS(ContextID, hProfile);
1281 ColorSpaceOut = cmsGetColorSpace(ContextID, hProfile);
1282 }
1283
1284 if (i==0)
1285 *Input = ColorSpaceIn;
1286
1287 PostColorSpace = ColorSpaceOut;
1288 }
1289
1290 *Output = PostColorSpace;
1291
1292 return TRUE;
1293 }
1294
1295 // Check colorspace
1296 static
IsProperColorSpace(cmsContext ContextID,cmsColorSpaceSignature Check,cmsUInt32Number dwFormat)1297 cmsBool IsProperColorSpace(cmsContext ContextID, cmsColorSpaceSignature Check, cmsUInt32Number dwFormat)
1298 {
1299 int Space1 = (int) T_COLORSPACE(dwFormat);
1300 int Space2 = _cmsLCMScolorSpace(ContextID, Check);
1301
1302 if (Space1 == PT_ANY) return TRUE;
1303 if (Space1 == Space2) return TRUE;
1304
1305 if (Space1 == PT_LabV2 && Space2 == PT_Lab) return TRUE;
1306 if (Space1 == PT_Lab && Space2 == PT_LabV2) return TRUE;
1307
1308 return FALSE;
1309 }
1310
1311 // ----------------------------------------------------------------------------------------------------------------
1312
1313 // Jun-21-2000: Some profiles (those that comes with W2K) comes
1314 // with the media white (media black?) x 100. Add a sanity check
1315
1316 static
NormalizeXYZ(cmsCIEXYZ * Dest)1317 void NormalizeXYZ(cmsCIEXYZ* Dest)
1318 {
1319 while (Dest -> X > 2. &&
1320 Dest -> Y > 2. &&
1321 Dest -> Z > 2.) {
1322
1323 Dest -> X /= 10.;
1324 Dest -> Y /= 10.;
1325 Dest -> Z /= 10.;
1326 }
1327 }
1328
1329 static
SetWhitePoint(cmsCIEXYZ * wtPt,const cmsCIEXYZ * src)1330 void SetWhitePoint(cmsCIEXYZ* wtPt, const cmsCIEXYZ* src)
1331 {
1332 if (src == NULL) {
1333 wtPt ->X = cmsD50X;
1334 wtPt ->Y = cmsD50Y;
1335 wtPt ->Z = cmsD50Z;
1336 }
1337 else {
1338 wtPt ->X = src->X;
1339 wtPt ->Y = src->Y;
1340 wtPt ->Z = src->Z;
1341
1342 NormalizeXYZ(wtPt);
1343 }
1344
1345 }
1346
1347 // New to lcms 2.0 -- have all parameters available.
cmsCreateExtendedTransform(cmsContext ContextID,cmsUInt32Number nProfiles,cmsHPROFILE hProfiles[],cmsBool BPC[],cmsUInt32Number Intents[],cmsFloat64Number AdaptationStates[],cmsHPROFILE hGamutProfile,cmsUInt32Number nGamutPCSposition,cmsUInt32Number InputFormat,cmsUInt32Number OutputFormat,cmsUInt32Number dwFlags)1348 cmsHTRANSFORM CMSEXPORT cmsCreateExtendedTransform(cmsContext ContextID,
1349 cmsUInt32Number nProfiles, cmsHPROFILE hProfiles[],
1350 cmsBool BPC[],
1351 cmsUInt32Number Intents[],
1352 cmsFloat64Number AdaptationStates[],
1353 cmsHPROFILE hGamutProfile,
1354 cmsUInt32Number nGamutPCSposition,
1355 cmsUInt32Number InputFormat,
1356 cmsUInt32Number OutputFormat,
1357 cmsUInt32Number dwFlags)
1358 {
1359 _cmsTRANSFORM* xform;
1360 cmsColorSpaceSignature EntryColorSpace;
1361 cmsColorSpaceSignature ExitColorSpace;
1362 cmsPipeline* Lut;
1363 cmsUInt32Number LastIntent = Intents[nProfiles-1];
1364
1365 // If it is a fake transform
1366 if (dwFlags & cmsFLAGS_NULLTRANSFORM)
1367 {
1368 return AllocEmptyTransform(ContextID, NULL, INTENT_PERCEPTUAL, &InputFormat, &OutputFormat, &dwFlags);
1369 }
1370
1371 // If gamut check is requested, make sure we have a gamut profile
1372 if (dwFlags & cmsFLAGS_GAMUTCHECK) {
1373 if (hGamutProfile == NULL) dwFlags &= ~cmsFLAGS_GAMUTCHECK;
1374 }
1375
1376 // On floating point transforms, inhibit cache
1377 if (_cmsFormatterIsFloat(InputFormat) || _cmsFormatterIsFloat(OutputFormat))
1378 dwFlags |= cmsFLAGS_NOCACHE;
1379
1380 // Mark entry/exit spaces
1381 if (!GetXFormColorSpaces(ContextID, nProfiles, hProfiles, &EntryColorSpace, &ExitColorSpace)) {
1382 cmsSignalError(ContextID, cmsERROR_NULL, "NULL input profiles on transform");
1383 return NULL;
1384 }
1385
1386 // Check if proper colorspaces
1387 if (!IsProperColorSpace(ContextID, EntryColorSpace, InputFormat)) {
1388 cmsSignalError(ContextID, cmsERROR_COLORSPACE_CHECK, "Wrong input color space on transform");
1389 return NULL;
1390 }
1391
1392 if (!IsProperColorSpace(ContextID, ExitColorSpace, OutputFormat)) {
1393 cmsSignalError(ContextID, cmsERROR_COLORSPACE_CHECK, "Wrong output color space on transform");
1394 return NULL;
1395 }
1396
1397 // Create a pipeline with all transformations
1398 Lut = _cmsLinkProfiles(ContextID, nProfiles, Intents, hProfiles, BPC, AdaptationStates, dwFlags);
1399 if (Lut == NULL) {
1400 cmsSignalError(ContextID, cmsERROR_NOT_SUITABLE, "Couldn't link the profiles");
1401 return NULL;
1402 }
1403
1404 // Check channel count
1405 if ((cmsChannelsOf(ContextID, EntryColorSpace) != cmsPipelineInputChannels(ContextID, Lut)) ||
1406 (cmsChannelsOf(ContextID, ExitColorSpace) != cmsPipelineOutputChannels(ContextID, Lut))) {
1407 cmsPipelineFree(ContextID, Lut);
1408 cmsSignalError(ContextID, cmsERROR_NOT_SUITABLE, "Channel count doesn't match. Profile is corrupted");
1409 return NULL;
1410 }
1411
1412
1413 // All seems ok
1414 xform = AllocEmptyTransform(ContextID, Lut, LastIntent, &InputFormat, &OutputFormat, &dwFlags);
1415 if (xform == NULL) {
1416 return NULL;
1417 }
1418
1419 // Keep values
1420 xform->core->EntryColorSpace = EntryColorSpace;
1421 xform->core->ExitColorSpace = ExitColorSpace;
1422 xform->core->RenderingIntent = Intents[nProfiles-1];
1423
1424 // Take white points
1425 SetWhitePoint(&xform->core->EntryWhitePoint, (cmsCIEXYZ*) cmsReadTag(ContextID, hProfiles[0], cmsSigMediaWhitePointTag));
1426 SetWhitePoint(&xform->core->ExitWhitePoint, (cmsCIEXYZ*) cmsReadTag(ContextID, hProfiles[nProfiles-1], cmsSigMediaWhitePointTag));
1427
1428
1429 // Create a gamut check LUT if requested
1430 if (hGamutProfile != NULL && (dwFlags & cmsFLAGS_GAMUTCHECK))
1431 xform->core->GamutCheck = _cmsCreateGamutCheckPipeline(ContextID, hProfiles,
1432 BPC, Intents,
1433 AdaptationStates,
1434 nGamutPCSposition,
1435 hGamutProfile);
1436
1437
1438 // Try to read input and output colorant table
1439 if (cmsIsTag(ContextID, hProfiles[0], cmsSigColorantTableTag)) {
1440
1441 // Input table can only come in this way.
1442 xform->core->InputColorant = cmsDupNamedColorList(ContextID, (cmsNAMEDCOLORLIST*) cmsReadTag(ContextID, hProfiles[0], cmsSigColorantTableTag));
1443 }
1444
1445 // Output is a little bit more complex.
1446 if (cmsGetDeviceClass(ContextID, hProfiles[nProfiles-1]) == cmsSigLinkClass) {
1447
1448 // This tag may exist only on devicelink profiles.
1449 if (cmsIsTag(ContextID, hProfiles[nProfiles-1], cmsSigColorantTableOutTag)) {
1450
1451 // It may be NULL if error
1452 xform->core->OutputColorant = cmsDupNamedColorList(ContextID, (cmsNAMEDCOLORLIST*) cmsReadTag(ContextID, hProfiles[nProfiles-1], cmsSigColorantTableOutTag));
1453 }
1454
1455 } else {
1456
1457 if (cmsIsTag(ContextID, hProfiles[nProfiles-1], cmsSigColorantTableTag)) {
1458
1459 xform->core->OutputColorant = cmsDupNamedColorList(ContextID, (cmsNAMEDCOLORLIST*) cmsReadTag(ContextID, hProfiles[nProfiles-1], cmsSigColorantTableTag));
1460 }
1461 }
1462
1463 // Store the sequence of profiles
1464 if (dwFlags & cmsFLAGS_KEEP_SEQUENCE) {
1465 xform->core->Sequence = _cmsCompileProfileSequence(ContextID, nProfiles, hProfiles);
1466 }
1467 else
1468 xform->core->Sequence = NULL;
1469
1470 // If this is a cached transform, init first value, which is zero (16 bits only)
1471 if (!(dwFlags & cmsFLAGS_NOCACHE)) {
1472
1473 memset(&xform ->Cache.CacheIn, 0, sizeof(xform ->Cache.CacheIn));
1474
1475 if (xform->core->GamutCheck != NULL) {
1476 TransformOnePixelWithGamutCheck(ContextID, xform, xform->Cache.CacheIn, xform->Cache.CacheOut);
1477 }
1478 else {
1479
1480 xform->core->Lut->Eval16Fn(ContextID, xform ->Cache.CacheIn, xform->Cache.CacheOut, xform->core->Lut->Data);
1481 }
1482
1483 }
1484
1485 return (cmsHTRANSFORM) xform;
1486 }
1487
1488 // Multiprofile transforms: Gamut check is not available here, as it is unclear from which profile the gamut comes.
cmsCreateMultiprofileTransform(cmsContext ContextID,cmsHPROFILE hProfiles[],cmsUInt32Number nProfiles,cmsUInt32Number InputFormat,cmsUInt32Number OutputFormat,cmsUInt32Number Intent,cmsUInt32Number dwFlags)1489 cmsHTRANSFORM CMSEXPORT cmsCreateMultiprofileTransform(cmsContext ContextID,
1490 cmsHPROFILE hProfiles[],
1491 cmsUInt32Number nProfiles,
1492 cmsUInt32Number InputFormat,
1493 cmsUInt32Number OutputFormat,
1494 cmsUInt32Number Intent,
1495 cmsUInt32Number dwFlags)
1496 {
1497 cmsUInt32Number i;
1498 cmsBool BPC[256];
1499 cmsUInt32Number Intents[256];
1500 cmsFloat64Number AdaptationStates[256];
1501
1502 if (nProfiles <= 0 || nProfiles > 255) {
1503 cmsSignalError(ContextID, cmsERROR_RANGE, "Wrong number of profiles. 1..255 expected, %d found.", nProfiles);
1504 return NULL;
1505 }
1506
1507 for (i=0; i < nProfiles; i++) {
1508 BPC[i] = dwFlags & cmsFLAGS_BLACKPOINTCOMPENSATION ? TRUE : FALSE;
1509 Intents[i] = Intent;
1510 AdaptationStates[i] = cmsSetAdaptationState(ContextID, -1);
1511 }
1512
1513
1514 return cmsCreateExtendedTransform(ContextID, nProfiles, hProfiles, BPC, Intents, AdaptationStates, NULL, 0, InputFormat, OutputFormat, dwFlags);
1515 }
1516
1517
1518
cmsCreateTransform(cmsContext ContextID,cmsHPROFILE Input,cmsUInt32Number InputFormat,cmsHPROFILE Output,cmsUInt32Number OutputFormat,cmsUInt32Number Intent,cmsUInt32Number dwFlags)1519 cmsHTRANSFORM CMSEXPORT cmsCreateTransform(cmsContext ContextID,
1520 cmsHPROFILE Input,
1521 cmsUInt32Number InputFormat,
1522 cmsHPROFILE Output,
1523 cmsUInt32Number OutputFormat,
1524 cmsUInt32Number Intent,
1525 cmsUInt32Number dwFlags)
1526 {
1527
1528 cmsHPROFILE hArray[2];
1529
1530 hArray[0] = Input;
1531 hArray[1] = Output;
1532
1533 return cmsCreateMultiprofileTransform(ContextID, hArray, Output == NULL ? 1U : 2U, InputFormat, OutputFormat, Intent, dwFlags);
1534 }
1535
1536
cmsCreateProofingTransform(cmsContext ContextID,cmsHPROFILE InputProfile,cmsUInt32Number InputFormat,cmsHPROFILE OutputProfile,cmsUInt32Number OutputFormat,cmsHPROFILE ProofingProfile,cmsUInt32Number nIntent,cmsUInt32Number ProofingIntent,cmsUInt32Number dwFlags)1537 cmsHTRANSFORM CMSEXPORT cmsCreateProofingTransform(cmsContext ContextID,
1538 cmsHPROFILE InputProfile,
1539 cmsUInt32Number InputFormat,
1540 cmsHPROFILE OutputProfile,
1541 cmsUInt32Number OutputFormat,
1542 cmsHPROFILE ProofingProfile,
1543 cmsUInt32Number nIntent,
1544 cmsUInt32Number ProofingIntent,
1545 cmsUInt32Number dwFlags)
1546 {
1547 cmsHPROFILE hArray[4];
1548 cmsUInt32Number Intents[4];
1549 cmsBool BPC[4];
1550 cmsFloat64Number Adaptation[4];
1551 cmsBool DoBPC = (dwFlags & cmsFLAGS_BLACKPOINTCOMPENSATION) ? TRUE : FALSE;
1552
1553
1554 hArray[0] = InputProfile; hArray[1] = ProofingProfile; hArray[2] = ProofingProfile; hArray[3] = OutputProfile;
1555 Intents[0] = nIntent; Intents[1] = nIntent; Intents[2] = INTENT_RELATIVE_COLORIMETRIC; Intents[3] = ProofingIntent;
1556 BPC[0] = DoBPC; BPC[1] = DoBPC; BPC[2] = 0; BPC[3] = 0;
1557
1558 Adaptation[0] = Adaptation[1] = Adaptation[2] = Adaptation[3] = cmsSetAdaptationState(ContextID, -1);
1559
1560 if (!(dwFlags & (cmsFLAGS_SOFTPROOFING|cmsFLAGS_GAMUTCHECK)))
1561 return cmsCreateTransform(ContextID, InputProfile, InputFormat, OutputProfile, OutputFormat, nIntent, dwFlags);
1562
1563 return cmsCreateExtendedTransform(ContextID, 4, hArray, BPC, Intents, Adaptation,
1564 ProofingProfile, 1, InputFormat, OutputFormat, dwFlags);
1565
1566 }
1567
1568
1569
1570 // Grab the input/output formats
cmsGetTransformInputFormat(cmsContext ContextID,cmsHTRANSFORM hTransform)1571 cmsUInt32Number CMSEXPORT cmsGetTransformInputFormat(cmsContext ContextID, cmsHTRANSFORM hTransform)
1572 {
1573 _cmsTRANSFORM* xform = (_cmsTRANSFORM*) hTransform;
1574 cmsUNUSED_PARAMETER(ContextID);
1575
1576 if (xform == NULL) return 0;
1577 return xform->InputFormat;
1578 }
1579
cmsGetTransformOutputFormat(cmsContext ContextID,cmsHTRANSFORM hTransform)1580 cmsUInt32Number CMSEXPORT cmsGetTransformOutputFormat(cmsContext ContextID, cmsHTRANSFORM hTransform)
1581 {
1582 _cmsTRANSFORM* xform = (_cmsTRANSFORM*) hTransform;
1583 cmsUNUSED_PARAMETER(ContextID);
1584
1585 if (xform == NULL) return 0;
1586 return xform->OutputFormat;
1587 }
1588
cmsCloneTransformChangingFormats(cmsContext ContextID,const cmsHTRANSFORM hTransform,cmsUInt32Number InputFormat,cmsUInt32Number OutputFormat)1589 cmsHTRANSFORM cmsCloneTransformChangingFormats(cmsContext ContextID,
1590 const cmsHTRANSFORM hTransform,
1591 cmsUInt32Number InputFormat,
1592 cmsUInt32Number OutputFormat)
1593 {
1594 const _cmsTRANSFORM *oldXform = (const _cmsTRANSFORM *)hTransform;
1595 _cmsTRANSFORM *xform;
1596 cmsFormatter16 FromInput, ToOutput;
1597 _cmsTransformPluginChunkType* ctx = (_cmsTransformPluginChunkType*)_cmsContextGetClientChunk(ContextID, TransformPlugin);
1598 _cmsTransformCollection* Plugin;
1599 _cmsTRANSFORMCORE *core;
1600
1601 _cmsAssert(oldXform != NULL && oldXform->core != NULL);
1602
1603 // We only can afford to change formatters if previous transform is at least 16 bits
1604 if (!(oldXform->core->dwOriginalFlags & cmsFLAGS_CAN_CHANGE_FORMATTER)) {
1605 cmsSignalError(ContextID, cmsERROR_NOT_SUITABLE, "cmsCloneTransformChangingFormats works only on transforms created originally with at least 16 bits of precision");
1606 return NULL;
1607 }
1608
1609 xform = _cmsMalloc(ContextID, sizeof(*xform));
1610 if (xform == NULL)
1611 return NULL;
1612
1613 memcpy(xform, oldXform, sizeof(*xform));
1614
1615 FromInput = _cmsGetFormatter(ContextID, InputFormat, cmsFormatterInput, CMS_PACK_FLAGS_16BITS).Fmt16;
1616 ToOutput = _cmsGetFormatter(ContextID, OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_16BITS).Fmt16;
1617
1618 if (FromInput == NULL || ToOutput == NULL) {
1619
1620 cmsSignalError(ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported raster format");
1621 return NULL;
1622 }
1623
1624 xform ->InputFormat = InputFormat;
1625 xform ->OutputFormat = OutputFormat;
1626 xform ->FromInput = FromInput;
1627 xform ->ToOutput = ToOutput;
1628
1629 /* Transformation plug-in support needed here but only if lcms has determined
1630 that this lut is not the identity transform */
1631 if (oldXform->core->Lut != NULL && _cmsLutIsIdentity(oldXform->core->Lut) == FALSE) {
1632 for (Plugin = ctx->TransformCollection; Plugin != NULL; Plugin = Plugin->Next) {
1633 core = xform->core;
1634 if (Plugin->Factory(ContextID, &xform->xform, &core->UserData,
1635 &core->FreeUserData, &core->Lut, &InputFormat, &OutputFormat, NULL)) {
1636 (void)_cmsAdjustReferenceCount(&xform->core->refs, 1);
1637 return xform;
1638 }
1639 }
1640 }
1641
1642 _cmsFindFormatter(xform, InputFormat, OutputFormat, xform->core->dwOriginalFlags);
1643
1644 (void)_cmsAdjustReferenceCount(&xform->core->refs, 1);
1645
1646 return xform;
1647 }
1648