summaryrefslogtreecommitdiff
path: root/pcl/pcl/pcpalet.c
diff options
context:
space:
mode:
authorChris Liddell <chris.liddell@artifex.com>2013-07-23 16:24:19 +0100
committerChris Liddell <chris.liddell@artifex.com>2015-07-20 18:21:17 +0100
commit6948650efd3fb9e2a70b8cf16aca57e9d0b7eb0a (patch)
tree5c2a1c671c1d4521f8a770d1e69e3d4342718030 /pcl/pcl/pcpalet.c
parent7fd9e0be26e67c36f87733bc89ea07dc26d9f839 (diff)
downloadghostpdl-6948650efd3fb9e2a70b8cf16aca57e9d0b7eb0a.tar.gz
Commit of build_consolidation branch
Squashed into one commit (see branch for details of the evolution of the branch). This brings gpcl6 and gxps into the Ghostscript build system, and a shared set of graphics library object files for all the interpreters. Also, brings the same configuration options to the pcl and xps products as we have for Ghostscript.
Diffstat (limited to 'pcl/pcl/pcpalet.c')
-rw-r--r--pcl/pcl/pcpalet.c1017
1 files changed, 1017 insertions, 0 deletions
diff --git a/pcl/pcl/pcpalet.c b/pcl/pcl/pcpalet.c
new file mode 100644
index 000000000..c4bda694f
--- /dev/null
+++ b/pcl/pcl/pcpalet.c
@@ -0,0 +1,1017 @@
+/* Copyright (C) 2001-2012 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 the license contained in the file LICENSE in this distribution.
+
+ 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.
+*/
+
+
+/* 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 "gsparam.h"
+#include "gsdevice.h"
+#include "gxcmap.h"
+#include "gxdcconv.h"
+#include "gzstate.h"
+#include "gxdevsop.h" /* for special ops */
+#include "gsutil.h" /* for gs_next_ids */
+#include "gdevmplt.h"
+
+extern gx_device_mplt gs_pcl_mono_palette_device;
+
+/* RC routines */
+gs_private_st_simple(st_palette_t, pcl_palette_t, "pcl palette object");
+gs_private_st_simple(st_pstack_entry_t, pstack_entry_t,
+ "palette stack entry");
+
+/*
+ * 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->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");
+ gs_free_object(mem, ppalette, "free_default_palette ppalette free");
+ pcs->pdflt_palette = 0;
+ }
+ /* what on earth is this? */
+ 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");
+}
+/*
+ * 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->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_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)
+ 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.
+ */
+static 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 white and black point of the GL/2 color palette.
+ *
+ * Returns 0 on success, < 0 in the event of an error.
+ */
+
+int
+pcl_palette_CR(pcl_state_t * pcs,
+ double wht0,
+ double wht1,
+ double wht2, double blk0, double blk1, double blk2)
+{
+ 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;
+
+ return pcl_cs_indexed_set_norm_and_Decode(&(pcs->ppalet->pindexed),
+ wht0, wht1, wht2,
+ blk0, blk1, blk2);
+}
+
+/*
+ * 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);
+
+ /* Check if the device requires that the palette must not change */
+ if (dev_proc(pcs->pgs->device, dev_spec_op)(pcs->pgs->device,
+ gxdso_needs_invariant_palette, 0, 0))
+ /* change the ID if so to indicate a difdferent colour space */
+ pcs->ppalet->pindexed->pcspace->id = gs_next_ids(pcs->memory, 1);
+
+ 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, double 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)
+{
+ /* should be reimplemented with icc profiles */
+ return 0;
+}
+
+/*
+ * 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->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->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));
+}
+
+/* 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 *pdev, *dev = gs_currentdevice(pcs->pgs);
+ int code = 0;
+
+ if (pcs->monochrome_mode) {
+ pdev = dev;
+ while (dev) {
+ pdev = dev;
+ if (strcmp(dev->dname, "PCL_Mono_Palette") == 0){
+ break;
+ }
+ dev = dev->child;
+ }
+ if (!dev){
+ code = gx_device_subclass(pdev, (gx_device *)&gs_pcl_mono_palette_device, sizeof(pcl_mono_palette_subclass_data));
+ }
+ } else {
+ while (dev) {
+ if (strcmp(dev->dname, "PCL_Mono_Palette") == 0){
+ break;
+ }
+ dev = dev->child;
+ }
+ if (dev){
+ gx_device_unsubclass(dev);
+ }
+ }
+ gx_unset_dev_color(pcs->pgs);
+ return code;
+}
+
+/*
+ * 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 | pca_dont_lockout_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,
+ "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
+};