xref: /plan9/sys/src/cmd/gs/src/gscdevn.c (revision 593dc095aefb2a85c828727bbfa9da139a49bdf4)
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, &params);
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