Coverage Report

Created: 2026-09-14 07:34

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
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, &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
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, &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
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(&params.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, &params, 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(&params, 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
};