diff options
Diffstat (limited to 'gs/base/gsfunc0.c')
-rw-r--r-- | gs/base/gsfunc0.c | 1523 |
1 files changed, 1523 insertions, 0 deletions
diff --git a/gs/base/gsfunc0.c b/gs/base/gsfunc0.c new file mode 100644 index 000000000..4fc24c9bd --- /dev/null +++ b/gs/base/gsfunc0.c @@ -0,0 +1,1523 @@ +/* Copyright (C) 2001-2006 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 that + license. Refer to licensing information at http://www.artifex.com/ + or contact Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, + San Rafael, CA 94903, U.S.A., +1(415)492-9861, for further information. +*/ + +/* $Id$ */ +/* Implementation of FunctionType 0 (Sampled) Functions */ +#include "math_.h" +#include "gx.h" +#include "gserrors.h" +#include "gsfunc0.h" +#include "gsparam.h" +#include "gxfarith.h" +#include "gxfunc.h" +#include "stream.h" + +#define POLE_CACHE_DEBUG 0 /* A temporary development technology need. + Remove after the beta testing. */ +#define POLE_CACHE_GENERIC_1D 1 /* A temporary development technology need. + Didn't decide yet - see fn_Sd_evaluate_cubic_cached_1d. */ +#define POLE_CACHE_IGNORE 0 /* A temporary development technology need. + Remove after the beta testing. */ + +#define MAX_FAST_COMPS 8 + +typedef struct gs_function_Sd_s { + gs_function_head_t head; + gs_function_Sd_params_t params; +} gs_function_Sd_t; + +/* GC descriptor */ +private_st_function_Sd(); +static +ENUM_PTRS_WITH(function_Sd_enum_ptrs, gs_function_Sd_t *pfn) +{ + index -= 6; + if (index < st_data_source_max_ptrs) + return ENUM_USING(st_data_source, &pfn->params.DataSource, + sizeof(pfn->params.DataSource), index); + return ENUM_USING_PREFIX(st_function, st_data_source_max_ptrs); +} +ENUM_PTR3(0, gs_function_Sd_t, params.Encode, params.Decode, params.Size); +ENUM_PTR3(3, gs_function_Sd_t, params.pole, params.array_step, params.stream_step); +ENUM_PTRS_END +static +RELOC_PTRS_WITH(function_Sd_reloc_ptrs, gs_function_Sd_t *pfn) +{ + RELOC_PREFIX(st_function); + RELOC_USING(st_data_source, &pfn->params.DataSource, + sizeof(pfn->params.DataSource)); + RELOC_PTR3(gs_function_Sd_t, params.Encode, params.Decode, params.Size); + RELOC_PTR3(gs_function_Sd_t, params.pole, params.array_step, params.stream_step); +} +RELOC_PTRS_END + +/* Define the maximum plausible number of inputs and outputs */ +/* for a Sampled function. */ +#define max_Sd_m 16 +#define max_Sd_n 16 + +/* Get one set of sample values. */ +#define SETUP_SAMPLES(bps, nbytes)\ + int n = pfn->params.n;\ + byte buf[max_Sd_n * ((bps + 7) >> 3)];\ + const byte *p;\ + int i;\ +\ + data_source_access(&pfn->params.DataSource, offset >> 3,\ + nbytes, buf, &p) + +static int +fn_gets_1(const gs_function_Sd_t * pfn, ulong offset, uint * samples) +{ + SETUP_SAMPLES(1, ((offset & 7) + n + 7) >> 3); + for (i = 0; i < n; ++i) { + samples[i] = (*p >> (~offset & 7)) & 1; + if (!(++offset & 7)) + p++; + } + return 0; +} +static int +fn_gets_2(const gs_function_Sd_t * pfn, ulong offset, uint * samples) +{ + SETUP_SAMPLES(2, (((offset & 7) >> 1) + n + 3) >> 2); + for (i = 0; i < n; ++i) { + samples[i] = (*p >> (6 - (offset & 7))) & 3; + if (!((offset += 2) & 7)) + p++; + } + return 0; +} +static int +fn_gets_4(const gs_function_Sd_t * pfn, ulong offset, uint * samples) +{ + SETUP_SAMPLES(4, (((offset & 7) >> 2) + n + 1) >> 1); + for (i = 0; i < n; ++i) { + samples[i] = ((offset ^= 4) & 4 ? *p >> 4 : *p++ & 0xf); + } + return 0; +} +static int +fn_gets_8(const gs_function_Sd_t * pfn, ulong offset, uint * samples) +{ + SETUP_SAMPLES(8, n); + for (i = 0; i < n; ++i) { + samples[i] = *p++; + } + return 0; +} +static int +fn_gets_12(const gs_function_Sd_t * pfn, ulong offset, uint * samples) +{ + SETUP_SAMPLES(12, (((offset & 7) >> 2) + 3 * n + 1) >> 1); + for (i = 0; i < n; ++i) { + if (offset & 4) + samples[i] = ((*p & 0xf) << 8) + p[1], p += 2; + else + samples[i] = (*p << 4) + (p[1] >> 4), p++; + offset ^= 4; + } + return 0; +} +static int +fn_gets_16(const gs_function_Sd_t * pfn, ulong offset, uint * samples) +{ + SETUP_SAMPLES(16, n * 2); + for (i = 0; i < n; ++i) { + samples[i] = (*p << 8) + p[1]; + p += 2; + } + return 0; +} +static int +fn_gets_24(const gs_function_Sd_t * pfn, ulong offset, uint * samples) +{ + SETUP_SAMPLES(24, n * 3); + for (i = 0; i < n; ++i) { + samples[i] = (*p << 16) + (p[1] << 8) + p[2]; + p += 3; + } + return 0; +} +static int +fn_gets_32(const gs_function_Sd_t * pfn, ulong offset, uint * samples) +{ + SETUP_SAMPLES(32, n * 4); + for (i = 0; i < n; ++i) { + samples[i] = (*p << 24) + (p[1] << 16) + (p[2] << 8) + p[3]; + p += 4; + } + return 0; +} + +static int (*const fn_get_samples[]) (const gs_function_Sd_t * pfn, + ulong offset, uint * samples) = +{ + 0, fn_gets_1, fn_gets_2, 0, fn_gets_4, 0, 0, 0, + fn_gets_8, 0, 0, 0, fn_gets_12, 0, 0, 0, + fn_gets_16, 0, 0, 0, 0, 0, 0, 0, + fn_gets_24, 0, 0, 0, 0, 0, 0, 0, + fn_gets_32 +}; + +/* + * Compute a value by cubic interpolation. + * f[] = f(0), f(1), f(2), f(3); 1 < x < 2. + * The formula is derived from those presented in + * http://www.cs.uwa.edu.au/undergraduate/units/233.413/Handouts/Lecture04.html + * (thanks to Raph Levien for the reference). + */ +static double +interpolate_cubic(floatp x, floatp f0, floatp f1, floatp f2, floatp f3) +{ + /* + * The parameter 'a' affects the contribution of the high-frequency + * components. The abovementioned source suggests a = -0.5. + */ +#define a (-0.5) +#define SQR(v) ((v) * (v)) +#define CUBE(v) ((v) * (v) * (v)) + const double xm1 = x - 1, m2x = 2 - x, m3x = 3 - x; + const double c = + (a * CUBE(x) - 5 * a * SQR(x) + 8 * a * x - 4 * a) * f0 + + ((a+2) * CUBE(xm1) - (a+3) * SQR(xm1) + 1) * f1 + + ((a+2) * CUBE(m2x) - (a+3) * SQR(m2x) + 1) * f2 + + (a * CUBE(m3x) - 5 * a * SQR(m3x) + 8 * a * m3x - 4 * a) * f3; + + if_debug6('~', "[~](%g, %g, %g, %g)order3(%g) => %g\n", + f0, f1, f2, f3, x, c); + return c; +#undef a +#undef SQR +#undef CUBE +} + +/* + * Compute a value by quadratic interpolation. + * f[] = f(0), f(1), f(2); 0 < x < 1. + * + * We used to use a quadratic formula for this, derived from + * f(0) = f0, f(1) = f1, f'(1) = (f2 - f0) / 2, but now we + * match what we believe is Acrobat Reader's behavior. + */ +static inline double +interpolate_quadratic(floatp x, floatp f0, floatp f1, floatp f2) +{ + return interpolate_cubic(x + 1, f0, f0, f1, f2); +} + +/* Calculate a result by multicubic interpolation. */ +static void +fn_interpolate_cubic(const gs_function_Sd_t *pfn, const float *fparts, + const int *iparts, const ulong *factors, + float *samples, ulong offset, int m) +{ + int j; + +top: + if (m == 0) { + uint sdata[max_Sd_n]; + + (*fn_get_samples[pfn->params.BitsPerSample])(pfn, offset, sdata); + for (j = pfn->params.n - 1; j >= 0; --j) + samples[j] = (float)sdata[j]; + } else { + float fpart = *fparts++; + int ipart = *iparts++; + ulong delta = *factors++; + int size = pfn->params.Size[pfn->params.m - m]; + float samples1[max_Sd_n], samplesm1[max_Sd_n], samples2[max_Sd_n]; + + --m; + if (is_fzero(fpart)) + goto top; + fn_interpolate_cubic(pfn, fparts, iparts, factors, samples, + offset, m); + fn_interpolate_cubic(pfn, fparts, iparts, factors, samples1, + offset + delta, m); + /* Ensure we don't try to access out of bounds. */ + /* + * If size == 1, the only possible value for ipart and fpart is + * 0, so we've already handled this case. + */ + if (size == 2) { /* ipart = 0 */ + /* Use linear interpolation. */ + for (j = pfn->params.n - 1; j >= 0; --j) + samples[j] += (samples1[j] - samples[j]) * fpart; + return; + } + if (ipart == 0) { + /* Use quadratic interpolation. */ + fn_interpolate_cubic(pfn, fparts, iparts, factors, + samples2, offset + delta * 2, m); + for (j = pfn->params.n - 1; j >= 0; --j) + samples[j] = + interpolate_quadratic(fpart, samples[j], + samples1[j], samples2[j]); + return; + } + /* At this point we know ipart > 0, size >= 3. */ + fn_interpolate_cubic(pfn, fparts, iparts, factors, samplesm1, + offset - delta, m); + if (ipart == size - 2) { + /* Use quadratic interpolation. */ + for (j = pfn->params.n - 1; j >= 0; --j) + samples[j] = + interpolate_quadratic(1 - fpart, samples1[j], + samples[j], samplesm1[j]); + return; + } + /* Now we know 0 < ipart < size - 2, size > 3. */ + fn_interpolate_cubic(pfn, fparts, iparts, factors, + samples2, offset + delta * 2, m); + for (j = pfn->params.n - 1; j >= 0; --j) + samples[j] = + interpolate_cubic(fpart + 1, samplesm1[j], samples[j], + samples1[j], samples2[j]); + } +} + +/* Calculate a result by multilinear interpolation. */ +static void +fn_interpolate_linear(const gs_function_Sd_t *pfn, const float *fparts, + const ulong *factors, float *samples, ulong offset, int m) +{ + int j; + +top: + if (m == 0) { + uint sdata[max_Sd_n]; + + (*fn_get_samples[pfn->params.BitsPerSample])(pfn, offset, sdata); + for (j = pfn->params.n - 1; j >= 0; --j) + samples[j] = (float)sdata[j]; + } else { + float fpart = *fparts++; + float samples1[max_Sd_n]; + + if (is_fzero(fpart)) { + ++factors; + --m; + goto top; + } + fn_interpolate_linear(pfn, fparts, factors + 1, samples, + offset, m - 1); + fn_interpolate_linear(pfn, fparts, factors + 1, samples1, + offset + *factors, m - 1); + for (j = pfn->params.n - 1; j >= 0; --j) + samples[j] += (samples1[j] - samples[j]) * fpart; + } +} + +static inline double +fn_Sd_encode(const gs_function_Sd_t *pfn, int i, double sample) +{ + float d0, d1, r0, r1; + double value; + int bps = pfn->params.BitsPerSample; + /* x86 machines have problems with shifts if bps >= 32 */ + uint max_samp = (bps < (sizeof(uint) * 8)) ? ((1 << bps) - 1) : max_uint; + + if (pfn->params.Range) + r0 = pfn->params.Range[2 * i], r1 = pfn->params.Range[2 * i + 1]; + else + r0 = 0, r1 = (float)((1 << bps) - 1); + if (pfn->params.Decode) + d0 = pfn->params.Decode[2 * i], d1 = pfn->params.Decode[2 * i + 1]; + else + d0 = r0, d1 = r1; + + value = sample * (d1 - d0) / max_samp + d0; + if (value < r0) + value = r0; + else if (value > r1) + value = r1; + return value; +} + +/* Evaluate a Sampled function. */ +/* A generic algorithm with a recursion by dimentions. */ +static int +fn_Sd_evaluate_general(const gs_function_t * pfn_common, const float *in, float *out) +{ + const gs_function_Sd_t *pfn = (const gs_function_Sd_t *)pfn_common; + int bps = pfn->params.BitsPerSample; + ulong offset = 0; + int i; + float encoded[max_Sd_m]; + int iparts[max_Sd_m]; /* only needed for cubic interpolation */ + ulong factors[max_Sd_m]; + float samples[max_Sd_n]; + + /* Encode the input values. */ + + for (i = 0; i < pfn->params.m; ++i) { + float d0 = pfn->params.Domain[2 * i], + d1 = pfn->params.Domain[2 * i + 1]; + float arg = in[i], enc; + + if (arg < d0) + arg = d0; + else if (arg > d1) + arg = d1; + if (pfn->params.Encode) { + float e0 = pfn->params.Encode[2 * i]; + float e1 = pfn->params.Encode[2 * i + 1]; + + enc = (arg - d0) * (e1 - e0) / (d1 - d0) + e0; + if (enc < 0) + encoded[i] = 0; + else if (enc >= pfn->params.Size[i] - 1) + encoded[i] = (float)pfn->params.Size[i] - 1; + else + encoded[i] = enc; + } else { + /* arg is guaranteed to be in bounds, ergo so is enc */ + encoded[i] = (arg - d0) * (pfn->params.Size[i] - 1) / (d1 - d0); + } + } + + /* Look up and interpolate the output values. */ + + { + ulong factor = bps * pfn->params.n; + + for (i = 0; i < pfn->params.m; factor *= pfn->params.Size[i++]) { + int ipart = (int)encoded[i]; + + offset += (factors[i] = factor) * ipart; + iparts[i] = ipart; /* only needed for cubic interpolation */ + encoded[i] -= ipart; + } + } + if (pfn->params.Order == 3) + fn_interpolate_cubic(pfn, encoded, iparts, factors, samples, + offset, pfn->params.m); + else + fn_interpolate_linear(pfn, encoded, factors, samples, offset, + pfn->params.m); + + /* Encode the output values. */ + + for (i = 0; i < pfn->params.n; ++i) + out[i] = (float)fn_Sd_encode(pfn, i, samples[i]); + + return 0; +} + +static const double double_stub = 1e90; + +static inline void +fn_make_cubic_poles(double *p, double f0, double f1, double f2, double f3, + const int pole_step_minor) +{ /* The following is poles of the polinomial, + which represents interpolate_cubic in [1,2]. */ + const double a = -0.5; + + p[pole_step_minor * 1] = (a*f0 + 3*f1 - a*f2)/3.0; + p[pole_step_minor * 2] = (-a*f1 + 3*f2 + a*f3)/3.0; +} + +static void +fn_make_poles(double *p, const int pole_step, int power, int bias) +{ + const int pole_step_minor = pole_step / 3; + switch(power) { + case 1: /* A linear 3d power curve. */ + /* bias must be 0. */ + p[pole_step_minor * 1] = (2 * p[pole_step * 0] + 1 * p[pole_step * 1]) / 3; + p[pole_step_minor * 2] = (1 * p[pole_step * 0] + 2 * p[pole_step * 1]) / 3; + break; + case 2: + /* bias may be be 0 or 1. */ + /* Duplicate the beginning or the ending pole (the old code compatible). */ + fn_make_cubic_poles(p + pole_step * bias, + p[pole_step * 0], p[pole_step * bias], + p[pole_step * (1 + bias)], p[pole_step * 2], + pole_step_minor); + break; + case 3: + /* bias must be 1. */ + fn_make_cubic_poles(p + pole_step * bias, + p[pole_step * 0], p[pole_step * 1], p[pole_step * 2], p[pole_step * 3], + pole_step_minor); + break; + default: /* Must not happen. */ + DO_NOTHING; + } +} + +/* Evaluate a Sampled function. + A cubic interpolation with a pole cache. + Allows a fast check for extreme suspection. */ +/* This implementation is a particular case of 1 dimension. + maybe we'll use as an optimisation of the generic case, + so keep it for a while. */ +static int +fn_Sd_evaluate_cubic_cached_1d(const gs_function_Sd_t *pfn, const float *in, float *out) +{ + float d0 = pfn->params.Domain[2 * 0]; + float d1 = pfn->params.Domain[2 * 0 + 1]; + float x0 = in[0]; + const int pole_step_minor = pfn->params.n; + const int pole_step = 3 * pole_step_minor; + int i0; /* A cell index. */ + int ib, ie, i, k; + double *p, t0, t1, tt; + + if (x0 < d0) + x0 = d0; + if (x0 > d1) + x0 = d1; + tt = (in[0] - d0) * (pfn->params.Size[0] - 1) / (d1 - d0); + i0 = (int)floor(tt); + ib = max(i0 - 1, 0); + ie = min(pfn->params.Size[0], i0 + 3); + for (i = ib; i < ie; i++) { + if (pfn->params.pole[i * pole_step] == double_stub) { + uint sdata[max_Sd_n]; + int bps = pfn->params.BitsPerSample; + + p = &pfn->params.pole[i * pole_step]; + fn_get_samples[pfn->params.BitsPerSample](pfn, i * bps * pfn->params.n, sdata); + for (k = 0; k < pfn->params.n; k++, p++) + *p = fn_Sd_encode(pfn, k, (double)sdata[k]); + } + } + p = &pfn->params.pole[i0 * pole_step]; + t0 = tt - i0; + if (t0 == 0) { + for (k = 0; k < pfn->params.n; k++, p++) + out[k] = *p; + } else { + if (p[1 * pole_step_minor] == double_stub) { + for (k = 0; k < pfn->params.n; k++) + fn_make_poles(&pfn->params.pole[ib * pole_step + k], pole_step, + ie - ib - 1, i0 - ib); + } + t1 = 1 - t0; + for (k = 0; k < pfn->params.n; k++, p++) { + double y = p[0 * pole_step_minor] * t1 * t1 * t1 + + p[1 * pole_step_minor] * t1 * t1 * t0 * 3 + + p[2 * pole_step_minor] * t1 * t0 * t0 * 3 + + p[3 * pole_step_minor] * t0 * t0 * t0; + if (y < pfn->params.Range[0]) + y = pfn->params.Range[0]; + if (y > pfn->params.Range[1]) + y = pfn->params.Range[1]; + out[k] = y; + } + } + return 0; +} + +static inline void +decode_argument(const gs_function_Sd_t *pfn, const float *in, double T[max_Sd_m], int I[max_Sd_m]) +{ + int i; + + for (i = 0; i < pfn->params.m; i++) { + float xi = in[i]; + float d0 = pfn->params.Domain[2 * i + 0]; + float d1 = pfn->params.Domain[2 * i + 1]; + double t; + + if (xi < d0) + xi = d0; + if (xi > d1) + xi = d1; + t = (xi - d0) * (pfn->params.Size[i] - 1) / (d1 - d0); + I[i] = (int)floor(t); + T[i] = t - I[i]; + } +} + +static inline void +index_span(const gs_function_Sd_t *pfn, int *I, double *T, int ii, int *Ii, int *ib, int *ie) +{ + *Ii = I[ii]; + if (T[ii] != 0) { + *ib = max(*Ii - 1, 0); + *ie = min(pfn->params.Size[ii], *Ii + 3); + } else { + *ib = *Ii; + *ie = *Ii + 1; + } +} + +static inline int +load_vector_to(const gs_function_Sd_t *pfn, int s_offset, double *V) +{ + uint sdata[max_Sd_n]; + int k, code; + + code = fn_get_samples[pfn->params.BitsPerSample](pfn, s_offset, sdata); + if (code < 0) + return code; + for (k = 0; k < pfn->params.n; k++) + V[k] = fn_Sd_encode(pfn, k, (double)sdata[k]); + return 0; +} + +static inline int +load_vector(const gs_function_Sd_t *pfn, int a_offset, int s_offset) +{ + if (*(pfn->params.pole + a_offset) == double_stub) { + uint sdata[max_Sd_n]; + int k, code; + + code = fn_get_samples[pfn->params.BitsPerSample](pfn, s_offset, sdata); + if (code < 0) + return code; + for (k = 0; k < pfn->params.n; k++) + *(pfn->params.pole + a_offset + k) = fn_Sd_encode(pfn, k, (double)sdata[k]); + } + return 0; +} + +static inline void +interpolate_vector(const gs_function_Sd_t *pfn, int offset, int pole_step, int power, int bias) +{ + int k; + + for (k = 0; k < pfn->params.n; k++) + fn_make_poles(pfn->params.pole + offset + k, pole_step, power, bias); +} + +static inline void +interpolate_tensors(const gs_function_Sd_t *pfn, int *I, double *T, + int offset, int pole_step, int power, int bias, int ii) +{ + if (ii < 0) + interpolate_vector(pfn, offset, pole_step, power, bias); + else { + int s = pfn->params.array_step[ii]; + int Ii = I[ii]; + + if (T[ii] == 0) { + interpolate_tensors(pfn, I, T, offset + Ii * s, pole_step, power, bias, ii - 1); + } else { + int l; + + for (l = 0; l < 4; l++) + interpolate_tensors(pfn, I, T, offset + Ii * s + l * s / 3, pole_step, power, bias, ii - 1); + } + } +} + +static inline bool +is_tensor_done(const gs_function_Sd_t *pfn, int *I, double *T, int a_offset, int ii) +{ + /* Check an inner pole of the cell. */ + int i, o = 0; + + for (i = ii; i >= 0; i--) { + o += I[i] * pfn->params.array_step[i]; + if (T[i] != 0) + o += pfn->params.array_step[i] / 3; + } + if (*(pfn->params.pole + a_offset + o) != double_stub) + return true; + return false; +} + +/* Creates a tensor of Bezier coefficients by node interpolation. */ +static inline int +make_interpolation_tensor(const gs_function_Sd_t *pfn, int *I, double *T, + int a_offset, int s_offset, int ii) +{ + /* Well, this function isn't obvious. Trying to explain what it does. + + Suppose we have a 4x4x4...x4 hypercube of nodes, and we want to build + a multicubic interpolation function for the inner 2x2x2...x2 hypercube. + We represent the multicubic function with a tensor of Besier poles, + and the size of the tensor is 4x4x....x4. Note that the corners + of the tensor are equal to the corners of the 2x2x...x2 hypercube. + + We organize the 'pole' array so that a tensor of a cell + occupies the cell, and tensors for neighbour cells have a common hyperplane. + + For a 1-dimentional case let the nodes are n0, n1, n2, n3. + It defines 3 cells n0...n1, n1...n2, n2...n3. + For the 2nd cell n1...n2 let the tensor coefficients are q10, q11, q12, q13. + We choose a cubic approximation, in which tangents at nodes n1, n2 + are parallel to (n2 - n0) and (n3 - n1) correspondingly. + (Well, this doesn't give a the minimal curvity, but likely it is + what Adobe implementations do, see the bug 687352, + and we agree that it's some reasonable). + + Then we have : + + q11 = n0 + q12 = (n0/2 + 3*n1 - n2/2)/3; + q11 = (n1/2 + 3*n2 - n3/2)/3; + q13 = n2 + + When the source node array have an insufficient nomber of nodes + along a dimension to determine tangents a cell + (this happens near the array boundaries), + we simply duplicate ending nodes. This solution is done + for the compatibility to the old code, and definitely + there exists a better one. Likely Adobe does the same. + + For a 2-dimensional case we apply the 1-dimentional case through + the first dimension, and then construct a surface by varying the + second coordinate as a parameter. It gives a bicubic surface, + and the result doesn't depend on the order of coordinates + (I proved the latter with Matematica 3.0). + Then we know that an interpolation by one coordinate and + a differentiation by another coordinate are interchangeble operators. + Due to that poles of the interpolated function are same as + interpolated poles of the function (well, we didn't spend time + for a strong proof, but this fact was confirmed with testing the + implementation with POLE_CACHE_DEBUG). + + Then we apply the 2-dimentional considerations recursively + to all dimensions. This is exactly what the function does. + + */ + int code; + + if (ii < 0) { + if (POLE_CACHE_IGNORE || *(pfn->params.pole + a_offset) == double_stub) { + code = load_vector(pfn, a_offset, s_offset); + if (code < 0) + return code; + } + } else { + int Ii, ib, ie, i; + int sa = pfn->params.array_step[ii]; + int ss = pfn->params.stream_step[ii]; + + index_span(pfn, I, T, ii, &Ii, &ib, &ie); + if (POLE_CACHE_IGNORE || !is_tensor_done(pfn, I, T, a_offset, ii)) { + for (i = ib; i < ie; i++) { + code = make_interpolation_tensor(pfn, I, T, + a_offset + i * sa, s_offset + i * ss, ii - 1); + if (code < 0) + return code; + } + if (T[ii] != 0) + interpolate_tensors(pfn, I, T, a_offset + ib * sa, sa, ie - ib - 1, + Ii - ib, ii - 1); + } + } + return 0; +} + +/* Creates a subarray of samples. */ +static inline int +make_interpolation_nodes(const gs_function_Sd_t *pfn, double *T0, double *T1, + int *I, double *T, + int a_offset, int s_offset, int ii) +{ + int code; + + if (ii < 0) { + if (POLE_CACHE_IGNORE || *(pfn->params.pole + a_offset) == double_stub) { + code = load_vector(pfn, a_offset, s_offset); + if (code < 0) + return code; + } + if (pfn->params.Order == 3) { + code = make_interpolation_tensor(pfn, I, T, 0, 0, pfn->params.m - 1); + } + } else { + int i; + int i0 = (int)floor(T0[ii]); + int i1 = (int)ceil(T1[ii]); + int sa = pfn->params.array_step[ii]; + int ss = pfn->params.stream_step[ii]; + + if (i0 < 0 || i0 >= pfn->params.Size[ii]) + return_error(gs_error_unregistered); /* Must not happen. */ + if (i1 < 0 || i1 >= pfn->params.Size[ii]) + return_error(gs_error_unregistered); /* Must not happen. */ + I[ii] = i0; + T[ii] = (i1 > i0 ? 1 : 0); + for (i = i0; i <= i1; i++) { + code = make_interpolation_nodes(pfn, T0, T1, I, T, + a_offset + i * sa, s_offset + i * ss, ii - 1); + if (code < 0) + return code; + } + } + return 0; +} + +static inline int +evaluate_from_tenzor(const gs_function_Sd_t *pfn, int *I, double *T, int offset, int ii, double *y) +{ + int s = pfn->params.array_step[ii], k, l, code; + + if (ii < 0) { + for (k = 0; k < pfn->params.n; k++) + y[k] = *(pfn->params.pole + offset + k); + } else if (T[ii] == 0) { + return evaluate_from_tenzor(pfn, I, T, offset + s * I[ii], ii - 1, y); + } else { + double t0 = T[ii], t1 = 1 - t0; + double p[4][max_Sd_n]; + + for (l = 0; l < 4; l++) { + code = evaluate_from_tenzor(pfn, I, T, offset + s * I[ii] + l * (s / 3), ii - 1, p[l]); + if (code < 0) + return code; + } + for (k = 0; k < pfn->params.n; k++) + y[k] = p[0][k] * t1 * t1 * t1 + + p[1][k] * t1 * t1 * t0 * 3 + + p[2][k] * t1 * t0 * t0 * 3 + + p[3][k] * t0 * t0 * t0; + } + return 0; +} + + +/* Evaluate a Sampled function. */ +/* A cubic interpolation with pole cache. */ +/* Allows a fast check for extreme suspection with is_tensor_monotonic. */ +static int +fn_Sd_evaluate_multicubic_cached(const gs_function_Sd_t *pfn, const float *in, float *out) +{ + double T[max_Sd_m], y[max_Sd_n]; + int I[max_Sd_m], k, code; + + decode_argument(pfn, in, T, I); + code = make_interpolation_tensor(pfn, I, T, 0, 0, pfn->params.m - 1); + if (code < 0) + return code; + evaluate_from_tenzor(pfn, I, T, 0, pfn->params.m - 1, y); + for (k = 0; k < pfn->params.n; k++) { + double yk = y[k]; + + if (yk < pfn->params.Range[k * 2 + 0]) + yk = pfn->params.Range[k * 2 + 0]; + if (yk > pfn->params.Range[k * 2 + 1]) + yk = pfn->params.Range[k * 2 + 1]; + out[k] = yk; + } + return 0; +} + +/* Evaluate a Sampled function. */ +static int +fn_Sd_evaluate(const gs_function_t * pfn_common, const float *in, float *out) +{ + const gs_function_Sd_t *pfn = (const gs_function_Sd_t *)pfn_common; + int code; + + if (pfn->params.Order == 3) { + if (POLE_CACHE_GENERIC_1D || pfn->params.m > 1) + code = fn_Sd_evaluate_multicubic_cached(pfn, in, out); + else + code = fn_Sd_evaluate_cubic_cached_1d(pfn, in, out); +# if POLE_CACHE_DEBUG + { float y[max_Sd_n]; + int k, code1; + + code1 = fn_Sd_evaluate_general(pfn_common, in, y); + if (code != code1) + return_error(gs_error_unregistered); /* Must not happen. */ + for (k = 0; k < pfn->params.n; k++) { + if (any_abs(y[k] - out[k]) > 1e-6 * (pfn->params.Range[k * 2 + 1] - pfn->params.Range[k * 2 + 0])) + return_error(gs_error_unregistered); /* Must not happen. */ + } + } +# endif + } else + code = fn_Sd_evaluate_general(pfn_common, in, out); + return code; +} + +/* Map a function subdomain to the sample index subdomain. */ +static inline int +get_scaled_range(const gs_function_Sd_t *const pfn, + const float *lower, const float *upper, + int i, float *pw0, float *pw1) +{ + float d0 = pfn->params.Domain[i * 2 + 0], d1 = pfn->params.Domain[i * 2 + 1]; + float v0 = lower[i], v1 = upper[i]; + float e0, e1, w0, w1, w; + const float small_noise = (float)1e-6; + + if (v0 < d0 || v0 > d1) + return gs_error_rangecheck; + if (pfn->params.Encode) + e0 = pfn->params.Encode[i * 2 + 0], e1 = pfn->params.Encode[i * 2 + 1]; + else + e0 = 0, e1 = (float)pfn->params.Size[i] - 1; + w0 = (v0 - d0) * (e1 - e0) / (d1 - d0) + e0; + if (w0 < 0) + w0 = 0; + else if (w0 >= pfn->params.Size[i] - 1) + w0 = (float)pfn->params.Size[i] - 1; + w1 = (v1 - d0) * (e1 - e0) / (d1 - d0) + e0; + if (w1 < 0) + w1 = 0; + else if (w1 >= pfn->params.Size[i] - 1) + w1 = (float)pfn->params.Size[i] - 1; + if (w0 > w1) { + w = w0; w0 = w1; w1 = w; + } + if (floor(w0 + 1) - w0 < small_noise * any_abs(e1 - e0)) + w0 = (floor(w0) + 1); + if (w1 - floor(w1) < small_noise * any_abs(e1 - e0)) + w1 = floor(w1); + if (w0 > w1) + w0 = w1; + *pw0 = w0; + *pw1 = w1; + return 0; +} + +/* Copy a tensor to a differently indexed pole array. */ +static int +copy_poles(const gs_function_Sd_t *pfn, int *I, double *T0, double *T1, int a_offset, + int ii, double *pole, int p_offset, int pole_step) +{ + int i, ei, sa, code; + int order = pfn->params.Order; + + if (pole_step <= 0) + return_error(gs_error_limitcheck); /* Too small buffer. */ + ei = (T0[ii] == T1[ii] ? 1 : order + 1); + sa = pfn->params.array_step[ii]; + if (ii == 0) { + for (i = 0; i < ei; i++) + *(pole + p_offset + i * pole_step) = + *(pfn->params.pole + a_offset + I[ii] * sa + i * (sa / order)); + } else { + for (i = 0; i < ei; i++) { + code = copy_poles(pfn, I, T0, T1, a_offset + I[ii] * sa + i * (sa / order), ii - 1, + pole, p_offset + i * pole_step, pole_step / 4); + if (code < 0) + return code; + } + } + return 0; +} + +static inline void +subcurve(double *pole, int pole_step, double t0, double t1) +{ + /* Generated with subcurve.nb using Mathematica 3.0. */ + double q0 = pole[pole_step * 0]; + double q1 = pole[pole_step * 1]; + double q2 = pole[pole_step * 2]; + double q3 = pole[pole_step * 3]; + double t01 = t0 - 1, t11 = t1 - 1; + double small = 1e-13; + +#define Power2(a) (a) * (a) +#define Power3(a) (a) * (a) * (a) + pole[pole_step * 0] = t0*(t0*(q3*t0 - 3*q2*t01) + 3*q1*Power2(t01)) - q0*Power3(t01); + pole[pole_step * 1] = q1*t01*(-2*t0 - t1 + 3*t0*t1) + t0*(q2*t0 + 2*q2*t1 - + 3*q2*t0*t1 + q3*t0*t1) - q0*t11*Power2(t01); + pole[pole_step * 2] = t1*(2*q2*t0 + q2*t1 - 3*q2*t0*t1 + q3*t0*t1) + + q1*(-t0 - 2*t1 + 3*t0*t1)*t11 - q0*t01*Power2(t11); + pole[pole_step * 3] = t1*(t1*(3*q2 - 3*q2*t1 + q3*t1) + + 3*q1*Power2(t11)) - q0*Power3(t11); +#undef Power2 +#undef Power3 + if (any_abs(pole[pole_step * 1] - pole[pole_step * 0]) < small) + pole[pole_step * 1] = pole[pole_step * 0]; + if (any_abs(pole[pole_step * 2] - pole[pole_step * 3]) < small) + pole[pole_step * 2] = pole[pole_step * 3]; +} + +static inline void +subline(double *pole, int pole_step, double t0, double t1) +{ + double q0 = pole[pole_step * 0]; + double q1 = pole[pole_step * 1]; + + pole[pole_step * 0] = (1 - t0) * q0 + t0 * q1; + pole[pole_step * 1] = (1 - t1) * q0 + t1 * q1; +} + +static void +clamp_poles(double *T0, double *T1, int ii, int i, double * pole, + int p_offset, int pole_step, int pole_step_i, int order) +{ + if (ii < 0) { + if (order == 3) + subcurve(pole + p_offset, pole_step_i, T0[i], T1[i]); + else + subline(pole + p_offset, pole_step_i, T0[i], T1[i]); + } else if (i == ii) { + clamp_poles(T0, T1, ii - 1, i, pole, p_offset, pole_step / 4, pole_step, order); + } else { + int j, ei = (T0[ii] == T1[ii] ? 1 : order + 1); + + for (j = 0; j < ei; j++) + clamp_poles(T0, T1, ii - 1, i, pole, p_offset + j * pole_step, + pole_step / 4, pole_step_i, order); + } +} + +static inline int /* 3 - don't know, 2 - decreesing, 0 - constant, 1 - increasing. */ +curve_monotonity(double *pole, int pole_step) +{ + double p0 = pole[pole_step * 0]; + double p1 = pole[pole_step * 1]; + double p2 = pole[pole_step * 2]; + double p3 = pole[pole_step * 3]; + + if (p0 == p1 && any_abs(p1 - p2) < 1e-13 && p2 == p3) + return 0; + if (p0 <= p1 && p1 <= p2 && p2 <= p3) + return 1; + if (p0 >= p1 && p1 >= p2 && p2 >= p3) + return 2; + /* Maybe not monotonic. + Don't want to solve quadratic equations, so return "don't know". + This case should be rare. + */ + return 3; +} + +static inline int /* 2 - decreesing, 0 - constant, 1 - increasing. */ +line_monotonity(double *pole, int pole_step) +{ + double p0 = pole[pole_step * 0]; + double p1 = pole[pole_step * 1]; + + if (p1 - p0 > 1e-13) + return 1; + if (p0 - p1 > 1e-13) + return 2; + return 0; +} + +static int /* 3 bits per guide : 3 - non-monotonic or don't know, + 2 - decreesing, 0 - constant, 1 - increasing. + The number of guides is order+1. */ +tensor_dimension_monotonity(const double *T0, const double *T1, int ii, int i0, double *pole, + int p_offset, int pole_step, int pole_step_i, int order) +{ + if (ii < 0) { + if (order == 3) + return curve_monotonity(pole + p_offset, pole_step_i); + else + return line_monotonity(pole + p_offset, pole_step_i); + } else if (i0 == ii) { + /* Delay the dimension till the end, and adjust pole_step. */ + return tensor_dimension_monotonity(T0, T1, ii - 1, i0, pole, p_offset, + pole_step / 4, pole_step, order); + } else { + int j, ei = (T0[ii] == T1[ii] ? 1 : order + 1), m = 0, mm; + + for (j = 0; j < ei; j++) { + mm = tensor_dimension_monotonity(T0, T1, ii - 1, i0, pole, p_offset + j * pole_step, + pole_step/ 4, pole_step_i, order); + m |= mm << (j * 3); + if (mm == 3) { + /* If one guide is not monotonic, the dimension is not monotonic. + Can return early. */ + break; + } + } + return m; + } +} + +static inline int +is_tensor_monotonic_by_dimension(const gs_function_Sd_t *pfn, int *I, double *T0, double *T1, int i0, int k, + uint *mask /* 3 bits per guide : 3 - non-monotonic or don't know, + 2 - decreesing, 0 - constant, 1 - increasing. + The number of guides is order+1. */) +{ + double pole[4*4*4]; /* For a while restricting with 3-in cubic functions. + More arguments need a bigger buffer, but the rest of code is same. */ + int i, code, ii = pfn->params.m - 1; + double TT0[3], TT1[3]; + + *mask = 0; + if (ii >= 3) { + /* Unimplemented. We don't know practical cases, + because currently it is only called while decomposing a shading. */ + return_error(gs_error_limitcheck); + } + code = copy_poles(pfn, I, T0, T1, k, ii, pole, 0, count_of(pole) / 4); + if (code < 0) + return code; + for (i = ii; i >= 0; i--) { + TT0[i] = 0; + if (T0[i] != T1[i]) { + if (T0[i] != 0 || T1[i] != 1) + clamp_poles(T0, T1, ii, i, pole, 0, count_of(pole) / 4, -1, pfn->params.Order); + TT1[i] = 1; + } else + TT1[i] = 0; + } + *mask = tensor_dimension_monotonity(TT0, TT1, ii, i0, pole, 0, + count_of(pole) / 4, -1, pfn->params.Order); + return 0; +} + +static int /* error code */ +is_lattice_monotonic_by_dimension(const gs_function_Sd_t *pfn, const double *T0, const double *T1, + int *I, double *S0, double *S1, int ii, int i0, int k, + uint *mask /* 3 bits per guide : 1 - non-monotonic or don't know, 0 - monotonic; + The number of guides is order+1. */) +{ + if (ii == -1) { + /* fixme : could cache the cell monotonity against redundant evaluation. */ + return is_tensor_monotonic_by_dimension(pfn, I, S0, S1, i0, k, mask); + } else { + int i1 = (ii > i0 ? ii : ii == 0 ? i0 : ii - 1); /* Delay the dimension i0 till the end of recursion. */ + int j, code; + int bi = (int)floor(T0[i1]); + int ei = (int)floor(T1[i1]); + uint m, mm, m1 = 0x49249249 & ((1 << ((pfn->params.Order + 1) * 3)) - 1); + + if (floor(T1[i1]) == T1[i1]) + ei --; + m = 0; + for (j = bi; j <= ei; j++) { + /* fixme : A better performance may be obtained with comparing central nodes with side ones. */ + I[i1] = j; + S0[i1] = max(T0[i1] - j, 0); + S1[i1] = min(T1[i1] - j, 1); + code = is_lattice_monotonic_by_dimension(pfn, T0, T1, I, S0, S1, ii - 1, i0, k, &mm); + if (code < 0) + return code; + m |= mm; + if (m == m1) /* Don't return early - shadings need to know about all dimensions. */ + break; + } + if (ii == 0) { + /* Detect non-monotonic guides. */ + m = m & (m >> 1); + } + *mask = m; + return 0; + } +} + +static inline int /* error code */ +is_lattice_monotonic(const gs_function_Sd_t *pfn, const double *T0, const double *T1, + int *I, double *S0, double *S1, + int k, uint *mask /* 1 bit per dimension : 1 - non-monotonic or don't know, + 0 - monotonic. */) +{ + uint m, mm = 0; + int i, code; + + for (i = 0; i < pfn->params.m; i++) { + if (T0[i] != T1[i]) { + code = is_lattice_monotonic_by_dimension(pfn, T0, T1, I, S0, S1, pfn->params.m - 1, i, k, &m); + if (code < 0) + return code; + if (m) + mm |= 1 << i; + } + } + *mask = mm; + return 0; +} + +static int /* 3 bits per result : 3 - non-monotonic or don't know, + 2 - decreesing, 0 - constant, 1 - increasing, + <0 - error. */ +fn_Sd_1arg_linear_monotonic_rec(const gs_function_Sd_t *const pfn, int i0, int i1, + const double *V0, const double *V1) +{ + if (i1 - i0 <= 1) { + int code = 0, i; + + for (i = 0; i < pfn->params.n; i++) { + if (V0[i] < V1[i]) + code |= 1 << (i * 3); + else if (V0[i] > V1[i]) + code |= 2 << (i * 3); + } + return code; + } else { + double VV[MAX_FAST_COMPS]; + int ii = (i0 + i1) / 2, code, cod1; + + code = load_vector_to(pfn, ii * pfn->params.n * pfn->params.BitsPerSample, VV); + if (code < 0) + return code; + if (code & (code >> 1)) + return code; /* Not monotonic by some component of the result. */ + code = fn_Sd_1arg_linear_monotonic_rec(pfn, i0, ii, V0, VV); + if (code < 0) + return code; + cod1 = fn_Sd_1arg_linear_monotonic_rec(pfn, ii, i1, VV, V1); + if (cod1 < 0) + return cod1; + return code | cod1; + } +} + +static int +fn_Sd_1arg_linear_monotonic(const gs_function_Sd_t *const pfn, double T0, double T1, + uint *mask /* 1 - non-monotonic or don't know, 0 - monotonic. */) +{ + int i0 = (int)floor(T0); + int i1 = (int)ceil(T1), code; + double V0[MAX_FAST_COMPS], V1[MAX_FAST_COMPS]; + + if (i1 - i0 > 1) { + code = load_vector_to(pfn, i0 * pfn->params.n * pfn->params.BitsPerSample, V0); + if (code < 0) + return code; + code = load_vector_to(pfn, i1 * pfn->params.n * pfn->params.BitsPerSample, V1); + if (code < 0) + return code; + code = fn_Sd_1arg_linear_monotonic_rec(pfn, i0, i1, V0, V1); + if (code < 0) + return code; + if (code & (code >> 1)) { + *mask = 1; + return 0; + } + } + *mask = 0; + return 1; +} + +#define DEBUG_Sd_1arg 0 + +/* Test whether a Sampled function is monotonic. */ +static int /* 1 = monotonic, 0 = not or don't know, <0 = error. */ +fn_Sd_is_monotonic_aux(const gs_function_Sd_t *const pfn, + const float *lower, const float *upper, + uint *mask /* 1 bit per dimension : 1 - non-monotonic or don't know, + 0 - monotonic. */) +{ + int i, code, ii = pfn->params.m - 1; + int I[4]; + double T0[count_of(I)], T1[count_of(I)]; + double S0[count_of(I)], S1[count_of(I)]; + uint m, mm, m1; +# if DEBUG_Sd_1arg + int code1, mask1; +# endif + + if (ii >= count_of(T0)) { + /* Unimplemented. We don't know practical cases, + because currently it is only called while decomposing a shading. */ + return_error(gs_error_limitcheck); + } + for (i = 0; i <= ii; i++) { + float w0, w1; + + code = get_scaled_range(pfn, lower, upper, i, &w0, &w1); + if (code < 0) + return code; + T0[i] = w0; + T1[i] = w1; + } + if (pfn->params.m == 1 && pfn->params.Order == 1 && pfn->params.n <= MAX_FAST_COMPS) { + code = fn_Sd_1arg_linear_monotonic(pfn, T0[0], T1[0], mask); +# if !DEBUG_Sd_1arg + return code; +# else + mask1 = *mask; + code1 = code; +# endif + } + m1 = (1 << pfn->params.m )- 1; + code = make_interpolation_nodes(pfn, T0, T1, I, S0, 0, 0, ii); + if (code < 0) + return code; + mm = 0; + for (i = 0; i < pfn->params.n; i++) { + code = is_lattice_monotonic(pfn, T0, T1, I, S0, S1, i, &m); + if (code < 0) + return code; + mm |= m; + if (mm == m1) /* Don't return early - shadings need to know about all dimensions. */ + break; + } +# if DEBUG_Sd_1arg + if (mask1 != mm) + return_error(gs_error_unregistered); + if (code1 != !mm) + return_error(gs_error_unregistered); +# endif + *mask = mm; + return !mm; +} + +/* Test whether a Sampled function is monotonic. */ +/* 1 = monotonic, 0 = don't know, <0 = error. */ +static int +fn_Sd_is_monotonic(const gs_function_t * pfn_common, + const float *lower, const float *upper, uint *mask) +{ + const gs_function_Sd_t *const pfn = + (const gs_function_Sd_t *)pfn_common; + + return fn_Sd_is_monotonic_aux(pfn, lower, upper, mask); +} + +/* Return Sampled function information. */ +static void +fn_Sd_get_info(const gs_function_t *pfn_common, gs_function_info_t *pfi) +{ + const gs_function_Sd_t *const pfn = + (const gs_function_Sd_t *)pfn_common; + long size; + int i; + + gs_function_get_info_default(pfn_common, pfi); + pfi->DataSource = &pfn->params.DataSource; + for (i = 0, size = 1; i < pfn->params.m; ++i) + size *= pfn->params.Size[i]; + pfi->data_size = + (size * pfn->params.n * pfn->params.BitsPerSample + 7) >> 3; +} + +/* Write Sampled function parameters on a parameter list. */ +static int +fn_Sd_get_params(const gs_function_t *pfn_common, gs_param_list *plist) +{ + const gs_function_Sd_t *const pfn = + (const gs_function_Sd_t *)pfn_common; + int ecode = fn_common_get_params(pfn_common, plist); + int code; + + if (pfn->params.Order != 1) { + if ((code = param_write_int(plist, "Order", &pfn->params.Order)) < 0) + ecode = code; + } + if ((code = param_write_int(plist, "BitsPerSample", + &pfn->params.BitsPerSample)) < 0) + ecode = code; + if (pfn->params.Encode) { + if ((code = param_write_float_values(plist, "Encode", + pfn->params.Encode, + 2 * pfn->params.m, false)) < 0) + ecode = code; + } + if (pfn->params.Decode) { + if ((code = param_write_float_values(plist, "Decode", + pfn->params.Decode, + 2 * pfn->params.n, false)) < 0) + ecode = code; + } + if (pfn->params.Size) { + if ((code = param_write_int_values(plist, "Size", pfn->params.Size, + pfn->params.m, false)) < 0) + ecode = code; + } + return ecode; +} + +/* Make a scaled copy of a Sampled function. */ +static int +fn_Sd_make_scaled(const gs_function_Sd_t *pfn, gs_function_Sd_t **ppsfn, + const gs_range_t *pranges, gs_memory_t *mem) +{ + gs_function_Sd_t *psfn = + gs_alloc_struct(mem, gs_function_Sd_t, &st_function_Sd, + "fn_Sd_make_scaled"); + int code; + + if (psfn == 0) + return_error(gs_error_VMerror); + psfn->params = pfn->params; + psfn->params.Encode = 0; /* in case of failure */ + psfn->params.Decode = 0; + psfn->params.Size = + fn_copy_values(pfn->params.Size, pfn->params.m, sizeof(int), mem); + if ((code = (psfn->params.Size == 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_pairs(&psfn->params.Encode, pfn->params.Encode, + pfn->params.m, NULL, mem)) < 0 || + (code = fn_scale_pairs(&psfn->params.Decode, pfn->params.Decode, + pfn->params.n, pranges, mem)) < 0) { + gs_function_free((gs_function_t *)psfn, true, mem); + } else + *ppsfn = psfn; + return code; +} + +/* Free the parameters of a Sampled function. */ +void +gs_function_Sd_free_params(gs_function_Sd_params_t * params, gs_memory_t * mem) +{ + gs_free_const_object(mem, params->Size, "Size"); + gs_free_const_object(mem, params->Decode, "Decode"); + gs_free_const_object(mem, params->Encode, "Encode"); + fn_common_free_params((gs_function_params_t *) params, mem); + gs_free_object(mem, params->pole, "gs_function_Sd_free_params"); + gs_free_object(mem, params->array_step, "gs_function_Sd_free_params"); + gs_free_object(mem, params->stream_step, "gs_function_Sd_free_params"); +} + +/* aA helper for gs_function_Sd_serialize. */ +static int serialize_array(const float *a, int half_size, stream *s) +{ + uint n; + const float dummy[2] = {0, 0}; + int i, code; + + if (a != NULL) + return sputs(s, (const byte *)a, sizeof(a[0]) * half_size * 2, &n); + for (i = 0; i < half_size; i++) { + code = sputs(s, (const byte *)dummy, sizeof(dummy), &n); + if (code < 0) + return code; + } + return 0; +} + +/* Serialize. */ +static int +gs_function_Sd_serialize(const gs_function_t * pfn, stream *s) +{ + uint n; + const gs_function_Sd_params_t * p = (const gs_function_Sd_params_t *)&pfn->params; + gs_function_info_t info; + int code = fn_common_serialize(pfn, s); + ulong pos; + uint count; + byte buf[100]; + const byte *ptr; + + if (code < 0) + return code; + code = sputs(s, (const byte *)&p->Order, sizeof(p->Order), &n); + if (code < 0) + return code; + code = sputs(s, (const byte *)&p->BitsPerSample, sizeof(p->BitsPerSample), &n); + if (code < 0) + return code; + code = serialize_array(p->Encode, p->m, s); + if (code < 0) + return code; + code = serialize_array(p->Decode, p->n, s); + if (code < 0) + return code; + gs_function_get_info(pfn, &info); + code = sputs(s, (const byte *)&info.data_size, sizeof(info.data_size), &n); + if (code < 0) + return code; + for (pos = 0; pos < info.data_size; pos += count) { + count = min(sizeof(buf), info.data_size - pos); + data_source_access_only(info.DataSource, pos, count, buf, &ptr); + code = sputs(s, ptr, count, &n); + if (code < 0) + return code; + } + return 0; +} + +/* Allocate and initialize a Sampled function. */ +int +gs_function_Sd_init(gs_function_t ** ppfn, + const gs_function_Sd_params_t * params, gs_memory_t * mem) +{ + static const gs_function_head_t function_Sd_head = { + function_type_Sampled, + { + (fn_evaluate_proc_t) fn_Sd_evaluate, + (fn_is_monotonic_proc_t) fn_Sd_is_monotonic, + (fn_get_info_proc_t) fn_Sd_get_info, + (fn_get_params_proc_t) fn_Sd_get_params, + (fn_make_scaled_proc_t) fn_Sd_make_scaled, + (fn_free_params_proc_t) gs_function_Sd_free_params, + fn_common_free, + (fn_serialize_proc_t) gs_function_Sd_serialize, + } + }; + int code; + int i; + + *ppfn = 0; /* in case of error */ + code = fn_check_mnDR((const gs_function_params_t *)params, + params->m, params->n); + if (code < 0) + return code; + if (params->m > max_Sd_m) + return_error(gs_error_limitcheck); + switch (params->Order) { + case 0: /* use default */ + case 1: + case 3: + break; + default: + return_error(gs_error_rangecheck); + } + switch (params->BitsPerSample) { + case 1: + case 2: + case 4: + case 8: + case 12: + case 16: + case 24: + case 32: + break; + default: + return_error(gs_error_rangecheck); + } + for (i = 0; i < params->m; ++i) + if (params->Size[i] <= 0) + return_error(gs_error_rangecheck); + { + gs_function_Sd_t *pfn = + gs_alloc_struct(mem, gs_function_Sd_t, &st_function_Sd, + "gs_function_Sd_init"); + int bps, sa, ss, i, order; + + if (pfn == 0) + return_error(gs_error_VMerror); + pfn->params = *params; + if (params->Order == 0) + pfn->params.Order = 1; /* default */ + pfn->params.pole = NULL; + pfn->params.array_step = NULL; + pfn->params.stream_step = NULL; + pfn->head = function_Sd_head; + pfn->params.array_size = 0; + if (pfn->params.m == 1 && pfn->params.Order == 1 && pfn->params.n <= MAX_FAST_COMPS && !DEBUG_Sd_1arg) { + /* Won't use pole cache. Call fn_Sd_1arg_linear_monotonic instead. */ + } else { + pfn->params.array_step = (int *)gs_alloc_byte_array(mem, + max_Sd_m, sizeof(int), "gs_function_Sd_init"); + pfn->params.stream_step = (int *)gs_alloc_byte_array(mem, + max_Sd_m, sizeof(int), "gs_function_Sd_init"); + if (pfn->params.array_step == NULL || pfn->params.stream_step == NULL) + return_error(gs_error_VMerror); + bps = pfn->params.BitsPerSample; + sa = pfn->params.n; + ss = pfn->params.n * bps; + order = pfn->params.Order; + for (i = 0; i < pfn->params.m; i++) { + pfn->params.array_step[i] = sa * order; + sa = (pfn->params.Size[i] * order - (order - 1)) * sa; + pfn->params.stream_step[i] = ss; + ss = pfn->params.Size[i] * ss; + } + pfn->params.pole = (double *)gs_alloc_byte_array(mem, + sa, sizeof(double), "gs_function_Sd_init"); + if (pfn->params.pole == NULL) + return_error(gs_error_VMerror); + for (i = 0; i < sa; i++) + pfn->params.pole[i] = double_stub; + pfn->params.array_size = sa; + } + *ppfn = (gs_function_t *) pfn; + } + return 0; +} |