Coverage Report

Created: 2026-10-28 08:05

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
292
#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
292
#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, &params, 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, &params, 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
218k
#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
292
{
147
292
    int i, total_size = num_outputs * sample_size;
148
149
584
    for (i = 0; i < num_inputs; i++) {
150
292
        if (Size[i] <= 0 || Size[i] > MAX_DATA_SIZE / total_size)
151
0
            return false;
152
292
        total_size *= Size[i];
153
292
    }
154
292
    return true;
155
292
}
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
292
{
175
292
    static const int size_list[] = {512, 50, 20, 10, 7, 5, 4, 3};
176
292
    int i, size;
177
178
    /* Start with initial guess at cube size */
179
292
    if (num_inputs > 0 && num_inputs <= 8)
180
292
        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
292
    while (true) {
188
        /* Fill Size array with value. */
189
584
        for (i = 0; i < num_inputs; i++)
190
292
            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
292
        if (size > 2) {
196
292
            if (valid_cube_size(num_inputs, num_outputs, sample_size, Size))
197
292
                return 0;    /* We have a valid size */
198
0
            size--;
199
0
        } else {
200
0
            return 0;
201
0
        }
202
292
    }
203
292
}
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
292
{
212
292
    return gs_alloc_struct(mem, gs_sampled_data_enum,
213
292
                                &st_gs_sampled_data_enum, cname);
214
292
}
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
109k
{
224
109k
    int i, sum = indexes[params->m - 1];
225
226
109k
    for (i = params->m - 2; i >= 0; i--) {
227
0
        sum *= params->Size[i];
228
0
        sum += indexes[i];
229
0
    }
230
109k
    return (byte *)(params->DataSource.data.str.data) +
231
109k
        sum * params->n * bits2bytes(params->BitsPerSample);
232
109k
}
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
109k
{
242
109k
    int i = 0;
243
244
109k
    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
109k
        indexes[i]++;
250
109k
        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
108k
            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
213
        indexes[i] = 0;
261
213
        i++;
262
213
        if (i == params->m)
263
            /*
264
             * We have finished the last edge of the hyper cube.
265
             * We are done.
266
             */
267
213
            return true;
268
213
    }
269
109k
}
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, &params->Order)) < 0 ||
286
0
        (code = dict_int_param(pdict, "BitsPerSample", 1, 32, 0,
287
0
                               &params->BitsPerSample)) < 0 ||
288
0
        ((code = params->m =
289
0
            fn_build_float_array(pdict, "Domain", false, true,
290
0
                                        &params->Domain, mem)) < 0 ) ||
291
0
        ((code = params->n =
292
0
            fn_build_float_array(pdict, "Range", false, true,
293
0
                                        &params->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(&params->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
516
#define estack_storage 3
377
426
#define esp_finish_proc (*real_opproc(esp - 2))
378
71.9k
#define sample_proc esp[-1]
379
181k
#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
316k
#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
292
{
399
292
    os_ptr op = osp;
400
292
    gs_sampled_data_enum *penum;
401
292
    int i;
402
292
    gs_function_Sd_params_t * params = (gs_function_Sd_params_t *)&pfn->params;
403
404
292
    check_estack(estack_storage + 1);   /* Verify space on estack */
405
292
    check_ostack(params->m + O_STACK_PAD); /* and the operand stack */
406
292
    check_ostack(params->n + O_STACK_PAD);
407
408
    /*
409
     * Allocate space for the enumerator data structure.
410
     */
411
292
    penum = gs_sampled_data_enum_alloc(imemory, "zbuildsampledfuntion(params)");
412
292
    if (penum == NULL)
413
0
        return_error(gs_error_VMerror);
414
415
    /* Initialize data in the enumeration structure */
416
417
292
    penum->pfn = pfn;
418
584
    for(i=0; i< params->m; i++)
419
292
        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
292
    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
292
    push(O_STACK_PAD);
433
1.16k
    for (i = 0; i < O_STACK_PAD; i++)    /* Set space = null */
434
876
        make_null(op - i);
435
436
    /* Push everything on the estack */
437
438
292
    esp += estack_storage;
439
292
    make_op_estack(esp - 2, finish_proc); /* Finish proc onto estack */
440
292
    sample_proc = *pproc;      /* Save function to be sampled */
441
292
    make_istruct(esp, 0, penum);    /* Color cube enumeration structure */
442
292
    push_op_estack(sampled_data_sample);  /* Start sampling data */
443
292
    return o_push_estack;
444
292
}
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
71.6k
{
452
71.6k
    os_ptr op = osp;
453
71.6k
    gs_sampled_data_enum *penum = senum;
454
71.6k
    ref proc;
455
71.6k
    gs_function_Sd_params_t * params =
456
71.6k
                        (gs_function_Sd_params_t *)&penum->pfn->params;
457
71.6k
    int num_inputs = params->m;
458
71.6k
    int i;
459
460
    /* Put set of input values onto the stack. */
461
71.6k
    push(num_inputs);
462
143k
    for (i = 0; i < num_inputs; i++) {
463
71.6k
        double dmin = params->Domain[2 * i];
464
71.6k
        double dmax = params->Domain[2 * i + 1];
465
466
71.6k
        make_real(op - num_inputs + i + 1, (float) (
467
71.6k
            penum->indexes[i] * (dmax - dmin)/(params->Size[i] - 1) + dmin));
468
71.6k
    }
469
470
71.6k
    proc = sample_proc;         /* Get procedure from storage */
471
71.6k
    push_op_estack(sampled_data_continue);  /* Put 'save' routine on estack, after sample proc */
472
71.6k
    *++esp = proc;          /* Put procedure to be executed */
473
71.6k
    return o_push_estack;
474
71.6k
}
475
476
/*
477
 * Continuation procedure for processing sampled values.
478
 */
479
static int
480
sampled_data_continue(i_ctx_t *i_ctx_p)
481
109k
{
482
109k
    os_ptr op = osp;
483
109k
    gs_sampled_data_enum *penum = senum;
484
109k
    gs_function_Sd_params_t * params =
485
109k
            (gs_function_Sd_params_t *)&penum->pfn->params;
486
109k
    int i, j, num_out = params->n;
487
109k
    int code = 0;
488
109k
    byte * data_ptr;
489
109k
    double sampled_data_value_max = (double)((1 << params->BitsPerSample) - 1);
490
109k
    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
109k
    if (num_out + O_STACK_PAD + penum->o_stack_depth != ref_stack_count(&o_stack)) {
503
103k
        stack_depth_adjust = ref_stack_count(&o_stack) - penum->o_stack_depth;
504
505
103k
        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
24
            push(-stack_depth_adjust);
515
24
            return_error(gs_error_undefinedresult);
516
24
        }
517
103k
    }
518
109k
    if ( op < osbot + ((num_out) - 1) ) {
519
2
        return_error(gs_error_stackunderflow);
520
2
    }
521
    /* Save data from the given function */
522
109k
    data_ptr = cube_ptr_from_index(params, penum->indexes);
523
545k
    for (i=0; i < num_out; i++) {
524
436k
        ulong cv;
525
436k
        double value;
526
436k
        double rmin = params->Range[2 * i];
527
436k
        double rmax = params->Range[2 * i + 1];
528
529
436k
        code = real_param(op + i - num_out + 1, &value);
530
436k
        if (code < 0) {
531
11
            esp -= estack_storage;
532
11
            return code;
533
11
        }
534
436k
        if (value < rmin)
535
0
            value = rmin;
536
436k
        else if (value > rmax)
537
4.19k
            value = rmax;
538
436k
        value = (value - rmin) / (rmax - rmin);   /* Convert to 0 to 1.0 */
539
436k
        cv = (int) (value * sampled_data_value_max + 0.5);
540
1.30M
        for (j = 0; j < bps; j++)
541
872k
            data_ptr[bps * i + j] = (byte)(cv >> ((bps - 1 - j) * 8)); /* MSB first */
542
436k
    }
543
544
109k
    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
109k
    if (increment_cube_indexes(params, penum->indexes)) {
552
213
        int to_pop;
553
213
        if (stack_depth_adjust == 0)
554
11
            if (ref_stack_count(&o_stack) >= O_STACK_PAD)
555
11
                to_pop = O_STACK_PAD;      /* Remove spare stack space */
556
0
            else
557
0
                to_pop = ref_stack_count(&o_stack);
558
202
        else
559
202
            to_pop = stack_depth_adjust - num_out;
560
561
213
        if (to_pop < 0)
562
0
            return_error(gs_error_stackunderflow);
563
564
213
        ref_stack_pop(&o_stack, to_pop);
565
566
        /* Execute the closing procedure, if given */
567
213
        code = 0;
568
213
        if (esp_finish_proc != 0)
569
213
            code = esp_finish_proc(i_ctx_p);
570
571
213
        return code;
572
108k
    } else {
573
108k
        if (stack_depth_adjust) {
574
103k
            stack_depth_adjust -= num_out;
575
103k
            if ((O_STACK_PAD - stack_depth_adjust) < 0) {
576
103k
                stack_depth_adjust = -(O_STACK_PAD - stack_depth_adjust);
577
103k
                check_op(stack_depth_adjust);
578
65.7k
                ref_stack_pop(&o_stack, stack_depth_adjust);
579
65.7k
            }
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
103k
        }
590
108k
    }
591
592
    /* Now get the data for the next location */
593
594
71.3k
    return sampled_data_sample(i_ctx_p);
595
109k
}
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
213
{
603
213
    os_ptr op = osp;
604
213
    gs_sampled_data_enum *penum = senum;
605
    /* Build a type 0 function using the given parameters */
606
213
    gs_function_Sd_params_t * params =
607
213
        (gs_function_Sd_params_t *)&penum->pfn->params;
608
213
    gs_function_t * pfn;
609
213
    ref cref;     /* closure */
610
213
    int code = gs_function_Sd_init(&pfn, params, imemory);
611
612
213
    check_op(1);
613
213
    if (code < 0) {
614
0
        esp -= estack_storage;
615
0
        return code;
616
0
    }
617
618
213
    code = ialloc_ref_array(&cref, a_executable | a_execute, 2,
619
213
                            "sampled_data_finish(cref)");
620
213
    if (code < 0) {
621
0
        esp -= estack_storage;
622
0
        return code;
623
0
    }
624
625
213
    make_istruct_new(cref.value.refs, a_executable | a_execute, pfn);
626
213
    make_oper_new(cref.value.refs + 1, 0, zexecfunction);
627
213
    ref_assign(op, &cref);
628
629
    /* See bug #707007, explicitly freed structures on the stacks need to be made NULL */
630
213
    make_null(esp);
631
213
    esp -= estack_storage;
632
213
    ifree_object(penum->pfn, "sampled_data_finish(pfn)");
633
213
    ifree_object(penum, "sampled_data_finish(enum)");
634
213
    return o_pop_estack;
635
213
}
636
637
int make_sampled_function(i_ctx_t * i_ctx_p, ref *arr, ref *pproc, gs_function_t **func)
638
292
{
639
292
    int code = 0, *ptr, i, total_size, num_components, CIESubst;
640
292
    byte * bytes = 0;
641
292
    float *fptr;
642
292
    gs_function_t *pfn = *func;
643
292
    gs_function_Sd_params_t params = {0};
644
292
    ref alternatespace, *palternatespace = &alternatespace;
645
292
    PS_colour_space_t *space, *altspace;
646
647
292
    code = get_space_object(i_ctx_p, arr, &space);
648
292
    if (code < 0)
649
0
        return code;
650
292
    if (!space->alternateproc)
651
0
        return gs_error_typecheck;
652
292
    code = space->alternateproc(i_ctx_p, arr, &palternatespace, &CIESubst);
653
292
    if (code < 0)
654
0
        return code;
655
292
    code = get_space_object(i_ctx_p, palternatespace, &altspace);
656
292
    if (code < 0)
657
0
        return code;
658
    /*
659
     * Set up the hyper cube function data structure.
660
     */
661
292
    params.BitsPerSample = 16;
662
663
292
    code = space->numcomponents(i_ctx_p, arr, &num_components);
664
292
    if (code < 0)
665
0
        return code;
666
292
    fptr = (float *)gs_alloc_byte_array(imemory, num_components * 2, sizeof(float), "make_sampled_function(Domain)");
667
292
    if (!fptr)
668
0
        return gs_error_VMerror;
669
292
    code = space->domain(i_ctx_p, arr, fptr);
670
292
    if (code < 0) {
671
0
        gs_free_const_object(imemory, fptr, "make_sampled_function(Domain)");
672
0
        return code;
673
0
    }
674
292
    params.Domain = fptr;
675
292
    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
292
    if (params.m <= 8)
684
292
        params.Order = 3;
685
0
    else
686
0
        params.Order = 1;
687
688
292
    if (params.m > MAX_NUM_INPUTS)
689
0
        return_error(gs_error_rangecheck);
690
691
292
    code = altspace->numcomponents(i_ctx_p, palternatespace, &num_components);
692
292
    if (code < 0) {
693
0
        gs_free_const_object(imemory, params.Domain, "make_type4_function(Domain)");
694
0
        return code;
695
0
    }
696
292
    fptr = (float *)gs_alloc_byte_array(imemory, num_components * 2, sizeof(float), "make_sampled_function(Range)");
697
292
    if (!fptr) {
698
0
        gs_free_const_object(imemory, params.Domain, "make_sampled_function(Domain)");
699
0
        return gs_error_VMerror;
700
0
    }
701
292
    code = altspace->range(i_ctx_p, palternatespace, fptr);
702
292
    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
292
    params.Range = fptr;
708
292
    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
292
    ptr = (int *)gs_alloc_byte_array(imemory, params.m, sizeof(int), "Size");
715
292
    if (ptr == NULL) {
716
0
        code = gs_note_error(gs_error_VMerror);
717
0
        goto fail;
718
0
    }
719
292
    params.Size = ptr;
720
    /*
721
     * Determine a default
722
     * set of values.
723
     */
724
292
    code = determine_sampled_data_size(params.m, params.n,
725
292
                        params.BitsPerSample, (int *)params.Size);
726
292
    if (code < 0)
727
0
        goto fail;
728
    /*
729
     * Determine space required for the sample data storage.
730
     */
731
292
    total_size = params.n * bits2bytes(params.BitsPerSample);
732
584
    for (i = 0; i < params.m; i++) {
733
292
        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
292
    }
738
    /*
739
     * Allocate space for the data cube itself.
740
     */
741
292
    bytes = gs_alloc_byte_array(imemory, total_size, 1, "cube_build_func0(bytes)");
742
292
    if (!bytes) {
743
0
        code = gs_note_error(gs_error_VMerror);
744
0
        goto fail;
745
0
    }
746
292
    data_source_init_bytes(&params.DataSource,
747
292
                                (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
292
    code = gs_function_Sd_init(&pfn, &params, imemory);
758
292
    if (code < 0)
759
0
        goto fail;
760
    /*
761
     * Now setup to collect the sample data.
762
     */
763
292
    return sampled_data_setup(i_ctx_p, pfn, pproc, sampled_data_finish, imemory);
764
765
0
fail:
766
0
    gs_function_Sd_free_params(&params, imemory);
767
0
    return code;
768
292
}
769
770
/* ------ Initialization procedure ------ */
771
772
const op_def zfsample_op_defs[] =
773
{
774
    op_def_begin_level2(),
775
    {"1.buildsampledfunction", zbuildsampledfunction},
776
                /* Internal operators */
777
    {"0%sampled_data_continue", sampled_data_continue},
778
    op_def_end(0)
779
};