/* Copyright (C) 1989, 1992, 1993, 1994, 1996, 1997, 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: gscolor.c,v 1.5.2.1.2.1 2003/01/17 00:49:02 giles Exp $ */ /* Color and halftone operators for Ghostscript library */ #include "gx.h" #include "gserrors.h" #include "gsstruct.h" #include "gsutil.h" /* for gs_next_ids */ #include "gsccolor.h" #include "gscssub.h" #include "gxcspace.h" #include "gxdcconv.h" #include "gxdevice.h" /* for gx_color_index */ #include "gxcmap.h" #include "gzstate.h" /* Imported from gsht.c */ void gx_set_effective_transfer(P1(gs_state *)); /* Structure descriptors */ public_st_client_color(); public_st_transfer_map(); /* GC procedures */ private ENUM_PTRS_WITH(transfer_map_enum_ptrs, gx_transfer_map *mptr) return 0; case 0: ENUM_RETURN((mptr->proc == 0 ? mptr->closure.data : 0)); ENUM_PTRS_END private RELOC_PTRS_WITH(transfer_map_reloc_ptrs, gx_transfer_map *mptr) { if (mptr->proc == 0) RELOC_PTR(gx_transfer_map, closure.data); } RELOC_PTRS_END /* Initialize colors with 1, or 3, or 4 paint components. */ /* (These are only used by setcolorspace.) */ void gx_init_paint_1(gs_client_color * pcc, const gs_color_space * pcs) { pcc->paint.values[0] = 0.0; } void gx_init_paint_3(gs_client_color * pcc, const gs_color_space * pcs) { pcc->paint.values[2] = 0.0; pcc->paint.values[1] = 0.0; pcc->paint.values[0] = 0.0; } void gx_init_paint_4(gs_client_color * pcc, const gs_color_space * pcs) { /* DeviceCMYK and CIEBasedDEFG spaces initialize to 0,0,0,1. */ pcc->paint.values[3] = 1.0; gx_init_paint_3(pcc, pcs); } /* Force a value into the range [0.0..1.0]. */ #define FORCE_UNIT(p) (p <= 0.0 ? 0.0 : p >= 1.0 ? 1.0 : p) /* Restrict colors with 1, 3, or 4 components to the range (0,1). */ void gx_restrict01_paint_1(gs_client_color * pcc, const gs_color_space * pcs) { pcc->paint.values[0] = FORCE_UNIT(pcc->paint.values[0]); } void gx_restrict01_paint_3(gs_client_color * pcc, const gs_color_space * pcs) { pcc->paint.values[2] = FORCE_UNIT(pcc->paint.values[2]); pcc->paint.values[1] = FORCE_UNIT(pcc->paint.values[1]); pcc->paint.values[0] = FORCE_UNIT(pcc->paint.values[0]); } void gx_restrict01_paint_4(gs_client_color * pcc, const gs_color_space * pcs) { pcc->paint.values[3] = FORCE_UNIT(pcc->paint.values[3]); gx_restrict01_paint_3(pcc, pcs); } /* Null reference count adjustment procedure. */ void gx_no_adjust_color_count(const gs_client_color * pcc, const gs_color_space * pcs, int delta) { } /* Forward declarations */ void load_transfer_map(P3(gs_state *, gx_transfer_map *, floatp)); /* setgray */ int gs_setgray(gs_state * pgs, floatp gray) { gs_client_color *pcc = pgs->ccolor; if (pgs->in_cachedevice) return_error(gs_error_undefined); cs_adjust_color_count(pgs, -1); gs_cspace_assign(pgs->color_space, gs_current_DeviceGray_space(pgs)); pgs->orig_cspace_index = pgs->orig_base_cspace_index = gs_color_space_index_DeviceGray; pcc->paint.values[0] = FORCE_UNIT(gray); pcc->pattern = 0; /* for GC */ gx_unset_dev_color(pgs); return 0; } /* currentgray */ int gs_currentgray(const gs_state * pgs, float *pg) { const gs_client_color *pcc = pgs->ccolor; const gs_imager_state *const pis = (const gs_imager_state *)pgs; switch (pgs->orig_cspace_index) { case gs_color_space_index_DeviceGray: *pg = pcc->paint.values[0]; break; case gs_color_space_index_DeviceRGB: *pg = frac2float(color_rgb_to_gray( float2frac(pcc->paint.values[0]), float2frac(pcc->paint.values[1]), float2frac(pcc->paint.values[2]), pis)); break; case gs_color_space_index_DeviceCMYK: *pg = frac2float(color_cmyk_to_gray( float2frac(pcc->paint.values[0]), float2frac(pcc->paint.values[1]), float2frac(pcc->paint.values[2]), float2frac(pcc->paint.values[3]), pis)); break; default: /* * Might be another convertible color space, but this is rare, * so we don't care about speed or (to some extent) accuracy. */ { float rgb[3]; int code = gs_currentrgbcolor(pgs, rgb); if (code < 0) return code; *pg = frac2float(color_rgb_to_gray( float2frac(rgb[0]), float2frac(rgb[1]), float2frac(rgb[2]), pis)); } } return 0; } /* setrgbcolor */ int gs_setrgbcolor(gs_state * pgs, floatp r, floatp g, floatp b) { gs_client_color *pcc = pgs->ccolor; if (pgs->in_cachedevice) return_error(gs_error_undefined); cs_adjust_color_count(pgs, -1); gs_cspace_assign(pgs->color_space, gs_current_DeviceRGB_space(pgs)); pgs->orig_cspace_index = pgs->orig_base_cspace_index = gs_color_space_index_DeviceRGB; pcc->paint.values[0] = FORCE_UNIT(r); pcc->paint.values[1] = FORCE_UNIT(g); pcc->paint.values[2] = FORCE_UNIT(b); pcc->pattern = 0; /* for GC */ gx_unset_dev_color(pgs); return 0; } /* currentrgbcolor */ int gs_currentrgbcolor(const gs_state * pgs, float pr3[3]) { const gs_client_color *pcc = pgs->ccolor; const gs_color_space *pcs = pgs->color_space; const gs_color_space *pbcs = pcs; const gs_imager_state *const pis = (const gs_imager_state *)pgs; gs_color_space_index csi = pgs->orig_cspace_index; frac fcc[4]; gs_client_color cc; int code; sw:switch (csi) { case gs_color_space_index_DeviceGray: pr3[0] = pr3[1] = pr3[2] = pcc->paint.values[0]; return 0; case gs_color_space_index_DeviceRGB: pr3[0] = pcc->paint.values[0]; pr3[1] = pcc->paint.values[1]; pr3[2] = pcc->paint.values[2]; return 0; case gs_color_space_index_DeviceCMYK: color_cmyk_to_rgb( float2frac(pcc->paint.values[0]), float2frac(pcc->paint.values[1]), float2frac(pcc->paint.values[2]), float2frac(pcc->paint.values[3]), pis, fcc); pr3[0] = frac2float(fcc[0]); pr3[1] = frac2float(fcc[1]); pr3[2] = frac2float(fcc[2]); return 0; case gs_color_space_index_CIEICC: icc_cs:if (gs_cspace_base_space(pbcs) != NULL) goto bcs; break; case gs_color_space_index_DeviceN: case gs_color_space_index_Separation: ds:if (cs_concrete_space(pbcs, pis) == pbcs) break; /* not using alternative space */ /* (falls through) */ case gs_color_space_index_Indexed: bcs:pbcs = gs_cspace_base_space(pbcs); switch (pbcs->type->index) { case gs_color_space_index_DeviceN: case gs_color_space_index_Separation: goto ds; case gs_color_space_index_CIEICC: goto icc_cs; default: /* outer switch will catch undefined cases */ break; } code = cs_concretize_color(pcc, pcs, fcc, pis); if (code < 0) return code; cc.paint.values[0] = frac2float(fcc[0]); cc.paint.values[1] = frac2float(fcc[1]); cc.paint.values[2] = frac2float(fcc[2]); cc.paint.values[3] = frac2float(fcc[3]); pcc = &cc; pcs = pbcs; csi = pgs->orig_base_cspace_index; goto sw; default: break; } pr3[0] = pr3[1] = pr3[2] = 0.0; return 0; } /* setnullcolor */ int gs_setnullcolor(gs_state * pgs) { if (pgs->in_cachedevice) return_error(gs_error_undefined); gs_setgray(pgs, 0.0); /* set color space to something harmless */ color_set_null(pgs->dev_color); return 0; } /* settransfer */ /* Remap=0 is used by the interpreter. */ int gs_settransfer(gs_state * pgs, gs_mapping_proc tproc) { return gs_settransfer_remap(pgs, tproc, true); } int gs_settransfer_remap(gs_state * pgs, gs_mapping_proc tproc, bool remap) { gx_transfer_colored *ptran = &pgs->set_transfer.colored; /* * We can safely decrement the reference counts * of the non-gray transfer maps, because * if any of them get freed, the rc_unshare can't fail. */ rc_decrement(ptran->red, "gs_settransfer"); rc_decrement(ptran->green, "gs_settransfer"); rc_decrement(ptran->blue, "gs_settransfer"); rc_unshare_struct(ptran->gray, gx_transfer_map, &st_transfer_map, pgs->memory, goto fail, "gs_settransfer"); ptran->gray->proc = tproc; ptran->gray->id = gs_next_ids(1); ptran->red = ptran->gray; ptran->green = ptran->gray; ptran->blue = ptran->gray; ptran->gray->rc.ref_count += 3; if (remap) { load_transfer_map(pgs, ptran->gray, 0.0); gx_set_effective_transfer(pgs); gx_unset_dev_color(pgs); } return 0; fail: rc_increment(ptran->red); rc_increment(ptran->green); rc_increment(ptran->blue); return_error(gs_error_VMerror); } /* currenttransfer */ gs_mapping_proc gs_currenttransfer(const gs_state * pgs) { return pgs->set_transfer.colored.gray->proc; } /* ------ Non-operator routines ------ */ /* Set device color = 1 for writing into the character cache. */ void gx_set_device_color_1(gs_state * pgs) { gx_device_color *pdc = pgs->dev_color; gs_client_color *pcc = pgs->ccolor; cs_adjust_color_count(pgs, -1); pcc->paint.values[0] = 0.0; pcc->pattern = 0; /* for GC */ color_set_pure(pdc, 1); pgs->log_op = lop_default; gs_cspace_assign(pgs->color_space, gs_cspace_DeviceGray((const gs_imager_state *)pgs)); pgs->orig_cspace_index = pgs->orig_base_cspace_index = gs_color_space_index_DeviceGray; } /* ------ Internal routines ------ */ /* * Load one cached transfer map. We export this for gscolor1.c. * Note that we must deal with both old (proc) and new (closure) maps. */ private float transfer_use_proc(floatp value, const gx_transfer_map * pmap, const void *ignore_proc_data) { return (*pmap->proc) (value, pmap); } void load_transfer_map(gs_state * pgs, gx_transfer_map * pmap, floatp min_value) { gs_mapping_closure_proc_t proc; const void *proc_data; frac *values = pmap->values; frac fmin = float2frac(min_value); int i; if (pmap->proc == 0) /* use closure */ proc = pmap->closure.proc, proc_data = pmap->closure.data; else /* use proc */ proc = transfer_use_proc, proc_data = 0 /* not used */; for (i = 0; i < transfer_map_size; i++) { float fval = (*proc) ((float)i / (transfer_map_size - 1), pmap, proc_data); values[i] = (fval < min_value ? fmin : fval >= 1.0 ? frac_1 : float2frac(fval)); } }