/* Copyright (C) 1994, 2000 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: gscsepr.c,v 1.12.2.2.2.1 2003/01/17 00:49:02 giles Exp $ */ /* Separation color space and operation definition */ #include "memory_.h" #include "gx.h" #include "gserrors.h" #include "gsfunc.h" #include "gsrefct.h" #include "gsmatrix.h" /* for gscolor2.h */ #include "gscsepr.h" #include "gxcspace.h" #include "gxfixed.h" /* for gxcolor2.h */ #include "gxcolor2.h" /* for gs_indexed_map */ #include "gzstate.h" /* for pgs->overprint */ /* ---------------- Color space ---------------- */ gs_private_st_composite(st_color_space_Separation, gs_paint_color_space, "gs_color_space_Separation", cs_Separation_enum_ptrs, cs_Separation_reloc_ptrs); /* Define the Separation color space type. */ private cs_proc_base_space(gx_alt_space_Separation); private cs_proc_equal(gx_equal_Separation); private cs_proc_init_color(gx_init_Separation); private cs_proc_concrete_space(gx_concrete_space_Separation); private cs_proc_concretize_color(gx_concretize_Separation); private cs_proc_remap_concrete_color(gx_remap_concrete_Separation); private cs_proc_remap_color(gx_remap_Separation); private cs_proc_install_cspace(gx_install_Separation); private cs_proc_adjust_cspace_count(gx_adjust_cspace_Separation); const gs_color_space_type gs_color_space_type_Separation = { gs_color_space_index_Separation, true, false, &st_color_space_Separation, gx_num_components_1, gx_alt_space_Separation, gx_equal_Separation, gx_init_Separation, gx_restrict01_paint_1, gx_concrete_space_Separation, gx_concretize_Separation, gx_remap_concrete_Separation, gx_remap_Separation, gx_install_Separation, gx_adjust_cspace_Separation, gx_no_adjust_color_count }; /* GC procedures */ private ENUM_PTRS_WITH(cs_Separation_enum_ptrs, gs_color_space *pcs) { return ENUM_USING(*pcs->params.separation.alt_space.type->stype, &pcs->params.separation.alt_space, sizeof(pcs->params.separation.alt_space), index - 1); } ENUM_PTR(0, gs_color_space, params.separation.map); ENUM_PTRS_END private RELOC_PTRS_WITH(cs_Separation_reloc_ptrs, gs_color_space *pcs) { RELOC_PTR(gs_color_space, params.separation.map); RELOC_USING(*pcs->params.separation.alt_space.type->stype, &pcs->params.separation.alt_space, sizeof(gs_base_color_space)); } RELOC_PTRS_END /* Get the alternate space for a Separation space. */ private const gs_color_space * gx_alt_space_Separation(const gs_color_space * pcs) { return (const gs_color_space *)&(pcs->params.separation.alt_space); } /* Test whether one Separation color space equals another. */ private bool gx_equal_Separation(const gs_color_space *pcs1, const gs_color_space *pcs2) { return (gs_color_space_equal(gx_alt_space_Separation(pcs1), gx_alt_space_Separation(pcs2)) && pcs1->params.separation.sname == pcs2->params.separation.sname && ((pcs1->params.separation.map->proc.tint_transform == pcs2->params.separation.map->proc.tint_transform && pcs1->params.separation.map->proc_data == pcs2->params.separation.map->proc_data) || !memcmp(pcs1->params.separation.map->values, pcs2->params.separation.map->values, pcs1->params.separation.map->num_values * sizeof(pcs1->params.separation.map->values[0])))); } /* Get the concrete space for a Separation space. */ /* (We don't support concrete Separation spaces yet.) */ private const gs_color_space * gx_concrete_space_Separation(const gs_color_space * pcs, const gs_imager_state * pis) { const gs_color_space *pacs = (const gs_color_space *)&pcs->params.separation.alt_space; return cs_concrete_space(pacs, pis); } /* Install a Separation color space. */ private int gx_install_Separation(const gs_color_space * pcs, gs_state * pgs) { return (*pcs->params.separation.alt_space.type->install_cspace) ((const gs_color_space *) & pcs->params.separation.alt_space, pgs); } /* Adjust the reference count of a Separation color space. */ private void gx_adjust_cspace_Separation(const gs_color_space * pcs, int delta) { rc_adjust_const(pcs->params.separation.map, delta, "gx_adjust_Separation"); (*pcs->params.separation.alt_space.type->adjust_cspace_count) ((const gs_color_space *)&pcs->params.separation.alt_space, delta); } /* ------ Constructors/accessors ------ */ /* * The default separation tint transformation function. This will just return * the information in the cache or, if the cache is of zero size, set all * components in the alternative color space to 0. * * No special cases are provided for this routine, as the use of separations * (particular in this form) is sufficiently rare to not have a significant * performance impact. */ private int map_tint_value(const gs_separation_params * pcssepr, floatp in_val, float *out_vals) { int ncomps = cs_num_components((const gs_color_space *)&pcssepr->alt_space); int nentries = pcssepr->map->num_values / ncomps; int indx; const float *pv = pcssepr->map->values; int i; if (nentries == 0) { for (i = 0; i < ncomps; i++) out_vals[i] = 0.0; return 0; } if (in_val > 1) indx = nentries - 1; else if (in_val <= 0) indx = 0; else indx = (int)(in_val * nentries + 0.5); pv += indx * ncomps; for (i = 0; i < ncomps; i++) out_vals[i] = pv[i]; return 0; } /* * Allocate the indexed map required by a separation color space. */ private gs_indexed_map * alloc_separation_map(const gs_color_space * palt_cspace, int cache_size, gs_memory_t * pmem) { int num_values = (cache_size == 0 ? 0 : cache_size * gs_color_space_num_components(palt_cspace)); gs_indexed_map *pimap; int code = alloc_indexed_map(&pimap, num_values, pmem, "gs_cspace_build_Separation"); if (code < 0) return 0; pimap->proc.tint_transform = map_tint_value; return pimap; } /* * Build a separation color space. * * The values array provided with separation color spaces is actually cached * information, but filled in by the client. The alternative space is the * color space in which the tint procedure will provide alternative colors. */ 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 ) { gs_color_space *pcspace = 0; gs_separation_params *pcssepr = 0; int code; if (palt_cspace == 0 || !palt_cspace->type->can_be_alt_space) return_error(gs_error_rangecheck); code = gs_cspace_alloc(&pcspace, &gs_color_space_type_Separation, pmem); if (code < 0) return code; pcssepr = &pcspace->params.separation; pcssepr->map = alloc_separation_map(palt_cspace, cache_size, pmem); if (pcssepr->map == 0) { gs_free_object(pmem, pcspace, "gs_cspace_build_Separation"); return_error(gs_error_VMerror); } pcssepr->sname = sname; gs_cspace_init_from((gs_color_space *) & pcssepr->alt_space, palt_cspace); *ppcspace = pcspace; return 0; } /* * Get the cached value array for a separation color space. This will return * a null pointer if the color space is not a separation color space, or if * the separation color space has a cache size of 0. */ float * gs_cspace_get_sepr_value_array(const gs_color_space * pcspace) { if (gs_color_space_get_index(pcspace) != gs_color_space_index_Separation) return 0; return pcspace->params.separation.map->values; } /* * Set the tint transformation procedure used by a Separation color space. */ int gs_cspace_set_sepr_proc(gs_color_space * pcspace, int (*proc) (P3(const gs_separation_params *, floatp, float *))) { gs_indexed_map *pimap; if (gs_color_space_get_index(pcspace) != gs_color_space_index_Separation) return_error(gs_error_rangecheck); pimap = pcspace->params.separation.map; pimap->proc.tint_transform = proc; pimap->proc_data = 0; return 0; } /* Map a Separation tint using a Function. */ private int map_sepr_using_function(const gs_separation_params * pcssepr, floatp in_val, float *out_vals) { float in = in_val; gs_function_t *const pfn = pcssepr->map->proc_data; return gs_function_evaluate(pfn, &in, out_vals); } /* * Set the Separation tint transformation procedure to a Function. */ int gs_cspace_set_sepr_function(const gs_color_space *pcspace, gs_function_t *pfn) { gs_indexed_map *pimap; if (gs_color_space_get_index(pcspace) != gs_color_space_index_Separation || pfn->params.m != 1 || pfn->params.n != gs_color_space_num_components((const gs_color_space *) &pcspace->params.separation.alt_space) ) return_error(gs_error_rangecheck); pimap = pcspace->params.separation.map; pimap->proc.tint_transform = map_sepr_using_function; pimap->proc_data = pfn; return 0; } /* * If the Separation tint transformation procedure is a Function, * return the function object, otherwise return 0. */ gs_function_t * gs_cspace_get_sepr_function(const gs_color_space *pcspace) { if (gs_color_space_get_index(pcspace) == gs_color_space_index_Separation && pcspace->params.separation.map->proc.tint_transform == map_sepr_using_function) return pcspace->params.separation.map->proc_data; return 0; } /* ---------------- Graphics state ---------------- */ /* setoverprint */ void gs_setoverprint(gs_state * pgs, bool ovp) { pgs->overprint = ovp; } /* currentoverprint */ bool gs_currentoverprint(const gs_state * pgs) { return pgs->overprint; } /* setoverprintmode */ int gs_setoverprintmode(gs_state * pgs, int mode) { if (mode < 0 || mode > 1) return_error(gs_error_rangecheck); pgs->overprint_mode = mode; return 0; } /* currentoverprintmode */ int gs_currentoverprintmode(const gs_state * pgs) { return pgs->overprint_mode; } /* ------ Internal procedures ------ */ /* Initialize a Separation color. */ private void gx_init_Separation(gs_client_color * pcc, const gs_color_space * pcs) { pcc->paint.values[0] = 1.0; } /* Remap a Separation color. */ 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) { if (pcs->params.separation.sep_type != SEP_NONE) return gx_default_remap_color(pcc, pcs, pdc, pis, dev, select); color_set_null(pdc); return 0; } private int gx_concretize_Separation(const gs_client_color *pc, const gs_color_space *pcs, frac *pconc, const gs_imager_state *pis) { float tint; int code; gs_client_color cc; const gs_color_space *pacs = (const gs_color_space *)&pcs->params.separation.alt_space; if (pcs->params.separation.sep_type == SEP_ALL) { /* "All" means setting all device components to same value. */ const gs_color_space *pconcs = cs_concrete_space(pacs, pis); int i, n = cs_num_components(pconcs); frac conc; gs_client_color hack_color = *pc; /* Invert the photometric interpretation for additive * color spaces because separations are always subtractive. * fixme: this code sets all colorants in the alternative * color space, not the destination color space. This is * wrong. */ if(n==1 || n==3) hack_color.paint.values[0] = 1 - pc->paint.values[0]; /* hack: using DeviceGray's function to concretize single component color : */ code = gx_concretize_DeviceGray(&hack_color, pacs, &conc, pis); for (i = 0; i < n; i++) pconc[i] = conc; return code; } tint = pc->paint.values[0]; if (tint < 0) tint = 0; else if (tint > 1) tint = 1; /* We always map into the alternate color space. */ code = (*pcs->params.separation.map->proc.tint_transform) (&pcs->params.separation, tint, &cc.paint.values[0]); if (code < 0) return code; return (*pacs->type->concretize_color) (&cc, pacs, pconc, pis); } private int gx_remap_concrete_Separation(const frac * pconc, gx_device_color * pdc, const gs_imager_state * pis, gx_device * dev, gs_color_select_t select) { /* We don't support concrete Separation colors yet. */ return_error(gs_error_rangecheck); } /* ---------------- Notes on real Separation colors ---------------- */ typedef ulong gs_separation; /* BOGUS */ #define gs_no_separation ((gs_separation)(-1L)) #define dev_proc_lookup_separation(proc)\ gs_separation proc(P4(gx_device *dev, const byte *sname, uint len,\ gx_color_value *num_levels)) #define dev_proc_map_tint_color(proc)\ gx_color_index proc(P4(gx_device *dev, gs_separation sepr, bool overprint,\ gx_color_value tint)) /* * In principle, setting a Separation color space, or setting the device * when the current color space is a Separation space, calls the * lookup_separation device procedure to obtain the separation ID and * the number of achievable levels. Currently, the only hooks for doing * this are unsuitable: gx_set_cmap_procs isn't called when the color * space changes, and doing it in gx_remap_Separation is inefficient. * Probably the best approach is to call gx_set_cmap_procs whenever the * color space changes. In fact, if we do this, we can probably short-cut * two levels of procedure call in color remapping (gx_remap_color, by * turning it into a macro, and gx_remap_DeviceXXX, by calling the * cmap_proc procedure directly). Some care will be required for the * implicit temporary resetting of the color space in [color]image. * * For actual remapping of Separation colors, we need cmap_separation_direct * and cmap_separation_halftoned, just as for the other device color spaces. * So we need to break apart gx_render_gray in gxdither.c so it can also * do the job for separations. */