summaryrefslogtreecommitdiff
path: root/gs/base/gsequivc.c
diff options
context:
space:
mode:
Diffstat (limited to 'gs/base/gsequivc.c')
-rw-r--r--gs/base/gsequivc.c402
1 files changed, 402 insertions, 0 deletions
diff --git a/gs/base/gsequivc.c b/gs/base/gsequivc.c
new file mode 100644
index 000000000..5c7bb16b5
--- /dev/null
+++ b/gs/base/gsequivc.c
@@ -0,0 +1,402 @@
+/* Copyright (C) 2001-2006 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied, modified
+ or distributed except as expressly authorized under the terms of that
+ license. Refer to licensing information at http://www.artifex.com/
+ or contact Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134,
+ San Rafael, CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+/*$Id$ */
+/* Routines for determining equivalent color for spot colors */
+
+#include "math_.h"
+#include "gdevprn.h"
+#include "gsparam.h"
+#include "gstypes.h"
+#include "gxdcconv.h"
+#include "gdevdevn.h"
+#include "gsequivc.h"
+#include "gzstate.h"
+#include "gsstate.h"
+#include "gscspace.h"
+#include "gxcspace.h"
+
+/*
+ * These routines are part of the logic for determining an equivalent
+ * process color model color for a spot color. The definition of a
+ * Separation or DeviceN color space include a tint transform function.
+ * This tint transform function is used if the specified spot or separation
+ * colorant is not present. We want to be able to display the spot colors
+ * on our RGB or CMYK display. We are using the tint transform function
+ * to determine a CMYK equivalent to the spot color. Current only CMYK
+ * equivalent colors are supported. This is because the CMYK is the only
+ * standard subtractive color space.
+ *
+ * This process consists of the following steps:
+ *
+ * 1. Whenever new spot colors are found, set status flags indicating
+ * that we have one or more spot colors for which we need to determine an
+ * equivalent color. New spot colors can either be explicitly specified by
+ * the SeparationColorNames device parameter or they may be detected by the
+ * device's get_color_comp_index routine.
+ *
+ * 2. Whenever a Separation or DeviceN color space is installed, the
+ * update_spot_equivalent_colors device proc is called. This allows the
+ * device to check if the color space contains a spot color for which the
+ * device does not know the equivalent CMYK color. The routine
+ * update_spot_equivalent_cmyk_colors is provided for this task (and the
+ * next item).
+ *
+ * 3. For each spot color for which an equivalent color is not known, we
+ * do the following:
+ * a. Create a copy of the color space and change the copy so that it
+ * uses its alternate colorspace.
+ * b. Create a copy of the current imager state and modify its color
+ * mapping (cmap) procs to use a special set of 'capture' procs.
+ * c. Based upon the type of color space (Separation or DeviceN) create
+ * a 'color' which consists of the desired spot color set to 100% and
+ * and other components set to zero.
+ * d. Call the remap_color routine using our modified color space and
+ * state structures. Since we have forced the use of the alternate
+ * color space, we will eventually execute one of the 'capture' color
+ * space mapping procs. This will give us either a gray, RGB, or
+ * CMYK color which is equivalent to the original spot color. If the
+ * color is gray or RGB we convert it to CMYK.
+ * e. Save the equivalent CMYK color in the device structure.
+ *
+ * 4. When a page is to be displayed or a file created, the saved equivalent
+ * color is used as desired. It can be written into the output file. It
+ * may also be used to provide color values which can be combined with the
+ * process color model components for a pixel, to correctly display spot
+ * colors on a monitor. (Note: Overprinting effects with spot colors are
+ * not correctly displayed on an RGB monitor if the device simply uses an RGB
+ * process color model. Instead it is necessary to use a subtractive process
+ * color model and save both process color and spot color data and then
+ * convert the overall result to RGB for display.)
+ *
+ * For this process to work properly, the following changes need to made to
+ * the device.
+ *
+ * 1. The device source module needs to include gsequivc.h for a definition
+ * of the relevant structures and routines. An equivalent_cmyk_color_params
+ * structure needs to be added to the device's structure definition and
+ * it needs to be initialized. For examples see the definition of the
+ * psd_device structure in src/gdevpsd.c and the definitions of the
+ * gs_psdrgb_device and gs_psdcmyk_devices devices in the same module.
+ * 2. Logic needs to be added to the device's get_color_comp_index and
+ * put_param routines to check if any separations have been added to the
+ * device. For examples see code fragments in psd_get_color_comp_index and
+ * psd_put_params in src/gdevpsd.c.
+ * 3. The device needs to have its own version of the
+ * update_spot_equivalent_colors routine. For examples see the definition
+ * of the device_procs macro and the psd_update_spot_equivalent_colors
+ * routine in src/gdevpsd.c.
+ * 4. The device then uses the saved equivalent color values when its output
+ * is created. For example see the psd_write_header routine in
+ * src/gdevpsd.c.
+ */
+
+#define compare_color_names(name, name_size, str, str_size) \
+ (name_size == str_size && \
+ (strncmp((const char *)name, (const char *)str, name_size) == 0))
+
+static 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)
+{
+ int i;
+
+ /*
+ * Check if the color space's separation name matches any of the
+ * separations for which we need an equivalent CMYK color.
+ */
+ for (i = 0; i < pdevn_params->separations.num_separations; i++) {
+ if (pparams->color[i].color_info_valid == false) {
+ const devn_separation_name * dev_sep_name =
+ &(pdevn_params->separations.names[i]);
+ unsigned int cs_sep_name_size;
+ unsigned char * pcs_sep_name;
+
+ pcs->params.separation.get_colorname_string
+ (pdev->memory, pcs->params.separation.sep_name, &pcs_sep_name,
+ &cs_sep_name_size);
+ if (compare_color_names(dev_sep_name->data, dev_sep_name->size,
+ pcs_sep_name, cs_sep_name_size)) {
+ gs_color_space temp_cs = *pcs;
+ gs_client_color client_color;
+
+ /*
+ * Create a copy of the color space and then modify it
+ * to force the use of the alternate color space.
+ */
+ temp_cs.params.separation.use_alt_cspace = true;
+ client_color.paint.values[0] = 1.0;
+ capture_spot_equivalent_cmyk_colors(pdev, pgs, &client_color,
+ &temp_cs, i, pparams);
+ break;
+ }
+ }
+ }
+}
+
+static 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)
+{
+ int i;
+ unsigned int j;
+ unsigned int cs_sep_name_size;
+ unsigned char * pcs_sep_name;
+
+ /*
+ * Check if the color space contains components named 'None'. If so then
+ * our capture logic does not work properly. When present, the 'None'
+ * components contain alternate color information. However this info is
+ * specified as part of the 'color' and not part of the color space. Thus
+ * we do not have this data when this routine is called. See the
+ * description of DeviceN color spaces in section 4.5 of the PDF spec.
+ * In this situation we exit rather than produce invalid values.
+ */
+ for (j = 0; j < pcs->params.device_n.num_components; j++) {
+ pcs->params.device_n.get_colorname_string
+ (pdev->memory, pcs->params.device_n.names[j],
+ &pcs_sep_name, &cs_sep_name_size);
+ if (compare_color_names("None", 4, pcs_sep_name, cs_sep_name_size))
+ return;
+ }
+
+ /*
+ * Check if the color space's separation names matches any of the
+ * separations for which we need an equivalent CMYK color.
+ */
+ for (i = 0; i < pdevn_params->separations.num_separations; i++) {
+ if (pparams->color[i].color_info_valid == false) {
+ const devn_separation_name * dev_sep_name =
+ &(pdevn_params->separations.names[i]);
+
+ for (j = 0; j < pcs->params.device_n.num_components; j++) {
+ pcs->params.device_n.get_colorname_string
+ (pdev->memory, pcs->params.device_n.names[j], &pcs_sep_name,
+ &cs_sep_name_size);
+ if (compare_color_names(dev_sep_name->data, dev_sep_name->size,
+ pcs_sep_name, cs_sep_name_size)) {
+ gs_color_space temp_cs = *pcs;
+ gs_client_color client_color;
+
+ /*
+ * Create a copy of the color space and then modify it
+ * to force the use of the alternate color space.
+ */
+ memset(&client_color, 0, sizeof(client_color));
+ temp_cs.params.device_n.use_alt_cspace = true;
+ client_color.paint.values[j] = 1.0;
+ capture_spot_equivalent_cmyk_colors(pdev, pgs, &client_color,
+ &temp_cs, i, pparams);
+ break;
+ }
+ }
+ }
+ }
+}
+
+static bool check_all_colors_known(int num_spot,
+ equivalent_cmyk_color_params * pparams)
+{
+ for (num_spot--; num_spot >= 0; num_spot--)
+ if (pparams->color[num_spot].color_info_valid == false)
+ return false;
+ return true;
+}
+
+/* If possible, update the equivalent CMYK color for a spot color */
+void
+update_spot_equivalent_cmyk_colors(gx_device * pdev, const gs_state * pgs,
+ gs_devn_params * pdevn_params, equivalent_cmyk_color_params * pparams)
+{
+ const gs_color_space * pcs;
+
+ /* If all of the color_info is valid then there is nothing to do. */
+ if (pparams->all_color_info_valid)
+ return;
+
+ /* Verify that we really have some separations. */
+ if (pdevn_params->separations.num_separations == 0) {
+ pparams->all_color_info_valid = true;
+ return;
+ }
+ /*
+ * Verify that the given color space is a Separation or a DeviceN color
+ * space. If so then when check if the color space contains a separation
+ * color for which we need a CMYK equivalent.
+ */
+ pcs = pgs->color_space;
+ if (pcs != NULL) {
+ if (pcs->type->index == gs_color_space_index_Separation) {
+ update_Separation_spot_equivalent_cmyk_colors(pdev, pgs, pcs,
+ pdevn_params, pparams);
+ pparams->all_color_info_valid = check_all_colors_known
+ (pdevn_params->separations.num_separations, pparams);
+ }
+ else if (pcs->type->index == gs_color_space_index_DeviceN) {
+ update_DeviceN_spot_equivalent_cmyk_colors(pdev, pgs, pcs,
+ pdevn_params, pparams);
+ pparams->all_color_info_valid = check_all_colors_known
+ (pdevn_params->separations.num_separations, pparams);
+ }
+ }
+}
+
+static void
+save_spot_equivalent_cmyk_color(int sep_num,
+ equivalent_cmyk_color_params * pparams, frac cmyk[4])
+{
+ pparams->color[sep_num].c = cmyk[0];
+ pparams->color[sep_num].m = cmyk[1];
+ pparams->color[sep_num].y = cmyk[2];
+ pparams->color[sep_num].k = cmyk[3];
+ pparams->color[sep_num].color_info_valid = true;
+}
+
+/*
+ * A structure definition for a device for capturing equivalent colors
+ */
+typedef struct color_capture_device_s {
+ gx_device_common;
+ gx_prn_device_common;
+ /* ... device-specific parameters ... */
+ /* The following values are needed by the cmap procs for saving data */
+ int sep_num; /* Number of the separation being captured */
+ /* Pointer to original device's equivalent CMYK colors */
+ equivalent_cmyk_color_params * pequiv_cmyk_colors;
+} color_capture_device;
+
+/*
+ * Replacement routines for the cmap procs. These routines will capture the
+ * equivalent color.
+ */
+static cmap_proc_gray(cmap_gray_capture_cmyk_color);
+static cmap_proc_rgb(cmap_rgb_capture_cmyk_color);
+static cmap_proc_cmyk(cmap_cmyk_capture_cmyk_color);
+static cmap_proc_rgb_alpha(cmap_rgb_alpha_capture_cmyk_color);
+static cmap_proc_separation(cmap_separation_capture_cmyk_color);
+static cmap_proc_devicen(cmap_devicen_capture_cmyk_color);
+
+static const gx_color_map_procs cmap_capture_cmyk_color = {
+ cmap_gray_capture_cmyk_color,
+ cmap_rgb_capture_cmyk_color,
+ cmap_cmyk_capture_cmyk_color,
+ cmap_rgb_alpha_capture_cmyk_color,
+ cmap_separation_capture_cmyk_color,
+ cmap_devicen_capture_cmyk_color
+};
+
+static 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)
+{
+ equivalent_cmyk_color_params * pparams =
+ ((color_capture_device *)dev)->pequiv_cmyk_colors;
+ int sep_num = ((color_capture_device *)dev)->sep_num;
+ frac cmyk[4];
+
+ cmyk[0] = cmyk[1] = cmyk[2] = frac_0;
+ cmyk[3] = frac_1 - gray;
+ save_spot_equivalent_cmyk_color(sep_num, pparams, cmyk);
+}
+
+static 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)
+{
+ equivalent_cmyk_color_params * pparams =
+ ((color_capture_device *)dev)->pequiv_cmyk_colors;
+ int sep_num = ((color_capture_device *)dev)->sep_num;
+ frac cmyk[4];
+
+ color_rgb_to_cmyk(r, g, b, pis, cmyk);
+ save_spot_equivalent_cmyk_color(sep_num, pparams, cmyk);
+}
+
+static 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)
+{
+ equivalent_cmyk_color_params * pparams =
+ ((color_capture_device *)dev)->pequiv_cmyk_colors;
+ int sep_num = ((color_capture_device *)dev)->sep_num;
+ frac cmyk[4];
+
+ cmyk[0] = c;
+ cmyk[1] = m;
+ cmyk[2] = y;
+ cmyk[3] = k;
+ save_spot_equivalent_cmyk_color(sep_num, pparams, cmyk);
+}
+
+static 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)
+{
+ cmap_rgb_capture_cmyk_color(r, g, b, pdc, pis, dev, select);
+}
+
+static 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)
+{
+ dprintf("cmap_separation_capture_cmyk_color - this routine should not be executed\n");
+}
+
+static 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)
+{
+ dprintf("cmap_devicen_capture_cmyk_color - this routine should not be executed\n");
+}
+
+/*
+ * Note: The color space (pcs) has already been modified to use the
+ * alternate color space.
+ */
+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)
+{
+ gs_imager_state temp_state = *((const gs_imager_state *)pgs);
+ color_capture_device temp_device = { 0 };
+ gx_device_color dev_color;
+
+ /*
+ * Create a temp device. The primary purpose of this device is pass the
+ * separation number and a pointer to the original device's equivalent
+ * color parameters. Since we only using this device for a very specific
+ * purpose, we only set up the color_info structure and and our data.
+ */
+ temp_device.color_info = pdev->color_info;
+ temp_device.sep_num = sep_num;
+ temp_device.pequiv_cmyk_colors = pparams;
+ /*
+ * Create a temp copy of the imager state. We do this so that we
+ * can modify the color space mapping (cmap) procs. We use our
+ * replacment procs to capture the color. The installation of a
+ * Separation or DeviceN color space also sets a use_alt_cspace flag
+ * in the state. We also need to set this to use the alternate space.
+ */
+ temp_state.cmap_procs = &cmap_capture_cmyk_color;
+ temp_state.color_component_map.use_alt_cspace = true;
+
+ /* Now capture the color */
+ pcs->type->remap_color (pcc, pcs, &dev_color, &temp_state,
+ (gx_device *)&temp_device, gs_color_select_texture);
+}