Coverage Report

Created: 2025-06-10 07:19

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