diff options
Diffstat (limited to 'pcl/pcpalet.c')
-rw-r--r-- | pcl/pcpalet.c | 1144 |
1 files changed, 1144 insertions, 0 deletions
diff --git a/pcl/pcpalet.c b/pcl/pcpalet.c new file mode 100644 index 000000000..02d2e9ccf --- /dev/null +++ b/pcl/pcpalet.c @@ -0,0 +1,1144 @@ +/* Portions Copyright (C) 2001 artofcode LLC. + Portions Copyright (C) 1996, 2001 Artifex Software Inc. + Portions Copyright (C) 1988, 2000 Aladdin Enterprises. + This software is based in part on the work of the Independent JPEG Group. + All Rights Reserved. + + 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., 101 Lucas Valley Road #110, + San Rafael, CA 94903, (415)492-9861, for further information. */ +/*$Id$ */ + +/* pcpalet.c - PCL 5c palette object implementation */ +#include "gx.h" +#include "pldict.h" +#include "pcdraw.h" +#include "pcpage.h" +#include "pcursor.h" +#include "pcpalet.h" +#include "pcfrgrnd.h" +#include "pccrd.h" +#include "gsparam.h" +#include "gsdevice.h" +#include "gxcmap.h" +#include "gxdcconv.h" +#include "gzstate.h" + +/* GC routines */ +private_st_palette_t(); +private_st_pstack_entry_t(); + +/* + * Free a PCL palette. + */ +static void +free_palette( + gs_memory_t * pmem, + void * pvpalet, + client_name_t cname +) +{ + pcl_palette_t * ppalet = (pcl_palette_t *)pvpalet; + + if (ppalet->pindexed != 0) + pcl_cs_indexed_release(ppalet->pindexed); + if (ppalet->pcrd != 0) + pcl_crd_release(ppalet->pcrd); + if (ppalet->pht != 0) + pcl_ht_release(ppalet->pht); + gs_free_object(pmem, pvpalet, cname); +} + +void +pcl_free_default_objects( + gs_memory_t * mem, + pcl_state_t * pcs + ) +{ + pcl_palette_t * ppalette = (pcl_palette_t *)pcs->pdflt_palette; + + rc_decrement(pcs->pdflt_cs_indexed, "free_default_palette(pdflt_cs_indexed)"); + + if (ppalette != 0) { + + rc_decrement(ppalette->pindexed, "free_default_palette cs indexed released"); + if (ppalette->pht) + rc_decrement(ppalette->pht, "free_default_palette ht released"); + if (ppalette->pcrd) + rc_decrement(ppalette->pcrd, "free_default_palette pcl_crd_release"); + gs_free_object(mem, ppalette, "free_default_palette ppalette free"); + pcs->pdflt_palette = 0; + } + rc_decrement(pcs->pdflt_ht, "free_default_palette pdflt_ht release"); + rc_decrement(pcs->pdflt_ht, "free_default_palette pdflt_ht release"); + rc_decrement(pcs->pdflt_ht, "free_default_palette pdflt_ht release"); + rc_decrement(pcs->pdflt_ht, "free_default_palette pdflt_ht release"); + + if(pcs->pcl_default_crd) + free_crd(mem, pcs->pcl_default_crd, "free_default_palette pcl_default_crd free"); +} +/* + * Free procedure for palettes to be used by the dictionary which implements + * the palette store. + */ + static void +dict_free_palette( + gs_memory_t * pmem, + void * pvpalet, + client_name_t cname +) +{ + pcl_palette_t * ppalet = (pcl_palette_t *)pvpalet; + + rc_decrement(ppalet, cname); +} + +/* + * Allocat a PCL palette object. + * + * Returns 0 on success, < 0 in the event of an error. + */ + static int +alloc_palette( + pcl_state_t * pcs, + pcl_palette_t ** pppalet, + gs_memory_t * pmem +) +{ + pcl_palette_t * ppalet = 0; + rc_alloc_struct_1( ppalet, + pcl_palette_t, + &st_palette_t, + pmem, + return e_Memory, + "allocate pcl palette object" + ); + ppalet->rc.free = free_palette; + ppalet->id = pcl_next_id(pcs); + ppalet->pindexed = 0; + ppalet->pcrd = 0; + ppalet->pht = 0; + *pppalet = ppalet; + return 0; +} + +/* + * Create a unique copy of a PCL palette. + * + * The unsharing of PCL palettes is a bit involved due to the nature of the + * palette store. In normal handling, the current PCL state does not maintain + * a reference to the current palette. Rather, it keeps the current palette id., + * and the definition of this id. in the dictionary that implements the palette + * store is the only reference to the palette. + * + * The structure of XL dictionaries is (properly) opaque, so there is no direct + * access is available to pointer to a palette. Furthermore, the dictionary + * get, put, and undef procedures do not recognize object structure, hence they + * provide no native support for reference counted objects. + * + * To work around these problems, a separate pointer to a palette is maintained + * in the PCL, though one that does not normally counted as a reference. The + * make-unique operation will modify this pointer, and immediately redefine + * the current palette id. + * + * To simplify the remainder of this code, unshared palettes are given new + * identifiers, even if the unshare operation does nothing. This operation is + * specifically overridden when modifying pen widths, since the latter are never + * cached. + * + * Unlike the "unshare" functions for other objects, this function will create + * the PCL palette object if it does not already exist. + * + * Returns 0 on success, < 0 in the event of an error. + */ + static int +unshare_palette( + pcl_state_t * pcs +) +{ + pcl_palette_t * ppalet = pcs->ppalet; + pcl_palette_t * pnew = 0; + int code = 0; + pcl_id_t key; + + /* check if there is anything to do */ + if ((ppalet != 0) && (ppalet->rc.ref_count == 1)) { + ppalet->id = pcl_next_id(pcs); + return 0; + } + + /* allocate a new palette */ + if ((code = alloc_palette(pcs, &pnew, pcs->memory)) < 0) + return code; + if (ppalet != 0) { + pcl_cs_indexed_init_from(pnew->pindexed, ppalet->pindexed); + pcl_crd_init_from(pnew->pcrd, ppalet->pcrd); + pcl_ht_init_from(pnew->pht, ppalet->pht); + } + + /* redefine the current palette id. */ + pcs->ppalet = pnew; + id_set_value(key, pcs->sel_palette_id); + code = pl_dict_put(&pcs->palette_store, id_key(key), 2, pnew); + return (code == -1 ? e_Memory : 0); +} + +/* + * Build a default palette, and define it to be the currently selected + * palette. + */ + static int +build_default_palette( + pcl_state_t * pcs +) +{ + pcl_id_t key; + gs_memory_t * pmem = pcs->memory; + pcl_palette_t * ppalet = 0; + int code = 0; + + if (pcs->pdflt_palette == 0) { + code = alloc_palette(pcs, &ppalet, pmem); + if (code == 0) + code = pcl_cs_indexed_build_default_cspace( pcs, + &(ppalet->pindexed), + pmem + ); + if ((code == 0) && (pcs->pcl_default_crd == 0)) + code = pcl_crd_build_default_crd(pcs); + if (code == 0) + pcl_crd_init_from(ppalet->pcrd, pcs->pcl_default_crd); + if (code == 0) + code = pcl_ht_build_default_ht(pcs, &(ppalet->pht), pmem); + if (code < 0) { + if (ppalet != 0) + free_palette(pmem, ppalet, "build default palette"); + return code; + } + pcl_palette_init_from(pcs->pdflt_palette, ppalet); + } else + pcl_palette_init_from(ppalet, pcs->pdflt_palette); + + + /* NB: definitions do NOT record a referece */ + id_set_value(key, pcs->sel_palette_id); + code = pl_dict_put(&pcs->palette_store, id_key(key), 2, ppalet); + if (code < 0) + return e_Memory; + rc_increment(ppalet); + /* the graphic state pointer does not (yet) amount to a reference */ + pcs->ppalet = ppalet; + return 0; +} + +/* + * Clear the palette stack. + */ + void +clear_palette_stack( + pcl_state_t * pcs, + gs_memory_t * pmem +) +{ + pstack_entry_t * pentry = pcs->palette_stack; + + while (pentry != 0) { + pstack_entry_t * pnext = pentry->pnext; + + pcl_palette_release(pentry->ppalet); + gs_free_object(pmem, pentry, "clear palette stack"); + pentry = pnext; + } + pcs->palette_stack = 0; +} + +/* + * ESC * p # P + * + * Push a copy of the current palette onto the palette stack, or pop one off. + * Note the need to redefine the select palette id. in the event that a palette + * is popped. + */ + static int +push_pop_palette( + pcl_args_t * pargs, + pcl_state_t * pcs +) +{ + int action = uint_arg(pargs); + + if ( pcs->personality == pcl5e || pcs->raster_state.graphics_mode ) + return 0; + + if (action == 0) { + pstack_entry_t * pentry; + int code; + + /* if there is not yet a palette, build the default */ + if ((pcs->ppalet == 0) && ((code = build_default_palette(pcs)) < 0)) + return code; + + pentry = gs_alloc_struct( pcs->memory, + pstack_entry_t, + &st_pstack_entry_t, + "push pcl palette" + ); + if (pentry == 0) + return e_Memory; + + pcl_palette_init_from(pentry->ppalet, pcs->ppalet); + pentry->pnext = pcs->palette_stack; + pcs->palette_stack = pentry; + + return 0; + + } else if (action == 1) { + pstack_entry_t * pentry = pcs->palette_stack; + int code = 0; + + if (pentry != 0) { + pcl_id_t key; + + pcs->palette_stack = pentry->pnext; + + /* NB: just set - pcs->ppalet is not a reference */ + pcs->ppalet = pentry->ppalet; + + /* the dictionary gets the stack reference on the palette */ + id_set_value(key, pcs->sel_palette_id); + code = pl_dict_put(&pcs->palette_store, id_key(key), 2, pentry->ppalet); + gs_free_object(pcs->memory, pentry, "pop pcl palette"); + } + + return (code < 0 ? e_Memory : 0); + + } else + return 0; +} + + +/* + * Set the number of entries in a color palette. This is needed only for the + * GL/2 NP command; PCL sets the number of entries in a palette via the + * configure image data command, which creates a new indexed color space. + * + * Returns 0 on success, < 0 in the event of an error. + */ + int +pcl_palette_NP( + pcl_state_t * pcs, + int num_entries +) +{ + int code = unshare_palette(pcs); + + /* if the default color space must be built, it is fixed, so don't bother */ + if (pcs->ppalet->pindexed == 0) + return code; + + if (code == 0) + code = pcl_cs_indexed_set_num_entries( &(pcs->ppalet->pindexed), + num_entries, + true + ); + /* shrinking a palette may make it gray, growing may make it color */ + if (code == 0) + code = pcl_ht_remap_render_method(pcs, + &(pcs->ppalet->pht), + pcl_ht_is_all_gray_palette(pcs)); + return code; +} + +/* + * Set the render method for the palette. + * + * Returns 0 on success, < 0 in the event of an error. + * + * The value of the render method is not checked against the existing value, + * since the fact that two are the same is neither a necessary nor sufficient + * condition for determining that the render algorithm has changed: it is + * possible that the rendering remap array (see pcht.c) has been modified. + */ + int +pcl_palette_set_render_method( + pcl_state_t * pcs, + uint render_method +) +{ + int code = unshare_palette(pcs); + + if ((code == 0) && (pcs->ppalet->pht == 0)) + code = pcl_ht_build_default_ht(pcs, &(pcs->ppalet->pht), pcs->memory); + if (code >= 0) + code = pcl_ht_set_render_method(pcs, &(pcs->ppalet->pht), render_method); + if (code >= 0) + pcs->render_mode = render_method; + return code; +} + +/* + * Set gamma correction information for a palette. + * + * Gamma correction and the color lookup table for device specific color spaces + * perform the same function, but are of different origins. Hence, while a + * configure image data command will discard all color lookup tables, it inherits + * the gamma configuration parameter from the existing palette. In addition, + * while there is an "unset" command for color lookup tables, there is no such + * command for gamma correction: to override the existing gamma correction, + * either specify a new one or download a color correction table for a device + * specific color space. + * + * Returns 0 on success, < 0 in the event of an error. + */ + int +pcl_palette_set_gamma( + pcl_state_t * pcs, + float gamma +) +{ + int code = unshare_palette(pcs); + + if ((code == 0) && (pcs->ppalet->pht == 0)) + code = pcl_ht_build_default_ht(pcs, &(pcs->ppalet->pht), pcs->memory); + if (code == 0) + code = pcl_ht_set_gamma(&(pcs->ppalet->pht), gamma); + return code; +} + +/* + * Set color lookup table information for a palette. + * + * Lookup tables for device-specific and device-independent color spaces are + * implemented in different ways. The former are implemented via transfer + * functions, and thus affect the halftone component of the current palette. + * The latter are implemented in the device-independent color spaces themselves, + * and thus affect the color spaces component of the palette. + * + * An anachronism of the PCL is that, while color lookup tables may be set + * individually for different color spaces, they only be cleared all at once. + * This is accomplished by calling this routine with a null lookup table pointer. + * + * Returns 0 on success, < 0 in the event of an error. + */ + int +pcl_palette_set_lookup_tbl( + pcl_state_t * pcs, + pcl_lookup_tbl_t * plktbl +) +{ + int code = unshare_palette(pcs); + pcl_cspace_type_t lktype; + + if ((code == 0) && (pcs->ppalet->pindexed == 0)) + code = pcl_cs_indexed_build_default_cspace( pcs, + &(pcs->ppalet->pindexed), + pcs->memory + ); + if ((code == 0) && (pcs->ppalet->pht == 0)) + code = pcl_ht_build_default_ht(pcs, &(pcs->ppalet->pht), pcs->memory); + if (code < 0) + return code; + + /* all look tables are cleared simultaneously */ + if (plktbl != 0) + lktype = pcl_lookup_tbl_get_cspace(plktbl); + if ((plktbl == 0) || (lktype <= pcl_cspace_CMY)) + code = pcl_ht_set_lookup_tbl(&(pcs->ppalet->pht), plktbl); + if ( (code == 0) && ((plktbl == 0) || (lktype >= pcl_cspace_Colorimetric)) ) + code = pcl_cs_indexed_update_lookup_tbl(&(pcs->ppalet->pindexed), plktbl); + return code; +} + +/* + * Set an entry in a color palette. + * + * Returns 0 on success, < 0 in the event of an error. The returned code will + * normally be ignored. + */ + int +pcl_palette_set_color( + pcl_state_t * pcs, + int indx, + const float comps[3] +) +{ + int code = unshare_palette(pcs); + bool was_gray; + bool now_gray; + + /* if the default color space must be built, it is fixed, so don't bother */ + if (pcs->ppalet->pindexed == 0) + return code; + + if (code == 0) + code = pcl_cs_indexed_set_palette_entry( &(pcs->ppalet->pindexed), + indx, + comps + ); + + if ( pcs->monochrome_mode == 0 ) { + was_gray = pcs->ppalet->pht->is_gray_render_method; + + now_gray = ((pcs->ppalet->pindexed->palette.data[indx*3 + 0] == + pcs->ppalet->pindexed->palette.data[indx*3 + 1]) && + (pcs->ppalet->pindexed->palette.data[indx*3 + 1] == + pcs->ppalet->pindexed->palette.data[indx*3 + 2]) ); + + if ( !was_gray && now_gray ) { + /* change one entry from color to gray, + * check entire palette for grey + */ + code = pcl_ht_remap_render_method(pcs, + &(pcs->ppalet->pht), + pcl_ht_is_all_gray_palette(pcs)); + } + else if ( was_gray && !now_gray ) { + /* one color entry in gray palette makes it color + */ + code = pcl_ht_remap_render_method(pcs, + &(pcs->ppalet->pht), + false); + } + } + return code; +} + +/* + * Set a palette entry to its default color. + * + * Returns 0 on success, < 0 in the event of an error. The returned code will + * normally be ignored. + */ + int +pcl_palette_set_default_color( + pcl_state_t * pcs, + int indx +) +{ + int code = unshare_palette(pcs); + + /* if the default color space must be built, it is fixed, so don't bother */ + if (pcs->ppalet->pindexed == 0) + return code; + + if (code == 0) + code = pcl_cs_indexed_set_default_palette_entry( &(pcs->ppalet->pindexed), + indx + ); + if (code == 0) + code = pcl_ht_remap_render_method(pcs, + &(pcs->ppalet->pht), + pcl_ht_is_all_gray_palette(pcs)); + return code; +} + +/* + * Set a pen width. Note that this does NOT change the palette id. This + * procedure can only be called from GL/2, hence the procedure name. + * + * Returns 0 on success, < 0 in the even of an error; + */ + int +pcl_palette_PW( + pcl_state_t * pcs, + int pen, + floatp width +) +{ + int code = 0; + pcl_gsid_t palette_id; + pcl_palette_t * ppalet = pcs->ppalet; + + if (ppalet != 0) { + pcl_cs_indexed_t * pindexed = ppalet->pindexed; + + if ( (pindexed != 0) && + (pen >= 0) && + (pen < pcl_cs_indexed_get_num_entries(pindexed)) && + (width == pcl_cs_indexed_get_pen_widths(pindexed)[pen]) ) + return 0; + + palette_id = ppalet->id; + if ((code = unshare_palette(pcs)) < 0) + return code; + ppalet = pcs->ppalet; + ppalet->id = palette_id; + + } else { + if ((code = unshare_palette(pcs)) < 0) + return code; + ppalet = pcs->ppalet; + } + + return pcl_cs_indexed_set_pen_width(&(ppalet->pindexed), pen, width); +} + +/* + * Set the user-defined dither matrix. + * + * Returns 0 on success, < 0 in the event of an error. + */ + int +pcl_palette_set_udither( + pcl_state_t * pcs, + pcl_udither_t * pdither +) +{ + int code = unshare_palette(pcs); + + if ((code == 0) && (pcs->ppalet->pht == 0)) + code = pcl_ht_build_default_ht(pcs, &(pcs->ppalet->pht), pcs->memory); + if (code == 0) + code = pcl_ht_set_udither(&(pcs->ppalet->pht), pdither); + return code; +} + +/* + * Overwrite the current palette with new a new image data configuration. + * This will rebuild the indexed color space, and discard any currently + * installed color lookup tables. + * + * Tf the operand "fixed" is true, this procedure is being called as part of + * a "simple color mode" command, and the resulting color palette will have + * fixed entries. + * + * The boolean operand gl2 indicates if this call is being made as the result + * of an IN command in GL/2. If so, the default set of entries in the color + * palette is modified. + * + * Returns 0 on success, < 0 in the event of an error. + */ + int +pcl_palette_set_cid( + pcl_state_t * pcs, + pcl_cid_data_t * pcid, + bool fixed, + bool gl2 +) +{ + int code = unshare_palette(pcs); + pcl_palette_t * ppalet = pcs->ppalet; + pcl_cspace_type_t cstype_new = pcl_cid_get_cspace(pcid); + pcl_cspace_type_t cstype_old; + + if (code < 0) + return code; + + /* record the old color space type, if it is present */ + if (ppalet->pindexed != 0) + cstype_old = (pcl_cspace_type_t)ppalet->pindexed->cid.cspace; + else + cstype_old = cstype_new; + + /* pcl_cspace_bnuild_indexed_cspace will release the old space */ + code = pcl_cs_indexed_build_cspace( pcs, + &(ppalet->pindexed), + pcid, + fixed, + gl2, + pcs->memory + ); + if (code == 0) { + bool is_gray = false; + /* direct raster is always color */ + if ( pcl_cid_get_encoding(pcid) <= 1 ) { + /* indexed used palette which maybe gray or color */ + is_gray = pcl_ht_is_all_gray_palette(pcs); + } + code = pcl_ht_remap_render_method(pcs, + &(pcs->ppalet->pht), + is_gray); + } + + /* if a halftone exist, inform it of the update and discard lookup tables */ + if ((code == 0) && (ppalet->pht != 0)) { + code = pcl_ht_update_cspace(pcs, &(ppalet->pht), cstype_old, cstype_new); + if (code == 0) + code = pcl_ht_set_lookup_tbl(&(ppalet->pht), NULL); + } + + return code; +} + +/* + * Set the view illuminant for a palette. + * + * Returns 0 on success, < 0 in the event of an error. + */ + int +pcl_palette_set_view_illuminant( + pcl_state_t * pcs, + const gs_vector3 * pwht_pt +) +{ + int code = unshare_palette(pcs); + + if ((code == 0) && (pcs->ppalet->pcrd == 0)) { + if ((code = pcl_crd_build_default_crd(pcs)) == 0) + pcl_crd_init_from(pcs->ppalet->pcrd, pcs->pcl_default_crd); + } + if (code == 0) + code = pcl_crd_set_view_illuminant(pcs, &(pcs->ppalet->pcrd), pwht_pt); + return code; +} + +/* + * Check that all parts of a PCL palette have been built. If not, build the + * necessary default objects. + * + * This procedure should never be required, as the reset operation should build + * a default palette, but it is kept in case that routine does not succeed. + * + * Returns 0 on success, < 0 in the event of an error. + */ + int +pcl_palette_check_complete( + pcl_state_t * pcs +) +{ + pcl_palette_t * ppalet = pcs->ppalet; + int code = 0; + + if ( (ppalet != 0) && + (ppalet->pindexed != 0) && + (ppalet->pcrd != 0) && + (ppalet->pht != 0) ) + return 0; + + if ((code = unshare_palette(pcs)) < 0) + return code; + ppalet = pcs->ppalet; + if (ppalet->pindexed == 0) + code = pcl_cs_indexed_build_default_cspace( pcs, + &(ppalet->pindexed), + pcs->memory + ); + if ((code == 0) && (ppalet->pcrd == 0)) { + if ((code = pcl_crd_build_default_crd(pcs)) == 0) + pcl_crd_init_from(pcs->ppalet->pcrd, pcs->pcl_default_crd); + } + if ((code == 0) && (ppalet->pht == 0)) + code = pcl_ht_build_default_ht(pcs, &(ppalet->pht), pcs->memory); + return code; +} + + +/* + * ESC & p # S + * + * Change the select palette id. Note that, since the pointer to the palette in + * the pcl state does not count as a reference, no reference counts are adjusted. + */ + static int +set_sel_palette_id( + pcl_args_t * pargs, + pcl_state_t * pcs +) +{ + uint id = uint_arg(pargs); + pcl_id_t key; + + if ( pcs->personality == pcl5e || pcs->raster_state.graphics_mode ) + return 0; + + /* ignore attempts to select non-existent palettes */ + id_set_value(key, id); + if ( pl_dict_lookup( &pcs->palette_store, + id_key(key), + 2, + (void **)&(pcs->ppalet), + false, + NULL + ) ) + pcs->sel_palette_id = id; + return 0; +} + +/* + * ESC & p # I + * + * Set the palette control id. + */ + static int +set_ctrl_palette_id( + pcl_args_t * pargs, + pcl_state_t * pcs +) +{ + if ( pcs->personality == pcl5e || pcs->raster_state.graphics_mode) + return 0; + + pcs->ctrl_palette_id = uint_arg(pargs); + return 0; +} + +/* + * Clear the palette store. This will delete the current palette, but will + * NOT build the default palette in its place. + * + * If the current palette id. already has the default palette assigned, don't + * bother removing it. This is helpful when working with memory leak detection + * tools. + */ + static void +clear_palette_store( + pcl_state_t * pcs +) +{ + pl_dict_enum_t denum; + void * pvalue; + gs_const_string plkey; + int sel_id = pcs->sel_palette_id; + + pl_dict_enum_begin(&pcs->palette_store, &denum); + while (pl_dict_enum_next(&denum, &plkey, &pvalue)) { + int id = (((int)plkey.data[0]) << 8) + plkey.data[1]; + + if (id == sel_id) { + if (pvalue != pcs->pdflt_palette) + build_default_palette(pcs); /* will redefine sel_id */ + } else + pl_dict_undef(&pcs->palette_store, plkey.data, plkey.size); + } +} + +/* + * ESC & p # C + * + * Palette control + */ + static int +palette_control( + pcl_args_t * pargs, + pcl_state_t * pcs +) +{ + uint action = uint_arg(pargs); + + if ( pcs->personality == pcl5e || pcs->raster_state.graphics_mode ) + return 0; + + switch (action) { + + case 0: + clear_palette_store(pcs); + break; + + case 1: + clear_palette_stack(pcs, pcs->memory); + break; + + case 2: + if (pcs->ctrl_palette_id == pcs->sel_palette_id) { + if ((pcs->ppalet == 0) || (pcs->ppalet != pcs->pdflt_palette)) + build_default_palette(pcs); + } else { + pcl_id_t key; + + id_set_value(key, pcs->ctrl_palette_id); + pl_dict_undef(&pcs->palette_store, id_key(key), 2); + } + break; + + case 6: + if (pcs->ctrl_palette_id != pcs->sel_palette_id) { + pcl_id_t key; + int code = 0; + + /* NB: definitions don't incremente refernece counts */ + id_set_value(key, pcs->ctrl_palette_id); + code = pl_dict_put(&pcs->palette_store, id_key(key), 2, pcs->ppalet); + if (code < 0) + return code; + rc_increment(pcs->ppalet); + + } + break; + + default: + return e_Range; + } + + return 0; +} + +/* + * ESC * t # J + * + * Set render method. The rendering method is specifically part of the PCL + * halftone structure, but the setting command is included here because it + * must run via the palette mechanism. + */ + static int +set_render_algorithm( + pcl_args_t * pargs, + pcl_state_t * pcs +) +{ + if ( pcs->personality == pcl5e || pcs->raster_state.graphics_mode ) + return 0; + + return pcl_palette_set_render_method(pcs, uint_arg(pargs)); +} + +static bool swapped_device_color_procs = false; +static gx_cm_color_map_procs device_cm_procs; +/* needs type */ +static dev_proc_get_color_mapping_procs(*saved_get_color_map_proc); + +static void +pcl_gray_cs_to_cm(gx_device * dev, frac gray, frac out[]) +{ + /* just pass it along */ + device_cm_procs.map_gray(dev, gray, out ); +} + +static void +pcl_rgb_cs_to_cm(gx_device * dev, const gs_imager_state * pis, frac r, frac g, frac b, frac out[]) +{ + frac gray = color_rgb_to_gray(r, g, b, NULL); + device_cm_procs.map_rgb(dev, pis, gray, gray, gray, out); +} + +static void +pcl_cmyk_cs_to_cm(gx_device * dev, frac c, frac m, frac y, frac k, frac out[]) +{ + frac gray = color_cmyk_to_gray(c, m, y, k, NULL); + device_cm_procs.map_cmyk(dev, gray, gray, gray, gray, out); +} + +static const gx_cm_color_map_procs pcl_mono_procs = { + pcl_gray_cs_to_cm, pcl_rgb_cs_to_cm, pcl_cmyk_cs_to_cm +}; + +static const gx_cm_color_map_procs * +pcl_mono_color_mapping_procs(const gx_device * dev) +{ + return &pcl_mono_procs; +} + + +/* set monochrome page device parameter. NB needs testing. We don't + currently have a device that does what we need with + ProcessColorModel. We assume non color devices will simply ignore + the parameter. */ +static int +pcl_update_mono(pcl_state_t *pcs) +{ + gx_device *dev = gs_currentdevice(pcs->pgs); + const gx_cm_color_map_procs *cm_procs = dev_proc(dev, get_color_mapping_procs)(dev); + if (pcs->monochrome_mode) { + if (swapped_device_color_procs == false) { + device_cm_procs = *cm_procs; + saved_get_color_map_proc = dev->procs.get_color_mapping_procs; + dev->procs.get_color_mapping_procs = pcl_mono_color_mapping_procs; + swapped_device_color_procs = true; + } + } else { + if (swapped_device_color_procs == true) { + dev->procs.get_color_mapping_procs = saved_get_color_map_proc; + swapped_device_color_procs = false; + } + } + gx_unset_dev_color(pcs->pgs); + return 0; +} + +/* + * ESC & b # M + * + * Set monochrome or normal print mode. + * Note ForceMono=1 is similar to monochrome mode locked on. + * + * The monochrome print mode command is ignored if the page is dirty. + * + */ + static int +set_print_mode( + pcl_args_t * pargs, + pcl_state_t * pcs +) +{ + uint mode = uint_arg(pargs); + + if ( pcs->personality == pcl5e || pcs->raster_state.graphics_mode ) + return 0; + + if (mode > 1) + return 0; + + if (pcs->page_marked) + return 0; + + if (mode == 1) + pcs->monochrome_mode = true; + else + pcs->monochrome_mode = false; + + return pcl_update_mono(pcs); +} + +/* + * Initialization routine for palettes. + */ + static int +palette_do_registration( + pcl_parser_state_t *pcl_parser_state, + gs_memory_t * pmem +) +{ + DEFINE_CLASS('*') + { + 'p', 'P', + PCL_COMMAND( "Push/Pop Palette", + push_pop_palette, + pca_neg_ok | pca_big_ignore | pca_in_rtl + ) + }, + { + 't', 'J', + PCL_COMMAND( "Render Algorithm", + set_render_algorithm, + pca_neg_ok | pca_big_ignore | pca_in_rtl + ) + }, + END_CLASS + + DEFINE_CLASS('&') + { + 'b', 'M', + PCL_COMMAND( "Monochrome Printing", + set_print_mode, + pca_neg_ok | pca_big_ignore | pca_in_rtl + ) + }, + { + 'p', 'S', + PCL_COMMAND( "Select Palette", + set_sel_palette_id, + pca_neg_ok | pca_big_ignore | pca_in_rtl + ) + }, + { + 'p', 'I', + PCL_COMMAND( "Palette Control ID", + set_ctrl_palette_id, + pca_neg_ok | pca_big_ignore | pca_in_rtl + ) + }, + { + 'p', 'C', + PCL_COMMAND( "Palette Control", + palette_control, + pca_neg_ok | pca_big_ignore | pca_in_rtl + ) + }, + END_CLASS + return 0; +} + +/* + * Reset routine for palettes. + */ + static void +palette_do_reset( + pcl_state_t * pcs, + pcl_reset_type_t type +) +{ + static const uint mask = ( pcl_reset_initial + | pcl_reset_cold + | pcl_reset_printer + | pcl_reset_overlay + | pcl_reset_permanent); + + + + if ((type & mask) == 0) + return; + + /* for initial reset, set up the palette store */ + if ((type & pcl_reset_initial) != 0) { + pl_dict_init(&pcs->palette_store, pcs->memory, dict_free_palette); + pcs->ppalet = 0; + pcs->pfrgrnd = 0; + + /* set up the built-in render methods and dithers matrices */ + pcl_ht_init_render_methods(pcs, pcs->memory); + + + } else if ((type & (pcl_reset_cold | pcl_reset_printer | pcl_reset_permanent)) != 0) { + pcs->monochrome_mode = 0; + pcl_update_mono(pcs); + /* clear the palette stack and store */ + clear_palette_stack(pcs, pcs->memory); + clear_palette_store(pcs); + } + if ( type & pcl_reset_permanent ) { + pl_dict_release(&pcs->palette_store); + if (pcs->ppalet != pcs->pdflt_palette) { + /* stefan foo: free or decrement reference counts? */ + gs_free_object(pcs->memory, pcs->ppalet->pindexed, "palette cs indexed released permanent reset"); + gs_free_object(pcs->memory, pcs->ppalet->pht, "palette ht released permanent reset"); + gs_free_object(pcs->memory, pcs->ppalet->pcrd, "palette ht released permanent reset"); + gs_free_object(pcs->memory, pcs->ppalet, "palette released permanent reset"); + } + } + /* select and control palette ID's must be set back to 0 */ + pcs->sel_palette_id = 0; + pcs->ctrl_palette_id = 0; + + if ( !(type & pcl_reset_permanent) ) { + (void)build_default_palette(pcs); + (void)pcl_frgrnd_set_default_foreground(pcs); + } +} + +/* + * The copy function for palettes. + * + * This procedure implements the two most unusual features of the palette + * object: + * + * The palette pointer becomes a reference on the palette when the + * state is saved. + * + * When the state is restored, the palette in the saved state is used + * to re-define the select palette id. in the palette store. Palettes are + * the only PCL resource object with this behavior. + * + * It is not clear HP intended palette to have this behavior. More likely, + * it was a by-product of the way in which they implemented the save/restore + * for foregrounds. + * + * Note that, in the restore case, the dictionary definition absorbs the + * reference to the palette held by the saved state, so there is no need to + * explicitly release this reference (an example of the asymmetric define/ + * undefine properties of the pl_dict_t object). + */ + static int +palette_do_copy( + pcl_state_t * psaved, + const pcl_state_t * pcs, + pcl_copy_operation_t operation +) +{ + if ((operation & (pcl_copy_before_call | pcl_copy_before_overlay)) != 0) + pcl_palette_init_from(psaved->ppalet, pcs->ppalet); + else if ((operation & pcl_copy_after) != 0) { + pcl_id_t key; + /* fix the compiler warning resulting from overuse of const */ + pcl_state_t *pcs2 = (pcl_state_t *)pcs; + id_set_value(key, psaved->sel_palette_id); + pl_dict_put(&pcs2->palette_store, id_key(key), 2, psaved->ppalet); + psaved->palette_stack = pcs2->palette_stack; + psaved->palette_store = pcs2->palette_store; + } + return 0; +} + +const pcl_init_t pcl_palette_init = { + palette_do_registration, palette_do_reset, palette_do_copy +}; |