Coverage Report

Created: 2026-10-28 08:05

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
17.7k
{
71
17.7k
    int ncomp = gs_color_space_num_components(params->ColorSpace);
72
73
17.7k
    if (ncomp < 0 ||
74
17.7k
        (params->have_BBox &&
75
43
         (params->BBox.p.x > params->BBox.q.x ||
76
43
          params->BBox.p.y > params->BBox.q.y))
77
17.7k
        )
78
0
        return_error(gs_error_rangecheck);
79
17.7k
    if (function != 0) {
80
16.6k
        if (function->params.m != m || function->params.n != ncomp)
81
7
            return_error(gs_error_rangecheck);
82
16.5k
        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
16.5k
    }
102
17.7k
    return 0;
103
17.7k
}
104
105
/* Check parameters for a mesh shading. */
106
static int
107
check_mesh(const gs_shading_mesh_params_t * params)
108
1.58k
{
109
1.58k
    const float *domain;
110
111
1.58k
    if (data_source_is_array(params->DataSource))
112
0
        domain = 0;
113
1.58k
    else {
114
1.58k
        domain = params->Decode;
115
1.58k
        switch (params->BitsPerCoordinate) {
116
3
            case  1: case  2: case  4: case  8:
117
1.58k
            case 12: case 16: case 24: case 32:
118
1.58k
                break;
119
0
            default:
120
0
                return_error(gs_error_rangecheck);
121
1.58k
        }
122
1.58k
        switch (params->BitsPerComponent) {
123
811
            case  1: case  2: case  4: case  8:
124
1.58k
            case 12: case 16:
125
1.58k
                break;
126
0
            default:
127
0
                return_error(gs_error_rangecheck);
128
1.58k
        }
129
1.58k
    }
130
1.58k
    return check_CBFD((const gs_shading_params_t *)params,
131
1.58k
                      params->Function, domain, 1);
132
1.58k
}
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.28k
{
138
1.28k
    if (data_source_is_array(*pds))
139
0
        return 2;
140
1.28k
    switch (bpf) {
141
1.28k
    case 2: case 4: case 8:
142
1.28k
        return bpf;
143
0
    default:
144
0
        return_error(gs_error_rangecheck);
145
1.28k
    }
146
1.28k
}
147
148
/* Initialize common shading parameters. */
149
static void
150
shading_params_init(gs_shading_params_t *params)
151
22
{
152
22
    params->ColorSpace = 0; /* must be set by client */
153
22
    params->cie_joint_caches = 0;
154
22
    params->Background = 0;
155
22
    params->have_BBox = false;
156
22
    params->AntiAlias = false;
157
22
}
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
35.5k
  BEGIN\
174
35.5k
    psh = gs_alloc_struct(mem, void, sttype, cname);\
175
35.5k
    if ( psh == 0 )\
176
35.5k
      return_error(gs_error_VMerror);\
177
35.5k
    psh->head.type = stype;\
178
17.7k
    psh->head.procs = sprocs;\
179
17.7k
    psh->params = *params;\
180
17.7k
    *ppsh = (gs_shading_t *)psh;\
181
17.7k
  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
11
{
228
11
    shading_params_init((gs_shading_params_t *)params);
229
    /* Coords must be set by client */
230
11
    params->Domain[0] = 0;
231
11
    params->Domain[1] = 1;
232
11
    params->Function = 0; /* must be set by client */
233
11
    params->Extend[0] = params->Extend[1] = false;
234
11
}
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.0k
{
244
16.0k
    gs_shading_A_t *psh;
245
16.0k
    int code = check_CBFD((const gs_shading_params_t *)params,
246
16.0k
                          params->Function, params->Domain, 1);
247
248
16.0k
    if (code < 0)
249
7
        return code;
250
16.0k
    ALLOC_SHADING(ppsh, psh, mem, &st_shading_A, shading_type_Axial,
251
16.0k
                  shading_A_procs, "gs_shading_A_init", params);
252
16.0k
    return 0;
253
16.0k
}
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
11
{
263
11
    shading_params_init((gs_shading_params_t *)params);
264
    /* Coords must be set by client */
265
11
    params->Domain[0] = 0;
266
11
    params->Domain[1] = 1;
267
11
    params->Function = 0; /* must be set by client */
268
11
    params->Extend[0] = params->Extend[1] = false;
269
11
}
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
123
{
279
123
    gs_shading_R_t *psh;
280
123
    int code;
281
282
123
    if (params == NULL || params->Domain[0] == params->Domain[1] ||
283
123
        params->Coords[2] < 0 || params->Coords[5] < 0)
284
0
        return_error(gs_error_rangecheck);
285
123
    code = check_CBFD((const gs_shading_params_t *)params,
286
123
                          params->Function, params->Domain, 1);
287
288
123
    if (code < 0)
289
0
        return code;
290
123
    ALLOC_SHADING(ppsh, psh, mem, &st_shading_R, shading_type_Radial,
291
123
                  shading_R_procs, "gs_shading_R_init", params);
292
123
    return 0;
293
123
}
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
314
{
316
314
    gs_shading_FfGt_t *psh;
317
314
    int code = check_mesh((const gs_shading_mesh_params_t *)params);
318
314
    int bpf = check_BPF(&params->DataSource, params->BitsPerFlag);
319
320
314
    if (code < 0)
321
0
        return code;
322
314
    if (bpf < 0)
323
0
        return bpf;
324
314
    ALLOC_SHADING(ppsh, psh, mem, &st_shading_FfGt, shading_type_Free_form_Gouraud_triangle,
325
314
                  shading_FfGt_procs, "gs_shading_FfGt_init", params);
326
314
    psh->params.BitsPerFlag = bpf;
327
314
    return 0;
328
314
}
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
301
{
350
301
    gs_shading_LfGt_t *psh;
351
301
    int code = check_mesh((const gs_shading_mesh_params_t *)params);
352
353
301
    if (code < 0)
354
0
        return code;
355
301
    if (params->VerticesPerRow < 2)
356
0
        return_error(gs_error_rangecheck);
357
301
    ALLOC_SHADING(ppsh, psh, mem, &st_shading_LfGt, shading_type_Lattice_form_Gouraud_triangle,
358
301
                  shading_LfGt_procs, "gs_shading_LfGt_init", params);
359
301
    return 0;
360
301
}
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
163
{
382
163
    gs_shading_Cp_t *psh;
383
163
    int code = check_mesh((const gs_shading_mesh_params_t *)params);
384
163
    int bpf = check_BPF(&params->DataSource, params->BitsPerFlag);
385
386
163
    if (code < 0)
387
0
        return code;
388
163
    if (bpf < 0)
389
0
        return bpf;
390
163
    ALLOC_SHADING(ppsh, psh, mem, &st_shading_Cp, shading_type_Coons_patch,
391
163
                  shading_Cp_procs, "gs_shading_Cp_init", params);
392
163
    psh->params.BitsPerFlag = bpf;
393
163
    return 0;
394
163
}
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
807
{
416
807
    gs_shading_Tpp_t *psh;
417
807
    int code = check_mesh((const gs_shading_mesh_params_t *)params);
418
807
    int bpf = check_BPF(&params->DataSource, params->BitsPerFlag);
419
420
807
    if (code < 0)
421
0
        return code;
422
807
    if (bpf < 0)
423
0
        return bpf;
424
807
    ALLOC_SHADING(ppsh, psh, mem, &st_shading_Tpp, shading_type_Tensor_product_patch,
425
807
                  shading_Tpp_procs, "gs_shading_Tpp_init", params);
426
807
    psh->params.BitsPerFlag = bpf;
427
807
    return 0;
428
807
}
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
33
{
437
33
    gs_fixed_point pt;
438
33
    gs_fixed_point pts[3];
439
33
    int code;
440
441
33
    if ((code = gs_point_transform2fixed(pmat, pbox->p.x, pbox->p.y,
442
33
                                         &pt)) < 0 ||
443
33
        (code = gx_path_add_point(ppath, pt.x, pt.y)) < 0 ||
444
33
        (code = gs_point_transform2fixed(pmat, pbox->q.x, pbox->p.y,
445
33
                                         &pts[0])) < 0 ||
446
33
        (code = gs_point_transform2fixed(pmat, pbox->q.x, pbox->q.y,
447
33
                                         &pts[1])) < 0 ||
448
33
        (code = gs_point_transform2fixed(pmat, pbox->p.x, pbox->q.y,
449
33
                                         &pts[2])) < 0 ||
450
33
        (code = gx_path_add_lines(ppath, pts, 3)) < 0
451
33
        )
452
33
        DO_NOTHING;
453
33
    return code;
454
33
}
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
29.5k
{   /* If you need to fill a path, clip the output device before calling this function. */
462
29.5k
    const gs_matrix_fixed *pmat = &pgs->ctm;
463
29.5k
    gs_fixed_rect path_box;
464
29.5k
    gs_rect path_rect;
465
29.5k
    gs_rect rect;
466
29.5k
    int code = 0;
467
468
29.5k
    dev_proc(dev, get_clipping_box)(dev, &path_box);
469
29.5k
    if (prect)
470
29.5k
        rect_intersect(path_box, *prect);
471
29.5k
    if (psh->params.Background && fill_background) {
472
9.92k
        const gs_color_space *pcs = psh->params.ColorSpace;
473
9.92k
        gs_client_color cc;
474
9.92k
        gx_device_color dev_color;
475
476
9.92k
        cc = *psh->params.Background;
477
9.92k
        (*pcs->type->restrict_color)(&cc, pcs);
478
9.92k
        code = (*pcs->type->remap_color)(&cc, pcs, &dev_color, pgs,
479
9.92k
                                         dev, gs_color_select_texture);
480
481
        /****** WRONG IF NON-IDEMPOTENT RasterOp ******/
482
9.92k
        if (code >= 0)
483
9.92k
            code = gx_shade_background(dev, &path_box, &dev_color, pgs->log_op);
484
9.92k
    }
485
29.5k
    if (code >= 0) {
486
29.5k
        path_rect.p.x = fixed2float(path_box.p.x);
487
29.5k
        path_rect.p.y = fixed2float(path_box.p.y);
488
29.5k
        path_rect.q.x = fixed2float(path_box.q.x);
489
29.5k
        path_rect.q.y = fixed2float(path_box.q.y);
490
29.5k
        code = gs_bbox_transform_inverse(&path_rect, (const gs_matrix *)pmat, &rect);
491
29.5k
        if (code >= 0)
492
29.4k
            code = gs_shading_fill_rectangle(psh, &rect, &path_box, dev, pgs);
493
29.5k
    }
494
29.5k
    return code;
495
29.5k
}