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