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, ¶ms, 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, ¶ms, 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, ¶ms->Order)) < 0 ||
282 (code = dict_int_param(pdict, "BitsPerSample", 1, 32, 0,
283 ¶ms->BitsPerSample)) < 0 ||
284 ((code = params->m =
285 fn_build_float_array(pdict, "Domain", false, true,
286 ¶ms->Domain, mem)) < 0 ) ||
287 ((code = params->n =
288 fn_build_float_array(pdict, "Range", false, true,
289 ¶ms->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(¶ms->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