xref: /plan9/sys/src/cmd/gs/src/zfunc.c (revision 593dc095aefb2a85c828727bbfa9da139a49bdf4)
1 /* Copyright (C) 1997, 2000 Aladdin Enterprises.  All rights reserved.
2 
3   This software is provided AS-IS with no warranty, either express or
4   implied.
5 
6   This software is distributed under license and may not be copied,
7   modified or distributed except as expressly authorized under the terms
8   of the license contained in the file LICENSE in this distribution.
9 
10   For more information about licensing, please refer to
11   http://www.ghostscript.com/licensing/. For information on
12   commercial licensing, go to http://www.artifex.com/licensing/ or
13   contact Artifex Software, Inc., 101 Lucas Valley Road #110,
14   San Rafael, CA  94903, U.S.A., +1(415)492-9861.
15 */
16 
17 /* $Id: zfunc.c,v 1.14 2004/08/04 19:36:13 stefan Exp $ */
18 /* Generic PostScript language interface to Functions */
19 #include "memory_.h"
20 #include "ghost.h"
21 #include "oper.h"
22 #include "gscdefs.h"
23 #include "gsfunc.h"
24 #include "gsstruct.h"
25 #include "ialloc.h"
26 #include "idict.h"
27 #include "idparam.h"
28 #include "ifunc.h"
29 #include "store.h"
30 
31 /*#define TEST*/
32 
33 /* Define the maximum depth of nesting of subsidiary functions. */
34 #define MAX_SUB_FUNCTION_DEPTH 3
35 
36 /* ------ Operators ------ */
37 
38 /* Create a function procedure from a function structure. */
39 private int
make_function_proc(i_ctx_t * i_ctx_p,ref * op,gs_function_t * pfn)40 make_function_proc(i_ctx_t *i_ctx_p, ref *op, gs_function_t *pfn)
41 {
42     ref cref;			/* closure */
43     int code;
44 
45     code = ialloc_ref_array(&cref, a_executable | a_execute, 2,
46 			    ".buildfunction");
47     if (code < 0)
48 	return code;
49     make_istruct_new(cref.value.refs, a_executable | a_execute, pfn);
50     make_oper_new(cref.value.refs + 1, 0, zexecfunction);
51     ref_assign(op, &cref);
52     return 0;
53 }
54 
55 /* <dict> .buildfunction <function_proc> */
56 private int
zbuildfunction(i_ctx_t * i_ctx_p)57 zbuildfunction(i_ctx_t *i_ctx_p)
58 {
59     os_ptr op = osp;
60     gs_function_t *pfn;
61     int code = fn_build_function(i_ctx_p, op, &pfn, imemory);
62 
63     if (code < 0)
64 	return code;
65     code = make_function_proc(i_ctx_p, op, pfn);
66     if (code < 0)
67 	gs_function_free(pfn, true, imemory);
68     return 0;
69 }
70 
71 #ifdef TEST
72 
73 /* <function_proc> <array> .scalefunction <function_proc> */
74 private int
zscalefunction(i_ctx_t * i_ctx_p)75 zscalefunction(i_ctx_t *i_ctx_p)
76 {
77     os_ptr op = osp;
78     gs_function_t *pfn;
79     gs_function_t *psfn;
80     gs_range_t *ranges;
81     int code;
82     uint i;
83 
84     check_proc(op[-1]);
85     pfn = ref_function(op - 1);
86     if (pfn == 0 || !r_is_array(op))
87 	return_error(e_typecheck);
88     if (r_size(op) != 2 * pfn->params.n)
89 	return_error(e_rangecheck);
90     ranges = (gs_range_t *)
91 	gs_alloc_byte_array(imemory, pfn->params.n, sizeof(gs_range_t),
92 			    "zscalefunction");
93     if (ranges == 0)
94 	return_error(e_VMerror);
95     for (i = 0; i < pfn->params.n; ++i) {
96 	ref rval[2];
97 	float val[2];
98 
99 	if ((code = array_get(op, 2 * i, &rval[0])) < 0 ||
100 	    (code = array_get(op, 2 * i + 1, &rval[1])) < 0 ||
101 	    (code = float_params(rval + 1, 2, val)) < 0)
102 	    return code;
103 	ranges[i].rmin = val[0];
104 	ranges[i].rmax = val[1];
105     }
106     code = gs_function_make_scaled(pfn, &psfn, ranges, imemory);
107     gs_free_object(imemory, ranges, "zscalefunction");
108     if (code < 0 ||
109 	(code = make_function_proc(i_ctx_p, op - 1, psfn)) < 0) {
110 	gs_function_free(psfn, true, imemory);
111 	return code;
112     }
113     pop(1);
114     return 0;
115 }
116 
117 #endif /* TEST */
118 
119 /* <in1> ... <function_struct> %execfunction <out1> ... */
120 int
zexecfunction(i_ctx_t * i_ctx_p)121 zexecfunction(i_ctx_t *i_ctx_p)
122 {
123     os_ptr op = osp;
124 
125 	/*
126 	 * Since this operator's name begins with %, the name is not defined
127 	 * in systemdict.  The only place this operator can ever appear is
128 	 * in the execute-only closure created by .buildfunction.
129 	 * Therefore, in principle it is unnecessary to check the argument.
130 	 * However, we do a little checking anyway just on general
131 	 * principles.  Note that since the argument may be an instance of
132 	 * any subclass of gs_function_t, we currently have no way to check
133 	 * its type.
134 	 */
135     if (!r_is_struct(op) ||
136 	!r_has_masked_attrs(op, a_executable | a_execute, a_executable | a_all)
137 	)
138 	return_error(e_typecheck);
139     {
140 	gs_function_t *pfn = (gs_function_t *) op->value.pstruct;
141 	int m = pfn->params.m, n = pfn->params.n;
142 	int diff = n - (m + 1);
143 
144 	if (diff > 0)
145 	    check_ostack(diff);
146 	{
147 	    float params[20];	/* arbitrary size, just to avoid allocs */
148 	    float *in;
149 	    float *out;
150 	    int code = 0;
151 
152 	    if (m + n <= countof(params)) {
153 		in = params;
154 	    } else {
155 		in = (float *)ialloc_byte_array(m + n, sizeof(float),
156 						"%execfunction(in/out)");
157 		if (in == 0)
158 		    code = gs_note_error(e_VMerror);
159 	    }
160 	    out = in + m;
161 	    if (code < 0 ||
162 		(code = float_params(op - 1, m, in)) < 0 ||
163 		(code = gs_function_evaluate(pfn, in, out)) < 0
164 		)
165 		DO_NOTHING;
166 	    else {
167 		if (diff > 0)
168 		    push(diff);	/* can't fail */
169 		else if (diff < 0) {
170 		    pop(-diff);
171 		    op = osp;
172 		}
173 		code = make_floats(op + 1 - n, out, n);
174 	    }
175 	    if (in != params)
176 		ifree_object(in, "%execfunction(in)");
177 	    return code;
178 	}
179     }
180 }
181 
182 /*
183  * <proc> .isencapfunction <bool>
184  *
185  * This routine checks if a given Postscript procedure is an "encapsulated"
186  * function of the type made by .buildfunction.  These functions can then
187  * be executed without executing the interpreter.  These functions can be
188  * executed directly from within C code inside the graphics library.
189  */
190 private int
zisencapfunction(i_ctx_t * i_ctx_p)191 zisencapfunction(i_ctx_t *i_ctx_p)
192 {
193     os_ptr op = osp;
194     gs_function_t *pfn;
195 
196     check_proc(*op);
197     pfn = ref_function(op);
198     make_bool(op, pfn != NULL);
199     return 0;
200 }
201 
202 /* ------ Procedures ------ */
203 
204 /* Build a function structure from a PostScript dictionary. */
205 int
fn_build_function(i_ctx_t * i_ctx_p,const ref * op,gs_function_t ** ppfn,gs_memory_t * mem)206 fn_build_function(i_ctx_t *i_ctx_p, const ref * op, gs_function_t ** ppfn, gs_memory_t *mem)
207 {
208     return fn_build_sub_function(i_ctx_p, op, ppfn, 0, mem);
209 }
210 int
fn_build_sub_function(i_ctx_t * i_ctx_p,const ref * op,gs_function_t ** ppfn,int depth,gs_memory_t * mem)211 fn_build_sub_function(i_ctx_t *i_ctx_p, const ref * op, gs_function_t ** ppfn,
212 		      int depth, gs_memory_t *mem)
213 {
214     int code, type, i;
215     gs_function_params_t params;
216 
217     if (depth > MAX_SUB_FUNCTION_DEPTH)
218 	return_error(e_limitcheck);
219     check_type(*op, t_dictionary);
220     code = dict_int_param(op, "FunctionType", 0, max_int, -1, &type);
221     if (code < 0)
222 	return code;
223     for (i = 0; i < build_function_type_table_count; ++i)
224 	if (build_function_type_table[i].type == type)
225 	    break;
226     if (i == build_function_type_table_count)
227 	return_error(e_rangecheck);
228     /* Collect parameters common to all function types. */
229     params.Domain = 0;
230     params.Range = 0;
231     code = fn_build_float_array(op, "Domain", true, true, &params.Domain, mem);
232     if (code < 0)
233 	goto fail;
234     params.m = code >> 1;
235     code = fn_build_float_array(op, "Range", false, true, &params.Range, mem);
236     if (code < 0)
237 	goto fail;
238     params.n = code >> 1;
239     /* Finish building the function. */
240     /* If this fails, it will free all the parameters. */
241     return (*build_function_type_table[i].proc)
242 	(i_ctx_p, op, &params, depth + 1, ppfn, mem);
243 fail:
244     gs_free_const_object(mem, params.Range, "Range");
245     gs_free_const_object(mem, params.Domain, "Domain");
246     return code;
247 }
248 
249 /*
250  * Collect a heap-allocated array of floats.  If the key is missing, set
251  * *pparray = 0 and return 0; otherwise set *pparray and return the number
252  * of elements.  Note that 0-length arrays are acceptable, so if the value
253  * returned is 0, the caller must check whether *pparray == 0.
254  */
255 int
fn_build_float_array(const ref * op,const char * kstr,bool required,bool even,const float ** pparray,gs_memory_t * mem)256 fn_build_float_array(const ref * op, const char *kstr, bool required,
257 		     bool even, const float **pparray, gs_memory_t *mem)
258 {
259     ref *par;
260     int code;
261 
262     *pparray = 0;
263     if (dict_find_string(op, kstr, &par) <= 0)
264 	return (required ? gs_note_error(e_rangecheck) : 0);
265     if (!r_is_array(par))
266 	return_error(e_typecheck);
267     {
268 	uint size = r_size(par);
269 	float *ptr = (float *)
270 	    gs_alloc_byte_array(mem, size, sizeof(float), kstr);
271 
272 	if (ptr == 0)
273 	    return_error(e_VMerror);
274 	code = dict_float_array_check_param(mem, op, kstr, size,
275 					    ptr, NULL,
276 					    0, e_rangecheck);
277 	if (code < 0 || (even && (code & 1) != 0)) {
278 	    gs_free_object(mem, ptr, kstr);
279 	    return(code < 0 ? code : gs_note_error(e_rangecheck));
280 	}
281 	*pparray = ptr;
282     }
283     return code;
284 }
285 
286 /*
287  * Similar to fn_build_float_array() except
288  * - numeric parameter is accepted and converted to 1-element array
289  * - number of elements is not checked for even/odd
290  */
291 int
fn_build_float_array_forced(const ref * op,const char * kstr,bool required,const float ** pparray,gs_memory_t * mem)292 fn_build_float_array_forced(const ref * op, const char *kstr, bool required,
293 		     const float **pparray, gs_memory_t *mem)
294 {
295     ref *par;
296     int code;
297     uint size;
298     float *ptr;
299 
300     *pparray = 0;
301     if (dict_find_string(op, kstr, &par) <= 0)
302 	return (required ? gs_note_error(e_rangecheck) : 0);
303 
304     if( r_is_array(par) )
305 	size = r_size(par);
306     else if(r_type(par) == t_integer || r_type(par) == t_real)
307         size = 1;
308     else
309 	return_error(e_typecheck);
310     ptr = (float *)gs_alloc_byte_array(mem, size, sizeof(float), kstr);
311 
312     if (ptr == 0)
313         return_error(e_VMerror);
314     if(r_is_array(par) )
315         code = dict_float_array_check_param(mem, op, kstr,
316 					    size, ptr, NULL,
317 					    0, e_rangecheck);
318     else {
319         code = dict_float_param(op, kstr, 0., ptr); /* defailt cannot happen */
320         if( code == 0 )
321             code = 1;
322     }
323 
324     if (code < 0 ) {
325         gs_free_object(mem, ptr, kstr);
326         return code;
327     }
328     *pparray = ptr;
329     return code;
330 }
331 
332 /*
333  * If a PostScript object is a Function procedure, return the function
334  * object, otherwise return 0.
335  */
336 gs_function_t *
ref_function(const ref * op)337 ref_function(const ref *op)
338 {
339     if (r_has_type(op, t_array) &&
340 	r_has_masked_attrs(op, a_executable | a_execute,
341 			   a_executable | a_all) &&
342 	r_size(op) == 2 &&
343 	r_has_type_attrs(op->value.refs + 1, t_operator, a_executable) &&
344 	op->value.refs[1].value.opproc == zexecfunction &&
345 	r_is_struct(op->value.refs) &&
346 	r_has_masked_attrs(op->value.refs, a_executable | a_execute,
347 			   a_executable | a_all)
348 	)
349 	return (gs_function_t *)op->value.refs->value.pstruct;
350     return 0;
351 }
352 
353 /* ------ Initialization procedure ------ */
354 
355 const op_def zfunc_op_defs[] =
356 {
357     {"1.buildfunction", zbuildfunction},
358 #ifdef TEST
359     {"2.scalefunction", zscalefunction},
360 #endif /* TEST */
361     {"1%execfunction", zexecfunction},
362     {"1.isencapfunction", zisencapfunction},
363     op_def_end(0)
364 };
365