xref: /plan9-contrib/sys/src/cmd/gs/src/zimage.c (revision 593dc095aefb2a85c828727bbfa9da139a49bdf4)
17dd7cddfSDavid du Colombier /* Copyright (C) 1989, 1995, 1996, 1997, 1998, 1999, 2000 Aladdin Enterprises.  All rights reserved.
27dd7cddfSDavid du Colombier 
3*593dc095SDavid du Colombier   This software is provided AS-IS with no warranty, either express or
4*593dc095SDavid du Colombier   implied.
57dd7cddfSDavid du Colombier 
6*593dc095SDavid du Colombier   This software is distributed under license and may not be copied,
7*593dc095SDavid du Colombier   modified or distributed except as expressly authorized under the terms
8*593dc095SDavid du Colombier   of the license contained in the file LICENSE in this distribution.
97dd7cddfSDavid du Colombier 
10*593dc095SDavid du Colombier   For more information about licensing, please refer to
11*593dc095SDavid du Colombier   http://www.ghostscript.com/licensing/. For information on
12*593dc095SDavid du Colombier   commercial licensing, go to http://www.artifex.com/licensing/ or
13*593dc095SDavid du Colombier   contact Artifex Software, Inc., 101 Lucas Valley Road #110,
14*593dc095SDavid du Colombier   San Rafael, CA  94903, U.S.A., +1(415)492-9861.
157dd7cddfSDavid du Colombier */
167dd7cddfSDavid du Colombier 
17*593dc095SDavid du Colombier /* $Id: zimage.c,v 1.15 2005/06/15 18:40:08 igor Exp $ */
187dd7cddfSDavid du Colombier /* Image operators */
19*593dc095SDavid du Colombier #include "math_.h"
207dd7cddfSDavid du Colombier #include "memory_.h"
217dd7cddfSDavid du Colombier #include "ghost.h"
227dd7cddfSDavid du Colombier #include "oper.h"
23*593dc095SDavid du Colombier #include "gscolor.h"
24*593dc095SDavid du Colombier #include "gscspace.h"
25*593dc095SDavid du Colombier #include "gscolor2.h"
26*593dc095SDavid du Colombier #include "gsmatrix.h"
27*593dc095SDavid du Colombier #include "gsimage.h"
28*593dc095SDavid du Colombier #include "gxfixed.h"
297dd7cddfSDavid du Colombier #include "gsstruct.h"
30*593dc095SDavid du Colombier #include "gxiparam.h"
31*593dc095SDavid du Colombier #include "idict.h"
32*593dc095SDavid du Colombier #include "idparam.h"
33*593dc095SDavid du Colombier #include "estack.h"		/* for image[mask] */
347dd7cddfSDavid du Colombier #include "ialloc.h"
357dd7cddfSDavid du Colombier #include "igstate.h"
367dd7cddfSDavid du Colombier #include "ilevel.h"
377dd7cddfSDavid du Colombier #include "store.h"
387dd7cddfSDavid du Colombier #include "stream.h"
397dd7cddfSDavid du Colombier #include "ifilter.h"		/* for stream exception handling */
407dd7cddfSDavid du Colombier #include "iimage.h"
417dd7cddfSDavid du Colombier 
427dd7cddfSDavid du Colombier /* Forward references */
43*593dc095SDavid du Colombier private int zimage_data_setup(i_ctx_t *i_ctx_p, const gs_pixel_image_t * pim,
44*593dc095SDavid du Colombier 				 gx_image_enum_common_t * pie,
45*593dc095SDavid du Colombier 				 const ref * sources, int npop);
46*593dc095SDavid du Colombier private int image_proc_process(i_ctx_t *);
47*593dc095SDavid du Colombier private int image_file_continue(i_ctx_t *);
48*593dc095SDavid du Colombier private int image_string_continue(i_ctx_t *);
49*593dc095SDavid du Colombier private int image_cleanup(i_ctx_t *);
507dd7cddfSDavid du Colombier 
51*593dc095SDavid du Colombier 
52*593dc095SDavid du Colombier 
53*593dc095SDavid du Colombier /* Extract and check the parameters for a gs_data_image_t. */
547dd7cddfSDavid du Colombier 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)55*593dc095SDavid du Colombier data_image_params(const gs_memory_t *mem,
56*593dc095SDavid du Colombier 		  const ref *op, gs_data_image_t *pim,
57*593dc095SDavid du Colombier 		  image_params *pip, bool require_DataSource,
58*593dc095SDavid du Colombier 		  int num_components, int max_bits_per_component,
59*593dc095SDavid du Colombier 		  bool has_alpha)
607dd7cddfSDavid du Colombier {
617dd7cddfSDavid du Colombier     int code;
62*593dc095SDavid du Colombier     int decode_size;
63*593dc095SDavid du Colombier     ref *pds;
647dd7cddfSDavid du Colombier 
65*593dc095SDavid du Colombier     check_type(*op, t_dictionary);
66*593dc095SDavid du Colombier     check_dict_read(*op);
67*593dc095SDavid du Colombier     if ((code = dict_int_param(op, "Width", 0, max_int_in_fixed / 2,
68*593dc095SDavid du Colombier 			       -1, &pim->Width)) < 0 ||
69*593dc095SDavid du Colombier 	(code = dict_int_param(op, "Height", 0, max_int_in_fixed / 2,
70*593dc095SDavid du Colombier 			       -1, &pim->Height)) < 0 ||
71*593dc095SDavid du Colombier 	(code = dict_matrix_param(mem, op, "ImageMatrix",
72*593dc095SDavid du Colombier 				  &pim->ImageMatrix)) < 0 ||
73*593dc095SDavid du Colombier 	(code = dict_bool_param(op, "MultipleDataSources", false,
74*593dc095SDavid du Colombier 				&pip->MultipleDataSources)) < 0 ||
75*593dc095SDavid du Colombier 	(code = dict_int_param(op, "BitsPerComponent", 1,
76*593dc095SDavid du Colombier 			       max_bits_per_component, -1,
77*593dc095SDavid du Colombier 			       &pim->BitsPerComponent)) < 0 ||
78*593dc095SDavid du Colombier 	(code = decode_size = dict_floats_param(mem, op, "Decode",
79*593dc095SDavid du Colombier 						num_components * 2,
80*593dc095SDavid du Colombier 						&pim->Decode[0], NULL)) < 0 ||
81*593dc095SDavid du Colombier 	(code = dict_bool_param(op, "Interpolate", false,
82*593dc095SDavid du Colombier 				&pim->Interpolate)) < 0
83*593dc095SDavid du Colombier 	)
847dd7cddfSDavid du Colombier 	return code;
85*593dc095SDavid du Colombier     pip->pDecode = &pim->Decode[0];
86*593dc095SDavid du Colombier     /* Extract and check the data sources. */
87*593dc095SDavid du Colombier     if ((code = dict_find_string(op, "DataSource", &pds)) <= 0) {
88*593dc095SDavid du Colombier 	if (require_DataSource)
89*593dc095SDavid du Colombier 	    return (code < 0 ? code : gs_note_error(e_rangecheck));
90*593dc095SDavid du Colombier 	return 1;		/* no data source */
91*593dc095SDavid du Colombier     }
92*593dc095SDavid du Colombier     if (pip->MultipleDataSources) {
93*593dc095SDavid du Colombier 	long i, n = num_components + (has_alpha ? 1 : 0);
94*593dc095SDavid du Colombier         if (!r_is_array(pds))
95*593dc095SDavid du Colombier             return_error(e_typecheck);
96*593dc095SDavid du Colombier 	if (r_size(pds) != n)
97*593dc095SDavid du Colombier 	    return_error(e_rangecheck);
98*593dc095SDavid du Colombier 	for (i = 0; i < n; ++i)
99*593dc095SDavid du Colombier 	    array_get(mem, pds, i, &pip->DataSource[i]);
100*593dc095SDavid du Colombier     } else
101*593dc095SDavid du Colombier 	pip->DataSource[0] = *pds;
102*593dc095SDavid du Colombier     return 0;
103*593dc095SDavid du Colombier }
104*593dc095SDavid du Colombier 
105*593dc095SDavid du Colombier /* Extract and check the parameters for a gs_pixel_image_t. */
106*593dc095SDavid du Colombier 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)107*593dc095SDavid du Colombier pixel_image_params(i_ctx_t *i_ctx_p, const ref *op, gs_pixel_image_t *pim,
108*593dc095SDavid du Colombier 		   image_params *pip, int max_bits_per_component,
109*593dc095SDavid du Colombier 		   bool has_alpha)
110*593dc095SDavid du Colombier {
111*593dc095SDavid du Colombier     int num_components =
112*593dc095SDavid du Colombier 	gs_color_space_num_components(gs_currentcolorspace(igs));
113*593dc095SDavid du Colombier     int code;
114*593dc095SDavid du Colombier 
115*593dc095SDavid du Colombier     if (num_components < 1)
116*593dc095SDavid du Colombier 	return_error(e_rangecheck);	/* Pattern space not allowed */
117*593dc095SDavid du Colombier     pim->ColorSpace = gs_currentcolorspace(igs);
118*593dc095SDavid du Colombier     code = data_image_params(imemory, op, (gs_data_image_t *) pim, pip, true,
119*593dc095SDavid du Colombier 			     num_components, max_bits_per_component,
120*593dc095SDavid du Colombier 			     has_alpha);
121*593dc095SDavid du Colombier     if (code < 0)
122*593dc095SDavid du Colombier 	return code;
123*593dc095SDavid du Colombier     pim->format =
124*593dc095SDavid du Colombier 	(pip->MultipleDataSources ? gs_image_format_component_planar :
125*593dc095SDavid du Colombier 	 gs_image_format_chunky);
126*593dc095SDavid du Colombier     return dict_bool_param(op, "CombineWithColor", false,
127*593dc095SDavid du Colombier 			   &pim->CombineWithColor);
1287dd7cddfSDavid du Colombier }
1297dd7cddfSDavid du Colombier 
1307dd7cddfSDavid du Colombier /* Common setup for all Level 1 and 2 images, and ImageType 4 images. */
1317dd7cddfSDavid du Colombier int
zimage_setup(i_ctx_t * i_ctx_p,const gs_pixel_image_t * pim,const ref * sources,bool uses_color,int npop)1327dd7cddfSDavid du Colombier zimage_setup(i_ctx_t *i_ctx_p, const gs_pixel_image_t * pim,
1337dd7cddfSDavid du Colombier 	     const ref * sources, bool uses_color, int npop)
1347dd7cddfSDavid du Colombier {
1357dd7cddfSDavid du Colombier     gx_image_enum_common_t *pie;
1367dd7cddfSDavid du Colombier     int code =
1377dd7cddfSDavid du Colombier 	gs_image_begin_typed((const gs_image_common_t *)pim, igs,
1387dd7cddfSDavid du Colombier 			     uses_color, &pie);
1397dd7cddfSDavid du Colombier 
1407dd7cddfSDavid du Colombier     if (code < 0)
1417dd7cddfSDavid du Colombier 	return code;
1427dd7cddfSDavid du Colombier     return zimage_data_setup(i_ctx_p, (const gs_pixel_image_t *)pim, pie,
1437dd7cddfSDavid du Colombier 			     sources, npop);
1447dd7cddfSDavid du Colombier }
1457dd7cddfSDavid du Colombier 
146*593dc095SDavid du Colombier /* Common code for .image1 and .alphaimage operators */
147*593dc095SDavid du Colombier int
image1_setup(i_ctx_t * i_ctx_p,bool has_alpha)148*593dc095SDavid du Colombier image1_setup(i_ctx_t * i_ctx_p, bool has_alpha)
149*593dc095SDavid du Colombier {
150*593dc095SDavid du Colombier     os_ptr          op = osp;
151*593dc095SDavid du Colombier     gs_image_t      image;
152*593dc095SDavid du Colombier     image_params    ip;
153*593dc095SDavid du Colombier     int             code;
154*593dc095SDavid du Colombier 
155*593dc095SDavid du Colombier     gs_image_t_init(&image, gs_currentcolorspace(igs));
156*593dc095SDavid du Colombier     code = pixel_image_params( i_ctx_p,
157*593dc095SDavid du Colombier                                op,
158*593dc095SDavid du Colombier                                (gs_pixel_image_t *)&image,
159*593dc095SDavid du Colombier                                &ip,
160*593dc095SDavid du Colombier 			       (level2_enabled ? 16 : 8),
161*593dc095SDavid du Colombier                                has_alpha );
162*593dc095SDavid du Colombier     if (code < 0)
163*593dc095SDavid du Colombier 	return code;
164*593dc095SDavid du Colombier 
165*593dc095SDavid du Colombier     image.Alpha = (has_alpha ? gs_image_alpha_last : gs_image_alpha_none);
166*593dc095SDavid du Colombier     return zimage_setup( i_ctx_p,
167*593dc095SDavid du Colombier                          (gs_pixel_image_t *)&image,
168*593dc095SDavid du Colombier                          &ip.DataSource[0],
169*593dc095SDavid du Colombier 			 image.CombineWithColor,
170*593dc095SDavid du Colombier                          1 );
171*593dc095SDavid du Colombier }
172*593dc095SDavid du Colombier 
173*593dc095SDavid du Colombier /* <dict> .image1 - */
174*593dc095SDavid du Colombier private int
zimage1(i_ctx_t * i_ctx_p)175*593dc095SDavid du Colombier zimage1(i_ctx_t *i_ctx_p)
176*593dc095SDavid du Colombier {
177*593dc095SDavid du Colombier     return image1_setup(i_ctx_p, false);
178*593dc095SDavid du Colombier }
179*593dc095SDavid du Colombier 
180*593dc095SDavid du Colombier /* <dict> .imagemask1 - */
181*593dc095SDavid du Colombier private int
zimagemask1(i_ctx_t * i_ctx_p)182*593dc095SDavid du Colombier zimagemask1(i_ctx_t *i_ctx_p)
183*593dc095SDavid du Colombier {
184*593dc095SDavid du Colombier     os_ptr op = osp;
185*593dc095SDavid du Colombier     gs_image_t image;
186*593dc095SDavid du Colombier     image_params ip;
187*593dc095SDavid du Colombier     int code;
188*593dc095SDavid du Colombier 
189*593dc095SDavid du Colombier     gs_image_t_init_mask_adjust(&image, false,
190*593dc095SDavid du Colombier 				gs_incachedevice(igs) != CACHE_DEVICE_NONE);
191*593dc095SDavid du Colombier     code = data_image_params(imemory, op, (gs_data_image_t *) & image,
192*593dc095SDavid du Colombier 			     &ip, true, 1, 1, false);
193*593dc095SDavid du Colombier     if (code < 0)
194*593dc095SDavid du Colombier 	return code;
195*593dc095SDavid du Colombier     if (ip.MultipleDataSources)
196*593dc095SDavid du Colombier 	return_error(e_rangecheck);
197*593dc095SDavid du Colombier     return zimage_setup(i_ctx_p, (gs_pixel_image_t *)&image, &ip.DataSource[0],
198*593dc095SDavid du Colombier 			true, 1);
199*593dc095SDavid du Colombier }
200*593dc095SDavid du Colombier 
201*593dc095SDavid du Colombier 
2027dd7cddfSDavid du Colombier /* Common setup for all Level 1 and 2 images, and ImageType 3 and 4 images. */
2037dd7cddfSDavid du Colombier /*
2047dd7cddfSDavid du Colombier  * We push the following on the estack.
2057dd7cddfSDavid du Colombier  *      control mark,
2067dd7cddfSDavid du Colombier  *	num_sources,
2077dd7cddfSDavid du Colombier  *      for I = num_sources-1 ... 0:
2087dd7cddfSDavid du Colombier  *          data source I,
2097dd7cddfSDavid du Colombier  *          aliasing information:
2107dd7cddfSDavid du Colombier  *              if source is not file, 1, except that the topmost value
2117dd7cddfSDavid du Colombier  *		  is used for bookkeeping in the procedure case (see below);
2127dd7cddfSDavid du Colombier  *              if file is referenced by a total of M different sources and
2137dd7cddfSDavid du Colombier  *                this is the occurrence with the lowest I, M;
2147dd7cddfSDavid du Colombier  *              otherwise, -J, where J is the lowest I of the same file as
2157dd7cddfSDavid du Colombier  *                this one;
2167dd7cddfSDavid du Colombier  *      current plane index,
2177dd7cddfSDavid du Colombier  *      num_sources,
2187dd7cddfSDavid du Colombier  *      enumeration structure.
2197dd7cddfSDavid du Colombier  */
2207dd7cddfSDavid du Colombier #define NUM_PUSH(nsource) ((nsource) * 2 + 5)
2217dd7cddfSDavid du Colombier /*
2227dd7cddfSDavid du Colombier  * We can access these values either from the bottom (esp at control mark - 1,
2237dd7cddfSDavid du Colombier  * EBOT macros) or the top (esp = enumeration structure, ETOP macros).
2247dd7cddfSDavid du Colombier  * Note that all macros return pointers.
2257dd7cddfSDavid du Colombier  */
2267dd7cddfSDavid du Colombier #define EBOT_NUM_SOURCES(ep) ((ep) + 2)
2277dd7cddfSDavid du Colombier #define EBOT_SOURCE(ep, i)\
2287dd7cddfSDavid du Colombier   ((ep) + 3 + (EBOT_NUM_SOURCES(ep)->value.intval - 1 - (i)) * 2)
2297dd7cddfSDavid du Colombier #define ETOP_SOURCE(ep, i)\
2307dd7cddfSDavid du Colombier   ((ep) - 4 - (i) * 2)
2317dd7cddfSDavid du Colombier #define ETOP_PLANE_INDEX(ep) ((ep) - 2)
2327dd7cddfSDavid du Colombier #define ETOP_NUM_SOURCES(ep) ((ep) - 1)
233*593dc095SDavid du Colombier private 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)2347dd7cddfSDavid du Colombier zimage_data_setup(i_ctx_t *i_ctx_p, const gs_pixel_image_t * pim,
2357dd7cddfSDavid du Colombier 		  gx_image_enum_common_t * pie, const ref * sources, int npop)
2367dd7cddfSDavid du Colombier {
2377dd7cddfSDavid du Colombier     int num_sources = pie->num_planes;
2387dd7cddfSDavid du Colombier     int inumpush = NUM_PUSH(num_sources);
2397dd7cddfSDavid du Colombier     int code;
2407dd7cddfSDavid du Colombier     gs_image_enum *penum;
2417dd7cddfSDavid du Colombier     int px;
2427dd7cddfSDavid du Colombier     const ref *pp;
2437dd7cddfSDavid du Colombier 
2447dd7cddfSDavid du Colombier     check_estack(inumpush + 2);	/* stuff above, + continuation + proc */
2457dd7cddfSDavid du Colombier     make_int(EBOT_NUM_SOURCES(esp), num_sources);
2467dd7cddfSDavid du Colombier     /*
2477dd7cddfSDavid du Colombier      * Note that the data sources may be procedures, strings, or (Level
2487dd7cddfSDavid du Colombier      * 2 only) files.  (The Level 1 reference manual says that Level 1
2497dd7cddfSDavid du Colombier      * requires procedures, but Adobe Level 1 interpreters also accept
2507dd7cddfSDavid du Colombier      * strings.)  The sources must all be of the same type.
2517dd7cddfSDavid du Colombier      *
2527dd7cddfSDavid du Colombier      * The Adobe documentation explicitly says that if two or more of the
2537dd7cddfSDavid du Colombier      * data sources are the same or inter-dependent files, the result is not
2547dd7cddfSDavid du Colombier      * defined.  We don't have a problem with the bookkeeping for
2557dd7cddfSDavid du Colombier      * inter-dependent files, since each one has its own buffer, but we do
2567dd7cddfSDavid du Colombier      * have to be careful if two or more sources are actually the same file.
2577dd7cddfSDavid du Colombier      * That is the reason for the aliasing information described above.
2587dd7cddfSDavid du Colombier      */
2597dd7cddfSDavid du Colombier     for (px = 0, pp = sources; px < num_sources; px++, pp++) {
2607dd7cddfSDavid du Colombier 	es_ptr ep = EBOT_SOURCE(esp, px);
2617dd7cddfSDavid du Colombier 
2627dd7cddfSDavid du Colombier 	make_int(ep + 1, 1);	/* default is no aliasing */
2637dd7cddfSDavid du Colombier 	switch (r_type(pp)) {
2647dd7cddfSDavid du Colombier 	    case t_file:
2657dd7cddfSDavid du Colombier 		if (!level2_enabled)
2667dd7cddfSDavid du Colombier 		    return_error(e_typecheck);
2677dd7cddfSDavid du Colombier 		/* Check for aliasing. */
2687dd7cddfSDavid du Colombier 		{
2697dd7cddfSDavid du Colombier 		    int pi;
2707dd7cddfSDavid du Colombier 
2717dd7cddfSDavid du Colombier 		    for (pi = 0; pi < px; ++pi)
2727dd7cddfSDavid du Colombier 			if (sources[pi].value.pfile == pp->value.pfile) {
2737dd7cddfSDavid du Colombier 			    /* Record aliasing */
2747dd7cddfSDavid du Colombier 			    make_int(ep + 1, -pi);
2753ff48bf5SDavid du Colombier 			    EBOT_SOURCE(esp, pi)[1].value.intval++;
2767dd7cddfSDavid du Colombier 			    break;
2777dd7cddfSDavid du Colombier 			}
2787dd7cddfSDavid du Colombier 		}
2797dd7cddfSDavid du Colombier 		/* falls through */
2807dd7cddfSDavid du Colombier 	    case t_string:
281*593dc095SDavid du Colombier 		if (r_type(pp) != r_type(sources)) {
282*593dc095SDavid du Colombier     		    if (pie != NULL)
283*593dc095SDavid du Colombier 		        gx_image_end(pie, false);    /* Clean up pie */
2847dd7cddfSDavid du Colombier 		    return_error(e_typecheck);
285*593dc095SDavid du Colombier 		}
2867dd7cddfSDavid du Colombier 		check_read(*pp);
2877dd7cddfSDavid du Colombier 		break;
2887dd7cddfSDavid du Colombier 	    default:
289*593dc095SDavid du Colombier 		if (!r_is_proc(sources)) {
290*593dc095SDavid du Colombier     		    if (pie != NULL)
291*593dc095SDavid du Colombier 		        gx_image_end(pie, false);    /* Clean up pie */
2927dd7cddfSDavid du Colombier 		    return_error(e_typecheck);
293*593dc095SDavid du Colombier 		}
2947dd7cddfSDavid du Colombier 		check_proc(*pp);
2957dd7cddfSDavid du Colombier 	}
2967dd7cddfSDavid du Colombier 	*ep = *pp;
2977dd7cddfSDavid du Colombier     }
298*593dc095SDavid du Colombier     /* Always place the image enumerator into local memory,
299*593dc095SDavid du Colombier        because pie may have local objects inherited from igs,
300*593dc095SDavid du Colombier        which may be local when the current allocation mode is global.
301*593dc095SDavid du Colombier        Bug 688140. */
302*593dc095SDavid du Colombier     if ((penum = gs_image_enum_alloc(imemory_local, "image_setup")) == 0)
3037dd7cddfSDavid du Colombier 	return_error(e_VMerror);
3047dd7cddfSDavid du Colombier     code = gs_image_enum_init(penum, pie, (const gs_data_image_t *)pim, igs);
3057dd7cddfSDavid du Colombier     if (code != 0) {		/* error, or empty image */
306*593dc095SDavid du Colombier 	int code1 = gs_image_cleanup_and_free_enum(penum);
307*593dc095SDavid du Colombier 
3087dd7cddfSDavid du Colombier 	if (code >= 0)		/* empty image */
3097dd7cddfSDavid du Colombier 	    pop(npop);
310*593dc095SDavid du Colombier 	if (code >= 0 && code1 < 0)
311*593dc095SDavid du Colombier 	    code = code1;
3127dd7cddfSDavid du Colombier 	return code;
3137dd7cddfSDavid du Colombier     }
3147dd7cddfSDavid du Colombier     push_mark_estack(es_other, image_cleanup);
3157dd7cddfSDavid du Colombier     esp += inumpush - 1;
3167dd7cddfSDavid du Colombier     make_int(ETOP_PLANE_INDEX(esp), 0);
3177dd7cddfSDavid du Colombier     make_int(ETOP_NUM_SOURCES(esp), num_sources);
318*593dc095SDavid du Colombier     make_struct(esp, avm_local, penum);
3197dd7cddfSDavid du Colombier     switch (r_type(sources)) {
3207dd7cddfSDavid du Colombier 	case t_file:
3217dd7cddfSDavid du Colombier 	    push_op_estack(image_file_continue);
3227dd7cddfSDavid du Colombier 	    break;
3237dd7cddfSDavid du Colombier 	case t_string:
3247dd7cddfSDavid du Colombier 	    push_op_estack(image_string_continue);
3257dd7cddfSDavid du Colombier 	    break;
3267dd7cddfSDavid du Colombier 	default:		/* procedure */
3277dd7cddfSDavid du Colombier 	    push_op_estack(image_proc_process);
3287dd7cddfSDavid du Colombier 	    break;
3297dd7cddfSDavid du Colombier     }
3307dd7cddfSDavid du Colombier     pop(npop);
3317dd7cddfSDavid du Colombier     return o_push_estack;
3327dd7cddfSDavid du Colombier }
3337dd7cddfSDavid du Colombier /* Pop all the control information off the e-stack. */
3347dd7cddfSDavid du Colombier private es_ptr
zimage_pop_estack(es_ptr tep)3357dd7cddfSDavid du Colombier zimage_pop_estack(es_ptr tep)
3367dd7cddfSDavid du Colombier {
3377dd7cddfSDavid du Colombier     return tep - NUM_PUSH(ETOP_NUM_SOURCES(tep)->value.intval);
3387dd7cddfSDavid du Colombier }
3397dd7cddfSDavid du Colombier 
3407dd7cddfSDavid du Colombier /*
3417dd7cddfSDavid du Colombier  * Continuation for procedure data source.  We use the topmost aliasing slot
3427dd7cddfSDavid du Colombier  * to remember whether we've just called the procedure (1) or whether we're
3437dd7cddfSDavid du Colombier  * returning from a RemapColor callout (0).
3447dd7cddfSDavid du Colombier  */
3457dd7cddfSDavid du Colombier private int
image_proc_continue(i_ctx_t * i_ctx_p)3467dd7cddfSDavid du Colombier image_proc_continue(i_ctx_t *i_ctx_p)
3477dd7cddfSDavid du Colombier {
3487dd7cddfSDavid du Colombier     os_ptr op = osp;
3497dd7cddfSDavid du Colombier     gs_image_enum *penum = r_ptr(esp, gs_image_enum);
3507dd7cddfSDavid du Colombier     int px = ETOP_PLANE_INDEX(esp)->value.intval;
3517dd7cddfSDavid du Colombier     int num_sources = ETOP_NUM_SOURCES(esp)->value.intval;
3527dd7cddfSDavid du Colombier     uint size, used[gs_image_max_planes];
3537dd7cddfSDavid du Colombier     gs_const_string plane_data[gs_image_max_planes];
3547dd7cddfSDavid du Colombier     const byte *wanted;
3557dd7cddfSDavid du Colombier     int i, code;
3567dd7cddfSDavid du Colombier 
3577dd7cddfSDavid du Colombier     if (!r_has_type_attrs(op, t_string, a_read)) {
3587dd7cddfSDavid du Colombier 	check_op(1);
3597dd7cddfSDavid du Colombier 	/* Procedure didn't return a (readable) string.  Quit. */
3607dd7cddfSDavid du Colombier 	esp = zimage_pop_estack(esp);
3617dd7cddfSDavid du Colombier 	image_cleanup(i_ctx_p);
3627dd7cddfSDavid du Colombier 	return_error(!r_has_type(op, t_string) ? e_typecheck : e_invalidaccess);
3637dd7cddfSDavid du Colombier     }
3647dd7cddfSDavid du Colombier     size = r_size(op);
3657dd7cddfSDavid du Colombier     if (size == 0 && ETOP_SOURCE(esp, 0)[1].value.intval == 0)
3667dd7cddfSDavid du Colombier 	code = 1;
3677dd7cddfSDavid du Colombier     else {
3687dd7cddfSDavid du Colombier 	for (i = 0; i < num_sources; i++)
3697dd7cddfSDavid du Colombier 	    plane_data[i].size = 0;
3707dd7cddfSDavid du Colombier 	plane_data[px].data = op->value.bytes;
3717dd7cddfSDavid du Colombier 	plane_data[px].size = size;
3727dd7cddfSDavid du Colombier 	code = gs_image_next_planes(penum, plane_data, used);
3737dd7cddfSDavid du Colombier 	if (code == e_RemapColor) {
3747dd7cddfSDavid du Colombier 	    op->value.bytes += used[px]; /* skip used data */
3757dd7cddfSDavid du Colombier 	    r_dec_size(op, used[px]);
3767dd7cddfSDavid du Colombier 	    ETOP_SOURCE(esp, 0)[1].value.intval = 0; /* RemapColor callout */
3777dd7cddfSDavid du Colombier 	    return code;
3787dd7cddfSDavid du Colombier 	}
3797dd7cddfSDavid du Colombier     }
3807dd7cddfSDavid du Colombier     if (code) {			/* Stop now. */
3817dd7cddfSDavid du Colombier 	esp = zimage_pop_estack(esp);
3827dd7cddfSDavid du Colombier 	pop(1);
3837dd7cddfSDavid du Colombier 	image_cleanup(i_ctx_p);
3847dd7cddfSDavid du Colombier 	return (code < 0 ? code : o_pop_estack);
3857dd7cddfSDavid du Colombier     }
3867dd7cddfSDavid du Colombier     pop(1);
3877dd7cddfSDavid du Colombier     wanted = gs_image_planes_wanted(penum);
3887dd7cddfSDavid du Colombier     do {
3897dd7cddfSDavid du Colombier 	if (++px == num_sources)
3907dd7cddfSDavid du Colombier 	    px = 0;
3917dd7cddfSDavid du Colombier     } while (!wanted[px]);
3927dd7cddfSDavid du Colombier     ETOP_PLANE_INDEX(esp)->value.intval = px;
3937dd7cddfSDavid du Colombier     return image_proc_process(i_ctx_p);
3947dd7cddfSDavid du Colombier }
3957dd7cddfSDavid du Colombier private int
image_proc_process(i_ctx_t * i_ctx_p)3967dd7cddfSDavid du Colombier image_proc_process(i_ctx_t *i_ctx_p)
3977dd7cddfSDavid du Colombier {
3987dd7cddfSDavid du Colombier     int px = ETOP_PLANE_INDEX(esp)->value.intval;
3997dd7cddfSDavid du Colombier     gs_image_enum *penum = r_ptr(esp, gs_image_enum);
4007dd7cddfSDavid du Colombier     const byte *wanted = gs_image_planes_wanted(penum);
4017dd7cddfSDavid du Colombier     int num_sources = ETOP_NUM_SOURCES(esp)->value.intval;
4027dd7cddfSDavid du Colombier     const ref *pp;
4037dd7cddfSDavid du Colombier 
4047dd7cddfSDavid du Colombier     ETOP_SOURCE(esp, 0)[1].value.intval = 0; /* procedure callout */
4057dd7cddfSDavid du Colombier     while (!wanted[px]) {
4067dd7cddfSDavid du Colombier 	if (++px == num_sources)
4077dd7cddfSDavid du Colombier 	    px = 0;
4087dd7cddfSDavid du Colombier 	ETOP_PLANE_INDEX(esp)->value.intval = px;
4097dd7cddfSDavid du Colombier     }
4107dd7cddfSDavid du Colombier     pp = ETOP_SOURCE(esp, px);
4117dd7cddfSDavid du Colombier     push_op_estack(image_proc_continue);
4127dd7cddfSDavid du Colombier     *++esp = *pp;
4137dd7cddfSDavid du Colombier     return o_push_estack;
4147dd7cddfSDavid du Colombier }
4157dd7cddfSDavid du Colombier 
4167dd7cddfSDavid du Colombier /* Continue processing data from an image with file data sources. */
4177dd7cddfSDavid du Colombier private int
image_file_continue(i_ctx_t * i_ctx_p)4187dd7cddfSDavid du Colombier image_file_continue(i_ctx_t *i_ctx_p)
4197dd7cddfSDavid du Colombier {
4207dd7cddfSDavid du Colombier     gs_image_enum *penum = r_ptr(esp, gs_image_enum);
4217dd7cddfSDavid du Colombier     int num_sources = ETOP_NUM_SOURCES(esp)->value.intval;
4227dd7cddfSDavid du Colombier 
4237dd7cddfSDavid du Colombier     for (;;) {
4247dd7cddfSDavid du Colombier 	uint min_avail = max_int;
4257dd7cddfSDavid du Colombier 	gs_const_string plane_data[gs_image_max_planes];
4267dd7cddfSDavid du Colombier 	int code;
4277dd7cddfSDavid du Colombier 	int px;
4287dd7cddfSDavid du Colombier 	const ref *pp;
4297dd7cddfSDavid du Colombier 	bool at_eof = false;
4307dd7cddfSDavid du Colombier 
4317dd7cddfSDavid du Colombier 	/*
4327dd7cddfSDavid du Colombier 	 * Do a first pass through the files to ensure that at least
4337dd7cddfSDavid du Colombier 	 * one has data available in its buffer.
4347dd7cddfSDavid du Colombier 	 */
4357dd7cddfSDavid du Colombier 
4367dd7cddfSDavid du Colombier 	for (px = 0, pp = ETOP_SOURCE(esp, 0); px < num_sources;
4377dd7cddfSDavid du Colombier 	     ++px, pp -= 2
4387dd7cddfSDavid du Colombier 	    ) {
4397dd7cddfSDavid du Colombier 	    int num_aliases = pp[1].value.intval;
4407dd7cddfSDavid du Colombier 	    stream *s = pp->value.pfile;
4417dd7cddfSDavid du Colombier 	    int min_left;
4427dd7cddfSDavid du Colombier 	    uint avail;
4437dd7cddfSDavid du Colombier 
4447dd7cddfSDavid du Colombier 	    if (num_aliases <= 0)
4453ff48bf5SDavid du Colombier 		num_aliases = ETOP_SOURCE(esp, -num_aliases)[1].value.intval;
4467dd7cddfSDavid du Colombier 	    while ((avail = sbufavailable(s)) <=
4477dd7cddfSDavid du Colombier 		   (min_left = sbuf_min_left(s)) + num_aliases - 1) {
4487dd7cddfSDavid du Colombier 		int next = s->end_status;
4497dd7cddfSDavid du Colombier 
4507dd7cddfSDavid du Colombier 		switch (next) {
4517dd7cddfSDavid du Colombier 		case 0:
4527dd7cddfSDavid du Colombier 		    s_process_read_buf(s);
4537dd7cddfSDavid du Colombier 		    continue;
4547dd7cddfSDavid du Colombier 		case EOFC:
4557dd7cddfSDavid du Colombier 		    at_eof = true;
4567dd7cddfSDavid du Colombier 		    break;	/* with no data available */
4577dd7cddfSDavid du Colombier 		case INTC:
4587dd7cddfSDavid du Colombier 		case CALLC:
4597dd7cddfSDavid du Colombier 		    return
4607dd7cddfSDavid du Colombier 			s_handle_read_exception(i_ctx_p, next, pp,
4617dd7cddfSDavid du Colombier 						NULL, 0, image_file_continue);
4627dd7cddfSDavid du Colombier 		default:
4637dd7cddfSDavid du Colombier 		    /* case ERRC: */
4647dd7cddfSDavid du Colombier 		    return_error(e_ioerror);
4657dd7cddfSDavid du Colombier 		}
4667dd7cddfSDavid du Colombier 		break;		/* for EOFC */
4677dd7cddfSDavid du Colombier 	    }
4687dd7cddfSDavid du Colombier 	    /*
4697dd7cddfSDavid du Colombier 	     * Note that in the EOF case, we can get here with no data
4707dd7cddfSDavid du Colombier 	     * available.
4717dd7cddfSDavid du Colombier 	     */
4727dd7cddfSDavid du Colombier 	    if (avail >= min_left)
4737dd7cddfSDavid du Colombier 		avail = (avail - min_left) / num_aliases; /* may be 0 */
4747dd7cddfSDavid du Colombier 	    if (avail < min_avail)
4757dd7cddfSDavid du Colombier 		min_avail = avail;
4767dd7cddfSDavid du Colombier 	    plane_data[px].data = sbufptr(s);
4777dd7cddfSDavid du Colombier 	    plane_data[px].size = avail;
4787dd7cddfSDavid du Colombier 	}
4797dd7cddfSDavid du Colombier 
4807dd7cddfSDavid du Colombier 	/*
4817dd7cddfSDavid du Colombier 	 * Now pass the available buffered data to the image processor.
4827dd7cddfSDavid du Colombier 	 * Even if there is no available data, we must call
4837dd7cddfSDavid du Colombier 	 * gs_image_next_planes one more time to finish processing any
4847dd7cddfSDavid du Colombier 	 * retained data.
4857dd7cddfSDavid du Colombier 	 */
4867dd7cddfSDavid du Colombier 
4877dd7cddfSDavid du Colombier 	{
4887dd7cddfSDavid du Colombier 	    int pi;
4897dd7cddfSDavid du Colombier 	    uint used[gs_image_max_planes];
4907dd7cddfSDavid du Colombier 
4917dd7cddfSDavid du Colombier 	    code = gs_image_next_planes(penum, plane_data, used);
4927dd7cddfSDavid du Colombier 	    /* Now that used has been set, update the streams. */
4937dd7cddfSDavid du Colombier 	    for (pi = 0, pp = ETOP_SOURCE(esp, 0); pi < num_sources;
4947dd7cddfSDavid du Colombier 		 ++pi, pp -= 2
4957dd7cddfSDavid du Colombier 		 )
4967dd7cddfSDavid du Colombier 		sbufskip(pp->value.pfile, used[pi]);
4977dd7cddfSDavid du Colombier 	    if (code == e_RemapColor)
4987dd7cddfSDavid du Colombier 		return code;
4997dd7cddfSDavid du Colombier 	}
5007dd7cddfSDavid du Colombier 	if (at_eof)
5017dd7cddfSDavid du Colombier 	    code = 1;
5027dd7cddfSDavid du Colombier 	if (code) {
503*593dc095SDavid du Colombier 	    int code1;
504*593dc095SDavid du Colombier 
5057dd7cddfSDavid du Colombier 	    esp = zimage_pop_estack(esp);
506*593dc095SDavid du Colombier 	    code1 = image_cleanup(i_ctx_p);
507*593dc095SDavid du Colombier 	    return (code < 0 ? code : code1 < 0 ? code1 : o_pop_estack);
5087dd7cddfSDavid du Colombier 	}
5097dd7cddfSDavid du Colombier     }
5107dd7cddfSDavid du Colombier }
5117dd7cddfSDavid du Colombier 
5127dd7cddfSDavid du Colombier /* Process data from an image with string data sources. */
5137dd7cddfSDavid du Colombier /* This may still encounter a RemapColor callback. */
5147dd7cddfSDavid du Colombier private int
image_string_continue(i_ctx_t * i_ctx_p)5157dd7cddfSDavid du Colombier image_string_continue(i_ctx_t *i_ctx_p)
5167dd7cddfSDavid du Colombier {
5177dd7cddfSDavid du Colombier     gs_image_enum *penum = r_ptr(esp, gs_image_enum);
5187dd7cddfSDavid du Colombier     int num_sources = ETOP_NUM_SOURCES(esp)->value.intval;
5197dd7cddfSDavid du Colombier     gs_const_string sources[gs_image_max_planes];
5207dd7cddfSDavid du Colombier     uint used[gs_image_max_planes];
5217dd7cddfSDavid du Colombier 
5227dd7cddfSDavid du Colombier     /* Pass no data initially, to find out how much is retained. */
5237dd7cddfSDavid du Colombier     memset(sources, 0, sizeof(sources[0]) * num_sources);
5247dd7cddfSDavid du Colombier     for (;;) {
5257dd7cddfSDavid du Colombier 	int px;
5267dd7cddfSDavid du Colombier 	int code = gs_image_next_planes(penum, sources, used);
5277dd7cddfSDavid du Colombier 
5287dd7cddfSDavid du Colombier 	if (code == e_RemapColor)
5297dd7cddfSDavid du Colombier 	    return code;
5303ff48bf5SDavid du Colombier     stop_now:
5317dd7cddfSDavid du Colombier 	if (code) {		/* Stop now. */
5327dd7cddfSDavid du Colombier 	    esp -= NUM_PUSH(num_sources);
5337dd7cddfSDavid du Colombier 	    image_cleanup(i_ctx_p);
5347dd7cddfSDavid du Colombier 	    return (code < 0 ? code : o_pop_estack);
5357dd7cddfSDavid du Colombier 	}
5367dd7cddfSDavid du Colombier 	for (px = 0; px < num_sources; ++px)
5377dd7cddfSDavid du Colombier 	    if (sources[px].size == 0) {
5387dd7cddfSDavid du Colombier 		const ref *psrc = ETOP_SOURCE(esp, px);
5393ff48bf5SDavid du Colombier 		uint size = r_size(psrc);
5407dd7cddfSDavid du Colombier 
5413ff48bf5SDavid du Colombier 		if (size == 0) {	    /* empty source */
5423ff48bf5SDavid du Colombier 		    code = 1;
5433ff48bf5SDavid du Colombier 		    goto stop_now;
5443ff48bf5SDavid du Colombier                 }
5457dd7cddfSDavid du Colombier 		sources[px].data = psrc->value.bytes;
5463ff48bf5SDavid du Colombier 		sources[px].size = size;
5477dd7cddfSDavid du Colombier 	    }
5487dd7cddfSDavid du Colombier     }
5497dd7cddfSDavid du Colombier }
5507dd7cddfSDavid du Colombier 
5517dd7cddfSDavid du Colombier /* Clean up after enumerating an image */
5527dd7cddfSDavid du Colombier private int
image_cleanup(i_ctx_t * i_ctx_p)5537dd7cddfSDavid du Colombier image_cleanup(i_ctx_t *i_ctx_p)
5547dd7cddfSDavid du Colombier {
5557dd7cddfSDavid du Colombier     es_ptr ep_top = esp + NUM_PUSH(EBOT_NUM_SOURCES(esp)->value.intval);
5567dd7cddfSDavid du Colombier     gs_image_enum *penum = r_ptr(ep_top, gs_image_enum);
5577dd7cddfSDavid du Colombier 
558*593dc095SDavid du Colombier     return gs_image_cleanup_and_free_enum(penum);
5597dd7cddfSDavid du Colombier }
5607dd7cddfSDavid du Colombier 
5617dd7cddfSDavid du Colombier /* ------ Initialization procedure ------ */
5627dd7cddfSDavid du Colombier 
5637dd7cddfSDavid du Colombier const op_def zimage_op_defs[] =
5647dd7cddfSDavid du Colombier {
565*593dc095SDavid du Colombier     {"1.image1", zimage1},
566*593dc095SDavid du Colombier     {"1.imagemask1", zimagemask1},
5677dd7cddfSDavid du Colombier 		/* Internal operators */
5687dd7cddfSDavid du Colombier     {"1%image_proc_continue", image_proc_continue},
5697dd7cddfSDavid du Colombier     {"0%image_file_continue", image_file_continue},
5707dd7cddfSDavid du Colombier     {"0%image_string_continue", image_string_continue},
5717dd7cddfSDavid du Colombier     op_def_end(0)
5727dd7cddfSDavid du Colombier };
573