/src/ghostpdl/base/gscdevn.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* Copyright (C) 2001-2025 Artifex Software, Inc. |
2 | | All Rights Reserved. |
3 | | |
4 | | This software is provided AS-IS with no warranty, either express or |
5 | | implied. |
6 | | |
7 | | This software is distributed under license and may not be copied, |
8 | | modified or distributed except as expressly authorized under the terms |
9 | | of the license contained in the file LICENSE in this distribution. |
10 | | |
11 | | Refer to licensing information at http://www.artifex.com or contact |
12 | | Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco, |
13 | | CA 94129, USA, for further information. |
14 | | */ |
15 | | |
16 | | |
17 | | /* DeviceN color space and operation definition */ |
18 | | |
19 | | #include "memory_.h" |
20 | | #include "string_.h" |
21 | | #include "gx.h" |
22 | | #include "gserrors.h" |
23 | | #include "gscdevn.h" |
24 | | #include "gsfunc.h" |
25 | | #include "gsrefct.h" |
26 | | #include "gsmatrix.h" /* for gscolor2.h */ |
27 | | #include "gsstruct.h" |
28 | | #include "gxcspace.h" |
29 | | #include "gxcdevn.h" |
30 | | #include "gxfarith.h" |
31 | | #include "gxfrac.h" |
32 | | #include "gxcmap.h" |
33 | | #include "gxgstate.h" |
34 | | #include "gscoord.h" |
35 | | #include "gzstate.h" |
36 | | #include "gxdevcli.h" |
37 | | #include "gsovrc.h" |
38 | | #include "stream.h" |
39 | | #include "gsnamecl.h" /* Custom color call back define */ |
40 | | #include "gsicc_manage.h" |
41 | | #include "gsicc.h" |
42 | | #include "gsicc_cache.h" |
43 | | #include "gxdevice.h" |
44 | | #include "gxcie.h" |
45 | | #include "gxdevsop.h" |
46 | | |
47 | | /* ---------------- Color space ---------------- */ |
48 | | |
49 | | /* GC descriptors */ |
50 | | gs_private_st_composite(st_color_space_DeviceN, gs_color_space, |
51 | | "gs_color_space_DeviceN", cs_DeviceN_enum_ptrs, cs_DeviceN_reloc_ptrs); |
52 | | private_st_device_n_colorant(); |
53 | | private_st_device_n_map(); |
54 | | |
55 | | /* Define the DeviceN color space type. */ |
56 | | static cs_proc_num_components(gx_num_components_DeviceN); |
57 | | static cs_proc_init_color(gx_init_DeviceN); |
58 | | static cs_proc_restrict_color(gx_restrict_DeviceN); |
59 | | static cs_proc_concrete_space(gx_concrete_space_DeviceN); |
60 | | static cs_proc_concretize_color(gx_concretize_DeviceN); |
61 | | static cs_proc_remap_concrete_color(gx_remap_concrete_DeviceN); |
62 | | static cs_proc_remap_color(gx_remap_DeviceN); |
63 | | static cs_proc_install_cspace(gx_install_DeviceN); |
64 | | static cs_proc_set_overprint(gx_set_overprint_DeviceN); |
65 | | static cs_proc_final(gx_final_DeviceN); |
66 | | static cs_proc_serialize(gx_serialize_DeviceN); |
67 | | static cs_proc_polarity(gx_polarity_DeviceN); |
68 | | |
69 | | const gs_color_space_type gs_color_space_type_DeviceN = { |
70 | | gs_color_space_index_DeviceN, true, false, |
71 | | &st_color_space_DeviceN, gx_num_components_DeviceN, |
72 | | gx_init_DeviceN, gx_restrict_DeviceN, |
73 | | gx_concrete_space_DeviceN, |
74 | | gx_concretize_DeviceN, gx_remap_concrete_DeviceN, |
75 | | gx_remap_DeviceN, gx_install_DeviceN, |
76 | | gx_set_overprint_DeviceN, |
77 | | gx_final_DeviceN, gx_no_adjust_color_count, |
78 | | gx_serialize_DeviceN, |
79 | | gx_cspace_is_linear_default, gx_polarity_DeviceN |
80 | | }; |
81 | | |
82 | | /* GC procedures */ |
83 | | |
84 | | static |
85 | 0 | ENUM_PTRS_BEGIN(cs_DeviceN_enum_ptrs) |
86 | 0 | return 0; |
87 | 0 | ENUM_PTR(0, gs_color_space, params.device_n.map); |
88 | 0 | ENUM_PTR(1, gs_color_space, params.device_n.colorants); |
89 | 0 | ENUM_PTRS_END |
90 | 0 | static RELOC_PTRS_BEGIN(cs_DeviceN_reloc_ptrs) |
91 | 0 | { |
92 | 0 | RELOC_PTR(gs_color_space, params.device_n.map); |
93 | 0 | RELOC_PTR(gs_color_space, params.device_n.colorants); |
94 | 0 | } |
95 | 0 | RELOC_PTRS_END |
96 | | |
97 | | /* ------ Public procedures ------ */ |
98 | | |
99 | | /* |
100 | | * Create a new DeviceN colorspace. |
101 | | */ |
102 | | int |
103 | | gs_cspace_new_DeviceN( |
104 | | gs_color_space **ppcs, |
105 | | uint num_components, |
106 | | gs_color_space *palt_cspace, |
107 | | gs_memory_t *pmem |
108 | | ) |
109 | 19.4k | { |
110 | 19.4k | gs_color_space *pcs; |
111 | 19.4k | gs_device_n_params *pcsdevn; |
112 | 19.4k | char **pnames; |
113 | 19.4k | int i, code; |
114 | | |
115 | 19.4k | if (palt_cspace == 0 || !palt_cspace->type->can_be_alt_space) |
116 | 0 | return_error(gs_error_rangecheck); |
117 | | |
118 | 19.4k | pcs = gs_cspace_alloc(pmem, &gs_color_space_type_DeviceN); |
119 | 19.4k | if (pcs == NULL) |
120 | 0 | return_error(gs_error_VMerror); |
121 | 19.4k | pcsdevn = &pcs->params.device_n; |
122 | 19.4k | pcsdevn->names = NULL; |
123 | 19.4k | pcsdevn->map = NULL; |
124 | 19.4k | pcsdevn->colorants = NULL; |
125 | 19.4k | pcsdevn->named_color_supported = false; |
126 | 19.4k | pcsdevn->num_process_names = 0; |
127 | 19.4k | pcsdevn->process_names = NULL; |
128 | 19.4k | pcsdevn->mem = pmem->non_gc_memory; |
129 | 19.4k | pcsdevn->all_none = false; |
130 | | |
131 | | /* Allocate space for color names list. */ |
132 | 19.4k | code = alloc_device_n_map(&pcsdevn->map, pmem, "gs_cspace_build_DeviceN"); |
133 | 19.4k | if (code < 0) { |
134 | 0 | gs_free_object(pmem, pcs, "gs_cspace_new_DeviceN"); |
135 | 0 | return code; |
136 | 0 | } |
137 | 19.4k | pnames = (char **)gs_alloc_bytes(pcsdevn->mem, num_components * sizeof(char *), "gs_cspace_new_DeviceN"); |
138 | 19.4k | if (pnames == 0) { |
139 | 0 | gs_free_object(pmem, pcsdevn->map, ".gs_cspace_build_DeviceN(map)"); |
140 | 0 | gs_free_object(pmem, pcs, "gs_cspace_new_DeviceN"); |
141 | 0 | return_error(gs_error_VMerror); |
142 | 0 | } |
143 | 45.0k | for (i=0; i<num_components; i++) |
144 | 25.5k | pnames[i] = NULL; /* empty string for check_DeviceN_component_names */ |
145 | 19.4k | pcs->base_space = palt_cspace; |
146 | 19.4k | rc_increment_cs(palt_cspace); |
147 | 19.4k | pcsdevn->names = pnames; |
148 | 19.4k | pcsdevn->num_components = num_components; |
149 | 19.4k | *ppcs = pcs; |
150 | 19.4k | return 0; |
151 | 19.4k | } |
152 | | |
153 | | /* Allocate and initialize a DeviceN map. */ |
154 | | int |
155 | | alloc_device_n_map(gs_device_n_map ** ppmap, gs_memory_t * mem, |
156 | | client_name_t cname) |
157 | 80.1k | { |
158 | 80.1k | gs_device_n_map *pimap; |
159 | | |
160 | 80.1k | rc_alloc_struct_1(pimap, gs_device_n_map, &st_device_n_map, mem, |
161 | 80.1k | return_error(gs_error_VMerror), cname); |
162 | 80.1k | pimap->tint_transform = 0; |
163 | 80.1k | pimap->tint_transform_data = 0; |
164 | 80.1k | pimap->cache_valid = false; |
165 | 80.1k | *ppmap = pimap; |
166 | 80.1k | return 0; |
167 | 80.1k | } |
168 | | |
169 | | /* |
170 | | * DeviceN and NChannel color spaces can have an attributes dict. In the |
171 | | * attribute dict can be a Colorants dict which contains Separation color |
172 | | * spaces. If the Colorant dict is present, the PS logic will build each of |
173 | | * the Separation color spaces in a temp gstate and then call this procedure |
174 | | * to attach the Separation color space to the DeviceN color space. |
175 | | * The parameter to this procedure is a colorant name. The Separation |
176 | | * color space is in the current (temp) gstate. The DeviceN color space is |
177 | | * in the next gstate down in the gstate list (pgs->saved). |
178 | | */ |
179 | | int |
180 | | gs_attachcolorant(char *sep_name, gs_gstate * pgs) |
181 | 0 | { |
182 | 0 | gs_color_space * pdevncs; |
183 | 0 | gs_device_n_colorant * patt; |
184 | | |
185 | | /* Verify that we have a DeviceN color space */ |
186 | 0 | if (!pgs->saved) |
187 | 0 | return_error(gs_error_rangecheck); |
188 | 0 | pdevncs = gs_currentcolorspace_inline(pgs->saved); |
189 | 0 | if (pdevncs->type != &gs_color_space_type_DeviceN) |
190 | 0 | return_error(gs_error_rangecheck); |
191 | | |
192 | | /* Allocate an attribute list element for our linked list of colorants */ |
193 | 0 | rc_alloc_struct_1(patt, gs_device_n_colorant, &st_device_n_colorant, |
194 | 0 | pgs->memory, return_error(gs_error_VMerror), |
195 | 0 | "gs_attachattributrescolorspace"); |
196 | | |
197 | | /* Point our attribute list entry to the attribute color space */ |
198 | 0 | patt->colorant_name = sep_name; |
199 | 0 | patt->cspace = gs_currentcolorspace_inline(pgs); |
200 | 0 | rc_increment_cs(patt->cspace); |
201 | | |
202 | | /* Link our new attribute color space to the DeviceN color space */ |
203 | 0 | patt->next = pdevncs->params.device_n.colorants; |
204 | 0 | pdevncs->params.device_n.colorants = patt; |
205 | |
|
206 | 0 | return 0; |
207 | 0 | } |
208 | | |
209 | | int |
210 | | gs_attach_colorant_to_space(char *sep_name, gs_color_space *pcs, gs_color_space *colorant_space, gs_memory_t *mem) |
211 | 0 | { |
212 | 0 | gs_device_n_colorant * patt; |
213 | |
|
214 | 0 | if (pcs->type != &gs_color_space_type_DeviceN) |
215 | 0 | return_error(gs_error_rangecheck); |
216 | | |
217 | | /* Allocate an attribute list element for our linked list of colorants */ |
218 | 0 | rc_alloc_struct_1(patt, gs_device_n_colorant, &st_device_n_colorant, |
219 | 0 | mem, return_error(gs_error_VMerror), |
220 | 0 | "gs_attachattributrescolorspace"); |
221 | | |
222 | | /* Point our attribute list entry to the attribute color space */ |
223 | 0 | patt->colorant_name = sep_name; |
224 | 0 | patt->cspace = colorant_space; |
225 | 0 | rc_increment_cs(patt->cspace); |
226 | | |
227 | | /* Link our new attribute color space to the DeviceN color space */ |
228 | 0 | patt->next = pcs->params.device_n.colorants; |
229 | 0 | pcs->params.device_n.colorants = patt; |
230 | |
|
231 | 0 | return 0; |
232 | 0 | } |
233 | | |
234 | | #if 0 /* Unused; Unsupported by gx_serialize_device_n_map. */ |
235 | | /* |
236 | | * Set the DeviceN tint transformation procedure. |
237 | | */ |
238 | | int |
239 | | gs_cspace_set_devn_proc(gs_color_space * pcspace, |
240 | | int (*proc)(const float *, |
241 | | float *, |
242 | | const gs_gstate *, |
243 | | void * |
244 | | ), |
245 | | void *proc_data |
246 | | ) |
247 | | { |
248 | | gs_device_n_map *pimap; |
249 | | |
250 | | if (gs_color_space_get_index(pcspace) != gs_color_space_index_DeviceN) |
251 | | return_error(gs_error_rangecheck); |
252 | | pimap = pcspace->params.device_n.map; |
253 | | pimap->tint_transform = proc; |
254 | | pimap->tint_transform_data = proc_data; |
255 | | pimap->cache_valid = false; |
256 | | return 0; |
257 | | } |
258 | | #endif |
259 | | |
260 | | /* |
261 | | * Check if we are using the alternate color space. |
262 | | */ |
263 | | bool |
264 | | using_alt_color_space(const gs_gstate * pgs) |
265 | 98.4k | { |
266 | 98.4k | return (pgs->color_component_map.use_alt_cspace); |
267 | 98.4k | } |
268 | | |
269 | | /* Map a DeviceN color using a Function. */ |
270 | | int |
271 | | map_devn_using_function(const float *in, float *out, |
272 | | const gs_gstate *pgs, void *data) |
273 | | |
274 | 11.6M | { |
275 | 11.6M | gs_function_t *const pfn = data; |
276 | | |
277 | 11.6M | return gs_function_evaluate(pfn, in, out); |
278 | 11.6M | } |
279 | | |
280 | | /* |
281 | | * Set the DeviceN tint transformation procedure to a Function. |
282 | | */ |
283 | | int |
284 | | gs_cspace_set_devn_function(gs_color_space *pcspace, gs_function_t *pfn) |
285 | 19.4k | { |
286 | 19.4k | gs_device_n_map *pimap; |
287 | | |
288 | 19.4k | if (gs_color_space_get_index(pcspace) != gs_color_space_index_DeviceN || |
289 | 19.4k | pfn->params.m != pcspace->params.device_n.num_components || |
290 | 19.4k | pfn->params.n != gs_color_space_num_components(pcspace->base_space) |
291 | 19.4k | ) |
292 | 0 | return_error(gs_error_rangecheck); |
293 | 19.4k | pimap = pcspace->params.device_n.map; |
294 | 19.4k | pimap->tint_transform = map_devn_using_function; |
295 | 19.4k | pimap->tint_transform_data = pfn; |
296 | 19.4k | pimap->cache_valid = false; |
297 | 19.4k | return 0; |
298 | 19.4k | } |
299 | | |
300 | | /* |
301 | | * If the DeviceN tint transformation procedure is a Function, |
302 | | * return the function object, otherwise return 0. |
303 | | */ |
304 | | gs_function_t * |
305 | | gs_cspace_get_devn_function(const gs_color_space *pcspace) |
306 | 19.4k | { |
307 | 19.4k | if (gs_color_space_get_index(pcspace) == gs_color_space_index_DeviceN && |
308 | 19.4k | pcspace->params.device_n.map->tint_transform == |
309 | 19.4k | map_devn_using_function) |
310 | 19.4k | return pcspace->params.device_n.map->tint_transform_data; |
311 | 0 | return 0; |
312 | 19.4k | } |
313 | | |
314 | | /* ------ Color space implementation ------ */ |
315 | | |
316 | | /* Return the number of components of a DeviceN space. */ |
317 | | static int |
318 | | gx_num_components_DeviceN(const gs_color_space * pcs) |
319 | 16.6M | { |
320 | 16.6M | return pcs->params.device_n.num_components; |
321 | 16.6M | } |
322 | | |
323 | | /* Determine best guess of polarity */ |
324 | | static gx_color_polarity_t |
325 | | gx_polarity_DeviceN(const gs_color_space * pcs) |
326 | 0 | { |
327 | | /* DeviceN initializes to 1.0 like a separation so |
328 | | for now, treat this as subtractive. It is possible |
329 | | that we may have to do a special test for Red, Green |
330 | | and Blue but for now, I believe the following is |
331 | | correct */ |
332 | 0 | return GX_CINFO_POLARITY_SUBTRACTIVE; |
333 | 0 | } |
334 | | |
335 | | /* Initialize a DeviceN color. */ |
336 | | static void |
337 | | gx_init_DeviceN(gs_client_color * pcc, const gs_color_space * pcs) |
338 | 50.2k | { |
339 | 50.2k | uint i; |
340 | | |
341 | 135k | for (i = 0; i < pcs->params.device_n.num_components; ++i) |
342 | 85.6k | pcc->paint.values[i] = 1.0; |
343 | 50.2k | } |
344 | | |
345 | | /* Force a DeviceN color into legal range. */ |
346 | | static void |
347 | | gx_restrict_DeviceN(gs_client_color * pcc, const gs_color_space * pcs) |
348 | 6.00M | { |
349 | 6.00M | uint i; |
350 | | |
351 | 14.5M | for (i = 0; i < pcs->params.device_n.num_components; ++i) { |
352 | 8.54M | double value = pcc->paint.values[i]; |
353 | 8.54M | pcc->paint.values[i] = (value <= 0 ? 0 : value >= 1 ? 1 : value); |
354 | 8.54M | } |
355 | 6.00M | } |
356 | | |
357 | | /* Remap a DeviceN color. */ |
358 | | static const gs_color_space * |
359 | | gx_concrete_space_DeviceN(const gs_color_space * pcs, |
360 | | const gs_gstate * pgs) |
361 | 15.8M | { |
362 | 15.8M | bool is_lab = false; |
363 | | #ifdef DEBUG |
364 | | /* |
365 | | * Verify that the color space and gs_gstate info match. |
366 | | */ |
367 | | if (pcs->id != pgs->color_component_map.cspace_id) |
368 | | dmprintf(pgs->memory, "gx_concrete_space_DeviceN: color space id mismatch"); |
369 | | #endif |
370 | | /* |
371 | | * Check if we are using the alternate color space. |
372 | | */ |
373 | 15.8M | if (pgs->color_component_map.use_alt_cspace) { |
374 | | /* Need to handle PS CIE space */ |
375 | 11.1M | if (gs_color_space_is_PSCIE(pcs->base_space)) { |
376 | 0 | if (pcs->base_space->icc_equivalent == NULL) { |
377 | 0 | (void)gs_colorspace_set_icc_equivalent(pcs->base_space, |
378 | 0 | &is_lab, pgs->memory); |
379 | 0 | } |
380 | 0 | return (pcs->base_space->icc_equivalent); |
381 | 0 | } |
382 | 11.1M | return cs_concrete_space(pcs->base_space, pgs); |
383 | 11.1M | } |
384 | | /* |
385 | | * DeviceN color spaces are concrete (when not using alt. color space). |
386 | | */ |
387 | 4.73M | return pcs; |
388 | 15.8M | } |
389 | | |
390 | | static int |
391 | | gx_remap_DeviceN(const gs_client_color * pcc, const gs_color_space * pcs, |
392 | | gx_device_color * pdc, const gs_gstate * pgs, gx_device * dev, |
393 | | gs_color_select_t select) |
394 | 8.62M | { |
395 | 8.62M | frac conc[GS_CLIENT_COLOR_MAX_COMPONENTS]; |
396 | 8.62M | const gs_color_space *pconcs; |
397 | 8.62M | int i = pcs->type->num_components(pcs),k; |
398 | 8.62M | int code = 0; |
399 | 8.62M | const gs_color_space *pacs = pcs->base_space; |
400 | 8.62M | gs_client_color temp; |
401 | 8.62M | bool mapped = false; |
402 | | |
403 | 8.62M | if ( pcs->cmm_icc_profile_data != NULL && pgs->color_component_map.use_alt_cspace) { |
404 | | /* This is the case where we have placed a N-CLR source profile for |
405 | | this color space */ |
406 | 0 | if (pcs->cmm_icc_profile_data->devicen_permute_needed) { |
407 | 0 | for ( k = 0; k < i; k++) { |
408 | 0 | temp.paint.values[k] = pcc->paint.values[pcs->cmm_icc_profile_data->devicen_permute[k]]; |
409 | 0 | } |
410 | 0 | code = pacs->type->remap_color(&temp, pacs, pdc, pgs, dev, select); |
411 | 0 | } else { |
412 | 0 | code = pacs->type->remap_color(pcc, pacs, pdc, pgs, dev, select); |
413 | 0 | } |
414 | 0 | return(code); |
415 | 8.62M | } else { |
416 | | /* Check if we are doing any named color management. This check happens |
417 | | regardless if we are doing the alternate tint transform or not. If |
418 | | desired, that check could be made in gsicc_transform_named_color and |
419 | | a decision made to return true or false */ |
420 | 8.62M | if (pgs->icc_manager->device_named != NULL) { |
421 | | /* Try to apply the direct replacement */ |
422 | 0 | mapped = gx_remap_named_color(pcc, pcs, pdc, pgs, dev, select); |
423 | 0 | } |
424 | 8.62M | if (!mapped) { |
425 | 8.62M | cmm_dev_profile_t *dev_profile; |
426 | 8.62M | code = dev_proc(dev, get_profile)(dev, &dev_profile); |
427 | 8.62M | if (code < 0) |
428 | 0 | return code; |
429 | 8.62M | code = (*pcs->type->concretize_color)(pcc, pcs, conc, pgs, dev); |
430 | 8.62M | if (code < 0) |
431 | 131 | return code; |
432 | 8.62M | pconcs = cs_concrete_space(pcs, pgs); |
433 | 8.62M | code = (*pconcs->type->remap_concrete_color)(pconcs, conc, pdc, pgs, dev, select, dev_profile); |
434 | 8.62M | } |
435 | | /* Save original color space and color info into dev color */ |
436 | 8.62M | i = any_abs(i); |
437 | 25.2M | for (i--; i >= 0; i--) |
438 | 16.6M | pdc->ccolor.paint.values[i] = pcc->paint.values[i]; |
439 | 8.62M | pdc->ccolor_valid = true; |
440 | 8.62M | return code; |
441 | 8.62M | } |
442 | 8.62M | } |
443 | | |
444 | | static int |
445 | | gx_concretize_DeviceN(const gs_client_color * pc, const gs_color_space * pcs, |
446 | | frac * pconc, const gs_gstate * pgs, gx_device *dev) |
447 | 15.8M | { |
448 | 15.8M | int code, tcode = 0; |
449 | 15.8M | gs_client_color cc; |
450 | 15.8M | gs_color_space *pacs = (gs_color_space*) (pcs->base_space); |
451 | 15.8M | gs_device_n_map *map = pcs->params.device_n.map; |
452 | 15.8M | bool is_lab; |
453 | 15.8M | int num_src_comps = pcs->params.device_n.num_components; |
454 | | |
455 | | #ifdef DEBUG |
456 | | /* |
457 | | * Verify that the color space and gs_gstate info match. |
458 | | */ |
459 | | if (pcs->id != pgs->color_component_map.cspace_id) |
460 | | dmprintf(dev->memory, "gx_concretize_DeviceN: color space id mismatch"); |
461 | | #endif |
462 | | |
463 | | /* |
464 | | * Check if we need to map into the alternate color space. |
465 | | * We must preserve tcode for implementing a semi-hack in the interpreter. |
466 | | */ |
467 | | |
468 | 15.8M | if (pgs->color_component_map.use_alt_cspace) { |
469 | | /* Check the 1-element cache first. */ |
470 | 11.1M | if (map->cache_valid) { |
471 | 0 | int i; |
472 | |
|
473 | 0 | for (i = pcs->params.device_n.num_components; --i >= 0;) { |
474 | 0 | if (map->tint[i] != pc->paint.values[i]) |
475 | 0 | break; |
476 | 0 | } |
477 | 0 | if (i < 0) { |
478 | 0 | int num_out = gs_color_space_num_components(pacs); |
479 | |
|
480 | 0 | for (i = 0; i < num_out; ++i) |
481 | 0 | pconc[i] = map->conc[i]; |
482 | 0 | return 0; |
483 | 0 | } |
484 | 0 | } |
485 | 11.1M | tcode = (*pcs->params.device_n.map->tint_transform) |
486 | 11.1M | (pc->paint.values, &cc.paint.values[0], |
487 | 11.1M | pgs, pcs->params.device_n.map->tint_transform_data); |
488 | 11.1M | if (tcode < 0) |
489 | 0 | return tcode; |
490 | 11.1M | (*pacs->type->restrict_color)(&cc, pacs); |
491 | | /* First check if this was PS based. */ |
492 | 11.1M | if (gs_color_space_is_PSCIE(pacs)) { |
493 | | /* We may have to rescale data to 0 to 1 range */ |
494 | 0 | rescale_cie_colors(pacs, &cc); |
495 | | /* If we have not yet created the profile do that now */ |
496 | 0 | if (pacs->icc_equivalent == NULL) { |
497 | 0 | code = gs_colorspace_set_icc_equivalent(pacs, &(is_lab), pgs->memory); |
498 | 0 | if (code < 0) |
499 | 0 | return code; |
500 | 0 | } |
501 | | /* Use the ICC equivalent color space */ |
502 | 0 | pacs = pacs->icc_equivalent; |
503 | 0 | } |
504 | 11.1M | if (pacs->cmm_icc_profile_data && |
505 | 11.1M | (pacs->cmm_icc_profile_data->data_cs == gsCIELAB || |
506 | 11.1M | pacs->cmm_icc_profile_data->islab)) { |
507 | | /* Get the data in a form that is concrete for the CMM */ |
508 | 0 | cc.paint.values[0] /= 100.0; |
509 | 0 | cc.paint.values[1] = (cc.paint.values[1]+128)/255.0; |
510 | 0 | cc.paint.values[2] = (cc.paint.values[2]+128)/255.0; |
511 | 0 | } |
512 | 11.1M | code = cs_concretize_color(&cc, pacs, pconc, pgs, dev); |
513 | 11.1M | } |
514 | 4.73M | else { |
515 | 4.73M | int i; |
516 | | |
517 | 10.6M | for (i = num_src_comps; --i >= 0;) |
518 | 5.88M | pconc[i] = gx_unit_frac(pc->paint.values[i]); |
519 | 4.73M | return 0; |
520 | 4.73M | } |
521 | 11.1M | return (code < 0 || tcode == 0 ? code : tcode); |
522 | 15.8M | } |
523 | | |
524 | | static int |
525 | | gx_remap_concrete_DeviceN(const gs_color_space * pcs, const frac * pconc, |
526 | | gx_device_color * pdc, const gs_gstate * pgs, |
527 | | gx_device * dev, gs_color_select_t select, |
528 | | const cmm_dev_profile_t *dev_profile) |
529 | 4.73M | { |
530 | 4.73M | int code = 0; |
531 | | |
532 | | #ifdef DEBUG |
533 | | /* |
534 | | * Verify that the color space and gs_gstate info match. |
535 | | */ |
536 | | if (pcs->id != pgs->color_component_map.cspace_id) |
537 | | dmprintf(pgs->memory, "gx_remap_concrete_DeviceN: color space id mismatch"); |
538 | | #endif |
539 | 4.73M | if (pgs->color_component_map.use_alt_cspace) { |
540 | 0 | const gs_color_space *pacs = pcs->base_space; |
541 | 0 | return (*pacs->type->remap_concrete_color) |
542 | 0 | (pacs, pconc, pdc, pgs, dev, select, dev_profile); |
543 | 4.73M | } else { |
544 | | /* If we are going DeviceN out to real sep device that understands these, |
545 | | and if the destination profile is DeviceN, we print the colors directly. |
546 | | Make sure to disable the DeviceN profile color map so that is does not |
547 | | get used in gx_remap_concrete_devicen. We probably should pass something |
548 | | through here but it is a pain due to the change in the proc. */ |
549 | 4.73M | bool temp_val; |
550 | | |
551 | 4.73M | if (dev_profile->spotnames != NULL && code >= 0) { |
552 | 0 | temp_val = dev_profile->spotnames->equiv_cmyk_set; |
553 | 0 | dev_profile->spotnames->equiv_cmyk_set = false; |
554 | 0 | gx_remap_concrete_devicen(pconc, pdc, pgs, dev, select, pcs); |
555 | 0 | dev_profile->spotnames->equiv_cmyk_set = temp_val; |
556 | 4.73M | } else { |
557 | 4.73M | gx_remap_concrete_devicen(pconc, pdc, pgs, dev, select, pcs); |
558 | 4.73M | } |
559 | 4.73M | return 0; |
560 | 4.73M | } |
561 | 4.73M | } |
562 | | |
563 | | /* |
564 | | * Check that the color component names for a DeviceN color space |
565 | | * match the device colorant names. Also build a gs_devicen_color_map |
566 | | * structure. |
567 | | */ |
568 | | static int |
569 | | check_DeviceN_component_names(const gs_color_space * pcs, gs_gstate * pgs) |
570 | 19.5k | { |
571 | 19.5k | char **names = pcs->params.device_n.names; |
572 | 19.5k | int num_comp = pcs->params.device_n.num_components; |
573 | 19.5k | int i; |
574 | 19.5k | int colorant_number; |
575 | 19.5k | const char *pname; |
576 | 19.5k | uint name_size; |
577 | 19.5k | gs_devicen_color_map * pcolor_component_map |
578 | 19.5k | = &pgs->color_component_map; |
579 | 19.5k | gx_device * dev = pgs->device; |
580 | 19.5k | bool non_match = false; |
581 | 19.5k | int none_count = 0; |
582 | | |
583 | 19.5k | pcolor_component_map->num_components = num_comp; |
584 | 19.5k | pcolor_component_map->cspace_id = pcs->id; |
585 | 19.5k | pcolor_component_map->num_colorants = dev->color_info.num_components; |
586 | 19.5k | pcolor_component_map->sep_type = SEP_OTHER; |
587 | | |
588 | | /* If the named color profile supports the components, don't use |
589 | | the alternate tint transform. */ |
590 | 19.5k | if (gsicc_support_named_color(pcs, pgs)) { |
591 | 0 | pcolor_component_map->use_alt_cspace = false; |
592 | 0 | return 0; |
593 | 0 | } |
594 | | |
595 | | /* If our device is using an additive color model, then we need to |
596 | | * consider using the alternative color space, as separations are |
597 | | * generally only used with a subtractive color model. There are |
598 | | * exceptions, however. |
599 | | * |
600 | | * If we don't support devn, then we will certainly have to use the |
601 | | * alternative color space. |
602 | | * |
603 | | * If we are a pdf14 device, and we are doing transparency blending |
604 | | * in an additive space, we need to keep the spots separated and |
605 | | * blend them individually as per the PDF specification. Note |
606 | | * however, if the spot is a CMYK process color and we are doing |
607 | | * the blend in an additive color space the alternate color space |
608 | | * is used. This matches AR. |
609 | | * |
610 | | * Otherwise, we will always use the alternate colorspace, unless |
611 | | * our device specifically claims to be a separation-supporting |
612 | | * additive device. Possibly all additive devn devices should |
613 | | * support this now, but I lack to confidence to make this change |
614 | | * globally. Instead we'll just enable it on a device by device |
615 | | * basis for now. |
616 | | * |
617 | | * This matches logic in check_Separation_component_name. |
618 | | */ |
619 | 19.5k | if (dev->color_info.polarity == GX_CINFO_POLARITY_ADDITIVE) { |
620 | 1.12k | if (dev_proc(dev, dev_spec_op)(dev, gxdso_supports_devn, NULL, 0) == 0 || |
621 | 1.12k | (dev_proc(dev, dev_spec_op)(dev, gxdso_is_pdf14_device, NULL, 0) == 0 && |
622 | 1.12k | dev_proc(dev, dev_spec_op)(dev, gxdso_is_sep_supporting_additive_device, NULL, 0) == 0)) { |
623 | 1.12k | pcolor_component_map->use_alt_cspace = true; |
624 | 1.12k | return 0; |
625 | 1.12k | } |
626 | 1.12k | } |
627 | | |
628 | | /* |
629 | | * Now check the names of the color components. If any of the colorants |
630 | | * come back as process type (i.e. CMYK) and we are drawing in a pdf14 |
631 | | * additive color space then use the alternate tint transform. |
632 | | */ |
633 | | |
634 | 41.2k | for(i = 0; i < num_comp; i++ ) { |
635 | | /* |
636 | | * Get the character string and length for the component name. |
637 | | */ |
638 | 22.7k | pname = names[i]; |
639 | 22.7k | if (pname == NULL) |
640 | 29 | pname = ""; |
641 | 22.7k | name_size = strlen(pname); |
642 | | /* |
643 | | * Compare the colorant name to the device's. If the device's |
644 | | * compare routine returns GX_DEVICE_COLOR_MAX_COMPONENTS then the |
645 | | * colorant is in the SeparationNames list but not in the |
646 | | * SeparationOrder list. |
647 | | */ |
648 | 22.7k | colorant_number = (*dev_proc(dev, get_color_comp_index)) |
649 | 22.7k | (dev, pname, name_size, SEPARATION_NAME); |
650 | 22.7k | if (colorant_number >= 0) { /* If valid colorant name */ |
651 | 22.7k | pcolor_component_map->color_map[i] = |
652 | 22.7k | (colorant_number == GX_DEVICE_COLOR_MAX_COMPONENTS) ? -1 |
653 | 22.7k | : colorant_number; |
654 | 22.7k | } else { |
655 | 41 | if (strncmp(pname, "None", name_size) != 0) { |
656 | 0 | non_match = true; |
657 | 41 | } else { |
658 | | /* Device N includes one or more None Entries. We can't reduce |
659 | | the number of components in the list count, since the None Name(s) |
660 | | are present in the list and GCd and we may need the None |
661 | | entries in the alternate tint trasform calcuation. |
662 | | So we will detect the presence of them by setting |
663 | | pcolor_component_map->color_map[i] = -1 and watching |
664 | | for this case later during the remap operation. */ |
665 | 41 | pcolor_component_map->color_map[i] = -1; |
666 | 41 | none_count++; |
667 | 41 | } |
668 | 41 | } |
669 | 22.7k | } |
670 | 18.4k | pcolor_component_map->use_alt_cspace = non_match; |
671 | | |
672 | 18.4k | if (none_count == num_comp) |
673 | 0 | return 1; |
674 | | |
675 | 18.4k | return 0; |
676 | 18.4k | } |
677 | | |
678 | | /* Check if the colorants are all process colorants and of the same type */ |
679 | | static separation_colors |
680 | | gx_check_process_names_DeviceN(gs_color_space * pcs, gs_gstate * pgs) |
681 | 19.5k | { |
682 | 19.5k | int i, num_comp, num_spots = 0, num_rgb_process = 0; |
683 | 19.5k | int num_cmyk_process = 0, num_other = 0; |
684 | 19.5k | char **names; |
685 | 19.5k | const char *pname; |
686 | 19.5k | uint name_size; |
687 | | |
688 | 19.5k | names = pcs->params.device_n.names; |
689 | 19.5k | num_comp = pcs->params.device_n.num_components; |
690 | | |
691 | | /* Step through the color space colorants */ |
692 | 45.5k | for (i = 0; i < num_comp; i++) { |
693 | 25.9k | pname = names[i]; |
694 | 25.9k | if (pname == NULL) |
695 | 29 | pname = ""; |
696 | 25.9k | name_size = strlen(pname); |
697 | | |
698 | | /* Classify */ |
699 | 25.9k | if (strncmp(pname, "None", name_size) == 0) { |
700 | 251 | num_other++; |
701 | 25.7k | } else { |
702 | 25.7k | if (strncmp(pname, "Cyan", name_size) == 0 || |
703 | 25.7k | strncmp(pname, "Magenta", name_size) == 0 || |
704 | 25.7k | strncmp(pname, "Yellow", name_size) == 0 || |
705 | 25.7k | strncmp(pname, "Black", name_size) == 0) { |
706 | 24.4k | num_cmyk_process++; |
707 | 24.4k | } else if (strncmp(pname, "Red", name_size) == 0 || |
708 | 1.27k | strncmp(pname, "Green", name_size) == 0 || |
709 | 1.27k | strncmp(pname, "Blue", name_size) == 0) { |
710 | 640 | num_rgb_process++; |
711 | 640 | } else { |
712 | 637 | num_spots++; |
713 | 637 | } |
714 | 25.7k | } |
715 | 25.9k | } |
716 | | |
717 | 19.5k | if (num_cmyk_process > 0 && num_rgb_process == 0 && num_spots == 0) |
718 | 18.9k | return SEP_PURE_CMYK; |
719 | 567 | if (num_rgb_process > 0 && num_cmyk_process == 0 && num_spots == 0) |
720 | 184 | return SEP_PURE_RGB; |
721 | 383 | if (num_spots > 0 && num_cmyk_process == 0 && num_rgb_process == 0) |
722 | 74 | return SEP_PURE_SPOT; |
723 | 309 | return SEP_MIX; |
724 | 383 | } |
725 | | |
726 | | /* Install a DeviceN color space. */ |
727 | | static int |
728 | | gx_install_DeviceN(gs_color_space * pcs, gs_gstate * pgs) |
729 | 19.5k | { |
730 | 19.5k | int code; |
731 | 19.5k | code = check_DeviceN_component_names(pcs, pgs); |
732 | 19.5k | if (code < 0) |
733 | 0 | return code; |
734 | | |
735 | | /* Indicates all colorants are /None */ |
736 | 19.5k | if (code > 0) |
737 | 0 | pcs->params.device_n.all_none = true; |
738 | | |
739 | 19.5k | if (pgs->icc_manager->device_named != NULL) { |
740 | 0 | pcs->params.device_n.named_color_supported = |
741 | 0 | gsicc_support_named_color(pcs, pgs); |
742 | 0 | } |
743 | 19.5k | pcs->params.device_n.color_type = gx_check_process_names_DeviceN(pcs, pgs); |
744 | | |
745 | | /* See if we have an ICC profile that we can associate with |
746 | | this DeviceN color space */ |
747 | 19.5k | if (pgs->icc_manager->device_n != NULL) { |
748 | | /* An nclr profile is in the manager. Grab one that matches. */ |
749 | 0 | cmm_profile_t *profdata = gsicc_finddevicen(pcs, pgs->icc_manager); |
750 | 0 | if (profdata != NULL) |
751 | 0 | gsicc_adjust_profile_rc(profdata, 1, "gx_install_DeviceN"); |
752 | 0 | if (pcs->cmm_icc_profile_data != NULL) |
753 | 0 | gsicc_adjust_profile_rc(pcs->cmm_icc_profile_data, -1, "gx_install_DeviceN"); |
754 | 0 | pcs->cmm_icc_profile_data = profdata; |
755 | 0 | } |
756 | | /* {csrc} was pgs->color_space->params.device_n.use_alt_cspace */ |
757 | 19.5k | ((gs_color_space *)pcs)->params.device_n.use_alt_cspace = |
758 | 19.5k | using_alt_color_space(pgs); |
759 | 19.5k | if (pcs->params.device_n.use_alt_cspace && pcs->cmm_icc_profile_data == NULL ) { |
760 | | /* No nclr ICC profile */ |
761 | 1.12k | code = (pcs->base_space->type->install_cspace) |
762 | 1.12k | (pcs->base_space, pgs); |
763 | 18.4k | } else if (pcs->params.device_n.use_alt_cspace) { |
764 | 0 | gs_color_space *nclr_pcs; |
765 | | /* Need to install the nclr cspace */ |
766 | 0 | code = gs_cspace_build_ICC(&nclr_pcs, NULL, pgs->memory); |
767 | 0 | nclr_pcs->cmm_icc_profile_data = pcs->cmm_icc_profile_data; |
768 | 0 | gsicc_adjust_profile_rc(pcs->cmm_icc_profile_data, 1, "gx_install_DeviceN"); |
769 | 0 | rc_increment(nclr_pcs); /* FIXME: Suspicious - RJW */ |
770 | 0 | rc_decrement(pcs->base_space, "gx_install_DeviceN"); |
771 | 0 | pcs->base_space = nclr_pcs; |
772 | 0 | } |
773 | | /* |
774 | | * Give the device an opportunity to capture equivalent colors for any |
775 | | * spot colors which might be present in the color space. |
776 | | */ |
777 | 19.5k | if (code >= 0) { |
778 | 19.5k | if (dev_proc(pgs->device, update_spot_equivalent_colors)) |
779 | 19.5k | code = dev_proc(pgs->device, update_spot_equivalent_colors) |
780 | 19.5k | (pgs->device, pgs, pcs); |
781 | 19.5k | } |
782 | 19.5k | return code; |
783 | 19.5k | } |
784 | | |
785 | | /* Set overprint information for a DeviceN color space */ |
786 | | static int |
787 | | gx_set_overprint_DeviceN(const gs_color_space * pcs, gs_gstate * pgs) |
788 | 0 | { |
789 | 0 | gs_devicen_color_map * pcmap = &pgs->color_component_map; |
790 | 0 | int code; |
791 | | |
792 | | /* It is possible that the color map information in the graphic state |
793 | | is not current due to save/restore and or if we are coming from |
794 | | a color space that is inside a PatternType 2 */ |
795 | 0 | code = check_DeviceN_component_names(pcs, pgs); |
796 | 0 | if (code < 0) |
797 | 0 | return code; |
798 | 0 | if (pcmap->use_alt_cspace) { |
799 | 0 | const gs_color_space_type* base_type = pcs->base_space->type; |
800 | | |
801 | | /* If the base space is DeviceCMYK, handle overprint as DeviceCMYK */ |
802 | 0 | if ( base_type->index == gs_color_space_index_DeviceCMYK ) |
803 | 0 | return base_type->set_overprint( pcs->base_space, pgs ); |
804 | 0 | else |
805 | 0 | return gx_set_no_overprint(pgs); |
806 | 0 | } else { |
807 | 0 | gs_overprint_params_t params = { 0 }; |
808 | |
|
809 | 0 | params.retain_any_comps = (pgs->overprint && pgs->is_fill_color) || |
810 | 0 | (pgs->stroke_overprint && !pgs->is_fill_color); |
811 | |
|
812 | 0 | params.drawn_comps = 0; |
813 | 0 | if (params.retain_any_comps) { |
814 | 0 | int i, ncomps = pcs->params.device_n.num_components; |
815 | |
|
816 | 0 | params.is_fill_color = pgs->is_fill_color; /* for fill_stroke */ |
817 | 0 | for (i = 0; i < ncomps; i++) { |
818 | 0 | int mcomp = pcmap->color_map[i]; |
819 | 0 | if (mcomp >= 0) |
820 | 0 | gs_overprint_set_drawn_comp( params.drawn_comps, mcomp); |
821 | 0 | } |
822 | 0 | } |
823 | | |
824 | | /* Only DeviceCMYK can use overprint mode */ |
825 | 0 | params.effective_opm = pgs->color[0].effective_opm = 0; |
826 | 0 | params.op_state = OP_STATE_NONE; |
827 | 0 | params.is_fill_color =pgs->is_fill_color; |
828 | 0 | return gs_gstate_update_overprint(pgs, ¶ms); |
829 | 0 | } |
830 | 0 | } |
831 | | |
832 | | /* Finalize contents of a DeviceN color space. */ |
833 | | static void |
834 | | gx_final_DeviceN(gs_color_space * pcs) |
835 | 19.4k | { |
836 | 19.4k | gs_device_n_colorant * pnextatt, * patt = pcs->params.device_n.colorants; |
837 | 19.4k | uint num_proc_names = pcs->params.device_n.num_process_names; |
838 | 19.4k | gs_memory_t *mem = pcs->params.device_n.mem->non_gc_memory; |
839 | 19.4k | char **proc_names = pcs->params.device_n.process_names; |
840 | 19.4k | int k; |
841 | | |
842 | 45.0k | for (k = 0; k < pcs->params.device_n.num_components; k++) |
843 | 25.5k | gs_free_object(mem, pcs->params.device_n.names[k], "gx_final_DeviceN"); |
844 | 19.4k | gs_free_object(mem, pcs->params.device_n.names, "gx_final_DeviceN"); |
845 | | |
846 | 19.4k | if (num_proc_names > 0 && proc_names != NULL) { |
847 | 92.1k | for (k = 0; k < num_proc_names; k++) { |
848 | 73.7k | gs_free_object(mem, proc_names[k], "gx_final_DeviceN"); |
849 | 73.7k | } |
850 | 18.4k | gs_free_object(mem, proc_names, "gx_final_DeviceN"); |
851 | 18.4k | } |
852 | | |
853 | 19.4k | rc_decrement_only(pcs->params.device_n.map, "gx_adjust_DeviceN"); |
854 | 19.4k | while (patt != NULL) { |
855 | 0 | pnextatt = patt->next; |
856 | 0 | gs_free_object(mem, patt->colorant_name, "gx_final_DeviceN"); |
857 | 0 | rc_decrement_cs(patt->cspace, "gx_final_DeviceN"); |
858 | 0 | rc_decrement(patt, "gx_adjust_DeviceN"); |
859 | 0 | patt = pnextatt; |
860 | 0 | } |
861 | 19.4k | if (pcs->params.device_n.devn_process_space) |
862 | 19.4k | rc_decrement_only(pcs->params.device_n.devn_process_space, "gx_final_DeviceN"); |
863 | | /* Ensure idempotency */ |
864 | 19.4k | memset(&pcs->params.device_n, 0, sizeof(pcs->params.device_n)); |
865 | 19.4k | } |
866 | | |
867 | | /* ---------------- Serialization. -------------------------------- */ |
868 | | |
869 | | int |
870 | | gx_serialize_device_n_map(const gs_color_space * pcs, gs_device_n_map * m, stream * s) |
871 | 53.8k | { |
872 | 53.8k | const gs_function_t *pfn; |
873 | | |
874 | 53.8k | if (m->tint_transform != map_devn_using_function) |
875 | 0 | return_error(gs_error_unregistered); /* Unimplemented. */ |
876 | 53.8k | pfn = (const gs_function_t *)m->tint_transform_data; |
877 | 53.8k | return gs_function_serialize(pfn, s); |
878 | 53.8k | } |
879 | | |
880 | | static int |
881 | | gx_serialize_DeviceN(const gs_color_space * pcs, stream * s) |
882 | 118 | { |
883 | 118 | const gs_device_n_params * p = &pcs->params.device_n; |
884 | 118 | uint n, m; |
885 | 118 | int code = gx_serialize_cspace_type(pcs, s); |
886 | | |
887 | 118 | if (code < 0) |
888 | 0 | return code; |
889 | 118 | code = sputs(s, (const byte *)&p->num_components, sizeof(p->num_components), &n); |
890 | 118 | if (code < 0) |
891 | 0 | return code; |
892 | 478 | for (n=0;n < p->num_components;n++) { |
893 | 360 | const char *name = p->names[n]; |
894 | 360 | if (name == NULL) |
895 | 0 | name = ""; |
896 | 360 | code = sputs(s, (const byte *)name, strlen(name) + 1, &m); |
897 | 360 | if (code < 0) |
898 | 0 | return code; |
899 | 360 | } |
900 | 118 | code = cs_serialize(pcs->base_space, s); |
901 | 118 | if (code < 0) |
902 | 0 | return code; |
903 | 118 | return gx_serialize_device_n_map(pcs, p->map, s); |
904 | | /* p->use_alt_cspace isn't a property of the space. */ |
905 | 118 | } |