Coverage Report

Created: 2025-06-10 07:27

/src/ghostpdl/base/gsicc_lcms2mt.c
Line
Count
Source (jump to first uncovered line)
1
/* Copyright (C) 2001-2023 Artifex Software, Inc.
2
   All Rights Reserved.
3
4
   This software is provided AS-IS with no warranty, either express or
5
   implied.
6
7
   This software is distributed under license and may not be copied,
8
   modified or distributed except as expressly authorized under the terms
9
   of the license contained in the file LICENSE in this distribution.
10
11
   Refer to licensing information at http://www.artifex.com or contact
12
   Artifex Software, Inc.,  39 Mesa Street, Suite 108A, San Francisco,
13
   CA 94129, USA, for further information.
14
*/
15
16
17
/* gsicc interface to LittleCMS2-MT */
18
19
#include "memory_.h"
20
#include "lcms2mt.h"
21
#include "lcms2mt_plugin.h"
22
#include "gslibctx.h"
23
#include "gserrors.h"
24
#include "gp.h"
25
#include "gsicc_cms.h"
26
#include "gxdevice.h"
27
28
#ifdef WITH_CAL
29
#include "cal.h"
30
#endif
31
32
#define USE_LCMS2_LOCKING
33
34
#ifdef USE_LCMS2_LOCKING
35
#include "gxsync.h"
36
#endif
37
38
#define DUMP_CMS_BUFFER 0
39
#define DEBUG_LCMS_MEM 0
40
#define LCMS_BYTES_MASK T_BYTES(-1) /* leaves only mask for the BYTES (currently 7) */
41
#define LCMS_ENDIAN16_MASK T_ENDIAN16(-1) /* similarly, for ENDIAN16 bit */
42
43
#define gsicc_link_flags(hasalpha, planarIN, planarOUT, endianswapIN, endianswapOUT, bytesIN, bytesOUT) \
44
6.83M
    ((hasalpha != 0) << 2 | \
45
6.83M
     (planarIN != 0) << 5 | (planarOUT != 0) << 4 | \
46
6.83M
     (endianswapIN != 0) << 3 | (endianswapOUT != 0) << 2 | \
47
6.83M
     (bytesIN == 1) << 1 | (bytesOUT == 1))
48
49
typedef struct gsicc_lcms2mt_link_list_s {
50
    int flags;
51
    cmsHTRANSFORM *hTransform;
52
    struct gsicc_lcms2mt_link_list_s *next;
53
} gsicc_lcms2mt_link_list_t;
54
55
/* Only provide warning about issues in lcms if debug build */
56
static void
57
gscms_error(cmsContext       ContextID,
58
            cmsUInt32Number  error_code,
59
            const char      *error_text)
60
28.1k
{
61
#ifdef DEBUG
62
    gs_warn1("cmm error : %s",error_text);
63
#endif
64
28.1k
}
65
66
static
67
void *gs_lcms2_malloc(cmsContext id, unsigned int size)
68
7.99M
{
69
7.99M
    void *ptr;
70
7.99M
    gs_memory_t *mem = (gs_memory_t *)cmsGetContextUserData(id);
71
72
#if defined(SHARE_LCMS) && SHARE_LCMS==1
73
    ptr = malloc(size);
74
#else
75
7.99M
    ptr = gs_alloc_bytes(mem, size, "lcms");
76
7.99M
#endif
77
78
#if DEBUG_LCMS_MEM
79
    gs_warn2("lcms malloc (%d) at 0x%x",size,ptr);
80
#endif
81
7.99M
    return ptr;
82
7.99M
}
83
84
static
85
void gs_lcms2_free(cmsContext id, void *ptr)
86
7.99M
{
87
7.99M
    gs_memory_t *mem = (gs_memory_t *)cmsGetContextUserData(id);
88
7.99M
    if (ptr != NULL) {
89
#if DEBUG_LCMS_MEM
90
        gs_warn1("lcms free at 0x%x",ptr);
91
#endif
92
93
#if defined(SHARE_LCMS) && SHARE_LCMS==1
94
        free(ptr);
95
#else
96
7.99M
        gs_free_object(mem, ptr, "lcms");
97
7.99M
#endif
98
7.99M
    }
99
7.99M
}
100
101
static
102
void *gs_lcms2_realloc(cmsContext id, void *ptr, unsigned int size)
103
256k
{
104
256k
    gs_memory_t *mem = (gs_memory_t *)cmsGetContextUserData(id);
105
256k
    void *ptr2;
106
107
256k
    if (ptr == 0)
108
256k
        return gs_lcms2_malloc(id, size);
109
0
    if (size == 0)
110
0
    {
111
0
        gs_lcms2_free(id, ptr);
112
0
        return NULL;
113
0
    }
114
#if defined(SHARE_LCMS) && SHARE_LCMS==1
115
    ptr2 = realloc(ptr, size);
116
#else
117
0
    ptr2 = gs_resize_object(mem, ptr, size, "lcms");
118
0
#endif
119
120
#if DEBUG_LCMS_MEM
121
    gs_warn3("lcms realloc (%x,%d) at 0x%x",ptr,size,ptr2);
122
#endif
123
0
    return ptr2;
124
0
}
125
126
static cmsPluginMemHandler gs_cms_memhandler =
127
{
128
    {
129
        cmsPluginMagicNumber,
130
        LCMS_VERSION,
131
        cmsPluginMemHandlerSig,
132
        NULL
133
    },
134
    gs_lcms2_malloc,
135
    gs_lcms2_free,
136
    gs_lcms2_realloc,
137
    NULL,
138
    NULL,
139
    NULL,
140
};
141
142
#ifdef USE_LCMS2_LOCKING
143
144
static
145
void *gs_lcms2_createMutex(cmsContext id)
146
115k
{
147
115k
    gs_memory_t *mem = (gs_memory_t *)cmsGetContextUserData(id);
148
149
115k
    return gx_monitor_label(gx_monitor_alloc(mem), "lcms2");
150
115k
}
151
152
static
153
void gs_lcms2_destroyMutex(cmsContext id, void* mtx)
154
115k
{
155
115k
    gx_monitor_free((gx_monitor_t *)mtx);
156
115k
}
157
158
static
159
cmsBool gs_lcms2_lockMutex(cmsContext id, void* mtx)
160
1.07M
{
161
1.07M
    return !gx_monitor_enter((gx_monitor_t *)mtx);
162
1.07M
}
163
164
static
165
void gs_lcms2_unlockMutex(cmsContext id, void* mtx)
166
1.07M
{
167
1.07M
    gx_monitor_leave((gx_monitor_t *)mtx);
168
1.07M
}
169
170
static cmsPluginMutex gs_cms_mutexhandler =
171
{
172
    {
173
        cmsPluginMagicNumber,
174
        LCMS_VERSION,
175
        cmsPluginMutexSig,
176
        NULL
177
    },
178
    gs_lcms2_createMutex,
179
    gs_lcms2_destroyMutex,
180
    gs_lcms2_lockMutex,
181
    gs_lcms2_unlockMutex
182
};
183
184
#endif
185
186
static int
187
gscms_get_accuracy(gs_memory_t *mem)
188
25.7k
{
189
25.7k
    gs_lib_ctx_t *ctx = gs_lib_ctx_get_interp_instance(mem);
190
191
25.7k
    switch (ctx->icc_color_accuracy) {
192
0
    case 0:
193
0
        return cmsFLAGS_LOWRESPRECALC;
194
0
    case 1:
195
0
        return 0;
196
25.7k
    case 2:
197
25.7k
    default:
198
25.7k
        return cmsFLAGS_HIGHRESPRECALC;
199
25.7k
    }
200
25.7k
}
201
202
/* Get the number of channels for the profile.
203
  Input count */
204
int
205
gscms_get_input_channel_count(gcmmhprofile_t profile, gs_memory_t *memory)
206
48.5k
{
207
48.5k
    cmsColorSpaceSignature colorspace;
208
48.5k
    cmsContext ctx = gs_lib_ctx_get_cms_context(memory);
209
210
48.5k
    colorspace = cmsGetColorSpace(ctx, profile);
211
48.5k
    return cmsChannelsOf(ctx, colorspace);
212
48.5k
}
213
214
/* Get the number of output channels for the profile */
215
int
216
gscms_get_output_channel_count(gcmmhprofile_t profile, gs_memory_t *memory)
217
48.5k
{
218
48.5k
    cmsColorSpaceSignature colorspace;
219
48.5k
    cmsContext ctx = gs_lib_ctx_get_cms_context(memory);
220
221
48.5k
    colorspace = cmsGetPCS(ctx, profile);
222
48.5k
    return cmsChannelsOf(ctx, colorspace);
223
48.5k
}
224
225
/* Get the number of colorant names in the clrt tag */
226
int
227
gscms_get_numberclrtnames(gcmmhprofile_t profile, gs_memory_t *memory)
228
0
{
229
0
    cmsNAMEDCOLORLIST *lcms_names;
230
0
    cmsContext ctx = gs_lib_ctx_get_cms_context(memory);
231
232
0
    lcms_names = (cmsNAMEDCOLORLIST *)cmsReadTag(ctx, profile,
233
0
        cmsSigColorantTableTag);
234
0
    return cmsNamedColorCount(ctx, lcms_names);
235
0
}
236
237
/* Get the nth colorant name in the clrt tag */
238
char*
239
gscms_get_clrtname(gcmmhprofile_t profile, int colorcount, gs_memory_t *memory)
240
0
{
241
0
    cmsNAMEDCOLORLIST *lcms_names;
242
0
    char name[256];
243
0
    char *buf;
244
0
    int length;
245
0
    cmsContext ctx = gs_lib_ctx_get_cms_context(memory);
246
247
0
    lcms_names = (cmsNAMEDCOLORLIST *)cmsReadTag(ctx, profile,
248
0
                                                 cmsSigColorantTableTag);
249
0
    if (colorcount >= cmsNamedColorCount(ctx, lcms_names)) return(NULL);
250
0
    if (cmsNamedColorInfo(ctx, lcms_names, colorcount, name, NULL, NULL, NULL,
251
0
                          NULL) == 0)
252
0
        return NULL;
253
0
    length = strlen(name);
254
0
    buf = (char*) gs_alloc_bytes(memory, length + 1, "gscms_get_clrtname");
255
0
    if (buf)
256
0
        strcpy(buf, name);
257
0
    return buf;
258
0
}
259
260
/* Check if the profile is a device link type */
261
bool
262
gscms_is_device_link(gcmmhprofile_t profile, gs_memory_t *memory)
263
0
{
264
0
    cmsContext ctx = gs_lib_ctx_get_cms_context(memory);
265
266
0
    return cmsGetDeviceClass(ctx, profile) == cmsSigLinkClass;
267
0
}
268
269
/* Needed for v2 profile creation */
270
int
271
gscms_get_device_class(gcmmhprofile_t profile, gs_memory_t *memory)
272
0
{
273
0
    cmsContext ctx = gs_lib_ctx_get_cms_context(memory);
274
275
0
    return cmsGetDeviceClass(ctx, profile);
276
0
}
277
278
/* Check if the profile is a input type */
279
bool
280
gscms_is_input(gcmmhprofile_t profile, gs_memory_t *memory)
281
0
{
282
0
    cmsContext ctx = gs_lib_ctx_get_cms_context(memory);
283
284
0
    return cmsGetDeviceClass(ctx, profile) == cmsSigInputClass;
285
0
}
286
287
/* Get the device space associated with this profile */
288
gsicc_colorbuffer_t
289
gscms_get_profile_data_space(gcmmhprofile_t profile, gs_memory_t *memory)
290
49.6k
{
291
49.6k
    cmsColorSpaceSignature colorspace;
292
49.6k
    cmsContext ctx = gs_lib_ctx_get_cms_context(memory);
293
294
49.6k
    colorspace = cmsGetColorSpace(ctx, profile);
295
49.6k
    switch (colorspace) {
296
0
        case cmsSigXYZData:
297
0
            return gsCIEXYZ;
298
14
        case cmsSigLabData:
299
14
            return gsCIELAB;
300
14.8k
        case cmsSigRgbData:
301
14.8k
            return gsRGB;
302
33.8k
        case cmsSigGrayData:
303
33.8k
            return gsGRAY;
304
959
        case cmsSigCmykData:
305
959
            return gsCMYK;
306
9
        default:
307
9
            return gsNCHANNEL;
308
49.6k
    }
309
49.6k
}
310
311
/* Get ICC Profile handle from buffer */
312
gcmmhprofile_t
313
gscms_get_profile_handle_mem(unsigned char *buffer, unsigned int input_size,
314
    gs_memory_t *mem)
315
50.8k
{
316
50.8k
    cmsContext ctx = gs_lib_ctx_get_cms_context(mem);
317
318
50.8k
    cmsSetLogErrorHandler(ctx, gscms_error);
319
50.8k
    return cmsOpenProfileFromMem(ctx,buffer,input_size);
320
50.8k
}
321
322
/* Get ICC Profile handle from file ptr */
323
gcmmhprofile_t
324
gscms_get_profile_handle_file(const char *filename, gs_memory_t *mem)
325
0
{
326
0
    cmsContext ctx = gs_lib_ctx_get_cms_context(mem);
327
328
0
    return cmsOpenProfileFromFile(ctx, filename, "r");
329
0
}
330
331
/* Transform an entire buffer */
332
int
333
gscms_transform_color_buffer(gx_device *dev, gsicc_link_t *icclink,
334
                             gsicc_bufferdesc_t *input_buff_desc,
335
                             gsicc_bufferdesc_t *output_buff_desc,
336
                             void *inputbuffer, void *outputbuffer)
337
129k
{
338
129k
    gsicc_lcms2mt_link_list_t *link_handle = (gsicc_lcms2mt_link_list_t *)(icclink->link_handle);
339
129k
    cmsHTRANSFORM hTransform = (cmsHTRANSFORM)link_handle->hTransform;
340
129k
    cmsUInt32Number dwInputFormat, dwOutputFormat, num_src_lcms, num_des_lcms;
341
129k
    int  hasalpha, planarIN, planarOUT, numbytesIN, numbytesOUT, swap_endianIN, swap_endianOUT;
342
129k
    int needed_flags = 0;
343
129k
    unsigned char *inputpos, *outputpos;
344
129k
    cmsContext ctx = gs_lib_ctx_get_cms_context(icclink->memory);
345
346
#if DUMP_CMS_BUFFER
347
    gp_file *fid_in, *fid_out;
348
#endif
349
    /* Although little CMS does  make assumptions about data types in its
350
       transformations we can change it after the fact by cloning from any
351
       other transform. We always create [0] which is no_alpha, chunky IN/OUT,
352
       no endian swap IN/OUT, 2-bytes_per_component IN/OUT. */
353
    /* Set us to the proper output type */
354
    /* Note, we could speed this up by passing back the encoded data type
355
        to the caller so that we could avoid having to go through this
356
        computation each time if they are doing multiple calls to this
357
        operation, but this is working on a buffer at a time. */
358
359
    /* Now set if we have planar, num bytes, endian case, and alpha data to skip */
360
    /* Planar -- pdf14 case for example */
361
129k
    planarIN = input_buff_desc->is_planar;
362
129k
    planarOUT = output_buff_desc->is_planar;
363
364
    /* 8 or 16 byte input and output */
365
129k
    numbytesIN = input_buff_desc->bytes_per_chan;
366
129k
    numbytesOUT = output_buff_desc->bytes_per_chan;
367
129k
    if (numbytesIN > 2 || numbytesOUT > 2)
368
0
        return_error(gs_error_rangecheck);  /* TODO: we don't support float */
369
370
    /* endian */
371
129k
    swap_endianIN = input_buff_desc->endian_swap;
372
129k
    swap_endianOUT = output_buff_desc->endian_swap;
373
374
    /* alpha, which is passed through unmolested */
375
    /* TODO:  Right now we always must have alpha last */
376
    /* This is really only going to be an issue when we have interleaved alpha data */
377
129k
    hasalpha = input_buff_desc->has_alpha;
378
379
129k
    needed_flags = gsicc_link_flags(hasalpha, planarIN, planarOUT,
380
129k
                                    swap_endianIN, swap_endianOUT,
381
129k
                                    numbytesIN, numbytesOUT);
382
258k
    while (link_handle->flags != needed_flags) {
383
129k
        if (link_handle->next == NULL) {
384
167
            hTransform = NULL;
385
167
            break;
386
128k
        } else {
387
128k
            link_handle = link_handle->next;
388
128k
            hTransform = link_handle->hTransform;
389
128k
        }
390
129k
    }
391
129k
    if (hTransform == NULL) {
392
        /* the variant we want wasn't present, clone it from the last on the list */
393
167
        gsicc_lcms2mt_link_list_t *new_link_handle =
394
167
            (gsicc_lcms2mt_link_list_t *)gs_alloc_bytes(icclink->memory->non_gc_memory,
395
167
                                                         sizeof(gsicc_lcms2mt_link_list_t),
396
167
                                                         "gscms_transform_color_buffer");
397
167
        if (new_link_handle == NULL) {
398
0
            return_error(gs_error_VMerror);
399
0
        }
400
167
        new_link_handle->next = NULL;   /* new end of list */
401
167
        new_link_handle->flags = needed_flags;
402
167
        hTransform = link_handle->hTransform; /* doesn't really matter which we start with */
403
        /* Color space MUST be the same */
404
167
        dwInputFormat = COLORSPACE_SH(T_COLORSPACE(cmsGetTransformInputFormat(ctx, hTransform)));
405
167
        dwOutputFormat = COLORSPACE_SH(T_COLORSPACE(cmsGetTransformOutputFormat(ctx, hTransform)));
406
        /* number of channels.  This should not really be changing! */
407
167
        num_src_lcms = T_CHANNELS(cmsGetTransformInputFormat(ctx, hTransform));
408
167
        num_des_lcms = T_CHANNELS(cmsGetTransformOutputFormat(ctx, hTransform));
409
167
        if (num_src_lcms != input_buff_desc->num_chan ||
410
167
            num_des_lcms != output_buff_desc->num_chan) {
411
            /* We can't transform this. Someone is doing something odd */
412
0
            return_error(gs_error_unknownerror);
413
0
        }
414
167
        dwInputFormat = dwInputFormat | CHANNELS_SH(num_src_lcms);
415
167
        dwOutputFormat = dwOutputFormat | CHANNELS_SH(num_des_lcms);
416
        /* set the remaining parameters, alpha, planar, num_bytes */
417
167
        dwInputFormat = dwInputFormat | EXTRA_SH(hasalpha);
418
167
        dwOutputFormat = dwOutputFormat | EXTRA_SH(hasalpha);
419
167
        dwInputFormat = dwInputFormat | PLANAR_SH(planarIN);
420
167
        dwOutputFormat = dwOutputFormat | PLANAR_SH(planarOUT);
421
167
        dwInputFormat = dwInputFormat | ENDIAN16_SH(swap_endianIN);
422
167
        dwOutputFormat = dwOutputFormat | ENDIAN16_SH(swap_endianOUT);
423
167
        dwInputFormat = dwInputFormat | BYTES_SH(numbytesIN);
424
167
        dwOutputFormat = dwOutputFormat | BYTES_SH(numbytesOUT);
425
426
167
        hTransform = cmsCloneTransformChangingFormats(ctx, hTransform, dwInputFormat, dwOutputFormat);
427
167
        if (hTransform == NULL)
428
0
            return_error(gs_error_unknownerror);
429
        /* Now we have a new hTransform to add to the list, BUT some other thread */
430
        /* may have been working in the same one. Lock, check again and add to    */
431
        /* the (potentially new) end of the list */
432
167
        gx_monitor_enter(icclink->lock);
433
167
        while (link_handle->next != NULL) {
434
0
            if (link_handle->flags == needed_flags) {
435
                /* OOPS. Someone else did it while we were building it */
436
0
                cmsDeleteTransform(ctx, hTransform);
437
0
                hTransform = link_handle->hTransform;
438
0
                new_link_handle = NULL;
439
0
                break;
440
0
            }
441
0
            link_handle = link_handle->next;
442
0
        }
443
167
        gx_monitor_leave(icclink->lock);
444
167
        if (new_link_handle != NULL) {
445
167
            new_link_handle->hTransform = hTransform;
446
167
            link_handle->next = new_link_handle;    /* link to end of list */
447
167
        }
448
167
    }
449
450
129k
    inputpos = (byte *) inputbuffer;
451
129k
    outputpos = (byte *) outputbuffer;
452
129k
    cmsDoTransformLineStride(ctx, hTransform,
453
129k
        inputpos, outputpos, input_buff_desc->pixels_per_row,
454
129k
        input_buff_desc->num_rows, input_buff_desc->row_stride,
455
129k
        output_buff_desc->row_stride, input_buff_desc->plane_stride,
456
129k
        output_buff_desc->plane_stride);
457
458
#if DUMP_CMS_BUFFER
459
    fid_in = gp_fopen(icclink->memory,"CM_Input.raw","ab");
460
    fid_out = gp_fopen(icclink->memory,"CM_Output.raw","ab");
461
    fwrite((unsigned char*) inputbuffer,sizeof(unsigned char),
462
                            input_buff_desc->row_stride,fid_in);
463
    fwrite((unsigned char*) outputbuffer,sizeof(unsigned char),
464
                            output_buff_desc->row_stride,fid_out);
465
    fclose(fid_in);
466
    fclose(fid_out);
467
#endif
468
129k
    return 0;
469
129k
}
470
471
/* Transform a single color. We assume we have passed to us the proper number
472
   of elements of size gx_device_color. It is up to the caller to make sure
473
   the proper allocations for the colors are there. */
474
int
475
gscms_transform_color(gx_device *dev, gsicc_link_t *icclink, void *inputcolor,
476
                             void *outputcolor, int num_bytes)
477
6.68M
{
478
6.68M
    return gscms_transform_color_const(dev, icclink, inputcolor, outputcolor, num_bytes);
479
6.68M
}
480
481
int
482
gscms_transform_color_const(const gx_device *dev, gsicc_link_t *icclink, void *inputcolor,
483
                             void *outputcolor, int num_bytes)
484
6.68M
{
485
6.68M
    gsicc_lcms2mt_link_list_t *link_handle = (gsicc_lcms2mt_link_list_t *)(icclink->link_handle);
486
6.68M
    cmsHTRANSFORM hTransform = (cmsHTRANSFORM)link_handle->hTransform;
487
6.68M
    cmsUInt32Number dwInputFormat, dwOutputFormat;
488
6.68M
    cmsContext ctx = gs_lib_ctx_get_cms_context(icclink->memory);
489
6.68M
    int big_endianIN, big_endianOUT, needed_flags;
490
491
    /* For a single color, we are going to use the link as it is
492
       with the exception of taking care of the word size. */
493
6.68M
    if (num_bytes > 2)
494
0
        return_error(gs_error_rangecheck);  /* TODO: we don't support float */
495
496
6.68M
    dwInputFormat = cmsGetTransformInputFormat(ctx, hTransform);
497
6.68M
    big_endianIN = T_ENDIAN16(dwInputFormat);
498
6.68M
    dwOutputFormat = cmsGetTransformOutputFormat(ctx, hTransform);
499
6.68M
    big_endianOUT = T_ENDIAN16(dwOutputFormat);
500
501
6.68M
    needed_flags = gsicc_link_flags(0, 0, 0, /* alpha and planar not used for single color */
502
6.68M
                                    big_endianIN, big_endianOUT,
503
6.68M
                                    num_bytes, num_bytes);
504
6.68M
    while (link_handle->flags != needed_flags) {
505
0
        if (link_handle->next == NULL) {
506
0
            hTransform = NULL;
507
0
            break;
508
0
        } else {
509
0
            link_handle = link_handle->next;
510
0
            hTransform = link_handle->hTransform;
511
0
        }
512
0
    }
513
6.68M
    if (hTransform == NULL) {
514
0
        gsicc_lcms2mt_link_list_t *new_link_handle =
515
0
            (gsicc_lcms2mt_link_list_t *)gs_alloc_bytes(icclink->memory->non_gc_memory,
516
0
                                                         sizeof(gsicc_lcms2mt_link_list_t),
517
0
                                                         "gscms_transform_color_buffer");
518
0
        if (new_link_handle == NULL) {
519
0
            return_error(gs_error_VMerror);
520
0
        }
521
0
        new_link_handle->next = NULL;   /* new end of list */
522
0
        new_link_handle->flags = needed_flags;
523
0
        hTransform = link_handle->hTransform;
524
525
        /* the variant we want wasn't present, clone it from the HEAD (no alpha, not planar) */
526
0
        dwInputFormat = COLORSPACE_SH(T_COLORSPACE(cmsGetTransformInputFormat(ctx, hTransform)));
527
0
        dwOutputFormat = COLORSPACE_SH(T_COLORSPACE(cmsGetTransformOutputFormat(ctx, hTransform)));
528
0
        dwInputFormat = dwInputFormat | CHANNELS_SH(T_CHANNELS(cmsGetTransformInputFormat(ctx, hTransform)));
529
0
        dwOutputFormat = dwOutputFormat | CHANNELS_SH(T_CHANNELS(cmsGetTransformOutputFormat(ctx, hTransform)));
530
0
        dwInputFormat = dwInputFormat | ENDIAN16_SH(big_endianIN);
531
0
        dwOutputFormat = dwOutputFormat | ENDIAN16_SH(big_endianOUT);
532
0
        dwInputFormat = dwInputFormat | BYTES_SH(num_bytes);
533
0
        dwOutputFormat = dwOutputFormat | BYTES_SH(num_bytes);
534
535
        /* Get the transform with the settings we need */
536
0
        hTransform = cmsCloneTransformChangingFormats(ctx, hTransform, dwInputFormat, dwOutputFormat);
537
538
0
        if (hTransform == NULL)
539
0
            return_error(gs_error_unknownerror);
540
541
        /* Now we have a new hTransform to add to the list, BUT some other thread */
542
        /* may have been working in the same one. Lock, check again and add to    */
543
        /* the (potentially new) end of the list */
544
0
        gx_monitor_enter(icclink->lock);
545
0
        while (link_handle->next != NULL) {
546
0
            if (link_handle->flags == needed_flags) {
547
                /* OOPS. Someone else did it while we were building it */
548
0
                cmsDeleteTransform(ctx, hTransform);
549
0
                hTransform = link_handle->hTransform;
550
0
                new_link_handle = NULL;
551
0
                break;
552
0
            }
553
0
            link_handle = link_handle->next;
554
0
        }
555
0
        gx_monitor_leave(icclink->lock);
556
0
        if (new_link_handle != NULL) {
557
0
            new_link_handle->hTransform = hTransform;
558
0
            link_handle->next = new_link_handle;    /* link to end of list */
559
0
        }
560
0
    }
561
562
    /* Do conversion */
563
6.68M
    cmsDoTransform(ctx, hTransform, inputcolor, outputcolor, 1);
564
565
6.68M
    return 0;
566
6.68M
}
567
568
/* Get the flag to avoid having to the cmm do any white fix up, it such a flag
569
   exists for the cmm */
570
int
571
gscms_avoid_white_fix_flag(gs_memory_t *memory)
572
0
{
573
0
    return cmsFLAGS_NOWHITEONWHITEFIXUP;
574
0
}
575
576
void
577
gscms_get_link_dim(gcmmhlink_t link, int *num_inputs, int *num_outputs,
578
    gs_memory_t *memory)
579
22.9k
{
580
22.9k
    cmsContext ctx = gs_lib_ctx_get_cms_context(memory);
581
22.9k
    gsicc_lcms2mt_link_list_t *link_handle = (gsicc_lcms2mt_link_list_t *)(link);
582
22.9k
    cmsHTRANSFORM hTransform = (cmsHTRANSFORM)link_handle->hTransform;
583
584
22.9k
    *num_inputs = T_CHANNELS(cmsGetTransformInputFormat(ctx, hTransform));
585
22.9k
    *num_outputs = T_CHANNELS(cmsGetTransformOutputFormat(ctx, hTransform));
586
22.9k
}
587
588
/* Get the link from the CMS. TODO:  Add error checking */
589
gcmmhlink_t
590
gscms_get_link(gcmmhprofile_t  lcms_srchandle, gcmmhprofile_t lcms_deshandle,
591
               gsicc_rendering_param_t *rendering_params, int cmm_flags,
592
               gs_memory_t *memory)
593
25.7k
{
594
25.7k
    cmsUInt32Number src_data_type,des_data_type;
595
25.7k
    cmsColorSpaceSignature src_color_space,des_color_space;
596
25.7k
    int src_nChannels,des_nChannels;
597
25.7k
    int lcms_src_color_space, lcms_des_color_space;
598
25.7k
    unsigned int flag;
599
25.7k
    cmsContext ctx = gs_lib_ctx_get_cms_context(memory);
600
25.7k
    gsicc_lcms2mt_link_list_t *link_handle;
601
602
    /* Check for case of request for a transfrom from a device link profile
603
       in that case, the destination profile is NULL */
604
605
    /* First handle all the source stuff */
606
25.7k
    src_color_space  = cmsGetColorSpace(ctx, lcms_srchandle);
607
25.7k
    lcms_src_color_space = _cmsLCMScolorSpace(ctx, src_color_space);
608
609
    /* littlecms returns -1 for types it does not (but should) understand */
610
25.7k
    if (lcms_src_color_space < 0)
611
0
        lcms_src_color_space = 0;
612
25.7k
    src_nChannels = cmsChannelsOf(ctx, src_color_space);
613
614
    /* For now, just do single byte data, interleaved.  We can change this
615
      when we use the transformation. */
616
25.7k
    src_data_type = (COLORSPACE_SH(lcms_src_color_space)|
617
25.7k
                        CHANNELS_SH(src_nChannels)|BYTES_SH(2));
618
619
25.7k
    if (lcms_deshandle != NULL) {
620
25.7k
        des_color_space  = cmsGetColorSpace(ctx, lcms_deshandle);
621
25.7k
    } else {
622
        /* We must have a device link profile. Use it's PCS space. */
623
0
        des_color_space = cmsGetPCS(ctx, lcms_srchandle);
624
0
    }
625
25.7k
    lcms_des_color_space = _cmsLCMScolorSpace(ctx, des_color_space);
626
25.7k
    if (lcms_des_color_space < 0)
627
0
        lcms_des_color_space = 0;
628
25.7k
    des_nChannels = cmsChannelsOf(ctx, des_color_space);
629
25.7k
    des_data_type = (COLORSPACE_SH(lcms_des_color_space)|
630
25.7k
                        CHANNELS_SH(des_nChannels)|BYTES_SH(2));
631
632
    /* Set up the flags */
633
25.7k
    flag = gscms_get_accuracy(memory);
634
25.7k
    if (rendering_params->black_point_comp == gsBLACKPTCOMP_ON
635
25.7k
        || rendering_params->black_point_comp == gsBLACKPTCOMP_ON_OR) {
636
25.7k
        flag = (flag | cmsFLAGS_BLACKPOINTCOMPENSATION);
637
25.7k
    }
638
25.7k
    if (rendering_params->preserve_black == gsBLACKPRESERVE_KONLY) {
639
0
        switch (rendering_params->rendering_intent) {
640
0
            case INTENT_PERCEPTUAL:
641
0
                rendering_params->rendering_intent = INTENT_PRESERVE_K_ONLY_PERCEPTUAL;
642
0
                break;
643
0
            case INTENT_RELATIVE_COLORIMETRIC:
644
0
                rendering_params->rendering_intent = INTENT_PRESERVE_K_ONLY_RELATIVE_COLORIMETRIC;
645
0
                break;
646
0
            case INTENT_SATURATION:
647
0
                rendering_params->rendering_intent = INTENT_PRESERVE_K_ONLY_SATURATION;
648
0
                break;
649
0
            default:
650
0
                break;
651
0
        }
652
0
    }
653
25.7k
    if (rendering_params->preserve_black == gsBLACKPRESERVE_KPLANE) {
654
0
        switch (rendering_params->rendering_intent) {
655
0
            case INTENT_PERCEPTUAL:
656
0
                rendering_params->rendering_intent = INTENT_PRESERVE_K_PLANE_PERCEPTUAL;
657
0
                break;
658
0
            case INTENT_RELATIVE_COLORIMETRIC:
659
0
                rendering_params->rendering_intent = INTENT_PRESERVE_K_PLANE_RELATIVE_COLORIMETRIC;
660
0
                break;
661
0
            case INTENT_SATURATION:
662
0
                rendering_params->rendering_intent = INTENT_PRESERVE_K_PLANE_SATURATION;
663
0
                break;
664
0
            default:
665
0
                break;
666
0
        }
667
0
    }
668
669
    /* Create the link */
670
25.7k
    link_handle = (gsicc_lcms2mt_link_list_t *)gs_alloc_bytes(memory->non_gc_memory,
671
25.7k
                                                         sizeof(gsicc_lcms2mt_link_list_t),
672
25.7k
                                                         "gscms_transform_color_buffer");
673
25.7k
    if (link_handle == NULL)
674
0
        return NULL;
675
25.7k
    link_handle->hTransform = cmsCreateTransform(ctx, lcms_srchandle, src_data_type,
676
25.7k
                                                    lcms_deshandle, des_data_type,
677
25.7k
                                                    rendering_params->rendering_intent,
678
25.7k
                                                    flag | cmm_flags);
679
25.7k
    if (link_handle->hTransform == NULL) {
680
2.82k
        int k;
681
682
        /* Add a bit of robustness here. Some profiles are ill-formed and
683
           do not have all the intents.  If we failed due to a missing
684
           intent, lets go ahead and try each and see if we can get something
685
           that works. */
686
14.1k
        for (k = 0; k <= gsABSOLUTECOLORIMETRIC; k++) {
687
11.3k
            link_handle->hTransform = cmsCreateTransform(ctx, lcms_srchandle, src_data_type,
688
11.3k
                lcms_deshandle, des_data_type, k, flag | cmm_flags);
689
11.3k
            if (link_handle->hTransform != NULL)
690
0
                break;
691
11.3k
        }
692
693
2.82k
        if (link_handle->hTransform == NULL) {
694
2.82k
            gs_free_object(memory, link_handle, "gscms_get_link");
695
2.82k
            return NULL;
696
2.82k
        }
697
2.82k
    }
698
699
22.9k
    link_handle->next = NULL;
700
22.9k
    link_handle->flags = gsicc_link_flags(0, 0, 0, 0, 0,    /* no alpha, not planar, no endian swap */
701
22.9k
                                          sizeof(gx_color_value), sizeof(gx_color_value));
702
22.9k
    return link_handle;
703
    /* cmsFLAGS_HIGHRESPRECALC)  cmsFLAGS_NOTPRECALC  cmsFLAGS_LOWRESPRECALC*/
704
25.7k
}
705
706
/* Get the link from the CMS, but include proofing and/or a device link
707
   profile.  Note also, that the source may be a device link profile, in
708
   which case we will not have a destination profile but could still have
709
   a proof profile or an additional device link profile */
710
gcmmhlink_t
711
gscms_get_link_proof_devlink(gcmmhprofile_t lcms_srchandle,
712
                             gcmmhprofile_t lcms_proofhandle,
713
                             gcmmhprofile_t lcms_deshandle,
714
                             gcmmhprofile_t lcms_devlinkhandle,
715
                             gsicc_rendering_param_t *rendering_params,
716
                             bool src_dev_link, int cmm_flags,
717
                             gs_memory_t *memory)
718
0
{
719
0
    cmsUInt32Number src_data_type,des_data_type;
720
0
    cmsColorSpaceSignature src_color_space,des_color_space;
721
0
    int src_nChannels,des_nChannels;
722
0
    int lcms_src_color_space, lcms_des_color_space;
723
0
    cmsHPROFILE hProfiles[5];
724
0
    int nProfiles = 0;
725
0
    unsigned int flag;
726
0
    cmsContext ctx = gs_lib_ctx_get_cms_context(memory);
727
0
    gsicc_lcms2mt_link_list_t *link_handle;
728
729
0
    link_handle = (gsicc_lcms2mt_link_list_t *)gs_alloc_bytes(memory->non_gc_memory,
730
0
                                                         sizeof(gsicc_lcms2mt_link_list_t),
731
0
                                                         "gscms_transform_color_buffer");
732
0
    if (link_handle == NULL)
733
0
         return NULL;
734
0
    link_handle->next = NULL;
735
0
    link_handle->flags = gsicc_link_flags(0, 0, 0, 0, 0,    /* no alpha, not planar, no endian swap */
736
0
                                          sizeof(gx_color_value), sizeof(gx_color_value));
737
    /* Check if the rendering intent is something other than relative colorimetric
738
       and if we have a proofing profile.  In this case we need to create the
739
       combined profile a bit different.  LCMS does not allow us to use different
740
       intents in the cmsCreateMultiprofileTransform transform.  Also, don't even
741
       think about doing this if someone has snuck in a source based device link
742
       profile into the mix */
743
0
    if (lcms_proofhandle != NULL &&
744
0
        rendering_params->rendering_intent != gsRELATIVECOLORIMETRIC &&
745
0
        !src_dev_link) {
746
747
        /* First handle the source to proof profile with its particular intent as
748
           a device link profile */
749
0
        cmsHPROFILE src_to_proof;
750
751
0
        link_handle = gscms_get_link(lcms_srchandle, lcms_proofhandle,
752
0
            rendering_params, cmm_flags, memory);
753
0
        if (link_handle->hTransform == NULL) {
754
0
            gs_free_object(memory, link_handle, "gscms_get_link_proof_devlink");
755
0
            return NULL;
756
0
        }
757
758
        /* Now mash that to a device link profile */
759
0
        flag = gscms_get_accuracy(memory);
760
0
        if (rendering_params->black_point_comp == gsBLACKPTCOMP_ON ||
761
0
            rendering_params->black_point_comp == gsBLACKPTCOMP_ON_OR) {
762
0
            flag = (flag | cmsFLAGS_BLACKPOINTCOMPENSATION);
763
0
        }
764
0
        src_to_proof = cmsTransform2DeviceLink(ctx, link_handle->hTransform, 3.4, flag);
765
0
        cmsDeleteTransform(ctx, link_handle->hTransform);
766
767
0
        src_color_space  = cmsGetColorSpace(ctx, src_to_proof);
768
0
        lcms_src_color_space = _cmsLCMScolorSpace(ctx, src_color_space);
769
770
        /* littlecms returns -1 for types it does not (but should) understand */
771
0
        if (lcms_src_color_space < 0)
772
0
            lcms_src_color_space = 0;
773
0
        src_nChannels = cmsChannelsOf(ctx, src_color_space);
774
775
        /* For now, just do single byte data, interleaved.  We can change this
776
          when we use the transformation. */
777
0
        src_data_type = (COLORSPACE_SH(lcms_src_color_space)|
778
0
                            CHANNELS_SH(src_nChannels)|BYTES_SH(2));
779
0
        if (lcms_devlinkhandle == NULL) {
780
0
            des_color_space = cmsGetColorSpace(ctx, lcms_deshandle);
781
0
        } else {
782
0
            des_color_space = cmsGetPCS(ctx, lcms_devlinkhandle);
783
0
        }
784
0
        lcms_des_color_space = _cmsLCMScolorSpace(ctx, des_color_space);
785
0
        if (lcms_des_color_space < 0) lcms_des_color_space = 0;
786
0
        des_nChannels = cmsChannelsOf(ctx, des_color_space);
787
0
        des_data_type = (COLORSPACE_SH(lcms_des_color_space)|
788
0
                            CHANNELS_SH(des_nChannels)|BYTES_SH(2));
789
790
        /* Now, we need to go back through the proofing profile, to the
791
           destination and then to the device link profile if there was one. */
792
0
        hProfiles[nProfiles++] = src_to_proof;  /* Src to proof with special intent */
793
0
        hProfiles[nProfiles++] = lcms_proofhandle; /* Proof to CIELAB */
794
0
        if (lcms_deshandle != NULL) {
795
0
            hProfiles[nProfiles++] = lcms_deshandle;  /* Our destination */
796
0
        }
797
798
        /* The output device link profile */
799
0
        if (lcms_devlinkhandle != NULL) {
800
0
            hProfiles[nProfiles++] = lcms_devlinkhandle;
801
0
        }
802
0
        flag = gscms_get_accuracy(memory);
803
0
        if (rendering_params->black_point_comp == gsBLACKPTCOMP_ON
804
0
            || rendering_params->black_point_comp == gsBLACKPTCOMP_ON_OR) {
805
0
            flag = (flag | cmsFLAGS_BLACKPOINTCOMPENSATION);
806
0
        }
807
808
        /* Use relative colorimetric here */
809
0
        link_handle->hTransform = cmsCreateMultiprofileTransform(ctx,
810
0
                    hProfiles, nProfiles, src_data_type, des_data_type,
811
0
                    gsRELATIVECOLORIMETRIC, flag);
812
0
        cmsCloseProfile(ctx, src_to_proof);
813
0
    } else {
814
       /* First handle all the source stuff */
815
0
        src_color_space  = cmsGetColorSpace(ctx, lcms_srchandle);
816
0
        lcms_src_color_space = _cmsLCMScolorSpace(ctx, src_color_space);
817
818
        /* littlecms returns -1 for types it does not (but should) understand */
819
0
        if (lcms_src_color_space < 0)
820
0
            lcms_src_color_space = 0;
821
0
        src_nChannels = cmsChannelsOf(ctx, src_color_space);
822
823
        /* For now, just do single byte data, interleaved.  We can change this
824
          when we use the transformation. */
825
0
        src_data_type = (COLORSPACE_SH(lcms_src_color_space)|
826
0
                            CHANNELS_SH(src_nChannels)|BYTES_SH(2));
827
0
        if (lcms_devlinkhandle == NULL) {
828
0
            if (src_dev_link) {
829
0
                des_color_space = cmsGetPCS(ctx, lcms_srchandle);
830
0
            } else {
831
0
                des_color_space = cmsGetColorSpace(ctx, lcms_deshandle);
832
0
            }
833
0
        } else {
834
0
            des_color_space = cmsGetPCS(ctx, lcms_devlinkhandle);
835
0
        }
836
0
        lcms_des_color_space = _cmsLCMScolorSpace(ctx, des_color_space);
837
0
        if (lcms_des_color_space < 0) lcms_des_color_space = 0;
838
0
        des_nChannels = cmsChannelsOf(ctx, des_color_space);
839
0
        des_data_type = (COLORSPACE_SH(lcms_des_color_space)|
840
0
                            CHANNELS_SH(des_nChannels)|BYTES_SH(2));
841
842
        /* lcms proofing transform has a clunky API and can't include the device
843
           link profile if we have both. So use cmsCreateMultiprofileTransform
844
           instead and round trip the proofing profile. */
845
0
        hProfiles[nProfiles++] = lcms_srchandle;
846
847
        /* Note if source is device link, we cannot do any proofing */
848
0
        if (lcms_proofhandle != NULL && !src_dev_link) {
849
0
            hProfiles[nProfiles++] = lcms_proofhandle;
850
0
            hProfiles[nProfiles++] = lcms_proofhandle;
851
0
        }
852
853
        /* This should be NULL if we have a source device link */
854
0
        if (lcms_deshandle != NULL) {
855
0
            hProfiles[nProfiles++] = lcms_deshandle;
856
0
        }
857
858
        /* Someone could have a device link at the output, giving us possibly two
859
           device link profiles to smash together */
860
0
        if (lcms_devlinkhandle != NULL) {
861
0
            hProfiles[nProfiles++] = lcms_devlinkhandle;
862
0
        }
863
0
        flag = gscms_get_accuracy(memory);
864
0
        if (rendering_params->black_point_comp == gsBLACKPTCOMP_ON
865
0
            || rendering_params->black_point_comp == gsBLACKPTCOMP_ON_OR) {
866
0
            flag = (flag | cmsFLAGS_BLACKPOINTCOMPENSATION);
867
0
        }
868
0
        link_handle->hTransform =  cmsCreateMultiprofileTransform(ctx,
869
0
                                       hProfiles, nProfiles, src_data_type,
870
0
                                       des_data_type, rendering_params->rendering_intent, flag);
871
0
    }
872
0
    if (link_handle->hTransform == NULL) {
873
0
        gs_free_object(memory, link_handle, "gscms_get_link_proof_devlink");
874
0
        return NULL;
875
0
    }
876
0
    return link_handle;
877
0
}
878
879
/* Do any initialization if needed to the CMS */
880
void *
881
gscms_create(gs_memory_t *memory)
882
10.8k
{
883
10.8k
    cmsContext ctx;
884
885
    /* Set our own error handling function */
886
10.8k
    ctx = cmsCreateContext((void *)&gs_cms_memhandler, memory);
887
10.8k
    if (ctx == NULL)
888
0
        return NULL;
889
890
10.8k
#ifdef USE_LCMS2_LOCKING
891
10.8k
    cmsPlugin(ctx, (void *)&gs_cms_mutexhandler);
892
10.8k
#endif
893
894
#ifdef WITH_CAL
895
    cmsPlugin(ctx, cal_cms_extensions2());
896
#endif
897
898
10.8k
    cmsSetLogErrorHandler(ctx, gscms_error);
899
900
10.8k
    return ctx;
901
10.8k
}
902
903
/* Do any clean up when done with the CMS if needed */
904
void
905
gscms_destroy(void *cmsContext_)
906
10.8k
{
907
10.8k
    cmsContext ctx = (cmsContext)cmsContext_;
908
10.8k
    if (ctx == NULL)
909
0
        return;
910
911
10.8k
    cmsDeleteContext(ctx);
912
10.8k
}
913
914
/* Have the CMS release the link */
915
void
916
gscms_release_link(gsicc_link_t *icclink)
917
51.5k
{
918
51.5k
    cmsContext ctx = gs_lib_ctx_get_cms_context(icclink->memory);
919
51.5k
    gsicc_lcms2mt_link_list_t *link_handle = (gsicc_lcms2mt_link_list_t *)(icclink->link_handle);
920
921
74.7k
    while (link_handle != NULL) {
922
23.1k
        gsicc_lcms2mt_link_list_t *next_handle;
923
23.1k
        cmsDeleteTransform(ctx, link_handle->hTransform);
924
23.1k
        next_handle = link_handle->next;
925
23.1k
        gs_free_object(icclink->memory->non_gc_memory, link_handle, "gscms_release_link");
926
23.1k
        link_handle = next_handle;
927
23.1k
    }
928
51.5k
    icclink->link_handle = NULL;
929
51.5k
}
930
931
/* Have the CMS release the profile handle */
932
void
933
gscms_release_profile(void *profile, gs_memory_t *memory)
934
50.6k
{
935
50.6k
    cmsHPROFILE profile_handle;
936
50.6k
    cmsContext ctx = gs_lib_ctx_get_cms_context(memory);
937
938
50.6k
    profile_handle = (cmsHPROFILE) profile;
939
50.6k
    cmsCloseProfile(ctx, profile_handle);
940
50.6k
}
941
942
/* Named color, color management */
943
/* Get a device value for the named color.  Since there exist named color
944
   ICC profiles and littleCMS supports them, we will use
945
   that format in this example.  However it should be noted
946
   that this object need not be an ICC named color profile
947
   but can be a proprietary type table. Some CMMs do not
948
   support named color profiles.  In that case, or if
949
   the named color is not found, the caller should use an alternate
950
   tint transform or other method. If a proprietary
951
   format (nonICC) is being used, the operators given below must
952
   be implemented. In every case that I can imagine, this should be
953
   straight forward.  Note that we allow the passage of a tint
954
   value also.  Currently the ICC named color profile does not provide
955
   tint related information, only a value for 100% coverage.
956
   It is provided here for use in proprietary
957
   methods, which may be able to provide the desired effect.  We will
958
   at the current time apply a direct tint operation to the returned
959
   device value.
960
   Right now I don't see any reason to have the named color profile
961
   ever return CIELAB. It will either return device values directly or
962
   it will return values defined by the output device profile */
963
964
int
965
gscms_transform_named_color(gsicc_link_t *icclink,  float tint_value,
966
                            const char* ColorName, gx_color_value device_values[])
967
0
{
968
0
    gsicc_lcms2mt_link_list_t *link_handle = (gsicc_lcms2mt_link_list_t *)(icclink->link_handle);
969
0
    cmsHTRANSFORM hTransform = (cmsHTRANSFORM)link_handle->hTransform;
970
0
    unsigned short *deviceptr = device_values;
971
0
    int index;
972
0
    cmsContext ctx = gs_lib_ctx_get_cms_context(icclink->memory);
973
974
    /* Check if name is present in the profile */
975
    /* If it is not return -1.  Caller will need to use an alternate method */
976
0
    if((index = cmsNamedColorIndex(ctx, hTransform, ColorName)) < 0)
977
0
        return -1;
978
979
    /* Get the device value. */
980
/* FIXME: This looks WRONG. Doesn't it need to use tint_value??? */
981
/*        Also, the hTransform from link_handle is 2-byte IN, *NOT* float */
982
0
   cmsDoTransform(ctx, hTransform,&index,deviceptr,1);
983
0
   return 0;
984
0
}
985
986
/* Create a link to return device values inside the named color profile or link
987
   it with a destination profile and potentially a proofing profile.  If the
988
   output_colorspace and the proof_color space are NULL, then we will be
989
   returning the device values that are contained in the named color profile.
990
   i.e. in namedcolor_information.  Note that an ICC named color profile
991
    need NOT contain the device values but must contain the CIELAB values. */
992
void
993
gscms_get_name2device_link(gsicc_link_t *icclink,
994
                           gcmmhprofile_t  lcms_srchandle,
995
                           gcmmhprofile_t lcms_deshandle,
996
                           gcmmhprofile_t lcms_proofhandle,
997
                           gsicc_rendering_param_t *rendering_params)
998
0
{
999
0
    cmsHTRANSFORM hTransform, hTransformNew;
1000
0
    cmsUInt32Number dwOutputFormat;
1001
0
    cmsUInt32Number lcms_proof_flag;
1002
0
    int number_colors;
1003
0
    cmsContext ctx = gs_lib_ctx_get_cms_context(icclink->memory);
1004
0
    gsicc_lcms2mt_link_list_t *link_handle;
1005
1006
0
    icclink->link_handle = NULL;    /* in case of failure */
1007
    /* NOTE:  We need to add a test here to check that we even HAVE
1008
    device values in here and NOT just CIELAB values */
1009
0
    if ( lcms_proofhandle != NULL ){
1010
0
        lcms_proof_flag = cmsFLAGS_GAMUTCHECK | cmsFLAGS_SOFTPROOFING;
1011
0
    } else {
1012
0
        lcms_proof_flag = 0;
1013
0
    }
1014
1015
    /* Create the transform */
1016
    /* ToDo:  Adjust rendering intent */
1017
0
    hTransform = cmsCreateProofingTransform(ctx,
1018
0
                                            lcms_srchandle, TYPE_NAMED_COLOR_INDEX,
1019
0
                                            lcms_deshandle, TYPE_CMYK_8,
1020
0
                                            lcms_proofhandle,INTENT_PERCEPTUAL,
1021
0
                                            INTENT_ABSOLUTE_COLORIMETRIC,
1022
0
                                            lcms_proof_flag);
1023
0
    if (hTransform == NULL)
1024
0
        return;         /* bail */
1025
1026
    /* In littleCMS there is no easy way to find out the size of the device
1027
        space returned by the named color profile until after the transform is made.
1028
        Hence we adjust our output format after creating the transform.  It is
1029
        set to CMYK8 initially. */
1030
0
    number_colors = cmsNamedColorCount(ctx, cmsGetNamedColorList(hTransform));
1031
1032
    /* NOTE: Output size of gx_color_value with no color space type check */
1033
0
    dwOutputFormat =  (CHANNELS_SH(number_colors)|BYTES_SH(sizeof(gx_color_value)));
1034
1035
    /* Change the formatters */
1036
0
    hTransformNew  = cmsCloneTransformChangingFormats(ctx, hTransform,TYPE_NAMED_COLOR_INDEX,dwOutputFormat);
1037
0
    cmsDeleteTransform(ctx, hTransform);  /* release the original after cloning */
1038
0
    if (hTransformNew == NULL)
1039
0
        return;         /* bail */
1040
0
    link_handle = (gsicc_lcms2mt_link_list_t *)gs_alloc_bytes(icclink->memory->non_gc_memory,
1041
0
                                                         sizeof(gsicc_lcms2mt_link_list_t),
1042
0
                                                         "gscms_transform_color_buffer");
1043
0
    if (link_handle == NULL) {
1044
0
        cmsDeleteTransform(ctx, hTransformNew);
1045
0
        return;         /* bail */
1046
0
    }
1047
0
    link_handle->flags = gsicc_link_flags(0, 0, 0, 0, 0,    /* no alpha, not planar, no endian swap */
1048
0
                                          sizeof(gx_color_value), sizeof(gx_color_value));
1049
0
    link_handle->hTransform = hTransformNew;
1050
0
    link_handle->next = NULL;
1051
0
    icclink->link_handle = link_handle;
1052
1053
0
    cmsCloseProfile(ctx, lcms_srchandle);
1054
0
    if(lcms_deshandle)
1055
0
        cmsCloseProfile(ctx, lcms_deshandle);
1056
0
    if(lcms_proofhandle)
1057
0
        cmsCloseProfile(ctx, lcms_proofhandle);
1058
0
    return;
1059
0
}
1060
1061
bool
1062
gscms_is_threadsafe(void)
1063
51.5k
{
1064
51.5k
    return true;              /* threads work correctly */
1065
51.5k
}