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