1 /* Copyright (C) 1997, 2000 Aladdin Enterprises. All rights reserved.
2
3 This software is provided AS-IS with no warranty, either express or
4 implied.
5
6 This software is distributed under license and may not be copied,
7 modified or distributed except as expressly authorized under the terms
8 of the license contained in the file LICENSE in this distribution.
9
10 For more information about licensing, please refer to
11 http://www.ghostscript.com/licensing/. For information on
12 commercial licensing, go to http://www.artifex.com/licensing/ or
13 contact Artifex Software, Inc., 101 Lucas Valley Road #110,
14 San Rafael, CA 94903, U.S.A., +1(415)492-9861.
15 */
16
17 /* $Id: gscdevn.c,v 1.21 2004/08/04 19:36:12 stefan Exp $ */
18 /* DeviceN color space and operation definition */
19
20 #include "memory_.h"
21 #include "string_.h"
22 #include "gx.h"
23 #include "gserrors.h"
24 #include "gscdevn.h"
25 #include "gsfunc.h"
26 #include "gsrefct.h"
27 #include "gsmatrix.h" /* for gscolor2.h */
28 #include "gsstruct.h"
29 #include "gxcspace.h"
30 #include "gxcdevn.h"
31 #include "gxfarith.h"
32 #include "gxfrac.h"
33 #include "gxcmap.h"
34 #include "gxistate.h"
35 #include "gscoord.h"
36 #include "gzstate.h"
37 #include "gxdevcli.h"
38 #include "gsovrc.h"
39 #include "stream.h"
40
41 /* ---------------- Color space ---------------- */
42
43 /* GC descriptors */
44 gs_private_st_composite(st_color_space_DeviceN, gs_paint_color_space,
45 "gs_color_space_DeviceN", cs_DeviceN_enum_ptrs, cs_DeviceN_reloc_ptrs);
46 private_st_device_n_map();
47
48 /* Define the DeviceN color space type. */
49 private cs_proc_num_components(gx_num_components_DeviceN);
50 private cs_proc_base_space(gx_alt_space_DeviceN);
51 private cs_proc_init_color(gx_init_DeviceN);
52 private cs_proc_restrict_color(gx_restrict_DeviceN);
53 private cs_proc_concrete_space(gx_concrete_space_DeviceN);
54 private cs_proc_concretize_color(gx_concretize_DeviceN);
55 private cs_proc_remap_concrete_color(gx_remap_concrete_DeviceN);
56 private cs_proc_install_cspace(gx_install_DeviceN);
57 private cs_proc_set_overprint(gx_set_overprint_DeviceN);
58 private cs_proc_adjust_cspace_count(gx_adjust_cspace_DeviceN);
59 private cs_proc_serialize(gx_serialize_DeviceN);
60 const gs_color_space_type gs_color_space_type_DeviceN = {
61 gs_color_space_index_DeviceN, true, false,
62 &st_color_space_DeviceN, gx_num_components_DeviceN,
63 gx_alt_space_DeviceN,
64 gx_init_DeviceN, gx_restrict_DeviceN,
65 gx_concrete_space_DeviceN,
66 gx_concretize_DeviceN, gx_remap_concrete_DeviceN,
67 gx_default_remap_color, gx_install_DeviceN,
68 gx_set_overprint_DeviceN,
69 gx_adjust_cspace_DeviceN, gx_no_adjust_color_count,
70 gx_serialize_DeviceN,
71 gx_cspace_is_linear_default
72 };
73
74 /* GC procedures */
75
76 private
ENUM_PTRS_WITH(cs_DeviceN_enum_ptrs,gs_color_space * pcs)77 ENUM_PTRS_WITH(cs_DeviceN_enum_ptrs, gs_color_space *pcs)
78 {
79 return ENUM_USING(*pcs->params.device_n.alt_space.type->stype,
80 &pcs->params.device_n.alt_space,
81 sizeof(pcs->params.device_n.alt_space), index - 2);
82 }
83 ENUM_PTR(0, gs_color_space, params.device_n.names);
84 ENUM_PTR(1, gs_color_space, params.device_n.map);
85 ENUM_PTRS_END
RELOC_PTRS_WITH(cs_DeviceN_reloc_ptrs,gs_color_space * pcs)86 private RELOC_PTRS_WITH(cs_DeviceN_reloc_ptrs, gs_color_space *pcs)
87 {
88 RELOC_PTR(gs_color_space, params.device_n.names);
89 RELOC_PTR(gs_color_space, params.device_n.map);
90 RELOC_USING(*pcs->params.device_n.alt_space.type->stype,
91 &pcs->params.device_n.alt_space,
92 sizeof(gs_base_color_space));
93 }
94 RELOC_PTRS_END
95
96 /* ------ Public procedures ------ */
97
98 /*
99 * Build a DeviceN color space. Not including allocation and
100 * initialization of the color space.
101 */
102 int
gs_build_DeviceN(gs_color_space * pcspace,uint num_components,const gs_color_space * palt_cspace,gs_memory_t * pmem)103 gs_build_DeviceN(
104 gs_color_space *pcspace,
105 uint num_components,
106 const gs_color_space *palt_cspace,
107 gs_memory_t *pmem
108 )
109 {
110 gs_device_n_params *pcsdevn = pcsdevn = &pcspace->params.device_n;
111 gs_separation_name *pnames = 0;
112 int code;
113
114 if (palt_cspace == 0 || !palt_cspace->type->can_be_alt_space)
115 return_error(gs_error_rangecheck);
116
117 /* Allocate space for color names list. */
118 code = alloc_device_n_map(&pcsdevn->map, pmem, "gs_cspace_build_DeviceN");
119 if (code < 0) {
120 return code;
121 }
122 /* Allocate space for color names list. */
123 pnames = (gs_separation_name *)
124 gs_alloc_byte_array(pmem, num_components, sizeof(gs_separation_name),
125 ".gs_cspace_build_DeviceN(names)");
126 if (pnames == 0) {
127 gs_free_object(pmem, pcsdevn->map, ".gs_cspace_build_DeviceN(map)");
128 return_error(gs_error_VMerror);
129 }
130 pcsdevn->names = pnames;
131 pcsdevn->num_components = num_components;
132 return 0;
133 }
134
135 /*
136 * Build a DeviceN color space. Including allocation and initialization
137 * of the color space.
138 */
139 int
gs_cspace_build_DeviceN(gs_color_space ** ppcspace,gs_separation_name * psnames,uint num_components,const gs_color_space * palt_cspace,gs_memory_t * pmem)140 gs_cspace_build_DeviceN(
141 gs_color_space **ppcspace,
142 gs_separation_name *psnames,
143 uint num_components,
144 const gs_color_space *palt_cspace,
145 gs_memory_t *pmem
146 )
147 {
148 gs_color_space *pcspace = 0; /* bogus initialization */
149 gs_device_n_params *pcsdevn = 0; /* bogus initialization */
150 int code;
151
152 code = gs_cspace_alloc(&pcspace, &gs_color_space_type_DeviceN, pmem);
153 if (code < 0)
154 return code;
155
156 code = gs_build_DeviceN(pcspace, num_components, palt_cspace, pmem);
157 if (code < 0) {
158 gs_free_object(pmem, pcspace, "gs_cspace_build_DeviceN");
159 return code;
160 }
161 gs_cspace_init_from((gs_color_space *)&pcsdevn->alt_space, palt_cspace);
162 *ppcspace = pcspace;
163 return 0;
164 }
165
166 /* Allocate and initialize a DeviceN map. */
167 int
alloc_device_n_map(gs_device_n_map ** ppmap,gs_memory_t * mem,client_name_t cname)168 alloc_device_n_map(gs_device_n_map ** ppmap, gs_memory_t * mem,
169 client_name_t cname)
170 {
171 gs_device_n_map *pimap;
172
173 rc_alloc_struct_1(pimap, gs_device_n_map, &st_device_n_map, mem,
174 return_error(gs_error_VMerror), cname);
175 pimap->tint_transform = 0;
176 pimap->tint_transform_data = 0;
177 pimap->cache_valid = false;
178 *ppmap = pimap;
179 return 0;
180 }
181
182 #if 0 /* Unused; Unsupported by gx_serialize_device_n_map. */
183 /*
184 * Set the DeviceN tint transformation procedure.
185 */
186 int
187 gs_cspace_set_devn_proc(gs_color_space * pcspace,
188 int (*proc)(const float *,
189 float *,
190 const gs_imager_state *,
191 void *
192 ),
193 void *proc_data
194 )
195 {
196 gs_device_n_map *pimap;
197
198 if (gs_color_space_get_index(pcspace) != gs_color_space_index_DeviceN)
199 return_error(gs_error_rangecheck);
200 pimap = pcspace->params.device_n.map;
201 pimap->tint_transform = proc;
202 pimap->tint_transform_data = proc_data;
203 pimap->cache_valid = false;
204 return 0;
205 }
206 #endif
207
208 /*
209 * Check if we are using the alternate color space.
210 */
211 bool
using_alt_color_space(const gs_state * pgs)212 using_alt_color_space(const gs_state * pgs)
213 {
214 return (pgs->color_component_map.use_alt_cspace);
215 }
216
217 /* Map a DeviceN color using a Function. */
218 int
map_devn_using_function(const float * in,float * out,const gs_imager_state * pis,void * data)219 map_devn_using_function(const float *in, float *out,
220 const gs_imager_state *pis, void *data)
221
222 {
223 gs_function_t *const pfn = data;
224
225 return gs_function_evaluate(pfn, in, out);
226 }
227
228 /*
229 * Set the DeviceN tint transformation procedure to a Function.
230 */
231 int
gs_cspace_set_devn_function(gs_color_space * pcspace,gs_function_t * pfn)232 gs_cspace_set_devn_function(gs_color_space *pcspace, gs_function_t *pfn)
233 {
234 gs_device_n_map *pimap;
235
236 if (gs_color_space_get_index(pcspace) != gs_color_space_index_DeviceN ||
237 pfn->params.m != pcspace->params.device_n.num_components ||
238 pfn->params.n !=
239 gs_color_space_num_components((gs_color_space *)
240 &pcspace->params.device_n.alt_space)
241 )
242 return_error(gs_error_rangecheck);
243 pimap = pcspace->params.device_n.map;
244 pimap->tint_transform = map_devn_using_function;
245 pimap->tint_transform_data = pfn;
246 pimap->cache_valid = false;
247 return 0;
248 }
249
250 /*
251 * If the DeviceN tint transformation procedure is a Function,
252 * return the function object, otherwise return 0.
253 */
254 gs_function_t *
gs_cspace_get_devn_function(const gs_color_space * pcspace)255 gs_cspace_get_devn_function(const gs_color_space *pcspace)
256 {
257 if (gs_color_space_get_index(pcspace) == gs_color_space_index_DeviceN &&
258 pcspace->params.device_n.map->tint_transform ==
259 map_devn_using_function)
260 return pcspace->params.device_n.map->tint_transform_data;
261 return 0;
262 }
263
264 /* ------ Color space implementation ------ */
265
266 /* Return the number of components of a DeviceN space. */
267 private int
gx_num_components_DeviceN(const gs_color_space * pcs)268 gx_num_components_DeviceN(const gs_color_space * pcs)
269 {
270 return pcs->params.device_n.num_components;
271 }
272
273 /* Return the alternate space of a DeviceN space. */
274 private const gs_color_space *
gx_alt_space_DeviceN(const gs_color_space * pcs)275 gx_alt_space_DeviceN(const gs_color_space * pcs)
276 {
277 return pcs->params.device_n.use_alt_cspace
278 ? (const gs_color_space *)&(pcs->params.device_n.alt_space)
279 : NULL;
280 }
281
282 /* Initialize a DeviceN color. */
283 private void
gx_init_DeviceN(gs_client_color * pcc,const gs_color_space * pcs)284 gx_init_DeviceN(gs_client_color * pcc, const gs_color_space * pcs)
285 {
286 uint i;
287
288 for (i = 0; i < pcs->params.device_n.num_components; ++i)
289 pcc->paint.values[i] = 1.0;
290 }
291
292 /* Force a DeviceN color into legal range. */
293 private void
gx_restrict_DeviceN(gs_client_color * pcc,const gs_color_space * pcs)294 gx_restrict_DeviceN(gs_client_color * pcc, const gs_color_space * pcs)
295 {
296 uint i;
297
298 for (i = 0; i < pcs->params.device_n.num_components; ++i) {
299 floatp value = pcc->paint.values[i];
300
301 pcc->paint.values[i] = (value <= 0 ? 0 : value >= 1 ? 1 : value);
302 }
303 }
304
305 /* Remap a DeviceN color. */
306 private const gs_color_space *
gx_concrete_space_DeviceN(const gs_color_space * pcs,const gs_imager_state * pis)307 gx_concrete_space_DeviceN(const gs_color_space * pcs,
308 const gs_imager_state * pis)
309 {
310 #ifdef DEBUG
311 /*
312 * Verify that the color space and imager state info match.
313 */
314 if (pcs->id != pis->color_component_map.cspace_id)
315 dprintf("gx_concrete_space_DeviceN: color space id mismatch");
316 #endif
317
318 /*
319 * Check if we are using the alternate color space.
320 */
321 if (pis->color_component_map.use_alt_cspace) {
322 const gs_color_space *pacs =
323 (const gs_color_space *)&pcs->params.device_n.alt_space;
324
325 return cs_concrete_space(pacs, pis);
326 }
327 /*
328 * DeviceN color spaces are concrete (when not using alt. color space).
329 */
330 return pcs;
331 }
332
333
334 private int
gx_concretize_DeviceN(const gs_client_color * pc,const gs_color_space * pcs,frac * pconc,const gs_imager_state * pis)335 gx_concretize_DeviceN(const gs_client_color * pc, const gs_color_space * pcs,
336 frac * pconc, const gs_imager_state * pis)
337 {
338 int code, tcode = 0;
339 gs_client_color cc;
340 const gs_color_space *pacs =
341 (const gs_color_space *)&pcs->params.device_n.alt_space;
342 gs_device_n_map *map = pcs->params.device_n.map;
343
344 #ifdef DEBUG
345 /*
346 * Verify that the color space and imager state info match.
347 */
348 if (pcs->id != pis->color_component_map.cspace_id)
349 dprintf("gx_concretize_DeviceN: color space id mismatch");
350 #endif
351
352 /*
353 * Check if we need to map into the alternate color space.
354 * We must preserve tcode for implementing a semi-hack in the interpreter.
355 */
356 if (pis->color_component_map.use_alt_cspace) {
357 /* Check the 1-element cache first. */
358 if (map->cache_valid) {
359 int i;
360
361 for (i = pcs->params.device_n.num_components; --i >= 0;) {
362 if (map->tint[i] != pc->paint.values[i])
363 break;
364 }
365 if (i < 0) {
366 int num_out = gs_color_space_num_components(pacs);
367
368 for (i = 0; i < num_out; ++i)
369 pconc[i] = map->conc[i];
370 return 0;
371 }
372 }
373 tcode = (*pcs->params.device_n.map->tint_transform)
374 (pc->paint.values, &cc.paint.values[0],
375 pis, pcs->params.device_n.map->tint_transform_data);
376 if (tcode < 0)
377 return tcode;
378 code = cs_concretize_color(&cc, pacs, pconc, pis);
379 }
380 else {
381 float ftemp;
382 int i;
383
384 for (i = pcs->params.device_n.num_components; --i >= 0;)
385 pconc[i] = unit_frac(pc->paint.values[i], ftemp);
386 return 0;
387 }
388 return (code < 0 || tcode == 0 ? code : tcode);
389 }
390
391 private int
gx_remap_concrete_DeviceN(const frac * pconc,const gs_color_space * pcs,gx_device_color * pdc,const gs_imager_state * pis,gx_device * dev,gs_color_select_t select)392 gx_remap_concrete_DeviceN(const frac * pconc, const gs_color_space * pcs,
393 gx_device_color * pdc, const gs_imager_state * pis, gx_device * dev,
394 gs_color_select_t select)
395 {
396 #ifdef DEBUG
397 /*
398 * Verify that the color space and imager state info match.
399 */
400 if (pcs->id != pis->color_component_map.cspace_id)
401 dprintf("gx_remap_concrete_DeviceN: color space id mismatch");
402 #endif
403
404 if (pis->color_component_map.use_alt_cspace) {
405 const gs_color_space *pacs =
406 (const gs_color_space *)&pcs->params.device_n.alt_space;
407
408 return (*pacs->type->remap_concrete_color)
409 (pconc, pacs, pdc, pis, dev, select);
410 }
411 else {
412 gx_remap_concrete_devicen(pconc, pdc, pis, dev, select);
413 return 0;
414 }
415 }
416
417 /*
418 * Check that the color component names for a DeviceN color space
419 * match the device colorant names. Also build a gs_devicen_color_map
420 * structure.
421 */
422 private int
check_DeviceN_component_names(const gs_color_space * pcs,gs_state * pgs)423 check_DeviceN_component_names(const gs_color_space * pcs, gs_state * pgs)
424 {
425 const gs_separation_name *names = pcs->params.device_n.names;
426 int num_comp = pcs->params.device_n.num_components;
427 int i, j;
428 int colorant_number;
429 byte * pname;
430 uint name_size;
431 gs_devicen_color_map * pcolor_component_map
432 = &pgs->color_component_map;
433 gx_device * dev = pgs->device;
434 const char none_str[] = "None";
435 const uint none_size = strlen(none_str);
436 bool non_match = false;
437
438 pcolor_component_map->num_components = num_comp;
439 pcolor_component_map->cspace_id = pcs->id;
440 pcolor_component_map->num_colorants = dev->color_info.num_components;
441 pcolor_component_map->sep_type = SEP_OTHER;
442 /*
443 * Always use the alternate color space if the current device is
444 * using an additive color model.
445 */
446 if (dev->color_info.polarity == GX_CINFO_POLARITY_ADDITIVE) {
447 pcolor_component_map->use_alt_cspace = true;
448 return 0;
449 }
450 /*
451 * Now check the names of the color components.
452 */
453 non_match = false;
454 for(i = 0; i < num_comp; i++ ) {
455 /*
456 * Get the character string and length for the component name.
457 */
458 pcs->params.device_n.get_colorname_string(dev->memory, names[i], &pname, &name_size);
459 /*
460 * Postscript does not accept /None as a color component but it is
461 * allowed in PDF so we accept it. It is also accepted as a
462 * separation name.
463 */
464 if (name_size == none_size &&
465 (strncmp(none_str, (const char *)pname, name_size) == 0)) {
466 pcolor_component_map->color_map[i] = -1;
467 }
468 else {
469 /*
470 * Check for duplicated names. Except for /None, no components
471 * are allowed to have duplicated names.
472 */
473 for (j = 0; j < i; j++) {
474 if (names[i] == names[j])
475 return_error(gs_error_rangecheck);
476 }
477 /*
478 * Compare the colorant name to the device's. If the device's
479 * compare routine returns GX_DEVICE_COLOR_MAX_COMPONENTS then the
480 * colorant is in the SeparationNames list but not in the
481 * SeparationOrder list.
482 */
483 colorant_number = (*dev_proc(dev, get_color_comp_index))
484 (dev, (const char *)pname, name_size, SEPARATION_NAME);
485 if (colorant_number >= 0) { /* If valid colorant name */
486 pcolor_component_map->color_map[i] =
487 (colorant_number == GX_DEVICE_COLOR_MAX_COMPONENTS) ? -1
488 : colorant_number;
489 }
490 else
491 non_match = true;
492 }
493 }
494 pcolor_component_map->use_alt_cspace = non_match;
495 return 0;
496 }
497
498 /* Install a DeviceN color space. */
499 private int
gx_install_DeviceN(const gs_color_space * pcs,gs_state * pgs)500 gx_install_DeviceN(const gs_color_space * pcs, gs_state * pgs)
501 {
502 int code = check_DeviceN_component_names(pcs, pgs);
503
504 if (code < 0)
505 return code;
506 pgs->color_space->params.device_n.use_alt_cspace =
507 using_alt_color_space(pgs);
508 if (pgs->color_space->params.device_n.use_alt_cspace)
509 code = (*pcs->params.device_n.alt_space.type->install_cspace)
510 ((const gs_color_space *) & pcs->params.device_n.alt_space, pgs);
511 /*
512 * Give the device an opportunity to capture equivalent colors for any
513 * spot colors which might be present in the color space.
514 */
515 if (code >= 0)
516 code = dev_proc(pgs->device, update_spot_equivalent_colors)
517 (pgs->device, pgs);
518 return code;
519 }
520
521 /* Set overprint information for a DeviceN color space */
522 private int
gx_set_overprint_DeviceN(const gs_color_space * pcs,gs_state * pgs)523 gx_set_overprint_DeviceN(const gs_color_space * pcs, gs_state * pgs)
524 {
525 gs_devicen_color_map * pcmap = &pgs->color_component_map;
526
527 if (pcmap->use_alt_cspace)
528 return gx_spot_colors_set_overprint(
529 (const gs_color_space *)&pcs->params.device_n.alt_space,
530 pgs );
531 else {
532 gs_overprint_params_t params;
533
534 if ((params.retain_any_comps = pgs->overprint)) {
535 int i, ncomps = pcs->params.device_n.num_components;
536
537 params.retain_spot_comps = false;
538 params.drawn_comps = 0;
539 for (i = 0; i < ncomps; i++) {
540 int mcomp = pcmap->color_map[i];
541
542 if (mcomp >= 0)
543 gs_overprint_set_drawn_comp( params.drawn_comps, mcomp);
544 }
545 }
546
547 pgs->effective_overprint_mode = 0;
548 return gs_state_update_overprint(pgs, ¶ms);
549 }
550 }
551
552 /* Adjust the reference count of a DeviceN color space. */
553 private void
gx_adjust_cspace_DeviceN(const gs_color_space * pcs,int delta)554 gx_adjust_cspace_DeviceN(const gs_color_space * pcs, int delta)
555 {
556 rc_adjust_const(pcs->params.device_n.map, delta, "gx_adjust_DeviceN");
557 (*pcs->params.device_n.alt_space.type->adjust_cspace_count)
558 ((const gs_color_space *)&pcs->params.device_n.alt_space, delta);
559 }
560
561 /* ---------------- Serialization. -------------------------------- */
562
563 int
gx_serialize_device_n_map(const gs_color_space * pcs,gs_device_n_map * m,stream * s)564 gx_serialize_device_n_map(const gs_color_space * pcs, gs_device_n_map * m, stream * s)
565 {
566 const gs_function_t *pfn;
567
568 if (m->tint_transform != map_devn_using_function)
569 return_error(gs_error_unregistered); /* Unimplemented. */
570 pfn = (const gs_function_t *)m->tint_transform_data;
571 return gs_function_serialize(pfn, s);
572 }
573
574 private int
gx_serialize_DeviceN(const gs_color_space * pcs,stream * s)575 gx_serialize_DeviceN(const gs_color_space * pcs, stream * s)
576 {
577 const gs_device_n_params * p = &pcs->params.device_n;
578 uint n;
579 int code = gx_serialize_cspace_type(pcs, s);
580
581 if (code < 0)
582 return code;
583 code = sputs(s, (const byte *)&p->num_components, sizeof(p->num_components), &n);
584 if (code < 0)
585 return code;
586 code = sputs(s, (const byte *)&p->names[0], sizeof(p->names[0]) * p->num_components, &n);
587 if (code < 0)
588 return code;
589 code = cs_serialize((const gs_color_space *)&p->alt_space, s);
590 if (code < 0)
591 return code;
592 return gx_serialize_device_n_map(pcs, p->map, s);
593 /* p->use_alt_cspace isn't a property of the space. */
594 }
595