Coverage Report

Created: 2026-02-14 07:09

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
56
#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
31
{
54
31
    gs_rect ubox, dbox;
55
31
    gs_fixed_rect obox, bbox;
56
31
    gx_path *ppath = pgs->path;
57
31
    int code;
58
59
31
    if (llx > urx || lly > ury)
60
9
        return_error(gs_error_rangecheck);
61
    /* Transform box to device coordinates. */
62
22
    ubox.p.x = llx;
63
22
    ubox.p.y = lly;
64
22
    ubox.q.x = urx;
65
22
    ubox.q.y = ury;
66
22
    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
22
    if (dbox.p.x < fixed2float(min_fixed + box_rounding_slop_fixed) ||
72
20
        dbox.p.y < fixed2float(min_fixed + box_rounding_slop_fixed) ||
73
19
        dbox.q.x >= fixed2float(max_fixed - box_rounding_slop_fixed + fixed_epsilon) ||
74
16
        dbox.q.y >= fixed2float(max_fixed - box_rounding_slop_fixed + fixed_epsilon)
75
22
        )
76
8
        return_error(gs_error_limitcheck);
77
14
    bbox.p.x =
78
14
        (fixed) floor(dbox.p.x * fixed_scale) - box_rounding_slop_fixed;
79
14
    bbox.p.y =
80
14
        (fixed) floor(dbox.p.y * fixed_scale) - box_rounding_slop_fixed;
81
14
    bbox.q.x =
82
14
        (fixed) ceil(dbox.q.x * fixed_scale) + box_rounding_slop_fixed;
83
14
    bbox.q.y =
84
14
        (fixed) ceil(dbox.q.y * fixed_scale) + box_rounding_slop_fixed;
85
14
    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
12
    } else {     /* empty path *//* Just set the bbox. */
91
12
        ppath->bbox = bbox;
92
12
    }
93
14
    ppath->bbox_set = 1;
94
14
    return 0;
95
22
}
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
355k
{
103
355k
    bool CPSI_mode = gs_currentcpsimode(pgs->memory);
104
105
710k
    for (; count != 0; count--, pr++) {
106
355k
        double px = pr->p.x, py = pr->p.y, qx = pr->q.x, qy = pr->q.y;
107
355k
        int code;
108
109
355k
        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
355k
        } else {
144
            /* Ensure counter-clockwise drawing. */
145
355k
            if ((qx >= px) != (qy >= py))
146
5.27k
                qx = px, px = pr->q.x; /* swap x values */
147
355k
            if ((code = gs_moveto(pgs, px, py)) < 0 ||
148
355k
                (code = gs_lineto(pgs, qx, py)) < 0 ||
149
355k
                (code = gs_lineto(pgs, qx, qy)) < 0 ||
150
355k
                (code = gs_lineto(pgs, px, qy)) < 0 ||
151
355k
                (code = gs_closepath(pgs)) < 0
152
355k
                )
153
0
                return code;
154
355k
        }
155
355k
    }
156
355k
    return 0;
157
355k
}
158
int
159
gs_rectappend(gs_gstate * pgs, const gs_rect * pr, uint count)
160
12.5k
{
161
12.5k
    return gs_rectappend_compat(pgs, pr, count, false);
162
12.5k
}
163
164
/* Clip to a list of rectangles. */
165
int
166
gs_rectclip(gs_gstate * pgs, const gs_rect * pr, uint count)
167
342k
{
168
342k
    int code;
169
342k
    gx_path save;
170
171
342k
    gx_path_init_local(&save, pgs->memory);
172
342k
    gx_path_assign_preserve(&save, pgs->path);
173
342k
    gs_newpath(pgs);
174
342k
    if ((code = gs_rectappend_compat(pgs, pr, count, true)) < 0 ||
175
342k
        (code = gs_clip(pgs)) < 0
176
342k
        ) {
177
0
        gx_path_assign_free(pgs->path, &save);
178
0
        return code;
179
0
    }
180
342k
    gx_path_free(&save, "gs_rectclip");
181
342k
    gs_newpath(pgs);
182
342k
    return 0;
183
342k
}
184
185
/* Setup for black vector handling */
186
static inline bool black_vectors(gs_gstate *pgs, gx_device *dev)
187
50.1k
{
188
50.1k
    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
50.1k
    return false;
193
50.1k
}
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
50.1k
{
200
50.1k
    const gs_rect *rlist = pr;
201
50.1k
    gx_clip_path *pcpath;
202
50.1k
    uint rcount = count;
203
50.1k
    int code;
204
50.1k
    gx_device * pdev = pgs->device;
205
50.1k
    gx_device_color *pdc = gs_currentdevicecolor_inline(pgs);
206
50.1k
    const gs_gstate *pgs2 = (const gs_gstate *)pgs;
207
50.1k
    bool hl_color_available = gx_hld_is_hl_color_available(pgs2, pdc);
208
50.1k
    bool hl_color = (hl_color_available && (dev_proc(pdev, dev_spec_op)(pdev, gxdso_supports_hlcolor, NULL, 0) > 0));
209
50.1k
    bool center_of_pixel = (pgs->fill_adjust.x == 0 && pgs->fill_adjust.y == 0);
210
50.1k
    bool black_vector = false;
211
212
    /* Processing a fill object operation */
213
50.1k
    ensure_tag_is_set(pgs, pgs->device, GS_VECTOR_TAG); /* NB: may unset_dev_color */
214
215
50.1k
    black_vector = black_vectors(pgs, pgs->device); /* Set vector fill to black */
216
217
50.1k
    code = gx_set_dev_color(pgs);
218
50.1k
    if (code != 0)
219
3
        goto exit;
220
221
50.1k
    if ( !(pgs->device->page_uses_transparency ||
222
50.1k
          dev_proc(pgs->device, dev_spec_op)(pgs->device,
223
50.1k
              gxdso_is_pdf14_device, &(pgs->device),
224
50.1k
              sizeof(pgs->device))) &&
225
49.6k
        (is_fzero2(pgs->ctm.xy, pgs->ctm.yx) ||
226
13
         is_fzero2(pgs->ctm.xx, pgs->ctm.yy)) &&
227
49.6k
        gx_effective_clip_path(pgs, &pcpath) >= 0 &&
228
49.6k
        clip_list_is_rectangle(gx_cpath_list(pcpath)) &&
229
49.4k
        (hl_color ||
230
36.6k
         pdc->type == gx_dc_type_pure ||
231
11.8k
         pdc->type == gx_dc_type_ht_binary ||
232
5.82k
         pdc->type == gx_dc_type_ht_colored) &&
233
48.9k
        gs_gstate_color_load(pgs) >= 0 &&
234
48.9k
        (*dev_proc(pdev, get_alpha_bits)) (pdev, go_graphics)
235
48.9k
        <= 1 &&
236
48.9k
        (!pgs->overprint || !gs_currentcolor_eopm(pgs))
237
50.1k
        ) {
238
48.9k
        uint i;
239
48.9k
        gs_fixed_rect clip_rect;
240
241
48.9k
        gx_cpath_inner_box(pcpath, &clip_rect);
242
        /* We should never plot anything for an empty clip rectangle */
243
48.9k
        if ((clip_rect.p.x >= clip_rect.q.x) &&
244
40
            (clip_rect.p.y >= clip_rect.q.y))
245
40
            goto exit;
246
89.1k
        for (i = 0; i < count; ++i) {
247
48.8k
            gs_fixed_point p, q;
248
48.8k
            gs_fixed_rect draw_rect;
249
250
48.8k
            if (gs_point_transform2fixed(&pgs->ctm, pr[i].p.x, pr[i].p.y, &p) < 0 ||
251
46.4k
                gs_point_transform2fixed(&pgs->ctm, pr[i].q.x, pr[i].q.y, &q) < 0
252
48.8k
                ) {   /* Switch to the slow algorithm. */
253
7.92k
                goto slow;
254
7.92k
            }
255
40.9k
            draw_rect.p.x = min(p.x, q.x);
256
40.9k
            draw_rect.p.y = min(p.y, q.y);
257
40.9k
            draw_rect.q.x = max(p.x, q.x);
258
40.9k
            draw_rect.q.y = max(p.y, q.y);
259
40.9k
            if (hl_color) {
260
10.8k
                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
10.8k
                if (draw_rect.p.x <= draw_rect.q.x &&
268
10.3k
                    draw_rect.p.y <= draw_rect.q.y) {
269
10.1k
                    code = dev_proc(pdev, fill_rectangle_hl_color)(pdev,
270
10.1k
                             &draw_rect, pgs2, pdc, pcpath);
271
10.1k
                    if (code < 0)
272
0
                        goto exit;
273
10.1k
                }
274
30.0k
            } else {
275
30.0k
                int x, y, w, h;
276
277
30.0k
                rect_intersect(draw_rect, clip_rect);
278
30.0k
                if (center_of_pixel) {
279
0
                    draw_rect.p.x = fixed_rounded(draw_rect.p.x);
280
0
                    draw_rect.p.y = fixed_rounded(draw_rect.p.y);
281
0
                    draw_rect.q.x = fixed_rounded(draw_rect.q.x);
282
0
                    draw_rect.q.y = fixed_rounded(draw_rect.q.y);
283
30.0k
                } else { /* any part of pixel rule - touched */
284
30.0k
                    draw_rect.p.x = fixed_floor(draw_rect.p.x);
285
30.0k
                    draw_rect.p.y = fixed_floor(draw_rect.p.y);
286
30.0k
                    draw_rect.q.x = fixed_ceiling(draw_rect.q.x);
287
30.0k
                    draw_rect.q.y = fixed_ceiling(draw_rect.q.y);
288
30.0k
                }
289
30.0k
                x = fixed2int(draw_rect.p.x);
290
30.0k
                y = fixed2int(draw_rect.p.y);
291
30.0k
                w = fixed2int(draw_rect.q.x) - x;
292
30.0k
                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
30.0k
                if (!center_of_pixel) {
297
30.0k
                    if (w == 0)
298
5.09k
                        w = 1;
299
                    /* yes Adobe Acrobat 8, seems to back up the y
300
                       coordinate when the width is 0, sigh. */
301
30.0k
                    if (h == 0) {
302
2.43k
                        y--;
303
2.43k
                        h = 1;
304
2.43k
                    }
305
30.0k
                }
306
30.0k
                if (gx_fill_rectangle(x, y, w, h, pdc, pgs) < 0)
307
664
                    goto slow;
308
30.0k
            }
309
40.9k
        }
310
40.3k
        code = 0;
311
40.3k
        goto exit;
312
8.59k
      slow:rlist = pr + i;
313
8.59k
        rcount = count - i;
314
9.80k
    } {
315
9.80k
        bool do_save = !gx_path_is_null(pgs->path);
316
317
9.80k
        if (do_save) {
318
14
            if ((code = gs_gsave(pgs)) < 0)
319
0
                goto exit;
320
14
            code = gs_newpath(pgs);
321
14
        }
322
9.80k
        if ((code >= 0) &&
323
9.80k
            (((code = gs_rectappend(pgs, rlist, rcount)) < 0) ||
324
9.80k
            ((code = gs_fill(pgs)) < 0))
325
9.80k
            )
326
9.80k
            DO_NOTHING;
327
9.80k
        if (do_save)
328
14
            gs_grestore(pgs);
329
9.79k
        else if (code < 0)
330
0
            gs_newpath(pgs);
331
9.80k
    }
332
333
50.1k
exit:
334
50.1k
    if (black_vector) {
335
        /* Restore color */
336
0
        gsicc_restore_blacktextvec(pgs, false);
337
0
    }
338
50.1k
    return code;
339
9.80k
}
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.76k
{
347
2.76k
    bool do_save = pmat != NULL || !gx_path_is_null(pgs->path);
348
2.76k
    int code;
349
350
2.76k
    if (do_save) {
351
40
        if ((code = gs_gsave(pgs)) < 0)
352
0
            return code;
353
40
        gs_newpath(pgs);
354
40
    }
355
2.76k
    if ((code = gs_rectappend(pgs, pr, count)) < 0 ||
356
2.76k
        (pmat != NULL && (code = gs_concat(pgs, pmat)) < 0) ||
357
2.76k
        (code = gs_stroke(pgs)) < 0
358
2.76k
        )
359
2.76k
        DO_NOTHING;
360
2.76k
    if (do_save)
361
40
        gs_grestore(pgs);
362
2.72k
    else if (code < 0)
363
0
        gs_newpath(pgs);
364
2.76k
    return code;
365
2.76k
}