Coverage Report

Created: 2025-08-28 07:06

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