summaryrefslogtreecommitdiff
path: root/base/gscolor2.c
diff options
context:
space:
mode:
Diffstat (limited to 'base/gscolor2.c')
-rw-r--r--base/gscolor2.c802
1 files changed, 802 insertions, 0 deletions
diff --git a/base/gscolor2.c b/base/gscolor2.c
new file mode 100644
index 000000000..64874e845
--- /dev/null
+++ b/base/gscolor2.c
@@ -0,0 +1,802 @@
+/* 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.
+*/
+
+
+/* Level 2 color operators for Ghostscript library */
+#include "memory_.h"
+#include "gx.h"
+#include "gserrors.h"
+#include "gxarith.h"
+#include "gxfixed.h" /* ditto */
+#include "gxmatrix.h" /* for gzstate.h */
+#include "gxcspace.h" /* for gscolor2.h */
+#include "gxcolor2.h"
+#include "gzstate.h"
+#include "gxpcolor.h"
+#include "stream.h"
+#include "gxcie.h"
+#include "gxfrac.h"
+
+/* ---------------- General colors and color spaces ---------------- */
+int
+gs_setcolorspace_only(gs_state * pgs, gs_color_space * pcs)
+{
+ int code = 0;
+ gs_color_space *cs_old = pgs->color[0].color_space;
+ gs_client_color cc_old = *pgs->color[0].ccolor;
+
+ if (pgs->in_cachedevice)
+ return_error(gs_error_undefined);
+
+ if (pcs->id != cs_old->id) {
+ rc_increment_cs(pcs);
+ pgs->color[0].color_space = pcs;
+ if ( (code = pcs->type->install_cspace(pcs, pgs)) < 0 ||
+ (pgs->overprint && (code = gs_do_set_overprint(pgs)) < 0) ) {
+ pgs->color[0].color_space = cs_old;
+ rc_decrement_only_cs(pcs, "gs_setcolorspace");
+ } else {
+ cs_old->type->adjust_color_count(&cc_old, cs_old, -1);
+ rc_decrement_only_cs(cs_old, "gs_setcolorspace");
+ }
+ }
+ return(code);
+}
+
+/* setcolorspace */
+int
+gs_setcolorspace(gs_state * pgs, gs_color_space * pcs)
+{
+ int code = 0;
+
+ code = gs_setcolorspace_only(pgs, pcs);
+ if (code >= 0) {
+ pgs->color[0].color_space->pclient_color_space_data =
+ pcs->pclient_color_space_data;
+ cs_full_init_color(pgs->color[0].ccolor, pcs);
+ gx_unset_dev_color(pgs);
+ }
+ return code;
+}
+
+/* currentcolorspace */
+gs_color_space *
+gs_currentcolorspace(const gs_state * pgs)
+{
+ return pgs->color[0].color_space;
+}
+
+/* setcolor */
+int
+gs_setcolor(gs_state * pgs, const gs_client_color * pcc)
+{
+ gs_color_space * pcs = pgs->color[0].color_space;
+ gs_client_color cc_old = *pgs->color[0].ccolor;
+ gx_device_color *dev_color = pgs->color[0].dev_color;
+ bool do_unset = true;
+
+ if (pgs->in_cachedevice)
+ return_error(gs_error_undefined); /* PLRM3 page 215. */
+ if (dev_color->ccolor_valid && gx_dc_is_pure(dev_color)) { /* change of colorspace will set type to _none */
+ int i;
+ int ncomps = cs_num_components(pcs);
+
+ for(i=0; i < ncomps; i++)
+ if (dev_color->ccolor.paint.values[i] != pcc->paint.values[i])
+ break;
+ do_unset = i < ncomps; /* if i == ncomps, color unchanged, optimized */
+ }
+ if (do_unset)
+ gx_unset_dev_color(pgs);
+ (*pcs->type->adjust_color_count)(pcc, pcs, 1);
+ *pgs->color[0].ccolor = *pcc;
+ (*pcs->type->restrict_color)(pgs->color[0].ccolor, pcs);
+ (*pcs->type->adjust_color_count)(&cc_old, pcs, -1);
+
+ return 0;
+}
+
+/* currentcolor */
+const gs_client_color *
+gs_currentcolor(const gs_state * pgs)
+{
+ return pgs->color[0].ccolor;
+}
+
+/* currentdevicecolor */
+const gx_device_color *
+gs_currentdevicecolor(const gs_state * pgs)
+{
+ return pgs->color[0].dev_color;
+}
+
+/* ------ Internal procedures ------ */
+
+/* GC descriptors */
+private_st_indexed_map();
+
+/* Define a lookup_index procedure that just returns the map values. */
+int
+lookup_indexed_map(const gs_color_space * pcs, int index, float *values)
+{
+ int m = cs_num_components(pcs->base_space);
+ const float *pv = &pcs->params.indexed.lookup.map->values[index * m];
+
+ memcpy(values, pv, sizeof(*values) * m);
+ return 0;
+}
+
+/* Free an indexed map and its values when the reference count goes to 0. */
+void
+free_indexed_map(gs_memory_t * pmem, void *pmap, client_name_t cname)
+{
+ gs_free_object(pmem, ((gs_indexed_map *) pmap)->values, cname);
+ gs_free_object(pmem, pmap, cname);
+}
+
+/*
+ * Allocate an indexed map for an Indexed or Separation color space.
+ */
+int
+alloc_indexed_map(gs_indexed_map ** ppmap, int nvals, gs_memory_t * pmem,
+ client_name_t cname)
+{
+ gs_indexed_map *pimap;
+
+ rc_alloc_struct_1(pimap, gs_indexed_map, &st_indexed_map, pmem,
+ return_error(gs_error_VMerror), cname);
+ if (nvals > 0) {
+ pimap->values =
+ (float *)gs_alloc_byte_array(pmem, nvals, sizeof(float), cname);
+
+ if (pimap->values == 0) {
+ gs_free_object(pmem, pimap, cname);
+ return_error(gs_error_VMerror);
+ }
+ } else
+ pimap->values = 0;
+ pimap->rc.free = free_indexed_map;
+ pimap->proc_data = 0; /* for GC */
+ pimap->num_values = nvals;
+ *ppmap = pimap;
+ return 0;
+}
+
+/* ---------------- Indexed color spaces ---------------- */
+
+gs_private_st_composite(st_color_space_Indexed, gs_color_space,
+ "gs_color_space_Indexed", cs_Indexed_enum_ptrs, cs_Indexed_reloc_ptrs);
+
+/* ------ Color space ------ */
+
+/* Define the Indexed color space type. */
+static cs_proc_restrict_color(gx_restrict_Indexed);
+static cs_proc_concrete_space(gx_concrete_space_Indexed);
+static cs_proc_concretize_color(gx_concretize_Indexed);
+static cs_proc_remap_color(gx_remap_IndexedNamed);
+static cs_proc_install_cspace(gx_install_Indexed);
+static cs_proc_set_overprint(gx_set_overprint_Indexed);
+static cs_proc_final(gx_final_Indexed);
+static cs_proc_serialize(gx_serialize_Indexed);
+static cs_proc_polarity(gx_polarity_Indexed);
+const gs_color_space_type gs_color_space_type_Indexed = {
+ gs_color_space_index_Indexed, false, false,
+ &st_color_space_Indexed, gx_num_components_1,
+ gx_init_paint_1, gx_restrict_Indexed,
+ gx_concrete_space_Indexed,
+ gx_concretize_Indexed, NULL,
+ gx_default_remap_color,
+ gx_install_Indexed,
+ gx_set_overprint_Indexed,
+ gx_final_Indexed, gx_no_adjust_color_count,
+ gx_serialize_Indexed,
+ gx_cspace_is_linear_default, gx_polarity_Indexed
+};
+
+/* To keep things vectorized and avoid an if test during the remap proc we
+ have another set of procedures to use for indexed color spaces when
+ someone has specified a named color profile and the base space of the
+ index color space is DeviceN or Separation */
+const gs_color_space_type gs_color_space_type_Indexed_Named = {
+ gs_color_space_index_Indexed, false, false,
+ &st_color_space_Indexed, gx_num_components_1,
+ gx_init_paint_1, gx_restrict_Indexed,
+ gx_concrete_space_Indexed,
+ gx_concretize_Indexed, NULL,
+ gx_remap_IndexedNamed,
+ gx_install_Indexed,
+ gx_set_overprint_Indexed,
+ gx_final_Indexed, gx_no_adjust_color_count,
+ gx_serialize_Indexed,
+ gx_cspace_is_linear_default, gx_polarity_Indexed
+};
+
+/* GC procedures. */
+
+static uint
+indexed_table_size(const gs_color_space *pcs)
+{
+ return (pcs->params.indexed.hival + 1) * pcs->params.indexed.n_comps;
+}
+static
+ENUM_PTRS_WITH(cs_Indexed_enum_ptrs, gs_color_space *pcs) return 0;
+case 0:
+if (pcs->params.indexed.use_proc)
+ ENUM_RETURN((void *)pcs->params.indexed.lookup.map);
+else
+ return ENUM_CONST_STRING2(pcs->params.indexed.lookup.table.data,
+ indexed_table_size(pcs));
+ENUM_PTRS_END
+static RELOC_PTRS_WITH(cs_Indexed_reloc_ptrs, gs_color_space *pcs)
+{
+ if (pcs->params.indexed.use_proc)
+ RELOC_PTR(gs_color_space, params.indexed.lookup.map);
+ else {
+ gs_const_string table;
+
+ table.data = pcs->params.indexed.lookup.table.data;
+ table.size = indexed_table_size(pcs);
+ RELOC_CONST_STRING_VAR(table);
+ pcs->params.indexed.lookup.table.data = table.data;
+ }
+}
+RELOC_PTRS_END
+
+/* Color space installation for an Indexed color space. */
+
+/* Return polarity of base space */
+static gx_color_polarity_t
+gx_polarity_Indexed(const gs_color_space * pcs)
+{
+ return (*pcs->base_space->type->polarity)
+ ((const gs_color_space *)pcs->base_space);
+}
+
+static int
+gx_install_Indexed(gs_color_space * pcs, gs_state * pgs)
+{
+ return (*pcs->base_space->type->install_cspace)
+ (pcs->base_space, pgs);
+}
+
+/* Color space overprint setting ditto. */
+
+static int
+gx_set_overprint_Indexed(const gs_color_space * pcs, gs_state * pgs)
+{
+ return (*pcs->base_space->type->set_overprint)
+ ((const gs_color_space *)pcs->base_space, pgs);
+}
+
+/* Color space finalization ditto. */
+
+static void
+gx_final_Indexed(const gs_color_space * pcs)
+{
+ if (pcs->params.indexed.use_proc) {
+ rc_adjust_const(pcs->params.indexed.lookup.map, -1,
+ "gx_adjust_Indexed");
+ } else {
+ byte *data = (byte *)pcs->params.indexed.lookup.table.data; /* Break 'const'. */
+
+ gs_free_string(pcs->rc.memory, data,
+ pcs->params.indexed.lookup.table.size, "gx_final_Indexed");
+ }
+}
+
+/*
+ * Default palette mapping functions for indexed color maps. These just
+ * return the values already in the palette.
+ *
+ * For performance reasons, we provide four functions: special cases for 1,
+ * 3, and 4 entry palettes, and a general case. Note that these procedures
+ * do not range-check their input values.
+ */
+static int
+map_palette_entry_1(const gs_color_space * pcs, int indx, float *values)
+{
+ values[0] = pcs->params.indexed.lookup.map->values[indx];
+ return 0;
+}
+
+static int
+map_palette_entry_3(const gs_color_space * pcs, int indx, float *values)
+{
+ const float *pv = &(pcs->params.indexed.lookup.map->values[3 * indx]);
+
+ values[0] = pv[0];
+ values[1] = pv[1];
+ values[2] = pv[2];
+ return 0;
+}
+
+static int
+map_palette_entry_4(const gs_color_space * pcs, int indx, float *values)
+{
+ const float *pv = &(pcs->params.indexed.lookup.map->values[4 * indx]);
+
+ values[0] = pv[0];
+ values[1] = pv[1];
+ values[2] = pv[2];
+ values[3] = pv[3];
+ return 0;
+}
+
+static int
+map_palette_entry_n(const gs_color_space * pcs, int indx, float *values)
+{
+ int m = cs_num_components(pcs->base_space);
+
+ memcpy((void *)values,
+ (const void *)(pcs->params.indexed.lookup.map->values + indx * m),
+ m * sizeof(float)
+ );
+
+ return 0;
+}
+
+/*
+ * Allocate an indexed map to be used as a palette for indexed color space.
+ */
+static gs_indexed_map *
+alloc_indexed_palette(
+ const gs_color_space * pbase_cspace,
+ int nvals,
+ gs_memory_t * pmem
+)
+{
+ int num_comps = gs_color_space_num_components(pbase_cspace);
+ gs_indexed_map *pimap;
+ int code =
+ alloc_indexed_map(&pimap, nvals * num_comps, pmem,
+ "alloc_indexed_palette");
+
+ if (code < 0)
+ return 0;
+ if (num_comps == 1)
+ pimap->proc.lookup_index = map_palette_entry_1;
+ else if (num_comps == 3)
+ pimap->proc.lookup_index = map_palette_entry_3;
+ else if (num_comps == 4)
+ pimap->proc.lookup_index = map_palette_entry_4;
+ else
+ pimap->proc.lookup_index = map_palette_entry_n;
+ return pimap;
+}
+
+/*
+ * Build an indexed color space.
+ */
+int
+gs_cspace_build_Indexed(
+ gs_color_space ** ppcspace,
+ gs_color_space * pbase_cspace,
+ uint num_entries,
+ const gs_const_string * ptbl,
+ gs_memory_t * pmem
+)
+{
+ gs_color_space *pcspace = 0;
+ gs_indexed_params *pindexed = 0;
+
+ if ((pbase_cspace == 0) || !pbase_cspace->type->can_be_base_space)
+ return_error(gs_error_rangecheck);
+
+ pcspace = gs_cspace_alloc(pmem, &gs_color_space_type_Indexed);
+ if (pcspace == NULL)
+ return_error(gs_error_VMerror);
+ pindexed = &(pcspace->params.indexed);
+ if (ptbl == 0) {
+ pindexed->lookup.map =
+ alloc_indexed_palette(pbase_cspace, num_entries, pmem);
+ if (pindexed->lookup.map == 0) {
+ gs_free_object(pmem, pcspace, "gs_cspace_build_Indexed");
+ return_error(gs_error_VMerror);
+ }
+ pindexed->use_proc = true;
+ } else {
+ pindexed->lookup.table = *ptbl;
+ pindexed->use_proc = false;
+ }
+ pcspace->base_space = pbase_cspace;
+ rc_increment_cs(pbase_cspace);
+ pindexed->hival = num_entries - 1;
+ pindexed->n_comps = cs_num_components(pbase_cspace);
+ *ppcspace = pcspace;
+ return 0;
+}
+
+/*
+ * Return the number of entries in an indexed color space.
+ */
+int
+gs_cspace_indexed_num_entries(const gs_color_space * pcspace)
+{
+ if (gs_color_space_get_index(pcspace) != gs_color_space_index_Indexed)
+ return 0;
+ return pcspace->params.indexed.hival + 1;
+}
+
+/*
+ * Get the palette for an indexed color space. This will return a null
+ * pointer if the color space is not an indexed color space or if the
+ * color space does not use the mapped index palette.
+ */
+float *
+gs_cspace_indexed_value_array(const gs_color_space * pcspace)
+{
+ if ((gs_color_space_get_index(pcspace) != gs_color_space_index_Indexed) ||
+ pcspace->params.indexed.use_proc
+ )
+ return 0;
+ return pcspace->params.indexed.lookup.map->values;
+}
+
+/*
+ * Set the lookup procedure to be used with an indexed color space.
+ */
+int
+gs_cspace_indexed_set_proc(
+ gs_color_space * pcspace,
+ int (*proc)(const gs_color_space *, int, float *)
+)
+{
+ if ((gs_color_space_get_index(pcspace) != gs_color_space_index_Indexed) ||
+ !pcspace->params.indexed.use_proc
+ )
+ return_error(gs_error_rangecheck);
+ pcspace->params.indexed.lookup.map->proc.lookup_index = proc;
+ return 0;
+}
+
+/* ------ Colors ------ */
+
+/* Force an Indexed color into legal range. */
+static void
+gx_restrict_Indexed(gs_client_color * pcc, const gs_color_space * pcs)
+{
+ float value = pcc->paint.values[0];
+
+ pcc->paint.values[0] =
+ (is_fneg(value) ? 0 :
+ value >= pcs->params.indexed.hival ? pcs->params.indexed.hival :
+ value);
+}
+
+/* Color remapping for Indexed color spaces. */
+static const gs_color_space *
+gx_concrete_space_Indexed(const gs_color_space * pcs,
+ const gs_imager_state * pis)
+{
+ bool is_lab = false;
+
+ if (gs_color_space_is_PSCIE(pcs->base_space)) {
+ if (pcs->base_space->icc_equivalent == NULL) {
+ gs_colorspace_set_icc_equivalent(pcs->base_space,
+ &is_lab, pis->memory);
+ }
+ return (pcs->base_space->icc_equivalent);
+ }
+ return cs_concrete_space(pcs->base_space, pis);
+}
+
+static int
+gx_concretize_Indexed(const gs_client_color * pc, const gs_color_space * pcs,
+ frac * pconc, const gs_imager_state * pis, gx_device *dev)
+{
+ gs_client_color cc;
+ const gs_color_space *pbcs =
+ (const gs_color_space *)pcs->base_space;
+ int code = gs_indexed_limit_and_lookup(pc, pcs, &cc);
+
+ if (code < 0)
+ return code;
+ return (*pbcs->type->concretize_color) (&cc, pbcs, pconc, pis, dev);
+}
+
+/* We should only be here for cases where the base space is DeviceN or Sep and
+ we are doing named color replacement. */
+static int
+gx_remap_IndexedNamed(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)
+{
+ frac conc[GS_CLIENT_COLOR_MAX_COMPONENTS];
+ const gs_color_space *pconcs;
+ int i = pcs->type->num_components(pcs);
+ gs_client_color cc;
+ bool mapped;
+ int code = gs_indexed_limit_and_lookup(pcc, pcs, &cc);
+
+ if (code < 0)
+ return code;
+
+ pconcs = cs_concrete_space(pcs, pis);
+ /* Now see if we can do the named color replacement */
+ mapped = gx_remap_named_color(&cc, pconcs, pdc, pis, dev, select);
+
+ if (!mapped) {
+ /* Named color remap failed perhaps due to colorant not found. Do the
+ old approach of concretize of the base space and remap concrete color */
+ const gs_color_space *pbcs =
+ (const gs_color_space *)pcs->base_space;
+
+ code = (*pbcs->type->concretize_color) (&cc, pbcs, conc, pis, dev);
+ if (code < 0)
+ return code;
+ code = (*pconcs->type->remap_concrete_color)(conc, pconcs, pdc, pis, dev, select);
+ }
+
+ /* Save original color space and color info into dev color */
+ i = any_abs(i);
+ for (i--; i >= 0; i--)
+ pdc->ccolor.paint.values[i] = pcc->paint.values[i];
+ pdc->ccolor_valid = true;
+ return code;
+}
+
+/* Look up an index in an Indexed color space. */
+int
+gs_cspace_indexed_lookup(const gs_color_space *pcs, int index,
+ gs_client_color *pcc)
+{
+ const gs_indexed_params *pip = &pcs->params.indexed;
+ if (pip->use_proc) {
+ return pip->lookup.map->proc.lookup_index
+ (pcs, index, &pcc->paint.values[0]);
+ } else {
+ const gs_color_space *pbcs = pcs->base_space;
+ int m = cs_num_components(pbcs);
+ const byte *pcomp = pip->lookup.table.data + m * index;
+
+ switch (m) {
+ default: { /* DeviceN */
+ int i;
+
+ for (i = 0; i < m; ++i)
+ pcc->paint.values[i] = pcomp[i] * (1.0 / 255.0);
+ }
+ break;
+ case 4:
+ pcc->paint.values[3] = pcomp[3] * (1.0 / 255.0);
+ case 3:
+ pcc->paint.values[2] = pcomp[2] * (1.0 / 255.0);
+ case 2:
+ pcc->paint.values[1] = pcomp[1] * (1.0 / 255.0);
+ case 1:
+ pcc->paint.values[0] = pcomp[0] * (1.0 / 255.0);
+ }
+ return 0;
+ }
+}
+
+/* Look up an index in an Indexed color space, return value as byte value(s). */
+int
+gs_cspace_indexed_lookup_bytes(const gs_color_space *pcs, float index_float,
+ unsigned char *output)
+{
+ const gs_indexed_params *pip = &pcs->params.indexed;
+ const gs_color_space *pbcs = pcs->base_space;
+ int m = cs_num_components(pbcs);
+ int index;
+
+ index = (is_fneg(index_float) ? 0 :
+ index_float >= pcs->params.indexed.hival ? pcs->params.indexed.hival :
+ (int) index_float);
+
+ if (pip->use_proc) {
+
+ float values[GS_CLIENT_COLOR_MAX_COMPONENTS];
+ int ok;
+
+ ok = pip->lookup.map->proc.lookup_index(pcs, index, values);
+
+ /* Get out of float and to uchar.
+ Note the fall through in the switch statement to
+ handle the number of channels */
+
+ switch (m) {
+ default: { /* DeviceN */
+ int i;
+
+ for (i = 0; i < m; ++i)
+ output[i] = float_color_to_byte_color(values[i]);
+ }
+ break;
+ case 4:
+ output[3] = float_color_to_byte_color(values[3]);
+ case 3:
+ output[2] = float_color_to_byte_color(values[2]);
+ case 2:
+ output[1] = float_color_to_byte_color(values[1]);
+ case 1:
+ output[0] = float_color_to_byte_color(values[0]);
+ }
+
+ return ok;
+
+ } else {
+
+ /* Here it uses a 1-D LUT. Again the fall through
+ in the switch statement */
+
+ const byte *pcomp = pip->lookup.table.data + m * index;
+
+ switch (m) {
+ default: { /* DeviceN */
+ int i;
+
+ for (i = 0; i < m; ++i)
+ output[i] = pcomp[i];
+ }
+ break;
+ case 4:
+ output[3] = pcomp[3];
+ case 3:
+ output[2] = pcomp[2];
+ case 2:
+ output[1] = pcomp[1];
+ case 1:
+ output[0] = pcomp[0];
+ }
+ return 0;
+ }
+}
+
+/* Look up an index in an Indexed color space, return value as frac value(s). */
+int
+gs_cspace_indexed_lookup_frac(const gs_color_space *pcs, float index_float,
+ frac *output)
+{
+ const gs_indexed_params *pip = &pcs->params.indexed;
+ const gs_color_space *pbcs = pcs->base_space;
+ int m = cs_num_components(pbcs);
+ int index;
+
+ index = (is_fneg(index_float) ? 0 :
+ index_float >= pcs->params.indexed.hival ? pcs->params.indexed.hival :
+ (int) index_float);
+
+ if (pip->use_proc) {
+
+ float values[GS_CLIENT_COLOR_MAX_COMPONENTS];
+ int ok;
+
+ ok = pip->lookup.map->proc.lookup_index(pcs, index, values);
+
+ /* Get out of float and to frac.
+ Note the fall through in the switch statement to
+ handle the number of channels */
+
+ switch (m) {
+ default: { /* DeviceN */
+ int i;
+
+ for (i = 0; i < m; ++i)
+ output[i] = float2frac(values[i]);
+ }
+ break;
+ case 4:
+ output[3] = float2frac(values[3]);
+ case 3:
+ output[2] = float2frac(values[2]);
+ case 2:
+ output[1] = float2frac(values[1]);
+ case 1:
+ output[0] = float2frac(values[0]);
+ }
+
+ return ok;
+
+ } else {
+
+ /* Here it uses a 1-D LUT
+ Again the fall through */
+
+ const byte *pcomp = pip->lookup.table.data + m * index;
+
+ switch (m) {
+ default: { /* DeviceN */
+ int i;
+
+ for (i = 0; i < m; ++i)
+ output[i] = byte2frac(pcomp[i]);
+ }
+ break;
+ case 4:
+ output[3] = byte2frac(pcomp[3]);
+ case 3:
+ output[2] = byte2frac(pcomp[2]);
+ case 2:
+ output[1] = byte2frac(pcomp[1]);
+ case 1:
+ output[0] = byte2frac(pcomp[0]);
+ }
+ return 0;
+ }
+}
+
+/* Look up with restriction */
+
+int
+gs_indexed_limit_and_lookup(const gs_client_color * pc,const gs_color_space *pcs,
+ gs_client_color *pcc)
+{
+
+ float value = pc->paint.values[0] + 0.001;
+ int index =
+ (is_fneg(value) ? 0 :
+ value >= pcs->params.indexed.hival ? pcs->params.indexed.hival :
+ (int)value);
+ return(gs_cspace_indexed_lookup(pcs, index, pcc));
+
+}
+
+/* ---------------- Serialization. -------------------------------- */
+
+static int
+gx_serialize_Indexed(const gs_color_space * pcs, stream * s)
+{
+ const gs_indexed_params * p = &pcs->params.indexed;
+ uint n;
+ int code = gx_serialize_cspace_type(pcs, s);
+
+ if (code < 0)
+ return code;
+ code = cs_serialize(pcs->base_space, s);
+ if (code < 0)
+ return code;
+ code = sputs(s, (const byte *)&p->hival, sizeof(p->hival), &n);
+ if (code < 0)
+ return code;
+ code = sputs(s, (const byte *)&p->use_proc, sizeof(p->use_proc), &n);
+ if (code < 0)
+ return code;
+ if (p->use_proc) {
+ code = sputs(s, (const byte *)&p->lookup.map->num_values,
+ sizeof(p->lookup.map->num_values), &n);
+ if (code < 0)
+ return code;
+ code = sputs(s, (const byte *)&p->lookup.map->values[0],
+ sizeof(p->lookup.map->values[0]) * p->lookup.map->num_values, &n);
+ } else {
+ code = sputs(s, (const byte *)&p->lookup.table.size,
+ sizeof(p->lookup.table.size), &n);
+ if (code < 0)
+ return code;
+ code = sputs(s, p->lookup.table.data, p->lookup.table.size, &n);
+ }
+ return code;
+}
+
+/* ---------------- High level device support -------------------------------- */
+
+/*
+ * This special function forces a device to include the current
+ * color space into the output. Returns 'rangecheck' if the device can't handle it.
+ * The primary reason is to include DefaultGray, DefaultRGB, DefaultCMYK into PDF.
+ * Should be called for each page that requires the resource.
+ * Redundant calls per page with same cspace id are allowed.
+ * Redundant calls per page with different cspace id are are allowed but
+ * highly undesirable.
+ * No need to call it with color spaces explicitly referred by the document,
+ * because they are included automatically.
+ * res_name and name_length passes the resource name.
+ */
+int
+gs_includecolorspace(gs_state * pgs, const byte *res_name, int name_length)
+{
+ return (*dev_proc(pgs->device, include_color_space))(pgs->device, gs_currentcolorspace_inline(pgs), res_name, name_length);
+}