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, ¶ms);
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