1 //---------------------------------------------------------------------------------
2 //
3 // Little Color Management System
4 // Copyright (c) 1998-2016 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 cmsCreateExtendedTransformTHR(). Little CMS can handle incomplete adaptation states.
cmsSetAdaptationStateTHR(cmsContext ContextID,cmsFloat64Number d)57 cmsFloat64Number CMSEXPORT cmsSetAdaptationStateTHR(cmsContext ContextID, cmsFloat64Number d)
58 {
59 cmsFloat64Number prev;
60 _cmsAdaptationStateChunkType* ptr = (_cmsAdaptationStateChunkType*) _cmsContextGetClientChunk(ContextID, AdaptationStateContext);
61
62 // Get previous value for return
63 prev = ptr ->AdaptationState;
64
65 // Set the value if d is positive or zero
66 if (d >= 0.0) {
67
68 ptr ->AdaptationState = d;
69 }
70
71 // Always return previous value
72 return prev;
73 }
74
75
76 // The adaptation state may be defaulted by this function. If you don't like it, use the extended transform routine
cmsSetAdaptationState(cmsFloat64Number d)77 cmsFloat64Number CMSEXPORT cmsSetAdaptationState(cmsFloat64Number d)
78 {
79 return cmsSetAdaptationStateTHR(NULL, d);
80 }
81
82 // -----------------------------------------------------------------------
83
84 // Alarm codes for 16-bit transformations, because the fixed range of containers there are
85 // no values left to mark out of gamut.
86
87 #define DEFAULT_ALARM_CODES_VALUE {0x7F00, 0x7F00, 0x7F00, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
88
89 _cmsAlarmCodesChunkType _cmsAlarmCodesChunk = { DEFAULT_ALARM_CODES_VALUE };
90
91 // Sets the codes used to mark out-out-gamut on Proofing transforms for a given context. Values are meant to be
92 // encoded in 16 bits.
cmsSetAlarmCodesTHR(cmsContext ContextID,const cmsUInt16Number AlarmCodesP[cmsMAXCHANNELS])93 void CMSEXPORT cmsSetAlarmCodesTHR(cmsContext ContextID, const cmsUInt16Number AlarmCodesP[cmsMAXCHANNELS])
94 {
95 _cmsAlarmCodesChunkType* ContextAlarmCodes = (_cmsAlarmCodesChunkType*) _cmsContextGetClientChunk(ContextID, AlarmCodesContext);
96
97 _cmsAssert(ContextAlarmCodes != NULL); // Can't happen
98
99 memcpy(ContextAlarmCodes->AlarmCodes, AlarmCodesP, sizeof(ContextAlarmCodes->AlarmCodes));
100 }
101
102 // Gets the current codes used to mark out-out-gamut on Proofing transforms for the given context.
103 // Values are meant to be encoded in 16 bits.
cmsGetAlarmCodesTHR(cmsContext ContextID,cmsUInt16Number AlarmCodesP[cmsMAXCHANNELS])104 void CMSEXPORT cmsGetAlarmCodesTHR(cmsContext ContextID, cmsUInt16Number AlarmCodesP[cmsMAXCHANNELS])
105 {
106 _cmsAlarmCodesChunkType* ContextAlarmCodes = (_cmsAlarmCodesChunkType*) _cmsContextGetClientChunk(ContextID, AlarmCodesContext);
107
108 _cmsAssert(ContextAlarmCodes != NULL); // Can't happen
109
110 memcpy(AlarmCodesP, ContextAlarmCodes->AlarmCodes, sizeof(ContextAlarmCodes->AlarmCodes));
111 }
112
cmsSetAlarmCodes(const cmsUInt16Number NewAlarm[cmsMAXCHANNELS])113 void CMSEXPORT cmsSetAlarmCodes(const cmsUInt16Number NewAlarm[cmsMAXCHANNELS])
114 {
115 _cmsAssert(NewAlarm != NULL);
116
117 cmsSetAlarmCodesTHR(NULL, NewAlarm);
118 }
119
cmsGetAlarmCodes(cmsUInt16Number OldAlarm[cmsMAXCHANNELS])120 void CMSEXPORT cmsGetAlarmCodes(cmsUInt16Number OldAlarm[cmsMAXCHANNELS])
121 {
122 _cmsAssert(OldAlarm != NULL);
123 cmsGetAlarmCodesTHR(NULL, OldAlarm);
124 }
125
126
127 // Init and duplicate alarm codes
_cmsAllocAlarmCodesChunk(struct _cmsContext_struct * ctx,const struct _cmsContext_struct * src)128 void _cmsAllocAlarmCodesChunk(struct _cmsContext_struct* ctx,
129 const struct _cmsContext_struct* src)
130 {
131 static _cmsAlarmCodesChunkType AlarmCodesChunk = { DEFAULT_ALARM_CODES_VALUE };
132 void* from;
133
134 if (src != NULL) {
135 from = src ->chunks[AlarmCodesContext];
136 }
137 else {
138 from = &AlarmCodesChunk;
139 }
140
141 ctx ->chunks[AlarmCodesContext] = _cmsSubAllocDup(ctx ->MemPool, from, sizeof(_cmsAlarmCodesChunkType));
142 }
143
144 // -----------------------------------------------------------------------
145
146 // Get rid of transform resources
cmsDeleteTransform(cmsHTRANSFORM hTransform)147 void CMSEXPORT cmsDeleteTransform(cmsHTRANSFORM hTransform)
148 {
149 _cmsTRANSFORM* p = (_cmsTRANSFORM*) hTransform;
150
151 _cmsAssert(p != NULL);
152
153 if (p -> GamutCheck)
154 cmsPipelineFree(p -> GamutCheck);
155
156 if (p -> Lut)
157 cmsPipelineFree(p -> Lut);
158
159 if (p ->InputColorant)
160 cmsFreeNamedColorList(p ->InputColorant);
161
162 if (p -> OutputColorant)
163 cmsFreeNamedColorList(p ->OutputColorant);
164
165 if (p ->Sequence)
166 cmsFreeProfileSequenceDescription(p ->Sequence);
167
168 if (p ->UserData)
169 p ->FreeUserData(p ->ContextID, p ->UserData);
170
171 _cmsFree(p ->ContextID, (void *) p);
172 }
173
174 // Apply transform.
cmsDoTransform(cmsHTRANSFORM Transform,const void * InputBuffer,void * OutputBuffer,cmsUInt32Number Size)175 void CMSEXPORT cmsDoTransform(cmsHTRANSFORM Transform,
176 const void* InputBuffer,
177 void* OutputBuffer,
178 cmsUInt32Number Size)
179
180 {
181 _cmsTRANSFORM* p = (_cmsTRANSFORM*) Transform;
182 cmsStride stride;
183
184 stride.BytesPerLineIn = 0; // Not used
185 stride.BytesPerLineOut = 0;
186 stride.BytesPerPlaneIn = Size;
187 stride.BytesPerPlaneOut = Size;
188
189 p -> xform(p, InputBuffer, OutputBuffer, Size, 1, &stride);
190 }
191
192
193 // This is a legacy stride for planar
cmsDoTransformStride(cmsHTRANSFORM Transform,const void * InputBuffer,void * OutputBuffer,cmsUInt32Number Size,cmsUInt32Number Stride)194 void CMSEXPORT cmsDoTransformStride(cmsHTRANSFORM Transform,
195 const void* InputBuffer,
196 void* OutputBuffer,
197 cmsUInt32Number Size, cmsUInt32Number Stride)
198
199 {
200 _cmsTRANSFORM* p = (_cmsTRANSFORM*) Transform;
201 cmsStride stride;
202
203 stride.BytesPerLineIn = 0;
204 stride.BytesPerLineOut = 0;
205 stride.BytesPerPlaneIn = Stride;
206 stride.BytesPerPlaneOut = Stride;
207
208 p -> xform(p, InputBuffer, OutputBuffer, Size, 1, &stride);
209 }
210
211 // This is the "fast" function for plugins
cmsDoTransformLineStride(cmsHTRANSFORM Transform,const void * InputBuffer,void * OutputBuffer,cmsUInt32Number PixelsPerLine,cmsUInt32Number LineCount,cmsUInt32Number BytesPerLineIn,cmsUInt32Number BytesPerLineOut,cmsUInt32Number BytesPerPlaneIn,cmsUInt32Number BytesPerPlaneOut)212 void CMSEXPORT cmsDoTransformLineStride(cmsHTRANSFORM Transform,
213 const void* InputBuffer,
214 void* OutputBuffer,
215 cmsUInt32Number PixelsPerLine,
216 cmsUInt32Number LineCount,
217 cmsUInt32Number BytesPerLineIn,
218 cmsUInt32Number BytesPerLineOut,
219 cmsUInt32Number BytesPerPlaneIn,
220 cmsUInt32Number BytesPerPlaneOut)
221
222 {
223 _cmsTRANSFORM* p = (_cmsTRANSFORM*) Transform;
224 cmsStride stride;
225
226 stride.BytesPerLineIn = BytesPerLineIn;
227 stride.BytesPerLineOut = BytesPerLineOut;
228 stride.BytesPerPlaneIn = BytesPerPlaneIn;
229 stride.BytesPerPlaneOut = BytesPerPlaneOut;
230
231 p->xform(p, InputBuffer, OutputBuffer, PixelsPerLine, LineCount, &stride);
232 }
233
234
235
236 // Transform routines ----------------------------------------------------------------------------------------------------------
237
238 // Float xform converts floats. Since there are no performance issues, one routine does all job, including gamut check.
239 // Note that because extended range, we can use a -1.0 value for out of gamut in this case.
240 static
FloatXFORM(_cmsTRANSFORM * p,const void * in,void * out,cmsUInt32Number PixelsPerLine,cmsUInt32Number LineCount,const cmsStride * Stride)241 void FloatXFORM(_cmsTRANSFORM* p,
242 const void* in,
243 void* out,
244 cmsUInt32Number PixelsPerLine,
245 cmsUInt32Number LineCount,
246 const cmsStride* Stride)
247 {
248 cmsUInt8Number* accum;
249 cmsUInt8Number* output;
250 cmsFloat32Number fIn[cmsMAXCHANNELS], fOut[cmsMAXCHANNELS];
251 cmsFloat32Number OutOfGamut;
252 cmsUInt32Number i, j, c, strideIn, strideOut;
253
254 _cmsHandleExtraChannels(p, in, out, PixelsPerLine, LineCount, Stride);
255
256 strideIn = 0;
257 strideOut = 0;
258
259 for (i = 0; i < LineCount; i++) {
260
261 accum = (cmsUInt8Number*)in + strideIn;
262 output = (cmsUInt8Number*)out + strideOut;
263
264 for (j = 0; j < PixelsPerLine; j++) {
265
266 accum = p->FromInputFloat(p, fIn, accum, Stride->BytesPerPlaneIn);
267
268 // Any gamut chack to do?
269 if (p->GamutCheck != NULL) {
270
271 // Evaluate gamut marker.
272 cmsPipelineEvalFloat(fIn, &OutOfGamut, p->GamutCheck);
273
274 // Is current color out of gamut?
275 if (OutOfGamut > 0.0) {
276
277 // Certainly, out of gamut
278 for (c = 0; c < cmsMAXCHANNELS; c++)
279 fOut[c] = -1.0;
280
281 }
282 else {
283 // No, proceed normally
284 cmsPipelineEvalFloat(fIn, fOut, p->Lut);
285 }
286 }
287 else {
288
289 // No gamut check at all
290 cmsPipelineEvalFloat(fIn, fOut, p->Lut);
291 }
292
293
294 output = p->ToOutputFloat(p, fOut, output, Stride->BytesPerPlaneOut);
295 }
296
297 strideIn += Stride->BytesPerLineIn;
298 strideOut += Stride->BytesPerLineOut;
299 }
300
301 }
302
303
304 static
NullFloatXFORM(_cmsTRANSFORM * p,const void * in,void * out,cmsUInt32Number PixelsPerLine,cmsUInt32Number LineCount,const cmsStride * Stride)305 void NullFloatXFORM(_cmsTRANSFORM* p,
306 const void* in,
307 void* out,
308 cmsUInt32Number PixelsPerLine,
309 cmsUInt32Number LineCount,
310 const cmsStride* Stride)
311
312 {
313 cmsUInt8Number* accum;
314 cmsUInt8Number* output;
315 cmsFloat32Number fIn[cmsMAXCHANNELS];
316 cmsUInt32Number i, j, strideIn, strideOut;
317
318 _cmsHandleExtraChannels(p, in, out, PixelsPerLine, LineCount, Stride);
319
320 strideIn = 0;
321 strideOut = 0;
322
323 for (i = 0; i < LineCount; i++) {
324
325 accum = (cmsUInt8Number*) in + strideIn;
326 output = (cmsUInt8Number*) out + strideOut;
327
328 for (j = 0; j < PixelsPerLine; j++) {
329
330 accum = p->FromInputFloat(p, fIn, accum, Stride ->BytesPerPlaneIn);
331 output = p->ToOutputFloat(p, fIn, output, Stride->BytesPerPlaneOut);
332 }
333
334 strideIn += Stride->BytesPerLineIn;
335 strideOut += Stride->BytesPerLineOut;
336 }
337 }
338
339 // 16 bit precision -----------------------------------------------------------------------------------------------------------
340
341 // Null transformation, only applies formatters. No cache
342 static
NullXFORM(_cmsTRANSFORM * p,const void * in,void * out,cmsUInt32Number PixelsPerLine,cmsUInt32Number LineCount,const cmsStride * Stride)343 void NullXFORM(_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(p, in, out, PixelsPerLine, LineCount, Stride);
356
357 strideIn = 0;
358 strideOut = 0;
359
360 for (i = 0; i < LineCount; i++) {
361
362 accum = (cmsUInt8Number*)in + strideIn;
363 output = (cmsUInt8Number*)out + strideOut;
364
365 for (j = 0; j < PixelsPerLine; j++) {
366
367 accum = p->FromInput(p, wIn, accum, Stride->BytesPerPlaneIn);
368 output = p->ToOutput(p, wIn, output, Stride->BytesPerPlaneOut);
369 }
370
371 strideIn += Stride->BytesPerLineIn;
372 strideOut += Stride->BytesPerLineOut;
373 }
374
375 }
376
377
378 // No gamut check, no cache, 16 bits
379 static
PrecalculatedXFORM(_cmsTRANSFORM * p,const void * in,void * out,cmsUInt32Number PixelsPerLine,cmsUInt32Number LineCount,const cmsStride * Stride)380 void PrecalculatedXFORM(_cmsTRANSFORM* p,
381 const void* in,
382 void* out,
383 cmsUInt32Number PixelsPerLine,
384 cmsUInt32Number LineCount,
385 const cmsStride* Stride)
386 {
387 register cmsUInt8Number* accum;
388 register cmsUInt8Number* output;
389 cmsUInt16Number wIn[cmsMAXCHANNELS], wOut[cmsMAXCHANNELS];
390 cmsUInt32Number i, j, strideIn, strideOut;
391
392 _cmsHandleExtraChannels(p, in, out, PixelsPerLine, LineCount, Stride);
393
394 strideIn = 0;
395 strideOut = 0;
396
397 for (i = 0; i < LineCount; i++) {
398
399 accum = (cmsUInt8Number*)in + strideIn;
400 output = (cmsUInt8Number*)out + strideOut;
401
402 for (j = 0; j < PixelsPerLine; j++) {
403
404 accum = p->FromInput(p, wIn, accum, Stride->BytesPerPlaneIn);
405 p->Lut->Eval16Fn(wIn, wOut, p->Lut->Data);
406 output = p->ToOutput(p, wOut, output, Stride->BytesPerPlaneOut);
407 }
408
409 strideIn += Stride->BytesPerLineIn;
410 strideOut += Stride->BytesPerLineOut;
411 }
412
413 }
414
415
416 // Auxiliary: Handle precalculated gamut check. The retrieval of context may be alittle bit slow, but this function is not critical.
417 static
TransformOnePixelWithGamutCheck(_cmsTRANSFORM * p,const cmsUInt16Number wIn[],cmsUInt16Number wOut[])418 void TransformOnePixelWithGamutCheck(_cmsTRANSFORM* p,
419 const cmsUInt16Number wIn[],
420 cmsUInt16Number wOut[])
421 {
422 cmsUInt16Number wOutOfGamut;
423
424 p ->GamutCheck ->Eval16Fn(wIn, &wOutOfGamut, p ->GamutCheck ->Data);
425 if (wOutOfGamut >= 1) {
426
427 cmsUInt16Number i;
428 _cmsAlarmCodesChunkType* ContextAlarmCodes = (_cmsAlarmCodesChunkType*) _cmsContextGetClientChunk(p->ContextID, AlarmCodesContext);
429
430 for (i=0; i < p ->Lut->OutputChannels; i++) {
431
432 wOut[i] = ContextAlarmCodes ->AlarmCodes[i];
433 }
434 }
435 else
436 p ->Lut ->Eval16Fn(wIn, wOut, p -> Lut->Data);
437 }
438
439 // Gamut check, No cache, 16 bits.
440 static
PrecalculatedXFORMGamutCheck(_cmsTRANSFORM * p,const void * in,void * out,cmsUInt32Number PixelsPerLine,cmsUInt32Number LineCount,const cmsStride * Stride)441 void PrecalculatedXFORMGamutCheck(_cmsTRANSFORM* p,
442 const void* in,
443 void* out,
444 cmsUInt32Number PixelsPerLine,
445 cmsUInt32Number LineCount,
446 const cmsStride* Stride)
447 {
448 cmsUInt8Number* accum;
449 cmsUInt8Number* output;
450 cmsUInt16Number wIn[cmsMAXCHANNELS], wOut[cmsMAXCHANNELS];
451 cmsUInt32Number i, j, strideIn, strideOut;
452
453 _cmsHandleExtraChannels(p, in, out, PixelsPerLine, LineCount, Stride);
454
455 strideIn = 0;
456 strideOut = 0;
457
458 for (i = 0; i < LineCount; i++) {
459
460 accum = (cmsUInt8Number*)in + strideIn;
461 output = (cmsUInt8Number*)out + strideOut;
462
463 for (j = 0; j < PixelsPerLine; j++) {
464
465 accum = p->FromInput(p, wIn, accum, Stride->BytesPerPlaneIn);
466 TransformOnePixelWithGamutCheck(p, wIn, wOut);
467 output = p->ToOutput(p, wOut, output, Stride->BytesPerPlaneOut);
468 }
469
470 strideIn += Stride->BytesPerLineIn;
471 strideOut += Stride->BytesPerLineOut;
472 }
473 }
474
475
476 // No gamut check, Cache, 16 bits,
477 static
CachedXFORM(_cmsTRANSFORM * p,const void * in,void * out,cmsUInt32Number PixelsPerLine,cmsUInt32Number LineCount,const cmsStride * Stride)478 void CachedXFORM(_cmsTRANSFORM* p,
479 const void* in,
480 void* out,
481 cmsUInt32Number PixelsPerLine,
482 cmsUInt32Number LineCount,
483 const cmsStride* Stride)
484 {
485 cmsUInt8Number* accum;
486 cmsUInt8Number* output;
487 cmsUInt16Number wIn[cmsMAXCHANNELS], wOut[cmsMAXCHANNELS];
488 _cmsCACHE Cache;
489 cmsUInt32Number i, j, strideIn, strideOut;
490
491 _cmsHandleExtraChannels(p, in, out, PixelsPerLine, LineCount, Stride);
492
493 // Empty buffers for quick memcmp
494 memset(wIn, 0, sizeof(wIn));
495 memset(wOut, 0, sizeof(wOut));
496
497 // Get copy of zero cache
498 memcpy(&Cache, &p->Cache, sizeof(Cache));
499
500 strideIn = 0;
501 strideOut = 0;
502
503 for (i = 0; i < LineCount; i++) {
504
505 accum = (cmsUInt8Number*)in + strideIn;
506 output = (cmsUInt8Number*)out + strideOut;
507
508 for (j = 0; j < PixelsPerLine; j++) {
509
510 accum = p->FromInput(p, wIn, accum, Stride->BytesPerPlaneIn);
511
512 if (memcmp(wIn, Cache.CacheIn, sizeof(Cache.CacheIn)) == 0) {
513
514 memcpy(wOut, Cache.CacheOut, sizeof(Cache.CacheOut));
515 }
516 else {
517 p->Lut->Eval16Fn(wIn, wOut, p->Lut->Data);
518
519 memcpy(Cache.CacheIn, wIn, sizeof(Cache.CacheIn));
520 memcpy(Cache.CacheOut, wOut, sizeof(Cache.CacheOut));
521 }
522
523 output = p->ToOutput(p, wOut, output, Stride->BytesPerPlaneOut);
524 }
525
526 strideIn += Stride->BytesPerLineIn;
527 strideOut += Stride->BytesPerLineOut;
528 }
529 }
530
531 // All those nice features together
532 static
CachedXFORMGamutCheck(_cmsTRANSFORM * p,const void * in,void * out,cmsUInt32Number PixelsPerLine,cmsUInt32Number LineCount,const cmsStride * Stride)533 void CachedXFORMGamutCheck(_cmsTRANSFORM* p,
534 const void* in,
535 void* out,
536 cmsUInt32Number PixelsPerLine,
537 cmsUInt32Number LineCount,
538 const cmsStride* Stride)
539 {
540 cmsUInt8Number* accum;
541 cmsUInt8Number* output;
542 cmsUInt16Number wIn[cmsMAXCHANNELS], wOut[cmsMAXCHANNELS];
543 _cmsCACHE Cache;
544 cmsUInt32Number i, j, strideIn, strideOut;
545
546 _cmsHandleExtraChannels(p, in, out, PixelsPerLine, LineCount, Stride);
547
548 // Empty buffers for quick memcmp
549 memset(wIn, 0, sizeof(wIn));
550 memset(wOut, 0, sizeof(wOut));
551
552 // Get copy of zero cache
553 memcpy(&Cache, &p->Cache, sizeof(Cache));
554
555 strideIn = 0;
556 strideOut = 0;
557
558 for (i = 0; i < LineCount; i++) {
559
560 accum = (cmsUInt8Number*)in + strideIn;
561 output = (cmsUInt8Number*)out + strideOut;
562
563 for (j = 0; j < PixelsPerLine; j++) {
564
565 accum = p->FromInput(p, wIn, accum, Stride->BytesPerPlaneIn);
566
567 if (memcmp(wIn, Cache.CacheIn, sizeof(Cache.CacheIn)) == 0) {
568
569 memcpy(wOut, Cache.CacheOut, sizeof(Cache.CacheOut));
570 }
571 else {
572 TransformOnePixelWithGamutCheck(p, wIn, wOut);
573
574 memcpy(Cache.CacheIn, wIn, sizeof(Cache.CacheIn));
575 memcpy(Cache.CacheOut, wOut, sizeof(Cache.CacheOut));
576 }
577
578 output = p->ToOutput(p, wOut, output, Stride->BytesPerPlaneOut);
579 }
580
581 strideIn += Stride->BytesPerLineIn;
582 strideOut += Stride->BytesPerLineOut;
583 }
584 }
585
586 // Transform plug-ins ----------------------------------------------------------------------------------------------------
587
588 // List of used-defined transform factories
589 typedef struct _cmsTransformCollection_st {
590
591 _cmsTransform2Factory Factory;
592 cmsBool OldXform; // Factory returns xform function in the old style
593
594 struct _cmsTransformCollection_st *Next;
595
596 } _cmsTransformCollection;
597
598 // The linked list head
599 _cmsTransformPluginChunkType _cmsTransformPluginChunk = { NULL };
600
601
602 // Duplicates the zone of memory used by the plug-in in the new context
603 static
DupPluginTransformList(struct _cmsContext_struct * ctx,const struct _cmsContext_struct * src)604 void DupPluginTransformList(struct _cmsContext_struct* ctx,
605 const struct _cmsContext_struct* src)
606 {
607 _cmsTransformPluginChunkType newHead = { NULL };
608 _cmsTransformCollection* entry;
609 _cmsTransformCollection* Anterior = NULL;
610 _cmsTransformPluginChunkType* head = (_cmsTransformPluginChunkType*) src->chunks[TransformPlugin];
611
612 // Walk the list copying all nodes
613 for (entry = head->TransformCollection;
614 entry != NULL;
615 entry = entry ->Next) {
616
617 _cmsTransformCollection *newEntry = ( _cmsTransformCollection *) _cmsSubAllocDup(ctx ->MemPool, entry, sizeof(_cmsTransformCollection));
618
619 if (newEntry == NULL)
620 return;
621
622 // We want to keep the linked list order, so this is a little bit tricky
623 newEntry -> Next = NULL;
624 if (Anterior)
625 Anterior -> Next = newEntry;
626
627 Anterior = newEntry;
628
629 if (newHead.TransformCollection == NULL)
630 newHead.TransformCollection = newEntry;
631 }
632
633 ctx ->chunks[TransformPlugin] = _cmsSubAllocDup(ctx->MemPool, &newHead, sizeof(_cmsTransformPluginChunkType));
634 }
635
636 // Allocates memory for transform plugin factory
_cmsAllocTransformPluginChunk(struct _cmsContext_struct * ctx,const struct _cmsContext_struct * src)637 void _cmsAllocTransformPluginChunk(struct _cmsContext_struct* ctx,
638 const struct _cmsContext_struct* src)
639 {
640 if (src != NULL) {
641
642 // Copy all linked list
643 DupPluginTransformList(ctx, src);
644 }
645 else {
646 static _cmsTransformPluginChunkType TransformPluginChunkType = { NULL };
647 ctx ->chunks[TransformPlugin] = _cmsSubAllocDup(ctx ->MemPool, &TransformPluginChunkType, sizeof(_cmsTransformPluginChunkType));
648 }
649 }
650
651 // Adaptor for old versions of plug-in
652 static
_cmsTransform2toTransformAdaptor(struct _cmstransform_struct * CMMcargo,const void * InputBuffer,void * OutputBuffer,cmsUInt32Number PixelsPerLine,cmsUInt32Number LineCount,const cmsStride * Stride)653 void _cmsTransform2toTransformAdaptor(struct _cmstransform_struct *CMMcargo,
654 const void* InputBuffer,
655 void* OutputBuffer,
656 cmsUInt32Number PixelsPerLine,
657 cmsUInt32Number LineCount,
658 const cmsStride* Stride)
659 {
660
661 cmsUInt32Number i, strideIn, strideOut;
662
663 _cmsHandleExtraChannels(CMMcargo, InputBuffer, OutputBuffer, PixelsPerLine, LineCount, Stride);
664
665 strideIn = 0;
666 strideOut = 0;
667
668 for (i = 0; i < LineCount; i++) {
669
670 void *accum = (cmsUInt8Number*)InputBuffer + strideIn;
671 void *output = (cmsUInt8Number*)OutputBuffer + strideOut;
672
673 CMMcargo->OldXform(CMMcargo, accum, output, PixelsPerLine, Stride->BytesPerPlaneIn);
674
675 strideIn += Stride->BytesPerLineIn;
676 strideOut += Stride->BytesPerLineOut;
677 }
678 }
679
680
681
682 // Register new ways to transform
_cmsRegisterTransformPlugin(cmsContext ContextID,cmsPluginBase * Data)683 cmsBool _cmsRegisterTransformPlugin(cmsContext ContextID, cmsPluginBase* Data)
684 {
685 cmsPluginTransform* Plugin = (cmsPluginTransform*) Data;
686 _cmsTransformCollection* fl;
687 _cmsTransformPluginChunkType* ctx = ( _cmsTransformPluginChunkType*) _cmsContextGetClientChunk(ContextID,TransformPlugin);
688
689 if (Data == NULL) {
690
691 // Free the chain. Memory is safely freed at exit
692 ctx->TransformCollection = NULL;
693 return TRUE;
694 }
695
696 // Factory callback is required
697 if (Plugin->factories.xform == NULL) return FALSE;
698
699
700 fl = (_cmsTransformCollection*) _cmsPluginMalloc(ContextID, sizeof(_cmsTransformCollection));
701 if (fl == NULL) return FALSE;
702
703 // Check for full xform plug-ins previous to 2.8, we would need an adapter in that case
704 if (Plugin->base.ExpectedVersion < 2080) {
705
706 fl->OldXform = TRUE;
707 }
708 else
709 fl->OldXform = FALSE;
710
711 // Copy the parameters
712 fl->Factory = Plugin->factories.xform;
713
714 // Keep linked list
715 fl ->Next = ctx->TransformCollection;
716 ctx->TransformCollection = fl;
717
718 // All is ok
719 return TRUE;
720 }
721
722
_cmsSetTransformUserData(struct _cmstransform_struct * CMMcargo,void * ptr,_cmsFreeUserDataFn FreePrivateDataFn)723 void CMSEXPORT _cmsSetTransformUserData(struct _cmstransform_struct *CMMcargo, void* ptr, _cmsFreeUserDataFn FreePrivateDataFn)
724 {
725 _cmsAssert(CMMcargo != NULL);
726 CMMcargo ->UserData = ptr;
727 CMMcargo ->FreeUserData = FreePrivateDataFn;
728 }
729
730 // returns the pointer defined by the plug-in to store private data
_cmsGetTransformUserData(struct _cmstransform_struct * CMMcargo)731 void * CMSEXPORT _cmsGetTransformUserData(struct _cmstransform_struct *CMMcargo)
732 {
733 _cmsAssert(CMMcargo != NULL);
734 return CMMcargo ->UserData;
735 }
736
737 // returns the current formatters
_cmsGetTransformFormatters16(struct _cmstransform_struct * CMMcargo,cmsFormatter16 * FromInput,cmsFormatter16 * ToOutput)738 void CMSEXPORT _cmsGetTransformFormatters16(struct _cmstransform_struct *CMMcargo, cmsFormatter16* FromInput, cmsFormatter16* ToOutput)
739 {
740 _cmsAssert(CMMcargo != NULL);
741 if (FromInput) *FromInput = CMMcargo ->FromInput;
742 if (ToOutput) *ToOutput = CMMcargo ->ToOutput;
743 }
744
_cmsGetTransformFormattersFloat(struct _cmstransform_struct * CMMcargo,cmsFormatterFloat * FromInput,cmsFormatterFloat * ToOutput)745 void CMSEXPORT _cmsGetTransformFormattersFloat(struct _cmstransform_struct *CMMcargo, cmsFormatterFloat* FromInput, cmsFormatterFloat* ToOutput)
746 {
747 _cmsAssert(CMMcargo != NULL);
748 if (FromInput) *FromInput = CMMcargo ->FromInputFloat;
749 if (ToOutput) *ToOutput = CMMcargo ->ToOutputFloat;
750 }
751
752
753 // Allocate transform struct and set it to defaults. Ask the optimization plug-in about if those formats are proper
754 // for separated transforms. If this is the case,
755 static
AllocEmptyTransform(cmsContext ContextID,cmsPipeline * lut,cmsUInt32Number Intent,cmsUInt32Number * InputFormat,cmsUInt32Number * OutputFormat,cmsUInt32Number * dwFlags)756 _cmsTRANSFORM* AllocEmptyTransform(cmsContext ContextID, cmsPipeline* lut,
757 cmsUInt32Number Intent, cmsUInt32Number* InputFormat, cmsUInt32Number* OutputFormat, cmsUInt32Number* dwFlags)
758 {
759 _cmsTransformPluginChunkType* ctx = ( _cmsTransformPluginChunkType*) _cmsContextGetClientChunk(ContextID, TransformPlugin);
760 _cmsTransformCollection* Plugin;
761
762 // Allocate needed memory
763 _cmsTRANSFORM* p = (_cmsTRANSFORM*)_cmsMallocZero(ContextID, sizeof(_cmsTRANSFORM));
764 if (!p) {
765 cmsPipelineFree(lut);
766 return NULL;
767 }
768
769 // Store the proposed pipeline
770 p->Lut = lut;
771
772 // Let's see if any plug-in want to do the transform by itself
773 if (p->Lut != NULL) {
774
775 for (Plugin = ctx->TransformCollection;
776 Plugin != NULL;
777 Plugin = Plugin->Next) {
778
779 if (Plugin->Factory(&p->xform, &p->UserData, &p->FreeUserData, &p->Lut, InputFormat, OutputFormat, dwFlags)) {
780
781 // Last plugin in the declaration order takes control. We just keep
782 // the original parameters as a logging.
783 // Note that cmsFLAGS_CAN_CHANGE_FORMATTER is not set, so by default
784 // an optimized transform is not reusable. The plug-in can, however, change
785 // the flags and make it suitable.
786
787 p->ContextID = ContextID;
788 p->InputFormat = *InputFormat;
789 p->OutputFormat = *OutputFormat;
790 p->dwOriginalFlags = *dwFlags;
791
792 // Fill the formatters just in case the optimized routine is interested.
793 // No error is thrown if the formatter doesn't exist. It is up to the optimization
794 // factory to decide what to do in those cases.
795 p->FromInput = _cmsGetFormatter(ContextID, *InputFormat, cmsFormatterInput, CMS_PACK_FLAGS_16BITS).Fmt16;
796 p->ToOutput = _cmsGetFormatter(ContextID, *OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_16BITS).Fmt16;
797 p->FromInputFloat = _cmsGetFormatter(ContextID, *InputFormat, cmsFormatterInput, CMS_PACK_FLAGS_FLOAT).FmtFloat;
798 p->ToOutputFloat = _cmsGetFormatter(ContextID, *OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_FLOAT).FmtFloat;
799
800 // Save the day?
801 if (Plugin->OldXform) {
802 p->OldXform = (_cmsTransformFn) p->xform;
803 p->xform = _cmsTransform2toTransformAdaptor;
804 }
805
806 return p;
807 }
808 }
809
810 // Not suitable for the transform plug-in, let's check the pipeline plug-in
811 _cmsOptimizePipeline(ContextID, &p->Lut, Intent, InputFormat, OutputFormat, dwFlags);
812 }
813
814 // Check whatever this is a true floating point transform
815 if (_cmsFormatterIsFloat(*InputFormat) && _cmsFormatterIsFloat(*OutputFormat)) {
816
817 // Get formatter function always return a valid union, but the contents of this union may be NULL.
818 p ->FromInputFloat = _cmsGetFormatter(ContextID, *InputFormat, cmsFormatterInput, CMS_PACK_FLAGS_FLOAT).FmtFloat;
819 p ->ToOutputFloat = _cmsGetFormatter(ContextID, *OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_FLOAT).FmtFloat;
820 *dwFlags |= cmsFLAGS_CAN_CHANGE_FORMATTER;
821
822 if (p ->FromInputFloat == NULL || p ->ToOutputFloat == NULL) {
823
824 cmsSignalError(ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported raster format");
825 cmsDeleteTransform(p);
826 return NULL;
827 }
828
829 if (*dwFlags & cmsFLAGS_NULLTRANSFORM) {
830
831 p ->xform = NullFloatXFORM;
832 }
833 else {
834 // Float transforms don't use cache, always are non-NULL
835 p ->xform = FloatXFORM;
836 }
837
838 }
839 else {
840
841 if (*InputFormat == 0 && *OutputFormat == 0) {
842 p ->FromInput = p ->ToOutput = NULL;
843 *dwFlags |= cmsFLAGS_CAN_CHANGE_FORMATTER;
844 }
845 else {
846
847 int BytesPerPixelInput;
848
849 p ->FromInput = _cmsGetFormatter(ContextID, *InputFormat, cmsFormatterInput, CMS_PACK_FLAGS_16BITS).Fmt16;
850 p ->ToOutput = _cmsGetFormatter(ContextID, *OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_16BITS).Fmt16;
851
852 if (p ->FromInput == NULL || p ->ToOutput == NULL) {
853
854 cmsSignalError(ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported raster format");
855 cmsDeleteTransform(p);
856 return NULL;
857 }
858
859 BytesPerPixelInput = T_BYTES(p ->InputFormat);
860 if (BytesPerPixelInput == 0 || BytesPerPixelInput >= 2)
861 *dwFlags |= cmsFLAGS_CAN_CHANGE_FORMATTER;
862
863 }
864
865 if (*dwFlags & cmsFLAGS_NULLTRANSFORM) {
866
867 p ->xform = NullXFORM;
868 }
869 else {
870 if (*dwFlags & cmsFLAGS_NOCACHE) {
871
872 if (*dwFlags & cmsFLAGS_GAMUTCHECK)
873 p ->xform = PrecalculatedXFORMGamutCheck; // Gamut check, no cache
874 else
875 p ->xform = PrecalculatedXFORM; // No cache, no gamut check
876 }
877 else {
878
879 if (*dwFlags & cmsFLAGS_GAMUTCHECK)
880 p ->xform = CachedXFORMGamutCheck; // Gamut check, cache
881 else
882 p ->xform = CachedXFORM; // No gamut check, cache
883
884 }
885 }
886 }
887
888 p ->InputFormat = *InputFormat;
889 p ->OutputFormat = *OutputFormat;
890 p ->dwOriginalFlags = *dwFlags;
891 p ->ContextID = ContextID;
892 p ->UserData = NULL;
893 return p;
894 }
895
896 static
GetXFormColorSpaces(int nProfiles,cmsHPROFILE hProfiles[],cmsColorSpaceSignature * Input,cmsColorSpaceSignature * Output)897 cmsBool GetXFormColorSpaces(int nProfiles, cmsHPROFILE hProfiles[], cmsColorSpaceSignature* Input, cmsColorSpaceSignature* Output)
898 {
899 cmsColorSpaceSignature ColorSpaceIn, ColorSpaceOut;
900 cmsColorSpaceSignature PostColorSpace;
901 int i;
902
903 if (nProfiles <= 0) return FALSE;
904 if (hProfiles[0] == NULL) return FALSE;
905
906 *Input = PostColorSpace = cmsGetColorSpace(hProfiles[0]);
907
908 for (i=0; i < nProfiles; i++) {
909
910 cmsProfileClassSignature cls;
911 cmsHPROFILE hProfile = hProfiles[i];
912
913 int lIsInput = (PostColorSpace != cmsSigXYZData) &&
914 (PostColorSpace != cmsSigLabData);
915
916 if (hProfile == NULL) return FALSE;
917
918 cls = cmsGetDeviceClass(hProfile);
919
920 if (cls == cmsSigNamedColorClass) {
921
922 ColorSpaceIn = cmsSig1colorData;
923 ColorSpaceOut = (nProfiles > 1) ? cmsGetPCS(hProfile) : cmsGetColorSpace(hProfile);
924 }
925 else
926 if (lIsInput || (cls == cmsSigLinkClass)) {
927
928 ColorSpaceIn = cmsGetColorSpace(hProfile);
929 ColorSpaceOut = cmsGetPCS(hProfile);
930 }
931 else
932 {
933 ColorSpaceIn = cmsGetPCS(hProfile);
934 ColorSpaceOut = cmsGetColorSpace(hProfile);
935 }
936
937 if (i==0)
938 *Input = ColorSpaceIn;
939
940 PostColorSpace = ColorSpaceOut;
941 }
942
943 *Output = PostColorSpace;
944
945 return TRUE;
946 }
947
948 // Check colorspace
949 static
IsProperColorSpace(cmsColorSpaceSignature Check,cmsUInt32Number dwFormat)950 cmsBool IsProperColorSpace(cmsColorSpaceSignature Check, cmsUInt32Number dwFormat)
951 {
952 int Space1 = T_COLORSPACE(dwFormat);
953 int Space2 = _cmsLCMScolorSpace(Check);
954
955 if (Space1 == PT_ANY) return TRUE;
956 if (Space1 == Space2) return TRUE;
957
958 if (Space1 == PT_LabV2 && Space2 == PT_Lab) return TRUE;
959 if (Space1 == PT_Lab && Space2 == PT_LabV2) return TRUE;
960
961 return FALSE;
962 }
963
964 // ----------------------------------------------------------------------------------------------------------------
965
966 // Jun-21-2000: Some profiles (those that comes with W2K) comes
967 // with the media white (media black?) x 100. Add a sanity check
968
969 static
NormalizeXYZ(cmsCIEXYZ * Dest)970 void NormalizeXYZ(cmsCIEXYZ* Dest)
971 {
972 while (Dest -> X > 2. &&
973 Dest -> Y > 2. &&
974 Dest -> Z > 2.) {
975
976 Dest -> X /= 10.;
977 Dest -> Y /= 10.;
978 Dest -> Z /= 10.;
979 }
980 }
981
982 static
SetWhitePoint(cmsCIEXYZ * wtPt,const cmsCIEXYZ * src)983 void SetWhitePoint(cmsCIEXYZ* wtPt, const cmsCIEXYZ* src)
984 {
985 if (src == NULL) {
986 wtPt ->X = cmsD50X;
987 wtPt ->Y = cmsD50Y;
988 wtPt ->Z = cmsD50Z;
989 }
990 else {
991 wtPt ->X = src->X;
992 wtPt ->Y = src->Y;
993 wtPt ->Z = src->Z;
994
995 NormalizeXYZ(wtPt);
996 }
997
998 }
999
1000 // 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)1001 cmsHTRANSFORM CMSEXPORT cmsCreateExtendedTransform(cmsContext ContextID,
1002 cmsUInt32Number nProfiles, cmsHPROFILE hProfiles[],
1003 cmsBool BPC[],
1004 cmsUInt32Number Intents[],
1005 cmsFloat64Number AdaptationStates[],
1006 cmsHPROFILE hGamutProfile,
1007 cmsUInt32Number nGamutPCSposition,
1008 cmsUInt32Number InputFormat,
1009 cmsUInt32Number OutputFormat,
1010 cmsUInt32Number dwFlags)
1011 {
1012 _cmsTRANSFORM* xform;
1013 cmsColorSpaceSignature EntryColorSpace;
1014 cmsColorSpaceSignature ExitColorSpace;
1015 cmsPipeline* Lut;
1016 cmsUInt32Number LastIntent = Intents[nProfiles-1];
1017
1018 // If it is a fake transform
1019 if (dwFlags & cmsFLAGS_NULLTRANSFORM)
1020 {
1021 return AllocEmptyTransform(ContextID, NULL, INTENT_PERCEPTUAL, &InputFormat, &OutputFormat, &dwFlags);
1022 }
1023
1024 // If gamut check is requested, make sure we have a gamut profile
1025 if (dwFlags & cmsFLAGS_GAMUTCHECK) {
1026 if (hGamutProfile == NULL) dwFlags &= ~cmsFLAGS_GAMUTCHECK;
1027 }
1028
1029 // On floating point transforms, inhibit cache
1030 if (_cmsFormatterIsFloat(InputFormat) || _cmsFormatterIsFloat(OutputFormat))
1031 dwFlags |= cmsFLAGS_NOCACHE;
1032
1033 // Mark entry/exit spaces
1034 if (!GetXFormColorSpaces(nProfiles, hProfiles, &EntryColorSpace, &ExitColorSpace)) {
1035 cmsSignalError(ContextID, cmsERROR_NULL, "NULL input profiles on transform");
1036 return NULL;
1037 }
1038
1039 // Check if proper colorspaces
1040 if (!IsProperColorSpace(EntryColorSpace, InputFormat)) {
1041 cmsSignalError(ContextID, cmsERROR_COLORSPACE_CHECK, "Wrong input color space on transform");
1042 return NULL;
1043 }
1044
1045 if (!IsProperColorSpace(ExitColorSpace, OutputFormat)) {
1046 cmsSignalError(ContextID, cmsERROR_COLORSPACE_CHECK, "Wrong output color space on transform");
1047 return NULL;
1048 }
1049
1050 // Create a pipeline with all transformations
1051 Lut = _cmsLinkProfiles(ContextID, nProfiles, Intents, hProfiles, BPC, AdaptationStates, dwFlags);
1052 if (Lut == NULL) {
1053 cmsSignalError(ContextID, cmsERROR_NOT_SUITABLE, "Couldn't link the profiles");
1054 return NULL;
1055 }
1056
1057 // Check channel count
1058 if ((cmsChannelsOf(EntryColorSpace) != cmsPipelineInputChannels(Lut)) ||
1059 (cmsChannelsOf(ExitColorSpace) != cmsPipelineOutputChannels(Lut))) {
1060 cmsPipelineFree(Lut);
1061 cmsSignalError(ContextID, cmsERROR_NOT_SUITABLE, "Channel count doesn't match. Profile is corrupted");
1062 return NULL;
1063 }
1064
1065
1066 // All seems ok
1067 xform = AllocEmptyTransform(ContextID, Lut, LastIntent, &InputFormat, &OutputFormat, &dwFlags);
1068 if (xform == NULL) {
1069 return NULL;
1070 }
1071
1072 // Keep values
1073 xform ->EntryColorSpace = EntryColorSpace;
1074 xform ->ExitColorSpace = ExitColorSpace;
1075 xform ->RenderingIntent = Intents[nProfiles-1];
1076
1077 // Take white points
1078 SetWhitePoint(&xform->EntryWhitePoint, (cmsCIEXYZ*) cmsReadTag(hProfiles[0], cmsSigMediaWhitePointTag));
1079 SetWhitePoint(&xform->ExitWhitePoint, (cmsCIEXYZ*) cmsReadTag(hProfiles[nProfiles-1], cmsSigMediaWhitePointTag));
1080
1081
1082 // Create a gamut check LUT if requested
1083 if (hGamutProfile != NULL && (dwFlags & cmsFLAGS_GAMUTCHECK))
1084 xform ->GamutCheck = _cmsCreateGamutCheckPipeline(ContextID, hProfiles,
1085 BPC, Intents,
1086 AdaptationStates,
1087 nGamutPCSposition,
1088 hGamutProfile);
1089
1090
1091 // Try to read input and output colorant table
1092 if (cmsIsTag(hProfiles[0], cmsSigColorantTableTag)) {
1093
1094 // Input table can only come in this way.
1095 xform ->InputColorant = cmsDupNamedColorList((cmsNAMEDCOLORLIST*) cmsReadTag(hProfiles[0], cmsSigColorantTableTag));
1096 }
1097
1098 // Output is a little bit more complex.
1099 if (cmsGetDeviceClass(hProfiles[nProfiles-1]) == cmsSigLinkClass) {
1100
1101 // This tag may exist only on devicelink profiles.
1102 if (cmsIsTag(hProfiles[nProfiles-1], cmsSigColorantTableOutTag)) {
1103
1104 // It may be NULL if error
1105 xform ->OutputColorant = cmsDupNamedColorList((cmsNAMEDCOLORLIST*) cmsReadTag(hProfiles[nProfiles-1], cmsSigColorantTableOutTag));
1106 }
1107
1108 } else {
1109
1110 if (cmsIsTag(hProfiles[nProfiles-1], cmsSigColorantTableTag)) {
1111
1112 xform -> OutputColorant = cmsDupNamedColorList((cmsNAMEDCOLORLIST*) cmsReadTag(hProfiles[nProfiles-1], cmsSigColorantTableTag));
1113 }
1114 }
1115
1116 // Store the sequence of profiles
1117 if (dwFlags & cmsFLAGS_KEEP_SEQUENCE) {
1118 xform ->Sequence = _cmsCompileProfileSequence(ContextID, nProfiles, hProfiles);
1119 }
1120 else
1121 xform ->Sequence = NULL;
1122
1123 // If this is a cached transform, init first value, which is zero (16 bits only)
1124 if (!(dwFlags & cmsFLAGS_NOCACHE)) {
1125
1126 memset(&xform ->Cache.CacheIn, 0, sizeof(xform ->Cache.CacheIn));
1127
1128 if (xform ->GamutCheck != NULL) {
1129 TransformOnePixelWithGamutCheck(xform, xform ->Cache.CacheIn, xform->Cache.CacheOut);
1130 }
1131 else {
1132
1133 xform ->Lut ->Eval16Fn(xform ->Cache.CacheIn, xform->Cache.CacheOut, xform -> Lut->Data);
1134 }
1135
1136 }
1137
1138 return (cmsHTRANSFORM) xform;
1139 }
1140
1141 // Multiprofile transforms: Gamut check is not available here, as it is unclear from which profile the gamut comes.
cmsCreateMultiprofileTransformTHR(cmsContext ContextID,cmsHPROFILE hProfiles[],cmsUInt32Number nProfiles,cmsUInt32Number InputFormat,cmsUInt32Number OutputFormat,cmsUInt32Number Intent,cmsUInt32Number dwFlags)1142 cmsHTRANSFORM CMSEXPORT cmsCreateMultiprofileTransformTHR(cmsContext ContextID,
1143 cmsHPROFILE hProfiles[],
1144 cmsUInt32Number nProfiles,
1145 cmsUInt32Number InputFormat,
1146 cmsUInt32Number OutputFormat,
1147 cmsUInt32Number Intent,
1148 cmsUInt32Number dwFlags)
1149 {
1150 cmsUInt32Number i;
1151 cmsBool BPC[256];
1152 cmsUInt32Number Intents[256];
1153 cmsFloat64Number AdaptationStates[256];
1154
1155 if (nProfiles <= 0 || nProfiles > 255) {
1156 cmsSignalError(ContextID, cmsERROR_RANGE, "Wrong number of profiles. 1..255 expected, %d found.", nProfiles);
1157 return NULL;
1158 }
1159
1160 for (i=0; i < nProfiles; i++) {
1161 BPC[i] = dwFlags & cmsFLAGS_BLACKPOINTCOMPENSATION ? TRUE : FALSE;
1162 Intents[i] = Intent;
1163 AdaptationStates[i] = cmsSetAdaptationStateTHR(ContextID, -1);
1164 }
1165
1166
1167 return cmsCreateExtendedTransform(ContextID, nProfiles, hProfiles, BPC, Intents, AdaptationStates, NULL, 0, InputFormat, OutputFormat, dwFlags);
1168 }
1169
1170
1171
cmsCreateMultiprofileTransform(cmsHPROFILE hProfiles[],cmsUInt32Number nProfiles,cmsUInt32Number InputFormat,cmsUInt32Number OutputFormat,cmsUInt32Number Intent,cmsUInt32Number dwFlags)1172 cmsHTRANSFORM CMSEXPORT cmsCreateMultiprofileTransform(cmsHPROFILE hProfiles[],
1173 cmsUInt32Number nProfiles,
1174 cmsUInt32Number InputFormat,
1175 cmsUInt32Number OutputFormat,
1176 cmsUInt32Number Intent,
1177 cmsUInt32Number dwFlags)
1178 {
1179
1180 if (nProfiles <= 0 || nProfiles > 255) {
1181 cmsSignalError(NULL, cmsERROR_RANGE, "Wrong number of profiles. 1..255 expected, %d found.", nProfiles);
1182 return NULL;
1183 }
1184
1185 return cmsCreateMultiprofileTransformTHR(cmsGetProfileContextID(hProfiles[0]),
1186 hProfiles,
1187 nProfiles,
1188 InputFormat,
1189 OutputFormat,
1190 Intent,
1191 dwFlags);
1192 }
1193
cmsCreateTransformTHR(cmsContext ContextID,cmsHPROFILE Input,cmsUInt32Number InputFormat,cmsHPROFILE Output,cmsUInt32Number OutputFormat,cmsUInt32Number Intent,cmsUInt32Number dwFlags)1194 cmsHTRANSFORM CMSEXPORT cmsCreateTransformTHR(cmsContext ContextID,
1195 cmsHPROFILE Input,
1196 cmsUInt32Number InputFormat,
1197 cmsHPROFILE Output,
1198 cmsUInt32Number OutputFormat,
1199 cmsUInt32Number Intent,
1200 cmsUInt32Number dwFlags)
1201 {
1202
1203 cmsHPROFILE hArray[2];
1204
1205 hArray[0] = Input;
1206 hArray[1] = Output;
1207
1208 return cmsCreateMultiprofileTransformTHR(ContextID, hArray, Output == NULL ? 1 : 2, InputFormat, OutputFormat, Intent, dwFlags);
1209 }
1210
cmsCreateTransform(cmsHPROFILE Input,cmsUInt32Number InputFormat,cmsHPROFILE Output,cmsUInt32Number OutputFormat,cmsUInt32Number Intent,cmsUInt32Number dwFlags)1211 CMSAPI cmsHTRANSFORM CMSEXPORT cmsCreateTransform(cmsHPROFILE Input,
1212 cmsUInt32Number InputFormat,
1213 cmsHPROFILE Output,
1214 cmsUInt32Number OutputFormat,
1215 cmsUInt32Number Intent,
1216 cmsUInt32Number dwFlags)
1217 {
1218 return cmsCreateTransformTHR(cmsGetProfileContextID(Input), Input, InputFormat, Output, OutputFormat, Intent, dwFlags);
1219 }
1220
1221
cmsCreateProofingTransformTHR(cmsContext ContextID,cmsHPROFILE InputProfile,cmsUInt32Number InputFormat,cmsHPROFILE OutputProfile,cmsUInt32Number OutputFormat,cmsHPROFILE ProofingProfile,cmsUInt32Number nIntent,cmsUInt32Number ProofingIntent,cmsUInt32Number dwFlags)1222 cmsHTRANSFORM CMSEXPORT cmsCreateProofingTransformTHR(cmsContext ContextID,
1223 cmsHPROFILE InputProfile,
1224 cmsUInt32Number InputFormat,
1225 cmsHPROFILE OutputProfile,
1226 cmsUInt32Number OutputFormat,
1227 cmsHPROFILE ProofingProfile,
1228 cmsUInt32Number nIntent,
1229 cmsUInt32Number ProofingIntent,
1230 cmsUInt32Number dwFlags)
1231 {
1232 cmsHPROFILE hArray[4];
1233 cmsUInt32Number Intents[4];
1234 cmsBool BPC[4];
1235 cmsFloat64Number Adaptation[4];
1236 cmsBool DoBPC = (dwFlags & cmsFLAGS_BLACKPOINTCOMPENSATION) ? TRUE : FALSE;
1237
1238
1239 hArray[0] = InputProfile; hArray[1] = ProofingProfile; hArray[2] = ProofingProfile; hArray[3] = OutputProfile;
1240 Intents[0] = nIntent; Intents[1] = nIntent; Intents[2] = INTENT_RELATIVE_COLORIMETRIC; Intents[3] = ProofingIntent;
1241 BPC[0] = DoBPC; BPC[1] = DoBPC; BPC[2] = 0; BPC[3] = 0;
1242
1243 Adaptation[0] = Adaptation[1] = Adaptation[2] = Adaptation[3] = cmsSetAdaptationStateTHR(ContextID, -1);
1244
1245 if (!(dwFlags & (cmsFLAGS_SOFTPROOFING|cmsFLAGS_GAMUTCHECK)))
1246 return cmsCreateTransformTHR(ContextID, InputProfile, InputFormat, OutputProfile, OutputFormat, nIntent, dwFlags);
1247
1248 return cmsCreateExtendedTransform(ContextID, 4, hArray, BPC, Intents, Adaptation,
1249 ProofingProfile, 1, InputFormat, OutputFormat, dwFlags);
1250
1251 }
1252
1253
cmsCreateProofingTransform(cmsHPROFILE InputProfile,cmsUInt32Number InputFormat,cmsHPROFILE OutputProfile,cmsUInt32Number OutputFormat,cmsHPROFILE ProofingProfile,cmsUInt32Number nIntent,cmsUInt32Number ProofingIntent,cmsUInt32Number dwFlags)1254 cmsHTRANSFORM CMSEXPORT cmsCreateProofingTransform(cmsHPROFILE InputProfile,
1255 cmsUInt32Number InputFormat,
1256 cmsHPROFILE OutputProfile,
1257 cmsUInt32Number OutputFormat,
1258 cmsHPROFILE ProofingProfile,
1259 cmsUInt32Number nIntent,
1260 cmsUInt32Number ProofingIntent,
1261 cmsUInt32Number dwFlags)
1262 {
1263 return cmsCreateProofingTransformTHR(cmsGetProfileContextID(InputProfile),
1264 InputProfile,
1265 InputFormat,
1266 OutputProfile,
1267 OutputFormat,
1268 ProofingProfile,
1269 nIntent,
1270 ProofingIntent,
1271 dwFlags);
1272 }
1273
1274
1275 // Grab the ContextID from an open transform. Returns NULL if a NULL transform is passed
cmsGetTransformContextID(cmsHTRANSFORM hTransform)1276 cmsContext CMSEXPORT cmsGetTransformContextID(cmsHTRANSFORM hTransform)
1277 {
1278 _cmsTRANSFORM* xform = (_cmsTRANSFORM*) hTransform;
1279
1280 if (xform == NULL) return NULL;
1281 return xform -> ContextID;
1282 }
1283
1284 // Grab the input/output formats
cmsGetTransformInputFormat(cmsHTRANSFORM hTransform)1285 cmsUInt32Number CMSEXPORT cmsGetTransformInputFormat(cmsHTRANSFORM hTransform)
1286 {
1287 _cmsTRANSFORM* xform = (_cmsTRANSFORM*) hTransform;
1288
1289 if (xform == NULL) return 0;
1290 return xform->InputFormat;
1291 }
1292
cmsGetTransformOutputFormat(cmsHTRANSFORM hTransform)1293 cmsUInt32Number CMSEXPORT cmsGetTransformOutputFormat(cmsHTRANSFORM hTransform)
1294 {
1295 _cmsTRANSFORM* xform = (_cmsTRANSFORM*) hTransform;
1296
1297 if (xform == NULL) return 0;
1298 return xform->OutputFormat;
1299 }
1300
1301 // For backwards compatibility
cmsChangeBuffersFormat(cmsHTRANSFORM hTransform,cmsUInt32Number InputFormat,cmsUInt32Number OutputFormat)1302 cmsBool CMSEXPORT cmsChangeBuffersFormat(cmsHTRANSFORM hTransform,
1303 cmsUInt32Number InputFormat,
1304 cmsUInt32Number OutputFormat)
1305 {
1306 _cmsTRANSFORM* xform = (_cmsTRANSFORM*) hTransform;
1307 cmsFormatter16 FromInput, ToOutput;
1308
1309
1310 // We only can afford to change formatters if previous transform is at least 16 bits
1311 if (!(xform ->dwOriginalFlags & cmsFLAGS_CAN_CHANGE_FORMATTER)) {
1312
1313 cmsSignalError(xform ->ContextID, cmsERROR_NOT_SUITABLE, "cmsChangeBuffersFormat works only on transforms created originally with at least 16 bits of precision");
1314 return FALSE;
1315 }
1316
1317 FromInput = _cmsGetFormatter(xform->ContextID, InputFormat, cmsFormatterInput, CMS_PACK_FLAGS_16BITS).Fmt16;
1318 ToOutput = _cmsGetFormatter(xform->ContextID, OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_16BITS).Fmt16;
1319
1320 if (FromInput == NULL || ToOutput == NULL) {
1321
1322 cmsSignalError(xform -> ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported raster format");
1323 return FALSE;
1324 }
1325
1326 xform ->InputFormat = InputFormat;
1327 xform ->OutputFormat = OutputFormat;
1328 xform ->FromInput = FromInput;
1329 xform ->ToOutput = ToOutput;
1330 return TRUE;
1331 }
1332