diff options
Diffstat (limited to 'base/gsfunc3.c')
-rw-r--r-- | base/gsfunc3.c | 779 |
1 files changed, 779 insertions, 0 deletions
diff --git a/base/gsfunc3.c b/base/gsfunc3.c new file mode 100644 index 00000000..08d1dc65 --- /dev/null +++ b/base/gsfunc3.c @@ -0,0 +1,779 @@ +/* Copyright (C) 2001-2019 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 the license contained in the file LICENSE in this distribution. + + Refer to licensing information at http://www.artifex.com or contact + Artifex Software, Inc., 1305 Grant Avenue - Suite 200, Novato, + CA 94945, U.S.A., +1(415)492-9861, for further information. +*/ + + +/* Implementation of LL3 Functions */ +#include "math_.h" +#include "memory_.h" +#include "gx.h" +#include "gserrors.h" +#include "gsfunc3.h" +#include "gsparam.h" +#include "gxfunc.h" +#include "gxarith.h" +#include "stream.h" + +/* ---------------- Utilities ---------------- */ + +#define MASK1 ((uint)(~0) / 3) + +/* + * Free an array of subsidiary Functions. Note that this may be called + * before the Functions array has been fully initialized. Note also that + * its argument conforms to the Functions array in the parameter structure, + * but it (necessarily) deconstifies it. + */ +static void +fn_free_functions(const gs_function_t *const * Functions, int count, + gs_memory_t * mem) +{ + int i; + + for (i = count; --i >= 0;) + if (Functions[i]) + gs_function_free((gs_function_t *)Functions[i], true, mem); + gs_free_const_object(mem, Functions, "Functions"); +} + +/* + * Scale an array of subsidiary functions. Note that the scale may either + * be propagated unchanged (step_ranges = false) or divided among the + * (1-output) subfunctions (step_ranges = true). + */ +static int +fn_scale_functions(gs_function_t ***ppsfns, const gs_function_t *const *pfns, + int count, const gs_range_t *pranges, bool step_ranges, + gs_memory_t *mem) +{ + gs_function_t **psfns; + int code = alloc_function_array(count, &psfns, mem); + const gs_range_t *ranges = pranges; + int i; + + if (code < 0) + return code; + for (i = 0; i < count; ++i) { + int code = gs_function_make_scaled(pfns[i], &psfns[i], ranges, mem); + + if (code < 0) { + fn_free_functions((const gs_function_t *const *)psfns, count, mem); + return code; + } + if (step_ranges) + ++ranges; + } + *ppsfns = psfns; + return 0; +} + +/* ---------------- Exponential Interpolation functions ---------------- */ + +typedef struct gs_function_ElIn_s { + gs_function_head_t head; + gs_function_ElIn_params_t params; +} gs_function_ElIn_t; + +private_st_function_ElIn(); + +/* Evaluate an Exponential Interpolation function. */ +static int +fn_ElIn_evaluate(const gs_function_t * pfn_common, const float *in, float *out) +{ + const gs_function_ElIn_t *const pfn = + (const gs_function_ElIn_t *)pfn_common; + double arg = in[0], raised; + int i; + + if (arg < pfn->params.Domain[0]) + arg = pfn->params.Domain[0]; + else if (arg > pfn->params.Domain[1]) + arg = pfn->params.Domain[1]; + raised = pow(arg, pfn->params.N); + for (i = 0; i < pfn->params.n; ++i) { + float v0 = (pfn->params.C0 == 0 ? 0.0 : pfn->params.C0[i]); + float v1 = (pfn->params.C1 == 0 ? 1.0 : pfn->params.C1[i]); + double value = v0 + raised * (v1 - v0); + + if (pfn->params.Range) { + float r0 = pfn->params.Range[2 * i], + r1 = pfn->params.Range[2 * i + 1]; + + if (value < r0) + value = r0; + else if (value > r1) + value = r1; + } + out[i] = value; + if_debug3('~', "[~]ElIn %g => [%d]%g\n", arg, i, out[i]); + } + return 0; +} + +/* Test whether an Exponential function is monotonic. (They always are.) */ +static int +fn_ElIn_is_monotonic(const gs_function_t * pfn_common, + const float *lower, const float *upper, uint *mask) +{ + const gs_function_ElIn_t *const pfn = + (const gs_function_ElIn_t *)pfn_common; + + if (lower[0] > pfn->params.Domain[1] || + upper[0] < pfn->params.Domain[0] + ) + return_error(gs_error_rangecheck); + *mask = 0; + return 1; +} + +/* Write Exponential Interpolation function parameters on a parameter list. */ +static int +fn_ElIn_get_params(const gs_function_t *pfn_common, gs_param_list *plist) +{ + const gs_function_ElIn_t *const pfn = + (const gs_function_ElIn_t *)pfn_common; + int ecode = fn_common_get_params(pfn_common, plist); + int code; + + if (pfn->params.C0) { + if ((code = param_write_float_values(plist, "C0", pfn->params.C0, + pfn->params.n, false)) < 0) + ecode = code; + } + if (pfn->params.C1) { + if ((code = param_write_float_values(plist, "C1", pfn->params.C1, + pfn->params.n, false)) < 0) + ecode = code; + } + if ((code = param_write_float(plist, "N", &pfn->params.N)) < 0) + ecode = code; + return ecode; +} + +/* Make a scaled copy of an Exponential Interpolation function. */ +static int +fn_ElIn_make_scaled(const gs_function_ElIn_t *pfn, + gs_function_ElIn_t **ppsfn, + const gs_range_t *pranges, gs_memory_t *mem) +{ + gs_function_ElIn_t *psfn = + gs_alloc_struct(mem, gs_function_ElIn_t, &st_function_ElIn, + "fn_ElIn_make_scaled"); + float *c0; + float *c1; + int code, i; + + if (psfn == 0) + return_error(gs_error_VMerror); + psfn->params = pfn->params; + psfn->params.C0 = c0 = + fn_copy_values(pfn->params.C0, pfn->params.n, sizeof(float), mem); + psfn->params.C1 = c1 = + fn_copy_values(pfn->params.C1, pfn->params.n, sizeof(float), mem); + if ((code = ((c0 == 0 && pfn->params.C0 != 0) || + (c1 == 0 && pfn->params.C1 != 0) ? + gs_note_error(gs_error_VMerror) : 0)) < 0 || + (code = fn_common_scale((gs_function_t *)psfn, + (const gs_function_t *)pfn, + pranges, mem)) < 0) { + gs_function_free((gs_function_t *)psfn, true, mem); + return code; + } + for (i = 0; i < pfn->params.n; ++i) { + double base = pranges[i].rmin, factor = pranges[i].rmax - base; + + c1[i] = c1[i] * factor + base; + c0[i] = c0[i] * factor + base; + } + *ppsfn = psfn; + return 0; +} + +/* Free the parameters of an Exponential Interpolation function. */ +void +gs_function_ElIn_free_params(gs_function_ElIn_params_t * params, + gs_memory_t * mem) +{ + gs_free_const_object(mem, params->C1, "C1"); + params->C1 = NULL; + gs_free_const_object(mem, params->C0, "C0"); + params->C0 = NULL; + fn_common_free_params((gs_function_params_t *) params, mem); +} + +/* Serialize. */ +static int +gs_function_ElIn_serialize(const gs_function_t * pfn, stream *s) +{ + uint n; + const gs_function_ElIn_params_t * p = (const gs_function_ElIn_params_t *)&pfn->params; + int code = fn_common_serialize(pfn, s); + float C0_default[2] = {0, 0}; + float C1_default[2] = {1, 0}; + + if (code < 0) + return code; + if (p->C0) + code = sputs(s, (const byte *)&p->C0[0], sizeof(p->C0[0]) * p->n, &n); + else + code = sputs(s, (const byte *)&C0_default, sizeof(float) * 2, &n); + if (code < 0) + return code; + + if (p->C1) + code = sputs(s, (const byte *)&p->C1[0], sizeof(p->C1[0]) * p->n, &n); + else + code = sputs(s, (const byte *)&C1_default, sizeof(float) * 2, &n); + if (code < 0) + return code; + return sputs(s, (const byte *)&p->N, sizeof(p->N), &n); +} + +/* Allocate and initialize an Exponential Interpolation function. */ +int +gs_function_ElIn_init(gs_function_t ** ppfn, + const gs_function_ElIn_params_t * params, + gs_memory_t * mem) +{ + static const gs_function_head_t function_ElIn_head = { + function_type_ExponentialInterpolation, + { + (fn_evaluate_proc_t) fn_ElIn_evaluate, + (fn_is_monotonic_proc_t) fn_ElIn_is_monotonic, + gs_function_get_info_default, + (fn_get_params_proc_t) fn_ElIn_get_params, + (fn_make_scaled_proc_t) fn_ElIn_make_scaled, + (fn_free_params_proc_t) gs_function_ElIn_free_params, + fn_common_free, + (fn_serialize_proc_t) gs_function_ElIn_serialize, + } + }; + int code; + + *ppfn = 0; /* in case of error */ + code = fn_check_mnDR((const gs_function_params_t *)params, 1, params->n); + if (code < 0) + return code; + if ((params->C0 == 0 || params->C1 == 0) && params->n != 1) + return_error(gs_error_rangecheck); + if (params->N != floor(params->N)) { + /* Non-integral exponent, all inputs must be non-negative. */ + if (params->Domain[0] < 0) + return_error(gs_error_rangecheck); + } + if (params->N < 0) { + /* Negative exponent, input must not be zero. */ + if (params->Domain[0] <= 0 && params->Domain[1] >= 0) + return_error(gs_error_rangecheck); + } { + gs_function_ElIn_t *pfn = + gs_alloc_struct(mem, gs_function_ElIn_t, &st_function_ElIn, + "gs_function_ElIn_init"); + + if (pfn == 0) + return_error(gs_error_VMerror); + pfn->params = *params; + pfn->params.m = 1; + pfn->head = function_ElIn_head; + *ppfn = (gs_function_t *) pfn; + } + return 0; +} + +/* ---------------- 1-Input Stitching functions ---------------- */ + +typedef struct gs_function_1ItSg_s { + gs_function_head_t head; + gs_function_1ItSg_params_t params; +} gs_function_1ItSg_t; + +private_st_function_1ItSg(); + +/* Evaluate a 1-Input Stitching function. */ +static int +fn_1ItSg_evaluate(const gs_function_t * pfn_common, const float *in, float *out) +{ + const gs_function_1ItSg_t *const pfn = + (const gs_function_1ItSg_t *)pfn_common; + float arg = in[0], b0, b1, e0, encoded; + int k = pfn->params.k; + int i; + + if (arg < pfn->params.Domain[0]) { + arg = pfn->params.Domain[0]; + i = 0; + } else if (arg > pfn->params.Domain[1]) { + arg = pfn->params.Domain[1]; + i = k - 1; + } else { + for (i = 0; i < k - 1; ++i) + if (arg <= pfn->params.Bounds[i]) + break; + } + b0 = (i == 0 ? pfn->params.Domain[0] : pfn->params.Bounds[i - 1]); + b1 = (i == k - 1 ? pfn->params.Domain[1] : pfn->params.Bounds[i]); + e0 = pfn->params.Encode[2 * i]; + if (b1 == b0) + encoded = e0; + else + encoded = + (arg - b0) * (pfn->params.Encode[2 * i + 1] - e0) / (b1 - b0) + e0; + if_debug3('~', "[~]1ItSg %g in %d => %g\n", arg, i, encoded); + return gs_function_evaluate(pfn->params.Functions[i], &encoded, out); +} + +/* Test whether a 1-Input Stitching function is monotonic. */ +static int +fn_1ItSg_is_monotonic(const gs_function_t * pfn_common, + const float *lower, const float *upper, uint *mask) +{ + const gs_function_1ItSg_t *const pfn = + (const gs_function_1ItSg_t *)pfn_common; + float v0 = lower[0], v1 = upper[0]; + float d0 = pfn->params.Domain[0], d1 = pfn->params.Domain[1]; + int k = pfn->params.k; + int i; + + *mask = 0; + + /* If the upper and lower parametric values are the same then this is a point + * and so is monotonic. + */ + if (v0 == v1) + return 1; + + if (v0 > v1) { + v0 = v1; v1 = lower[0]; + } + if (v0 > d1 || v1 < d0) + return_error(gs_error_rangecheck); + if (v0 < d0) + v0 = d0; + if (v1 > d1) + v1 = d1; + for (i = 0; i < pfn->params.k; ++i) { + float b0 = (i == 0 ? d0 : pfn->params.Bounds[i - 1]); + float b1 = (i == k - 1 ? d1 : pfn->params.Bounds[i]); + const float bsmall = (float)1e-6 * (b1 - b0); + float esmall; + float e0, e1; + float w0, w1; + float vv0, vv1; + float vb0, vb1; + + if (v0 >= b1 - bsmall) + continue; /* Ignore a small noise */ + vv0 = max(b0, v0); + /* make sure we promote *both* values, in case v0 was within the + * noise threshold above. + */ + vv1 = max(b0, v1); + if (vv1 > b1 && v1 < b1 + bsmall) + vv1 = b1; /* Ignore a small noise */ + if (vv0 == vv1) + return 1; + if (vv0 < b1 && vv1 > b1) { + *mask = 1; + return 0; /* Consider stitches as monotony breaks. */ + } + e0 = pfn->params.Encode[2 * i]; + e1 = pfn->params.Encode[2 * i + 1]; + esmall = (float)1e-6 * any_abs(e1 - e0); + vb0 = (float)max(vv0, b0); + vb1 = (float)min(vv1, b1); + if (b1 == b0) + return 1; /* function is monotonous in a point */ + w0 = (float)(vb0 - b0) * (e1 - e0) / (b1 - b0) + e0; + w1 = (float)(vb1 - b0) * (e1 - e0) / (b1 - b0) + e0; + /* Note that w0 > w1 is now possible if e0 > e1. */ + if (e0 > e1) { + if (w0 > e0 && w0 - esmall <= e0) + w0 = e0; /* Suppress a small noise */ + if (w1 < e1 && w1 + esmall >= e1) + w1 = e1; /* Suppress a small noise */ + } else { + if (w0 < e0 && w0 + esmall >= e0) + w0 = e0; /* Suppress a small noise */ + if (w1 > e1 && w1 - esmall <= e1) + w1 = e1; /* Suppress a small noise */ + } + if (w0 > w1) + return gs_function_is_monotonic(pfn->params.Functions[i], + &w1, &w0, mask); + else + return gs_function_is_monotonic(pfn->params.Functions[i], + &w0, &w1, mask); + } + /* v0 is equal to the range end. */ + *mask = 0; + return 1; +} + +/* Return 1-Input Stitching function information. */ +static void +fn_1ItSg_get_info(const gs_function_t *pfn_common, gs_function_info_t *pfi) +{ + const gs_function_1ItSg_t *const pfn = + (const gs_function_1ItSg_t *)pfn_common; + + gs_function_get_info_default(pfn_common, pfi); + pfi->Functions = pfn->params.Functions; + pfi->num_Functions = pfn->params.k; +} + +/* Write 1-Input Stitching function parameters on a parameter list. */ +static int +fn_1ItSg_get_params(const gs_function_t *pfn_common, gs_param_list *plist) +{ + const gs_function_1ItSg_t *const pfn = + (const gs_function_1ItSg_t *)pfn_common; + int ecode = fn_common_get_params(pfn_common, plist); + int code; + + if ((code = param_write_float_values(plist, "Bounds", pfn->params.Bounds, + pfn->params.k - 1, false)) < 0) + ecode = code; + if ((code = param_write_float_values(plist, "Encode", pfn->params.Encode, + 2 * pfn->params.k, false)) < 0) + ecode = code; + return ecode; +} + +/* Make a scaled copy of a 1-Input Stitching function. */ +static int +fn_1ItSg_make_scaled(const gs_function_1ItSg_t *pfn, + gs_function_1ItSg_t **ppsfn, + const gs_range_t *pranges, gs_memory_t *mem) +{ + gs_function_1ItSg_t *psfn = + gs_alloc_struct(mem, gs_function_1ItSg_t, &st_function_1ItSg, + "fn_1ItSg_make_scaled"); + int code; + + if (psfn == 0) + return_error(gs_error_VMerror); + psfn->params = pfn->params; + psfn->params.Functions = 0; /* in case of failure */ + psfn->params.Bounds = + fn_copy_values(pfn->params.Bounds, pfn->params.k - 1, sizeof(float), + mem); + psfn->params.Encode = + fn_copy_values(pfn->params.Encode, 2 * pfn->params.k, sizeof(float), + mem); + if ((code = (psfn->params.Bounds == 0 || psfn->params.Encode == 0 ? + gs_note_error(gs_error_VMerror) : 0)) < 0 || + (code = fn_common_scale((gs_function_t *)psfn, + (const gs_function_t *)pfn, + pranges, mem)) < 0 || + (code = fn_scale_functions((gs_function_t ***)&psfn->params.Functions, + pfn->params.Functions, + pfn->params.n, pranges, false, mem)) < 0) { + gs_function_free((gs_function_t *)psfn, true, mem); + return code; + } + *ppsfn = psfn; + return 0; +} + +/* Free the parameters of a 1-Input Stitching function. */ +void +gs_function_1ItSg_free_params(gs_function_1ItSg_params_t * params, + gs_memory_t * mem) +{ + gs_free_const_object(mem, params->Encode, "Encode"); + params->Encode = NULL; + gs_free_const_object(mem, params->Bounds, "Bounds"); + params->Bounds = NULL; + fn_free_functions(params->Functions, params->k, mem); + params->Functions = NULL; + fn_common_free_params((gs_function_params_t *) params, mem); +} + +/* Serialize. */ +static int +gs_function_1ItSg_serialize(const gs_function_t * pfn, stream *s) +{ + uint n; + const gs_function_1ItSg_params_t * p = (const gs_function_1ItSg_params_t *)&pfn->params; + int code = fn_common_serialize(pfn, s); + int k; + + if (code < 0) + return code; + code = sputs(s, (const byte *)&p->k, sizeof(p->k), &n); + if (code < 0) + return code; + + for (k = 0; k < p->k && code >= 0; k++) + code = gs_function_serialize(p->Functions[k], s); + if (code < 0) + return code; + code = sputs(s, (const byte *)&p->Bounds[0], sizeof(p->Bounds[0]) * (p->k - 1), &n); + if (code < 0) + return code; + return sputs(s, (const byte *)&p->Encode[0], sizeof(p->Encode[0]) * (p->k * 2), &n); +} + +/* Allocate and initialize a 1-Input Stitching function. */ +int +gs_function_1ItSg_init(gs_function_t ** ppfn, + const gs_function_1ItSg_params_t * params, gs_memory_t * mem) +{ + static const gs_function_head_t function_1ItSg_head = { + function_type_1InputStitching, + { + (fn_evaluate_proc_t) fn_1ItSg_evaluate, + (fn_is_monotonic_proc_t) fn_1ItSg_is_monotonic, + (fn_get_info_proc_t) fn_1ItSg_get_info, + (fn_get_params_proc_t) fn_1ItSg_get_params, + (fn_make_scaled_proc_t) fn_1ItSg_make_scaled, + (fn_free_params_proc_t) gs_function_1ItSg_free_params, + fn_common_free, + (fn_serialize_proc_t) gs_function_1ItSg_serialize, + } + }; + int n = (params->Range == 0 ? 0 : params->n); + float prev = params->Domain[0]; + int code, i; + + *ppfn = 0; /* in case of error */ + for (i = 0; i < params->k; ++i) { + const gs_function_t *psubfn = params->Functions[i]; + + if (psubfn->params.m != 1) + return_error(gs_error_rangecheck); + if (n == 0) + n = psubfn->params.n; + else if (psubfn->params.n != n) + return_error(gs_error_rangecheck); + /* There are only k - 1 Bounds, not k. */ + if (i < params->k - 1) { + if (params->Bounds[i] < prev) + return_error(gs_error_rangecheck); + prev = params->Bounds[i]; + } + } + if (params->Domain[1] < prev) + return_error(gs_error_rangecheck); + + code = fn_check_mnDR((const gs_function_params_t *)params, 1, n); + if(code < 0) + return code; + else + { + gs_function_1ItSg_t *pfn = + gs_alloc_struct(mem, gs_function_1ItSg_t, &st_function_1ItSg, + "gs_function_1ItSg_init"); + + if (pfn == 0) + return_error(gs_error_VMerror); + pfn->params = *params; + pfn->params.m = 1; + pfn->params.n = n; + pfn->head = function_1ItSg_head; + *ppfn = (gs_function_t *) pfn; + } + return 0; +} + +/* ---------------- Arrayed Output functions ---------------- */ + +typedef struct gs_function_AdOt_s { + gs_function_head_t head; + gs_function_AdOt_params_t params; +} gs_function_AdOt_t; + +private_st_function_AdOt(); + +/* Evaluate an Arrayed Output function. */ +static int +fn_AdOt_evaluate(const gs_function_t *pfn_common, const float *in0, float *out) +{ + const gs_function_AdOt_t *const pfn = + (const gs_function_AdOt_t *)pfn_common; + const float *in = in0; +#define MAX_ADOT_IN 16 + float in_buf[MAX_ADOT_IN]; + int i; + + /* + * We have to take special care to handle the case where in and out + * overlap. For the moment, handle it only for a limited number of + * input values. + */ + if (in <= out + (pfn->params.n - 1) && out <= in + (pfn->params.m - 1)) { + if (pfn->params.m > MAX_ADOT_IN) + return_error(gs_error_rangecheck); + memcpy(in_buf, in, pfn->params.m * sizeof(*in)); + in = in_buf; + } + for (i = 0; i < pfn->params.n; ++i) { + int code = + gs_function_evaluate(pfn->params.Functions[i], in, out + i); + + if (code < 0) + return code; + } + return 0; +#undef MAX_ADOT_IN +} + +/* Test whether an Arrayed Output function is monotonic. */ +static int +fn_AdOt_is_monotonic(const gs_function_t * pfn_common, + const float *lower, const float *upper, uint *mask) +{ + const gs_function_AdOt_t *const pfn = + (const gs_function_AdOt_t *)pfn_common; + int i; + + for (i = 0; i < pfn->params.n; ++i) { + int code = + gs_function_is_monotonic(pfn->params.Functions[i], lower, upper, mask); + + if (code <= 0) + return code; + } + return 1; +} + +/* Return Arrayed Output function information. */ +static void +fn_AdOt_get_info(const gs_function_t *pfn_common, gs_function_info_t *pfi) +{ + const gs_function_AdOt_t *const pfn = + (const gs_function_AdOt_t *)pfn_common; + + gs_function_get_info_default(pfn_common, pfi); + pfi->Functions = pfn->params.Functions; + pfi->num_Functions = pfn->params.n; +} + +/* Make a scaled copy of an Arrayed Output function. */ +static int +fn_AdOt_make_scaled(const gs_function_AdOt_t *pfn, gs_function_AdOt_t **ppsfn, + const gs_range_t *pranges, gs_memory_t *mem) +{ + gs_function_AdOt_t *psfn = + gs_alloc_struct(mem, gs_function_AdOt_t, &st_function_AdOt, + "fn_AdOt_make_scaled"); + int code; + + if (psfn == 0) + return_error(gs_error_VMerror); + psfn->params = pfn->params; + psfn->params.Functions = 0; /* in case of failure */ + if ((code = fn_common_scale((gs_function_t *)psfn, + (const gs_function_t *)pfn, + pranges, mem)) < 0 || + (code = fn_scale_functions((gs_function_t ***)&psfn->params.Functions, + pfn->params.Functions, + pfn->params.n, pranges, true, mem)) < 0) { + gs_function_free((gs_function_t *)psfn, true, mem); + return code; + } + *ppsfn = psfn; + return 0; +} + +/* Free the parameters of an Arrayed Output function. */ +void +gs_function_AdOt_free_params(gs_function_AdOt_params_t * params, + gs_memory_t * mem) +{ + fn_free_functions(params->Functions, params->n, mem); + params->Functions = NULL; + fn_common_free_params((gs_function_params_t *) params, mem); +} + +/* Serialize. */ +static int +gs_function_AdOt_serialize(const gs_function_t * pfn, stream *s) +{ + const gs_function_AdOt_params_t * p = (const gs_function_AdOt_params_t *)&pfn->params; + int code = fn_common_serialize(pfn, s); + int k; + + if (code < 0) + return code; + for (k = 0; k < p->n && code >= 0; k++) + code = gs_function_serialize(p->Functions[k], s); + return code; +} + +/* Allocate and initialize an Arrayed Output function. */ +int +gs_function_AdOt_init(gs_function_t ** ppfn, + const gs_function_AdOt_params_t * params, gs_memory_t * mem) +{ + static const gs_function_head_t function_AdOt_head = { + function_type_ArrayedOutput, + { + (fn_evaluate_proc_t) fn_AdOt_evaluate, + (fn_is_monotonic_proc_t) fn_AdOt_is_monotonic, + (fn_get_info_proc_t) fn_AdOt_get_info, + fn_common_get_params, /****** WHAT TO DO ABOUT THIS? ******/ + (fn_make_scaled_proc_t) fn_AdOt_make_scaled, + (fn_free_params_proc_t) gs_function_AdOt_free_params, + fn_common_free, + (fn_serialize_proc_t) gs_function_AdOt_serialize, + } + }; + int m = params->m, n = params->n; + + *ppfn = 0; /* in case of error */ + if (m <= 0 || n <= 0) + return_error(gs_error_rangecheck); + { + gs_function_AdOt_t *pfn = + gs_alloc_struct(mem, gs_function_AdOt_t, &st_function_AdOt, + "gs_function_AdOt_init"); + float *domain = (float *) + gs_alloc_byte_array(mem, 2 * m, sizeof(float), + "gs_function_AdOt_init(Domain)"); + int i, j; + + if (pfn == 0) + return_error(gs_error_VMerror); + pfn->params = *params; + pfn->params.Domain = domain; + pfn->params.Range = 0; + pfn->head = function_AdOt_head; + if (domain == 0) { + gs_function_free((gs_function_t *)pfn, true, mem); + return_error(gs_error_VMerror); + } + /* + * We compute the Domain as the intersection of the Domains of + * the individual subfunctions. This isn't quite right: some + * subfunction might actually make use of a larger domain of + * input values. However, the only place that Arrayed Output + * functions are used is in Shading and similar dictionaries, + * where the input values are clamped to the intersection of + * the individual Domains anyway. + */ + memcpy(domain, params->Functions[0]->params.Domain, + 2 * sizeof(float) * m); + for (i = 1; i < n; ++i) { + const float *dom = params->Functions[i]->params.Domain; + + for (j = 0; j < 2 * m; j += 2, dom += 2) { + domain[j] = max(domain[j], dom[0]); + domain[j + 1] = min(domain[j + 1], dom[1]); + } + } + *ppfn = (gs_function_t *) pfn; + } + return 0; +} |