/* Copyright (C) 1997, 2000 artofcode LLC. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA, 02111-1307. */ /*$Id: zfunc.c,v 1.6.6.3.2.1 2003/01/17 00:49:06 giles Exp $ */ /* Generic PostScript language interface to Functions */ #include "memory_.h" #include "ghost.h" #include "oper.h" #include "gscdefs.h" #include "gsfunc.h" #include "gsstruct.h" #include "ialloc.h" #include "idict.h" #include "idparam.h" #include "ifunc.h" #include "store.h" /* Define the maximum depth of nesting of subsidiary functions. */ #define MAX_SUB_FUNCTION_DEPTH 3 /* GC descriptors */ gs_private_st_ptr(st_function_ptr, gs_function_t *, "gs_function_t *", function_ptr_enum_ptrs, function_ptr_reloc_ptrs); gs_private_st_element(st_function_ptr_element, gs_function_t *, "gs_function_t *[]", function_ptr_element_enum_ptrs, function_ptr_element_reloc_ptrs, st_function_ptr); /* ------ Operators ------ */ /* .buildfunction */ private int zbuildfunction(i_ctx_t *i_ctx_p) { os_ptr op = osp; gs_function_t *pfn; ref cref; /* closure */ int code; code = ialloc_ref_array(&cref, a_executable | a_execute, 2, ".buildfunction"); if (code < 0) return code; code = fn_build_function(i_ctx_p, op, &pfn, imemory); if (code < 0) { ifree_ref_array(&cref, ".buildfunction"); return code; } make_istruct_new(cref.value.refs, a_executable | a_execute, pfn); make_oper_new(cref.value.refs + 1, 0, zexecfunction); ref_assign(op, &cref); return 0; } /* ... %execfunction ... */ int zexecfunction(i_ctx_t *i_ctx_p) { os_ptr op = osp; /* * Since this operator's name begins with %, the name is not defined * in systemdict. The only place this operator can ever appear is * in the execute-only closure created by .buildfunction. * Therefore, in principle it is unnecessary to check the argument. * However, we do a little checking anyway just on general * principles. Note that since the argument may be an instance of * any subclass of gs_function_t, we currently have no way to check * its type. */ if (!r_is_struct(op) || !r_has_masked_attrs(op, a_executable | a_execute, a_executable | a_all) ) return_error(e_typecheck); { gs_function_t *pfn = (gs_function_t *) op->value.pstruct; int m = pfn->params.m, n = pfn->params.n; int diff = n - (m + 1); if (diff > 0) check_ostack(diff); { float params[20]; /* arbitrary size, just to avoid allocs */ float *in; float *out; int code = 0; if (m + n <= countof(params)) { in = params; } else { in = (float *)ialloc_byte_array(m + n, sizeof(float), "%execfunction(in/out)"); if (in == 0) code = gs_note_error(e_VMerror); } out = in + m; if (code < 0 || (code = float_params(op - 1, m, in)) < 0 || (code = gs_function_evaluate(pfn, in, out)) < 0 ) DO_NOTHING; else { if (diff > 0) push(diff); /* can't fail */ else if (diff < 0) { pop(-diff); op = osp; } code = make_floats(op + 1 - n, out, n); } if (in != params) ifree_object(in, "%execfunction(in)"); return code; } } } /* ------ Procedures ------ */ /* Build a function structure from a PostScript dictionary. */ int fn_build_function(i_ctx_t *i_ctx_p, const ref * op, gs_function_t ** ppfn, gs_memory_t *mem) { return fn_build_sub_function(i_ctx_p, op, ppfn, 0, mem); } int fn_build_sub_function(i_ctx_t *i_ctx_p, const ref * op, gs_function_t ** ppfn, int depth, gs_memory_t *mem) { int code, type, i; gs_function_params_t params; if (depth > MAX_SUB_FUNCTION_DEPTH) return_error(e_limitcheck); check_type(*op, t_dictionary); code = dict_int_param(op, "FunctionType", 0, max_int, -1, &type); if (code < 0) return code; for (i = 0; i < build_function_type_table_count; ++i) if (build_function_type_table[i].type == type) break; if (i == build_function_type_table_count) return_error(e_rangecheck); /* Collect parameters common to all function types. */ params.Domain = 0; params.Range = 0; code = fn_build_float_array(op, "Domain", true, true, ¶ms.Domain, mem); if (code < 0) goto fail; params.m = code >> 1; code = fn_build_float_array(op, "Range", false, true, ¶ms.Range, mem); if (code < 0) goto fail; params.n = code >> 1; /* Finish building the function. */ /* If this fails, it will free all the parameters. */ return (*build_function_type_table[i].proc) (i_ctx_p, op, ¶ms, depth + 1, ppfn, mem); fail: gs_free_const_object(mem, params.Range, "Range"); gs_free_const_object(mem, params.Domain, "Domain"); return code; } /* Allocate an array of function objects. */ int alloc_function_array(uint count, gs_function_t *** pFunctions, gs_memory_t *mem) { gs_function_t **ptr; if (count == 0) return_error(e_rangecheck); ptr = gs_alloc_struct_array(mem, count, gs_function_t *, &st_function_ptr_element, "Functions"); if (ptr == 0) return_error(e_VMerror); memset(ptr, 0, sizeof(*ptr) * count); *pFunctions = ptr; return 0; } /* * Collect a heap-allocated array of floats. If the key is missing, set * *pparray = 0 and return 0; otherwise set *pparray and return the number * of elements. Note that 0-length arrays are acceptable, so if the value * returned is 0, the caller must check whether *pparray == 0. */ int fn_build_float_array(const ref * op, const char *kstr, bool required, bool even, const float **pparray, gs_memory_t *mem) { ref *par; int code; *pparray = 0; if (dict_find_string(op, kstr, &par) <= 0) return (required ? gs_note_error(e_rangecheck) : 0); if (!r_is_array(par)) return_error(e_typecheck); { uint size = r_size(par); float *ptr = (float *) gs_alloc_byte_array(mem, size, sizeof(float), kstr); if (ptr == 0) return_error(e_VMerror); code = dict_float_array_check_param(op, kstr, size, ptr, NULL, 0, e_rangecheck); if (code < 0 || (even && (code & 1) != 0)) { gs_free_object(mem, ptr, kstr); return(code < 0 ? code : gs_note_error(e_rangecheck)); } *pparray = ptr; } return code; } /* * If a PostScript object is a Function procedure, return the function * object, otherwise return 0. */ gs_function_t * ref_function(const ref *op) { if (r_has_type(op, t_array) && r_has_masked_attrs(op, a_executable | a_execute, a_executable | a_all) && r_size(op) == 2 && r_has_type_attrs(op->value.refs + 1, t_operator, a_executable) && op->value.refs[1].value.opproc == zexecfunction && r_is_struct(op->value.refs) && r_has_masked_attrs(op->value.refs, a_executable | a_execute, a_executable | a_all) ) return (gs_function_t *)op->value.refs->value.pstruct; return 0; } /* ------ Initialization procedure ------ */ const op_def zfunc_op_defs[] = { {"1.buildfunction", zbuildfunction}, {"1%execfunction", zexecfunction}, op_def_end(0) };