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