Index: src/pdf_function.c =================================================================== RCS file: /cvsroot/pdf/libgnupdf/src/pdf_function.c,v retrieving revision 1.3 diff -u -p -r1.3 pdf_function.c --- src/pdf_function.c 31 Oct 2007 16:53:16 -0000 1.3 +++ src/pdf_function.c 11 Nov 2007 13:38:44 -0000 @@ -1,85 +1,2088 @@ -/* -*- mode: C -*- Time-stamp: "07/10/31 17:37:28 jemarch" +/* Copyright (C) 2007 Free Software Foundation, Inc. */ + + +#include +#include +#include +#ifdef HAVE_MALLOC_H + #include +#endif +#include + + +#include +#include +#include + +#include +#include +#include +#include + + +PDF_GLOBALS + +static const char *error = "" ; +/* thread-local storage ? */ + +const char *pdf_get_last_error_from_func(void) +{ + const char *r = error ; + error = "" ; + return r ; +} + +/* On type 0 functions. + * Linear interpolation works the obvious way in 1D, + * and there is a simple,recursive generalization to multilinear interpolation. + * any performance penalty would show up only for large m ( # of inputs ). * - * File: pdf_function.c - * Author: Jose E. Marchesi (address@hidden) - * Date: Sun Sep 9 21:04:31 2007 + * True splines have C2 continuity, at least in the one-dimensional case. + * Solving the linear equations is easy ( see below ), and the results may be cached. + * There is, however, no straightforward generalization to m > 1. + * An iterative solution would require to solve many systems of linear equations. + * Only the outermost results would be cacheable. + * It does not feel right. * - * GNU PDF Library - Function common data type + * If we use Catmull-Rom splines, which are common in computer graphics, + * these difficulties vanish. + * They depend on four surrounding samples instead of two, + * but the algorithm is almost the same for order 1 and order 3. * */ -/* Copyright (C) 2007 Free Software Foundation, Inc. */ +static inline pdf_real_t +pdf_get_number( pdf_obj_t t ) ; -/* 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 3 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, see . +static inline double +clip( double x, const pdf_real_t r[2] ) +/* HF: clip() will pass NaNs */ +{ + if ( x < r[0] ) + x = r[0] ; + else if ( x > r[1] ) + x = r[1] ; + return x ; +} + +static inline void +setmap( double map[2], const pdf_real_t to[2] , const pdf_real_t from[2] ) +{ + map[0] = ( to[1] - to[0]) / ( from[1] - from[0] ) ; + map[1] = to[0] - map[0] * from[0] ; +} + +#if 0 +/* True Splines, see comments above */ +/* build two and expect to throw away one of them */ +/* the eqn sover is not used anymore */ + + +static pdf_real_t +eval_spline( + pdf_real_t x, + unsigned nsamples, + const pdf_real_t y[], + const pdf_real_t y1[] ) +{ + pdf_real_t t ; + pdf_real_t c,d ; + unsigned i ; + + if ( x <= 0 ) + return y[0] ; + else if ( x >= nsamples-1 ) + return y[nsamples-1] ; + + t = floor(x) ; + i = (unsigned)t ; + t = x-t ; + + assert( i < nsamples ) ; + + if ( nsamples < 4 ) + { + return y[i] + y1[i]*t ; + } + + /* From the spec: + * The /Size for an input dimension can be 1, in which case all input values in + * that dimension will be mapped to the single allowed value. If Size is less than 4, + * cubic spline interpolation is not possible and Order 3 will be ignored if specified. + * This seems to exclude the use of quadratic interpolation if nsamples = 3 + * what happens in mixed cases ? + * */ + + c = 3*( y[i+1]-y[i] ) - ( 2*y1[i] + y1[i+1] ); + d = -2*( y[i+1]-y[i] ) + ( y1[i] + y1[i+1] ) ; + + return (( d*t + c ) *t + y1[i] ) *t + y[i] ; +} + +static void +solve_spline( + unsigned nsamples, + const pdf_real_t y[], + pdf_real_t y1[], + pdf_real_t d[], + pdf_real_t b[] ) +/* calculate the vector y1[] of derivatives for a not-a-knot spline + * by solving a tridiagonal system. + * d[] and b[] are buffers for diagonal and right side, respectively */ +{ + unsigned i ; + if ( nsamples < 4 ) + { + for ( i = 0 ; i < nsamples-1 ; i++ ) + y1[i] = y[i+1] - y[i] ; + /* y1[nsamples-1] is not used in this case */ + } + else + { + b[0] = (-5*y[0] + 4*y[1] + y[2])/4 ; + d[0] = 0.5 ; + for ( i = 1 ; i < nsamples-1 ; i++ ) + { + b[i] = 3*( y[i+1] - y[i-1] ) ; + d[i] = 4 ; + } + b[i] = (5*y[i] - 4*y[i-1] - y[i-2])/4 ; + d[i] = 0.5 ; -#include + /* forward */ + for ( i = 0 ; i < nsamples-1 ; i++ ) + { + b[i+1] -= b[i]/d[i] ; + d[i+1] -= 1/d[i] ; + } -#ifdef HAVE_MALLOC_H - #include -#else - #include -#endif /* HAVE_MALLOC_H */ + /* backward */ + for ( i = nsamples-1 ; i > 0 ; --i ) + { + y1[i] = b[i]/d[i] ; + b[i-1] -= y1[i] ; + } + y1[0] = b[0]/d[0] ; + } +} -#include -#include -pdf_func_t -pdf_create_func_0 (pdf_obj_t stream) +static void pdf_spline_init( struct pdf_func0_s *p ) +/* precalculate spline coefficients, final initialization steps */ { - if (!IS_STREAM(stream)) - { - return NULL; - } + unsigned i,j,k,l ; + unsigned max ; + + max = 0 ; + for ( k = 0 ; k < p->m ; k++ ) + { + if ( max < p->size[k] ) + max = p->size[k] ; + } + + p->b = xmalloc( max * sizeof(p->b[0]) ) ; + p->d = xmalloc( max * sizeof(p->d[0]) ) ; + + p->y1 = xmalloc( sizeof(p->y1[0]) * p->n * p->nsamples ) ; + + l = p->nsamples/p->size[0] ; + + p->r = xmalloc( 2*sizeof(p->r[0]) * l ) ; - return NULL; + for ( j = 0 ; j < p->n ; j++ ) + { + for ( i = 0 ; i < l ; i++ ) + { + solve_spline(p->size[0], + p->y + j*p->nsamples + i*p->size[0], + p->y1 + j*p->nsamples + i*p->size[0], p->b , p->d) ; + } + } } -pdf_func_t -pdf_create_func_2 (pdf_obj_t dict) +static int +pdf_eval_spline( pdf_func_t fun , const pdf_real_t t[], pdf_real_t out[] ) +{ + struct pdf_func0_s *p ; + unsigned i,j,l,k ; + pdf_real_t *r,*r1 ; + pdf_real_t x,y ; + + p = &fun->u.t0 ; + + + l = p->nsamples/p->size[0] ; + + r = p->r ; + r1 = p->r + l ; + + + + for ( j = 0 ; j < p->n ; j++ ) + { + k = 0 ; + x = p->encode[0] * t[0] + p->encode[1] ; + for ( i = 0 ; i < l ; i++ ) + { + r[i] = eval_spline( x , p->size[k], + p->y + (j * p->nsamples + i*p->size[k] ) , + p->y1+ (j * p->nsamples + i*p->size[k]) ) ; + } + + for ( k = 1 ; k < p->m ; k++ ) + { + l /= p->size[k] ; + + for ( i = 0 ; i < l ; i++ ) + solve_spline(p->size[k], r+i*p->size[k],r1+i*p->size[k],p->b,p->d) ; + + x = p->encode[2*k] * t[k] + p->encode[2*k+1]; + for ( i = 0 ; i < l ; i++ ) + { + r[i] = eval_spline( x , p->size[k], r+i*p->size[k],r1+i*p->size[k]) ; + } + /* this reuse of r works! */ + } + + + y = r[0] ; + y = fun->u.t0.decode[2*j] * y + fun->u.t0.decode[2*j+1] ; + + if ( fun->range ) + y = clip(y,fun->range+2*j) ; + out[j] = y ; + } + return 0 ; +} + + + +static void +pdf_linear_init( struct pdf_func0_s *p ) +{ + p->r = xmalloc( sizeof(p->r[0]) * ( 1U << p->m ) ) ; +} + + +static int +pdf_eval_linear( pdf_func_t fun, const pdf_real_t t[], pdf_real_t out[] ) +/* the original code ( postscript ) was almost certainly more stack oriented */ { - if (!IS_DICT(dict)) + unsigned xcp ; + double x,y ; + struct pdf_func0_s *p ; + unsigned m ; + unsigned i,j,s,k,i0 ; + unsigned cc ; + double t0[TYPE0_MAX_DIM] ; + double t1[TYPE0_MAX_DIM] ; + + p = &fun->u.t0 ; + m = fun->m ; + + assert( m <= TYPE0_MAX_DIM ) ; + + xcp = 0 ; + i0 = 0 ; + cc = 1 ; + for ( k = 0 ; k < m ; k++ ) + { + x = t[k] * p->encode[2*k] + p->encode[2*k+1] ; + + if ( !( x < p->size[k]-1 ) ) /* catch NaN, and size [k] == 1, too */ + { + i = p->size[k]-1 ; + xcp |= (1U<size[k] ; + } + j = 0 ; + + for ( j = 0 ; j < p->n ; j++ ) + { + for ( i = 0 ; i < (1U<size[k] ; + } + p->r[i] = p->y[s] ; + printf("\n\tsample[%u] = [%u]",i,s) ; + } + printf("\n") ; + + for ( k = 0 ; k < m ; k++ ) /* reduction steps */ + { + /* i is dominant loop index. */ + for ( i = 0 ; i < 1U<<(m-k-1) ; i++ ) + { + p->r[i] = t0[k]*p->r[2*i] + t1[k] * p->r[2*i+1] ; + } + } + + y = p->r[0] ; + y = fun->u.t0.decode[2*j] * y + fun->u.t0.decode[2*j+1] ; + + if ( fun->range ) + y = clip(y,fun->range+2*j) ; + + out[j] = y ; + } + return 0 ; +} + +#endif +/* TRUE_C2_NOT_A_KNOT_SPLINES */ + + + + +static double +linear_interpolation( unsigned i, + const double w0[], const double w1[], + const unsigned k[], + const pdf_real_t y[], + unsigned stride, + const unsigned size[] ) +/* w0[i] and w1[i] are weights for samples surrounding samples k[i] and k[i+1] in the ith dimension */ +/* cubic interpolation will need four weights */ +/* Samples with weight 0 may have an index out of range */ +{ + unsigned j ; + j = k[i] ; + stride /= size[i] ; + + if ( w1[i] ) /* zero indicates a possibly invalid array index */ + { + assert( j+1 < size[i]) ; + if ( i == 0 ) + return w0[i] * y[j] + w1[i] * y[j+1] ; + else + return w0[i] * linear_interpolation(i-1,w0,w1,k,y+j*stride,stride,size) + + w1[i] * linear_interpolation(i-1,w0,w1,k,y+(j+1)*stride,stride,size) ; + } + else + { + if ( i == 0 ) + return y[j] ; + else + return linear_interpolation(i-1,w0,w1,k,y+j*stride,stride,size) ; + } +} + +static int +pdf_eval_linear( pdf_func_t fun, const pdf_real_t in[], pdf_real_t out[] ) +{ + unsigned i,j ; + double t ; + double y ; + for ( i = 0 ; i < fun->m ; i++ ) + { + t = in[i] * fun->u.t0.encode[2*i] + fun->u.t0.encode[2*i+1] ; + if ( isnan(t) ) + { + error = "domain error"; + return -1 ; + } + if ( t >= fun->u.t0.size[i]-1 ) + { + fun->u.t0.k[i] = fun->u.t0.size[i]-1 ; + fun->u.t0.w0[i] = 1 ; + fun->u.t0.w1[i] = 0 ; + } + else if ( t <= 0 ) + { + fun->u.t0.k[i] = 0 ; + fun->u.t0.w0[i] = 1 ; + fun->u.t0.w1[i] = 0 ; + } + else + { + fun->u.t0.k[i] = (unsigned)floor(t) ; + t = t - floor(t) ; + fun->u.t0.w0[i] = 1-t ; + fun->u.t0.w1[i] = t ; + } + } + + + for ( j = 0 ; j < fun->n ; j++ ) + { + y = linear_interpolation( fun->m-1,fun->u.t0.w0,fun->u.t0.w1,fun->u.t0.k, + &fun->u.t0.y[j*fun->u.t0.nsamples], fun->u.t0.nsamples, fun->u.t0.size ) ; + + y = fun->u.t0.decode[2*j] * y + fun->u.t0.decode[2*j+1] ; + if ( fun->range ) + y = clip(y,fun->range+2*j) ; + if ( isnan(y) ) + { + error = "range error" ; + return -1 ; + } + out[j] = y ; + } + return 0 ; +} + + + +static double +spline_interpolation( unsigned i, + const double wm[], const double w0[], const double w1[], const double w2[], + const unsigned k[], const pdf_real_t y[], unsigned stride, const unsigned size[] ) +/* Catmull-Rom splines, cf wikipedia or almost any texbook on CG */ +{ + unsigned j ; + double sum ; + stride /= size[i] ; + + j = k[i] ; + sum = 0 ; + if ( wm[i] ) + sum += wm[i] * ( (i) ? spline_interpolation(i-1,wm,w0,w1,w2,k,y+(j-1)*stride,stride,size) : y[j-1] ) ; + if ( w0[i] ) + sum += w0[i] * ( (i) ? spline_interpolation(i-1,wm,w0,w1,w2,k,y+(j)*stride,stride,size) : y[j] ) ; + if ( w1[i] ) + sum += w1[i] * ( (i) ? spline_interpolation(i-1,wm,w0,w1,w2,k,y+(j+1)*stride,stride,size) : y[j+1] ) ; + if ( w2[i] ) + sum += w2[i] * ( (i) ? spline_interpolation(i-1,wm,w0,w1,w2,k,y+(j+2)*stride,stride,size) : y[j+2] ) ; + /* the caller ensures that w[*] is zero if y[j+*] does not exist */ + /* if this seems ugly, try doing it with ... if ( j >= 1 && j < size[i]-2 && ... */ + return sum ; +} + + +static int +pdf_eval_spline( pdf_func_t fun, const pdf_real_t in[], pdf_real_t out[] ) +{ + unsigned i,j ; + double t,v ; + for ( i = 0 ; i < fun->m ; i++ ) + { + t = in[i] * fun->u.t0.encode[2*i] + fun->u.t0.encode[2*i+1] ; + if ( isnan(t) ) + { + error = "domain error" ; + return -1 ; + } + + v = t - floor(t) ; + fun->u.t0.k[i] = (unsigned)floor(t) ; + if ( t >= fun->u.t0.size[i]-1 ) + { + fun->u.t0.k[i] = fun->u.t0.size[i]-1 ; + fun->u.t0.wm[i] = 0 ; + fun->u.t0.w0[i] = 1 ; + fun->u.t0.w1[i] = 0 ; + fun->u.t0.w2[i] = 0 ; + } + else if ( t+1 >= fun->u.t0.size[i]-1 ) + { + if ( t-1 >= 0 ) + { + fun->u.t0.wm[i] = -0.5*v*(1-v) ; + fun->u.t0.w0[i] = 1-v*v ; + fun->u.t0.w1[i] = 0.5*v*(1+v) ; + } + else /* linear */ + { + fun->u.t0.wm[i] = 0 ; + fun->u.t0.w0[i] = 1-v ; + fun->u.t0.w1[i] = v ; + } + fun->u.t0.w2[i] = 0 ; + } + else + { + if ( t >= 1 ) + { + fun->u.t0.wm[i] = ((-0.5 * v + 1)*v-0.5)*v ; + fun->u.t0.w0[i] = (1.5* v -2.5)*v*v+1 ; + fun->u.t0.w1[i] = ((-1.5*v+2)*v+0.5)*v ; + fun->u.t0.w2[i] = (0.5*v-0.5)*v*v ; + } + else if ( t > 0 ) + { + assert( fun->u.t0.k[i] == 0 ) ; + fun->u.t0.wm[i] = 0 ; + fun->u.t0.w0[i] = (0.5*v-1.5)*v +1 ; + fun->u.t0.w1[i] = (2-v)*v ; + fun->u.t0.w2[i] = 0.5*(v-1)*v ; + } + else /* no samples */ + { + fun->u.t0.k[i] = 0 ; + fun->u.t0.wm[i] = 0 ; + fun->u.t0.w0[i] = 1 ; + fun->u.t0.w1[i] = 0 ; + fun->u.t0.w2[i] = 0 ; + } + } + } + + + for ( j = 0 ; j < fun->n ; j++ ) + { + double y ; + y = spline_interpolation( fun->m-1, + fun->u.t0.wm,fun->u.t0.w0,fun->u.t0.w1,fun->u.t0.w2, + fun->u.t0.k,&fun->u.t0.y[j*fun->u.t0.nsamples], fun->u.t0.nsamples, fun->u.t0.size ) ; + + y = y * fun->u.t0.decode[2*j] + fun->u.t0.decode[2*j+1] ; + if ( fun->range ) + y = clip(y,fun->range+2*j) ; + if ( isnan(y) ) + { + error = "range error" ; + return -1 ; + } + out[j] = y ; + } + return 0 ; +} + + +void pdf_destroy_func( pdf_func_t t ) +{ + unsigned i ; + + if (!t ) + return ; + /* pdf_destroy_func(0) is valid. */ + /* this simplifies cleanup after a 'goto fail' */ + free(t->domain) ; + free(t->range) ; + + switch( t->type ) + { + case 0: + free(t->u.t0.size) ; + free(t->u.t0.y) ; + free(t->u.t0.y1) ; + free(t->u.t0.r) ; + + free(t->u.t0.wm) ; + free(t->u.t0.w0) ; + free(t->u.t0.w1) ; + free(t->u.t0.w2) ; + + free(t->u.t0.b) ; + free(t->u.t0.d) ; + free(t->u.t0.encode) ; + free(t->u.t0.decode) ; + break ; + + case 2: + free(t->u.t2.c0) ; + free(t->u.t2.c1) ; + break ; + + case 3: + for ( i = 0 ; i < t->u.t3.k ; i++ ) + pdf_destroy_func( t->u.t3.functions[i] ) ; + free( t->u.t3.functions ) ; + free( t->u.t3.bounds ) ; + free( t->u.t3.encode ) ; + break ; + case 4: + free( t->u.t4.opcodes ) ; + break ; + } + + free(t) ; +} + + + + +static int +pdf_eval_exponential( pdf_func_t t , const pdf_real_t in[], pdf_real_t out[] ) +{ + unsigned j ; + pdf_real_t x,y ; + pdf_real_t c1,c0 ; + + x = clip(in[0],t->domain) ; + for ( j = 0 ; j < t->n ; j++ ) + { + c0 = (t->u.t2.c0) ? t->u.t2.c0[j] : 0.0 ; + c1 = (t->u.t2.c1) ? t->u.t2.c1[j] : 1.0 ; + y = c0 + pow(x,t->u.t2.N)*(c1-c0) ; + if ( t->range ) + y = clip(y, t->range+2*j) ; + out[j] = y ; + if ( isnan(y) ) + { + error = "range error" ; + return -1 ; + } + } + return 0 ; +} + +static int +pdf_eval_stitch( pdf_func_t t , const pdf_real_t in[], pdf_real_t out[] ) +{ + pdf_func_t f ; + pdf_real_t x ; + unsigned lo,hi ; + unsigned i ; + + assert( t->domain[0] == t->u.t3.bounds[0] ) ; + assert( t->domain[1] == t->u.t3.bounds[t->u.t3.k] ) ; + + + x = in[0] ; + + if ( isnan(x) ) + { + error = "domain error" ; + return -1 ; + } + + if ( ! ( x >= t->domain[0]) ) /* NaN */ + { + i = 0 ; + } + else if ( x >= t->domain[1] ) + { + i = t->u.t3.k - 1 ; + x = t->domain[1] ; + } + else + { + lo = 0 ; + hi = t->u.t3.k - 1 ; + /* k is at least 1 , one constituent function */ + while ( lo <= hi ) + { + i = lo + (hi-lo)/2 ; + if ( x < t->u.t3.bounds[i] ) + hi = i-1 ; + else if ( x >= t->u.t3.bounds[i+1] ) + lo = i+1 ; + else + { + break ; + } + } + if ( lo > hi ) + { + error = "/FunctionType 3: internal error" ; + return -1 ; + } + } + + x = x * t->u.t3.encode[2*i] + t->u.t3.encode[2*i+1] ; + + f = t->u.t3.functions[i] ; + return f->eval(f,&x,out) ; + +} + + +static int +init_func3( pdf_func_t f, pdf_obj_t dict ) +{ + pdf_obj_t functions,bounds,encode ; + pdf_obj_t t ; + unsigned i,k ; + + if ( f->m != 1 ) + { + error ="/FunctionType 4: input dimension is not 1" ; + return -1 ; + } + + functions = pdf_get_dict_entry(dict,PDF_N_FUNCTIONS) ; + if ( !functions || !IS_ARRAY(functions) ) + { + error = "missing /Functions array in type 3 function dictionary" ; + return -1 ; + } + + k = pdf_get_array_size(functions) ; + if ( !k ) + { + error = "empty /Functions array in type 3 function dictionary" ; + return -1 ; + } + + f->u.t3.k = k ; + + bounds = pdf_get_dict_entry(dict,PDF_N_BOUNDS) ; + if ( !bounds || !IS_ARRAY(bounds) ) + { + error = "missing /Bounds array in type 3 function dictionary" ; + return -1 ; + } + + if ( k-1 != pdf_get_array_size(bounds) ) + { + error = "number of /Bounds entries does not match number of functions"; + return -1 ; + } + + encode = pdf_get_dict_entry(dict,PDF_N_ENCODE) ; + if ( !encode || !IS_ARRAY(encode) ) + { + error = "/FunctionType 3: missing /Encode array in function dictionary" ; + return -1 ; + } + if ( 2*k != pdf_get_array_size(encode) ) + { + error = "/FunctionType 3: number of /Encode entries does not match number of functions"; + return -1 ; + } + + f->u.t3.functions = xmalloc( k * sizeof(*f->u.t3.functions) ) ; + for ( i = 0 ; i < k ; i++ ) + f->u.t3.functions[i] = NULL; /* allow safe destruction */ + + for ( i = 0 ; i < k ; i++ ) + { + f->u.t3.functions[i] = pdf_create_func( pdf_get_array_elt(functions,i) ) ; + if ( !f->u.t3.functions[i] ) + { + /* error has been set by pdf_create_func */ + return -1 ; + } + if ( !f->n ) + f->n = f->u.t3.functions[i]->n ; + else if ( f->n != f->u.t3.functions[i]->n ) + { + error = "/FunctionType 3: mismatch of output dimensions" ; + return -1 ; + } + } + + + + + f->u.t3.bounds = xmalloc( (1+k)*sizeof( f->u.t3.bounds[0] ) ) ; + f->u.t3.bounds[0] = f->domain[0] ; + for ( i = 1 ; i < k ; i++ ) + { + t = pdf_get_array_elt(bounds,i-1) ; + if ( !t || !IS_NUMBER(t) ) + { + error = "bad entry in /Bounds array" ; + return -1 ; + } + f->u.t3.bounds[i] = pdf_get_number(t) ; + } + f->u.t3.bounds[i] = f->domain[1] ; + + f->u.t3.encode = xmalloc( 2 * k * sizeof(f->u.t3.encode[0]) ) ; + for ( i = 0 ; i < k ; i++ ) + { + pdf_real_t enc[2] ; + t = pdf_get_array_elt(encode,2*i) ; + if ( !t || !IS_NUMBER(t) ) + { + error = "bad entry in /Encode array" ; + return -1 ; + } + enc[0] = pdf_get_number(t) ; + t = pdf_get_array_elt(encode,2*i+1) ; + if ( !t || !IS_NUMBER(t) ) + return -1 ; + enc[1] = pdf_get_number(t) ; + setmap(f->u.t3.encode+2*i, enc, f->u.t3.bounds+i ) ; + } + + f->eval = pdf_eval_stitch ; + + return 0 ; +} + + + +/* ------- decoder utilities ------ */ + +static unsigned int * +pdf_get_pos_array( pdf_obj_t t ) +{ + unsigned int *a ; + pdf_obj_t e ; + int i,n ; + + n = pdf_get_array_size(t) ; + if ( n <= 0 ) + return NULL ; + + a = xmalloc( sizeof(*a) * n ) ; + for ( i = 0 ; i < n ; i++ ) + { + e = pdf_get_array_elt(t,i) ; + if ( !e || !IS_INT(e) ) + goto fail ; + a[i] = pdf_get_int(e) ; + if ( a[i] <= 0 ) + goto fail ; + } + return a ; +fail: + free(a) ; + return NULL ; +} + +static inline pdf_real_t pdf_get_number( pdf_obj_t t ) +{ + if ( IS_REAL(t) ) + return pdf_get_real(t) ; + else if ( IS_INT(t) ) + return pdf_get_int(t) ; + else + return 0 ; /* bad */ +} + +static +pdf_real_t * pdf_get_real_array( pdf_obj_t t ) +/* return a homogeneous pdf_obj_t array as a C type + * returns xmalloc'ed memory or NULL on error ( type error or size 0 ) */ +{ + pdf_real_t *a ; + pdf_obj_t e ; + int i,n ; + + n = pdf_get_array_size(t) ; + if ( n <= 0 ) + return NULL ; + + a = xmalloc( sizeof(*a) * n ) ; + for ( i = 0 ; i < n ; i++ ) + { + e = pdf_get_array_elt(t,i) ; + if ( !e || !IS_NUMBER(e) ) + goto fail ; + if ( IS_REAL(e) ) + a[i] = pdf_get_real(e) ; + else if ( IS_INT(e) ) + a[i] = pdf_get_int(e) ; + else + goto fail ; + } + return a ; +fail: + free(a) ; + return NULL ; +} + + +static pdf_real_t * +read_type0_sample_table( pdf_stm_t stm, unsigned bps, unsigned nsamples , unsigned n ) +{ + pdf_real_t *y ; + uint32_t data,mask ; + unsigned avail ; + unsigned i ; + int c ; + + + if ( !n || nsamples > UINT_MAX/n ) + return NULL ; + + + n = n * nsamples ; + + y = xmalloc( nsamples * sizeof(*y) ) ; + /* single precision pdf_float_t loses precision with bps 32 */ + /* this should never be visible with pdf_float_t in[] and out[] */ + + avail = 0 ; + data = 0 ; + mask = ( bps >= 32 ) ? 0xFFFFFFFFU : ((1U<> ( avail - bps ) ) & mask ; + avail -= bps ; + } + return y ; +fail: + free(y) ; + return NULL ; +} + + + + + +static int +init_func0( pdf_func_t f, pdf_obj_t dict, pdf_stm_t stm ) +{ + pdf_obj_t t ; + int bps ; + int order ; + int i,j,m,k ; + unsigned nsamples ; + unsigned nsamples_limit ; + + if ( !stm ) + { + error = "/Type 0 function: no stream" ; + return -1 ; + } + + if ( !f->range ) + { + error = "/Type 0 function: /Range is mandatory" ; + return -1 ; + } + + + t = pdf_get_dict_entry(dict,PDF_N_SIZE) ; + if ( !t || !IS_ARRAY(t) ) + { + error = "missing /Size array in /Type 0 function dictionary" ; + return -1 ; + } + + m = pdf_get_array_size(t) ; + if ( m != f->m ) + { + error = "/Size mismatch" ; + return -1 ; + } + + f->u.t0.size = pdf_get_pos_array(t) ; + if ( !f->u.t0.size ) + { + + error = "bad entry in /Size array" ; + return -1 ; + } + + nsamples = 1 ; + nsamples_limit = 1U<<28/f->n ; /* insane values */ + for ( i = 0 ; i < m ; i++ ) + { + if ( f->u.t0.size[i] > nsamples_limit/nsamples ) /* avoid overflow */ + { + error = "/FunctionType 0: Sample Table too large" ; + return -1 ; + } + nsamples *= f->u.t0.size[i] ; + } + f->u.t0.nsamples = nsamples ; + + + t = pdf_get_dict_entry(dict,PDF_N_BITS_PER_SAMPLE) ; + if ( !t || !IS_INT(t) ) + { + error = "missing or mistyped /BitsPerSample" ; + return -1 ; + } + bps = pdf_get_int(t) ; + switch (bps) + { + case 1: + case 2: + case 4: + case 8: + case 12: + case 16: + case 24: + case 32: /* possible loss of precision in conversion to pdf_float_t */ + break ; + default: + error = "bad /BitsPerSample value" ; + /* ths implementation allows any value between 1 and 32 */ + /* the spec does not */ + return -1 ; + } + + order = 1 ; /* default */ + t = pdf_get_dict_entry(dict,PDF_N_ORDER) ; + if ( t ) + { + order = ( IS_INT(t) ) ? pdf_get_int(t) : 0 ; + if ( order != 1 && order != 3 ) + { + error = "bad /Order in /FunctionType 0" ; + return -1 ; + } + } + + f->u.t0.m = f->m ; + f->u.t0.n = f->n ; + + if ( f->u.t0.m > 12 ) /* sanity check */ + { + error = "/FunctionType 0: input dim too high ( <= 12 )" ; + return -1 ; + } + + f->u.t0.y = read_type0_sample_table(stm,bps,nsamples,f->n) ; + + if ( !f->u.t0.y ) + { + error = "/FunctionType 0: error while reading sample table" ; + return -1 ; + } + + + f->u.t0.encode = xmalloc( 2*f->m * sizeof(f->u.t0.encode[0] ) ) ; + t = pdf_get_dict_entry(dict,PDF_N_ENCODE) ; + if ( t ) + { + pdf_real_t *enc ; + if ( !IS_ARRAY(t) ) + { + error = "bad /Encode array" ; + return -1 ; + } + if ( 2*f->m != pdf_get_array_size(t) ) + { + error = "/Encode array size mismatch " ; + return -1 ; + } + + enc = pdf_get_real_array(t) ; + if ( enc ) + { + for ( k = 0 ; k < f->m ; k++ ) + setmap( f->u.t0.encode + 2*k , enc+2*k , f->domain+2*k ); + free(enc) ; + } + else + { + error = "bad /Encode array" ; + return -1 ; + } + } + else /* default encoding array */ + { + pdf_real_t enc[2] ; + enc[0] = 0 ; + for ( k = 0 ; k < f->m ; k++ ) + { + enc[1] = f->u.t0.size[k] - 1 ; + setmap( f->u.t0.encode + 2*k, enc, f->domain+2*k ) ; + } + } + + + t = pdf_get_dict_entry(dict,PDF_N_DECODE) ; + if ( t ) + { + if ( !IS_ARRAY(t) ) + { + error = "bad /Decode array" ; + return -1 ; + } + + if ( 2*f->n != pdf_get_array_size(t) ) + { + error = "/Decode array size mismatch" ; + return -1 ; + } + } + + f->u.t0.decode = xmalloc( 2*f->n * sizeof(f->u.t0.decode[0] ) ) ; + if ( t ) + { + pdf_real_t *dec = pdf_get_real_array(t) ; + if ( !dec ) + { + error = "bad /Decode array" ; + return -1 ; + } + for ( j = 0 ; j < f->n ; j++ ) + setmap( f->u.t0.decode + 2*j, f->range + 2*j, dec + 2*j ) ; + } + else /* default decode array */ + { + pdf_real_t dec[2] ; + dec[0] = 0 ; + dec[1] = (bps < 32 ) ? ((1U<n ; j++ ) + setmap( f->u.t0.decode + 2*j, f->range + 2*j , dec ) ; + } + + f->u.t0.k = xmalloc( m * sizeof(f->u.t0.k[0]) ) ; + f->u.t0.w0 = xmalloc( m * sizeof(f->u.t0.w0[0]) ) ; + f->u.t0.w1 = xmalloc( m * sizeof(f->u.t0.w1[0]) ) ; + if ( order == 3 ) + { + f->u.t0.wm = xmalloc( m * sizeof(f->u.t0.wm[0]) ) ; + f->u.t0.w2 = xmalloc( m * sizeof(f->u.t0.w2[0]) ) ; + f->eval = pdf_eval_spline ; + } + else + { + f->eval = pdf_eval_linear ; + } + + return 0 ; +} + + +static int +init_func2( pdf_func_t f, pdf_obj_t dict ) +{ + pdf_obj_t t ; + int n ; + t = pdf_get_dict_entry(dict,PDF_N_N) ; + if ( !t ) /* required */ + { + error="missing /N in /FunctionType 2 dictionary" ; + return -1 ; + } + if ( IS_REAL(t) ) + f->u.t2.N = pdf_get_real(t) ; + else if ( IS_INT(t) ) + f->u.t2.N = pdf_get_int(t) ; + else + { + error="bad /N in /FunctionType 2 dictionary" ; + return -1 ; + } + + t = pdf_get_dict_entry(dict,PDF_N_C0) ; + if ( t ) + { + if ( !IS_ARRAY(t) ) + { + error="bad /C0 in /FunctionType 2 dictionary" ; + return -1 ; + } + n = pdf_get_array_size(t) ; + if ( n <= 0 || n > 32 ) + { + error="bad size of /C0 in /FunctionType 2 dictionary" ; + return -1 ; + } + f->u.t2.c0 = pdf_get_real_array(t) ; + if ( !f->u.t2.c0 ) + { + error="bad data in /C0 in /FunctionType 2 dictionary" ; + return -1 ; + } + if ( f->n && f->n != n ) + { + error="size mismatch in /FunctionType 2 dictionary" ; + return -1 ; + } + if ( !f->n ) + f->n = n ; + } + + + t = pdf_get_dict_entry(dict,PDF_N_C1) ; + if ( t ) + { + if ( !IS_ARRAY(t) ) + { + error="bad /C1 in /FunctionType 2 dictionary" ; + return -1 ; + } + n = pdf_get_array_size(t) ; + if ( n <= 0 || n > 32 ) + { + error="bad size of /C1 in /FunctionType 2 dictionary" ; + return -1 ; + } + f->u.t2.c1 = pdf_get_real_array(t) ; + if ( !f->u.t2.c1 ) + { + error="bad data in /C1 in /FunctionType 2 dictionary" ; + return -1 ; + } + if ( f->n && f->n != n ) + { + error="size mismatch in /FunctionType 2 dictionary" ; + return -1 ; + } + + if ( !f->n ) + f->n = n ; + } + + if ( !f->u.t2.c0 && !f->u.t2.c1 ) + { + if ( !f->n ) + f->n = 1 ; + } + + f->eval = pdf_eval_exponential ; + + return 0 ; +} + +/* --- ------------------------------- --- */ +/* --- type 4 ( postscript ) functions --- */ +/* --- ------------------------------- --- */ + +typedef enum +{ + OPC_ret, /* end of function, return result */ + OPC_jnt, /* jump if not true , offset follows */ + OPC_jmp, /* jump always, offset follows */ + OPC_lit, /* literal follows. */ + OPC_begin, /* token opening brace */ + OPC_end, /* token closing brace */ + OPC_if, /* token */ + OPC_ifelse,/* token */ + OPC_bad, /* bad token */ + + OPC_floor, + OPC_ceiling, + OPC_truncate, + OPC_round, + + OPC_abs, + OPC_sqrt, + OPC_atan, + OPC_sin, + OPC_cos, + OPC_exp, + OPC_log, + OPC_ln, + OPC_neg, + OPC_cvi, + OPC_cvr, + + OPC_mod, + OPC_div, + OPC_add, + OPC_idiv, + OPC_bitshift, + OPC_mul, + OPC_sub, + + OPC_true, + OPC_false, + OPC_not, + OPC_and, + OPC_or, + OPC_xor, + + OPC_eq, + OPC_le, + OPC_ge, + OPC_lt, + OPC_gt, + OPC_ne, + + + OPC_copy, + OPC_exch, + OPC_pop, + OPC_dup, + OPC_index, + OPC_roll +} PDF_TYPE4_OPC ; + +#define NSTACK 100 +#define REP_TRUE (1.202056903159594285399738162) +#define REP_FALSE (-15116315767.09215686274509804) +#define INT(xyyzy) ((int)floor(xyyzy)) +#define INT_P(xyzzy) ( (xyzzy) == INT(xyzzy) ) + + + +int eval_type4( pdf_func_t t , const pdf_real_t in[], pdf_real_t out[] ) +{ + double stack[NSTACK+2] ; + unsigned char *op ; + unsigned n ; + double tmp ; + int sp ; + int pc ; + + op = t->u.t4.opcodes ; + n = t->u.t4.n_opcodes ; + + for ( sp = 0 ; sp < t->m ; sp++ ) + stack[sp] = in[sp] ; + sp-- ; + for( pc = 0 ; pc < n ; pc++ ) + { + switch( (PDF_TYPE4_OPC) op[pc] ) + { + case OPC_ret : + if ( sp+1 < t->n ) goto stack_underflow ; + if ( sp+1 > t->n ) goto stack_error ; + for ( sp = 0 ; sp < t->n ; sp++ ) + { + if ( isnan(stack[sp] ) ) + goto math_error ; + out[sp] = clip(stack[sp], t->range + 2*sp ) ; + } + return 0 ; + break ; + case OPC_lit : + if ( sp >= NSTACK ) goto stack_overflow ; + sp++ ; + memcpy(&stack[sp], op+pc+1, sizeof(stack[0]) ) ; + pc += sizeof(stack[0]) ; + break ; + case OPC_jmp : + memcpy(&pc,op+pc+1,sizeof(pc)) ; + break ; + case OPC_jnt : + if ( sp < 0 ) goto stack_underflow ; + tmp = stack[sp--] ; + if ( tmp == REP_TRUE ) + pc += sizeof(pc) ; + else if ( tmp == REP_FALSE ) + memcpy(&pc,op+pc+1,sizeof(pc)) ; + else goto type_error ; + break ; + case OPC_dup : + if ( sp < 0 ) goto stack_underflow ; + if ( sp >= NSTACK ) goto stack_overflow ; + stack[sp+1] = stack[sp] ; + sp++ ; + break ; + case OPC_pop : + if ( sp < 0 ) goto stack_underflow ; + sp-- ; + break ; + case OPC_exch : + if ( sp < 1 ) goto stack_underflow ; + tmp = stack[sp-1] ; + stack[sp-1] = stack[sp] ; + stack[sp] = tmp ; + break ; + case OPC_sin : + if ( sp < 0 ) goto stack_underflow ; + stack[sp] = sin((180/M_PI)*stack[sp]) ; + break ; + case OPC_cos : + if ( sp < 0 ) goto stack_underflow ; + stack[sp] = cos((180/M_PI)*stack[sp]) ; + break ; + case OPC_neg : + if ( sp < 0 ) goto stack_underflow ; + stack[sp] = - stack[sp] ; + break ; + case OPC_abs : + if ( sp < 0 ) goto stack_underflow ; + stack[sp] = fabs(stack[sp]) ; + break ; + case OPC_exp : + if ( sp < 0 ) goto stack_underflow ; + stack[sp] = exp(stack[sp]) ; + break ; + case OPC_log : + if ( sp < 0 ) goto stack_underflow ; + stack[sp] = log10(stack[sp]) ; + break ; + case OPC_ln : + if ( sp < 0 ) goto stack_underflow ; + stack[sp] = log(stack[sp]) ; + break ; + case OPC_sqrt : + if ( sp < 0 ) goto stack_underflow ; + stack[sp] = sqrt(stack[sp]) ; + break ; + case OPC_floor : + if ( sp < 0 ) goto stack_underflow ; + stack[sp] = floor(stack[sp]) ; + break ; + case OPC_ceiling : + if ( sp < 0 ) goto stack_underflow ; + stack[sp] = ceil(stack[sp]) ; + break ; + case OPC_truncate : + if ( sp < 0 ) goto stack_underflow ; + /* C99 stack[sp] = trunc(stack[sp]) ; */ + stack[sp] = (stack[sp] >= 0 ) + ? floor(stack[sp]) + : -floor(-stack[sp]) ; + break ; + case OPC_round : + if ( sp < 0 ) goto stack_underflow ; + stack[sp] = floor(0.5+stack[sp]) ; + break ; + case OPC_cvi : + if ( sp < 0 ) goto stack_underflow ; + /* behaviour checked against ghostscript interpreter */ + tmp = (stack[sp] >=0 ) ? floor(stack[sp]) : -floor(-stack[sp]); + if ( !INT_P(tmp) ) goto range_error ; + stack[sp] = INT(tmp) ; + break ; + case OPC_index : + if ( sp < 0 ) goto stack_underflow ; + if ( !INT_P(stack[sp]) ) goto type_error ; + if ( stack[sp] < 0 || stack[sp] > sp-1 ) goto range_error ; + stack[sp] = stack[sp-1-INT(stack[sp])] ; + break ; + case OPC_copy : + { + int n,i ; + if ( sp < 0 ) goto stack_underflow ; + if ( !INT_P(stack[sp]) ) goto type_error ; + n = INT(stack[sp]) ; + if ( n < 0 || n > sp ) goto range_error ; + if ( sp + n >= NSTACK ) goto stack_overflow ; + sp-- ; + for ( i = 0 ; i < n ; i++,sp++ ) + stack[sp+n] = stack[sp] ; + } + break ; + case OPC_roll : + { + int n,s ; + if ( sp < 2 ) goto stack_underflow ; + if ( !INT_P(stack[sp]) || !INT_P(stack[sp-1])) goto type_error ; + n = INT(stack[sp-1]) ; + s = INT(stack[sp]) ; + sp -= 2 ; + if ( n < 0 || n > sp ) goto range_error ; + if ( n >= 2 ) + { + double pp[NSTACK] ; + s = s % n ; + if ( s < 0 ) + s += n ; + memcpy(pp,&stack[n-s],s*sizeof(stack[0])) ; + memmove(stack,stack+s,(n-s)*sizeof(stack[0])) ; + memcpy(stack,pp,s*sizeof(double)) ; + } + } + break ; + case OPC_cvr : + if ( sp < 0 ) goto stack_underflow ; + /* boolean goes unchecked */ + break ; + case OPC_add : + if ( sp < 1 ) goto stack_underflow ; + stack[sp-1] += stack[sp] ; + sp-- ; + break ; + case OPC_sub : + if ( sp < 1 ) goto stack_underflow ; + stack[sp-1] -= stack[sp] ; + sp-- ; + break ; + case OPC_mul : + if ( sp < 1 ) goto stack_underflow ; + stack[sp-1] *= stack[sp] ; + sp-- ; + break ; + case OPC_div : + if ( sp < 1 ) goto stack_underflow ; + stack[sp-1] /= stack[sp] ; + sp-- ; + break ; + case OPC_atan : + if ( sp < 1 ) goto stack_underflow ; + stack[sp-1] = (180/M_PI)*atan2( stack[sp-1] , stack[sp] ) ; + /* check against Ghostscript */ + sp-- ; + break ; + case OPC_idiv : + if ( sp < 1 ) goto stack_underflow ; + if ( !INT_P(stack[sp]) || !INT_P(stack[sp-1]) ) + goto type_error ; + stack[sp-1] = INT(stack[sp]) / INT(stack[sp-1]) ; + sp-- ; + break ; + case OPC_mod : + if ( sp < 1 ) goto stack_underflow ; + if ( !INT_P(stack[sp]) || !INT_P(stack[sp-1]) ) + goto type_error ; + stack[sp-1] = INT(stack[sp]) % INT(stack[sp-1]) ; + sp-- ; + break ; + case OPC_and : + if ( sp < 1 ) goto stack_underflow ; + if ( !INT_P(stack[sp]) || !INT_P(stack[sp-1]) ) + goto type_error ; + stack[sp-1] = INT(stack[sp]) & INT(stack[sp-1]) ; + sp-- ; + break ; + case OPC_bitshift: + if ( sp < 1 ) goto stack_underflow ; + if ( !INT_P(stack[sp]) || !INT_P(stack[sp-1]) ) + goto type_error ; + { + uint32_t x = INT(stack[sp-1]) ; + int i = INT(stack[sp]) ; + if ( i >= 0 ) + x = ( i < 32 ) ? (x<>i) : 0 ; + stack[sp-1] = x ; + } + sp-- ; + break ; + case OPC_or : + if ( sp < 1 ) goto stack_underflow ; + if ( !INT_P(stack[sp]) || !INT_P(stack[sp-1]) ) + goto type_error ; + stack[sp-1] = INT(stack[sp]) | INT(stack[sp-1]) ; + sp-- ; + break ; + case OPC_xor : + if ( sp < 1 ) goto stack_underflow ; + if ( !INT_P(stack[sp]) || !INT_P(stack[sp-1]) ) + goto type_error ; + stack[sp-1] = INT(stack[sp]) ^ INT(stack[sp-1]) ; + sp-- ; + break ; + case OPC_not : + if ( sp < 0 ) goto stack_underflow ; + if ( !INT_P(stack[sp]) ) + goto type_error ; + stack[sp] = ~INT(stack[sp]) ; + sp-- ; + break ; + case OPC_eq : + if ( sp < 1 ) goto stack_underflow ; + if ( isnan(stack[sp-1]) || isnan(stack[sp]) ) + goto math_error ; + /* any comparison involving NaN is false. */ + /* this should not go unnoticed */ + stack[sp-1] = ( stack[sp-1] == stack[sp] ) ? (REP_TRUE):(REP_FALSE) ; + sp-- ; + break ; + case OPC_ne : + if ( sp < 1 ) goto stack_underflow ; + if ( isnan(stack[sp-1]) || isnan(stack[sp]) ) + goto math_error ; + stack[sp-1] = ( stack[sp-1] != stack[sp] ) ? (REP_TRUE):(REP_FALSE) ; + sp-- ; + break ; + case OPC_lt : + if ( sp < 1 ) goto stack_underflow ; + if ( isnan(stack[sp-1]) || isnan(stack[sp]) ) + goto math_error ; + stack[sp-1] = ( stack[sp-1] < stack[sp] ) ? (REP_TRUE):(REP_FALSE) ; + sp-- ; + break ; + case OPC_le : + if ( sp < 1 ) goto stack_underflow ; + if ( isnan(stack[sp-1]) || isnan(stack[sp]) ) + goto math_error ; + stack[sp-1] = ( stack[sp-1] <= stack[sp] ) ? (REP_TRUE):(REP_FALSE) ; + sp-- ; + break ; + case OPC_ge : + if ( sp < 1 ) goto stack_underflow ; + if ( isnan(stack[sp-1]) || isnan(stack[sp]) ) + goto math_error ; + stack[sp-1] = ( stack[sp-1] >= stack[sp] ) ? (REP_TRUE):(REP_FALSE) ; + sp-- ; + break ; + case OPC_gt : + if ( sp < 1 ) goto stack_underflow ; + if ( isnan(stack[sp-1]) || isnan(stack[sp]) ) + goto math_error ; + stack[sp-1] = ( stack[sp-1] > stack[sp] ) ? (REP_TRUE):(REP_FALSE) ; + sp-- ; + break ; + case OPC_true : + if ( sp >= NSTACK ) goto stack_overflow ; + stack[++sp] = REP_TRUE ; + break ; + case OPC_false : + if ( sp >= NSTACK ) goto stack_overflow ; + stack[++sp] = REP_FALSE ; + break ; + default: + goto block_error ; + } + } + + block_error : error="block error" ; return -1 ; + stack_underflow: error="stack underflow"; return -1 ; + stack_overflow : error="stack overflow" ; return -1 ; + stack_error : error="stack error" ; return -1 ; + range_error : error="range error" ; return -1 ; + type_error : error="type error" ; return -1 ; + math_error : error="math error" ; return -1 ; +} + + +/* ANSI-C code produced by gperf version 3.0.1 */ +/* Command-line: gperf -t -m 100 pdf_function_type4.gperf */ +/* Computed positions: -k'1-3' */ +/* Caution : 7 bit only */ + +struct toklut { const char *name ; int ret ; } ; + +#define TOTAL_KEYWORDS 44 +#define MIN_WORD_LENGTH 1 +#define MAX_WORD_LENGTH 8 +#define MIN_HASH_VALUE 2 +#define MAX_HASH_VALUE 50 +/* maximum key range = 49, duplicates = 0 */ +static inline unsigned int +hash (register const char *str, register unsigned int len) +{ + static unsigned char asso_values[] = { - return NULL; - } + 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, + 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, + 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, + 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, + 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, + 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, + 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, + 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, + 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, + 51, 51, 51, 51, 51, 51, 51, 3, 30, 10, + 4, 4, 12, 2, 51, 5, 51, 51, 1, 27, + 3, 16, 13, 33, 7, 4, 1, 4, 18, 51, + 11, 51, 51, 2, 51, 1, 51, 51 + }; + register int hval = len; - return NULL; + switch (hval) + { + default: + hval += asso_values[(unsigned char)str[2]]; + /*FALLTHROUGH*/ + case 2: + hval += asso_values[(unsigned char)str[1]]; + /*FALLTHROUGH*/ + case 1: + hval += asso_values[(unsigned char)str[0]]; + break; + } + return hval; } -pdf_func_t -pdf_create_func_3 (pdf_obj_t dict) +static inline struct toklut * +in_word_set (register const char *str, register unsigned int len) { - if (!IS_DICT(dict)) + static struct toklut wordlist[] = { - return NULL; + {""}, {""}, + {"}", OPC_end}, + {"{", OPC_begin}, + {"lt", OPC_lt}, + {"gt", OPC_gt}, + {"ln", OPC_ln}, + {"le", OPC_le}, + {"ge", OPC_ge}, + {"ne", OPC_ne}, + {""}, + {"atan", OPC_atan}, + {"neg", OPC_neg}, + {"and", OPC_and}, + {"add", OPC_add}, + {"sin", OPC_sin}, + {"true", OPC_true}, + {"index", OPC_index}, + {"idiv", OPC_idiv}, + {"if", OPC_if}, + {"truncate", OPC_truncate}, + {"false", OPC_false}, + {"log", OPC_log}, + {"not", OPC_not}, + {"dup", OPC_dup}, + {"or", OPC_or}, + {"ceiling", OPC_ceiling}, + {"ifelse", OPC_ifelse}, + {"roll", OPC_roll}, + {"exch", OPC_exch}, + {"div", OPC_div}, + {"exp", OPC_exp}, + {"round", OPC_round}, + {"cos", OPC_cos}, + {"floor", OPC_floor}, + {"mul", OPC_mul}, + {"cvi", OPC_cvi}, + {"xor", OPC_xor}, + {"cvr", OPC_cvr}, + {"eq", OPC_eq}, + {"abs", OPC_abs}, + {"sub", OPC_sub}, + {""}, + {"copy", OPC_copy}, + {"bitshift", OPC_bitshift}, + {"pop", OPC_pop}, + {""}, {""}, + {"sqrt", OPC_sqrt}, + {""}, + {"mod", OPC_mod} + }; + + if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH) + { + register int key = hash (str, len); + + if (key <= MAX_HASH_VALUE && key >= 0) + { + register const char *s = wordlist[key].name; + + if (*str == *s && !strcmp (str + 1, s + 1)) + return &wordlist[key]; + } } + return 0; +} + + + +static int get_token( pdf_stm_t stm , double *literal ) +{ + int c ; + char buf[128] ; + char *end ; + unsigned bp ; + +#define PDF_ISWHITE(foo) ( ((foo) <= 0x20 ) \ + && ( \ + (foo) == 0x20 || (foo) == 0x09 || (foo) == 0x0a || (foo) == 0x0c \ + || (foo) == 0x0d || (foo) == 0x00 ) ) + + while ( ( c = pdf_stm_read_char(stm)) != PDF_EOF && PDF_ISWHITE(c)) + ; + if ( c == PDF_EOF ) + return -1 ; + + bp = 0 ; + buf[bp++] = c ; + while ( ( c = pdf_stm_read_char(stm) ) != PDF_EOF && !PDF_ISWHITE(c) ) + { + buf[bp++] = c ; + if ( bp >= sizeof(buf)/sizeof(buf[0]) || c >= 0x80 ) + return OPC_bad ; + } + buf[bp] = '\0' ; + if ( isdigit(buf[0]) || buf[0] < 'a' ) + { + *literal = strtod(buf,&end) ; + /* HF: recognizes a language larger than specified */ + return (*end) ? OPC_bad : OPC_lit ; + } + else + { + struct toklut *tk ; + tk = in_word_set(buf,bp) ; + return (tk) ? tk->ret : OPC_bad ; + } +} - return NULL; + + + + + + + + + + + +static int +init_func4( pdf_func_t f, pdf_obj_t dict, pdf_stm_t stm ) +{ + int off[32] ; + unsigned char *op ; + int at ; + int to ; + int alloc ; + int bsp ; + double lit ; + int opc ; + + + alloc = 64 ; + op = xmalloc(alloc) ; + + if ( !stm ) + { + error = "/FunctionType 4: no stream" ; + goto fail ; + } + + if ( !f->range ) + { + error = "/FunctionType 4: /Range is mandatory" ; + goto fail ; + } + + opc = get_token(stm,&lit) ; + if ( opc != OPC_begin ) + { + error = "/FunctionType 4: missed opening '{'" ; + goto fail ; + } + + bsp = 0 ; + at = 0 ; + for(;;) + { + if ( bsp >= sizeof(off)/sizeof(off[0]) ) + { + error = "/FunctionType 4: braces nested too deep (32 levels)" ; + goto fail ; + } + + if ( at+1+sizeof(lit) >= alloc ) + { + alloc *= 2 ; + op = xrealloc(op,alloc) ; + } + opc = get_token(stm,&lit) ; + if ( opc < 0 ) + { + error = "/FunctionType 4: unexpected EOS" ; + goto fail ; + } + switch ( (PDF_TYPE4_OPC)opc ) + { + case OPC_lit: + op[at++] = opc ; + memcpy(op+at,&lit,sizeof(lit)) ; + at += sizeof(lit) ; + break ; + case OPC_begin: + off[bsp++] = at ; /* backpatched by if/ifelse */ + op[at] = OPC_begin ; /* OPC_error */ + at += 1+sizeof(at) ; + break ; + case OPC_end: + if ( bsp ) + { + off[bsp++] = at ; + } + else + { + void *r ; + op[at++] = OPC_ret ; + if ( get_token(stm,&lit) >= 0 ) + { + error = "/FunctionType 4: trailing tokens" ; + goto fail ; + } + /* memory is transferred to f, not freed here */ + r = realloc(op,at) ; + if ( r ) + op = r ; + f->u.t4.opcodes = op ; + f->u.t4.n_opcodes = at ; + f->eval = eval_type4 ; + return 0 ; + } + break ; + case OPC_if : + if ( bsp < 2 || at != off[--bsp] ) + { + error = "/FunctionType 4: error before if" ; + goto fail ; + } + op[off[--bsp]] = OPC_jnt ; + to = at - 1 ; + memcpy(op+1+off[bsp],&to,sizeof(to)) ; + break ; + case OPC_ifelse : + if ( bsp < 4 || at != off[--bsp] ) + { + error = "/FunctionType 4: error before ifelse" ; + goto fail ; + } + + op[off[--bsp]] = OPC_jmp ; + to = at-1 ; + memcpy(op+off[bsp]+1,&to,sizeof(to)) ; + + to = off[bsp--] ; + if ( to != off[bsp--] ) + { + error = "/FunctionType 4: error before ifelse" ; + goto fail ; + } + to += sizeof(to) ; + op[off[bsp]] = OPC_jnt ; + memcpy(op+off[bsp]+1,&to,sizeof(to)) ; + break ; + case OPC_bad: + error = "/FunctionType 4: bad token" ; + /* stream offset would be nice */ + goto fail ; + break ; + default: + op[at++] = opc ; + break ; + } + } + /* not reached */ + error = "/FunctionType 4 error" ; +fail: + free(op) ; + return -1 ; } + pdf_func_t -pdf_create_func_4 (pdf_obj_t stream) +pdf_create_func( pdf_obj_t o ) { - if (!IS_STREAM(stream)) - { - return NULL; - } + pdf_func_t f ; + pdf_stm_t stm ; + pdf_obj_t dict ; + pdf_obj_t t ; + unsigned len ; + + error = "" ; + if ( !o ) + return NULL ; + /* this is not an error, NULL preserving. */ + + dict = NULL ; + + if ( IS_STREAM(o) ) + { + dict = o->value.stream.dict ; + stm = o->value.stream.stm ; + } + else if ( IS_DICT(o) ) + { + dict = o ; + stm = NULL ; + } + + if ( !dict ) + { + error = "bad function object" ; + return NULL ; + } + + f = xmalloc(sizeof(*f)) ; + memset(f,0,sizeof(*f)) ; - return NULL; + f->m = 0 ; /* unknown */ + f->n = 0 ; /* unknown */ + f->type = -1 ; + /* goto fail should be safe from now on */ + + t = pdf_get_dict_entry(dict,PDF_N_FUNCTION_TYPE) ; + if ( !t ) + { + error = "missing /FunctionType in function dictionary" ; + goto fail ; + } + if ( !IS_INT(t) ) + { + error = "bad /FunctionType in function dictionary" ; + goto fail ; + } + f->type = pdf_get_int(t) ; + + t = pdf_get_dict_entry(dict,PDF_N_DOMAIN) ; + if ( !t ) + { + error = "missing /Domain in function dictionary" ; + goto fail ; + } + if ( !IS_ARRAY(t) ) + { + error = "non-array /Domain in function dictionary" ; + goto fail ; + } + len = pdf_get_array_size(t) ; + if ( len <= 0 || (len&1) || len > 32 ) + { + error = "bad array size of /Domain in function dictionary" ; + goto fail ; + } + f->m = len/2 ; + f->domain = pdf_get_real_array(t) ; + if ( !f->domain ) + { + error = "bad /Domain array in function dictionary" ; + goto fail ; + } + + t = pdf_get_dict_entry(dict,PDF_N_RANGE) ; + if ( t ) + { + if ( !IS_ARRAY(t) ) + { + error = "bad /Range array in function dictionary\n"; + goto fail ; + } + len = pdf_get_array_size(t) ; + if ( len <= 0 || (len&1) ) + { + error = "bad array size of /Range in function dictionary" ; + goto fail ; + } + f->n = len/2 ; + f->range = pdf_get_real_array(t) ; + if ( !f->range ) + { + error = "bad /Range array in function dictionary" ; + goto fail ; + } + } + /* range is optional for some types; f->n may still be 0 */ + + switch (f->type) + { + case 0: + if ( init_func0(f,dict,stm) ) + goto fail ; + break ; + case 2: + if ( init_func2(f,dict) ) + goto fail ; + break ; + case 3: + if ( init_func3(f,dict) ) + goto fail ; + break ; + case 4: + if ( init_func4(f,dict,stm) ) + goto fail ; + break ; + default: + error = "bad /FunctionType in function dictionary" ; + goto fail ; + break ; + } + + return f ; + +fail: + pdf_destroy_func(f) ; /* safe even for partially initialized *f */ + return NULL ; } -/* Private functions */ +int pdf_eval_func( pdf_func_t fun , const pdf_real_t in[], pdf_real_t out[] ) +{ + return fun->eval(fun,in,out) ; +} +void pdf_info_func( const pdf_func_t fun , int *m , int *n ) +{ + *m = fun->m ; + *n = fun->n ; +} -/* End of pdf_function.c */ Index: src/pdf_function.h =================================================================== RCS file: /cvsroot/pdf/libgnupdf/src/pdf_function.h,v retrieving revision 1.2 diff -u -p -r1.2 pdf_function.h --- src/pdf_function.h 23 Oct 2007 12:22:09 -0000 1.2 +++ src/pdf_function.h 11 Nov 2007 13:38:44 -0000 @@ -1,134 +1,171 @@ -/* -*- mode: C -*- Time-stamp: "07/10/23 12:52:47 jemarch" - * - * File: pdf_function.h - * Author: Jose E. Marchesi (address@hidden) - * Date: Sun Sep 9 21:02:24 2007 - * - * GNU PDF Library - Function common data type - * - */ - /* Copyright (C) 2007 Free Software Foundation, Inc. */ -/* 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 3 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, see . - */ #ifndef PDF_FUNCTION_H #define PDF_FUNCTION_H #include +#include #include #include -/* Type 0: sampled functions */ +typedef struct pdf_func_s *pdf_func_t ; -struct pdf_func_0_s -{ - pdf_obj_t size; /* An array of `m' positive integers specifying the - number of samples in each input dimension of the - sample table. */ - int bps; /* Bits per sample. The number of bits used to represent - each sample. (If the function has multiple output - values, each one occupies `bps' bits). Valid values are - 1, 2, 4, 8, 12, 16, 24 and 32. */ - int order; /* The order of interpolation between samples. Valid - values are 1 and 3, specifying linear and cubic spline - interpolation, respectively. Default value: 1 */ - pdf_obj_t encode; /* An array of `2*m' numbers specifying the linear - mapping of input values into the domain of the - function's sample table. Default value: [0 - (Size_0 - 1) 0 (Size_1 - 1) ...] */ - pdf_obj_t decode; /* An array of `2*n' numbers specifying the linear - mapping of sample values into the range - appropriate for the function's output - values. Default value: same as the value of - `range' (see below) */ -}; +pdf_func_t +pdf_create_func( pdf_obj_t function ) ; +/* + * create a pdf_func_t from a dictionary or stream object. + * a type 3 function may call pdf_create_func() recursively + */ -/* Type 2: exponential interpolation functions */ -struct pdf_func_2_s -{ +void +pdf_destroy_func( pdf_func_t) ; +/* deallocate any ressources */ + +int +pdf_eval_func( pdf_func_t, const pdf_real_t in[] , pdf_real_t out[] ) ; +/* zero on success, nonzero or any error + * any NaN on input is an error. + * any NaN on output is an error, too + * infinities and nans should never cause a crash + */ + +void +pdf_info_func( const pdf_func_t, int *m , int *n ) ; +/* + * m: number of input dimensions + * n: number of out dimensions + */ + +const char *pdf_get_last_error_from_func(void); +/* just a proposal. _from_func may be dropped. + * Error CODES are not that useful,IMHO, + * because we do not need to take different actions + * on different kinds of errors. + * I like to indicate errors with a non-null return value , + * or a NULL pointer iff the function returns a pointer. + * This function never returns NULL. + * calling it twice will return an empty string, however. + */ + -}; -/* Type 3: stitching functions */ -struct pdf_func_3_s +struct pdf_func0_s { + unsigned m ; + unsigned n ; + double *encode ; + double *decode ; + unsigned *size ; + // unsigned *stride ; /* stride[i] := prod(j=0,i-1,size[i]) */ + unsigned nsamples ; /* product of all size[] entries, number of samples per output dimension */ + + unsigned *k ; /* base index, never out of range */ + double *wm ; + double *w0 ; + double *w1 ; + double *w2 ; /* precalculated weights, do not index into y[] if zero */ + + pdf_real_t *y ; /* sample table */ + + /* not used for recursive variants */ + pdf_real_t *r ; /* reduction buffer */ + pdf_real_t *y1 ; /* spline derivatives */ + pdf_real_t *d ; /* tmp buffer for diagonal of spline eq */ + pdf_real_t *b ; /* tmp buffer for right hand side of spline eq */ +} ; -}; -/* Type 4: postscript calculator functions */ -struct pdf_func_4_s +struct pdf_func2_s { + pdf_real_t *c0 ; + pdf_real_t *c1 ; + pdf_real_t N ; +} ; +struct pdf_func3_s +{ + unsigned k ; + /* number of functions + * m = 1 for all component functions + * fixed n for all component functions + */ + pdf_func_t *functions ; + pdf_real_t *bounds ; /* bounds[0] == domain[0], bounds[k] == domain[1] */ + double *encode ; /* mapping of domains */ +} ; + + +struct pdf_func4_s +{ + unsigned char *opcodes ; + unsigned n_opcodes ; + unsigned n_alloc ; +} ; -}; -/* Function data structure */ struct pdf_func_s { - int type; /* The function type: - - 0 Sampled function - 2 Exponential interpolation function - 3 Stitching function - 4 PostScript calculator function */ - pdf_obj_t domain; /* An array of `2*m' numbers, where `m' is the - number of input values. For each `i' from `0' - to `m - 1', `Domain_2i' must be less than or - equal to `Domain_2i+1', and the `ith' input - value, `x_i' must lie in the interval - `Domain_2i <= x_i <= Domain_2i+1'. Input values - outside the declared domains are clipped to the - nearest boundary value. */ - pdf_obj_t range; /* An array of `2*n' numbers, where `n' is the - number of output values. For each 'j' from '0' - to `n-1', `Range_2j' must be less than or equal - to `Range_2j+1', and the `jth' output value, - `y_j', must lie in the interval `Range_2j <= y_j - <= Range_2j+1'. Output values outside the - declared range are clipped to the nearest - boundary value. If this entry is absent no - clipping is done. Only used with type 0 and 4 - functions. */ - - /* Type-specific data */ - union - { - struct pdf_func_0_s t0; - struct pdf_func_2_s t2; - struct pdf_func_3_s t3; - struct pdf_func_4_s t4; - } details; - -}; - -typedef struct pdf_func_s *pdf_func_t; - - -pdf_func_t pdf_create_func_0 (pdf_obj_t stream); -pdf_func_t pdf_create_func_2 (pdf_obj_t dict); -pdf_func_t pdf_create_func_3 (pdf_obj_t dict); -pdf_func_t pdf_create_func_4 (pdf_obj_t stream); + int type ; + unsigned m ; + unsigned n ; + pdf_real_t *domain ; + pdf_real_t *range ; + int init ; + int (*eval)(pdf_func_t, const pdf_real_t in[] , pdf_real_t out[] ) ; + union + { + struct pdf_func0_s t0 ; + struct pdf_func2_s t2 ; + struct pdf_func3_s t3 ; + struct pdf_func4_s t4 ; + } u ; +} ; + + + +/* + * function object constructors take a dict/stream argument. + * n,m,domain and range are decoded and checked immediately. + * part of the detail data is decoded and checked, too. + * + * function evaluation: + * func->eval(func, const pdf_real_t in[], pdf_real_t out[] ) + * the array sizes are in[m] , out[n] . + */ + + + +/* +pdf_func_t +pdf_create_func0_test( unsigned m, unsigned n, + const pdf_real_t domain[], + unsigned size[], int order, pdf_real_t samples[] ) ; + + +pdf_func_t +pdf_create_func2_test( + const pdf_real_t domain[], + unsigned n, const pdf_real_t *range , + pdf_real_t N , const pdf_real_t *c0, const pdf_real_t *c1 ) ; + + + +pdf_func_t +pdf_create_func3_test(void) ; + +*/ + + + -#endif /* pdf_function.h */ +/* Zu Testzwecken. Die richtigen Funktionen haben ein pdf_obj_t - Argument */ -/* End of pdf_function.h */ +#endif Index: src/pdf_obj.c =================================================================== RCS file: /cvsroot/pdf/libgnupdf/src/pdf_obj.c,v retrieving revision 1.17 diff -u -p -r1.17 pdf_obj.c --- src/pdf_obj.c 31 Oct 2007 16:53:16 -0000 1.17 +++ src/pdf_obj.c 11 Nov 2007 13:38:47 -0000 @@ -202,7 +202,8 @@ pdf_create_stream (pdf_obj_t dict, new_stream = pdf_alloc_obj (); new_stream->type = PDF_STREAM_OBJ; - new_stream->value.stream.dict = pdf_obj_dup (dict); +#warning HF modified: no pdf_obj_dup (dict) + new_stream->value.stream.dict = dict ; // pdf_obj_dup (dict); new_stream->value.stream.stm = stm; new_stream->value.stream.data = data; @@ -726,6 +727,11 @@ pdf_dealloc_obj (pdf_obj_t obj) gl_list_free (obj->value.dict.entries); break; } + case PDF_STREAM_OBJ: + { + pdf_destroy_obj(obj->value.stream.dict) ; + break ; + } default: { /* NOP */ @@ -989,8 +995,7 @@ static pdf_obj_t pdf_stream_dup (pdf_obj_t obj) { pdf_obj_t new_stream; - - new_stream = pdf_create_stream (pdf_obj_dup (obj->value.stream.dict), + new_stream = pdf_create_stream (pdf_obj_dup(obj->value.stream.dict), obj->value.stream.stm, obj->value.stream.data); Index: src/pdf_stm.c =================================================================== RCS file: /cvsroot/pdf/libgnupdf/src/pdf_stm.c,v retrieving revision 1.36 diff -u -p -r1.36 pdf_stm.c --- src/pdf_stm.c 31 Oct 2007 16:53:16 -0000 1.36 +++ src/pdf_stm.c 11 Nov 2007 13:38:50 -0000 @@ -612,7 +612,8 @@ pdf_stm_read_char (pdf_stm_t stm) } else { - c = 0; + c = PDF_EOF ; + /* HF: do not return a valid character on error */ } return c; Index: torture/Makefile.am =================================================================== RCS file: /cvsroot/pdf/libgnupdf/torture/Makefile.am,v retrieving revision 1.15 diff -u -p -r1.15 Makefile.am --- torture/Makefile.am 7 Nov 2007 18:29:28 -0000 1.15 +++ torture/Makefile.am 11 Nov 2007 13:38:50 -0000 @@ -18,14 +18,14 @@ if CHECK -noinst_PROGRAMS = testall +noinst_PROGRAMS = testall test-function LDADD = -lcheck $(top_srcdir)/src/libgnupdf.la AM_CPPFLAGS = -I ../lib AM_CFLAGS = -g -TESTS = testall +TESTS = testall testall_SOURCES = testall.c \ test-base.c \ @@ -36,7 +36,11 @@ testall_SOURCES = testall.c \ test-obj_dict.c \ test-obj_dupequality.c \ test-stm_openclose.c \ - test-rectangle.c + test-rectangle.c + + + + check_PROGRAMS = testall +++ torture/test-function.c 2007-11-11 10:00:30.000000000 +0100 @@ -0,0 +1,729 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +PDF_GLOBALS + +#define MAXDIM 4 + + +enum +{ + OPT_C0 = 256 , + OPT_C1 , + OPT_BOUNDS +} ; + +static struct option options[] = +{ + { "help",no_argument,NULL,'h' }, + { "type",required_argument,NULL,'t' }, /* 0,2,3,4 */ + { "domain",required_argument,NULL,'d' }, /* pdf array syntax */ + { "range",required_argument,NULL,'r' }, /* pdf array syntax */ + { "bounds",required_argument,NULL,OPT_BOUNDS }, /* type 3 : bounds array */ + { "n",required_argument,NULL,'n' }, /* type 0 : number of samples, repeat */ + { "order",required_argument,NULL,'o' }, /* type 0 : 1 or 3 , default 1 */ + { "bps",required_argument,NULL,'b' }, /* type 0 : bits per sample , no default */ + { "N",required_argument,NULL,'N' }, /* type 2 : exponent */ + { "C0",required_argument,NULL, OPT_C0 }, /* type 2 : values at 0 */ + { "C1",required_argument,NULL, OPT_C1 }, /* type 2 : values at 1 */ + { "code",required_argument,NULL,'c' }, /* type 4 : postscript code */ + { "step",required_argument,NULL,'s' }, /* stepsize for evaluation */ + { NULL,0,NULL,0 } +} ; + + +static void usage(void) +{ + fprintf(stderr, + "Usage: test-function [options]\n" + " -h, --help Show this list\n" + " -t, --type select a function type, often implied by other args\n" + " -d, --domain pdf array of numbers [ -3 3 ] \n" + " -r, --range pdf array of numbers [ -0.1 1.1 ] \n" + " -n, --n=int type 0, append to the /Size array\n" + " -o, --order=int type 0, value 1 or 3\n" + " -b, --bps=int type 0, bits per sample, 1,2,4,8,12,16,24 or 32\n" + " -c, --code='...' type 4, cf. pdf_reference section 3.9.4\n" + " -N, --N=exponent type 2, cf. pdf_reference section 3.9.2\n" + " --C0=\"...\" type 2, see 3.9.2 \n" + " --C1=\"...\" type 2\n" + " --step=number stepsize for function evaluation demo\n" + ) ; +} + +static pdf_real_t hat(int m, const pdf_real_t x[] ) +{ + int i ; + pdf_real_t y ; + y = 0 ; + for ( i = 0 ; i < m ; i++ ) + y += x[i]*x[i] / (2*i+1) ; + return exp( -y/2 ) ; +} + +static void test_eval( pdf_func_t fun, pdf_real_t step, pdf_real_t (*f)(int m, const pdf_real_t in[]) ) ; +/* f may be NULL */ + +static pdf_stm_t +build_0( pdf_obj_t dict ) ; +/* build a sample dictionary */ + +static pdf_obj_t build_3( double a, double step, double b ) ; +/* build a test dictionary */ + + +static pdf_obj_t make_real_array( char *s ) ; + +static uint32_t *get_uint_array( pdf_obj_t dict, pdf_obj_t name, int *n ) ; +static int get_int( pdf_obj_t dict, pdf_obj_t name, int *n ) ; /* nonzero return on error */ + + + +static void die(const char *s) +{ + fprintf(stderr,"die: %s\n",s) ; + /* no cleanup */ + exit(1) ; +} + +static void settype(int *type, int t ) +{ + if ( *type >= 0 && *type != t ) + die("inconsistent options/function type ") ; + *type = t ; +} + +int main( int argc, char *argv[] ) +{ + int success; + pdf_func_t fun ; + pdf_obj_t obj ; /* dict or stream */ + int type ; + int order ; + int bps ; + int c ; + double a,b ; + char *code ; + pdf_stm_t stm ; /* close afterwards */ + + pdf_obj_t c0,c1 ; + pdf_obj_t domain , range ; + pdf_obj_t size ; + pdf_obj_t dict ; + pdf_obj_t N ; + pdf_real_t step ; + pdf_obj_t bounds ; + + pdf_init (); + + bps = -1 ; + type = -1 ; + order = -1 ; + code = "" ; + step = 0.1 ; + + c0 = c1 = NULL ; + domain = NULL ; + range = NULL ; + bounds = NULL ; + N = NULL ; + size = 0 ; + + + obj = NULL ; + fun = NULL ; + + + + while ( ( c = getopt_long(argc,argv,"t:n:f:o:d:r:b:N:s:",options,NULL) ) > 0 ) + switch(c) + { + case 'h' : + default: + usage() ; + exit(1) ; + break ; + + case 't': + type = atoi(optarg) ; + break ; + case 's' : + step = atof(optarg) ; + break ; + case 'n': + if ( !size ) + size = pdf_create_array (); + if ( !size ) + die("???") ; + pdf_append_array_elt(size, pdf_create_integer(atoi(optarg)) ) ; + break ; + case 'd': + domain = make_real_array(optarg) ; + if ( !domain ) + die("bad data in /Domain ") ; + break ; + case 'r': + range = make_real_array(optarg) ; + if ( !range ) + die("bad data in /Range") ; + break ; + case 'o': + order = atoi(optarg) ; + settype(&type,0) ; + case 'b': + bps = atoi(optarg) ; + settype(&type,0) ; + break ; + case 'c' : + settype(&type,4) ; + code = optarg ; + break ; + case 'N' : + { + char b ; + double x ; + settype(&type,2) ; + if ( 1 != sscanf(optarg,"%lf %c",&x,&b) ) + die("bad N") ; + N = pdf_create_real( x ) ; + } + case OPT_C0 : + settype(&type,2) ; + c0 = make_real_array(optarg) ; + break ; + case OPT_C1 : + settype(&type,2) ; + c1 = make_real_array(optarg) ; + break ; + case OPT_BOUNDS : + settype(&type,3) ; + bounds = make_real_array(optarg) ; + break ; + } + + dict = pdf_create_dict() ; + + pdf_create_dict_entry(dict, pdf_obj_dup (PDF_N_FUNCTION_TYPE),pdf_create_integer(type)); + if ( domain ) + pdf_create_dict_entry (dict, pdf_obj_dup (PDF_N_DOMAIN), domain ) ; + if ( range ) + pdf_create_dict_entry (dict, pdf_obj_dup (PDF_N_RANGE), range ) ; + + if ( bps != -1 ) + pdf_create_dict_entry (dict, pdf_obj_dup (PDF_N_BITS_PER_SAMPLE), pdf_create_integer(bps)); + + if ( order != -1 ) + pdf_create_dict_entry (dict, pdf_obj_dup (PDF_N_ORDER), pdf_create_integer(order)); + + if ( size ) + pdf_create_dict_entry (dict, pdf_obj_dup (PDF_N_SIZE), size ) ; + + if ( N ) + pdf_create_dict_entry (dict, pdf_obj_dup (PDF_N_N), N ) ; + + if ( c0 ) + pdf_create_dict_entry (dict, pdf_obj_dup (PDF_N_C0), c0 ) ; + + if ( c1 ) + pdf_create_dict_entry (dict, pdf_obj_dup (PDF_N_C1), c1 ) ; + + switch (type) + { + case 0: + stm = build_0(dict) ; + if ( !stm ) + die("build_0() ???") ; + obj = pdf_create_stream(dict,stm,0) ; + if ( !obj ) + die("pdf_create_stream() ???") ; + dict = NULL ; + break ; + + case 2: + obj = dict ; + dict = NULL ; + break ; + + case 3: + obj = build_3( -3 , 1 , 3 ) ; + break ; + + case 4: + { + stm = pdf_create_mem_stm( strlen(code) , PDF_TRUE, 0xAA, PDF_FALSE ) ; + if ( !stm ) + die("pdf_create_mem_stm() ???") ; + pdf_stm_write(stm,code,strlen(code) ) ; + pdf_stm_seek(stm,0) ; + + obj = pdf_create_stream(dict,stm,0) ; + if ( !obj ) + die("pdf_create_stream() ???") ; + dict = NULL ; + } + } + + + fun = pdf_create_func(obj) ; + + if ( !fun ) + fprintf(stderr,"could not create pdf_func_t from pdf_obj_t ( %s )\n", + pdf_get_last_error_from_func()) ; + + test_eval(fun,step, (type==0||type==4) ? hat: NULL ) ; + + if ( dict ) + pdf_destroy_obj(dict) ; + + if ( obj ) + pdf_destroy_obj(obj) ; + +/* pdf_destroy_obj is not safe with a NULL argument. + * free(NULL) is explicitly allowed in ANSI C. + * Returning NULL as error indicator is similar to NaN in arithmetic: + * Any calculation involving NaN yields NaN. + * This allows deferred error checking. + */ + + if ( fun ) + pdf_destroy_func(fun); + + pdf_finish (); + + return 0 ; +} + + +static pdf_obj_t make_real_array_f(const pdf_real_t p[], int n) +{ + int i; + pdf_obj_t a = pdf_create_array (); + for (i = 0; i < n; i++) + pdf_add_array_elt (a, i, pdf_create_real (p[i])); + return a; +} + + +static pdf_obj_t make_int_array_f(const int p[], int n) +{ + int i; + pdf_obj_t a = pdf_create_array (); + for (i = 0; i < n; i++) + pdf_add_array_elt (a, i, pdf_create_integer(p[i])); + return a; +} + + + + +static pdf_stm_t +build_0( pdf_obj_t dict ) +/* build a sample stream */ +{ + pdf_stm_t stm ; + unsigned char *table ; + int tp ; + uint32_t nsamples ; + uint32_t data ; + uint32_t mask ; + uint32_t smp ; + uint32_t *size ; + unsigned m ; /* input */ + unsigned n ; /* output, should be 1 */ + int bps ; + int avail ; + + double y ; + pdf_real_t in[12] ; + int i[12] ; + double range[2] ; + double domain[24] ; + double scale ; + int k ; + + stm = NULL ; + + table = NULL ; + size = NULL ; + /* make goto fail safe */ + + n = 1 ; + size = get_uint_array(dict,PDF_N_SIZE,&m) ; + if ( !size || m > 12 ) + goto fail ; + + + nsamples = 1 ; + { + int i ; + for ( i = 0 ; i < m ; i++ ) + nsamples *= size[i] ; + } + + if ( get_int(dict,PDF_N_BITS_PER_SAMPLE,&bps) ) + goto fail ; + + if ( bps <= 0 || bps > 32 ) + goto fail ; + + table = xmalloc( 1 + nsamples * bps/8 ) ; + /* the lazy way, pdf_function.c does not use temporary storage */ + + tp = 0 ; + data = 0 ; + avail = 0 ; + mask = (bps < 32) ? ((1U<= range[0] ) ) + y = range[0] ; + else if ( y > range[1] ) + y = range[1] ; + + smp = ( y - range[0] ) / ( range[1]-range[0]) * mask ; + data = ((data << bps)&~mask) | ( smp & mask ) ; + avail += bps ; + + while ( avail >= 8 ) + { + table[tp++] = (data >> (avail-8) ) ; + avail -= 8 ; + } + + for ( k = 0 ; k < m ; k++ ) + { + if ( i[k] < size[k]-1 ) + i[k]++ ; + else + i[k] = 0 ; + + in[k] = domain[2*k] + i[k] * ( domain[2*k+1]-domain[2*k])/(size[k]-1) ; + + if ( i[k] ) + break ; + } + } + assert( avail <= 8 ) ; + if ( avail ) + table[tp++] = data << (8-avail) ; + + + stm = pdf_create_mem_stm( tp , PDF_TRUE, 0xAA, PDF_FALSE ) ; + if ( !stm ) + goto fail ; + + pdf_stm_write(stm,table, tp ) ; + pdf_stm_seek(stm,0) ; + + free(table) ; + + return stm ; + +fail: + free(table) ; + free(size) ; + return NULL ; +} + + +#if 0 +static void +show_0(pdf_func_t fun, int dims, pdf_real_t domain[] ) +{ + + int k ; + unsigned i[MAXDIM] ; + pdf_real_t in[MAXDIM] ; + pdf_real_t out ; + double y ; + int ns = 64 ; + + for ( k = 0 ; k < dims ; k++ ) + { + i[k] = 0 ; + in[k] = domain[2*k] ; + } + + k = 0 ; + /* evaluating a function on a grid */ + /* this looks weird if the number of dimensions is not fixed at compile time */ + while ( k < dims ) + { + y = 0 ; + for ( k = 0 ; k < dims ; k++ ) + { + y += in[k]*in[k]/(2*k+1) ; + printf(" %12.8f",in[k]) ; + } + y = exp(-y/2) ; + pdf_eval_func(fun,in,&out) ; + printf(" %12.8f ", out ) ; + printf(" %12.8f\n", y - out ) ; + /* approximation error */ + /* gnuplot ( not a gnu program ) : */ + /* splot "file.dat" using 1:2:4 */ + for ( k = 0 ; k < dims ; k++ ) + { + if ( i[k] < ns-1 ) + i[k]++ ; + else + { + i[k] = 0 ; + printf("\n"); + } + in[k] = domain[2*k] + i[k] * ( domain[2*k+1]-domain[2*k])/(ns-1) ; + if ( i[k] ) + break ; + } + } +} + + + +static void show_2( pdf_func_t fun ) +{ + unsigned i ; + pdf_real_t in ; + pdf_real_t out[MAXDIM] ; + + if ( !fun ) + { + printf("no function\n"); + return ; + } + + for ( in = fun->domain[0] - 1 ; in <= fun->domain[1] + 1.0001 ; in += 0.1 ) + { + int rc ; + if ( ( rc = pdf_eval_func(fun,&in,out) ) ) + { + printf("fun->eval error %d\n", rc ) ; + } + else + { + printf("%7.1f ",in) ; + for ( i = 0 ; i < fun->n ; i++ ) + printf(" %10.4f",out[i] ) ; + printf("\n"); + } + } +} + +#endif + + + + +static pdf_obj_t make_real_array( char *s ) +/* hand-written parser ahead, does not recognize the correct language */ +{ + pdf_real_t x ; + pdf_obj_t a ; + char *e , *r ; + a = pdf_create_array() ; + if ( s ) + { + for ( e = strtok_r(s," \t[",&r); e ; e = strtok_r(NULL," \t]",&r) ) + { + x = strtod(e,&e) ; + if ( *e ) + goto fail ; + pdf_append_array_elt(a, pdf_create_real(x) ) ; + } + } + return a ; +fail: + pdf_destroy_obj(a) ; + return NULL ; +} + + +static int get_int( pdf_obj_t dict, pdf_obj_t name, int *ret ) +{ + pdf_obj_t t ; + if ( !dict || !name || !ret ) + return -1 ; + t = pdf_get_dict_entry(dict,name) ; + if ( !t || !IS_INT(t) ) + return -1 ; + + *ret = GET_INT(t) ; + return 0 ; +} + +static uint32_t * +get_uint_array( pdf_obj_t dict, pdf_obj_t name, int *pn ) +{ + pdf_obj_t t,e ; + int i,j,n ; + uint32_t *ret ; + if ( !dict || !name || !pn ) + return NULL ; + t = pdf_get_dict_entry(dict,name) ; + if ( !t || !IS_ARRAY(t) ) + return NULL ; + n = pdf_get_array_size(t) ; + if ( !n ) + return NULL ; + ret = xmalloc( n * sizeof(*ret)) ; + for ( i = 0 ; i < n ; i++ ) + { + e = pdf_get_array_elt(t,i) ; + if ( !e || !IS_INT(e) || ( j = pdf_get_int(e) ) < 0 ) + { + free(ret) ; + return NULL ; + } + ret[i] = j ; + } + *pn = n ; + return ret ; +} + +static void test_eval( pdf_func_t fun, pdf_real_t step, pdf_real_t (*f)(int m, const pdf_real_t in[]) ) +{ + int m,n,k ; + pdf_real_t *in, *out ; + if ( !fun ) + return ; + + pdf_info_func(fun,&m,&n) ; + + in = alloca( m * sizeof(*in) ) ; + out = alloca( n * sizeof(*out) ) ; + + for ( k = 0 ; k < m ; k++ ) + in[k] = fun->domain[2*k] ; + + k = 0 ; + /* evaluating a function on a grid */ + /* this looks weird if the number of dimensions is not fixed at compile time */ + while ( k < m ) + { + if ( pdf_eval_func(fun,in,out) ) + { + printf("# pdf_eval_func(%s)\n", pdf_get_last_error_from_func()) ; + /* this is a comment line for gnuplot */ + } + else + { + for ( k = 0 ; k < m ; k++ ) + printf("%5.2f ",in[k] ) ; + for ( k = 0 ; k < n ; k++ ) + printf("\t%12.6f",out[k]) ; + printf("\t%12.6f\n", (f)? ( f(m,in) - out[0] ) : 0.0 ) ; + } + for ( k = 0 ; k < m ; k++ ) + { + in[k] += step ; + if ( in[k] > fun->domain[2*k+1] + 0.00001 ) + { + in[k] = fun->domain[2*k] ; + printf("\n") ; + } + else + break ; + } + } +} + +static pdf_obj_t make_type3_part(void) +/* f(t) = 0 if t < 0 + * f(t) = t if t in [0,1] + * f(t) = 1 if t > 1 + */ +{ + pdf_obj_t domain; + pdf_obj_t range; + pdf_obj_t dict ; + + dict = pdf_create_dict() ; + + domain = pdf_create_array() ; + pdf_append_array_elt(domain,pdf_create_real(0)) ; + pdf_append_array_elt(domain,pdf_create_real(1)) ; + pdf_create_dict_entry(dict,pdf_obj_dup(PDF_N_DOMAIN),domain) ; + range = pdf_create_array() ; + pdf_append_array_elt(range,pdf_create_real(0)) ; + pdf_append_array_elt(range,pdf_create_real(1)) ; + pdf_create_dict_entry(dict,pdf_obj_dup(PDF_N_RANGE),range) ; + + pdf_create_dict_entry(dict,pdf_obj_dup(PDF_N_FUNCTION_TYPE),pdf_create_integer(2) ) ; + pdf_create_dict_entry(dict,pdf_obj_dup(PDF_N_N),pdf_create_real(1) ) ; + return dict ; +} + +static pdf_obj_t +build_3( double a, double step, double b ) +/* just a single demo */ +{ + pdf_obj_t functions ; + pdf_obj_t domain,bounds,encode ; + pdf_obj_t dict ; + double t,x ; + + dict = pdf_create_dict() ; + pdf_create_dict_entry(dict,pdf_obj_dup(PDF_N_FUNCTION_TYPE),pdf_create_integer(3) ) ; + + domain = pdf_create_array() ; + bounds = pdf_create_array() ; + encode = pdf_create_array() ; + functions = pdf_create_array() ; + + + + pdf_append_array_elt(domain,pdf_create_real(a)) ; + pdf_append_array_elt(domain,pdf_create_real(b)) ; + + x = a ; + for ( t = a+step ; t < b ; t += step ) + { + pdf_append_array_elt(bounds, pdf_create_real(t) ) ; + pdf_append_array_elt(functions, make_type3_part()) ; + pdf_append_array_elt(encode, pdf_create_real(0) ) ; + pdf_append_array_elt(encode, pdf_create_real(1) ) ; + x = t ; + } + pdf_append_array_elt(functions, make_type3_part()) ; + pdf_append_array_elt(encode, pdf_create_real(0) ) ; + pdf_append_array_elt(encode, pdf_create_real(1) ) ; + + pdf_create_dict_entry (dict, pdf_obj_dup(PDF_N_BOUNDS), bounds ) ; + pdf_create_dict_entry (dict, pdf_obj_dup(PDF_N_DOMAIN), domain ) ; + pdf_create_dict_entry (dict, pdf_obj_dup(PDF_N_ENCODE), encode ) ; + pdf_create_dict_entry (dict, pdf_obj_dup(PDF_N_FUNCTIONS), functions ) ; + + return dict ; +} + + + +/* End of test-function.c */ +++ torture/test-all-functions 2007-11-07 15:03:12.000000000 +0100 @@ -0,0 +1,68 @@ +DATADIR=test-function-dat +PROGRAM="valgrind .libs/test-function" +export LD_LIBRARY_PATH=../src/.libs + +mkdir -p $DATADIR + +$PROGRAM --code=" +{ + dup mul 3 div + exch + dup mul + add + 2 div neg exp +} " --domain "[ -3 +3 -3 +3 ]" --range "[ 0 0.75 ]" --step 0.2 > $DATADIR/type-4-2D.dat + + +$PROGRAM --code=" +{ + dup 0 lt + { 0.1 mul } + { 0.1 ln add exp 0.1 sub } + ifelse +} " --domain "[ -4 +4 ]" --range "[ -100 100 ] " --step 0.1 > $DATADIR/type-4-1D.dat + + + +$PROGRAM --domain "[ -3 3 -3 3 ]" --range "[ 0 1 ]" \ + -n 11 -n 11 --step 0.2 --order 3 \ + --bps 24 > $DATADIR/type-0-spline.dat + +$PROGRAM --domain "[ -3 3 -3 3 ]" --range "[ 0 1 ]" \ + -n 11 -n 11 --step 0.2 \ + --bps 24 > $DATADIR/type-0-linear.dat + +# --bps 12 exposed a bug at the last sample */ + +$PROGRAM --domain "[ -3 3 -3 3 ]" --range "[ 0 1 ]" \ + -n 37 -n 37 --order 3 --step 0.1 \ + --bps 32 > $DATADIR/type-0-bps32.dat + +$PROGRAM --domain "[ -3 3 -3 3 ]" --range "[ 0 1 ]" \ + -n 37 -n 37 --order 3 --step 0.1 \ + --bps 24 > $DATADIR/type-0-bps24.dat + +$PROGRAM --domain "[ -3 3 -3 3 ]" --range "[ 0 1 ]" \ + -n 37 -n 37 --order 3 --step 0.1 \ + --bps 16 > $DATADIR/type-0-bps16.dat + +$PROGRAM --domain "[ -3 3 -3 3 ]" --range "[ 0 1 ]" \ + -n 37 -n 37 --order 3 --step 0.1 \ + --bps 12 > $DATADIR/type-0-bps12.dat + +$PROGRAM --domain "[ -3 3 -3 3 ]" --range "[ 0 1 ]" \ + -n 37 -n 37 --order 3 --step 0.1 \ + --bps 8 > $DATADIR/type-0-bps8.dat + +$PROGRAM --domain "[ -3 3 -3 3 ]" --range "[ 0 1 ]" \ + -n 37 -n 37 --order 3 --step 0.1 \ + --bps 4 > $DATADIR/type-0-bps4.dat + + +$PROGRAM --domain "[ 0 1 ]" --step 0.05 \ + -N 2.2 --C0 "[ 16 16 16 ]" --C1 " [ 240 240 240 ] " > $DATADIR/type-2.dat + + +$PROGRAM --type 3 > $DATADIR/type-3.dat + +