Coverage Report

Created: 2025-06-24 07:01

/src/ghostpdl/psi/zcrd.c
Line
Count
Source (jump to first uncovered line)
1
/* Copyright (C) 2001-2023 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
/* CIE color rendering operators */
18
#include "math_.h"
19
#include "ghost.h"
20
#include "oper.h"
21
#include "gsstruct.h"
22
#include "gscspace.h"
23
#include "gscolor2.h"
24
#include "gscrd.h"
25
#include "gscrdp.h"
26
#include "estack.h"
27
#include "ialloc.h"
28
#include "idict.h"
29
#include "idparam.h"
30
#include "igstate.h"
31
#include "icie.h"
32
#include "iparam.h"
33
#include "ivmspace.h"
34
#include "store.h"    /* for make_null */
35
36
/* Forward references */
37
static int zcrd1_proc_params(const gs_memory_t *mem, os_ptr op, ref_cie_render_procs * pcprocs);
38
static int zcrd1_params(os_ptr op, gs_cie_render * pcrd,
39
                         ref_cie_render_procs * pcprocs, gs_memory_t * mem);
40
/* - currentcolorrendering <dict> */
41
static int
42
zcurrentcolorrendering(i_ctx_t *i_ctx_p)
43
162k
{
44
162k
    os_ptr op = osp;
45
46
162k
    push(1);
47
162k
    *op = istate->colorrendering.dict;
48
162k
    return 0;
49
162k
}
50
51
/* <dict> .buildcolorrendering1 <crd> */
52
static int
53
zbuildcolorrendering1(i_ctx_t *i_ctx_p)
54
162k
{
55
162k
    os_ptr op = osp;
56
162k
    gs_memory_t *mem = gs_gstate_memory(igs);
57
162k
    int code;
58
162k
    es_ptr ep = esp;
59
162k
    gs_cie_render *pcrd;
60
162k
    ref_cie_render_procs procs;
61
62
162k
    check_op(1);
63
162k
    check_read_type(*op, t_dictionary);
64
162k
    check_dict_read(*op);
65
162k
    code = gs_cie_render1_build(&pcrd, mem, ".buildcolorrendering1");
66
162k
    if (code < 0)
67
0
        return code;
68
162k
    code = zcrd1_params(op, pcrd, &procs, mem);
69
162k
    if (code < 0 ) {
70
0
        rc_free_struct(pcrd, ".buildcolorrendering1");
71
0
        esp = ep;
72
0
        return code;
73
0
    }
74
    /****** FIX refct ******/
75
    /*rc_decrement(pcrd, ".buildcolorrendering1"); *//* build sets rc = 1 */
76
162k
    istate->colorrendering.dict = *op;
77
162k
    make_istruct_new(op, a_readonly, pcrd);
78
162k
    return (esp == ep ? 0 : o_push_estack);
79
162k
}
80
81
/* <dict> .builddevicecolorrendering1 <crd> */
82
static int
83
zbuilddevicecolorrendering1(i_ctx_t *i_ctx_p)
84
0
{
85
0
    os_ptr op = osp;
86
0
    gs_memory_t *mem = gs_gstate_memory(igs);
87
0
    dict_param_list list;
88
0
    gs_cie_render *pcrd = 0;
89
0
    int code;
90
91
0
    check_op(1);
92
0
    check_type(*op, t_dictionary);
93
0
    code = dict_param_list_read(&list, op, NULL, false, iimemory);
94
0
    if (code < 0)
95
0
        return code;
96
0
    code = gs_cie_render1_build(&pcrd, mem, ".builddevicecolorrendering1");
97
0
    if (code >= 0) {
98
0
        code = param_get_cie_render1(pcrd, (gs_param_list *) & list,
99
0
                                     gs_currentdevice(igs));
100
0
        if (code >= 0) {
101
            /****** FIX refct ******/
102
            /*rc_decrement(pcrd, ".builddevicecolorrendering1"); *//* build sets rc = 1 */
103
0
        }
104
0
    }
105
0
    iparam_list_release(&list);
106
0
    if (code < 0) {
107
0
        rc_free_struct(pcrd, ".builddevicecolorrendering1");
108
0
        return code;
109
0
    }
110
0
    istate->colorrendering.dict = *op;
111
0
    make_istruct_new(op, a_readonly, pcrd);
112
0
    return 0;
113
0
}
114
115
/* <dict> <crd> .setcolorrendering1 - */
116
static int
117
zsetcolorrendering1(i_ctx_t *i_ctx_p)
118
162k
{
119
162k
    os_ptr op = osp;
120
162k
    es_ptr ep = esp;
121
162k
    ref_cie_render_procs procs;
122
162k
    int code;
123
124
162k
    check_op(2);
125
162k
    check_type(op[-1], t_dictionary);
126
162k
    check_stype(*op, st_cie_render1);
127
162k
    code = zcrd1_proc_params(imemory, op - 1, &procs);
128
162k
    if (code < 0)
129
0
        return code;
130
162k
    code = gs_setcolorrendering(igs, r_ptr(op, gs_cie_render));
131
162k
    if (code < 0)
132
0
        return code;
133
162k
    if (gs_cie_cs_common(igs) != 0 &&
134
162k
        (code = cie_cache_joint(i_ctx_p, &procs, gs_cie_cs_common(igs), igs)) < 0
135
162k
        )
136
0
        return code;
137
162k
    istate->colorrendering.dict = op[-1];
138
162k
    istate->colorrendering.procs = procs;
139
162k
    pop(2);
140
162k
    return (esp == ep ? 0 : o_push_estack);
141
162k
}
142
143
/* <dict> <crd> .setdevicecolorrendering1 - */
144
static int
145
zsetdevicecolorrendering1(i_ctx_t *i_ctx_p)
146
0
{
147
0
    os_ptr op = osp;
148
0
    int code;
149
0
    ref_cie_render_procs procs;
150
151
0
    check_op(2);
152
0
    check_type(op[-1], t_dictionary);
153
0
    check_stype(*op, st_cie_render1);
154
0
    code = gs_setcolorrendering(igs, r_ptr(op, gs_cie_render));
155
0
    if (code < 0)
156
0
        return code;
157
0
    refset_null((ref *)&procs, sizeof(procs) / sizeof(ref));
158
0
    if (gs_cie_cs_common(igs) != 0 &&
159
0
        (code = cie_cache_joint(i_ctx_p, &procs, gs_cie_cs_common(igs), igs)) < 0
160
0
        )
161
0
        return code;
162
0
    istate->colorrendering.dict = op[-1];
163
0
    refset_null((ref *)&istate->colorrendering.procs,
164
0
                sizeof(istate->colorrendering.procs) / sizeof(ref));
165
0
    pop(2);
166
0
    return 0;
167
0
}
168
169
/* Get ColorRenderingType 1 procedures from the PostScript dictionary. */
170
static int
171
zcrd1_proc_params(const gs_memory_t *mem,
172
                  os_ptr op, ref_cie_render_procs * pcprocs)
173
324k
{
174
324k
    int code;
175
324k
    ref *pRT;
176
177
324k
    code = dict_proc3_param(mem, op, "EncodeLMN", &pcprocs->EncodeLMN);
178
324k
    if (code < 0)
179
0
        return code;
180
324k
    code = dict_proc3_param(mem, op, "EncodeABC", &pcprocs->EncodeABC);
181
324k
    if (code < 0)
182
0
        return code;
183
324k
    code = dict_proc3_param(mem, op, "TransformPQR", &pcprocs->TransformPQR);
184
324k
    if (code < 0)
185
0
        return code;
186
324k
    if (code == 1)
187
0
        return gs_note_error(gs_error_undefined);
188
324k
    if (dict_find_string(op, "RenderTable", &pRT) > 0) {
189
0
        const ref *prte;
190
0
        int size;
191
0
        int i;
192
193
0
        check_read_type(*pRT, t_array);
194
0
        size = r_size(pRT);
195
0
        if (size < 5)
196
0
            return_error(gs_error_rangecheck);
197
0
        prte = pRT->value.const_refs;
198
0
        for (i = 5; i < size; i++)
199
0
            check_proc_only(prte[i]);
200
0
        make_const_array(&pcprocs->RenderTableT, a_readonly | r_space(pRT),
201
0
                         size - 5, prte + 5);
202
0
    } else
203
324k
        make_null(&pcprocs->RenderTableT);
204
324k
    return 0;
205
324k
}
206
207
/* Get ColorRenderingType 1 parameters from the PostScript dictionary. */
208
static int
209
zcrd1_params(os_ptr op, gs_cie_render * pcrd,
210
             ref_cie_render_procs * pcprocs, gs_memory_t * mem)
211
162k
{
212
162k
    int code;
213
162k
    int ignore;
214
162k
    gx_color_lookup_table *const prtl = &pcrd->RenderTable.lookup;
215
162k
    ref *pRT;
216
217
162k
    if ((code = dict_int_param(op, "ColorRenderingType", 1, 1, 0, &ignore)) < 0)
218
0
        return code;
219
162k
    if ((code = zcrd1_proc_params(mem, op, pcprocs)) < 0)
220
0
        return code;
221
222
162k
    if ((code = dict_matrix3_param(mem, op, "MatrixLMN", &pcrd->MatrixLMN)) < 0)
223
0
        return code;
224
162k
    if ((code = dict_range3_param(mem, op, "RangeLMN", &pcrd->RangeLMN)) < 0)
225
0
        return code;
226
162k
    if ((code = dict_matrix3_param(mem, op, "MatrixABC", &pcrd->MatrixABC)) < 0)
227
0
        return code;
228
162k
    if ((code = dict_range3_param(mem, op, "RangeABC", &pcrd->RangeABC)) < 0)
229
0
        return code;
230
162k
    if ((code = cie_points_param(mem, op, &pcrd->points)) < 0)
231
0
        return code;
232
162k
    if ((code = dict_matrix3_param(mem, op, "MatrixPQR", &pcrd->MatrixPQR)) < 0)
233
0
        return code;
234
162k
    if ((code = dict_range3_param(mem,op, "RangePQR", &pcrd->RangePQR)) < 0)
235
0
        return code;
236
237
162k
    if (dict_find_string(op, "RenderTable", &pRT) > 0) {
238
0
        const ref *prte;
239
240
0
        check_read_type(*pRT, t_array);
241
0
        prte = pRT->value.const_refs;
242
        /* Finish unpacking and checking the RenderTable parameter. */
243
0
        check_type_only(prte[4], t_integer);
244
0
        if (!(prte[4].value.intval == 3 || prte[4].value.intval == 4))
245
0
            return_error(gs_error_rangecheck);
246
0
        prtl->n = 3;
247
0
        prtl->m = prte[4].value.intval;
248
0
        if (r_size(pRT) != prtl->m + 5)
249
0
            return_error(gs_error_rangecheck);
250
0
        code = cie_table_param(pRT, prtl, mem);
251
0
        if (code < 0)
252
0
            return code;
253
162k
    } else {
254
162k
        prtl->table = 0;
255
162k
    }
256
162k
    pcrd->EncodeLMN = Encode_default;
257
162k
    pcrd->EncodeABC = Encode_default;
258
162k
    pcrd->TransformPQR = TransformPQR_default;
259
162k
    pcrd->RenderTable.T = RenderTableT_default;
260
162k
    return 0;
261
162k
}
262
263
/* ------ Internal procedures ------ */
264
265
/* Load the joint caches. */
266
static int
267
    cie_exec_tpqr(i_ctx_t *),
268
    cie_post_exec_tpqr(i_ctx_t *),
269
    cie_tpqr_finish(i_ctx_t *);
270
int
271
cie_cache_joint(i_ctx_t *i_ctx_p, const ref_cie_render_procs * pcrprocs,
272
                const gs_cie_common *pcie, gs_gstate * pgs)
273
0
{
274
0
    const gs_cie_render *pcrd = gs_currentcolorrendering(pgs);
275
0
    gx_cie_joint_caches *pjc = gx_unshare_cie_caches(pgs);
276
0
    gs_ref_memory_t *imem = (gs_ref_memory_t *) gs_gstate_memory(pgs);
277
0
    ref pqr_procs;
278
0
    uint space;
279
0
    int code;
280
0
    int i;
281
282
0
    if (pcrd == 0)   /* cache is not set up yet */
283
0
        return 0;
284
0
    if (pjc == 0)   /* must already be allocated */
285
0
        return_error(gs_error_VMerror);
286
0
    if (r_has_type(&pcrprocs->TransformPQR, t_null)) {
287
        /*
288
         * This CRD came from a driver, not from a PostScript dictionary.
289
         * Resample TransformPQR in C code.
290
         */
291
0
        return gs_cie_cs_complete(pgs, true);
292
0
    }
293
0
    gs_cie_compute_points_sd(pjc, pcie, pcrd);
294
    /* We use global memory for this array because it's going to be pushed onto
295
       the exec stack, if it triggers an error and a restore happens, we don't want
296
       a dangling stack reference to a restored away object.
297
     */
298
0
    code = gs_alloc_ref_array(iimemory_global, &pqr_procs, a_readonly, 3 * (1 + 4 + 4 * 6),
299
0
                            "cie_cache_common");
300
0
    if (code < 0)
301
0
        return code;
302
    /* When we're done, deallocate the procs and complete the caches. */
303
0
    check_estack(3);
304
0
    code = cie_cache_push_finish(i_ctx_p, cie_tpqr_finish, imem, pgs);
305
0
    if (code < 0)
306
0
        return code;
307
308
0
    *++esp = pqr_procs;
309
0
    space = r_space(&pqr_procs);
310
0
    for (i = 0; i < 3; i++) {
311
0
        ref *p = pqr_procs.value.refs + 3 + (4 + 4 * 6) * i;
312
0
        const float *ppt = (float *)&pjc->points_sd;
313
0
        int j;
314
315
0
        make_array(pqr_procs.value.refs + i, a_readonly | a_executable | space,
316
0
                   4, p);
317
0
        make_array(p, a_readonly | space, 4 * 6, p + 4);
318
0
        p[1] = pcrprocs->TransformPQR.value.refs[i];
319
0
        make_oper(p + 2, 0, cie_exec_tpqr);
320
0
        make_oper(p + 3, 0, cie_post_exec_tpqr);
321
0
        for (j = 0, p += 4; j < 4 * 6; j++, p++, ppt++)
322
0
            make_real(p, *ppt);
323
0
    }
324
0
    return cie_prepare_cache3(i_ctx_p, &pcrd->RangePQR,
325
0
                              pqr_procs.value.const_refs,
326
0
                              pjc->TransformPQR.caches,
327
0
                              pjc, imem, "Transform.PQR");
328
0
}
329
330
/* Private operator to shuffle arguments for the TransformPQR procedure: */
331
/* v [ws wd bs bd] proc -> -mark- ws wd bs bd v proc + exec */
332
static int
333
cie_exec_tpqr(i_ctx_t *i_ctx_p)
334
0
{
335
0
    os_ptr op = osp;
336
0
    const ref *ppt = op[-1].value.const_refs;
337
0
    uint space = r_space(op - 1);
338
0
    int i;
339
340
0
    check_op(3);
341
0
    push(4);
342
0
    *op = op[-4];   /* proc */
343
0
    op[-1] = op[-6];    /* v */
344
0
    for (i = 0; i < 4; i++)
345
0
        make_const_array(op - 5 + i, a_readonly | space,
346
0
                         6, ppt + i * 6);
347
0
    make_mark(op - 6);
348
0
    return zexec(i_ctx_p);
349
0
}
350
351
/* Remove extraneous values from the stack after executing */
352
/* the TransformPQR procedure.  -mark- ... v -> v */
353
static int
354
cie_post_exec_tpqr(i_ctx_t *i_ctx_p)
355
0
{
356
0
    os_ptr op = osp;
357
0
    uint count = ref_stack_counttomark(&o_stack);
358
0
    ref vref;
359
360
0
    if (count < 2)
361
0
        return_error(gs_error_unmatchedmark);
362
0
    vref = *op;
363
0
    ref_stack_pop(&o_stack, count - 1);
364
0
    *osp = vref;
365
0
    return 0;
366
0
}
367
368
/* Free the procs array and complete the joint caches. */
369
static int
370
cie_tpqr_finish(i_ctx_t *i_ctx_p)
371
0
{
372
0
    os_ptr op = osp;
373
0
    gs_gstate *pgs = r_ptr(op, gs_gstate);
374
0
    gs_cie_render *pcrd =
375
0
        (gs_cie_render *)gs_currentcolorrendering(pgs);  /* break const */
376
0
    int code;
377
378
0
    ifree_ref_array(op - 1, "cie_tpqr_finish");
379
0
    pcrd->TransformPQR = TransformPQR_from_cache;
380
0
    code = gs_cie_cs_complete(pgs, false);
381
0
    pop(2);
382
0
    return code;
383
0
}
384
385
/* Ws Bs Wd Bd Ps .transformPQR_scale_wb[012] Pd
386
387
   The default TransformPQR procedure is implemented in C, rather than
388
   PostScript, as a speed optimization.
389
390
   This TransformPQR implements a relative colorimetric intent by scaling
391
   the XYZ values relative to the white and black points.
392
*/
393
static int
394
ztpqr_scale_wb_common(i_ctx_t *i_ctx_p, int idx)
395
0
{
396
0
    os_ptr op = osp;
397
0
    double a[4], Ps; /* a[0] = ws, a[1] = bs, a[2] = wd, a[3] = bd */
398
0
    double result;
399
0
    int code;
400
0
    int i;
401
402
0
    check_op(4);
403
0
    code = real_param(op, &Ps);
404
0
    if (code < 0) return code;
405
406
0
    for (i = 0; i < 4; i++) {
407
0
        ref tmp;
408
409
0
        code = array_get(imemory, op - 4 + i, idx, &tmp);
410
0
        if (code >= 0)
411
0
            code = real_param(&tmp, &a[i]);
412
0
        if (code < 0) return code;
413
0
    }
414
415
0
    if (a[0] == a[1])
416
0
        return_error(gs_error_undefinedresult);
417
0
    result = a[3] + (a[2] - a[3]) * (Ps - a[1]) / (a[0] - a[1]);
418
0
    make_real(op - 4, result);
419
0
    pop(4);
420
0
    return 0;
421
0
}
422
423
/* Ws Bs Wd Bd Ps .TransformPQR_scale_wb0 Pd */
424
static int
425
ztpqr_scale_wb0(i_ctx_t *i_ctx_p)
426
0
{
427
0
    return ztpqr_scale_wb_common(i_ctx_p, 3);
428
0
}
429
430
/* Ws Bs Wd Bd Ps .TransformPQR_scale_wb2 Pd */
431
static int
432
ztpqr_scale_wb1(i_ctx_t *i_ctx_p)
433
0
{
434
0
    return ztpqr_scale_wb_common(i_ctx_p, 4);
435
0
}
436
437
/* Ws Bs Wd Bd Ps .TransformPQR_scale_wb2 Pd */
438
static int
439
ztpqr_scale_wb2(i_ctx_t *i_ctx_p)
440
0
{
441
0
    return ztpqr_scale_wb_common(i_ctx_p, 5);
442
0
}
443
444
/* ------ Initialization procedure ------ */
445
446
const op_def zcrd_l2_op_defs[] =
447
{
448
    op_def_begin_level2(),
449
    {"0currentcolorrendering", zcurrentcolorrendering},
450
    {"2.setcolorrendering1", zsetcolorrendering1},
451
    {"2.setdevicecolorrendering1", zsetdevicecolorrendering1},
452
    {"1.buildcolorrendering1", zbuildcolorrendering1},
453
    {"1.builddevicecolorrendering1", zbuilddevicecolorrendering1},
454
                /* Internal "operators" */
455
    {"3%cie_exec_tpqr", cie_exec_tpqr},
456
    {"2%cie_post_exec_tpqr", cie_post_exec_tpqr},
457
    {"1%cie_tpqr_finish", cie_tpqr_finish},
458
    {"5.TransformPQR_scale_WB0", ztpqr_scale_wb0},
459
    {"5.TransformPQR_scale_WB1", ztpqr_scale_wb1},
460
    {"5.TransformPQR_scale_WB2", ztpqr_scale_wb2},
461
    op_def_end(0)
462
};