xref: /plan9/sys/src/cmd/gs/src/zfsample.c (revision 593dc095aefb2a85c828727bbfa9da139a49bdf4)
1 /* Copyright (C) 2002 Artifex Software Inc.  All rights reserved.
2 
3   This software is provided AS-IS with no warranty, either express or
4   implied.
5 
6   This software is distributed under license and may not be copied,
7   modified or distributed except as expressly authorized under the terms
8   of the license contained in the file LICENSE in this distribution.
9 
10   For more information about licensing, please refer to
11   http://www.ghostscript.com/licensing/. For information on
12   commercial licensing, go to http://www.artifex.com/licensing/ or
13   contact Artifex Software, Inc., 101 Lucas Valley Road #110,
14   San Rafael, CA  94903, U.S.A., +1(415)492-9861.
15 */
16 
17 /* $Id: zfsample.c,v 1.9 2003/04/26 18:40:16 dan Exp $ */
18 /* Sample data to create a type 0 function */
19 #include "memory_.h"
20 #include "ghost.h"
21 #include "oper.h"
22 #include "gxcspace.h"
23 #include "estack.h"
24 #include "ialloc.h"
25 #include "idict.h"
26 #include "idparam.h"
27 #include "ifunc.h"
28 #include "ostack.h"
29 #include "store.h"
30 #include "gsfunc0.h"
31 
32 /*
33  * We store the data in a string.  Since the max size for a string is 64k,
34  * we use that as our max data size.
35  */
36 #define MAX_DATA_SIZE 0x10000
37 /*
38  * We cannot handle more than  16 inputs.  Otherwise the the data will not
39  * fit within MAX_DATA_SIZE.
40  */
41 #define MAX_NUM_INPUTS 16
42 /*
43  * This value is rather arbitrary.
44  */
45 #define MAX_NUM_OUTPUTS 128
46 
47 /* --- Build sampled data function --- */
48 
49 /*
50  * This structure is used to hold data required while collecting samples
51  * for a type 0 function (sampled data).
52  */
53 struct gs_sampled_data_enum_s {
54     int indexes[MAX_NUM_INPUTS];
55     int o_stack_depth;		/* used to verify stack while sampling */
56     gs_function_t * pfn;
57 };
58 
59 typedef struct gs_sampled_data_enum_s gs_sampled_data_enum;
60 
61 
62 gs_private_st_ptrs1(st_gs_sampled_data_enum, gs_sampled_data_enum,
63 		"gs_sampled_data_enum", gs_sampled_data_enum_enum_ptrs,
64 		gs_sampled_data_enum_reloc_ptrs, pfn);
65 
66 
67 /* Forward references */
68 
69 private int cube_build_func0(const ref * pdict,
70 	gs_function_Sd_params_t * params, gs_memory_t *mem);
71 private int sampled_data_setup(i_ctx_t *i_ctx_p, gs_function_t *pfn,
72 	const ref * pproc, int (*finish_proc)(i_ctx_t *),
73 	gs_memory_t * mem);
74 private int sampled_data_sample(i_ctx_t *i_ctx_p);
75 private int sampled_data_continue(i_ctx_t *i_ctx_p);
76 private int sampled_data_finish(i_ctx_t *i_ctx_p);
77 
78 private gs_sampled_data_enum * gs_sampled_data_enum_alloc
79 	(gs_memory_t * mem, client_name_t cname);
80 
81 /*
82  * Collect data for a type 0 (sampled data) function
83  * <dict> .buildsampledfunction <function_struct>
84  *
85  * The following keys are used from the dictionary:
86  *    Function (required)
87  *    Domain (required)
88  *    Range (required)
89  *    Size (optional)  If Size is not specified then a default value is determined
90  *        based upon the number of inputs and outputs.
91  *    BitsPerSample (required) Only 8, 16, 24, and 32 accepted,
92  * The remaining keys are ignored.
93  */
94 private int
zbuildsampledfunction(i_ctx_t * i_ctx_p)95 zbuildsampledfunction(i_ctx_t *i_ctx_p)
96 {
97     os_ptr op = osp;
98     const ref * pdict = op;
99     ref * pfunc;
100     int code = 0;
101     gs_function_t *pfn;
102     gs_function_Sd_params_t params = {0};
103 
104     check_type(*pdict, t_dictionary);
105     /*
106      * Check procedure to be sampled.
107      */
108     if (dict_find_string(pdict, "Function", &pfunc) <= 0)
109 	return_error(e_rangecheck);
110     check_proc(*pfunc);
111     /*
112      * Set up the hyper cube function data structure.
113      */
114     code = cube_build_func0(pdict, &params, imemory);
115     if (code < 0)
116 	return code;
117     /*
118      * This is temporary.  We will call gs_function_Sd_init again after
119      * we have collected the cube data.  We are doing it now because we need
120      * a function structure created (along with its GC enumeration stuff)
121      * that we can use while collecting the cube data.  We will call
122      * the routine again after the cube data is collected to correctly
123      * initialize the function.
124      */
125     code = gs_function_Sd_init(&pfn, &params, imemory);
126     if (code < 0)
127 	return code;
128     /*
129      * Now setup to collect the sample data.
130      */
131     return sampled_data_setup(i_ctx_p, pfn, pfunc, sampled_data_finish, imemory);
132 }
133 
134 /* ------- Internal procedures ------- */
135 
136 
137 #define bits2bytes(x) ((x) >> 3)	/* Convert bit count to byte count */
138 
139 /*
140  * This routine will verify that the requested data hypercube parameters will require
141  * a data storage size less than or equal to the MAX_DATA_SIZE.
142  */
143 private bool
valid_cube_size(int num_inputs,int num_outputs,int sample_size,const int Size[])144 valid_cube_size(int num_inputs, int num_outputs, int sample_size, const int Size[])
145 {
146     int i, total_size = num_outputs * sample_size;
147 
148     for (i = 0; i < num_inputs; i++) {
149 	if (Size[i] <= 0 || Size[i] > MAX_DATA_SIZE / total_size)
150 	    return false;
151 	total_size *= Size[i];
152     }
153     return true;
154 }
155 
156 /*
157  * This routine is used to determine a default value for the sampled data size.
158  * As a default, we will build a hyper cube with each side having the same
159  * size.  The space requirements for a hypercube grow exponentially with the
160  * number of dimensions.  Thus we must use fewer points if our functions has
161  * many inputs.  The values returned were chosen simply to given a reasonable
162  * tradeoff between keeping storage requirements low but still having enough
163  * points per side to minimize loss of information.
164  *
165  * We do check to see if the data will fit using our initial guess.  If not
166  * then we decrement the size of each edge until it fits.  We will return a
167  * e_rangecheck error if the cube can not fit into the maximum  size.
168  * On exit the Size array contains the cube size (if a valid size was found).
169  */
170 private int
determine_sampled_data_size(int num_inputs,int num_outputs,int sample_size,int Size[])171 determine_sampled_data_size(int num_inputs, int num_outputs,
172 				int sample_size, int Size[])
173 {
174     static const int size_list[] = {512, 50, 20, 10, 7, 5, 4, 3};
175     int i, size;
176 
177     /* Start with initial guess at cube size */
178     if (num_inputs > 0 && num_inputs <= 8)
179 	size = size_list[num_inputs - 1];
180     else
181 	size = 2;
182     /*
183      * Verify that the cube will fit into MAX_DATA_SIZE.  If not then
184      * decrement the cube size until it will fit.
185      */
186     while (true) {
187         /* Fill Size array with value. */
188         for (i = 0; i < num_inputs; i++)
189             Size[i] = size;
190 
191 	if (valid_cube_size(num_inputs, num_outputs, sample_size, Size))
192 	    return 0;		/* We have a valid size */
193 
194 	if (size == 2)		/* Cannot have less than 2 points per side */
195 	    return_error(e_rangecheck);
196 	size--;
197     }
198 }
199 
200 
201 /*
202  * Allocate the enumerator used while collecting sampled data.  This enumerator
203  * is used to hold the various state data required while sampling.
204  */
205 private gs_sampled_data_enum *
gs_sampled_data_enum_alloc(gs_memory_t * mem,client_name_t cname)206 gs_sampled_data_enum_alloc(gs_memory_t * mem, client_name_t cname)
207 {
208     return gs_alloc_struct(mem, gs_sampled_data_enum,
209     				&st_gs_sampled_data_enum, cname);
210 }
211 
212 /*
213  * This routine will determine the location of a block of data
214  * in the hyper cube.  Basically this does an index calculation
215  * for an n dimensional cube.
216  */
217 private byte *
cube_ptr_from_index(gs_function_Sd_params_t * params,int indexes[])218 cube_ptr_from_index(gs_function_Sd_params_t * params, int indexes[])
219 {
220     int i, sum = indexes[params->m - 1];
221 
222     for (i = params->m - 2; i >= 0; i--) {
223 	sum *= params->Size[i];
224 	sum += indexes[i];
225     }
226     return (byte *)(params->DataSource.data.str.data) +
227 	sum * params->n * bits2bytes(params->BitsPerSample);
228 }
229 
230 /*
231  * This routine will increment the index values for the hypercube.  This
232  * is used for collecting the data.  If we have incremented the
233  * last index beyond its last value then we return a true, else false;
234  */
235 private bool
increment_cube_indexes(gs_function_Sd_params_t * params,int indexes[])236 increment_cube_indexes(gs_function_Sd_params_t * params, int indexes[])
237 {
238     int i = 0;
239 
240     while (true) {
241 	/*
242 	 * Increment an index value for an edge and test if we have
243 	 * gone past the final value for the edge.
244 	 */
245 	indexes[i]++;
246 	if (indexes[i] < params->Size[i])
247 	    /*
248 	     * We have not reached the end of the edge.  Exit but
249 	     * indicate that we are not done with the hypercube.
250 	     */
251 	    return false;
252 	/*
253 	 * We have reached the end of one edge of the hypercube and we
254 	 * need to increment the next index.
255 	 */
256 	indexes[i] = 0;
257 	i++;
258 	if (i == params->m)
259 	    /*
260 	     * We have finished the last edge of the hyper cube.
261 	     * We are done.
262 	     */
263 	    return true;
264     }
265 }
266 
267 /*
268  * Fill in the data for a function type 0 parameter object to be used while
269  * we collect the data for the data cube.  At the end of the process, we
270  * will create a function type 0 object to be used to calculate values
271  * as a replacement for the original function.
272  */
273 private int
cube_build_func0(const ref * pdict,gs_function_Sd_params_t * params,gs_memory_t * mem)274 cube_build_func0(const ref * pdict, gs_function_Sd_params_t * params,
275 							gs_memory_t *mem)
276 {
277     byte * bytes = 0;
278     int code, i;
279     int total_size;
280 
281     if ((code = dict_int_param(pdict, "Order", 1, 3, 1, &params->Order)) < 0 ||
282 	(code = dict_int_param(pdict, "BitsPerSample", 1, 32, 0,
283 			       &params->BitsPerSample)) < 0 ||
284 	((code = params->m =
285 	    fn_build_float_array(pdict, "Domain", false, true,
286 		    			&params->Domain, mem)) < 0 ) ||
287 	((code = params->n =
288 	    fn_build_float_array(pdict, "Range", false, true,
289 		    			&params->Range, mem)) < 0)
290 	) {
291 	goto fail;
292     }
293     /*
294      * The previous logic set the size of m and n to the size of the Domain
295      * and Range arrays.  This is twice the actual size.  Correct this and
296      * check for valid values.
297      */
298     params->m >>= 1;
299     params->n >>= 1;
300     if (params->m == 0 || params->n == 0 ||
301         params->m > MAX_NUM_INPUTS || params->n > MAX_NUM_OUTPUTS) {
302 	code = gs_note_error(e_rangecheck);
303         goto fail;
304     }
305     /*
306      * The Size array may or not be specified.  If it is not specified then
307      * we need to determine a set of default values for the Size array.
308      */
309     {
310 	int *ptr = (int *)
311 	    gs_alloc_byte_array(mem, params->m, sizeof(int), "Size");
312 
313 	if (ptr == NULL) {
314 	    code = gs_note_error(e_VMerror);
315 	    goto fail;
316 	}
317 	params->Size = ptr;
318 	code = dict_ints_param(pdict, "Size", params->m, ptr);
319         if (code < 0)
320 	    goto fail;
321         if (code == 0) {
322 	    /*
323 	     * The Size array has not been specified.  Determine a default
324 	     * set of values.
325 	     */
326             code = determine_sampled_data_size(params->m, params->n,
327 	     			params->BitsPerSample, (int *)params->Size);
328             if (code < 0)
329 	        goto fail;
330         }
331 	else {			/* Size array specified - verify valid */
332 	    if (code != params->m || !valid_cube_size(params->m, params->n,
333 	    				params->BitsPerSample, params->Size))
334 	        code = gs_note_error(e_rangecheck);
335 	        goto fail;
336 	}
337     }
338     /*
339      * Determine space required for the sample data storage.
340      */
341     total_size = params->n * bits2bytes(params->BitsPerSample);
342     for (i = 0; i < params->m; i++)
343 	total_size *= params->Size[i];
344     /*
345      * Allocate space for the data cube itself.
346      */
347     bytes = gs_alloc_byte_array(mem, total_size, 1, "cube_build_func0(bytes)");
348     if (!bytes) {
349 	code = gs_note_error(e_VMerror);
350 	goto fail;
351     }
352     data_source_init_bytes(&params->DataSource,
353     				(const unsigned char *)bytes, total_size);
354 
355     return 0;
356 
357 fail:
358     gs_function_Sd_free_params(params, mem);
359     return (code < 0 ? code : gs_note_error(e_rangecheck));
360 }
361 
362 /*
363  * Layout of stuff pushed on estack while collecting the sampled data.
364  * The data is saved there since it is safe from attack by the procedure
365  * being sampled and is convient.
366  *
367  *      finishing procedure (or 0)
368  *      procedure being sampled
369  *      enumeration structure (as bytes)
370  */
371 #define estack_storage 3
372 #define esp_finish_proc (*real_opproc(esp - 2))
373 #define sample_proc esp[-1]
374 #define senum r_ptr(esp, gs_sampled_data_enum)
375 /*
376  * Sone invalid tint transform functions pop more items off of the stack
377  * then they are supposed to use.  This is a violation of the PLRM however
378  * this is done by Adobe and we have to handle the situation.  This is
379  * a kludge but we set aside some unused stack space below the input
380  * variables.  The tint transform can trash this without causing any
381  * real problems.
382  */
383 #define O_STACK_PAD 3
384 
385 /*
386  * Set up to collect the data for the sampled function.  This is used for
387  * those alternate tint transforms that cannot be converted into a
388  * type 4 function.
389  */
390 private int
sampled_data_setup(i_ctx_t * i_ctx_p,gs_function_t * pfn,const ref * pproc,int (* finish_proc)(i_ctx_t *),gs_memory_t * mem)391 sampled_data_setup(i_ctx_t *i_ctx_p, gs_function_t *pfn,
392 	const ref * pproc, int (*finish_proc)(i_ctx_t *), gs_memory_t * mem)
393 {
394     os_ptr op = osp;
395     gs_sampled_data_enum *penum;
396     int i;
397     gs_function_Sd_params_t * params = (gs_function_Sd_params_t *)&pfn->params;
398 
399     check_estack(estack_storage + 1);		/* Verify space on estack */
400     check_ostack(params->m + O_STACK_PAD);	/* and the operand stack */
401     check_ostack(params->n + O_STACK_PAD);
402 
403     /*
404      * Allocate space for the enumerator data structure.
405      */
406     penum = gs_sampled_data_enum_alloc(imemory, "zbuildsampledfuntion(params)");
407     if (penum == NULL)
408 	return_error(e_VMerror);
409 
410     /* Initialize data in the enumeration structure */
411 
412     penum->pfn = pfn;
413     for(i=0; i< params->m; i++)
414         penum->indexes[i] = 0;
415     /*
416      * Save stack depth for checking the correct number of values on stack
417      * after the function, which is being sampled, is called.
418      */
419     penum->o_stack_depth = ref_stack_count(&o_stack);
420     /*
421      * Note:  As previously mentioned, we are putting some spare (unused) stack
422      * space under the input values in case the function unbalances the stack.
423      * It is possible for the function to pop or change values on the stack
424      * outside of the input values.  (This has been found to happen with some
425      * proc sets from Adobe.)
426      */
427     push(O_STACK_PAD);
428     for (i = 0; i < O_STACK_PAD; i++) 		/* Set space = null */
429 	make_null(op - i);
430 
431     /* Push everything on the estack */
432 
433     esp += estack_storage;
434     make_op_estack(esp - 2, finish_proc);	/* Finish proc onto estack */
435     sample_proc = *pproc;			/* Save function to be sampled */
436     make_istruct(esp, 0, penum);		/* Color cube enumeration structure */
437     push_op_estack(sampled_data_sample);	/* Start sampling data */
438     return o_push_estack;
439 }
440 
441 /*
442  * Set up to collect the next sampled data value.
443  */
444 private int
sampled_data_sample(i_ctx_t * i_ctx_p)445 sampled_data_sample(i_ctx_t *i_ctx_p)
446 {
447     os_ptr op = osp;
448     gs_sampled_data_enum *penum = senum;
449     ref proc;
450     gs_function_Sd_params_t * params =
451     			(gs_function_Sd_params_t *)&penum->pfn->params;
452     int num_inputs = params->m;
453     int i;
454 
455     /* Put set of input values onto the stack. */
456     push(num_inputs);
457     for (i = 0; i < num_inputs; i++) {
458 	double dmin = params->Domain[2 * i];
459 	double dmax = params->Domain[2 * i + 1];
460 
461 	make_real(op - num_inputs + i + 1, (float) (
462 	    penum->indexes[i] * (dmax - dmin)/(params->Size[i] - 1) + dmin));
463     }
464 
465     proc = sample_proc;			    /* Get procedure from storage */
466     push_op_estack(sampled_data_continue);  /* Put 'save' routine on estack, after sample proc */
467     *++esp = proc;			    /* Put procedure to be executed */
468     return o_push_estack;
469 }
470 
471 /*
472  * Continuation procedure for processing sampled values.
473  */
474 private int
sampled_data_continue(i_ctx_t * i_ctx_p)475 sampled_data_continue(i_ctx_t *i_ctx_p)
476 {
477     os_ptr op = osp;
478     gs_sampled_data_enum *penum = senum;
479     gs_function_Sd_params_t * params =
480 	    (gs_function_Sd_params_t *)&penum->pfn->params;
481     int i, j, num_out = params->n;
482     int code = 0;
483     byte * data_ptr;
484     double sampled_data_value_max = (double)((1 << params->BitsPerSample) - 1);
485     int bps = bits2bytes(params->BitsPerSample);
486 
487     /*
488      * Check to make sure that the procedure produced the correct number of
489      * values.  If not, move the stack back to where it belongs and abort
490      */
491     if (num_out + O_STACK_PAD + penum->o_stack_depth != ref_stack_count(&o_stack)) {
492 	int stack_depth_adjust = ref_stack_count(&o_stack) - penum->o_stack_depth;
493 
494 	if (stack_depth_adjust >= 0)
495 	    pop(stack_depth_adjust);
496 	else {
497 	    /*
498 	     * If we get to here then there were major problems.  The function
499 	     * removed too many items off of the stack.  We had placed extra
500 	     * (unused) stack stack space to allow for this but the function
501 	     * exceeded even that.  Data on the stack may have been lost.
502 	     * The only thing that we can do is move the stack pointer back and
503 	     * hope.  (We have not seen real Postscript files that have this
504 	     * problem.)
505 	     */
506 	    push(-stack_depth_adjust);
507 	}
508 	ifree_object(penum->pfn, "sampled_data_continue(pfn)");
509 	ifree_object(penum, "sampled_data_continue((enum)");
510 	return_error(e_undefinedresult);
511     }
512 
513     /* Save data from the given function */
514     data_ptr = cube_ptr_from_index(params, penum->indexes);
515     for (i=0; i < num_out; i++) {
516 	ulong cv;
517         double value;
518 	double rmin = params->Range[2 * i];
519 	double rmax = params->Range[2 * i + 1];
520 
521         code = real_param(op + i - num_out + 1, &value);
522         if (code < 0)
523 	    return code;
524 	if (value < rmin)
525 	    value = rmin;
526 	else if (value > rmax)
527 	    value = rmax;
528 	value = (value - rmin) / (rmax - rmin);		/* Convert to 0 to 1.0 */
529 	cv = (int) (value * sampled_data_value_max + 0.5);
530 	for (j = 0; j < bps; j++)
531 	    data_ptr[bps * i + j] = (byte)(cv >> ((bps - 1 - j) * 8));	/* MSB first */
532     }
533     pop(num_out);		    /* Move op to base of result values */
534 
535     /* Check if we are done collecting data. */
536 
537     if (increment_cube_indexes(params, penum->indexes)) {
538 	pop(O_STACK_PAD);	    /* Remove spare stack space */
539 	/* Execute the closing procedure, if given */
540 	code = 0;
541 	if (esp_finish_proc != 0)
542 	    code = esp_finish_proc(i_ctx_p);
543 
544 	return code;
545     }
546 
547     /* Now get the data for the next location */
548 
549     return sampled_data_sample(i_ctx_p);
550 }
551 
552 /*
553  * We have collected all of the sample data.  Create a type 0 function stucture.
554  */
555 private int
sampled_data_finish(i_ctx_t * i_ctx_p)556 sampled_data_finish(i_ctx_t *i_ctx_p)
557 {
558     os_ptr op = osp;
559     gs_sampled_data_enum *penum = senum;
560     /* Build a type 0 function using the given parameters */
561     gs_function_Sd_params_t * params =
562 	(gs_function_Sd_params_t *)&penum->pfn->params;
563     gs_function_t * pfn;
564     ref cref;			/* closure */
565     int code = gs_function_Sd_init(&pfn, params, imemory);
566 
567     if (code < 0)
568 	return code;
569 
570     code = ialloc_ref_array(&cref, a_executable | a_execute, 2,
571 			    "sampled_data_finish(cref)");
572     if (code < 0)
573 	return code;
574 
575     make_istruct_new(cref.value.refs, a_executable | a_execute, pfn);
576     make_oper_new(cref.value.refs + 1, 0, zexecfunction);
577     ref_assign(op, &cref);
578 
579     esp -= estack_storage;
580     ifree_object(penum->pfn, "sampled_data_finish(pfn)");
581     ifree_object(penum, "sampled_data_finish(enum)");
582     return o_pop_estack;
583 }
584 
585 
586 /* ------ Initialization procedure ------ */
587 
588 const op_def zfsample_op_defs[] =
589 {
590     op_def_begin_level2(),
591     {"1.buildsampledfunction", zbuildsampledfunction},
592     op_def_end(0)
593 };
594