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 /* GS ICC link cache. Initial stubbing of functions. */
17
18 #include "std.h"
19 #include "stdpre.h"
20 #include "gstypes.h"
21 #include "gsmemory.h"
22 #include "gsstruct.h"
23 #include "scommon.h"
24 #include "gx.h"
25 #include "gpsync.h" /* for MAX_THREADS */
26 #include "gxgstate.h"
27 #include "smd5.h"
28 #include "gscms.h"
29 #include "gsicc_cms.h"
30 #include "gsicc_manage.h"
31 #include "gsicc_cache.h"
32 #include "gserrors.h"
33 #include "gsmalloc.h" /* Needed for named color structure allocation */
34 #include "string_.h" /* Needed for named color structure allocation */
35 #include "gxsync.h"
36 #include "gzstate.h"
37 #include "stdint_.h"
38 /*
39 * Note that the the external memory used to maintain
40 * links in the CMS is generally not visible to GS.
41 * For most CMS's the links are 33x33x33x33x4 bytes at worst
42 * for a CMYK to CMYK MLUT which is about 4.5Mb per link.
43 * If the link were matrix based it would be much much smaller.
44 * We will likely want to do at least have an estimate of the
45 * memory used based upon how the CMS is configured.
46 * This will be done later. For now, just limit the number
47 * of links.
48 */
49 #define ICC_CACHE_MAXLINKS (MAX_THREADS*2) /* allow up to two active links per thread */
50
51 /* Static prototypes */
52
53 static gsicc_link_t * gsicc_alloc_link(gs_memory_t *memory, gsicc_hashlink_t hashcode);
54
55 static int gsicc_get_cspace_hash(gsicc_manager_t *icc_manager, gx_device *dev,
56 cmm_profile_t *profile, int64_t *hash);
57
58 static int gsicc_compute_linkhash(gsicc_manager_t *icc_manager, gx_device *dev,
59 cmm_profile_t *input_profile,
60 cmm_profile_t *output_profile,
61 gsicc_rendering_param_t *rendering_params,
62 gsicc_hashlink_t *hash);
63
64 static void gsicc_remove_link(gsicc_link_t *link, const gs_memory_t *memory);
65
66 static void gsicc_get_buff_hash(unsigned char *data, int64_t *hash, unsigned int num_bytes);
67
68 static void rc_gsicc_link_cache_free(gs_memory_t * mem, void *ptr_in, client_name_t cname);
69
70 /* Structure pointer information */
71
72 struct_proc_finalize(icc_link_finalize);
73
74 gs_private_st_ptrs3_final(st_icc_link, gsicc_link_t, "gsiccmanage_link",
75 icc_link_enum_ptrs, icc_link_reloc_ptrs, icc_link_finalize,
76 icc_link_cache, next, lock);
77
78 struct_proc_finalize(icc_linkcache_finalize);
79
80 gs_private_st_ptrs3_final(st_icc_linkcache, gsicc_link_cache_t, "gsiccmanage_linkcache",
81 icc_linkcache_enum_ptrs, icc_linkcache_reloc_ptrs, icc_linkcache_finalize,
82 head, lock, full_wait);
83
84 /* These are used to construct a hash for the ICC link based upon the
85 render parameters */
86
87 #define BP_SHIFT 0
88 #define REND_SHIFT 8
89 #define PRESERVE_SHIFT 16
90
91 /**
92 * gsicc_cache_new: Allocate a new ICC cache manager
93 * Return value: Pointer to allocated manager, or NULL on failure.
94 **/
95
96 gsicc_link_cache_t *
gsicc_cache_new(gs_memory_t * memory)97 gsicc_cache_new(gs_memory_t *memory)
98 {
99 gsicc_link_cache_t *result;
100
101 /* We want this to be maintained in stable_memory. It should be be effected by the
102 save and restores */
103 result = gs_alloc_struct(memory->stable_memory, gsicc_link_cache_t, &st_icc_linkcache,
104 "gsicc_cache_new");
105 if ( result == NULL )
106 return(NULL);
107 result->head = NULL;
108 result->num_links = 0;
109 result->cache_full = false;
110 result->memory = memory->stable_memory;
111 result->lock = gx_monitor_label(gx_monitor_alloc(memory->stable_memory),
112 "gsicc_cache_new");
113 if (result->lock == NULL) {
114 gs_free_object(memory->stable_memory, result, "gsicc_cache_new");
115 return(NULL);
116 }
117 result->full_wait = gx_semaphore_label(gx_semaphore_alloc(memory->stable_memory),
118 "gsicc_cache_new");
119 if (result->full_wait == NULL) {
120 gx_monitor_free(result->lock);
121 gs_free_object(memory->stable_memory, result, "gsicc_cache_new");
122 return(NULL);
123 }
124 rc_init_free(result, memory->stable_memory, 1, rc_gsicc_link_cache_free);
125 if_debug2m(gs_debug_flag_icc, memory,
126 "[icc] Allocating link cache = 0x%p memory = 0x%p\n",
127 result, result->memory);
128 return(result);
129 }
130
131 static void
rc_gsicc_link_cache_free(gs_memory_t * mem,void * ptr_in,client_name_t cname)132 rc_gsicc_link_cache_free(gs_memory_t * mem, void *ptr_in, client_name_t cname)
133 {
134 /* Ending the entire cache. The ref counts on all the links should be 0 */
135 gsicc_link_cache_t *link_cache = (gsicc_link_cache_t * ) ptr_in;
136
137 if_debug2m(gs_debug_flag_icc, mem,
138 "[icc] Removing link cache = 0x%p memory = 0x%p\n",
139 link_cache, link_cache->memory);
140 /* NB: freeing the link_cache will call icc_linkcache_finalize */
141 gs_free_object(mem->stable_memory, link_cache, "rc_gsicc_link_cache_free");
142 }
143
144 /* release the monitor of the link_cache when it is freed */
145 void
icc_linkcache_finalize(const gs_memory_t * mem,void * ptr)146 icc_linkcache_finalize(const gs_memory_t *mem, void *ptr)
147 {
148 gsicc_link_cache_t *link_cache = (gsicc_link_cache_t * ) ptr;
149
150 while (link_cache->head != NULL) {
151 if (link_cache->head->ref_count != 0) {
152 emprintf2(mem, "link at 0x%p being removed, but has ref_count = %d\n",
153 link_cache->head, link_cache->head->ref_count);
154 link_cache->head->ref_count = 0; /* force removal */
155 }
156 gsicc_remove_link(link_cache->head, mem);
157 }
158 #ifdef DEBUG
159 if (link_cache->num_links != 0) {
160 emprintf1(mem, "num_links is %d, should be 0.\n", link_cache->num_links);
161 }
162 #endif
163 if (link_cache->rc.ref_count == 0) {
164 gx_monitor_free(link_cache->lock);
165 link_cache->lock = NULL;
166 gx_semaphore_free(link_cache->full_wait);
167 link_cache->full_wait = 0;
168 }
169 }
170
171 /* This is a special allocation for a link that is used by devices for
172 doing color management on post rendered data. It is not tied into the
173 profile cache like gsicc_alloc_link. Also it goes ahead and creates
174 the link, i.e. link creation is not delayed. */
175 gsicc_link_t *
gsicc_alloc_link_dev(gs_memory_t * memory,cmm_profile_t * src_profile,cmm_profile_t * des_profile,gsicc_rendering_param_t * rendering_params)176 gsicc_alloc_link_dev(gs_memory_t *memory, cmm_profile_t *src_profile,
177 cmm_profile_t *des_profile, gsicc_rendering_param_t *rendering_params)
178 {
179 gsicc_link_t *result;
180 int cms_flags = 0;
181
182 result = (gsicc_link_t*) gs_malloc(memory->stable_memory, 1,
183 sizeof(gsicc_link_t), "gsicc_alloc_link_dev");
184
185 if (result == NULL)
186 return NULL;
187 result->lock = gx_monitor_label(gx_monitor_alloc(memory->stable_memory),
188 "gsicc_link_new");
189 if (result->lock == NULL) {
190 gs_free_object(memory->stable_memory, result, "gsicc_alloc_link(lock)");
191 return NULL;
192 }
193 gx_monitor_enter(result->lock);
194
195 /* set up placeholder values */
196 result->is_monitored = false;
197 result->orig_procs.map_buffer = NULL;
198 result->orig_procs.map_color = NULL;
199 result->orig_procs.free_link = NULL;
200 result->next = NULL;
201 result->link_handle = NULL;
202 result->icc_link_cache = NULL;
203 result->procs.map_buffer = gscms_transform_color_buffer;
204 result->procs.map_color = gscms_transform_color;
205 result->procs.free_link = gscms_release_link;
206 result->hashcode.link_hashcode = 0;
207 result->hashcode.des_hash = 0;
208 result->hashcode.src_hash = 0;
209 result->hashcode.rend_hash = 0;
210 result->ref_count = 1;
211 result->includes_softproof = 0;
212 result->includes_devlink = 0;
213 result->is_identity = false;
214 result->valid = true;
215 result->memory = memory->stable_memory;
216
217 if_debug1m('^', result->memory, "[^]icclink 0x%p init = 1\n",
218 result);
219
220 if (src_profile->profile_handle == NULL) {
221 src_profile->profile_handle = gsicc_get_profile_handle_buffer(
222 src_profile->buffer, src_profile->buffer_size, memory->stable_memory);
223 }
224
225 if (des_profile->profile_handle == NULL) {
226 des_profile->profile_handle = gsicc_get_profile_handle_buffer(
227 des_profile->buffer, des_profile->buffer_size, memory->stable_memory);
228 }
229
230 /* Check for problems.. */
231 if (src_profile->profile_handle == 0 || des_profile->profile_handle == 0) {
232 gs_free_object(memory->stable_memory, result, "gsicc_alloc_link_dev");
233 return NULL;
234 }
235
236 /* [0] is chunky, littleendian, noalpha, 16-in, 16-out */
237 result->link_handle = gscms_get_link(src_profile->profile_handle,
238 des_profile->profile_handle, rendering_params, cms_flags,
239 memory->stable_memory);
240
241 /* Check for problems.. */
242 if (result->link_handle == NULL) {
243 gs_free_object(memory->stable_memory, result, "gsicc_alloc_link_dev");
244 return NULL;
245 }
246
247 /* Check for identity transform */
248 if (gsicc_get_hash(src_profile) == gsicc_get_hash(des_profile))
249 result->is_identity = true;
250
251 /* Set the rest */
252 result->data_cs = src_profile->data_cs;
253 result->num_input = src_profile->num_comps;
254 result->num_output = des_profile->num_comps;
255
256 return result;
257 }
258
259 /* And the related release of the link */
260 void
gsicc_free_link_dev(gs_memory_t * memory,gsicc_link_t * link)261 gsicc_free_link_dev(gs_memory_t *memory, gsicc_link_t *link)
262 {
263 gs_memory_t *nongc_mem = memory->non_gc_memory;
264 gs_free_object(nongc_mem, link, "gsicc_free_link_dev");
265 }
266
267 static gsicc_link_t *
gsicc_alloc_link(gs_memory_t * memory,gsicc_hashlink_t hashcode)268 gsicc_alloc_link(gs_memory_t *memory, gsicc_hashlink_t hashcode)
269 {
270 gsicc_link_t *result;
271
272 /* The link has to be added in stable memory. We want them
273 to be maintained across the gsave and grestore process */
274 result = gs_alloc_struct(memory->stable_memory, gsicc_link_t, &st_icc_link,
275 "gsicc_alloc_link");
276 if (result == NULL)
277 return NULL;
278 /* set up placeholder values */
279 result->is_monitored = false;
280 result->orig_procs.map_buffer = NULL;
281 result->orig_procs.map_color = NULL;
282 result->orig_procs.free_link = NULL;
283 result->next = NULL;
284 result->link_handle = NULL;
285 result->procs.map_buffer = gscms_transform_color_buffer;
286 result->procs.map_color = gscms_transform_color;
287 result->procs.free_link = gscms_release_link;
288 result->hashcode.link_hashcode = hashcode.link_hashcode;
289 result->hashcode.des_hash = 0;
290 result->hashcode.src_hash = 0;
291 result->hashcode.rend_hash = 0;
292 result->ref_count = 1; /* prevent it from being freed */
293 result->includes_softproof = 0;
294 result->includes_devlink = 0;
295 result->is_identity = false;
296 result->valid = false; /* not yet complete */
297 result->memory = memory->stable_memory;
298
299 result->lock = gx_monitor_label(gx_monitor_alloc(memory->stable_memory),
300 "gsicc_link_new");
301 if (result->lock == NULL) {
302 gs_free_object(memory->stable_memory, result, "gsicc_alloc_link(lock)");
303 return NULL;
304 }
305 gx_monitor_enter(result->lock); /* this link is owned by this thread until built and made "valid" */
306
307 if_debug1m('^', result->memory, "[^]icclink 0x%p init = 1\n",
308 result);
309 return result;
310 }
311
312 static void
gsicc_set_link_data(gsicc_link_t * icc_link,void * link_handle,gsicc_hashlink_t hashcode,gx_monitor_t * lock,bool includes_softproof,bool includes_devlink,bool pageneutralcolor,gsicc_colorbuffer_t data_cs)313 gsicc_set_link_data(gsicc_link_t *icc_link, void *link_handle,
314 gsicc_hashlink_t hashcode, gx_monitor_t *lock,
315 bool includes_softproof, bool includes_devlink,
316 bool pageneutralcolor, gsicc_colorbuffer_t data_cs)
317 {
318 gx_monitor_enter(lock); /* lock the cache while changing data */
319 icc_link->link_handle = link_handle;
320 gscms_get_link_dim(link_handle, &(icc_link->num_input), &(icc_link->num_output),
321 icc_link->memory);
322 icc_link->hashcode.link_hashcode = hashcode.link_hashcode;
323 icc_link->hashcode.des_hash = hashcode.des_hash;
324 icc_link->hashcode.src_hash = hashcode.src_hash;
325 icc_link->hashcode.rend_hash = hashcode.rend_hash;
326 icc_link->includes_softproof = includes_softproof;
327 icc_link->includes_devlink = includes_devlink;
328 if ( (hashcode.src_hash == hashcode.des_hash) &&
329 !includes_softproof && !includes_devlink) {
330 icc_link->is_identity = true;
331 } else {
332 icc_link->is_identity = false;
333 }
334 /* Set up for monitoring */
335 icc_link->data_cs = data_cs;
336 if (pageneutralcolor)
337 gsicc_mcm_set_link(icc_link);
338
339 /* release the lock of the link so it can now be used */
340 icc_link->valid = true;
341 gx_monitor_leave(icc_link->lock);
342 gx_monitor_leave(lock); /* done with updating, let everyone run */
343 }
344
345 static void
gsicc_link_free_contents(gsicc_link_t * icc_link)346 gsicc_link_free_contents(gsicc_link_t *icc_link)
347 {
348 icc_link->procs.free_link(icc_link);
349 gx_monitor_free(icc_link->lock);
350 icc_link->lock = NULL;
351 }
352
353 void
gsicc_link_free(gsicc_link_t * icc_link,const gs_memory_t * memory)354 gsicc_link_free(gsicc_link_t *icc_link, const gs_memory_t *memory)
355 {
356 gsicc_link_free_contents(icc_link);
357
358 gs_free_object(memory->stable_memory, icc_link, "gsicc_link_free");
359 }
360
361 void
icc_link_finalize(const gs_memory_t * mem,void * ptr)362 icc_link_finalize(const gs_memory_t *mem, void *ptr)
363 {
364 gsicc_link_t *icc_link = (gsicc_link_t * ) ptr;
365
366 gsicc_link_free_contents(icc_link);
367 }
368
369 static void
gsicc_mash_hash(gsicc_hashlink_t * hash)370 gsicc_mash_hash(gsicc_hashlink_t *hash)
371 {
372 hash->link_hashcode =
373 (hash->des_hash >> 1) ^ (hash->rend_hash) ^ (hash->src_hash);
374 }
375
376 int64_t
gsicc_get_hash(cmm_profile_t * profile)377 gsicc_get_hash(cmm_profile_t *profile)
378 {
379 if (!profile->hash_is_valid) {
380 int64_t hash;
381
382 gsicc_get_icc_buff_hash(profile->buffer, &hash, profile->buffer_size);
383 profile->hashcode = hash;
384 profile->hash_is_valid = true;
385 }
386 return profile->hashcode;
387 }
388
389 void
gsicc_get_icc_buff_hash(unsigned char * buffer,int64_t * hash,unsigned int buff_size)390 gsicc_get_icc_buff_hash(unsigned char *buffer, int64_t *hash, unsigned int buff_size)
391 {
392 gsicc_get_buff_hash(buffer, hash, buff_size);
393 }
394
395 static void
gsicc_get_buff_hash(unsigned char * data,int64_t * hash,unsigned int num_bytes)396 gsicc_get_buff_hash(unsigned char *data, int64_t *hash, unsigned int num_bytes)
397 {
398 gs_md5_state_t md5;
399 byte digest[16];
400 int k;
401 int64_t word1,word2,shift;
402
403 /* We could probably do something faster than this. But use this for now. */
404 gs_md5_init(&md5);
405 gs_md5_append(&md5, data, num_bytes);
406 gs_md5_finish(&md5, digest);
407
408 /* For now, xor this into 64 bit word */
409 word1 = 0;
410 word2 = 0;
411 shift = 0;
412
413 /* need to do it this way because of
414 potential word boundary issues */
415 for( k = 0; k<8; k++) {
416 word1 += ((int64_t) digest[k]) << shift;
417 word2 += ((int64_t) digest[k+8]) << shift;
418 shift += 8;
419 }
420 *hash = word1 ^ word2;
421 }
422
423 /* Compute a hash code for the current transformation case.
424 This just computes a 64bit xor of upper and lower portions of
425 md5 for the input, output
426 and rendering params structure. We may change this later */
427 static int
gsicc_compute_linkhash(gsicc_manager_t * icc_manager,gx_device * dev,cmm_profile_t * input_profile,cmm_profile_t * output_profile,gsicc_rendering_param_t * rendering_params,gsicc_hashlink_t * hash)428 gsicc_compute_linkhash(gsicc_manager_t *icc_manager, gx_device *dev,
429 cmm_profile_t *input_profile,
430 cmm_profile_t *output_profile,
431 gsicc_rendering_param_t *rendering_params,
432 gsicc_hashlink_t *hash)
433 {
434 int code;
435
436 /* first get the hash codes for the color spaces */
437 code = gsicc_get_cspace_hash(icc_manager, dev, input_profile,
438 &(hash->src_hash));
439 if (code < 0)
440 return code;
441 code = gsicc_get_cspace_hash(icc_manager, dev, output_profile,
442 &(hash->des_hash));
443 if (code < 0)
444 return code;
445
446 /* now for the rendering paramaters, just use the word itself. At this
447 point in time, we only include the black point setting, the intent
448 and if we are preserving black. We don't differentiate at this time
449 with object type since the current CMM does not create different
450 links based upon this type setting. Other parameters such as cmm
451 and override ICC are used prior to a link creation and so should also
452 not factor into the link hash calculation */
453 hash->rend_hash = ((rendering_params->black_point_comp) << BP_SHIFT) +
454 ((rendering_params->rendering_intent) << REND_SHIFT) +
455 ((rendering_params->preserve_black) << PRESERVE_SHIFT);
456 /* for now, mash all of these into a link hash */
457 gsicc_mash_hash(hash);
458 return 0;
459 }
460
461 static int
gsicc_get_cspace_hash(gsicc_manager_t * icc_manager,gx_device * dev,cmm_profile_t * cmm_icc_profile_data,int64_t * hash)462 gsicc_get_cspace_hash(gsicc_manager_t *icc_manager, gx_device *dev,
463 cmm_profile_t *cmm_icc_profile_data, int64_t *hash)
464 {
465 cmm_dev_profile_t *dev_profile;
466 cmm_profile_t *icc_profile;
467 gsicc_rendering_param_t render_cond;
468 int code;
469
470 if (cmm_icc_profile_data == NULL)
471 {
472 if (dev == NULL)
473 return -1;
474 code = dev_proc(dev, get_profile)(dev, &dev_profile);
475 if (code < 0)
476 return code;
477 gsicc_extract_profile(dev->graphics_type_tag, dev_profile,
478 &(icc_profile), &render_cond);
479 *hash = icc_profile->hashcode;
480 return 0;
481 }
482 if (cmm_icc_profile_data->hash_is_valid ) {
483 *hash = cmm_icc_profile_data->hashcode;
484 } else {
485 /* We need to compute for this color space */
486 gsicc_get_icc_buff_hash(cmm_icc_profile_data->buffer, hash,
487 cmm_icc_profile_data->buffer_size);
488 cmm_icc_profile_data->hashcode = *hash;
489 cmm_icc_profile_data->hash_is_valid = true;
490 }
491 return 0;
492 }
493
494 gsicc_link_t*
gsicc_findcachelink(gsicc_hashlink_t hash,gsicc_link_cache_t * icc_link_cache,bool includes_proof,bool includes_devlink)495 gsicc_findcachelink(gsicc_hashlink_t hash, gsicc_link_cache_t *icc_link_cache,
496 bool includes_proof, bool includes_devlink)
497 {
498 gsicc_link_t *curr, *prev;
499 int64_t hashcode = hash.link_hashcode;
500
501 /* Look through the cache for the hashcode */
502 gx_monitor_enter(icc_link_cache->lock);
503
504 /* List scanning is fast, so we scan the entire list, this includes */
505 /* links that are currently unused, but still in the cache (zero_ref) */
506 curr = icc_link_cache->head;
507 prev = NULL;
508
509 while (curr != NULL ) {
510 if (curr->hashcode.link_hashcode == hashcode &&
511 includes_proof == curr->includes_softproof &&
512 includes_devlink == curr->includes_devlink) {
513 /* move this one to the front of the list hoping we will use it
514 again soon */
515 if (prev != NULL) {
516 /* if prev == NULL, curr is already the head */
517 prev->next = curr->next;
518 curr->next = icc_link_cache->head;
519 icc_link_cache->head = curr;
520 }
521 /* bump the ref_count since we will be using this one */
522 curr->ref_count++;
523 if_debug3m('^', curr->memory, "[^]%s 0x%p ++ => %d\n",
524 "icclink", curr, curr->ref_count);
525 while (curr->valid == false) {
526 gx_monitor_leave(icc_link_cache->lock); /* exit to let other threads run briefly */
527 gx_monitor_enter(curr->lock); /* wait until we can acquire the lock */
528 gx_monitor_leave(curr->lock); /* it _should be valid now */
529 /* If it is still not valid, but we were able to lock, it means that the thread */
530 /* that was building it failed to be able to complete building it */
531 /* this is probably a fatal error. MV ??? */
532 if (curr->valid == false) {
533 emprintf1(curr->memory, "link 0x%p lock released, but still not valid.\n", curr); /* Breakpoint here */
534 }
535 gx_monitor_enter(icc_link_cache->lock); /* re-enter to loop and check */
536 }
537 gx_monitor_leave(icc_link_cache->lock);
538 return(curr); /* success */
539 }
540 prev = curr;
541 curr = curr->next;
542 }
543 gx_monitor_leave(icc_link_cache->lock);
544 return NULL;
545 }
546
547 /* Remove link from cache. Notify CMS and free */
548 static void
gsicc_remove_link(gsicc_link_t * link,const gs_memory_t * memory)549 gsicc_remove_link(gsicc_link_t *link, const gs_memory_t *memory)
550 {
551 gsicc_link_t *curr, *prev;
552 gsicc_link_cache_t *icc_link_cache = link->icc_link_cache;
553
554 if_debug2m(gs_debug_flag_icc, memory,
555 "[icc] Removing link = 0x%p memory = 0x%p\n", link,
556 memory->stable_memory);
557 /* NOTE: link->ref_count must be 0: assert ? */
558 gx_monitor_enter(icc_link_cache->lock);
559 if (link->ref_count != 0) {
560 emprintf2(memory, "link at 0x%p being removed, but has ref_count = %d\n", link, link->ref_count);
561 }
562 curr = icc_link_cache->head;
563 prev = NULL;
564
565 while (curr != NULL ) {
566 /* don't get rid of it if another thread has decided to use it */
567 if (curr == link && link->ref_count == 0) {
568 /* remove this one from the list */
569 if (prev == NULL)
570 icc_link_cache->head = curr->next;
571 else
572 prev->next = curr->next;
573 break;
574 }
575 prev = curr;
576 curr = curr->next;
577 }
578 /* if curr != link we didn't find it or another thread may have decided to */
579 /* use it (ref_count > 0). Skip freeing it if so. */
580 if (curr == link && link->ref_count == 0) {
581 icc_link_cache->num_links--; /* no longer in the cache */
582 if (icc_link_cache->cache_full) {
583 icc_link_cache->cache_full = false;
584 gx_semaphore_signal(icc_link_cache->full_wait); /* let a waiting thread run */
585 }
586 gx_monitor_leave(icc_link_cache->lock);
587 gsicc_link_free(link, memory); /* outside link cache now. */
588 } else {
589 /* even if we didn't find the link to remove, unlock the cache */
590 gx_monitor_leave(icc_link_cache->lock);
591 }
592 }
593
594 static void
gsicc_get_srcprofile(gsicc_colorbuffer_t data_cs,gs_graphics_type_tag_t graphics_type_tag,cmm_srcgtag_profile_t * srcgtag_profile,cmm_profile_t ** profile,gsicc_rendering_param_t * render_cond)595 gsicc_get_srcprofile(gsicc_colorbuffer_t data_cs,
596 gs_graphics_type_tag_t graphics_type_tag,
597 cmm_srcgtag_profile_t *srcgtag_profile, cmm_profile_t **profile,
598 gsicc_rendering_param_t *render_cond)
599 {
600 (*profile) = NULL;
601 (*render_cond).rendering_intent = gsPERCEPTUAL;
602 (*render_cond).cmm = gsCMM_DEFAULT;
603 switch (graphics_type_tag & ~GS_DEVICE_ENCODES_TAGS) {
604 case GS_UNKNOWN_TAG:
605 case GS_UNTOUCHED_TAG:
606 default:
607 break;
608 case GS_PATH_TAG:
609 if (data_cs == gsRGB) {
610 (*profile) = srcgtag_profile->rgb_profiles[gsSRC_GRAPPRO];
611 *render_cond = srcgtag_profile->rgb_rend_cond[gsSRC_GRAPPRO];
612 }
613 else if (data_cs == gsCMYK) {
614 (*profile) = srcgtag_profile->cmyk_profiles[gsSRC_GRAPPRO];
615 *render_cond = srcgtag_profile->cmyk_rend_cond[gsSRC_GRAPPRO];
616 }
617 else if (data_cs == gsGRAY) {
618 (*profile) = srcgtag_profile->gray_profiles[gsSRC_GRAPPRO];
619 *render_cond = srcgtag_profile->gray_rend_cond[gsSRC_GRAPPRO];
620 }
621 break;
622 case GS_IMAGE_TAG:
623 if (data_cs == gsRGB) {
624 (*profile) = srcgtag_profile->rgb_profiles[gsSRC_IMAGPRO];
625 *render_cond = srcgtag_profile->rgb_rend_cond[gsSRC_IMAGPRO];
626 }
627 else if (data_cs == gsCMYK) {
628 (*profile) = srcgtag_profile->cmyk_profiles[gsSRC_IMAGPRO];
629 *render_cond = srcgtag_profile->cmyk_rend_cond[gsSRC_IMAGPRO];
630 }
631 else if (data_cs == gsGRAY) {
632 (*profile) = srcgtag_profile->gray_profiles[gsSRC_IMAGPRO];
633 *render_cond = srcgtag_profile->gray_rend_cond[gsSRC_IMAGPRO];
634 }
635 break;
636 case GS_TEXT_TAG:
637 if (data_cs == gsRGB) {
638 (*profile) = srcgtag_profile->rgb_profiles[gsSRC_TEXTPRO];
639 *render_cond = srcgtag_profile->rgb_rend_cond[gsSRC_TEXTPRO];
640 }
641 else if (data_cs == gsCMYK) {
642 (*profile) = srcgtag_profile->cmyk_profiles[gsSRC_TEXTPRO];
643 *render_cond = srcgtag_profile->cmyk_rend_cond[gsSRC_TEXTPRO];
644 }
645 else if (data_cs == gsGRAY) {
646 (*profile) = srcgtag_profile->gray_profiles[gsSRC_TEXTPRO];
647 *render_cond = srcgtag_profile->gray_rend_cond[gsSRC_TEXTPRO];
648 }
649 break;
650 }
651 }
652
653 gsicc_link_t*
gsicc_get_link(const gs_gstate * pgs1,gx_device * dev_in,const gs_color_space * pcs_in,gs_color_space * output_colorspace,gsicc_rendering_param_t * rendering_params,gs_memory_t * memory)654 gsicc_get_link(const gs_gstate *pgs1, gx_device *dev_in,
655 const gs_color_space *pcs_in,
656 gs_color_space *output_colorspace,
657 gsicc_rendering_param_t *rendering_params, gs_memory_t *memory)
658 {
659 cmm_profile_t *gs_input_profile;
660 cmm_profile_t *gs_srcgtag_profile = NULL;
661 cmm_profile_t *gs_output_profile;
662 gs_gstate *pgs = (gs_gstate *)pgs1;
663 gx_device *dev;
664 gsicc_rendering_param_t render_cond;
665 cmm_dev_profile_t *dev_profile;
666 int code;
667 bool devicegraytok;
668 gs_color_space *input_colorspace = (gs_color_space*) pcs_in;
669
670 if (dev_in == NULL) {
671 /* Get from the gs_gstate which is going to be a graphic state.
672 This only occurs for the other (non-ps/pdf) interpreters */
673 pgs = (gs_gstate*) pgs;
674 dev = pgs->device;
675 } else {
676 dev = dev_in;
677 }
678 if (input_colorspace->cmm_icc_profile_data == NULL) {
679 if (input_colorspace->icc_equivalent != NULL) {
680 gs_input_profile = input_colorspace->icc_equivalent->cmm_icc_profile_data;
681 } else {
682 /* Use default type */
683 gs_input_profile = gsicc_get_gscs_profile(input_colorspace,
684 pgs->icc_manager);
685 }
686 } else {
687 gs_input_profile = input_colorspace->cmm_icc_profile_data;
688 }
689 code = dev_proc(dev, get_profile)(dev, &dev_profile);
690 if (code < 0)
691 return NULL;
692 /* If present, use an graphic object defined source profile */
693 if (pgs->icc_manager != NULL &&
694 pgs->icc_manager->srcgtag_profile != NULL) {
695 if (gs_input_profile->data_cs == gsRGB
696 || gs_input_profile->data_cs == gsCMYK
697 || gs_input_profile->data_cs == gsGRAY) {
698 gsicc_get_srcprofile(gs_input_profile->data_cs,
699 dev->graphics_type_tag,
700 pgs->icc_manager->srcgtag_profile,
701 &(gs_srcgtag_profile), &render_cond);
702 if (gs_srcgtag_profile != NULL) {
703 /* In this case, the user is letting the source profiles
704 drive the color management. Let that set the
705 rendering intent and blackpoint compensation also as they
706 must know what they are doing. However, before we do
707 this we need to check if they want to overide
708 embedded source profiles. See if our profile is a
709 default one that came from DefaultRGB or DefaultCMYK
710 for example */
711 int csi;
712
713 csi = gsicc_get_default_type(gs_input_profile);
714 if (render_cond.override_icc ||
715 csi == gs_color_space_index_DeviceRGB ||
716 csi == gs_color_space_index_DeviceCMYK ||
717 csi == gs_color_space_index_DeviceGray) {
718 gs_input_profile = gs_srcgtag_profile;
719 (*rendering_params) = render_cond;
720 }
721 /* We also need to worry about the case when the source
722 profile is actually a device link profile. In this case
723 we can go ahead now and the our link transform as we
724 don't need to worry about a destination profile.
725 However, it is possible that someone could do another
726 device link profile associated with the device. */
727 if (gs_input_profile->isdevlink) {
728 /* OK. Go ahead and use this one. Note output profile
729 is not NULL so that we can compute a hash with out
730 special conditional logic */
731 rendering_params->rendering_intent =
732 render_cond.rendering_intent & gsRI_MASK;
733 rendering_params->black_point_comp =
734 render_cond.black_point_comp & gsBP_MASK;
735
736 return gsicc_get_link_profile(pgs, dev, gs_input_profile,
737 dev_profile->device_profile[0],
738 rendering_params, memory,
739 false);
740 }
741 } else {
742 /* In this case we may be wanting for a "unmanaged color"
743 result. This is done by specifying "None" on the
744 particular line for that source object. Check if this
745 is what is desired. If it is, then return the link now.
746 Also need to worry about the replace case */
747 if (render_cond.cmm == gsCMM_NONE) {
748 gsicc_link_t *link;
749
750 if (gs_input_profile->data_cs == gsRGB) {
751 link = gsicc_nocm_get_link(pgs, dev, 3);
752 } else {
753 link = gsicc_nocm_get_link(pgs, dev, 4);
754 }
755 /* Set the identity case if we are in that situation */
756 if (link != NULL) {
757 if (gs_input_profile->num_comps ==
758 dev_profile->device_profile[0]->num_comps) {
759 link->is_identity = true;
760 }
761 return link;
762 }
763 } else if (render_cond.cmm == gsCMM_REPLACE) {
764 return gsicc_rcm_get_link(pgs, dev,
765 gs_input_profile->data_cs);
766 /* Note that there is never an identity case */
767 }
768 }
769 }
770 }
771 if (output_colorspace != NULL) {
772 gs_output_profile = output_colorspace->cmm_icc_profile_data;
773 devicegraytok = false;
774 } else {
775 /* Use the device profile. Only use the rendering intent if it has
776 an override setting. Also, only use the blackpoint if overide_bp
777 is set. Note that this can conflict with intents set from the source
778 objects so the user needs to understand what options to set. */
779 code = dev_proc(dev, get_profile)(dev, &dev_profile);
780 if (code < 0)
781 return NULL;
782
783 /* Check for unmanaged color case */
784 if (gsicc_use_fast_color(gs_input_profile) > 0 && dev_profile->usefastcolor) {
785 /* Return a "link" from the source space to the device color space */
786 gsicc_link_t *link = gsicc_nocm_get_link(pgs, dev,
787 gs_input_profile->num_comps);
788 if (link != NULL) {
789 if (gs_input_profile->num_comps ==
790 dev_profile->device_profile[0]->num_comps) {
791 link->is_identity = true;
792 }
793 return link;
794 }
795 }
796 gsicc_extract_profile(dev->graphics_type_tag, dev_profile,
797 &(gs_output_profile), &render_cond);
798 /* Check if the incoming rendering intent was source based
799 (this can occur for high level images in the clist) in
800 that case we need to use the source ri and not the device one */
801 if (!(rendering_params->rendering_intent & gsRI_OVERRIDE)) {
802 /* No it was not. Check if our device profile RI was specified */
803 if (render_cond.rendering_intent != gsRINOTSPECIFIED) {
804 rendering_params->rendering_intent = render_cond.rendering_intent;
805 }
806 }
807 /* Similar for the black point compensation */
808 if (!(rendering_params->black_point_comp & gsBP_OVERRIDE)) {
809 if (render_cond.black_point_comp != gsBPNOTSPECIFIED) {
810 rendering_params->black_point_comp = render_cond.black_point_comp;
811 }
812 }
813 /* And the Black preservation */
814 if (!(rendering_params->preserve_black & gsKP_OVERRIDE)) {
815 if (render_cond.preserve_black != gsBKPRESNOTSPECIFIED) {
816 rendering_params->preserve_black = render_cond.preserve_black;
817 }
818 }
819 devicegraytok = dev_profile->devicegraytok;
820 }
821 /* If we are going from DeviceGray to DeviceCMYK and devicegraytok
822 is true then use the ps_gray and ps_cmyk profiles instead of these
823 profiles */
824 rendering_params->rendering_intent = rendering_params->rendering_intent & gsRI_MASK;
825 rendering_params->black_point_comp = rendering_params->black_point_comp & gsBP_MASK;
826 rendering_params->preserve_black = rendering_params->preserve_black & gsKP_MASK;
827 return gsicc_get_link_profile(pgs, dev, gs_input_profile, gs_output_profile,
828 rendering_params, memory, devicegraytok);
829 }
830
831 /* This operation of adding in a new link entry is actually shared amongst
832 different functions that can each add an entry. For example, entrys may
833 come from the CMM or they may come from the non color managed approach
834 (i.e. gsicc_nocm_get_link)
835 Returns true if link was found with that has, false if alloc fails.
836 */
837 bool
gsicc_alloc_link_entry(gsicc_link_cache_t * icc_link_cache,gsicc_link_t ** ret_link,gsicc_hashlink_t hash,bool include_softproof,bool include_devlink)838 gsicc_alloc_link_entry(gsicc_link_cache_t *icc_link_cache,
839 gsicc_link_t **ret_link, gsicc_hashlink_t hash,
840 bool include_softproof, bool include_devlink)
841 {
842 gs_memory_t *cache_mem = icc_link_cache->memory;
843 gsicc_link_t *link;
844
845 *ret_link = NULL;
846 /* First see if we can add a link */
847 /* TODO: this should be based on memory usage, not just num_links */
848 gx_monitor_enter(icc_link_cache->lock);
849 while (icc_link_cache->num_links >= ICC_CACHE_MAXLINKS) {
850 /* Look through the cache for first zero ref count to re-use that entry.
851 When ref counts go to zero, the icc_link will have been moved to
852 the end of the list, so the first we find is the 'oldest'.
853 If we get to the last entry we release the lock, set the cache_full
854 flag and wait on full_wait for some other thread to let this thread
855 run again after releasing a cache slot. Release the cache lock to
856 let other threads run and finish with (release) a cache entry.
857 */
858 link = icc_link_cache->head;
859 while (link != NULL ) {
860 if (link->ref_count == 0) {
861 /* we will use this one */
862 if_debug3m('^', cache_mem, "[^]%s 0x%lx ++ => %d\n",
863 "icclink", (ulong)link, link->ref_count);
864 break;
865 }
866 link = link->next;
867 }
868 if (link == NULL) {
869 icc_link_cache->cache_full = true;
870 /* unlock while waiting for a link to come available */
871 gx_monitor_leave(icc_link_cache->lock);
872 gx_semaphore_wait(icc_link_cache->full_wait);
873 /* repeat the findcachelink to see if some other thread has */
874 /* already started building the link we need */
875 *ret_link = gsicc_findcachelink(hash, icc_link_cache,
876 include_softproof, include_devlink);
877 /* Got a hit, return link. ref_count for the link was already bumped */
878 if (*ret_link != NULL)
879 return true;
880
881 gx_monitor_enter(icc_link_cache->lock); /* restore the lock */
882 } else {
883 /* Remove the zero ref_count link profile we found. */
884 /* Even if we remove this link, we may still be maxed out so*/
885 /* the outermost 'while' will check to make sure some other */
886 /* thread did not grab the one we remove. */
887 gsicc_remove_link(link, cache_mem);
888 }
889 }
890 /* insert an empty link that we will reserve so we can unlock while */
891 /* building the link contents. If successful, the entry will set */
892 /* the hash for the link, Set valid=false, and lock the profile */
893 (*ret_link) = gsicc_alloc_link(cache_mem->stable_memory, hash);
894 /* NB: the link returned will be have the lock owned by this thread */
895 /* the lock will be released when the link becomes valid. */
896 if (*ret_link) {
897 (*ret_link)->icc_link_cache = icc_link_cache;
898 (*ret_link)->next = icc_link_cache->head;
899 icc_link_cache->head = *ret_link;
900 icc_link_cache->num_links++;
901 }
902 /* unlock before returning */
903 gx_monitor_leave(icc_link_cache->lock);
904 return false; /* we didn't find it, but return a link to be filled */
905 }
906
907 /* This is the main function called to obtain a linked transform from the ICC
908 cache If the cache has the link ready, it will return it. If not, it will
909 request one from the CMS and then return it. We may need to do some cache
910 locking during this process to avoid multi-threaded issues (e.g. someone
911 deleting while someone is updating a reference count). Note that if the
912 source profile is a device link profile we have no output profile but
913 may still have a proofing or another device link profile to use */
914 gsicc_link_t*
gsicc_get_link_profile(const gs_gstate * pgs,gx_device * dev,cmm_profile_t * gs_input_profile,cmm_profile_t * gs_output_profile,gsicc_rendering_param_t * rendering_params,gs_memory_t * memory,bool devicegraytok)915 gsicc_get_link_profile(const gs_gstate *pgs, gx_device *dev,
916 cmm_profile_t *gs_input_profile,
917 cmm_profile_t *gs_output_profile,
918 gsicc_rendering_param_t *rendering_params,
919 gs_memory_t *memory, bool devicegraytok)
920 {
921 gsicc_hashlink_t hash;
922 gsicc_link_t *link, *found_link;
923 gcmmhlink_t link_handle = NULL;
924 gsicc_manager_t *icc_manager = pgs->icc_manager;
925 gsicc_link_cache_t *icc_link_cache = pgs->icc_link_cache;
926 gs_memory_t *cache_mem = pgs->icc_link_cache->memory;
927 gcmmhprofile_t *cms_input_profile;
928 gcmmhprofile_t *cms_output_profile = NULL;
929 gcmmhprofile_t *cms_proof_profile = NULL;
930 gcmmhprofile_t *cms_devlink_profile = NULL;
931 int code;
932 bool include_softproof = false;
933 bool include_devicelink = false;
934 cmm_dev_profile_t *dev_profile;
935 cmm_profile_t *proof_profile = NULL;
936 cmm_profile_t *devlink_profile = NULL;
937 bool src_dev_link = gs_input_profile->isdevlink;
938 bool pageneutralcolor = false;
939 int cms_flags = 0;
940
941 /* Determine if we are using a soft proof or device link profile */
942 if (dev != NULL ) {
943 code = dev_proc(dev, get_profile)(dev, &dev_profile);
944 if (code < 0)
945 return NULL;
946 if (dev_profile != NULL) {
947 proof_profile = dev_profile->proof_profile;
948 devlink_profile = dev_profile->link_profile;
949 pageneutralcolor = dev_profile->pageneutralcolor;
950 }
951 /* If the source color is the same as the proofing color then we do not
952 need to apply the proofing color in this case. This occurs in cases
953 where we have a CMYK output intent profile, a Device CMYK color, and
954 are going out to an RGB device */
955 if (proof_profile != NULL ) {
956 if (proof_profile->hashcode == gs_input_profile->hashcode) {
957 proof_profile = NULL;
958 } else {
959 include_softproof = true;
960 }
961 }
962 if (devlink_profile != NULL) include_devicelink = true;
963 }
964 /* First compute the hash code for the incoming case. If the output color
965 space is NULL we will use the device profile for the output color space */
966 code = gsicc_compute_linkhash(icc_manager, dev, gs_input_profile,
967 gs_output_profile,
968 rendering_params, &hash);
969 if (code < 0)
970 return NULL;
971 /* Check the cache for a hit. Need to check if softproofing was used */
972 found_link = gsicc_findcachelink(hash, icc_link_cache, include_softproof,
973 include_devicelink);
974 /* Got a hit, return link (ref_count for the link was already bumped */
975 if (found_link != NULL) {
976 if_debug2m(gs_debug_flag_icc, memory,
977 "[icc] Found Link = 0x%p, hash = %lld \n",
978 found_link, (long long)hash.link_hashcode);
979 if_debug2m(gs_debug_flag_icc, memory,
980 "[icc] input_numcomps = %d, input_hash = %lld \n",
981 gs_input_profile->num_comps,
982 (long long)gs_input_profile->hashcode);
983 if_debug2m(gs_debug_flag_icc, memory,
984 "[icc] output_numcomps = %d, output_hash = %lld \n",
985 gs_output_profile->num_comps,
986 (long long)gs_output_profile->hashcode);
987 return found_link;
988 }
989 /* Before we do anything, check if we have a case where the source profile
990 is coming from the clist and we don't even want to be doing any color
991 managment */
992 if (gs_input_profile->profile_handle == NULL &&
993 gs_input_profile->buffer == NULL &&
994 gs_input_profile->dev != NULL) {
995
996 /* ICC profile should be in clist. This is the first call to it. Note that
997 the profiles are not really shared amongst threads like the links are.
998 Hence the memory is for the local thread's chunk */
999 cms_input_profile =
1000 gsicc_get_profile_handle_clist(gs_input_profile,
1001 gs_input_profile->memory);
1002 gs_input_profile->profile_handle = cms_input_profile;
1003 /* It is possible that we are not using color management
1004 due to a setting forced from srcgtag object (the None option)
1005 which has made its way though the clist in the clist imaging
1006 code. In this case, the srcgtag_profile structure
1007 which was part of the ICC manager is no longer available.
1008 We also have the Replace option. */
1009 if (gs_input_profile->rend_is_valid &&
1010 gs_input_profile->rend_cond.cmm == gsCMM_NONE) {
1011
1012 if (gs_input_profile->data_cs == gsRGB) {
1013 link = gsicc_nocm_get_link(pgs, dev, 3);
1014 } else {
1015 link = gsicc_nocm_get_link(pgs, dev, 4);
1016 }
1017 /* Set the identity case if we are in that situation */
1018 if (link != NULL) {
1019 if (gs_input_profile->num_comps ==
1020 dev_profile->device_profile[0]->num_comps) {
1021 link->is_identity = true;
1022 }
1023 return link;
1024 }
1025 } else if (gs_input_profile->rend_is_valid &&
1026 gs_input_profile->rend_cond.cmm == gsCMM_REPLACE) {
1027 return gsicc_rcm_get_link(pgs, dev, gs_input_profile->data_cs);
1028 /* Note that there is never an identity case for
1029 this type. */
1030 }
1031 /* We may have a source profile that is a device link profile and
1032 made its way through the clist. If so get things set up to
1033 handle that properly. */
1034 src_dev_link = gs_input_profile->isdevlink;
1035 }
1036 /* No link was found so lets create a new one if there is room. This will
1037 usually return a link that is not yet valid, but may return a valid link
1038 if another thread has already created it */
1039 if (gsicc_alloc_link_entry(icc_link_cache, &link, hash, include_softproof,
1040 include_devicelink))
1041 return link;
1042 if (link == NULL)
1043 return NULL; /* error, couldn't allocate a link */
1044
1045 /* Here the link was new and the contents have valid=false and we */
1046 /* own the lock for the link_profile. Build the profile, set valid */
1047 /* to true and release the lock. */
1048 /* Now compute the link contents */
1049 cms_input_profile = gs_input_profile->profile_handle;
1050 /* Check if the source was generated from a PS CIE color space. If yes,
1051 then we need to make sure that the CMM does not do something like
1052 force a white point mapping like lcms does */
1053 if (gsicc_profile_from_ps(gs_input_profile)) {
1054 cms_flags = cms_flags | gscms_avoid_white_fix_flag(memory);
1055 }
1056 if (cms_input_profile == NULL) {
1057 if (gs_input_profile->buffer != NULL) {
1058 cms_input_profile =
1059 gsicc_get_profile_handle_buffer(gs_input_profile->buffer,
1060 gs_input_profile->buffer_size,
1061 memory);
1062 if (cms_input_profile == NULL)
1063 return NULL;
1064 gs_input_profile->profile_handle = cms_input_profile;
1065 /* This *must* be a default profile that was not set up at start-up/
1066 However it could be one from the icc creator code which does not
1067 do an initialization at the time of creation from CalRGB etc. */
1068 code = gsicc_initialize_default_profile(gs_input_profile);
1069 if (code < 0) return NULL;
1070 } else {
1071 /* Cant create the link. No profile present,
1072 nor any defaults to use for this. Really
1073 need to throw an error for this case. */
1074 gsicc_remove_link(link, cache_mem);
1075 return NULL;
1076 }
1077 }
1078 /* No need to worry about an output profile handle if our source is a
1079 device link profile */
1080 if (!src_dev_link) {
1081 cms_output_profile = gs_output_profile->profile_handle;
1082 }
1083 if (cms_output_profile == NULL && !src_dev_link) {
1084 if (gs_output_profile->buffer != NULL) {
1085 cms_output_profile =
1086 gsicc_get_profile_handle_buffer(gs_output_profile->buffer,
1087 gs_output_profile->buffer_size,
1088 memory);
1089 gs_output_profile->profile_handle = cms_output_profile;
1090 /* This *must* be a default profile that was not set up at start-up */
1091 code = gsicc_initialize_default_profile(gs_output_profile);
1092 if (code < 0) return NULL;
1093 } else {
1094 /* See if we have a clist device pointer. */
1095 if ( gs_output_profile->dev != NULL ) {
1096 /* ICC profile should be in clist. This is
1097 the first call to it. */
1098 cms_output_profile =
1099 gsicc_get_profile_handle_clist(gs_output_profile,
1100 gs_output_profile->memory);
1101 gs_output_profile->profile_handle = cms_output_profile;
1102 } else {
1103 /* Cant create the link. No profile present,
1104 nor any defaults to use for this. Really
1105 need to throw an error for this case. */
1106 gsicc_remove_link(link, cache_mem);
1107 return NULL;
1108 }
1109 }
1110 }
1111 if (include_softproof) {
1112 cms_proof_profile = proof_profile->profile_handle;
1113 if (cms_proof_profile == NULL) {
1114 if (proof_profile->buffer != NULL) {
1115 cms_proof_profile =
1116 gsicc_get_profile_handle_buffer(proof_profile->buffer,
1117 proof_profile->buffer_size,
1118 memory);
1119 proof_profile->profile_handle = cms_proof_profile;
1120 if (!gscms_is_threadsafe())
1121 gx_monitor_enter(proof_profile->lock);
1122 } else {
1123 /* Cant create the link */
1124 gsicc_remove_link(link, cache_mem);
1125 return NULL;
1126 }
1127 }
1128 }
1129 if (include_devicelink) {
1130 cms_devlink_profile = devlink_profile->profile_handle;
1131 if (cms_devlink_profile == NULL) {
1132 if (devlink_profile->buffer != NULL) {
1133 cms_devlink_profile =
1134 gsicc_get_profile_handle_buffer(devlink_profile->buffer,
1135 devlink_profile->buffer_size,
1136 memory);
1137 devlink_profile->profile_handle = cms_devlink_profile;
1138 if (!gscms_is_threadsafe())
1139 gx_monitor_enter(devlink_profile->lock);
1140 } else {
1141 /* Cant create the link */
1142 gsicc_remove_link(link, cache_mem);
1143 return NULL;
1144 }
1145 }
1146 }
1147 /* Profile reading of same structure not thread safe in CMM */
1148 if (!gscms_is_threadsafe()) {
1149 gx_monitor_enter(gs_input_profile->lock);
1150 if (!src_dev_link) {
1151 gx_monitor_enter(gs_output_profile->lock);
1152 }
1153 }
1154 /* We may have to worry about special handling for DeviceGray to
1155 DeviceCMYK to ensure that Gray is mapped to K only. This is only
1156 done once and then it is cached and the link used. Note that Adobe
1157 appears to do this only when the source color space was DeviceGray.
1158 For us, this requirement is meant by the test of
1159 gs_input_profile->default_match == DEFAULT_GRAY */
1160 if (!src_dev_link && gs_output_profile->data_cs == gsCMYK &&
1161 gs_input_profile->data_cs == gsGRAY &&
1162 gs_input_profile->default_match == DEFAULT_GRAY &&
1163 pgs->icc_manager != NULL && devicegraytok) {
1164 if (icc_manager->graytok_profile == NULL) {
1165 icc_manager->graytok_profile =
1166 gsicc_set_iccsmaskprofile(GRAY_TO_K, strlen(GRAY_TO_K),
1167 pgs->icc_manager,
1168 pgs->icc_manager->memory->stable_memory);
1169 if (icc_manager->graytok_profile == NULL) {
1170 /* Cant create the link */ /* FIXME: clean up allocations and locksso far ??? */
1171 gsicc_remove_link(link, cache_mem);
1172 return NULL;
1173 }
1174 }
1175 if (icc_manager->smask_profiles == NULL) {
1176 code = gsicc_initialize_iccsmask(icc_manager);
1177 }
1178 cms_input_profile =
1179 icc_manager->smask_profiles->smask_gray->profile_handle;
1180 cms_output_profile =
1181 icc_manager->graytok_profile->profile_handle;
1182 /* Turn off bp compensation in this case as there is a bug in lcms */
1183 rendering_params->black_point_comp = false;
1184 cms_flags = 0; /* Turn off any flag setting */
1185 }
1186 /* Get the link with the proof and or device link profile */
1187 if (include_softproof || include_devicelink || src_dev_link) {
1188 link_handle = gscms_get_link_proof_devlink(cms_input_profile,
1189 cms_proof_profile,
1190 cms_output_profile,
1191 cms_devlink_profile,
1192 rendering_params,
1193 src_dev_link, cms_flags,
1194 cache_mem->non_gc_memory);
1195 if (!gscms_is_threadsafe()) {
1196 if (include_softproof) {
1197 gx_monitor_leave(proof_profile->lock);
1198 }
1199 if (include_devicelink) {
1200 gx_monitor_leave(devlink_profile->lock);
1201 }
1202 }
1203 } else {
1204 link_handle = gscms_get_link(cms_input_profile, cms_output_profile,
1205 rendering_params, cms_flags,
1206 cache_mem->non_gc_memory);
1207 }
1208 if (!gscms_is_threadsafe()) {
1209 if (!src_dev_link) {
1210 gx_monitor_leave(gs_output_profile->lock);
1211 }
1212 gx_monitor_leave(gs_input_profile->lock);
1213 }
1214 if (link_handle != NULL) {
1215 if (gs_input_profile->data_cs == gsGRAY)
1216 pageneutralcolor = false;
1217
1218 gsicc_set_link_data(link, link_handle, hash, icc_link_cache->lock,
1219 include_softproof, include_devicelink, pageneutralcolor,
1220 gs_input_profile->data_cs);
1221 if_debug2m(gs_debug_flag_icc, cache_mem,
1222 "[icc] New Link = 0x%p, hash = %lld \n",
1223 link, (long long)hash.link_hashcode);
1224 if_debug2m(gs_debug_flag_icc, cache_mem,
1225 "[icc] input_numcomps = %d, input_hash = %lld \n",
1226 gs_input_profile->num_comps,
1227 (long long)gs_input_profile->hashcode);
1228 if_debug2m(gs_debug_flag_icc, cache_mem,
1229 "[icc] output_numcomps = %d, output_hash = %lld \n",
1230 gs_output_profile->num_comps,
1231 (long long)gs_output_profile->hashcode);
1232 } else {
1233 /* If other threads are waiting, we won't have set link->valid true. */
1234 /* This could result in an infinite loop if other threads are waiting */
1235 /* for it to be made valid. (see gsicc_findcachelink). */
1236 link->ref_count--; /* this thread no longer using this link entry */
1237 if_debug2m('^', link->memory, "[^]icclink 0x%p -- => %d\n",
1238 link, link->ref_count);
1239
1240 if (icc_link_cache->cache_full) {
1241 icc_link_cache->cache_full = false;
1242 gx_semaphore_signal(icc_link_cache->full_wait); /* let a waiting thread run */
1243 }
1244 gx_monitor_leave(link->lock);
1245 gsicc_remove_link(link, cache_mem);
1246 return NULL;
1247 }
1248 return link;
1249 }
1250
1251 /* The following is used to transform a named color value at a particular tint
1252 value to the output device values. This function is provided only as a
1253 demonstration and will likely need to be altered and optimized for those wishing
1254 to perform full spot color look-up support.
1255
1256 The object used to perform the transformation is typically
1257 a look-up table that contains the spot color name and a CIELAB value for
1258 100% colorant (it could also contain device values in the table).
1259 It can be more complex where-by you have a 1-D lut that
1260 provides CIELAB values or direct device values as a function of tint. In
1261 such a case, the table would be interpolated to compute all possible tint values.
1262 If CIELAB values are provided, they can be pushed through the
1263 device profile using the CMM. In this particular demonstration, we simply
1264 provide CIELAB for a few color names in the file
1265 toolbin/color/named_color/named_color_table.txt .
1266 The tint value is used to scale the CIELAB value from 100% colorant to a D50
1267 whitepoint. The resulting CIELAB value is then pushed through the CMM to
1268 obtain device values for the current device. The file named_colors.pdf
1269 which is in toolbin/color/named_color/ contains these
1270 spot colors and will enable the user to see how the code behaves. The named
1271 color table is specified to ghostscript by the command line option
1272 -sNamedProfile=./toolbin/color/named_color/named_color_table.txt (or with
1273 full path name). If it is desired to have ghostscript compiled with the
1274 named color table, it can be placed in the iccprofiles directory and then
1275 build ghostscript with COMPILE_INITS=1. When specified the file contents
1276 are pointed to by the buffer member variable of the device_named profile in
1277 profile manager. When the first call occurs in here, the contents of the
1278 buffer are parsed and placed into a custom stucture that is pointed to by
1279 the profile pointer. Note that this pointer is not visible to the garbage
1280 collector and should be allocated in non-gc memory as is demonstrated in
1281 this sample. The structure elements are released when the profile is
1282 destroyed through the call to gsicc_named_profile_release, which is set
1283 as the value of the profile member variable release.
1284
1285 Note that there are calls defined in gsicc_littlecms.c that will create link
1286 transforms between Named Color ICC profiles and the output device. Such
1287 profiles are rarely used (at least I have not run across any yet) so the
1288 code is currently not used. Also note that for those serious about named
1289 color support, a cache as well as efficient table-look-up methods would
1290 likely be important for performance.
1291
1292 Finally note that PANTONE is a registered trademark and PANTONE colors are a
1293 licensed product of XRITE Inc. See http://www.pantone.com
1294 for more information. Licensees of Pantone color libraries or similar
1295 libraries should find it straight forward to interface. Pantone names are
1296 referred to in named_color_table.txt and contained in the file named_colors.pdf.
1297
1298 !!!!IT WILL BE NECESSARY TO PERFORM THE PROPER DEALLOCATION
1299 CLEAN-UP OF THE STRUCTURES WHEN rc_free_icc_profile OCCURS FOR THE NAMED
1300 COLOR PROFILE!!!!!! See gsicc_named_profile_release below for an example.
1301 This is set in the profile release member variable.
1302 */
1303
1304 /* Define the demo structure and function for named color look-up */
1305
1306 typedef struct gsicc_namedcolortable_s {
1307 gsicc_namedcolor_t *named_color; /* The named color */
1308 unsigned int number_entries; /* The number of entries */
1309 gs_memory_t *memory;
1310 } gsicc_namedcolortable_t;
1311
1312 /* Support functions for parsing buffer */
1313
1314 static int
get_to_next_line(char ** buffptr,int * buffer_count)1315 get_to_next_line(char **buffptr, int *buffer_count)
1316 {
1317 while (1) {
1318 if (**buffptr == ';') {
1319 (*buffptr)++;
1320 (*buffer_count)--;
1321 return(0);
1322 } else {
1323 (*buffptr)++;
1324 (*buffer_count)--;
1325 }
1326 if (*buffer_count <= 0) {
1327 return -1;
1328 }
1329 }
1330 }
1331
1332 /* Release the structures we allocated in gsicc_transform_named_color */
1333 static void
gsicc_named_profile_release(void * ptr,gs_memory_t * memory)1334 gsicc_named_profile_release(void *ptr, gs_memory_t *memory)
1335 {
1336 gsicc_namedcolortable_t *namedcolor_table = (gsicc_namedcolortable_t*) ptr;
1337 unsigned int num_entries;
1338 gs_memory_t *mem;
1339 int k;
1340 gsicc_namedcolor_t *namedcolor_data;
1341
1342 if (namedcolor_table != NULL) {
1343 mem = namedcolor_table->memory;
1344 num_entries = namedcolor_table->number_entries;
1345 namedcolor_data = namedcolor_table->named_color;
1346
1347 for (k = 0; k < num_entries; k++) {
1348 gs_free(mem, namedcolor_data[k].colorant_name, 1,
1349 namedcolor_data[k].name_size + 1,
1350 "gsicc_named_profile_release (colorant_name)");
1351 }
1352
1353 gs_free(mem, namedcolor_data, num_entries, sizeof(gsicc_namedcolor_t),
1354 "gsicc_named_profile_release (namedcolor_data)");
1355
1356 gs_free(namedcolor_table->memory, namedcolor_table, 1,
1357 sizeof(gsicc_namedcolortable_t),
1358 "gsicc_named_profile_release (namedcolor_table)");
1359 }
1360 }
1361
1362 static int
create_named_profile(gs_memory_t * mem,cmm_profile_t * named_profile)1363 create_named_profile(gs_memory_t *mem, cmm_profile_t *named_profile)
1364 {
1365 /* Create the structure that we will use in searching */
1366 /* Note that we do this in non-GC memory since the
1367 profile pointer is not GC'd */
1368 gsicc_namedcolortable_t *namedcolor_table;
1369 gsicc_namedcolor_t *namedcolor_data;
1370 char *buffptr;
1371 int buffer_count;
1372 int count;
1373 unsigned int num_entries;
1374 int code;
1375 int k, j;
1376 char *pch, *temp_ptr, *last = NULL;
1377 bool done;
1378 int curr_name_size;
1379 float lab[3];
1380
1381 namedcolor_table =
1382 (gsicc_namedcolortable_t*)gs_malloc(mem, 1,
1383 sizeof(gsicc_namedcolortable_t), "create_named_profile");
1384 if (namedcolor_table == NULL)
1385 return_error(gs_error_VMerror);
1386 namedcolor_table->memory = mem;
1387
1388 /* Parse buffer and load the structure we will be searching */
1389 buffptr = (char*)named_profile->buffer;
1390 buffer_count = named_profile->buffer_size;
1391 count = sscanf(buffptr, "%d", &num_entries);
1392 if (num_entries < 1 || count == 0) {
1393 gs_free(mem, namedcolor_table, 1, sizeof(gsicc_namedcolortable_t),
1394 "create_named_profile");
1395 return -1;
1396 }
1397
1398 code = get_to_next_line(&buffptr, &buffer_count);
1399 if (code < 0) {
1400 gs_free(mem, namedcolor_table, 1, sizeof(gsicc_namedcolortable_t),
1401 "create_named_profile");
1402 return -1;
1403 }
1404 namedcolor_data =
1405 (gsicc_namedcolor_t*)gs_malloc(mem, num_entries,
1406 sizeof(gsicc_namedcolor_t), "create_named_profile");
1407 if (namedcolor_data == NULL) {
1408 gs_free(mem, namedcolor_table, num_entries,
1409 sizeof(gsicc_namedcolortable_t), "create_named_profile");
1410 return_error(gs_error_VMerror);
1411 }
1412 namedcolor_table->number_entries = num_entries;
1413 namedcolor_table->named_color = namedcolor_data;
1414 for (k = 0; k < num_entries; k++) {
1415 if (k == 0) {
1416 pch = gs_strtok(buffptr, ",;", &last);
1417 } else {
1418 pch = gs_strtok(NULL, ",;", &last);
1419 }
1420 /* Remove any /0d /0a stuff from start */
1421 temp_ptr = pch;
1422 done = 0;
1423 while (!done) {
1424 if (*temp_ptr == 0x0d || *temp_ptr == 0x0a) {
1425 temp_ptr++;
1426 } else {
1427 done = 1;
1428 }
1429 }
1430 curr_name_size = strlen(temp_ptr);
1431 namedcolor_data[k].name_size = curr_name_size;
1432
1433 /* +1 for the null */
1434 namedcolor_data[k].colorant_name =
1435 (char*)gs_malloc(mem, 1, curr_name_size + 1,
1436 "create_named_profile");
1437 if (namedcolor_data[k].colorant_name == NULL) {
1438 /* Free up all that has been allocated so far */
1439 for (j = 0; j < k; j++) {
1440 gs_free(mem, namedcolor_table, 1, namedcolor_data[j].name_size+1,
1441 "create_named_profile");
1442 }
1443 gs_free(mem, namedcolor_data, num_entries, sizeof(gsicc_namedcolor_t),
1444 "create_named_profile");
1445 gs_free(mem, namedcolor_table, num_entries,
1446 sizeof(gsicc_namedcolortable_t), "create_named_profile");
1447 return_error(gs_error_VMerror);
1448 }
1449 strncpy(namedcolor_data[k].colorant_name, temp_ptr,
1450 namedcolor_data[k].name_size + 1);
1451 for (j = 0; j < 3; j++) {
1452 pch = gs_strtok(NULL, ",;", &last);
1453 count = sscanf(pch, "%f", &(lab[j]));
1454 }
1455 lab[0] = lab[0] * 65535 / 100.0;
1456 lab[1] = (lab[1] + 128.0) * 65535 / 255;
1457 lab[2] = (lab[2] + 128.0) * 65535 / 255;
1458 for (j = 0; j < 3; j++) {
1459 if (lab[j] > 65535) lab[j] = 65535;
1460 if (lab[j] < 0) lab[j] = 0;
1461 namedcolor_data[k].lab[j] = (unsigned short)lab[j];
1462 }
1463 }
1464
1465 /* Assign to the profile pointer */
1466 named_profile->profile_handle = namedcolor_table;
1467 named_profile->release = gsicc_named_profile_release;
1468 return 0;
1469 }
1470
1471
1472 /* Check for support of named color at time of install of DeviceN or Sep color space.
1473 This function returning false means that Process colorants (e.g. CMYK) will
1474 not undergo color management. */
1475 bool
gsicc_support_named_color(const gs_color_space * pcs,const gs_gstate * pgs)1476 gsicc_support_named_color(const gs_color_space *pcs, const gs_gstate *pgs)
1477 {
1478 cmm_profile_t *named_profile;
1479 gsicc_namedcolortable_t *namedcolor_table;
1480 unsigned int num_entries;
1481 int k, code, i, num_comp, num_spots=0, num_process=0, num_other=0;
1482 gs_color_space_index type = gs_color_space_get_index(pcs);
1483 char **names = NULL;
1484 byte *pname;
1485 uint name_size;
1486 bool is_supported;
1487
1488 /* Get the data for the named profile */
1489 named_profile = pgs->icc_manager->device_named;
1490
1491 if (named_profile->buffer != NULL &&
1492 named_profile->profile_handle == NULL) {
1493 code = create_named_profile(pgs->memory->non_gc_memory, named_profile);
1494 if (code < 0)
1495 return false;
1496 }
1497 namedcolor_table =
1498 (gsicc_namedcolortable_t*)named_profile->profile_handle;
1499 num_entries = namedcolor_table->number_entries;
1500
1501 /* Get the color space specifics */
1502 if (type == gs_color_space_index_DeviceN) {
1503 names = pcs->params.device_n.names;
1504 num_comp = pcs->params.device_n.num_components;
1505 } else if (type == gs_color_space_index_Separation) {
1506 pname = (byte *)pcs->params.separation.sep_name;
1507 num_comp = 1;
1508 } else
1509 return false;
1510
1511 /* Step through the color space colorants */
1512 for (i = 0; i < num_comp; i++) {
1513 if (type == gs_color_space_index_DeviceN) {
1514 pname = (byte *)names[i];
1515 name_size = strlen(names[i]);
1516 }
1517 else {
1518 name_size = strlen(pcs->params.separation.sep_name);
1519 }
1520
1521 /* Classify */
1522 if (strncmp((char *)pname, "None", name_size) == 0 ||
1523 strncmp((char *)pname, "All", name_size) == 0) {
1524 num_other++;
1525 } else {
1526 if (strncmp((char *)pname, "Cyan", name_size) == 0 ||
1527 strncmp((char *)pname, "Magenta", name_size) == 0 ||
1528 strncmp((char *)pname, "Yellow", name_size) == 0 ||
1529 strncmp((char *)pname, "Black", name_size) == 0) {
1530 num_process++;
1531 } else {
1532 num_spots++;
1533 }
1534 }
1535
1536 /* Check if the colorant is supported */
1537 is_supported = false;
1538 for (k = 0; k < num_entries; k++) {
1539 if (name_size == namedcolor_table->named_color[k].name_size) {
1540 if (strncmp((const char *)namedcolor_table->named_color[k].colorant_name,
1541 (const char *)pname, name_size) == 0) {
1542 is_supported = true;
1543 break;
1544 }
1545 }
1546 }
1547 if (!is_supported)
1548 return false;
1549 }
1550 /* If we made it this far, all the individual colorants are supported.
1551 If the names contained no spots, then let standard color management
1552 processing occur. It may be that some applications want standard
1553 processing in other cases. For example, [Cyan Magenta Varnish] to
1554 a tiffsep-like device one may want color management to occur for the
1555 Cyan and Magenta but Varnish to pass to the separation device unmolested.
1556 You will then want to add "Varnish" to the list of names in the above test
1557 for the Process colorants of Cyan, Magenta, Yellow and Black to avoid
1558 "Varnish" being counted as a spot here */
1559 if (num_spots == 0)
1560 return false;
1561 return true;
1562 }
1563
1564 /* Function returns -1 if a name is not found. Otherwise it will transform
1565 the named colors and return 0 */
1566 int
gsicc_transform_named_color(const float tint_values[],gsicc_namedcolor_t color_names[],uint num_names,gx_color_value device_values[],const gs_gstate * pgs,gx_device * dev,cmm_profile_t * gs_output_profile,gsicc_rendering_param_t * rendering_params)1567 gsicc_transform_named_color(const float tint_values[],
1568 gsicc_namedcolor_t color_names[],
1569 uint num_names,
1570 gx_color_value device_values[],
1571 const gs_gstate *pgs, gx_device *dev,
1572 cmm_profile_t *gs_output_profile,
1573 gsicc_rendering_param_t *rendering_params)
1574 {
1575 unsigned int num_entries;
1576 cmm_profile_t *named_profile;
1577 gsicc_namedcolortable_t *namedcolor_table;
1578 int num_nonnone_names;
1579 uint k,j,n;
1580 int code;
1581 bool found_match;
1582 unsigned short psrc[GS_CLIENT_COLOR_MAX_COMPONENTS];
1583 unsigned short psrc_cm[GS_CLIENT_COLOR_MAX_COMPONENTS];
1584 unsigned short *psrc_temp;
1585 unsigned short white_lab[3] = {65535, 32767, 32767};
1586 gsicc_link_t *icc_link;
1587 cmm_profile_t *curr_output_profile;
1588 gsicc_rendering_param_t render_cond;
1589 cmm_dev_profile_t *dev_profile;
1590 int indices[GS_CLIENT_COLOR_MAX_COMPONENTS];
1591 gs_memory_t *nongc_mem = pgs->memory->non_gc_memory;
1592
1593 /* Set indices to avoid use of uninitialized index. It is actually not
1594 possible to access them using real data but someone could perhaps
1595 be malicious and cause a problem */
1596 memset(&(indices[0]), 0, sizeof(indices));
1597
1598 /* Check if the data that we have has already been generated. */
1599 if (pgs->icc_manager != NULL) {
1600 if (pgs->icc_manager->device_named != NULL) {
1601 named_profile = pgs->icc_manager->device_named;
1602 if (named_profile->buffer != NULL &&
1603 named_profile->profile_handle == NULL) {
1604 code = create_named_profile(nongc_mem, named_profile);
1605 if (code < 0)
1606 return -1;
1607 }
1608 namedcolor_table =
1609 (gsicc_namedcolortable_t*)named_profile->profile_handle;
1610 num_entries = namedcolor_table->number_entries;
1611
1612 /* Go through each of our spot names, getting the color value for
1613 each one. */
1614 num_nonnone_names = num_names;
1615 for (n = 0; n < num_names; n++) {
1616 /* Search our structure for the color name. Ignore the None
1617 colorant names. All is a special case that someone may
1618 want to detect and do some special handling for. In this
1619 particular example we would punt with All and let the default
1620 methods handle it */
1621 found_match = false;
1622
1623 if (strncmp("None", (const char *)color_names[n].colorant_name,
1624 color_names[n].name_size) == 0) {
1625 num_nonnone_names--;
1626 } else {
1627 /* Colorant was not None */
1628 for (k = 0; k < num_entries; k++) {
1629 if (color_names[n].name_size ==
1630 namedcolor_table->named_color[k].name_size) {
1631 if (strncmp((const char *)namedcolor_table->named_color[k].colorant_name,
1632 (const char *)color_names[n].colorant_name, color_names[n].name_size) == 0) {
1633 found_match = true;
1634 break;
1635 }
1636 }
1637 }
1638 if (found_match) {
1639 indices[n] = k;
1640 } else {
1641 /* We do not know this colorant, return -1 */
1642 return -1;
1643 }
1644 }
1645 }
1646 if (num_nonnone_names < 1)
1647 return -1; /* No non-None colorants. */
1648 /* We have all the colorants. Lets go through and see if we can
1649 make something that looks like a merge of the various ones */
1650 /* Apply tint, blend LAB values. Note that we may have wanted to
1651 check if we even want to do this. It is possible that the
1652 device directly supports this particular colorant. One may
1653 want to check the alt tint transform boolean */
1654
1655 /* Start with white */
1656 for (j = 0; j < 3; j++) {
1657 psrc[j] = white_lab[j];
1658 }
1659
1660 for (n = 0; n < num_nonnone_names; n++) {
1661 /* Blend with the current color based upon current tint value */
1662 for (j = 0; j < 3; j++) {
1663 psrc[j] = (unsigned short)
1664 ((float) namedcolor_table->named_color[indices[n]].lab[j] * tint_values[n]
1665 + (float) psrc[j] * (1.0 - tint_values[n]));
1666 }
1667 }
1668
1669 /* Push LAB value through CMM to get CMYK device values */
1670 /* Note that there are several options here. You could us an NCLR
1671 icc profile to compute the device colors that you want. For,
1672 example if the output device had an NCLR profile.
1673 However, what you MUST do here is set ALL the device values.
1674 Hence, below we initialize all of them to zero and in this
1675 particular example, set only the ones that were output from
1676 the device profile */
1677 if ( gs_output_profile != NULL ) {
1678 curr_output_profile = gs_output_profile;
1679 } else {
1680 /* Use the device profile. Note if one was not set for the
1681 device, the default CMYK profile is used. Note that
1682 if we specified and NCLR profile it will be used here */
1683 code = dev_proc(dev, get_profile)(dev, &dev_profile);
1684 gsicc_extract_profile(dev->graphics_type_tag,
1685 dev_profile, &(curr_output_profile),
1686 &render_cond);
1687 }
1688 icc_link = gsicc_get_link_profile(pgs, dev,
1689 pgs->icc_manager->lab_profile,
1690 curr_output_profile, rendering_params,
1691 pgs->memory, false);
1692 if (icc_link->is_identity) {
1693 psrc_temp = &(psrc[0]);
1694 } else {
1695 /* Transform the color */
1696 psrc_temp = &(psrc_cm[0]);
1697 (icc_link->procs.map_color)(dev, icc_link, psrc, psrc_temp, 2);
1698 }
1699 gsicc_release_link(icc_link);
1700
1701 /* Clear out ALL the color values */
1702 for (k = 0; k < dev->color_info.num_components; k++){
1703 device_values[k] = 0;
1704 }
1705 /* Set only the values that came from the profile. By default
1706 this would generally be just CMYK values. For the equivalent
1707 color computation case it certainly will be. If someone
1708 specified an NCLR profile it could be more. Note that if an
1709 NCLR profile is being used we will want to make sure the colorant
1710 order is correct */
1711 for (k = 0; k < curr_output_profile->num_comps; k++){
1712 device_values[k] = psrc_temp[k];
1713 }
1714 return 0;
1715 }
1716 }
1717 return -1; /* Color not found */
1718 }
1719
1720 /* Used by gs to notify the ICC manager that we are done with this link for now */
1721 /* This may release elements waiting on an icc_link_cache slot */
1722 void
gsicc_release_link(gsicc_link_t * icclink)1723 gsicc_release_link(gsicc_link_t *icclink)
1724 {
1725 gsicc_link_cache_t *icc_link_cache;
1726
1727 if (icclink == NULL)
1728 return;
1729
1730 icc_link_cache = icclink->icc_link_cache;
1731
1732 gx_monitor_enter(icc_link_cache->lock);
1733 if_debug2m('^', icclink->memory, "[^]icclink 0x%p -- => %d\n",
1734 icclink, icclink->ref_count - 1);
1735 /* Decrement the reference count */
1736 if (--(icclink->ref_count) == 0) {
1737
1738 gsicc_link_t *curr, *prev;
1739
1740 /* Find link in cache, and move it to the end of the list. */
1741 /* This way zero ref_count links are found LRU first */
1742 curr = icc_link_cache->head;
1743 prev = NULL;
1744 while (curr != icclink) {
1745 prev = curr;
1746 curr = curr->next;
1747 };
1748 if (prev == NULL) {
1749 /* this link was the head */
1750 icc_link_cache->head = curr->next;
1751 } else {
1752 prev->next = curr->next; /* de-link this one */
1753 }
1754 /* Find the first zero-ref entry on the list */
1755 curr = icc_link_cache->head;
1756 prev = NULL;
1757 while (curr != NULL && curr->ref_count > 0) {
1758 prev = curr;
1759 curr = curr->next;
1760 }
1761 /* Found where to link this one into the tail of the list */
1762 if (prev == NULL) {
1763 icc_link_cache->head = icclink;
1764 icclink->next = icc_link_cache->head->next;
1765 } else {
1766 /* link this one in here */
1767 prev->next = icclink;
1768 icclink->next = curr;
1769 }
1770 /* Finally, if some thread was waiting because the cache was full, let it run */
1771 if (icc_link_cache->cache_full) {
1772 icc_link_cache->cache_full = false;
1773 gx_semaphore_signal(icc_link_cache->full_wait); /* let a waiting thread run */
1774 }
1775 }
1776 gx_monitor_leave(icc_link_cache->lock);
1777 }
1778
1779 /* Used to initialize the buffer description prior to color conversion */
1780 void
gsicc_init_buffer(gsicc_bufferdesc_t * buffer_desc,unsigned char num_chan,unsigned char bytes_per_chan,bool has_alpha,bool alpha_first,bool is_planar,int plane_stride,int row_stride,int num_rows,int pixels_per_row)1781 gsicc_init_buffer(gsicc_bufferdesc_t *buffer_desc, unsigned char num_chan, unsigned char bytes_per_chan,
1782 bool has_alpha, bool alpha_first, bool is_planar, int plane_stride, int row_stride,
1783 int num_rows, int pixels_per_row)
1784 {
1785 buffer_desc->num_chan = num_chan;
1786 buffer_desc->bytes_per_chan = bytes_per_chan;
1787 buffer_desc->has_alpha = has_alpha;
1788 buffer_desc->alpha_first = alpha_first;
1789 buffer_desc->is_planar = is_planar;
1790 buffer_desc->plane_stride = plane_stride;
1791 buffer_desc->row_stride = row_stride;
1792 buffer_desc->num_rows = num_rows;
1793 buffer_desc->pixels_per_row = pixels_per_row;
1794
1795 /* sample endianess is consistent across platforms */
1796 buffer_desc->little_endian = true;
1797
1798 }
1799
1800 /* Return the proper component numbers based upon the profiles of the device.
1801 This is in here since it is usually called when creating and using a link
1802 from the link cache. */
1803 int
gsicc_get_device_profile_comps(const cmm_dev_profile_t * dev_profile)1804 gsicc_get_device_profile_comps(const cmm_dev_profile_t *dev_profile)
1805 {
1806 if (dev_profile->link_profile == NULL) {
1807 return dev_profile->device_profile[0]->num_comps;
1808 } else {
1809 return dev_profile->link_profile->num_comps_out;
1810 }
1811 }
1812