1 /* Copyright (C) 2004 Artifex Software Inc., artofcode LLC. 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: gsequivc.c,v 1.6 2005/07/08 22:04:31 dan Exp $ */
18 /* Routines for determining equivalent color for spot colors */
19
20 #include "math_.h"
21 #include "gdevprn.h"
22 #include "gsparam.h"
23 #include "gstypes.h"
24 #include "gxdcconv.h"
25 #include "gdevdevn.h"
26 #include "gsequivc.h"
27 #include "gzstate.h"
28 #include "gsstate.h"
29 #include "gscspace.h"
30 #include "gxcspace.h"
31
32 /*
33 * These routines are part of the logic for determining an equivalent
34 * process color model color for a spot color. The definition of a
35 * Separation or DeviceN color space include a tint transform function.
36 * This tint transform function is used if the specified spot or separation
37 * colorant is not present. We want to be able to display the spot colors
38 * on our RGB or CMYK display. We are using the tint transform function
39 * to determine a CMYK equivalent to the spot color. Current only CMYK
40 * equivalent colors are supported. This is because the CMYK is the only
41 * standard subtractive color space.
42 *
43 * This process consists of the following steps:
44 *
45 * 1. Whenever new spot colors are found, set status flags indicating
46 * that we have one or more spot colors for which we need to determine an
47 * equivalent color. New spot colors can either be explicitly specified by
48 * the SeparationColorNames device parameter or they may be detected by the
49 * device's get_color_comp_index routine.
50 *
51 * 2. Whenever a Separation or DeviceN color space is installed, the
52 * update_spot_equivalent_colors device proc is called. This allows the
53 * device to check if the color space contains a spot color for which the
54 * device does not know the equivalent CMYK color. The routine
55 * update_spot_equivalent_cmyk_colors is provided for this task (and the
56 * next item).
57 *
58 * 3. For each spot color for which an equivalent color is not known, we
59 * do the following:
60 * a. Create a copy of the color space and change the copy so that it
61 * uses its alternate colorspace.
62 * b. Create a copy of the current imager state and modify its color
63 * mapping (cmap) procs to use a special set of 'capture' procs.
64 * c. Based upon the type of color space (Separation or DeviceN) create
65 * a 'color' which consists of the desired spot color set to 100% and
66 * and other components set to zero.
67 * d. Call the remap_color routine using our modified color space and
68 * state structures. Since we have forced the use of the alternate
69 * color space, we will eventually execute one of the 'capture' color
70 * space mapping procs. This will give us either a gray, RGB, or
71 * CMYK color which is equivalent to the original spot color. If the
72 * color is gray or RGB we convert it to CMYK.
73 * e. Save the equivalent CMYK color in the device structure.
74 *
75 * 4. When a page is to be displayed or a file created, the saved equivalent
76 * color is used as desired. It can be written into the output file. It
77 * may also be used to provide color values which can be combined with the
78 * process color model components for a pixel, to correctly display spot
79 * colors on a monitor. (Note: Overprinting effects with spot colors are
80 * not correctly displayed on an RGB monitor if the device simply uses an RGB
81 * process color model. Instead it is necessary to use a subtractive process
82 * color model and save both process color and spot color data and then
83 * convert the overall result to RGB for display.)
84 *
85 * For this process to work properly, the following changes need to made to
86 * the device.
87 *
88 * 1. The device source module needs to include gsequivc.c for a definition
89 * of the relevant structures and routines. An equivalent_cmyk_color_params
90 * structure needs to be added to the device's structure definition and
91 * it needs to be initialized. For examples see the definition of the
92 * psd_device structure in src/gdevpsd.c and the definitions of the
93 * gs_psdrgb_device and gs_psdcmyk_devices devices in the same module.
94 * 2. Logic needs to be added to the device's get_color_comp_index and
95 * put_param routines to check if any separations have been added to the
96 * device. For examples see code fragments in psd_get_color_comp_index and
97 * psd_put_params in src/gdevpsd.c.
98 * 3. The device needs to have its own version of the
99 * update_spot_equivalent_colors routine. For examples see the definition
100 * of the device_procs macro and the psd_update_spot_equivalent_colors
101 * routine in src/gdevpsd.c.
102 * 4. The device then uses the saved equivalent color values when its output
103 * is created. For example see the psd_write_header routine in
104 * src/gdevpsd.c.
105 */
106
107 /* Function protypes */
108 private void capture_spot_equivalent_cmyk_colors(gx_device * pdev,
109 const gs_state * pgs, const gs_client_color * pcc,
110 const gs_color_space * pcs, int sep_num,
111 equivalent_cmyk_color_params * pparams);
112
113 #define compare_color_names(name, name_size, str, str_size) \
114 (name_size == str_size && \
115 (strncmp((const char *)name, (const char *)str, name_size) == 0))
116
117 private void
update_Separation_spot_equivalent_cmyk_colors(gx_device * pdev,const gs_state * pgs,const gs_color_space * pcs,gs_devn_params * pdevn_params,equivalent_cmyk_color_params * pparams)118 update_Separation_spot_equivalent_cmyk_colors(gx_device * pdev,
119 const gs_state * pgs, const gs_color_space * pcs,
120 gs_devn_params * pdevn_params,
121 equivalent_cmyk_color_params * pparams)
122 {
123 int i;
124
125 /*
126 * Check if the color space's separation name matches any of the
127 * separations for which we need an equivalent CMYK color.
128 */
129 for (i = 0; i < pdevn_params->separations.num_separations; i++) {
130 if (pparams->color[i].color_info_valid == false) {
131 const devn_separation_name * dev_sep_name =
132 &(pdevn_params->separations.names[i]);
133 unsigned int cs_sep_name_size;
134 unsigned char * pcs_sep_name;
135
136 pcs->params.separation.get_colorname_string
137 (pdev->memory, pcs->params.separation.sep_name, &pcs_sep_name,
138 &cs_sep_name_size);
139 if (compare_color_names(dev_sep_name->data, dev_sep_name->size,
140 pcs_sep_name, cs_sep_name_size)) {
141 gs_color_space temp_cs = *pcs;
142 gs_client_color client_color;
143
144 /*
145 * Create a copy of the color space and then modify it
146 * to force the use of the alternate color space.
147 */
148 temp_cs.params.separation.use_alt_cspace = true;
149 client_color.paint.values[0] = 1.0;
150 capture_spot_equivalent_cmyk_colors(pdev, pgs, &client_color,
151 &temp_cs, i, pparams);
152 break;
153 }
154 }
155 }
156 }
157
158 private void
update_DeviceN_spot_equivalent_cmyk_colors(gx_device * pdev,const gs_state * pgs,const gs_color_space * pcs,gs_devn_params * pdevn_params,equivalent_cmyk_color_params * pparams)159 update_DeviceN_spot_equivalent_cmyk_colors(gx_device * pdev,
160 const gs_state * pgs, const gs_color_space * pcs,
161 gs_devn_params * pdevn_params,
162 equivalent_cmyk_color_params * pparams)
163 {
164 int i;
165 unsigned int j;
166 unsigned int cs_sep_name_size;
167 unsigned char * pcs_sep_name;
168
169 /*
170 * Check if the color space contains components named 'None'. If so then
171 * our capture logic does not work properly. When present, the 'None'
172 * components contain alternate color information. However this info is
173 * specified as part of the 'color' and not part of the color space. Thus
174 * we do not have this data when this routine is called. See the
175 * description of DeviceN color spaces in section 4.5 of the PDF spec.
176 * In this situation we exit rather than produce invalid values.
177 */
178 for (j = 0; j < pcs->params.device_n.num_components; j++) {
179 pcs->params.device_n.get_colorname_string
180 (pdev->memory, pcs->params.device_n.names[j],
181 &pcs_sep_name, &cs_sep_name_size);
182 if (compare_color_names("None", 4, pcs_sep_name, cs_sep_name_size))
183 return;
184 }
185
186 /*
187 * Check if the color space's separation names matches any of the
188 * separations for which we need an equivalent CMYK color.
189 */
190 for (i = 0; i < pdevn_params->separations.num_separations; i++) {
191 if (pparams->color[i].color_info_valid == false) {
192 const devn_separation_name * dev_sep_name =
193 &(pdevn_params->separations.names[i]);
194
195 for (j = 0; j < pcs->params.device_n.num_components; j++) {
196 pcs->params.device_n.get_colorname_string
197 (pdev->memory, pcs->params.device_n.names[j], &pcs_sep_name,
198 &cs_sep_name_size);
199 if (compare_color_names(dev_sep_name->data, dev_sep_name->size,
200 pcs_sep_name, cs_sep_name_size)) {
201 gs_color_space temp_cs = *pcs;
202 gs_client_color client_color;
203
204 /*
205 * Create a copy of the color space and then modify it
206 * to force the use of the alternate color space.
207 */
208 memset(&client_color, 0, sizeof(client_color));
209 temp_cs.params.device_n.use_alt_cspace = true;
210 client_color.paint.values[j] = 1.0;
211 capture_spot_equivalent_cmyk_colors(pdev, pgs, &client_color,
212 &temp_cs, i, pparams);
213 break;
214 }
215 }
216 }
217 }
218 }
219
check_all_colors_known(int num_spot,equivalent_cmyk_color_params * pparams)220 private bool check_all_colors_known(int num_spot,
221 equivalent_cmyk_color_params * pparams)
222 {
223 for (num_spot--; num_spot >= 0; num_spot--)
224 if (pparams->color[num_spot].color_info_valid == false)
225 return false;
226 return true;
227 }
228
229 /* If possible, update the equivalent CMYK color for a spot color */
230 void
update_spot_equivalent_cmyk_colors(gx_device * pdev,const gs_state * pgs,gs_devn_params * pdevn_params,equivalent_cmyk_color_params * pparams)231 update_spot_equivalent_cmyk_colors(gx_device * pdev, const gs_state * pgs,
232 gs_devn_params * pdevn_params, equivalent_cmyk_color_params * pparams)
233 {
234 const gs_color_space * pcs;
235
236 /* If all of the color_info is valid then there is nothing to do. */
237 if (pparams->all_color_info_valid)
238 return;
239
240 /* Verify that we really have some separations. */
241 if (pdevn_params->separations.num_separations == 0) {
242 pparams->all_color_info_valid = true;
243 return;
244 }
245 /*
246 * Verify that the given color space is a Separation or a DeviceN color
247 * space. If so then when check if the color space contains a separation
248 * color for which we need a CMYK equivalent.
249 */
250 pcs = pgs->color_space;
251 if (pcs != NULL) {
252 if (pcs->type->index == gs_color_space_index_Separation) {
253 update_Separation_spot_equivalent_cmyk_colors(pdev, pgs, pcs,
254 pdevn_params, pparams);
255 pparams->all_color_info_valid = check_all_colors_known
256 (pdevn_params->separations.num_separations, pparams);
257 }
258 else if (pcs->type->index == gs_color_space_index_DeviceN) {
259 update_DeviceN_spot_equivalent_cmyk_colors(pdev, pgs, pcs,
260 pdevn_params, pparams);
261 pparams->all_color_info_valid = check_all_colors_known
262 (pdevn_params->separations.num_separations, pparams);
263 }
264 }
265 }
266
267 private void
save_spot_equivalent_cmyk_color(int sep_num,equivalent_cmyk_color_params * pparams,frac cmyk[4])268 save_spot_equivalent_cmyk_color(int sep_num,
269 equivalent_cmyk_color_params * pparams, frac cmyk[4])
270 {
271 pparams->color[sep_num].c = cmyk[0];
272 pparams->color[sep_num].m = cmyk[1];
273 pparams->color[sep_num].y = cmyk[2];
274 pparams->color[sep_num].k = cmyk[3];
275 pparams->color[sep_num].color_info_valid = true;
276 }
277
278 /*
279 * A structure definition for a device for capturing equivalent colors
280 */
281 typedef struct color_capture_device_s {
282 gx_device_common;
283 gx_prn_device_common;
284 /* ... device-specific parameters ... */
285 /* The following values are needed by the cmap procs for saving data */
286 int sep_num; /* Number of the separation being captured */
287 /* Pointer to original device's equivalent CMYK colors */
288 equivalent_cmyk_color_params * pequiv_cmyk_colors;
289 } color_capture_device;
290
291 /*
292 * Replacement routines for the cmap procs. These routines will capture the
293 * equivalent color.
294 */
295 private cmap_proc_gray(cmap_gray_capture_cmyk_color);
296 private cmap_proc_rgb(cmap_rgb_capture_cmyk_color);
297 private cmap_proc_cmyk(cmap_cmyk_capture_cmyk_color);
298 private cmap_proc_rgb_alpha(cmap_rgb_alpha_capture_cmyk_color);
299 private cmap_proc_separation(cmap_separation_capture_cmyk_color);
300 private cmap_proc_devicen(cmap_devicen_capture_cmyk_color);
301
302 private const gx_color_map_procs cmap_capture_cmyk_color = {
303 cmap_gray_capture_cmyk_color,
304 cmap_rgb_capture_cmyk_color,
305 cmap_cmyk_capture_cmyk_color,
306 cmap_rgb_alpha_capture_cmyk_color,
307 cmap_separation_capture_cmyk_color,
308 cmap_devicen_capture_cmyk_color
309 };
310
311 private void
cmap_gray_capture_cmyk_color(frac gray,gx_device_color * pdc,const gs_imager_state * pis,gx_device * dev,gs_color_select_t select)312 cmap_gray_capture_cmyk_color(frac gray, gx_device_color * pdc,
313 const gs_imager_state * pis, gx_device * dev, gs_color_select_t select)
314 {
315 equivalent_cmyk_color_params * pparams =
316 ((color_capture_device *)dev)->pequiv_cmyk_colors;
317 int sep_num = ((color_capture_device *)dev)->sep_num;
318 frac cmyk[4];
319
320 cmyk[0] = cmyk[1] = cmyk[2] = frac_0;
321 cmyk[3] = frac_1 - gray;
322 save_spot_equivalent_cmyk_color(sep_num, pparams, cmyk);
323 }
324
325 private void
cmap_rgb_capture_cmyk_color(frac r,frac g,frac b,gx_device_color * pdc,const gs_imager_state * pis,gx_device * dev,gs_color_select_t select)326 cmap_rgb_capture_cmyk_color(frac r, frac g, frac b, gx_device_color * pdc,
327 const gs_imager_state * pis, gx_device * dev, gs_color_select_t select)
328 {
329 equivalent_cmyk_color_params * pparams =
330 ((color_capture_device *)dev)->pequiv_cmyk_colors;
331 int sep_num = ((color_capture_device *)dev)->sep_num;
332 frac cmyk[4];
333
334 color_rgb_to_cmyk(r, g, b, pis, cmyk);
335 save_spot_equivalent_cmyk_color(sep_num, pparams, cmyk);
336 }
337
338 private void
cmap_cmyk_capture_cmyk_color(frac c,frac m,frac y,frac k,gx_device_color * pdc,const gs_imager_state * pis,gx_device * dev,gs_color_select_t select)339 cmap_cmyk_capture_cmyk_color(frac c, frac m, frac y, frac k, gx_device_color * pdc,
340 const gs_imager_state * pis, gx_device * dev, gs_color_select_t select)
341 {
342 equivalent_cmyk_color_params * pparams =
343 ((color_capture_device *)dev)->pequiv_cmyk_colors;
344 int sep_num = ((color_capture_device *)dev)->sep_num;
345 frac cmyk[4];
346
347 cmyk[0] = c;
348 cmyk[1] = m;
349 cmyk[2] = y;
350 cmyk[3] = k;
351 save_spot_equivalent_cmyk_color(sep_num, pparams, cmyk);
352 }
353
354 private void
cmap_rgb_alpha_capture_cmyk_color(frac r,frac g,frac b,frac alpha,gx_device_color * pdc,const gs_imager_state * pis,gx_device * dev,gs_color_select_t select)355 cmap_rgb_alpha_capture_cmyk_color(frac r, frac g, frac b, frac alpha,
356 gx_device_color * pdc, const gs_imager_state * pis, gx_device * dev,
357 gs_color_select_t select)
358 {
359 cmap_rgb_capture_cmyk_color(r, g, b, pdc, pis, dev, select);
360 }
361
362 private void
cmap_separation_capture_cmyk_color(frac all,gx_device_color * pdc,const gs_imager_state * pis,gx_device * dev,gs_color_select_t select)363 cmap_separation_capture_cmyk_color(frac all, gx_device_color * pdc,
364 const gs_imager_state * pis, gx_device * dev, gs_color_select_t select)
365 {
366 dprintf("cmap_separation_capture_cmyk_color - this routine should not be executed\n");
367 }
368
369 private void
cmap_devicen_capture_cmyk_color(const frac * pcc,gx_device_color * pdc,const gs_imager_state * pis,gx_device * dev,gs_color_select_t select)370 cmap_devicen_capture_cmyk_color(const frac * pcc, gx_device_color * pdc,
371 const gs_imager_state * pis, gx_device * dev, gs_color_select_t select)
372 {
373 dprintf("cmap_devicen_capture_cmyk_color - this routine should not be executed\n");
374 }
375
376 /*
377 * Note: The color space (pcs) has already been modified to use the
378 * alternate color space.
379 */
380 private void
capture_spot_equivalent_cmyk_colors(gx_device * pdev,const gs_state * pgs,const gs_client_color * pcc,const gs_color_space * pcs,int sep_num,equivalent_cmyk_color_params * pparams)381 capture_spot_equivalent_cmyk_colors(gx_device * pdev, const gs_state * pgs,
382 const gs_client_color * pcc, const gs_color_space * pcs,
383 int sep_num, equivalent_cmyk_color_params * pparams)
384 {
385 gs_imager_state temp_state = *((const gs_imager_state *)pgs);
386 color_capture_device temp_device = { 0 };
387 gx_device_color dev_color;
388
389 /*
390 * Create a temp device. The primary purpose of this device is pass the
391 * separation number and a pointer to the original device's equivalent
392 * color parameters. Since we only using this device for a very specific
393 * purpose, we only set up the color_info structure and and our data.
394 */
395 temp_device.color_info = pdev->color_info;
396 temp_device.sep_num = sep_num;
397 temp_device.pequiv_cmyk_colors = pparams;
398 /*
399 * Create a temp copy of the imager state. We do this so that we
400 * can modify the color space mapping (cmap) procs. We use our
401 * replacment procs to capture the color. The installation of a
402 * Separation or DeviceN color space also sets a use_alt_cspace flag
403 * in the state. We also need to set this to use the alternate space.
404 */
405 temp_state.cmap_procs = &cmap_capture_cmyk_color;
406 temp_state.color_component_map.use_alt_cspace = true;
407
408 /* Now capture the color */
409 pcs->type->remap_color (pcc, pcs, &dev_color, &temp_state,
410 (gx_device *)&temp_device, gs_color_select_texture);
411 }
412