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