Coverage Report

Created: 2026-09-14 07:34

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ghostpdl/base/gsshade.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
/* Constructors for shadings */
18
#include "gx.h"
19
#include "gscspace.h"
20
#include "gserrors.h"
21
#include "gsstruct.h"
22
#include "gsptype2.h"
23
#include "gxdevcli.h"
24
#include "gxcpath.h"
25
#include "gxcspace.h"
26
#include "gxdcolor.h"   /* for filling background rectangle */
27
#include "gxgstate.h"
28
#include "gxpaint.h"
29
#include "gxpath.h"
30
#include "gxshade.h"
31
#include "gxshade4.h"
32
#include "gzpath.h"
33
#include "gzcpath.h"
34
#include "gsfunc3.h"
35
36
/* ================ Initialize shadings ================ */
37
38
/* ---------------- Generic services ---------------- */
39
40
/* GC descriptors */
41
private_st_shading();
42
43
static
44
0
ENUM_PTRS_WITH(shading_mesh_enum_ptrs, gs_shading_mesh_t *psm)
45
0
{
46
0
    index -= 2;
47
0
    if (index < st_data_source_max_ptrs)
48
0
        return ENUM_USING(st_data_source, &psm->params.DataSource,
49
0
                          sizeof(psm->params.DataSource), index);
50
0
    return ENUM_USING_PREFIX(st_shading, st_data_source_max_ptrs);
51
0
}
52
0
ENUM_PTR2(0, gs_shading_mesh_t, params.Function, params.Decode);
53
0
ENUM_PTRS_END
54
55
static
56
0
RELOC_PTRS_WITH(shading_mesh_reloc_ptrs, gs_shading_mesh_t *psm)
57
0
{
58
0
    RELOC_PREFIX(st_shading);
59
0
    RELOC_USING(st_data_source, &psm->params.DataSource,
60
0
                sizeof(psm->params.DataSource));
61
0
    RELOC_PTR2(gs_shading_mesh_t, params.Function, params.Decode);
62
0
}
63
0
RELOC_PTRS_END
64
65
/* Check ColorSpace, BBox, and Function (if present). */
66
/* Free variables: params. */
67
static int
68
check_CBFD(const gs_shading_params_t * params,
69
           const gs_function_t * function, const float *domain, int m)
70
18.2k
{
71
18.2k
    int ncomp = gs_color_space_num_components(params->ColorSpace);
72
73
18.2k
    if (ncomp < 0 ||
74
18.2k
        (params->have_BBox &&
75
44
         (params->BBox.p.x > params->BBox.q.x ||
76
44
          params->BBox.p.y > params->BBox.q.y))
77
18.2k
        )
78
0
        return_error(gs_error_rangecheck);
79
18.2k
    if (function != 0) {
80
17.1k
        if (function->params.m != m || function->params.n != ncomp)
81
6
            return_error(gs_error_rangecheck);
82
17.1k
        if (function->head.type == function_type_ArrayedOutput) {
83
            /* If the Function member is an array it must contain either n 1-in, 1-out function dictionaries
84
             * or (type 1, function-based shadings) n 2-in, 1-out function dictionaries. Either way all the
85
             * functions have to have a single output, so we need to check that.
86
             */
87
0
            int i = 0;
88
0
            gs_function_AdOt_params_t *a_params = (gs_function_AdOt_params_t *)&function->params;
89
90
0
            for (i = 0; i < a_params->n;i++) {
91
0
                if (a_params->Functions[i]->params.m != m || a_params->Functions[i]->params.n != 1)
92
0
                    return_error(gs_error_rangecheck);
93
0
            }
94
0
        }
95
        /*
96
         * The Adobe documentation says that the function's domain must
97
         * be a superset of the domain defined in the shading dictionary.
98
         * However, Adobe implementations apparently don't necessarily
99
         * check this ahead of time; therefore, we do the same.
100
         */
101
17.1k
    }
102
18.2k
    return 0;
103
18.2k
}
104
105
/* Check parameters for a mesh shading. */
106
static int
107
check_mesh(const gs_shading_mesh_params_t * params)
108
1.44k
{
109
1.44k
    const float *domain;
110
111
1.44k
    if (data_source_is_array(params->DataSource))
112
0
        domain = 0;
113
1.44k
    else {
114
1.44k
        domain = params->Decode;
115
1.44k
        switch (params->BitsPerCoordinate) {
116
4
            case  1: case  2: case  4: case  8:
117
1.44k
            case 12: case 16: case 24: case 32:
118
1.44k
                break;
119
0
            default:
120
0
                return_error(gs_error_rangecheck);
121
1.44k
        }
122
1.44k
        switch (params->BitsPerComponent) {
123
723
            case  1: case  2: case  4: case  8:
124
1.44k
            case 12: case 16:
125
1.44k
                break;
126
0
            default:
127
0
                return_error(gs_error_rangecheck);
128
1.44k
        }
129
1.44k
    }
130
1.44k
    return check_CBFD((const gs_shading_params_t *)params,
131
1.44k
                      params->Function, domain, 1);
132
1.44k
}
133
134
/* Check the BitsPerFlag value.  Return the value or an error code. */
135
static int
136
check_BPF(const gs_data_source_t *pds, int bpf)
137
1.16k
{
138
1.16k
    if (data_source_is_array(*pds))
139
0
        return 2;
140
1.16k
    switch (bpf) {
141
1.16k
    case 2: case 4: case 8:
142
1.16k
        return bpf;
143
0
    default:
144
0
        return_error(gs_error_rangecheck);
145
1.16k
    }
146
1.16k
}
147
148
/* Initialize common shading parameters. */
149
static void
150
shading_params_init(gs_shading_params_t *params)
151
20
{
152
20
    params->ColorSpace = 0; /* must be set by client */
153
20
    params->cie_joint_caches = 0;
154
20
    params->Background = 0;
155
20
    params->have_BBox = false;
156
20
    params->AntiAlias = false;
157
20
}
158
159
/* Initialize common mesh shading parameters. */
160
static void
161
mesh_shading_params_init(gs_shading_mesh_params_t *params)
162
0
{
163
0
    shading_params_init((gs_shading_params_t *)params);
164
0
    data_source_init_floats(&params->DataSource, NULL, 0);/* client must set */
165
    /* Client must set BitsPerCoordinate and BitsPerComponent */
166
    /* if DataSource is not an array. */
167
0
    params->Decode = 0;
168
0
    params->Function = 0;
169
0
}
170
171
/* Allocate and initialize a shading. */
172
#define ALLOC_SHADING(ppsh, psh, mem, sttype, stype, sprocs, cname, params)\
173
36.4k
  BEGIN\
174
36.4k
    psh = gs_alloc_struct(mem, void, sttype, cname);\
175
36.4k
    if ( psh == 0 )\
176
36.4k
      return_error(gs_error_VMerror);\
177
36.4k
    psh->head.type = stype;\
178
18.2k
    psh->head.procs = sprocs;\
179
18.2k
    psh->params = *params;\
180
18.2k
    *ppsh = (gs_shading_t *)psh;\
181
18.2k
  END
182
183
/* ---------------- Function-based shading ---------------- */
184
185
private_st_shading_Fb();
186
187
/* Initialize parameters for a Function-based shading. */
188
void
189
gs_shading_Fb_params_init(gs_shading_Fb_params_t * params)
190
0
{
191
0
    shading_params_init((gs_shading_params_t *)params);
192
0
    params->Domain[0] = params->Domain[2] = 0;
193
0
    params->Domain[1] = params->Domain[3] = 1;
194
0
    gs_make_identity(&params->Matrix);
195
0
    params->Function = 0; /* must be set by client */
196
0
}
197
198
/* Allocate and initialize a Function-based shading. */
199
static const gs_shading_procs_t shading_Fb_procs = {
200
    gs_shading_Fb_fill_rectangle
201
};
202
int
203
gs_shading_Fb_init(gs_shading_t ** ppsh,
204
                   const gs_shading_Fb_params_t * params, gs_memory_t * mem)
205
0
{
206
0
    gs_shading_Fb_t *psh;
207
0
    gs_matrix imat;
208
0
    int code = check_CBFD((const gs_shading_params_t *)params,
209
0
                          params->Function, params->Domain, 2);
210
211
0
    if (code < 0 ||
212
0
        (code = gs_matrix_invert(&params->Matrix, &imat)) < 0
213
0
        )
214
0
        return code;
215
0
    ALLOC_SHADING(ppsh, psh, mem, &st_shading_Fb, shading_type_Function_based,
216
0
                  shading_Fb_procs, "gs_shading_Fb_init", params);
217
0
    return 0;
218
0
}
219
220
/* ---------------- Axial shading ---------------- */
221
222
private_st_shading_A();
223
224
/* Initialize parameters for an Axial shading. */
225
void
226
gs_shading_A_params_init(gs_shading_A_params_t * params)
227
10
{
228
10
    shading_params_init((gs_shading_params_t *)params);
229
    /* Coords must be set by client */
230
10
    params->Domain[0] = 0;
231
10
    params->Domain[1] = 1;
232
10
    params->Function = 0; /* must be set by client */
233
10
    params->Extend[0] = params->Extend[1] = false;
234
10
}
235
236
/* Allocate and initialize an Axial shading. */
237
static const gs_shading_procs_t shading_A_procs = {
238
    gs_shading_A_fill_rectangle
239
};
240
int
241
gs_shading_A_init(gs_shading_t ** ppsh,
242
                  const gs_shading_A_params_t * params, gs_memory_t * mem)
243
16.5k
{
244
16.5k
    gs_shading_A_t *psh;
245
16.5k
    int code = check_CBFD((const gs_shading_params_t *)params,
246
16.5k
                          params->Function, params->Domain, 1);
247
248
16.5k
    if (code < 0)
249
6
        return code;
250
16.5k
    ALLOC_SHADING(ppsh, psh, mem, &st_shading_A, shading_type_Axial,
251
16.5k
                  shading_A_procs, "gs_shading_A_init", params);
252
16.5k
    return 0;
253
16.5k
}
254
255
/* ---------------- Radial shading ---------------- */
256
257
private_st_shading_R();
258
259
/* Initialize parameters for a Radial shading. */
260
void
261
gs_shading_R_params_init(gs_shading_R_params_t * params)
262
10
{
263
10
    shading_params_init((gs_shading_params_t *)params);
264
    /* Coords must be set by client */
265
10
    params->Domain[0] = 0;
266
10
    params->Domain[1] = 1;
267
10
    params->Function = 0; /* must be set by client */
268
10
    params->Extend[0] = params->Extend[1] = false;
269
10
}
270
271
/* Allocate and initialize a Radial shading. */
272
static const gs_shading_procs_t shading_R_procs = {
273
    gs_shading_R_fill_rectangle
274
};
275
int
276
gs_shading_R_init(gs_shading_t ** ppsh,
277
                  const gs_shading_R_params_t * params, gs_memory_t * mem)
278
181
{
279
181
    gs_shading_R_t *psh;
280
181
    int code;
281
282
181
    if (params == NULL || params->Domain[0] == params->Domain[1] ||
283
181
        params->Coords[2] < 0 || params->Coords[5] < 0)
284
0
        return_error(gs_error_rangecheck);
285
181
    code = check_CBFD((const gs_shading_params_t *)params,
286
181
                          params->Function, params->Domain, 1);
287
288
181
    if (code < 0)
289
0
        return code;
290
181
    ALLOC_SHADING(ppsh, psh, mem, &st_shading_R, shading_type_Radial,
291
181
                  shading_R_procs, "gs_shading_R_init", params);
292
181
    return 0;
293
181
}
294
295
/* ---------------- Free-form Gouraud triangle mesh shading ---------------- */
296
297
private_st_shading_FfGt();
298
299
/* Initialize parameters for a Free-form Gouraud triangle mesh shading. */
300
void
301
gs_shading_FfGt_params_init(gs_shading_FfGt_params_t * params)
302
0
{
303
0
    mesh_shading_params_init((gs_shading_mesh_params_t *)params);
304
    /* Client must set BitsPerFlag if DataSource is not an array. */
305
0
}
306
307
/* Allocate and initialize a Free-form Gouraud triangle mesh shading. */
308
static const gs_shading_procs_t shading_FfGt_procs = {
309
    gs_shading_FfGt_fill_rectangle
310
};
311
int
312
gs_shading_FfGt_init(gs_shading_t ** ppsh,
313
                     const gs_shading_FfGt_params_t * params,
314
                     gs_memory_t * mem)
315
285
{
316
285
    gs_shading_FfGt_t *psh;
317
285
    int code = check_mesh((const gs_shading_mesh_params_t *)params);
318
285
    int bpf = check_BPF(&params->DataSource, params->BitsPerFlag);
319
320
285
    if (code < 0)
321
0
        return code;
322
285
    if (bpf < 0)
323
0
        return bpf;
324
285
    ALLOC_SHADING(ppsh, psh, mem, &st_shading_FfGt, shading_type_Free_form_Gouraud_triangle,
325
285
                  shading_FfGt_procs, "gs_shading_FfGt_init", params);
326
285
    psh->params.BitsPerFlag = bpf;
327
285
    return 0;
328
285
}
329
330
/* -------------- Lattice-form Gouraud triangle mesh shading -------------- */
331
332
private_st_shading_LfGt();
333
334
/* Initialize parameters for a Lattice-form Gouraud triangle mesh shading. */
335
void
336
gs_shading_LfGt_params_init(gs_shading_LfGt_params_t * params)
337
0
{
338
0
    mesh_shading_params_init((gs_shading_mesh_params_t *)params);
339
    /* Client must set VerticesPerRow. */
340
0
}
341
342
/* Allocate and initialize a Lattice-form Gouraud triangle mesh shading. */
343
static const gs_shading_procs_t shading_LfGt_procs = {
344
    gs_shading_LfGt_fill_rectangle
345
};
346
int
347
gs_shading_LfGt_init(gs_shading_t ** ppsh,
348
                 const gs_shading_LfGt_params_t * params, gs_memory_t * mem)
349
284
{
350
284
    gs_shading_LfGt_t *psh;
351
284
    int code = check_mesh((const gs_shading_mesh_params_t *)params);
352
353
284
    if (code < 0)
354
0
        return code;
355
284
    if (params->VerticesPerRow < 2)
356
0
        return_error(gs_error_rangecheck);
357
284
    ALLOC_SHADING(ppsh, psh, mem, &st_shading_LfGt, shading_type_Lattice_form_Gouraud_triangle,
358
284
                  shading_LfGt_procs, "gs_shading_LfGt_init", params);
359
284
    return 0;
360
284
}
361
362
/* ---------------- Coons patch mesh shading ---------------- */
363
364
private_st_shading_Cp();
365
366
/* Initialize parameters for a Coons patch mesh shading. */
367
void
368
gs_shading_Cp_params_init(gs_shading_Cp_params_t * params)
369
0
{
370
0
    mesh_shading_params_init((gs_shading_mesh_params_t *)params);
371
    /* Client must set BitsPerFlag if DataSource is not an array. */
372
0
}
373
374
/* Allocate and initialize a Coons patch mesh shading. */
375
static const gs_shading_procs_t shading_Cp_procs = {
376
    gs_shading_Cp_fill_rectangle
377
};
378
int
379
gs_shading_Cp_init(gs_shading_t ** ppsh,
380
                   const gs_shading_Cp_params_t * params, gs_memory_t * mem)
381
153
{
382
153
    gs_shading_Cp_t *psh;
383
153
    int code = check_mesh((const gs_shading_mesh_params_t *)params);
384
153
    int bpf = check_BPF(&params->DataSource, params->BitsPerFlag);
385
386
153
    if (code < 0)
387
0
        return code;
388
153
    if (bpf < 0)
389
0
        return bpf;
390
153
    ALLOC_SHADING(ppsh, psh, mem, &st_shading_Cp, shading_type_Coons_patch,
391
153
                  shading_Cp_procs, "gs_shading_Cp_init", params);
392
153
    psh->params.BitsPerFlag = bpf;
393
153
    return 0;
394
153
}
395
396
/* ---------------- Tensor product patch mesh shading ---------------- */
397
398
private_st_shading_Tpp();
399
400
/* Initialize parameters for a Tensor product patch mesh shading. */
401
void
402
gs_shading_Tpp_params_init(gs_shading_Tpp_params_t * params)
403
0
{
404
0
    mesh_shading_params_init((gs_shading_mesh_params_t *)params);
405
    /* Client must set BitsPerFlag if DataSource is not an array. */
406
0
}
407
408
/* Allocate and initialize a Tensor product patch mesh shading. */
409
static const gs_shading_procs_t shading_Tpp_procs = {
410
    gs_shading_Tpp_fill_rectangle
411
};
412
int
413
gs_shading_Tpp_init(gs_shading_t ** ppsh,
414
                  const gs_shading_Tpp_params_t * params, gs_memory_t * mem)
415
723
{
416
723
    gs_shading_Tpp_t *psh;
417
723
    int code = check_mesh((const gs_shading_mesh_params_t *)params);
418
723
    int bpf = check_BPF(&params->DataSource, params->BitsPerFlag);
419
420
723
    if (code < 0)
421
0
        return code;
422
723
    if (bpf < 0)
423
0
        return bpf;
424
723
    ALLOC_SHADING(ppsh, psh, mem, &st_shading_Tpp, shading_type_Tensor_product_patch,
425
723
                  shading_Tpp_procs, "gs_shading_Tpp_init", params);
426
723
    psh->params.BitsPerFlag = bpf;
427
723
    return 0;
428
723
}
429
430
/* ================ Shading rendering ================ */
431
432
/* Add a user-space rectangle to a path. */
433
int
434
gs_shading_path_add_box(gx_path *ppath, const gs_rect *pbox,
435
                     const gs_matrix_fixed *pmat)
436
32
{
437
32
    gs_fixed_point pt;
438
32
    gs_fixed_point pts[3];
439
32
    int code;
440
441
32
    if ((code = gs_point_transform2fixed(pmat, pbox->p.x, pbox->p.y,
442
32
                                         &pt)) < 0 ||
443
32
        (code = gx_path_add_point(ppath, pt.x, pt.y)) < 0 ||
444
32
        (code = gs_point_transform2fixed(pmat, pbox->q.x, pbox->p.y,
445
32
                                         &pts[0])) < 0 ||
446
32
        (code = gs_point_transform2fixed(pmat, pbox->q.x, pbox->q.y,
447
32
                                         &pts[1])) < 0 ||
448
32
        (code = gs_point_transform2fixed(pmat, pbox->p.x, pbox->q.y,
449
32
                                         &pts[2])) < 0 ||
450
32
        (code = gx_path_add_lines(ppath, pts, 3)) < 0
451
32
        )
452
32
        DO_NOTHING;
453
32
    return code;
454
32
}
455
456
/* Fill a path with a shading. */
457
int
458
gs_shading_do_fill_rectangle(const gs_shading_t *psh,
459
                     const gs_fixed_rect *prect, gx_device *dev,
460
                     gs_gstate *pgs, bool fill_background)
461
41.1k
{   /* If you need to fill a path, clip the output device before calling this function. */
462
41.1k
    const gs_matrix_fixed *pmat = &pgs->ctm;
463
41.1k
    gs_fixed_rect path_box;
464
41.1k
    gs_rect path_rect;
465
41.1k
    gs_rect rect;
466
41.1k
    int code = 0;
467
468
41.1k
    dev_proc(dev, get_clipping_box)(dev, &path_box);
469
41.1k
    if (prect)
470
41.1k
        rect_intersect(path_box, *prect);
471
41.1k
    if (psh->params.Background && fill_background) {
472
20.7k
        const gs_color_space *pcs = psh->params.ColorSpace;
473
20.7k
        gs_client_color cc;
474
20.7k
        gx_device_color dev_color;
475
476
20.7k
        cc = *psh->params.Background;
477
20.7k
        (*pcs->type->restrict_color)(&cc, pcs);
478
20.7k
        code = (*pcs->type->remap_color)(&cc, pcs, &dev_color, pgs,
479
20.7k
                                         dev, gs_color_select_texture);
480
481
        /****** WRONG IF NON-IDEMPOTENT RasterOp ******/
482
20.7k
        if (code >= 0)
483
20.7k
            code = gx_shade_background(dev, &path_box, &dev_color, pgs->log_op);
484
20.7k
    }
485
41.1k
    if (code >= 0) {
486
41.1k
        path_rect.p.x = fixed2float(path_box.p.x);
487
41.1k
        path_rect.p.y = fixed2float(path_box.p.y);
488
41.1k
        path_rect.q.x = fixed2float(path_box.q.x);
489
41.1k
        path_rect.q.y = fixed2float(path_box.q.y);
490
41.1k
        code = gs_bbox_transform_inverse(&path_rect, (const gs_matrix *)pmat, &rect);
491
41.1k
        if (code >= 0)
492
41.0k
            code = gs_shading_fill_rectangle(psh, &rect, &path_box, dev, pgs);
493
41.1k
    }
494
41.1k
    return code;
495
41.1k
}