/* Copyright (C) 1998, 1999 artofcode LLC. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA, 02111-1307. */ /*$Id: gdevcmap.c,v 1.2.6.1.2.1 2003/01/17 00:49:00 giles Exp $ */ /* Special color mapping device */ #include "gx.h" #include "gserrors.h" #include "gxdevice.h" #include "gxlum.h" #include "gxfrac.h" #include "gxdcconv.h" #include "gdevcmap.h" /* * The devices in this file exist only to implement the PCL5 special * color mapping algorithms. They are not useful for PostScript. */ /* GC descriptor */ public_st_device_cmap(); /* Device procedures */ private dev_proc_map_rgb_color(cmap_map_rgb_color); private dev_proc_map_rgb_alpha_color(cmap_map_rgb_alpha_color); private dev_proc_map_cmyk_color(cmap_map_cmyk_color); private dev_proc_get_params(cmap_get_params); private dev_proc_put_params(cmap_put_params); private dev_proc_begin_typed_image(cmap_begin_typed_image); private const gx_device_cmap gs_cmap_device = { std_device_dci_body(gx_device_cmap, 0, "special color mapper", 0, 0, 1, 1, 3, 24, 255, 255, 256, 256), { 0, 0, 0, 0, 0, cmap_map_rgb_color, 0, /* map_color_rgb */ gx_forward_fill_rectangle, gx_forward_tile_rectangle, gx_forward_copy_mono, gx_forward_copy_color, 0, 0, cmap_get_params, cmap_put_params, cmap_map_cmyk_color, 0, 0, cmap_map_rgb_alpha_color, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, gx_default_begin_image, 0, 0, 0, 0, 0, cmap_begin_typed_image, 0, 0 /* map_color_rgb_alpha */ }, 0, /* target */ device_cmap_identity }; /* Set the color mapping method. */ private int gdev_cmap_set_method(gx_device_cmap * cmdev, gx_device_color_mapping_method_t method) { gx_device *target = cmdev->target; /* * If we're transforming the color, we may need to fool the graphics * core into not halftoning. */ set_dev_proc(cmdev, map_cmyk_color, gx_default_map_cmyk_color); set_dev_proc(cmdev, map_color_rgb, gx_forward_map_color_rgb); switch (method) { case device_cmap_identity: /* * In this case, and only this case, we can allow the target's * color model to propagate here. */ set_dev_proc(cmdev, map_cmyk_color, gx_forward_map_cmyk_color); cmdev->color_info.max_gray = target->color_info.max_gray; cmdev->color_info.max_color = target->color_info.max_color; cmdev->color_info.num_components = target->color_info.num_components; gx_device_copy_color_procs((gx_device *)cmdev, target); break; case device_cmap_monochrome: cmdev->color_info.max_gray = target->color_info.max_gray; cmdev->color_info.max_color = target->color_info.max_color; cmdev->color_info.num_components = 1; break; case device_cmap_snap_to_primaries: case device_cmap_color_to_black_over_white: cmdev->color_info.max_gray = cmdev->color_info.max_color = 4095; /* * We have to be an RGB device, otherwise "primaries" doesn't * have the proper meaning. */ cmdev->color_info.num_components = 3; break; default: return_error(gs_error_rangecheck); } cmdev->mapping_method = method; return 0; } /* Initialize the device. */ int gdev_cmap_init(gx_device_cmap * dev, gx_device * target, gx_device_color_mapping_method_t method) { int code; gx_device_init((gx_device *) dev, (const gx_device *)&gs_cmap_device, target->memory, true); gx_device_set_target((gx_device_forward *)dev, target); gx_device_copy_params((gx_device *)dev, target); gx_device_forward_fill_in_procs((gx_device_forward *) dev); code = gdev_cmap_set_method(dev, method); if (code < 0) return code; return 0; } /* Map RGB colors, and convert to the target's native color model. */ /* Return true if the target is an RGB device, false if CMYK. */ private bool cmap_convert_rgb_color(const gx_device_cmap * cmdev, gx_color_value red, gx_color_value green, gx_color_value blue, gx_color_value cv[4]) { gx_color_value red_out, green_out, blue_out; switch (cmdev->mapping_method) { case device_cmap_snap_to_primaries: /* Snap each RGB primary component to 0 or 1 individually. */ red_out = (red <= gx_max_color_value / 2 ? 0 : gx_max_color_value); green_out = (green <= gx_max_color_value / 2 ? 0 : gx_max_color_value); blue_out = (blue <= gx_max_color_value / 2 ? 0 : gx_max_color_value); break; case device_cmap_color_to_black_over_white: /* Snap black to white, other colors to black. */ red_out = green_out = blue_out = ((red | green | blue) == 0 ? gx_max_color_value : 0); break; case device_cmap_identity: case device_cmap_monochrome: default: red_out = red, green_out = green, blue_out = blue; break; } /* Check for a CMYK device. */ if (cmdev->target->color_info.num_components <= 3) { cv[0] = red_out, cv[1] = green_out, cv[2] = blue_out; return true; } /* * Convert RGB to CMYK using default (null) black generation and * undercolor removal. This isn't right, but we don't have access to an * imager state to provide the correct procedures. */ cv[0] = gx_max_color_value - red_out; cv[1] = gx_max_color_value - green_out; cv[2] = gx_max_color_value - blue_out; cv[3] = 0; return false; } private gx_color_index cmap_map_rgb_color(gx_device * dev, gx_color_value red, gx_color_value green, gx_color_value blue) { const gx_device_cmap *const cmdev = (const gx_device_cmap *)dev; gx_device *target = cmdev->target; gx_color_value cv[4]; bool is_rgb = cmap_convert_rgb_color(cmdev, red, green, blue, cv); return (is_rgb ? target->procs.map_rgb_color(target, cv[0], cv[1], cv[2]) : target->procs.map_cmyk_color(target, cv[0], cv[1], cv[2], cv[3])); } private gx_color_index cmap_map_rgb_alpha_color(gx_device * dev, gx_color_value red, gx_color_value green, gx_color_value blue, gx_color_value alpha) { const gx_device_cmap *const cmdev = (const gx_device_cmap *)dev; gx_device *target = cmdev->target; gx_color_value cv[4]; bool is_rgb = cmap_convert_rgb_color(cmdev, red, green, blue, cv); return (is_rgb ? target->procs.map_rgb_alpha_color(target, cv[0], cv[1], cv[2], alpha) : /****** CMYK DISREGARDS ALPHA ******/ target->procs.map_cmyk_color(target, cv[0], cv[1], cv[2], cv[3])); } private gx_color_index cmap_map_cmyk_color(gx_device * dev, gx_color_value c, gx_color_value m, gx_color_value y, gx_color_value k) { const gx_device_cmap *const cmdev = (const gx_device_cmap *)dev; gx_device *target = cmdev->target; frac frac_rgb[3]; if (cmdev->mapping_method == device_cmap_identity) return target->procs.map_cmyk_color(target, c, m, y, k); color_cmyk_to_rgb(cv2frac(c), cv2frac(m), cv2frac(y), cv2frac(k), NULL, frac_rgb); return cmap_map_rgb_color(dev, frac2cv(frac_rgb[0]), frac2cv(frac_rgb[1]), frac2cv(frac_rgb[2])); } /* Get parameters. */ private int cmap_get_params(gx_device * dev, gs_param_list * plist) { int code = gx_forward_get_params(dev, plist); int ecode = code; gx_device_cmap * const cmdev = (gx_device_cmap *)dev; int cmm = cmdev->mapping_method; if ((code = param_write_int(plist, "ColorMappingMethod", &cmm)) < 0) ecode = code; return ecode; } /* Update parameters; copy the device information back afterwards. */ private int cmap_put_params(gx_device * dev, gs_param_list * plist) { int code = gx_forward_put_params(dev, plist); int ecode = code; gx_device_cmap * const cmdev = (gx_device_cmap *)dev; int cmm = cmdev->mapping_method; const char *param_name; switch (code = param_read_int(plist, param_name = "ColorMappingMethod", &cmm)) { case 0: if (cmm < 0 || cmm > device_cmap_max_method) { code = gs_note_error(gs_error_rangecheck); } else break; default: ecode = code; param_signal_error(plist, param_name, ecode); break; case 1: break; } if (code >= 0) { gx_device_copy_params(dev, cmdev->target); gdev_cmap_set_method(cmdev, cmm); } return ecode; } /* * Handle high-level images. The only reason we do this, rather than simply * pass the operation to the target, is that the image still has to call the * cmap device to do its color mapping. As presently implemented, this * disables any high-level implementation that the target may provide. */ private int cmap_begin_typed_image(gx_device * dev, const gs_imager_state * pis, const gs_matrix * pmat, const gs_image_common_t * pic, const gs_int_rect * prect, const gx_drawing_color * pdcolor, const gx_clip_path * pcpath, gs_memory_t * memory, gx_image_enum_common_t ** pinfo) { const gx_device_cmap *const cmdev = (const gx_device_cmap *)dev; gx_device *target = cmdev->target; if (cmdev->mapping_method == device_cmap_identity) return (*dev_proc(target, begin_typed_image)) (target, pis, pmat, pic, prect, pdcolor, pcpath, memory, pinfo); return gx_default_begin_typed_image(dev, pis, pmat, pic, prect, pdcolor, pcpath, memory, pinfo); }