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