Coverage Report

Created: 2025-06-24 07:01

/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
74.8k
{
70
74.8k
    int ncomp = gs_color_space_num_components(params->ColorSpace);
71
72
74.8k
    if (ncomp < 0 ||
73
74.8k
        (params->have_BBox &&
74
74.8k
         (params->BBox.p.x > params->BBox.q.x ||
75
101
          params->BBox.p.y > params->BBox.q.y))
76
74.8k
        )
77
0
        return_error(gs_error_rangecheck);
78
74.8k
    if (function != 0) {
79
72.3k
        if (function->params.m != m || function->params.n != ncomp)
80
5
            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
72.3k
    }
88
74.8k
    return 0;
89
74.8k
}
90
91
/* Check parameters for a mesh shading. */
92
static int
93
check_mesh(const gs_shading_mesh_params_t * params)
94
2.98k
{
95
2.98k
    const float *domain;
96
97
2.98k
    if (data_source_is_array(params->DataSource))
98
0
        domain = 0;
99
2.98k
    else {
100
2.98k
        domain = params->Decode;
101
2.98k
        switch (params->BitsPerCoordinate) {
102
0
            case  1: case  2: case  4: case  8:
103
2.98k
            case 12: case 16: case 24: case 32:
104
2.98k
                break;
105
0
            default:
106
0
                return_error(gs_error_rangecheck);
107
2.98k
        }
108
2.98k
        switch (params->BitsPerComponent) {
109
2.04k
            case  1: case  2: case  4: case  8:
110
2.98k
            case 12: case 16:
111
2.98k
                break;
112
0
            default:
113
0
                return_error(gs_error_rangecheck);
114
2.98k
        }
115
2.98k
    }
116
2.98k
    return check_CBFD((const gs_shading_params_t *)params,
117
2.98k
                      params->Function, domain, 1);
118
2.98k
}
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
2.58k
{
124
2.58k
    if (data_source_is_array(*pds))
125
0
        return 2;
126
2.58k
    switch (bpf) {
127
2.58k
    case 2: case 4: case 8:
128
2.58k
        return bpf;
129
0
    default:
130
0
        return_error(gs_error_rangecheck);
131
2.58k
    }
132
2.58k
}
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
149k
  BEGIN\
160
149k
    psh = gs_alloc_struct(mem, void, sttype, cname);\
161
149k
    if ( psh == 0 )\
162
149k
      return_error(gs_error_VMerror);\
163
149k
    psh->head.type = stype;\
164
74.8k
    psh->head.procs = sprocs;\
165
74.8k
    psh->params = *params;\
166
74.8k
    *ppsh = (gs_shading_t *)psh;\
167
74.8k
  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
71.7k
{
230
71.7k
    gs_shading_A_t *psh;
231
71.7k
    int code = check_CBFD((const gs_shading_params_t *)params,
232
71.7k
                          params->Function, params->Domain, 1);
233
234
71.7k
    if (code < 0)
235
5
        return code;
236
71.7k
    ALLOC_SHADING(ppsh, psh, mem, &st_shading_A, shading_type_Axial,
237
71.6k
                  shading_A_procs, "gs_shading_A_init", params);
238
71.6k
    return 0;
239
71.6k
}
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
180
{
265
180
    gs_shading_R_t *psh;
266
180
    int code;
267
268
180
    if (params == NULL || params->Domain[0] == params->Domain[1] ||
269
180
        params->Coords[2] < 0 || params->Coords[5] < 0)
270
0
        return_error(gs_error_rangecheck);
271
180
    code = check_CBFD((const gs_shading_params_t *)params,
272
180
                          params->Function, params->Domain, 1);
273
274
180
    if (code < 0)
275
0
        return code;
276
180
    ALLOC_SHADING(ppsh, psh, mem, &st_shading_R, shading_type_Radial,
277
180
                  shading_R_procs, "gs_shading_R_init", params);
278
180
    return 0;
279
180
}
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
360
{
302
360
    gs_shading_FfGt_t *psh;
303
360
    int code = check_mesh((const gs_shading_mesh_params_t *)params);
304
360
    int bpf = check_BPF(&params->DataSource, params->BitsPerFlag);
305
306
360
    if (code < 0)
307
0
        return code;
308
360
    if (bpf < 0)
309
0
        return bpf;
310
360
    ALLOC_SHADING(ppsh, psh, mem, &st_shading_FfGt, shading_type_Free_form_Gouraud_triangle,
311
360
                  shading_FfGt_procs, "gs_shading_FfGt_init", params);
312
360
    psh->params.BitsPerFlag = bpf;
313
360
    return 0;
314
360
}
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
405
{
336
405
    gs_shading_LfGt_t *psh;
337
405
    int code = check_mesh((const gs_shading_mesh_params_t *)params);
338
339
405
    if (code < 0)
340
0
        return code;
341
405
    if (params->VerticesPerRow < 2)
342
0
        return_error(gs_error_rangecheck);
343
405
    ALLOC_SHADING(ppsh, psh, mem, &st_shading_LfGt, shading_type_Lattice_form_Gouraud_triangle,
344
405
                  shading_LfGt_procs, "gs_shading_LfGt_init", params);
345
405
    return 0;
346
405
}
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
171
{
368
171
    gs_shading_Cp_t *psh;
369
171
    int code = check_mesh((const gs_shading_mesh_params_t *)params);
370
171
    int bpf = check_BPF(&params->DataSource, params->BitsPerFlag);
371
372
171
    if (code < 0)
373
0
        return code;
374
171
    if (bpf < 0)
375
0
        return bpf;
376
171
    ALLOC_SHADING(ppsh, psh, mem, &st_shading_Cp, shading_type_Coons_patch,
377
171
                  shading_Cp_procs, "gs_shading_Cp_init", params);
378
171
    psh->params.BitsPerFlag = bpf;
379
171
    return 0;
380
171
}
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
2.05k
{
402
2.05k
    gs_shading_Tpp_t *psh;
403
2.05k
    int code = check_mesh((const gs_shading_mesh_params_t *)params);
404
2.05k
    int bpf = check_BPF(&params->DataSource, params->BitsPerFlag);
405
406
2.05k
    if (code < 0)
407
0
        return code;
408
2.05k
    if (bpf < 0)
409
0
        return bpf;
410
2.05k
    ALLOC_SHADING(ppsh, psh, mem, &st_shading_Tpp, shading_type_Tensor_product_patch,
411
2.05k
                  shading_Tpp_procs, "gs_shading_Tpp_init", params);
412
2.05k
    psh->params.BitsPerFlag = bpf;
413
2.05k
    return 0;
414
2.05k
}
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
93
{
423
93
    gs_fixed_point pt;
424
93
    gs_fixed_point pts[3];
425
93
    int code;
426
427
93
    if ((code = gs_point_transform2fixed(pmat, pbox->p.x, pbox->p.y,
428
93
                                         &pt)) < 0 ||
429
93
        (code = gx_path_add_point(ppath, pt.x, pt.y)) < 0 ||
430
93
        (code = gs_point_transform2fixed(pmat, pbox->q.x, pbox->p.y,
431
93
                                         &pts[0])) < 0 ||
432
93
        (code = gs_point_transform2fixed(pmat, pbox->q.x, pbox->q.y,
433
93
                                         &pts[1])) < 0 ||
434
93
        (code = gs_point_transform2fixed(pmat, pbox->p.x, pbox->q.y,
435
93
                                         &pts[2])) < 0 ||
436
93
        (code = gx_path_add_lines(ppath, pts, 3)) < 0
437
93
        )
438
93
        DO_NOTHING;
439
93
    return code;
440
93
}
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
267k
{   /* If you need to fill a path, clip the output device before calling this function. */
448
267k
    const gs_matrix_fixed *pmat = &pgs->ctm;
449
267k
    gs_fixed_rect path_box;
450
267k
    gs_rect path_rect;
451
267k
    gs_rect rect;
452
267k
    int code = 0;
453
454
267k
    dev_proc(dev, get_clipping_box)(dev, &path_box);
455
267k
    if (prect)
456
267k
        rect_intersect(path_box, *prect);
457
267k
    if (psh->params.Background && fill_background) {
458
201k
        const gs_color_space *pcs = psh->params.ColorSpace;
459
201k
        gs_client_color cc;
460
201k
        gx_device_color dev_color;
461
462
201k
        cc = *psh->params.Background;
463
201k
        (*pcs->type->restrict_color)(&cc, pcs);
464
201k
        code = (*pcs->type->remap_color)(&cc, pcs, &dev_color, pgs,
465
201k
                                         dev, gs_color_select_texture);
466
467
        /****** WRONG IF NON-IDEMPOTENT RasterOp ******/
468
201k
        if (code >= 0)
469
201k
            code = gx_shade_background(dev, &path_box, &dev_color, pgs->log_op);
470
201k
    }
471
267k
    if (code >= 0) {
472
267k
        path_rect.p.x = fixed2float(path_box.p.x);
473
267k
        path_rect.p.y = fixed2float(path_box.p.y);
474
267k
        path_rect.q.x = fixed2float(path_box.q.x);
475
267k
        path_rect.q.y = fixed2float(path_box.q.y);
476
267k
        code = gs_bbox_transform_inverse(&path_rect, (const gs_matrix *)pmat, &rect);
477
267k
        if (code >= 0)
478
267k
            code = gs_shading_fill_rectangle(psh, &rect, &path_box, dev, pgs);
479
267k
    }
480
267k
    return code;
481
267k
}