xref: /plan9/sys/src/cmd/gs/src/gscsepr.c (revision 593dc095aefb2a85c828727bbfa9da139a49bdf4)
1 /* Copyright (C) 1994, 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: gscsepr.c,v 1.27 2004/08/04 19:36:12 stefan Exp $ */
18 /* Separation color space and operation definition */
19 #include "memory_.h"
20 #include "gx.h"
21 #include "gserrors.h"
22 #include "gsfunc.h"
23 #include "gsrefct.h"
24 #include "gsmatrix.h"		/* for gscolor2.h */
25 #include "gscsepr.h"
26 #include "gxcspace.h"
27 #include "gxfixed.h"		/* for gxcolor2.h */
28 #include "gxcolor2.h"		/* for gs_indexed_map */
29 #include "gzstate.h"		/* for pgs->overprint */
30 #include "gscdevn.h"		/* for alloc_device_n_map */
31 #include "gxcdevn.h"		/* for gs_device_n_map_s */
32 #include "gxcmap.h"
33 #include "gxdevcli.h"
34 #include "gsovrc.h"
35 #include "stream.h"
36 
37 /* ---------------- Color space ---------------- */
38 
39 gs_private_st_composite(st_color_space_Separation, gs_paint_color_space,
40 			"gs_color_space_Separation",
41 			cs_Separation_enum_ptrs, cs_Separation_reloc_ptrs);
42 
43 /* Define the Separation color space type. */
44 private cs_proc_base_space(gx_alt_space_Separation);
45 private cs_proc_init_color(gx_init_Separation);
46 private cs_proc_concrete_space(gx_concrete_space_Separation);
47 private cs_proc_concretize_color(gx_concretize_Separation);
48 private cs_proc_remap_concrete_color(gx_remap_concrete_Separation);
49 private cs_proc_remap_color(gx_remap_Separation);
50 private cs_proc_install_cspace(gx_install_Separation);
51 private cs_proc_set_overprint(gx_set_overprint_Separation);
52 private cs_proc_adjust_cspace_count(gx_adjust_cspace_Separation);
53 private cs_proc_serialize(gx_serialize_Separation);
54 const gs_color_space_type gs_color_space_type_Separation = {
55     gs_color_space_index_Separation, true, false,
56     &st_color_space_Separation, gx_num_components_1,
57     gx_alt_space_Separation,
58     gx_init_Separation, gx_restrict01_paint_1,
59     gx_concrete_space_Separation,
60     gx_concretize_Separation, gx_remap_concrete_Separation,
61     gx_remap_Separation, gx_install_Separation,
62     gx_set_overprint_Separation,
63     gx_adjust_cspace_Separation, gx_no_adjust_color_count,
64     gx_serialize_Separation,
65     gx_cspace_is_linear_default
66 };
67 
68 /* GC procedures */
69 
70 private
ENUM_PTRS_WITH(cs_Separation_enum_ptrs,gs_color_space * pcs)71 ENUM_PTRS_WITH(cs_Separation_enum_ptrs, gs_color_space *pcs)
72 {
73     return ENUM_USING(*pcs->params.separation.alt_space.type->stype,
74 		      &pcs->params.separation.alt_space,
75 		      sizeof(pcs->params.separation.alt_space), index - 1);
76 }
77 ENUM_PTR(0, gs_color_space, params.separation.map);
78 ENUM_PTRS_END
RELOC_PTRS_WITH(cs_Separation_reloc_ptrs,gs_color_space * pcs)79 private RELOC_PTRS_WITH(cs_Separation_reloc_ptrs, gs_color_space *pcs)
80 {
81     RELOC_PTR(gs_color_space, params.separation.map);
82     RELOC_USING(*pcs->params.separation.alt_space.type->stype,
83 		&pcs->params.separation.alt_space,
84 		sizeof(gs_base_color_space));
85 }
86 RELOC_PTRS_END
87 
88 /* Get the alternate space for a Separation space. */
89 private const gs_color_space *
gx_alt_space_Separation(const gs_color_space * pcs)90 gx_alt_space_Separation(const gs_color_space * pcs)
91 {
92     return pcs->params.separation.use_alt_cspace
93 	   ? (const gs_color_space *)&(pcs->params.separation.alt_space)
94     	   : NULL;
95 }
96 
97 /* Get the concrete space for a Separation space. */
98 private const gs_color_space *
gx_concrete_space_Separation(const gs_color_space * pcs,const gs_imager_state * pis)99 gx_concrete_space_Separation(const gs_color_space * pcs,
100 			     const gs_imager_state * pis)
101 {
102 #ifdef DEBUG
103     /*
104      * Verify that the color space and imager state info match.
105      */
106     if (pcs->id != pis->color_component_map.cspace_id)
107 	dprintf("gx_concretze_space_Separation: color space id mismatch");
108 #endif
109 
110     /*
111      * Check if we are using the alternate color space.
112      */
113     if (pis->color_component_map.use_alt_cspace) {
114         const gs_color_space *pacs =
115 	    (const gs_color_space *)&pcs->params.separation.alt_space;
116 
117         return cs_concrete_space(pacs, pis);
118     }
119     /*
120      * Separation color spaces are concrete (when not using alt. color space).
121      */
122     return pcs;
123 }
124 
125 private int
126 check_Separation_component_name(const gs_color_space * pcs, gs_state * pgs);
127 
128 /* Install a Separation color space. */
129 private int
gx_install_Separation(const gs_color_space * pcs,gs_state * pgs)130 gx_install_Separation(const gs_color_space * pcs, gs_state * pgs)
131 {
132     int code = check_Separation_component_name(pcs, pgs);
133 
134     if (code < 0)
135        return code;
136     pgs->color_space->params.separation.use_alt_cspace =
137 	using_alt_color_space(pgs);
138     if (pgs->color_space->params.separation.use_alt_cspace)
139         code = (*pcs->params.separation.alt_space.type->install_cspace)
140 	((const gs_color_space *) & pcs->params.separation.alt_space, pgs);
141     /*
142      * Give the device an opportunity to capture equivalent colors for any
143      * spot colors which might be present in the color space.
144      */
145     if (code >= 0)
146         code = dev_proc(pgs->device, update_spot_equivalent_colors)
147 							(pgs->device, pgs);
148     return code;
149 }
150 
151 /* Set the overprint information appropriate to a separation color space */
152 private int
gx_set_overprint_Separation(const gs_color_space * pcs,gs_state * pgs)153 gx_set_overprint_Separation(const gs_color_space * pcs, gs_state * pgs)
154 {
155     gs_devicen_color_map *  pcmap = &pgs->color_component_map;
156 
157     if (pcmap->use_alt_cspace)
158         return gx_spot_colors_set_overprint(
159                    (const gs_color_space *)&pcs->params.separation.alt_space,
160                    pgs );
161     else {
162         gs_overprint_params_t   params;
163 
164         params.retain_any_comps = pgs->overprint &&
165                                   pcs->params.separation.sep_type != SEP_ALL;
166         if (params.retain_any_comps) {
167             params.retain_spot_comps = false;
168             params.drawn_comps = 0;
169             if (pcs->params.separation.sep_type != SEP_NONE) {
170                 int     mcomp = pcmap->color_map[0];
171 
172                 if (mcomp >= 0)
173 		    gs_overprint_set_drawn_comp( params.drawn_comps, mcomp);
174             }
175         }
176 
177         pgs->effective_overprint_mode = 0;
178         return gs_state_update_overprint(pgs, &params);
179     }
180 }
181 
182 /* Adjust the reference count of a Separation color space. */
183 private void
gx_adjust_cspace_Separation(const gs_color_space * pcs,int delta)184 gx_adjust_cspace_Separation(const gs_color_space * pcs, int delta)
185 {
186     rc_adjust_const(pcs->params.separation.map, delta,
187 		    "gx_adjust_Separation");
188     (*pcs->params.separation.alt_space.type->adjust_cspace_count)
189 	((const gs_color_space *)&pcs->params.separation.alt_space, delta);
190 }
191 
192 /* ------ Constructors/accessors ------ */
193 
194 /*
195  * Build a separation color space.
196  */
197 int
gs_build_Separation(gs_color_space * pcspace,const gs_color_space * palt_cspace,gs_memory_t * pmem)198 gs_build_Separation(
199 		    gs_color_space * pcspace,
200 		    const gs_color_space * palt_cspace,
201 		    gs_memory_t * pmem
202 )
203 {
204     gs_separation_params * pcssepr = &pcspace->params.separation;
205     int code;
206 
207     if (palt_cspace == 0 || !palt_cspace->type->can_be_alt_space)
208 	return_error(gs_error_rangecheck);
209 
210     code = alloc_device_n_map(&pcssepr->map, pmem, "gs_cspace_build_Separation");
211     if (pcssepr->map == NULL) {
212 	gs_free_object(pmem, pcspace, "gs_cspace_build_Separation");
213 	return_error(gs_error_VMerror);
214     }
215     return 0;
216 }
217 
218 /*
219  * Build a separation color space.
220  *
221  * The values array provided with separation color spaces is actually cached
222  * information, but filled in by the client. The alternative space is the
223  * color space in which the tint procedure will provide alternative colors.
224  */
225 int
gs_cspace_build_Separation(gs_color_space ** ppcspace,gs_separation_name sname,const gs_color_space * palt_cspace,int cache_size,gs_memory_t * pmem)226 gs_cspace_build_Separation(
227 			      gs_color_space ** ppcspace,
228 			      gs_separation_name sname,
229 			      const gs_color_space * palt_cspace,
230 			      int cache_size,
231 			      gs_memory_t * pmem
232 )
233 {
234     gs_color_space *pcspace = NULL;
235     gs_separation_params *pcssepr = NULL;
236     int code;
237 
238     if (palt_cspace == 0 || !palt_cspace->type->can_be_alt_space)
239 	return_error(gs_error_rangecheck);
240 
241     code = gs_cspace_alloc(&pcspace, &gs_color_space_type_Separation, pmem);
242     if (code < 0)
243 	return code;
244 
245     code = gs_build_Separation(pcspace, palt_cspace, pmem);
246     if (code < 0) {
247 	gs_free_object(pmem, pcspace, "gs_cspace_build_Separation");
248 	return code;
249     }
250     pcssepr->sep_name = sname;
251     gs_cspace_init_from((gs_color_space *) & pcssepr->alt_space, palt_cspace);
252     *ppcspace = pcspace;
253     return 0;
254 }
255 
256 #if 0 /* Unused; Unsupported by gx_serialize_device_n_map. */
257 /*
258  * Set the tint transformation procedure used by a Separation color space.
259  */
260 int
261 gs_cspace_set_sepr_proc(gs_color_space * pcspace,
262 			int (*proc)(const float *,
263                                     float *,
264                                     const gs_imager_state *,
265                                     void *
266 				    ),
267 			void *proc_data
268 			)
269 {
270     gs_device_n_map *pimap;
271 
272     if (gs_color_space_get_index(pcspace) != gs_color_space_index_Separation)
273 	return_error(gs_error_rangecheck);
274     pimap = pcspace->params.separation.map;
275     pimap->tint_transform = proc;
276     pimap->tint_transform_data = proc_data;
277     pimap->cache_valid = false;
278 
279     return 0;
280 }
281 #endif
282 
283 /*
284  * Set the Separation tint transformation procedure to a Function.
285  */
286 int
gs_cspace_set_sepr_function(const gs_color_space * pcspace,gs_function_t * pfn)287 gs_cspace_set_sepr_function(const gs_color_space *pcspace, gs_function_t *pfn)
288 {
289     gs_device_n_map *pimap;
290 
291     if (gs_color_space_get_index(pcspace) != gs_color_space_index_Separation ||
292 	pfn->params.m != 1 || pfn->params.n !=
293 	  gs_color_space_num_components((const gs_color_space *)
294 					&pcspace->params.separation.alt_space)
295 	)
296 	return_error(gs_error_rangecheck);
297     pimap = pcspace->params.separation.map;
298     pimap->tint_transform = map_devn_using_function;
299     pimap->tint_transform_data = pfn;
300     pimap->cache_valid = false;
301     return 0;
302 }
303 
304 /*
305  * If the Separation tint transformation procedure is a Function,
306  * return the function object, otherwise return 0.
307  */
308 gs_function_t *
gs_cspace_get_sepr_function(const gs_color_space * pcspace)309 gs_cspace_get_sepr_function(const gs_color_space *pcspace)
310 {
311     if (gs_color_space_get_index(pcspace) == gs_color_space_index_Separation &&
312 	pcspace->params.separation.map->tint_transform ==
313 	  map_devn_using_function)
314 	return pcspace->params.separation.map->tint_transform_data;
315     return 0;
316 }
317 
318 /* ------ Internal procedures ------ */
319 
320 /* Initialize a Separation color. */
321 private void
gx_init_Separation(gs_client_color * pcc,const gs_color_space * pcs)322 gx_init_Separation(gs_client_color * pcc, const gs_color_space * pcs)
323 {
324     pcc->paint.values[0] = 1.0;
325 }
326 
327 /* Remap a Separation color. */
328 
329 private int
gx_remap_Separation(const gs_client_color * pcc,const gs_color_space * pcs,gx_device_color * pdc,const gs_imager_state * pis,gx_device * dev,gs_color_select_t select)330 gx_remap_Separation(const gs_client_color * pcc, const gs_color_space * pcs,
331 	gx_device_color * pdc, const gs_imager_state * pis, gx_device * dev,
332 		       gs_color_select_t select)
333 {
334     int code = 0;
335 
336     if (pcs->params.separation.sep_type != SEP_NONE)
337 	code = gx_default_remap_color(pcc, pcs, pdc, pis, dev, select);
338     else {
339         color_set_null(pdc);
340     }
341     /* Save original color space and color info into dev color */
342     pdc->ccolor.paint.values[0] = pcc->paint.values[0];
343     pdc->ccolor_valid = true;
344     return code;
345 }
346 
347 private int
gx_concretize_Separation(const gs_client_color * pc,const gs_color_space * pcs,frac * pconc,const gs_imager_state * pis)348 gx_concretize_Separation(const gs_client_color *pc, const gs_color_space *pcs,
349 			 frac *pconc, const gs_imager_state *pis)
350 {
351     float ftemp;
352     int code;
353     gs_client_color cc;
354     const gs_color_space *pacs =
355 	(const gs_color_space *)&pcs->params.separation.alt_space;
356 
357     if (pcs->params.separation.sep_type == SEP_OTHER &&
358         pcs->params.separation.use_alt_cspace) {
359         gs_device_n_map *map = pcs->params.separation.map;
360 
361 	/* Check the 1-element cache first. */
362 	if (map->cache_valid && map->tint[0] == pc->paint.values[0]) {
363 	    int i, num_out = gs_color_space_num_components(pacs);
364 
365 	    for (i = 0; i < num_out; ++i)
366 		pconc[i] = map->conc[i];
367 	    return 0;
368 	}
369         code = (*pcs->params.separation.map->tint_transform)
370 	    (pc->paint.values, &cc.paint.values[0],
371 	     pis, pcs->params.separation.map->tint_transform_data);
372         if (code < 0)
373 	    return code;
374 	return cs_concretize_color(&cc, pacs, pconc, pis);
375     }
376     else {
377     	pconc[0] = unit_frac(pc->paint.values[0], ftemp);
378     }
379     return 0;
380 }
381 
382 private int
gx_remap_concrete_Separation(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)383 gx_remap_concrete_Separation(const frac * pconc,  const gs_color_space * pcs,
384 	gx_device_color * pdc, const gs_imager_state * pis, gx_device * dev,
385 			     gs_color_select_t select)
386 {
387 #ifdef DEBUG
388     /*
389      * Verify that the color space and imager state info match.
390      */
391     if (pcs->id != pis->color_component_map.cspace_id)
392 	dprintf("gx_remap_concrete_Separation: color space id mismatch");
393 #endif
394 
395     if (pis->color_component_map.use_alt_cspace) {
396         const gs_color_space *pacs =
397 	    (const gs_color_space *)&pcs->params.separation.alt_space;
398 
399 	return (*pacs->type->remap_concrete_color)
400 				(pconc, pacs, pdc, pis, dev, select);
401     }
402     else {
403         gx_remap_concrete_separation(pconc[0], pdc, pis, dev, select);
404         return 0;
405     }
406 }
407 
408 /*
409  * Check that the color component name for a Separation color space
410  * matches the device colorant names.  Also build a gs_devicen_color_map
411  * structure.
412  */
413 private int
check_Separation_component_name(const gs_color_space * pcs,gs_state * pgs)414 check_Separation_component_name(const gs_color_space * pcs, gs_state * pgs)
415 {
416     const gs_separation_name name = pcs->params.separation.sep_name;
417     int colorant_number;
418     byte * pname;
419     uint name_size;
420     gs_devicen_color_map * pcolor_component_map
421 	= &pgs->color_component_map;
422     gx_device * dev = pgs->device;
423 
424     pcolor_component_map->num_components = 1;
425     pcolor_component_map->cspace_id = pcs->id;
426     pcolor_component_map->num_colorants = dev->color_info.num_components;
427     pcolor_component_map->sep_type = pcs->params.separation.sep_type;
428     /*
429      * If this is a None or All separation then we do not need to
430      * use the alternate color space.
431      */
432     if (pcs->params.separation.sep_type != SEP_OTHER) {
433 	pcolor_component_map->use_alt_cspace = false;
434 	return 0;
435     }
436     /*
437      * Always use the alternate color space if the current device is
438      * using an additive color model.  Separations are only for use
439      * with a subtractive color model.
440      */
441     if (dev->color_info.polarity == GX_CINFO_POLARITY_ADDITIVE) {
442 	pcolor_component_map->use_alt_cspace = true;
443 	return 0;
444     }
445     /*
446      * Get the character string and length for the component name.
447      */
448     pcs->params.separation.get_colorname_string(dev->memory, name, &pname, &name_size);
449     /*
450      * Compare the colorant name to the device's.  If the device's
451      * compare routine returns GX_DEVICE_COLOR_MAX_COMPONENTS then the
452      * colorant is in the SeparationNames list but not in the
453      * SeparationOrder list.
454      */
455     colorant_number = (*dev_proc(dev, get_color_comp_index))
456 		(dev, (const char *)pname, name_size, SEPARATION_NAME);
457     if (colorant_number >= 0) {		/* If valid colorant name */
458 	pcolor_component_map->color_map[0] =
459 		    (colorant_number == GX_DEVICE_COLOR_MAX_COMPONENTS) ? -1
460 		    					   : colorant_number;
461 	pcolor_component_map->use_alt_cspace = false;
462     }
463     else
464 	pcolor_component_map->use_alt_cspace = true;
465     return 0;
466 }
467 
468 
469 /* ---------------- Notes on real Separation colors ---------------- */
470 
471 typedef ulong gs_separation;	/* BOGUS */
472 
473 #define gs_no_separation ((gs_separation)(-1L))
474 
475 #define dev_proc_lookup_separation(proc)\
476   gs_separation proc(gx_device *dev, const byte *sname, uint len,\
477     gx_color_value *num_levels)
478 
479 #define dev_proc_map_tint_color(proc)\
480   gx_color_index proc(gx_device *dev, gs_separation sepr, bool overprint,\
481     gx_color_value tint)
482 
483 /*
484  * This next comment is outdated since the Separation color space no longer
485  * has the multi element cache (lookup table) however the remainder is
486  * still appropriate.
487  *
488  * In principle, setting a Separation color space, or setting the device
489  * when the current color space is a Separation space, calls the
490  * lookup_separation device procedure to obtain the separation ID and
491  * the number of achievable levels.  Currently, the only hooks for doing
492  * this are unsuitable: gx_set_cmap_procs isn't called when the color
493  * space changes, and doing it in gx_remap_Separation is inefficient.
494  * Probably the best approach is to call gx_set_cmap_procs whenever the
495  * color space changes.  In fact, if we do this, we can probably short-cut
496  * two levels of procedure call in color remapping (gx_remap_color, by
497  * turning it into a macro, and gx_remap_DeviceXXX, by calling the
498  * cmap_proc procedure directly).  Some care will be required for the
499  * implicit temporary resetting of the color space in [color]image.
500  */
501 
502 /* ---------------- Serialization. -------------------------------- */
503 
504 private int
gx_serialize_Separation(const gs_color_space * pcs,stream * s)505 gx_serialize_Separation(const gs_color_space * pcs, stream * s)
506 {
507     const gs_separation_params * p = &pcs->params.separation;
508     uint n;
509     int code = gx_serialize_cspace_type(pcs, s);
510 
511     if (code < 0)
512 	return code;
513     code = sputs(s, (const byte *)&p->sep_name, sizeof(p->sep_name), &n);
514     if (code < 0)
515 	return code;
516     code = cs_serialize((const gs_color_space *)&p->alt_space, s);
517     if (code < 0)
518 	return code;
519     code = gx_serialize_device_n_map(pcs, p->map, s);
520     if (code < 0)
521 	return code;
522     return sputs(s, (const byte *)&p->sep_type, sizeof(p->sep_type), &n);
523     /* p->use_alt_cspace isn't a property of the space. */
524 }
525