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