/src/ghostpdl/psi/zfsample.c
Line | Count | Source |
1 | | /* Copyright (C) 2001-2026 Artifex Software, Inc. |
2 | | All Rights Reserved. |
3 | | |
4 | | This software is provided AS-IS with no warranty, either express or |
5 | | implied. |
6 | | |
7 | | This software is distributed under license and may not be copied, |
8 | | modified or distributed except as expressly authorized under the terms |
9 | | of the license contained in the file LICENSE in this distribution. |
10 | | |
11 | | Refer to licensing information at http://www.artifex.com or contact |
12 | | Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco, |
13 | | CA 94129, USA, for further information. |
14 | | */ |
15 | | |
16 | | |
17 | | /* Sample data to create a type 0 function */ |
18 | | #include "memory_.h" |
19 | | #include "ghost.h" |
20 | | #include "oper.h" |
21 | | #include "gxcspace.h" |
22 | | #include "estack.h" |
23 | | #include "ialloc.h" |
24 | | #include "idict.h" |
25 | | #include "idparam.h" |
26 | | #include "ifunc.h" |
27 | | #include "ostack.h" |
28 | | #include "store.h" |
29 | | #include "gsfunc0.h" |
30 | | #include "gscdevn.h" |
31 | | #include "zfunc.h" |
32 | | #include "zcolor.h" |
33 | | #include "gxdevice.h" |
34 | | |
35 | | /* |
36 | | * We store the data in a string. Since the max size for a string is 64k, |
37 | | * we use that as our max data size. |
38 | | */ |
39 | 263 | #define MAX_DATA_SIZE 0x10000 |
40 | | /* |
41 | | * We cannot handle more than 16 inputs. Otherwise the the data will not |
42 | | * fit within MAX_DATA_SIZE. |
43 | | */ |
44 | 263 | #define MAX_NUM_INPUTS 16 |
45 | | /* |
46 | | * This value is rather arbitrary. |
47 | | */ |
48 | 0 | #define MAX_NUM_OUTPUTS 128 |
49 | | |
50 | | /* --- Build sampled data function --- */ |
51 | | |
52 | | /* |
53 | | * This structure is used to hold data required while collecting samples |
54 | | * for a type 0 function (sampled data). |
55 | | */ |
56 | | struct gs_sampled_data_enum_s { |
57 | | int indexes[MAX_NUM_INPUTS]; |
58 | | int o_stack_depth; /* used to verify stack while sampling */ |
59 | | gs_function_t * pfn; |
60 | | }; |
61 | | |
62 | | typedef struct gs_sampled_data_enum_s gs_sampled_data_enum; |
63 | | |
64 | | gs_private_st_ptrs1(st_gs_sampled_data_enum, gs_sampled_data_enum, |
65 | | "gs_sampled_data_enum", gs_sampled_data_enum_enum_ptrs, |
66 | | gs_sampled_data_enum_reloc_ptrs, pfn); |
67 | | |
68 | | /* Forward references */ |
69 | | |
70 | | static int cube_build_func0(const ref * pdict, |
71 | | gs_function_Sd_params_t * params, gs_memory_t *mem); |
72 | | static int sampled_data_setup(i_ctx_t *i_ctx_p, gs_function_t *pfn, |
73 | | const ref * pproc, int (*finish_proc)(i_ctx_t *), |
74 | | gs_memory_t * mem); |
75 | | static int sampled_data_sample(i_ctx_t *i_ctx_p); |
76 | | static int sampled_data_continue(i_ctx_t *i_ctx_p); |
77 | | static int sampled_data_finish(i_ctx_t *i_ctx_p); |
78 | | |
79 | | static gs_sampled_data_enum * gs_sampled_data_enum_alloc |
80 | | (gs_memory_t * mem, client_name_t cname); |
81 | | |
82 | | /* |
83 | | * Collect data for a type 0 (sampled data) function |
84 | | * <dict> .buildsampledfunction <function_struct> |
85 | | * |
86 | | * The following keys are used from the dictionary: |
87 | | * Function (required) |
88 | | * Domain (required) |
89 | | * Range (required) |
90 | | * Size (optional) If Size is not specified then a default value is determined |
91 | | * based upon the number of inputs and outputs. |
92 | | * BitsPerSample (required) Only 8, 16, 24, and 32 accepted, |
93 | | * The remaining keys are ignored. |
94 | | */ |
95 | | static int |
96 | | zbuildsampledfunction(i_ctx_t *i_ctx_p) |
97 | 0 | { |
98 | 0 | os_ptr op = osp; |
99 | 0 | const ref * pdict = op; |
100 | 0 | ref * pfunc; |
101 | 0 | int code = 0; |
102 | 0 | gs_function_t *pfn; |
103 | 0 | gs_function_Sd_params_t params = {0}; |
104 | |
|
105 | 0 | check_op(1); |
106 | 0 | check_type(*pdict, t_dictionary); |
107 | | /* |
108 | | * Check procedure to be sampled. |
109 | | */ |
110 | 0 | if (dict_find_string(pdict, "Function", &pfunc) <= 0) |
111 | 0 | return_error(gs_error_rangecheck); |
112 | 0 | check_proc(*pfunc); |
113 | | /* |
114 | | * Set up the hyper cube function data structure. |
115 | | */ |
116 | 0 | code = cube_build_func0(pdict, ¶ms, imemory); |
117 | 0 | if (code < 0) |
118 | 0 | return code; |
119 | | /* |
120 | | * This is temporary. We will call gs_function_Sd_init again after |
121 | | * we have collected the cube data. We are doing it now because we need |
122 | | * a function structure created (along with its GC enumeration stuff) |
123 | | * that we can use while collecting the cube data. We will call |
124 | | * the routine again after the cube data is collected to correctly |
125 | | * initialize the function. |
126 | | */ |
127 | 0 | code = gs_function_Sd_init(&pfn, ¶ms, imemory); |
128 | 0 | if (code < 0) |
129 | 0 | return code; |
130 | | /* |
131 | | * Now setup to collect the sample data. |
132 | | */ |
133 | 0 | return sampled_data_setup(i_ctx_p, pfn, pfunc, sampled_data_finish, imemory); |
134 | 0 | } |
135 | | |
136 | | /* ------- Internal procedures ------- */ |
137 | | |
138 | 195k | #define bits2bytes(x) ((x) >> 3) /* Convert bit count to byte count */ |
139 | | |
140 | | /* |
141 | | * This routine will verify that the requested data hypercube parameters will require |
142 | | * a data storage size less than or equal to the MAX_DATA_SIZE. |
143 | | */ |
144 | | static bool |
145 | | valid_cube_size(int num_inputs, int num_outputs, int sample_size, const int Size[]) |
146 | 263 | { |
147 | 263 | int i, total_size = num_outputs * sample_size; |
148 | | |
149 | 526 | for (i = 0; i < num_inputs; i++) { |
150 | 263 | if (Size[i] <= 0 || Size[i] > MAX_DATA_SIZE / total_size) |
151 | 0 | return false; |
152 | 263 | total_size *= Size[i]; |
153 | 263 | } |
154 | 263 | return true; |
155 | 263 | } |
156 | | |
157 | | /* |
158 | | * This routine is used to determine a default value for the sampled data size. |
159 | | * As a default, we will build a hyper cube with each side having the same |
160 | | * size. The space requirements for a hypercube grow exponentially with the |
161 | | * number of dimensions. Thus we must use fewer points if our functions has |
162 | | * many inputs. The values returned were chosen simply to given a reasonable |
163 | | * tradeoff between keeping storage requirements low but still having enough |
164 | | * points per side to minimize loss of information. |
165 | | * |
166 | | * We do check to see if the data will fit using our initial guess. If not |
167 | | * then we decrement the size of each edge until it fits. We will return a |
168 | | * gs_error_rangecheck error if the cube can not fit into the maximum size. |
169 | | * On exit the Size array contains the cube size (if a valid size was found). |
170 | | */ |
171 | | static int |
172 | | determine_sampled_data_size(int num_inputs, int num_outputs, |
173 | | int sample_size, int Size[]) |
174 | 263 | { |
175 | 263 | static const int size_list[] = {512, 50, 20, 10, 7, 5, 4, 3}; |
176 | 263 | int i, size; |
177 | | |
178 | | /* Start with initial guess at cube size */ |
179 | 263 | if (num_inputs > 0 && num_inputs <= 8) |
180 | 263 | size = size_list[num_inputs - 1]; |
181 | 0 | else |
182 | 0 | size = 2; |
183 | | /* |
184 | | * Verify that the cube will fit into MAX_DATA_SIZE. If not then |
185 | | * decrement the cube size until it will fit. |
186 | | */ |
187 | 263 | while (true) { |
188 | | /* Fill Size array with value. */ |
189 | 526 | for (i = 0; i < num_inputs; i++) |
190 | 263 | Size[i] = size; |
191 | | |
192 | | /* If we have reached the minimum size (2), don't bother checking if its 'valid' |
193 | | * as there is nothing we cna do now if it isn't. |
194 | | */ |
195 | 263 | if (size > 2) { |
196 | 263 | if (valid_cube_size(num_inputs, num_outputs, sample_size, Size)) |
197 | 263 | return 0; /* We have a valid size */ |
198 | 0 | size--; |
199 | 0 | } else { |
200 | 0 | return 0; |
201 | 0 | } |
202 | 263 | } |
203 | 263 | } |
204 | | |
205 | | /* |
206 | | * Allocate the enumerator used while collecting sampled data. This enumerator |
207 | | * is used to hold the various state data required while sampling. |
208 | | */ |
209 | | static gs_sampled_data_enum * |
210 | | gs_sampled_data_enum_alloc(gs_memory_t * mem, client_name_t cname) |
211 | 263 | { |
212 | 263 | return gs_alloc_struct(mem, gs_sampled_data_enum, |
213 | 263 | &st_gs_sampled_data_enum, cname); |
214 | 263 | } |
215 | | |
216 | | /* |
217 | | * This routine will determine the location of a block of data |
218 | | * in the hyper cube. Basically this does an index calculation |
219 | | * for an n dimensional cube. |
220 | | */ |
221 | | static byte * |
222 | | cube_ptr_from_index(gs_function_Sd_params_t * params, int indexes[]) |
223 | 97.8k | { |
224 | 97.8k | int i, sum = indexes[params->m - 1]; |
225 | | |
226 | 97.8k | for (i = params->m - 2; i >= 0; i--) { |
227 | 0 | sum *= params->Size[i]; |
228 | 0 | sum += indexes[i]; |
229 | 0 | } |
230 | 97.8k | return (byte *)(params->DataSource.data.str.data) + |
231 | 97.8k | sum * params->n * bits2bytes(params->BitsPerSample); |
232 | 97.8k | } |
233 | | |
234 | | /* |
235 | | * This routine will increment the index values for the hypercube. This |
236 | | * is used for collecting the data. If we have incremented the |
237 | | * last index beyond its last value then we return a true, else false; |
238 | | */ |
239 | | static bool |
240 | | increment_cube_indexes(gs_function_Sd_params_t * params, int indexes[]) |
241 | 97.8k | { |
242 | 97.8k | int i = 0; |
243 | | |
244 | 97.8k | while (true) { |
245 | | /* |
246 | | * Increment an index value for an edge and test if we have |
247 | | * gone past the final value for the edge. |
248 | | */ |
249 | 97.8k | indexes[i]++; |
250 | 97.8k | if (indexes[i] < params->Size[i]) |
251 | | /* |
252 | | * We have not reached the end of the edge. Exit but |
253 | | * indicate that we are not done with the hypercube. |
254 | | */ |
255 | 97.6k | return false; |
256 | | /* |
257 | | * We have reached the end of one edge of the hypercube and we |
258 | | * need to increment the next index. |
259 | | */ |
260 | 191 | indexes[i] = 0; |
261 | 191 | i++; |
262 | 191 | if (i == params->m) |
263 | | /* |
264 | | * We have finished the last edge of the hyper cube. |
265 | | * We are done. |
266 | | */ |
267 | 191 | return true; |
268 | 191 | } |
269 | 97.8k | } |
270 | | |
271 | | /* |
272 | | * Fill in the data for a function type 0 parameter object to be used while |
273 | | * we collect the data for the data cube. At the end of the process, we |
274 | | * will create a function type 0 object to be used to calculate values |
275 | | * as a replacement for the original function. |
276 | | */ |
277 | | static int |
278 | | cube_build_func0(const ref * pdict, gs_function_Sd_params_t * params, |
279 | | gs_memory_t *mem) |
280 | 0 | { |
281 | 0 | byte * bytes = 0; |
282 | 0 | int code, i; |
283 | 0 | int total_size; |
284 | |
|
285 | 0 | if ((code = dict_int_param(pdict, "Order", 1, 3, 1, ¶ms->Order)) < 0 || |
286 | 0 | (code = dict_int_param(pdict, "BitsPerSample", 1, 32, 0, |
287 | 0 | ¶ms->BitsPerSample)) < 0 || |
288 | 0 | ((code = params->m = |
289 | 0 | fn_build_float_array(pdict, "Domain", false, true, |
290 | 0 | ¶ms->Domain, mem)) < 0 ) || |
291 | 0 | ((code = params->n = |
292 | 0 | fn_build_float_array(pdict, "Range", false, true, |
293 | 0 | ¶ms->Range, mem)) < 0) |
294 | 0 | ) { |
295 | 0 | goto fail; |
296 | 0 | } |
297 | | /* |
298 | | * The previous logic set the size of m and n to the size of the Domain |
299 | | * and Range arrays. This is twice the actual size. Correct this and |
300 | | * check for valid values. |
301 | | */ |
302 | 0 | params->m >>= 1; |
303 | 0 | params->n >>= 1; |
304 | 0 | if (params->m == 0 || params->n == 0 || |
305 | 0 | params->m > MAX_NUM_INPUTS || params->n > MAX_NUM_OUTPUTS) { |
306 | 0 | code = gs_note_error(gs_error_rangecheck); |
307 | 0 | goto fail; |
308 | 0 | } |
309 | | /* |
310 | | * The Size array may or not be specified. If it is not specified then |
311 | | * we need to determine a set of default values for the Size array. |
312 | | */ |
313 | 0 | { |
314 | 0 | int *ptr = (int *) |
315 | 0 | gs_alloc_byte_array(mem, params->m, sizeof(int), "Size"); |
316 | |
|
317 | 0 | if (ptr == NULL) { |
318 | 0 | code = gs_note_error(gs_error_VMerror); |
319 | 0 | goto fail; |
320 | 0 | } |
321 | 0 | params->Size = ptr; |
322 | 0 | code = dict_ints_param(mem, pdict, "Size", params->m, ptr); |
323 | 0 | if (code < 0) |
324 | 0 | goto fail; |
325 | 0 | if (code == 0) { |
326 | | /* |
327 | | * The Size array has not been specified. Determine a default |
328 | | * set of values. |
329 | | */ |
330 | 0 | code = determine_sampled_data_size(params->m, params->n, |
331 | 0 | params->BitsPerSample, (int *)params->Size); |
332 | 0 | if (code < 0) |
333 | 0 | goto fail; |
334 | 0 | } |
335 | 0 | else { /* Size array specified - verify valid */ |
336 | 0 | if (code != params->m || !valid_cube_size(params->m, params->n, |
337 | 0 | params->BitsPerSample, params->Size)) { |
338 | 0 | code = gs_note_error(gs_error_rangecheck); |
339 | 0 | goto fail; |
340 | 0 | } |
341 | 0 | } |
342 | 0 | } |
343 | | /* |
344 | | * Determine space required for the sample data storage. |
345 | | */ |
346 | 0 | total_size = params->n * bits2bytes(params->BitsPerSample); |
347 | 0 | for (i = 0; i < params->m; i++) |
348 | 0 | total_size *= params->Size[i]; |
349 | | /* |
350 | | * Allocate space for the data cube itself. |
351 | | */ |
352 | 0 | bytes = gs_alloc_byte_array(mem, total_size, 1, "cube_build_func0(bytes)"); |
353 | 0 | if (!bytes) { |
354 | 0 | code = gs_note_error(gs_error_VMerror); |
355 | 0 | goto fail; |
356 | 0 | } |
357 | 0 | data_source_init_bytes(¶ms->DataSource, |
358 | 0 | (const unsigned char *)bytes, total_size); |
359 | |
|
360 | 0 | return 0; |
361 | | |
362 | 0 | fail: |
363 | 0 | gs_function_Sd_free_params(params, mem); |
364 | 0 | return (code < 0 ? code : gs_note_error(gs_error_rangecheck)); |
365 | 0 | } |
366 | | |
367 | | /* |
368 | | * Layout of stuff pushed on estack while collecting the sampled data. |
369 | | * The data is saved there since it is safe from attack by the procedure |
370 | | * being sampled and is convient. |
371 | | * |
372 | | * finishing procedure (or 0) |
373 | | * procedure being sampled |
374 | | * enumeration structure (as bytes) |
375 | | */ |
376 | 463 | #define estack_storage 3 |
377 | 382 | #define esp_finish_proc (*real_opproc(esp - 2)) |
378 | 64.7k | #define sample_proc esp[-1] |
379 | 162k | #define senum r_ptr(esp, gs_sampled_data_enum) |
380 | | /* |
381 | | * Sone invalid tint transform functions pop more items off of the stack |
382 | | * then they are supposed to use. This is a violation of the PLRM however |
383 | | * this is done by Adobe and we have to handle the situation. This is |
384 | | * a kludge but we set aside some unused stack space below the input |
385 | | * variables. The tint transform can trash this without causing any |
386 | | * real problems. |
387 | | */ |
388 | 284k | #define O_STACK_PAD 3 |
389 | | |
390 | | /* |
391 | | * Set up to collect the data for the sampled function. This is used for |
392 | | * those alternate tint transforms that cannot be converted into a |
393 | | * type 4 function. |
394 | | */ |
395 | | static int |
396 | | sampled_data_setup(i_ctx_t *i_ctx_p, gs_function_t *pfn, |
397 | | const ref * pproc, int (*finish_proc)(i_ctx_t *), gs_memory_t * mem) |
398 | 263 | { |
399 | 263 | os_ptr op = osp; |
400 | 263 | gs_sampled_data_enum *penum; |
401 | 263 | int i; |
402 | 263 | gs_function_Sd_params_t * params = (gs_function_Sd_params_t *)&pfn->params; |
403 | | |
404 | 263 | check_estack(estack_storage + 1); /* Verify space on estack */ |
405 | 263 | check_ostack(params->m + O_STACK_PAD); /* and the operand stack */ |
406 | 263 | check_ostack(params->n + O_STACK_PAD); |
407 | | |
408 | | /* |
409 | | * Allocate space for the enumerator data structure. |
410 | | */ |
411 | 263 | penum = gs_sampled_data_enum_alloc(imemory, "zbuildsampledfuntion(params)"); |
412 | 263 | if (penum == NULL) |
413 | 0 | return_error(gs_error_VMerror); |
414 | | |
415 | | /* Initialize data in the enumeration structure */ |
416 | | |
417 | 263 | penum->pfn = pfn; |
418 | 526 | for(i=0; i< params->m; i++) |
419 | 263 | penum->indexes[i] = 0; |
420 | | /* |
421 | | * Save stack depth for checking the correct number of values on stack |
422 | | * after the function, which is being sampled, is called. |
423 | | */ |
424 | 263 | penum->o_stack_depth = ref_stack_count(&o_stack); |
425 | | /* |
426 | | * Note: As previously mentioned, we are putting some spare (unused) stack |
427 | | * space under the input values in case the function unbalances the stack. |
428 | | * It is possible for the function to pop or change values on the stack |
429 | | * outside of the input values. (This has been found to happen with some |
430 | | * proc sets from Adobe.) |
431 | | */ |
432 | 263 | push(O_STACK_PAD); |
433 | 1.05k | for (i = 0; i < O_STACK_PAD; i++) /* Set space = null */ |
434 | 789 | make_null(op - i); |
435 | | |
436 | | /* Push everything on the estack */ |
437 | | |
438 | 263 | esp += estack_storage; |
439 | 263 | make_op_estack(esp - 2, finish_proc); /* Finish proc onto estack */ |
440 | 263 | sample_proc = *pproc; /* Save function to be sampled */ |
441 | 263 | make_istruct(esp, 0, penum); /* Color cube enumeration structure */ |
442 | 263 | push_op_estack(sampled_data_sample); /* Start sampling data */ |
443 | 263 | return o_push_estack; |
444 | 263 | } |
445 | | |
446 | | /* |
447 | | * Set up to collect the next sampled data value. |
448 | | */ |
449 | | static int |
450 | | sampled_data_sample(i_ctx_t *i_ctx_p) |
451 | 64.4k | { |
452 | 64.4k | os_ptr op = osp; |
453 | 64.4k | gs_sampled_data_enum *penum = senum; |
454 | 64.4k | ref proc; |
455 | 64.4k | gs_function_Sd_params_t * params = |
456 | 64.4k | (gs_function_Sd_params_t *)&penum->pfn->params; |
457 | 64.4k | int num_inputs = params->m; |
458 | 64.4k | int i; |
459 | | |
460 | | /* Put set of input values onto the stack. */ |
461 | 64.4k | push(num_inputs); |
462 | 128k | for (i = 0; i < num_inputs; i++) { |
463 | 64.4k | double dmin = params->Domain[2 * i]; |
464 | 64.4k | double dmax = params->Domain[2 * i + 1]; |
465 | | |
466 | 64.4k | make_real(op - num_inputs + i + 1, (float) ( |
467 | 64.4k | penum->indexes[i] * (dmax - dmin)/(params->Size[i] - 1) + dmin)); |
468 | 64.4k | } |
469 | | |
470 | 64.4k | proc = sample_proc; /* Get procedure from storage */ |
471 | 64.4k | push_op_estack(sampled_data_continue); /* Put 'save' routine on estack, after sample proc */ |
472 | 64.4k | *++esp = proc; /* Put procedure to be executed */ |
473 | 64.4k | return o_push_estack; |
474 | 64.4k | } |
475 | | |
476 | | /* |
477 | | * Continuation procedure for processing sampled values. |
478 | | */ |
479 | | static int |
480 | | sampled_data_continue(i_ctx_t *i_ctx_p) |
481 | 97.8k | { |
482 | 97.8k | os_ptr op = osp; |
483 | 97.8k | gs_sampled_data_enum *penum = senum; |
484 | 97.8k | gs_function_Sd_params_t * params = |
485 | 97.8k | (gs_function_Sd_params_t *)&penum->pfn->params; |
486 | 97.8k | int i, j, num_out = params->n; |
487 | 97.8k | int code = 0; |
488 | 97.8k | byte * data_ptr; |
489 | 97.8k | double sampled_data_value_max = (double)((1 << params->BitsPerSample) - 1); |
490 | 97.8k | int bps = bits2bytes(params->BitsPerSample), stack_depth_adjust = 0; |
491 | | |
492 | | /* |
493 | | * Check to make sure that the procedure produced the correct number of |
494 | | * values. If not, move the stack back to where it belongs and abort. |
495 | | * There are two forms of "stackunderflow" one is that there are genuinely |
496 | | * too few entries on the stack, the other is that there are too few entries |
497 | | * on this stack block. To establish the difference, we need to return the |
498 | | * stackunderflow error, without meddling with the exec stack, so gs_call_interp() |
499 | | * can try popping a stack block, and letting us retry. |
500 | | * Hence we check overall stack depth, *and* do check_op(). |
501 | | */ |
502 | 97.8k | if (num_out + O_STACK_PAD + penum->o_stack_depth != ref_stack_count(&o_stack)) { |
503 | 93.2k | stack_depth_adjust = ref_stack_count(&o_stack) - penum->o_stack_depth; |
504 | | |
505 | 93.2k | if (stack_depth_adjust < 0) { |
506 | | /* |
507 | | * If we get to here then there were major problems. The function |
508 | | * removed too many items off of the stack. We had placed extra |
509 | | * (unused) stack stack space to allow for this but the function |
510 | | * exceeded even that. Data on the stack may have been lost. |
511 | | * The only thing that we can do is move the stack pointer back and |
512 | | * hope. |
513 | | */ |
514 | 20 | push(-stack_depth_adjust); |
515 | 20 | return_error(gs_error_undefinedresult); |
516 | 20 | } |
517 | 93.2k | } |
518 | 97.8k | if ( op < osbot + ((num_out) - 1) ) { |
519 | 2 | return_error(gs_error_stackunderflow); |
520 | 2 | } |
521 | | /* Save data from the given function */ |
522 | 97.8k | data_ptr = cube_ptr_from_index(params, penum->indexes); |
523 | 489k | for (i=0; i < num_out; i++) { |
524 | 391k | ulong cv; |
525 | 391k | double value; |
526 | 391k | double rmin = params->Range[2 * i]; |
527 | 391k | double rmax = params->Range[2 * i + 1]; |
528 | | |
529 | 391k | code = real_param(op + i - num_out + 1, &value); |
530 | 391k | if (code < 0) { |
531 | 9 | esp -= estack_storage; |
532 | 9 | return code; |
533 | 9 | } |
534 | 391k | if (value < rmin) |
535 | 0 | value = rmin; |
536 | 391k | else if (value > rmax) |
537 | 3.59k | value = rmax; |
538 | 391k | value = (value - rmin) / (rmax - rmin); /* Convert to 0 to 1.0 */ |
539 | 391k | cv = (int) (value * sampled_data_value_max + 0.5); |
540 | 1.17M | for (j = 0; j < bps; j++) |
541 | 782k | data_ptr[bps * i + j] = (byte)(cv >> ((bps - 1 - j) * 8)); /* MSB first */ |
542 | 391k | } |
543 | | |
544 | 97.8k | pop(num_out); /* Move op to base of result values */ |
545 | | |
546 | | /* From here on, we have to use ref_stack_pop() rather than pop() |
547 | | so that it handles stack extension blocks properly, before calling |
548 | | sampled_data_sample() which also uses the op stack. |
549 | | */ |
550 | | /* Check if we are done collecting data. */ |
551 | 97.8k | if (increment_cube_indexes(params, penum->indexes)) { |
552 | 191 | int to_pop; |
553 | 191 | if (stack_depth_adjust == 0) |
554 | 9 | if (ref_stack_count(&o_stack) >= O_STACK_PAD) |
555 | 9 | to_pop = O_STACK_PAD; /* Remove spare stack space */ |
556 | 0 | else |
557 | 0 | to_pop = ref_stack_count(&o_stack); |
558 | 182 | else |
559 | 182 | to_pop = stack_depth_adjust - num_out; |
560 | | |
561 | 191 | if (to_pop < 0) |
562 | 0 | return_error(gs_error_stackunderflow); |
563 | | |
564 | 191 | ref_stack_pop(&o_stack, to_pop); |
565 | | |
566 | | /* Execute the closing procedure, if given */ |
567 | 191 | code = 0; |
568 | 191 | if (esp_finish_proc != 0) |
569 | 191 | code = esp_finish_proc(i_ctx_p); |
570 | | |
571 | 191 | return code; |
572 | 97.6k | } else { |
573 | 97.6k | if (stack_depth_adjust) { |
574 | 93.0k | stack_depth_adjust -= num_out; |
575 | 93.0k | if ((O_STACK_PAD - stack_depth_adjust) < 0) { |
576 | 93.0k | stack_depth_adjust = -(O_STACK_PAD - stack_depth_adjust); |
577 | 93.0k | check_op(stack_depth_adjust); |
578 | 59.6k | ref_stack_pop(&o_stack, stack_depth_adjust); |
579 | 59.6k | } |
580 | 0 | else { |
581 | 0 | check_ostack(O_STACK_PAD - stack_depth_adjust); |
582 | 0 | code = ref_stack_push(&o_stack, O_STACK_PAD - stack_depth_adjust); |
583 | 0 | if (code < 0) |
584 | 0 | return code; |
585 | | |
586 | 0 | for (i=0;i<O_STACK_PAD - stack_depth_adjust;i++) |
587 | 0 | make_null(op - i); |
588 | 0 | } |
589 | 93.0k | } |
590 | 97.6k | } |
591 | | |
592 | | /* Now get the data for the next location */ |
593 | | |
594 | 64.2k | return sampled_data_sample(i_ctx_p); |
595 | 97.8k | } |
596 | | |
597 | | /* |
598 | | * We have collected all of the sample data. Create a type 0 function stucture. |
599 | | */ |
600 | | static int |
601 | | sampled_data_finish(i_ctx_t *i_ctx_p) |
602 | 191 | { |
603 | 191 | os_ptr op = osp; |
604 | 191 | gs_sampled_data_enum *penum = senum; |
605 | | /* Build a type 0 function using the given parameters */ |
606 | 191 | gs_function_Sd_params_t * params = |
607 | 191 | (gs_function_Sd_params_t *)&penum->pfn->params; |
608 | 191 | gs_function_t * pfn; |
609 | 191 | ref cref; /* closure */ |
610 | 191 | int code = gs_function_Sd_init(&pfn, params, imemory); |
611 | | |
612 | 191 | check_op(1); |
613 | 191 | if (code < 0) { |
614 | 0 | esp -= estack_storage; |
615 | 0 | return code; |
616 | 0 | } |
617 | | |
618 | 191 | code = ialloc_ref_array(&cref, a_executable | a_execute, 2, |
619 | 191 | "sampled_data_finish(cref)"); |
620 | 191 | if (code < 0) { |
621 | 0 | esp -= estack_storage; |
622 | 0 | return code; |
623 | 0 | } |
624 | | |
625 | 191 | make_istruct_new(cref.value.refs, a_executable | a_execute, pfn); |
626 | 191 | make_oper_new(cref.value.refs + 1, 0, zexecfunction); |
627 | 191 | ref_assign(op, &cref); |
628 | | |
629 | | /* See bug #707007, explicitly freed structures on the stacks need to be made NULL */ |
630 | 191 | make_null(esp); |
631 | 191 | esp -= estack_storage; |
632 | 191 | ifree_object(penum->pfn, "sampled_data_finish(pfn)"); |
633 | 191 | ifree_object(penum, "sampled_data_finish(enum)"); |
634 | 191 | return o_pop_estack; |
635 | 191 | } |
636 | | |
637 | | int make_sampled_function(i_ctx_t * i_ctx_p, ref *arr, ref *pproc, gs_function_t **func) |
638 | 263 | { |
639 | 263 | int code = 0, *ptr, i, total_size, num_components, CIESubst; |
640 | 263 | byte * bytes = 0; |
641 | 263 | float *fptr; |
642 | 263 | gs_function_t *pfn = *func; |
643 | 263 | gs_function_Sd_params_t params = {0}; |
644 | 263 | ref alternatespace, *palternatespace = &alternatespace; |
645 | 263 | PS_colour_space_t *space, *altspace; |
646 | | |
647 | 263 | code = get_space_object(i_ctx_p, arr, &space); |
648 | 263 | if (code < 0) |
649 | 0 | return code; |
650 | 263 | if (!space->alternateproc) |
651 | 0 | return gs_error_typecheck; |
652 | 263 | code = space->alternateproc(i_ctx_p, arr, &palternatespace, &CIESubst); |
653 | 263 | if (code < 0) |
654 | 0 | return code; |
655 | 263 | code = get_space_object(i_ctx_p, palternatespace, &altspace); |
656 | 263 | if (code < 0) |
657 | 0 | return code; |
658 | | /* |
659 | | * Set up the hyper cube function data structure. |
660 | | */ |
661 | 263 | params.BitsPerSample = 16; |
662 | | |
663 | 263 | code = space->numcomponents(i_ctx_p, arr, &num_components); |
664 | 263 | if (code < 0) |
665 | 0 | return code; |
666 | 263 | fptr = (float *)gs_alloc_byte_array(imemory, num_components * 2, sizeof(float), "make_sampled_function(Domain)"); |
667 | 263 | if (!fptr) |
668 | 0 | return gs_error_VMerror; |
669 | 263 | code = space->domain(i_ctx_p, arr, fptr); |
670 | 263 | if (code < 0) { |
671 | 0 | gs_free_const_object(imemory, fptr, "make_sampled_function(Domain)"); |
672 | 0 | return code; |
673 | 0 | } |
674 | 263 | params.Domain = fptr; |
675 | 263 | params.m = num_components; |
676 | | |
677 | | /* The amount of memory required grows dramatitcally with the number of inputs when |
678 | | * Order is 3 (cubic interpolation). This is the same test as used in determine_sampled_data_size() |
679 | | * below to limit the number of samples in the cube. We use it here to switch to the |
680 | | * cheaper (memory usage) linear interpolation if there are a lot of input |
681 | | * components, in the hope of being able to continue. |
682 | | */ |
683 | 263 | if (params.m <= 8) |
684 | 263 | params.Order = 3; |
685 | 0 | else |
686 | 0 | params.Order = 1; |
687 | | |
688 | 263 | if (params.m > MAX_NUM_INPUTS) |
689 | 0 | return_error(gs_error_rangecheck); |
690 | | |
691 | 263 | code = altspace->numcomponents(i_ctx_p, palternatespace, &num_components); |
692 | 263 | if (code < 0) { |
693 | 0 | gs_free_const_object(imemory, params.Domain, "make_type4_function(Domain)"); |
694 | 0 | return code; |
695 | 0 | } |
696 | 263 | fptr = (float *)gs_alloc_byte_array(imemory, num_components * 2, sizeof(float), "make_sampled_function(Range)"); |
697 | 263 | if (!fptr) { |
698 | 0 | gs_free_const_object(imemory, params.Domain, "make_sampled_function(Domain)"); |
699 | 0 | return gs_error_VMerror; |
700 | 0 | } |
701 | 263 | code = altspace->range(i_ctx_p, palternatespace, fptr); |
702 | 263 | if (code < 0) { |
703 | 0 | gs_free_const_object(imemory, params.Domain, "make_sampled_function(Domain)"); |
704 | 0 | gs_free_const_object(imemory, fptr, "make_sampled_function(Range)"); |
705 | 0 | return code; |
706 | 0 | } |
707 | 263 | params.Range = fptr; |
708 | 263 | params.n = num_components; |
709 | | |
710 | | /* |
711 | | * The Size array may or not be specified. If it is not specified then |
712 | | * we need to determine a set of default values for the Size array. |
713 | | */ |
714 | 263 | ptr = (int *)gs_alloc_byte_array(imemory, params.m, sizeof(int), "Size"); |
715 | 263 | if (ptr == NULL) { |
716 | 0 | code = gs_note_error(gs_error_VMerror); |
717 | 0 | goto fail; |
718 | 0 | } |
719 | 263 | params.Size = ptr; |
720 | | /* |
721 | | * Determine a default |
722 | | * set of values. |
723 | | */ |
724 | 263 | code = determine_sampled_data_size(params.m, params.n, |
725 | 263 | params.BitsPerSample, (int *)params.Size); |
726 | 263 | if (code < 0) |
727 | 0 | goto fail; |
728 | | /* |
729 | | * Determine space required for the sample data storage. |
730 | | */ |
731 | 263 | total_size = params.n * bits2bytes(params.BitsPerSample); |
732 | 526 | for (i = 0; i < params.m; i++) { |
733 | 263 | if (check_int_multiply(params.Size[i], total_size, &total_size) < 0) { |
734 | 0 | code = gs_note_error(gs_error_limitcheck); |
735 | 0 | goto fail; |
736 | 0 | } |
737 | 263 | } |
738 | | /* |
739 | | * Allocate space for the data cube itself. |
740 | | */ |
741 | 263 | bytes = gs_alloc_byte_array(imemory, total_size, 1, "cube_build_func0(bytes)"); |
742 | 263 | if (!bytes) { |
743 | 0 | code = gs_note_error(gs_error_VMerror); |
744 | 0 | goto fail; |
745 | 0 | } |
746 | 263 | data_source_init_bytes(¶ms.DataSource, |
747 | 263 | (const unsigned char *)bytes, total_size); |
748 | | |
749 | | /* |
750 | | * This is temporary. We will call gs_function_Sd_init again after |
751 | | * we have collected the cube data. We are doing it now because we need |
752 | | * a function structure created (along with its GC enumeration stuff) |
753 | | * that we can use while collecting the cube data. We will call |
754 | | * the routine again after the cube data is collected to correctly |
755 | | * initialize the function. |
756 | | */ |
757 | 263 | code = gs_function_Sd_init(&pfn, ¶ms, imemory); |
758 | 263 | if (code < 0) |
759 | 0 | goto fail; |
760 | | /* |
761 | | * Now setup to collect the sample data. |
762 | | */ |
763 | 263 | return sampled_data_setup(i_ctx_p, pfn, pproc, sampled_data_finish, imemory); |
764 | | |
765 | 0 | fail: |
766 | 0 | gs_function_Sd_free_params(¶ms, imemory); |
767 | 0 | return code; |
768 | 263 | } |
769 | | |
770 | | /* ------ Initialization procedure ------ */ |
771 | | |
772 | | const op_def zfsample_op_defs[] = |
773 | | { |
774 | | op_def_begin_level2(), |
775 | | {"1.buildsampledfunction", zbuildsampledfunction}, |
776 | | op_def_end(0) |
777 | | }; |