summaryrefslogtreecommitdiff
path: root/gs/psi/zimage.c
diff options
context:
space:
mode:
Diffstat (limited to 'gs/psi/zimage.c')
-rw-r--r--gs/psi/zimage.c601
1 files changed, 601 insertions, 0 deletions
diff --git a/gs/psi/zimage.c b/gs/psi/zimage.c
new file mode 100644
index 000000000..faca23910
--- /dev/null
+++ b/gs/psi/zimage.c
@@ -0,0 +1,601 @@
+/* Copyright (C) 2001-2006 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 that
+ license. 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.
+*/
+
+/* $Id$ */
+/* Image operators */
+#include "math_.h"
+#include "memory_.h"
+#include "stat_.h" /* get system header early to avoid name clash on Cygwin */
+#include "ghost.h"
+#include "oper.h"
+#include "gscolor.h"
+#include "gscspace.h"
+#include "gscolor2.h"
+#include "gsmatrix.h"
+#include "gsimage.h"
+#include "gxfixed.h"
+#include "gsstruct.h"
+#include "gxiparam.h"
+#include "idict.h"
+#include "idparam.h"
+#include "estack.h" /* for image[mask] */
+#include "ialloc.h"
+#include "igstate.h"
+#include "ilevel.h"
+#include "store.h"
+#include "stream.h"
+#include "ifilter.h" /* for stream exception handling */
+#include "iimage.h"
+#include "gxcspace.h"
+
+/* Forward references */
+static int zimage_data_setup(i_ctx_t *i_ctx_p, const gs_pixel_image_t * pim,
+ gx_image_enum_common_t * pie,
+ const ref * sources, int npop);
+static int image_proc_process(i_ctx_t *);
+static int image_file_continue(i_ctx_t *);
+static int image_string_continue(i_ctx_t *);
+static int image_cleanup(i_ctx_t *);
+
+
+
+/* Extract and check the parameters for a gs_data_image_t. */
+int
+data_image_params(const gs_memory_t *mem,
+ const ref *op, gs_data_image_t *pim,
+ image_params *pip, bool require_DataSource,
+ int num_components, int max_bits_per_component,
+ bool has_alpha)
+{
+ int code;
+ int decode_size;
+ ref *pds;
+
+ check_type(*op, t_dictionary);
+ check_dict_read(*op);
+ if ((code = dict_int_param(op, "Width", 0, max_int_in_fixed / 2,
+ -1, &pim->Width)) < 0 ||
+ (code = dict_int_param(op, "Height", 0, max_int_in_fixed / 2,
+ -1, &pim->Height)) < 0 ||
+ (code = dict_matrix_param(mem, op, "ImageMatrix",
+ &pim->ImageMatrix)) < 0 ||
+ (code = dict_bool_param(op, "MultipleDataSources", false,
+ &pip->MultipleDataSources)) < 0 ||
+ (code = dict_int_param(op, "BitsPerComponent", 1,
+ max_bits_per_component, -1,
+ &pim->BitsPerComponent)) < 0 ||
+ (code = decode_size = dict_floats_param(mem, op, "Decode",
+ num_components * 2,
+ &pim->Decode[0], NULL)) < 0 ||
+ (code = dict_bool_param(op, "Interpolate", false,
+ &pim->Interpolate)) < 0
+ )
+ return code;
+ pip->pDecode = &pim->Decode[0];
+ /* Extract and check the data sources. */
+ if ((code = dict_find_string(op, "DataSource", &pds)) <= 0) {
+ if (require_DataSource)
+ return (code < 0 ? code : gs_note_error(e_rangecheck));
+ return 1; /* no data source */
+ }
+ if (pip->MultipleDataSources) {
+ ref *ds = pip->DataSource;
+ long i, n = num_components + (has_alpha ? 1 : 0);
+ if (!r_is_array(pds))
+ return_error(e_typecheck);
+ if (r_size(pds) != n)
+ return_error(e_rangecheck);
+ for (i = 0; i < n; ++i)
+ array_get(mem, pds, i, &ds[i]);
+ if (r_type(&ds[0]) == t_string) {
+ /* We don't have a problem with the strings of different length
+ * but Adobe does and CET tast 12-02.ps reports this as an error.
+ */
+ if (has_alpha)
+ n--;
+ for (i = 1; i < n; ++i) {
+ if (r_type(&ds[i]) == t_string && r_size(&ds[i]) != r_size(&ds[0])) {
+ return_error(e_rangecheck);
+ }
+ }
+ }
+ } else
+ pip->DataSource[0] = *pds;
+ return 0;
+}
+
+/* Extract and check the parameters for a gs_pixel_image_t. */
+int
+pixel_image_params(i_ctx_t *i_ctx_p, const ref *op, gs_pixel_image_t *pim,
+ image_params *pip, int max_bits_per_component,
+ bool has_alpha, gs_color_space *csp)
+{
+ int num_components =
+ gs_color_space_num_components(csp);
+ int code;
+
+ if (num_components < 1)
+ return_error(e_rangecheck); /* Pattern space not allowed */
+ pim->ColorSpace = csp;
+ code = data_image_params(imemory, op, (gs_data_image_t *) pim, pip, true,
+ num_components, max_bits_per_component,
+ has_alpha);
+ if (code < 0)
+ return code;
+ pim->format =
+ (pip->MultipleDataSources ? gs_image_format_component_planar :
+ gs_image_format_chunky);
+ return dict_bool_param(op, "CombineWithColor", false,
+ &pim->CombineWithColor);
+}
+
+/* Common setup for all Level 1 and 2 images, and ImageType 4 images. */
+int
+zimage_setup(i_ctx_t *i_ctx_p, const gs_pixel_image_t * pim,
+ const ref * sources, bool uses_color, int npop)
+{
+ gx_image_enum_common_t *pie;
+ int code =
+ gs_image_begin_typed((const gs_image_common_t *)pim, igs,
+ uses_color, &pie);
+
+ if (code < 0)
+ return code;
+ return zimage_data_setup(i_ctx_p, (const gs_pixel_image_t *)pim, pie,
+ sources, npop);
+}
+
+/* Common code for .image1 and .alphaimage operators */
+int
+image1_setup(i_ctx_t * i_ctx_p, bool has_alpha)
+{
+ os_ptr op = osp;
+ gs_image_t image;
+ image_params ip;
+ int code;
+ gs_color_space *csp = gs_currentcolorspace(igs);
+ extern bool CPSI_mode;
+
+ /* Adobe interpreters accept sampled images when the current color
+ * space is a pattern color space using the base color space instead
+ * of the pattern space. CET 12-07a-12
+ * If all conditions are not met the pattern color space goes through
+ * triggering a rangecheck error.
+ */
+ if (CPSI_mode && gs_color_space_num_components(csp) < 1) {
+ gs_color_space *bsp = csp->base_space;
+ if (bsp)
+ csp = bsp;
+ }
+
+ gs_image_t_init(&image, csp);
+ code = pixel_image_params( i_ctx_p,
+ op,
+ (gs_pixel_image_t *)&image,
+ &ip,
+ (level2_enabled ? 16 : 8),
+ has_alpha, csp);
+ if (code < 0)
+ return code;
+
+ image.Alpha = (has_alpha ? gs_image_alpha_last : gs_image_alpha_none);
+ return zimage_setup( i_ctx_p,
+ (gs_pixel_image_t *)&image,
+ &ip.DataSource[0],
+ image.CombineWithColor,
+ 1 );
+}
+
+/* <dict> .image1 - */
+static int
+zimage1(i_ctx_t *i_ctx_p)
+{
+ return image1_setup(i_ctx_p, false);
+}
+
+/* <dict> .imagemask1 - */
+static int
+zimagemask1(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ gs_image_t image;
+ image_params ip;
+ int code;
+
+ gs_image_t_init_mask_adjust(&image, false,
+ gs_incachedevice(igs) != CACHE_DEVICE_NONE);
+ code = data_image_params(imemory, op, (gs_data_image_t *) & image,
+ &ip, true, 1, 1, false);
+ if (code < 0)
+ return code;
+ return zimage_setup(i_ctx_p, (gs_pixel_image_t *)&image, &ip.DataSource[0],
+ true, 1);
+}
+
+
+/* Common setup for all Level 1 and 2 images, and ImageType 3 and 4 images. */
+/*
+ * We push the following on the estack.
+ * control mark,
+ * num_sources,
+ * for I = num_sources-1 ... 0:
+ * data source I,
+ * aliasing information:
+ * if source is not file, 1, except that the topmost value
+ * is used for bookkeeping in the procedure case (see below);
+ * if file is referenced by a total of M different sources and
+ * this is the occurrence with the lowest I, M;
+ * otherwise, -J, where J is the lowest I of the same file as
+ * this one;
+ * current plane index,
+ * num_sources,
+ * enumeration structure.
+ */
+#define NUM_PUSH(nsource) ((nsource) * 2 + 5)
+/*
+ * We can access these values either from the bottom (esp at control mark - 1,
+ * EBOT macros) or the top (esp = enumeration structure, ETOP macros).
+ * Note that all macros return pointers.
+ */
+#define EBOT_NUM_SOURCES(ep) ((ep) + 2)
+#define EBOT_SOURCE(ep, i)\
+ ((ep) + 3 + (EBOT_NUM_SOURCES(ep)->value.intval - 1 - (i)) * 2)
+#define ETOP_SOURCE(ep, i)\
+ ((ep) - 4 - (i) * 2)
+#define ETOP_PLANE_INDEX(ep) ((ep) - 2)
+#define ETOP_NUM_SOURCES(ep) ((ep) - 1)
+static int
+zimage_data_setup(i_ctx_t *i_ctx_p, const gs_pixel_image_t * pim,
+ gx_image_enum_common_t * pie, const ref * sources, int npop)
+{
+ int num_sources = pie->num_planes;
+ int inumpush = NUM_PUSH(num_sources);
+ int code;
+ gs_image_enum *penum;
+ int px;
+ const ref *pp;
+ bool string_sources = true;
+
+ check_estack(inumpush + 2); /* stuff above, + continuation + proc */
+ make_int(EBOT_NUM_SOURCES(esp), num_sources);
+ /*
+ * Note that the data sources may be procedures, strings, or (Level
+ * 2 only) files. (The Level 1 reference manual says that Level 1
+ * requires procedures, but Adobe Level 1 interpreters also accept
+ * strings.) The sources must all be of the same type.
+ *
+ * The Adobe documentation explicitly says that if two or more of the
+ * data sources are the same or inter-dependent files, the result is not
+ * defined. We don't have a problem with the bookkeeping for
+ * inter-dependent files, since each one has its own buffer, but we do
+ * have to be careful if two or more sources are actually the same file.
+ * That is the reason for the aliasing information described above.
+ */
+ for (px = 0, pp = sources; px < num_sources; px++, pp++) {
+ es_ptr ep = EBOT_SOURCE(esp, px);
+
+ make_int(ep + 1, 1); /* default is no aliasing */
+ switch (r_type(pp)) {
+ case t_file:
+ if (!level2_enabled)
+ return_error(e_typecheck);
+ /* Check for aliasing. */
+ {
+ int pi;
+
+ for (pi = 0; pi < px; ++pi)
+ if (sources[pi].value.pfile == pp->value.pfile) {
+ /* Record aliasing */
+ make_int(ep + 1, -pi);
+ EBOT_SOURCE(esp, pi)[1].value.intval++;
+ break;
+ }
+ }
+ string_sources = false;
+ /* falls through */
+ case t_string:
+ if (r_type(pp) != r_type(sources)) {
+ if (pie != NULL)
+ gx_image_end(pie, false); /* Clean up pie */
+ return_error(e_typecheck);
+ }
+ check_read(*pp);
+ break;
+ default:
+ if (!r_is_proc(sources)) {
+ static const char ds[] = "DataSource";
+ if (pie != NULL)
+ gx_image_end(pie, false); /* Clean up pie */
+ gs_errorinfo_put_pair(i_ctx_p, ds, sizeof(ds) - 1, pp);
+ return_error(e_typecheck);
+ }
+ check_proc(*pp);
+ string_sources = false;
+ }
+ *ep = *pp;
+ }
+ /* Always place the image enumerator into local memory,
+ because pie may have local objects inherited from igs,
+ which may be local when the current allocation mode is global.
+ Bug 688140. */
+ if ((penum = gs_image_enum_alloc(imemory_local, "image_setup")) == 0)
+ return_error(e_VMerror);
+ code = gs_image_enum_init(penum, pie, (const gs_data_image_t *)pim, igs);
+ if (code != 0 || (pie->skipping && string_sources)) { /* error, or empty image */
+ int code1 = gs_image_cleanup_and_free_enum(penum, igs);
+
+ if (code >= 0) /* empty image */
+ pop(npop);
+ if (code >= 0 && code1 < 0)
+ code = code1;
+ return code;
+ }
+ push_mark_estack(es_other, image_cleanup);
+ esp += inumpush - 1;
+ make_int(ETOP_PLANE_INDEX(esp), 0);
+ make_int(ETOP_NUM_SOURCES(esp), num_sources);
+ make_struct(esp, avm_local, penum);
+ switch (r_type(sources)) {
+ case t_file:
+ push_op_estack(image_file_continue);
+ break;
+ case t_string:
+ push_op_estack(image_string_continue);
+ break;
+ default: /* procedure */
+ push_op_estack(image_proc_process);
+ break;
+ }
+ pop(npop);
+ return o_push_estack;
+}
+/* Pop all the control information off the e-stack. */
+static es_ptr
+zimage_pop_estack(es_ptr tep)
+{
+ return tep - NUM_PUSH(ETOP_NUM_SOURCES(tep)->value.intval);
+}
+
+/*
+ * Continuation for procedure data source. We use the topmost aliasing slot
+ * to remember whether we've just called the procedure (1) or whether we're
+ * returning from a RemapColor callout (0).
+ */
+static int
+image_proc_continue(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ gs_image_enum *penum = r_ptr(esp, gs_image_enum);
+ int px = ETOP_PLANE_INDEX(esp)->value.intval;
+ int num_sources = ETOP_NUM_SOURCES(esp)->value.intval;
+ uint size, used[GS_IMAGE_MAX_COMPONENTS];
+ gs_const_string plane_data[GS_IMAGE_MAX_COMPONENTS];
+ const byte *wanted;
+ int i, code;
+
+ if (!r_has_type_attrs(op, t_string, a_read)) {
+ check_op(1);
+ /* Procedure didn't return a (readable) string. Quit. */
+ esp = zimage_pop_estack(esp);
+ image_cleanup(i_ctx_p);
+ return_error(!r_has_type(op, t_string) ? e_typecheck : e_invalidaccess);
+ }
+ size = r_size(op);
+ if (size == 0 && ETOP_SOURCE(esp, 0)[1].value.intval == 0)
+ code = 1;
+ else {
+ for (i = 0; i < num_sources; i++)
+ plane_data[i].size = 0;
+ plane_data[px].data = op->value.bytes;
+ plane_data[px].size = size;
+ code = gs_image_next_planes(penum, plane_data, used);
+ if (code == e_RemapColor) {
+ op->value.bytes += used[px]; /* skip used data */
+ r_dec_size(op, used[px]);
+ ETOP_SOURCE(esp, 0)[1].value.intval = 0; /* RemapColor callout */
+ return code;
+ }
+ }
+ if (code) { /* Stop now. */
+ esp = zimage_pop_estack(esp);
+ pop(1);
+ image_cleanup(i_ctx_p);
+ return (code < 0 ? code : o_pop_estack);
+ }
+ pop(1);
+ wanted = gs_image_planes_wanted(penum);
+ do {
+ if (++px == num_sources)
+ px = 0;
+ } while (!wanted[px]);
+ ETOP_PLANE_INDEX(esp)->value.intval = px;
+ return image_proc_process(i_ctx_p);
+}
+static int
+image_proc_process(i_ctx_t *i_ctx_p)
+{
+ int px = ETOP_PLANE_INDEX(esp)->value.intval;
+ gs_image_enum *penum = r_ptr(esp, gs_image_enum);
+ const byte *wanted = gs_image_planes_wanted(penum);
+ int num_sources = ETOP_NUM_SOURCES(esp)->value.intval;
+ const ref *pp;
+
+ ETOP_SOURCE(esp, 0)[1].value.intval = 0; /* procedure callout */
+ while (!wanted[px]) {
+ if (++px == num_sources)
+ px = 0;
+ ETOP_PLANE_INDEX(esp)->value.intval = px;
+ }
+ pp = ETOP_SOURCE(esp, px);
+ push_op_estack(image_proc_continue);
+ *++esp = *pp;
+ return o_push_estack;
+}
+
+/* Continue processing data from an image with file data sources. */
+static int
+image_file_continue(i_ctx_t *i_ctx_p)
+{
+ gs_image_enum *penum = r_ptr(esp, gs_image_enum);
+ int num_sources = ETOP_NUM_SOURCES(esp)->value.intval;
+
+ for (;;) {
+ uint min_avail = max_int;
+ gs_const_string plane_data[GS_IMAGE_MAX_COMPONENTS];
+ int code;
+ int px;
+ const ref *pp;
+ bool at_eof = false;
+
+ /*
+ * Do a first pass through the files to ensure that at least
+ * one has data available in its buffer.
+ */
+
+ for (px = 0, pp = ETOP_SOURCE(esp, 0); px < num_sources;
+ ++px, pp -= 2
+ ) {
+ int num_aliases = pp[1].value.intval;
+ stream *s = pp->value.pfile;
+ int min_left;
+ uint avail;
+
+ if (num_aliases <= 0)
+ num_aliases = ETOP_SOURCE(esp, -num_aliases)[1].value.intval;
+ while ((avail = sbufavailable(s)) <=
+ (min_left = sbuf_min_left(s)) + num_aliases - 1) {
+ int next = s->end_status;
+
+ switch (next) {
+ case 0:
+ s_process_read_buf(s);
+ continue;
+ case EOFC:
+ at_eof = true;
+ break; /* with no data available */
+ case INTC:
+ case CALLC:
+ return
+ s_handle_read_exception(i_ctx_p, next, pp,
+ NULL, 0, image_file_continue);
+ default:
+ /* case ERRC: */
+ return_error(e_ioerror);
+ }
+ break; /* for EOFC */
+ }
+ /*
+ * Note that in the EOF case, we can get here with no data
+ * available.
+ */
+ if (avail >= min_left)
+ avail = (avail - min_left) / num_aliases; /* may be 0 */
+ if (avail < min_avail)
+ min_avail = avail;
+ plane_data[px].data = sbufptr(s);
+ plane_data[px].size = avail;
+ }
+
+ /*
+ * Now pass the available buffered data to the image processor.
+ * Even if there is no available data, we must call
+ * gs_image_next_planes one more time to finish processing any
+ * retained data.
+ */
+
+ {
+ int pi;
+ uint used[GS_IMAGE_MAX_COMPONENTS];
+
+ code = gs_image_next_planes(penum, plane_data, used);
+ /* Now that used has been set, update the streams. */
+ for (pi = 0, pp = ETOP_SOURCE(esp, 0); pi < num_sources;
+ ++pi, pp -= 2
+ )
+ sbufskip(pp->value.pfile, used[pi]);
+ if (code == e_RemapColor)
+ return code;
+ }
+ if (at_eof)
+ code = 1;
+ if (code) {
+ int code1;
+
+ esp = zimage_pop_estack(esp);
+ code1 = image_cleanup(i_ctx_p);
+ return (code < 0 ? code : code1 < 0 ? code1 : o_pop_estack);
+ }
+ }
+}
+
+/* Process data from an image with string data sources. */
+/* This may still encounter a RemapColor callback. */
+static int
+image_string_continue(i_ctx_t *i_ctx_p)
+{
+ gs_image_enum *penum = r_ptr(esp, gs_image_enum);
+ int num_sources = ETOP_NUM_SOURCES(esp)->value.intval;
+ gs_const_string sources[GS_IMAGE_MAX_COMPONENTS];
+ uint used[GS_IMAGE_MAX_COMPONENTS];
+
+ /* Pass no data initially, to find out how much is retained. */
+ memset(sources, 0, sizeof(sources[0]) * num_sources);
+ for (;;) {
+ int px;
+ int code = gs_image_next_planes(penum, sources, used);
+
+ if (code == e_RemapColor)
+ return code;
+ stop_now:
+ if (code) { /* Stop now. */
+ esp -= NUM_PUSH(num_sources);
+ image_cleanup(i_ctx_p);
+ return (code < 0 ? code : o_pop_estack);
+ }
+ for (px = 0; px < num_sources; ++px)
+ if (sources[px].size == 0) {
+ const ref *psrc = ETOP_SOURCE(esp, px);
+ uint size = r_size(psrc);
+
+ if (size == 0) { /* empty source */
+ code = 1;
+ goto stop_now;
+ }
+ sources[px].data = psrc->value.bytes;
+ sources[px].size = size;
+ }
+ }
+}
+
+/* Clean up after enumerating an image */
+static int
+image_cleanup(i_ctx_t *i_ctx_p)
+{
+ es_ptr ep_top = esp + NUM_PUSH(EBOT_NUM_SOURCES(esp)->value.intval);
+ gs_image_enum *penum = r_ptr(ep_top, gs_image_enum);
+
+ return gs_image_cleanup_and_free_enum(penum, igs);
+}
+
+/* ------ Initialization procedure ------ */
+
+const op_def zimage_op_defs[] =
+{
+ {"1.image1", zimage1},
+ {"1.imagemask1", zimagemask1},
+ /* Internal operators */
+ {"1%image_proc_continue", image_proc_continue},
+ {"0%image_file_continue", image_file_continue},
+ {"0%image_string_continue", image_string_continue},
+ op_def_end(0)
+};