Subversion Repositories planix.SVN

Rev

Rev 2 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 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
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
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
 
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
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
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
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
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
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
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
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
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
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
}