Coverage Report

Created: 2026-04-01 07:17

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ghostpdl/base/gsdps1.c
Line
Count
Source
1
/* Copyright (C) 2001-2025 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
/* Display PostScript graphics additions for Ghostscript library */
18
#include "math_.h"
19
#include "gx.h"
20
#include "gserrors.h"
21
#include "gsmatrix.h"   /* for gscoord.h */
22
#include "gscoord.h"
23
#include "gspaint.h"
24
#include "gxdevice.h"
25
#include "gxfixed.h"
26
#include "gxmatrix.h"
27
#include "gxhldevc.h"
28
#include "gspath.h"
29
#include "gspath2.h"    /* defines interface */
30
#include "gzpath.h"
31
#include "gzcpath.h"
32
#include "gzstate.h"
33
#include "gsutil.h"
34
#include "gxdevsop.h"
35
36
/*
37
 * Define how much rounding slop setbbox should leave,
38
 * in device coordinates.  Because of rounding in transforming
39
 * path coordinates to fixed point, the minimum realistic value is:
40
 *
41
 *      #define box_rounding_slop_fixed (fixed_epsilon)
42
 *
43
 * But even this isn't enough to compensate for cumulative rounding error
44
 * in rmoveto or rcurveto.  Instead, we somewhat arbitrarily use:
45
 */
46
44
#define box_rounding_slop_fixed (fixed_epsilon * 3)
47
48
/* ------ Graphics state ------ */
49
50
/* Set the bounding box for the current path. */
51
int
52
gs_setbbox(gs_gstate * pgs, double llx, double lly, double urx, double ury)
53
27
{
54
27
    gs_rect ubox, dbox;
55
27
    gs_fixed_rect obox, bbox;
56
27
    gx_path *ppath = pgs->path;
57
27
    int code;
58
59
27
    if (llx > urx || lly > ury)
60
9
        return_error(gs_error_rangecheck);
61
    /* Transform box to device coordinates. */
62
18
    ubox.p.x = llx;
63
18
    ubox.p.y = lly;
64
18
    ubox.q.x = urx;
65
18
    ubox.q.y = ury;
66
18
    if ((code = gs_bbox_transform(&ubox, &ctm_only(pgs), &dbox)) < 0)
67
0
        return code;
68
    /* Round the corners in opposite directions. */
69
    /* Because we can't predict the magnitude of the dbox values, */
70
    /* we add/subtract the slop after fixing. */
71
18
    if (dbox.p.x < fixed2float(min_fixed + box_rounding_slop_fixed) ||
72
16
        dbox.p.y < fixed2float(min_fixed + box_rounding_slop_fixed) ||
73
15
        dbox.q.x >= fixed2float(max_fixed - box_rounding_slop_fixed + fixed_epsilon) ||
74
13
        dbox.q.y >= fixed2float(max_fixed - box_rounding_slop_fixed + fixed_epsilon)
75
18
        )
76
7
        return_error(gs_error_limitcheck);
77
11
    bbox.p.x =
78
11
        (fixed) floor(dbox.p.x * fixed_scale) - box_rounding_slop_fixed;
79
11
    bbox.p.y =
80
11
        (fixed) floor(dbox.p.y * fixed_scale) - box_rounding_slop_fixed;
81
11
    bbox.q.x =
82
11
        (fixed) ceil(dbox.q.x * fixed_scale) + box_rounding_slop_fixed;
83
11
    bbox.q.y =
84
11
        (fixed) ceil(dbox.q.y * fixed_scale) + box_rounding_slop_fixed;
85
11
    if (gx_path_bbox_set(ppath, &obox) >= 0) { /* Take the union of the bboxes. */
86
2
        ppath->bbox.p.x = min(obox.p.x, bbox.p.x);
87
2
        ppath->bbox.p.y = min(obox.p.y, bbox.p.y);
88
2
        ppath->bbox.q.x = max(obox.q.x, bbox.q.x);
89
2
        ppath->bbox.q.y = max(obox.q.y, bbox.q.y);
90
9
    } else {     /* empty path *//* Just set the bbox. */
91
9
        ppath->bbox = bbox;
92
9
    }
93
11
    ppath->bbox_set = 1;
94
11
    return 0;
95
18
}
96
97
/* ------ Rectangles ------ */
98
99
/* Append a list of rectangles to a path. */
100
static int
101
gs_rectappend_compat(gs_gstate * pgs, const gs_rect * pr, uint count, bool clip)
102
362k
{
103
362k
    bool CPSI_mode = gs_currentcpsimode(pgs->memory);
104
105
725k
    for (; count != 0; count--, pr++) {
106
362k
        double px = pr->p.x, py = pr->p.y, qx = pr->q.x, qy = pr->q.y;
107
362k
        int code;
108
109
362k
        if (CPSI_mode) {
110
            /* We believe that the result must be independent
111
               on the device initial matrix.
112
               Particularly for the correct dashing
113
               the starting point and the contour direction
114
               must be same with any device initial matrix.
115
               Only way to provide it is to choose the starting point
116
               and the direction in the user space. */
117
0
            if (clip) {
118
                /* CPSI starts a clippath with the upper right corner. */
119
                /* Debugged with CET 11-11.PS page 6 item much13.*/
120
0
                if ((code = gs_moveto(pgs, qx, qy)) < 0 ||
121
0
                    (code = gs_lineto(pgs, qx, py)) < 0 ||
122
0
                    (code = gs_lineto(pgs, px, py)) < 0 ||
123
0
                    (code = gs_lineto(pgs, px, qy)) < 0 ||
124
0
                    (code = gs_closepath(pgs)) < 0
125
0
                    )
126
0
                    return code;
127
0
            } else {
128
                /* Debugged with CET 12-12.PS page 10 item more20.*/
129
0
                if (px > qx) {
130
0
                    px = qx; qx = pr->p.x;
131
0
                }
132
0
                if (py > qy) {
133
0
                    py = qy; qy = pr->p.y;
134
0
                }
135
0
                if ((code = gs_moveto(pgs, px, py)) < 0 ||
136
0
                    (code = gs_lineto(pgs, qx, py)) < 0 ||
137
0
                    (code = gs_lineto(pgs, qx, qy)) < 0 ||
138
0
                    (code = gs_lineto(pgs, px, qy)) < 0 ||
139
0
                    (code = gs_closepath(pgs)) < 0
140
0
                    )
141
0
                    return code;
142
0
            }
143
362k
        } else {
144
            /* Ensure counter-clockwise drawing. */
145
362k
            if ((qx >= px) != (qy >= py))
146
5.38k
                qx = px, px = pr->q.x; /* swap x values */
147
362k
            if ((code = gs_moveto(pgs, px, py)) < 0 ||
148
362k
                (code = gs_lineto(pgs, qx, py)) < 0 ||
149
362k
                (code = gs_lineto(pgs, qx, qy)) < 0 ||
150
362k
                (code = gs_lineto(pgs, px, qy)) < 0 ||
151
362k
                (code = gs_closepath(pgs)) < 0
152
362k
                )
153
0
                return code;
154
362k
        }
155
362k
    }
156
362k
    return 0;
157
362k
}
158
int
159
gs_rectappend(gs_gstate * pgs, const gs_rect * pr, uint count)
160
28.3k
{
161
28.3k
    return gs_rectappend_compat(pgs, pr, count, false);
162
28.3k
}
163
164
/* Clip to a list of rectangles. */
165
int
166
gs_rectclip(gs_gstate * pgs, const gs_rect * pr, uint count)
167
334k
{
168
334k
    int code;
169
334k
    gx_path save;
170
171
334k
    gx_path_init_local(&save, pgs->memory);
172
334k
    gx_path_assign_preserve(&save, pgs->path);
173
334k
    gs_newpath(pgs);
174
334k
    if ((code = gs_rectappend_compat(pgs, pr, count, true)) < 0 ||
175
334k
        (code = gs_clip(pgs)) < 0
176
334k
        ) {
177
0
        gx_path_assign_free(pgs->path, &save);
178
0
        return code;
179
0
    }
180
334k
    gx_path_free(&save, "gs_rectclip");
181
334k
    gs_newpath(pgs);
182
334k
    return 0;
183
334k
}
184
185
/* Setup for black vector handling */
186
static inline bool black_vectors(gs_gstate *pgs, gx_device *dev)
187
53.8k
{
188
53.8k
    if (dev->icc_struct != NULL && dev->icc_struct->blackvector &&
189
0
        pgs->black_textvec_state == NULL) {
190
0
        return gsicc_setup_blacktextvec(pgs, dev, false);
191
0
    }
192
53.8k
    return false;
193
53.8k
}
194
195
/* Fill a list of rectangles. */
196
/* We take the trouble to do this efficiently in the simple cases. */
197
int
198
gs_rectfill(gs_gstate * pgs, const gs_rect * pr, uint count)
199
53.8k
{
200
53.8k
    const gs_rect *rlist = pr;
201
53.8k
    gx_clip_path *pcpath;
202
53.8k
    uint rcount = count;
203
53.8k
    int code;
204
53.8k
    gx_device * pdev = pgs->device;
205
53.8k
    gx_device_color *pdc = gs_currentdevicecolor_inline(pgs);
206
53.8k
    const gs_gstate *pgs2 = (const gs_gstate *)pgs;
207
53.8k
    bool hl_color_available = gx_hld_is_hl_color_available(pgs2, pdc);
208
53.8k
    bool hl_color = (hl_color_available && (dev_proc(pdev, dev_spec_op)(pdev, gxdso_supports_hlcolor, NULL, 0) > 0));
209
53.8k
    bool center_of_pixel = (pgs->fill_adjust.x == 0 && pgs->fill_adjust.y == 0);
210
53.8k
    bool black_vector = false;
211
212
    /* Processing a fill object operation */
213
53.8k
    ensure_tag_is_set(pgs, pgs->device, GS_VECTOR_TAG); /* NB: may unset_dev_color */
214
215
53.8k
    black_vector = black_vectors(pgs, pgs->device); /* Set vector fill to black */
216
217
53.8k
    code = gx_set_dev_color(pgs);
218
53.8k
    if (code != 0)
219
2
        goto exit;
220
221
53.7k
    if ( !(pgs->device->page_uses_transparency ||
222
53.7k
          dev_proc(pgs->device, dev_spec_op)(pgs->device,
223
53.7k
              gxdso_is_pdf14_device, &(pgs->device),
224
53.7k
              sizeof(pgs->device))) &&
225
53.4k
        (is_fzero2(pgs->ctm.xy, pgs->ctm.yx) ||
226
12
         is_fzero2(pgs->ctm.xx, pgs->ctm.yy)) &&
227
53.4k
        gx_effective_clip_path(pgs, &pcpath) >= 0 &&
228
53.4k
        clip_list_is_rectangle(gx_cpath_list(pcpath)) &&
229
53.3k
        (hl_color ||
230
43.4k
         pdc->type == gx_dc_type_pure ||
231
14.0k
         pdc->type == gx_dc_type_ht_binary ||
232
7.75k
         pdc->type == gx_dc_type_ht_colored) &&
233
51.5k
        gs_gstate_color_load(pgs) >= 0 &&
234
51.5k
        (*dev_proc(pdev, get_alpha_bits)) (pdev, go_graphics)
235
51.5k
        <= 1 &&
236
51.5k
        (!pgs->overprint || !gs_currentcolor_eopm(pgs))
237
53.7k
        ) {
238
51.5k
        uint i;
239
51.5k
        gs_fixed_rect clip_rect;
240
241
51.5k
        gx_cpath_inner_box(pcpath, &clip_rect);
242
        /* We should never plot anything for an empty clip rectangle */
243
51.5k
        if ((clip_rect.p.x >= clip_rect.q.x) &&
244
38
            (clip_rect.p.y >= clip_rect.q.y))
245
38
            goto exit;
246
94.1k
        for (i = 0; i < count; ++i) {
247
51.4k
            gs_fixed_point p, q;
248
51.4k
            gs_fixed_rect draw_rect;
249
250
51.4k
            if (gs_point_transform2fixed(&pgs->ctm, pr[i].p.x, pr[i].p.y, &p) < 0 ||
251
49.0k
                gs_point_transform2fixed(&pgs->ctm, pr[i].q.x, pr[i].q.y, &q) < 0
252
51.4k
                ) {   /* Switch to the slow algorithm. */
253
8.16k
                goto slow;
254
8.16k
            }
255
43.2k
            draw_rect.p.x = min(p.x, q.x);
256
43.2k
            draw_rect.p.y = min(p.y, q.y);
257
43.2k
            draw_rect.q.x = max(p.x, q.x);
258
43.2k
            draw_rect.q.y = max(p.y, q.y);
259
43.2k
            if (hl_color) {
260
8.12k
                rect_intersect(draw_rect, clip_rect);
261
                /* We do pass on 0 extant rectangles to high level
262
                   devices.  It isn't clear how a client and an output
263
                   device should interact if one uses a center of
264
                   pixel algorithm and the other uses any part of
265
                   pixel.  For now we punt and just pass the high
266
                   level rectangle on without adjustment. */
267
8.12k
                if (draw_rect.p.x <= draw_rect.q.x &&
268
7.68k
                    draw_rect.p.y <= draw_rect.q.y) {
269
7.53k
                    code = dev_proc(pdev, fill_rectangle_hl_color)(pdev,
270
7.53k
                             &draw_rect, pgs2, pdc, pcpath);
271
7.53k
                    if (code < 0)
272
0
                        goto exit;
273
7.53k
                }
274
35.1k
            } else {
275
35.1k
                int x, y, w, h;
276
277
35.1k
                rect_intersect(draw_rect, clip_rect);
278
35.1k
                if (center_of_pixel) {
279
5.21k
                    draw_rect.p.x = fixed_rounded(draw_rect.p.x);
280
5.21k
                    draw_rect.p.y = fixed_rounded(draw_rect.p.y);
281
5.21k
                    draw_rect.q.x = fixed_rounded(draw_rect.q.x);
282
5.21k
                    draw_rect.q.y = fixed_rounded(draw_rect.q.y);
283
29.9k
                } else { /* any part of pixel rule - touched */
284
29.9k
                    draw_rect.p.x = fixed_floor(draw_rect.p.x);
285
29.9k
                    draw_rect.p.y = fixed_floor(draw_rect.p.y);
286
29.9k
                    draw_rect.q.x = fixed_ceiling(draw_rect.q.x);
287
29.9k
                    draw_rect.q.y = fixed_ceiling(draw_rect.q.y);
288
29.9k
                }
289
35.1k
                x = fixed2int(draw_rect.p.x);
290
35.1k
                y = fixed2int(draw_rect.p.y);
291
35.1k
                w = fixed2int(draw_rect.q.x) - x;
292
35.1k
                h = fixed2int(draw_rect.q.y) - y;
293
                /* clients that use the "any part of pixel" rule also
294
                   fill 0 areas.  This is true of current graphics
295
                   library clients but not a general rule.  */
296
35.1k
                if (!center_of_pixel) {
297
29.9k
                    if (w == 0)
298
5.76k
                        w = 1;
299
                    /* yes Adobe Acrobat 8, seems to back up the y
300
                       coordinate when the width is 0, sigh. */
301
29.9k
                    if (h == 0) {
302
2.68k
                        y--;
303
2.68k
                        h = 1;
304
2.68k
                    }
305
29.9k
                }
306
35.1k
                if (gx_fill_rectangle(x, y, w, h, pdc, pgs) < 0)
307
653
                    goto slow;
308
35.1k
            }
309
43.2k
        }
310
42.6k
        code = 0;
311
42.6k
        goto exit;
312
8.81k
      slow:rlist = pr + i;
313
8.81k
        rcount = count - i;
314
11.1k
    } {
315
11.1k
        bool do_save = !gx_path_is_null(pgs->path);
316
317
11.1k
        if (do_save) {
318
693
            if ((code = gs_gsave(pgs)) < 0)
319
0
                goto exit;
320
693
            code = gs_newpath(pgs);
321
693
        }
322
11.1k
        if ((code >= 0) &&
323
11.1k
            (((code = gs_rectappend(pgs, rlist, rcount)) < 0) ||
324
11.1k
            ((code = gs_fill(pgs)) < 0))
325
11.1k
            )
326
11.1k
            DO_NOTHING;
327
11.1k
        if (do_save)
328
693
            gs_grestore(pgs);
329
10.4k
        else if (code < 0)
330
0
            gs_newpath(pgs);
331
11.1k
    }
332
333
53.8k
exit:
334
53.8k
    if (black_vector) {
335
        /* Restore color */
336
0
        gsicc_restore_blacktextvec(pgs, false);
337
0
    }
338
53.8k
    return code;
339
11.1k
}
340
341
/* Stroke a list of rectangles. */
342
/* (We could do this a lot more efficiently.) */
343
int
344
gs_rectstroke(gs_gstate * pgs, const gs_rect * pr, uint count,
345
              const gs_matrix * pmat)
346
2.39k
{
347
2.39k
    bool do_save = pmat != NULL || !gx_path_is_null(pgs->path);
348
2.39k
    int code;
349
350
2.39k
    if (do_save) {
351
31
        if ((code = gs_gsave(pgs)) < 0)
352
0
            return code;
353
31
        gs_newpath(pgs);
354
31
    }
355
2.39k
    if ((code = gs_rectappend(pgs, pr, count)) < 0 ||
356
2.39k
        (pmat != NULL && (code = gs_concat(pgs, pmat)) < 0) ||
357
2.39k
        (code = gs_stroke(pgs)) < 0
358
2.39k
        )
359
2.39k
        DO_NOTHING;
360
2.39k
    if (do_save)
361
31
        gs_grestore(pgs);
362
2.36k
    else if (code < 0)
363
0
        gs_newpath(pgs);
364
2.39k
    return code;
365
2.39k
}