Coverage Report

Created: 2025-06-10 07:27

/src/ghostpdl/base/gsdps1.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
/* 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
24
#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
19
{
54
19
    gs_rect ubox, dbox;
55
19
    gs_fixed_rect obox, bbox;
56
19
    gx_path *ppath = pgs->path;
57
19
    int code;
58
59
19
    if (llx > urx || lly > ury)
60
8
        return_error(gs_error_rangecheck);
61
    /* Transform box to device coordinates. */
62
11
    ubox.p.x = llx;
63
11
    ubox.p.y = lly;
64
11
    ubox.q.x = urx;
65
11
    ubox.q.y = ury;
66
11
    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
11
    if (dbox.p.x < fixed2float(min_fixed + box_rounding_slop_fixed) ||
72
11
        dbox.p.y < fixed2float(min_fixed + box_rounding_slop_fixed) ||
73
11
        dbox.q.x >= fixed2float(max_fixed - box_rounding_slop_fixed + fixed_epsilon) ||
74
11
        dbox.q.y >= fixed2float(max_fixed - box_rounding_slop_fixed + fixed_epsilon)
75
11
        )
76
5
        return_error(gs_error_limitcheck);
77
6
    bbox.p.x =
78
6
        (fixed) floor(dbox.p.x * fixed_scale) - box_rounding_slop_fixed;
79
6
    bbox.p.y =
80
6
        (fixed) floor(dbox.p.y * fixed_scale) - box_rounding_slop_fixed;
81
6
    bbox.q.x =
82
6
        (fixed) ceil(dbox.q.x * fixed_scale) + box_rounding_slop_fixed;
83
6
    bbox.q.y =
84
6
        (fixed) ceil(dbox.q.y * fixed_scale) + box_rounding_slop_fixed;
85
6
    if (gx_path_bbox_set(ppath, &obox) >= 0) { /* Take the union of the bboxes. */
86
0
        ppath->bbox.p.x = min(obox.p.x, bbox.p.x);
87
0
        ppath->bbox.p.y = min(obox.p.y, bbox.p.y);
88
0
        ppath->bbox.q.x = max(obox.q.x, bbox.q.x);
89
0
        ppath->bbox.q.y = max(obox.q.y, bbox.q.y);
90
6
    } else {     /* empty path *//* Just set the bbox. */
91
6
        ppath->bbox = bbox;
92
6
    }
93
6
    ppath->bbox_set = 1;
94
6
    return 0;
95
11
}
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
322k
{
103
322k
    bool CPSI_mode = gs_currentcpsimode(pgs->memory);
104
105
645k
    for (; count != 0; count--, pr++) {
106
322k
        double px = pr->p.x, py = pr->p.y, qx = pr->q.x, qy = pr->q.y;
107
322k
        int code;
108
109
322k
        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
322k
        } else {
144
            /* Ensure counter-clockwise drawing. */
145
322k
            if ((qx >= px) != (qy >= py))
146
8.63k
                qx = px, px = pr->q.x; /* swap x values */
147
322k
            if ((code = gs_moveto(pgs, px, py)) < 0 ||
148
322k
                (code = gs_lineto(pgs, qx, py)) < 0 ||
149
322k
                (code = gs_lineto(pgs, qx, qy)) < 0 ||
150
322k
                (code = gs_lineto(pgs, px, qy)) < 0 ||
151
322k
                (code = gs_closepath(pgs)) < 0
152
322k
                )
153
0
                return code;
154
322k
        }
155
322k
    }
156
322k
    return 0;
157
322k
}
158
int
159
gs_rectappend(gs_gstate * pgs, const gs_rect * pr, uint count)
160
10.5k
{
161
10.5k
    return gs_rectappend_compat(pgs, pr, count, false);
162
10.5k
}
163
164
/* Clip to a list of rectangles. */
165
int
166
gs_rectclip(gs_gstate * pgs, const gs_rect * pr, uint count)
167
312k
{
168
312k
    int code;
169
312k
    gx_path save;
170
171
312k
    gx_path_init_local(&save, pgs->memory);
172
312k
    gx_path_assign_preserve(&save, pgs->path);
173
312k
    gs_newpath(pgs);
174
312k
    if ((code = gs_rectappend_compat(pgs, pr, count, true)) < 0 ||
175
312k
        (code = gs_clip(pgs)) < 0
176
312k
        ) {
177
0
        gx_path_assign_free(pgs->path, &save);
178
0
        return code;
179
0
    }
180
312k
    gx_path_free(&save, "gs_rectclip");
181
312k
    gs_newpath(pgs);
182
312k
    return 0;
183
312k
}
184
185
/* Setup for black vector handling */
186
static inline bool black_vectors(gs_gstate *pgs, gx_device *dev)
187
42.0k
{
188
42.0k
    if (dev->icc_struct != NULL && dev->icc_struct->blackvector &&
189
42.0k
        pgs->black_textvec_state == NULL) {
190
0
        return gsicc_setup_blacktextvec(pgs, dev, false);
191
0
    }
192
42.0k
    return false;
193
42.0k
}
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
42.0k
{
200
42.0k
    const gs_rect *rlist = pr;
201
42.0k
    gx_clip_path *pcpath;
202
42.0k
    uint rcount = count;
203
42.0k
    int code;
204
42.0k
    gx_device * pdev = pgs->device;
205
42.0k
    gx_device_color *pdc = gs_currentdevicecolor_inline(pgs);
206
42.0k
    const gs_gstate *pgs2 = (const gs_gstate *)pgs;
207
42.0k
    bool hl_color_available = gx_hld_is_hl_color_available(pgs2, pdc);
208
42.0k
    bool hl_color = (hl_color_available &&
209
42.0k
                dev_proc(pdev, dev_spec_op)(pdev, gxdso_supports_hlcolor,
210
42.0k
                                  NULL, 0));
211
42.0k
    bool center_of_pixel = (pgs->fill_adjust.x == 0 && pgs->fill_adjust.y == 0);
212
42.0k
    bool black_vector = false;
213
214
    /* Processing a fill object operation */
215
42.0k
    ensure_tag_is_set(pgs, pgs->device, GS_VECTOR_TAG); /* NB: may unset_dev_color */
216
217
42.0k
    black_vector = black_vectors(pgs, pgs->device); /* Set vector fill to black */
218
219
42.0k
    code = gx_set_dev_color(pgs);
220
42.0k
    if (code != 0)
221
3
        goto exit;
222
223
42.0k
    if ( !(pgs->device->page_uses_transparency ||
224
42.0k
          dev_proc(pgs->device, dev_spec_op)(pgs->device,
225
42.0k
              gxdso_is_pdf14_device, &(pgs->device),
226
42.0k
              sizeof(pgs->device))) &&
227
42.0k
        (is_fzero2(pgs->ctm.xy, pgs->ctm.yx) ||
228
41.6k
         is_fzero2(pgs->ctm.xx, pgs->ctm.yy)) &&
229
42.0k
        gx_effective_clip_path(pgs, &pcpath) >= 0 &&
230
42.0k
        clip_list_is_rectangle(gx_cpath_list(pcpath)) &&
231
42.0k
        (hl_color ||
232
41.5k
         pdc->type == gx_dc_type_pure ||
233
41.5k
         pdc->type == gx_dc_type_ht_binary ||
234
41.5k
         pdc->type == gx_dc_type_ht_colored) &&
235
42.0k
        gs_gstate_color_load(pgs) >= 0 &&
236
42.0k
        (*dev_proc(pdev, get_alpha_bits)) (pdev, go_graphics)
237
41.2k
        <= 1 &&
238
42.0k
        (!pgs->overprint || !gs_currentcolor_eopm(pgs))
239
42.0k
        ) {
240
41.2k
        uint i;
241
41.2k
        gs_fixed_rect clip_rect;
242
243
41.2k
        gx_cpath_inner_box(pcpath, &clip_rect);
244
        /* We should never plot anything for an empty clip rectangle */
245
41.2k
        if ((clip_rect.p.x >= clip_rect.q.x) &&
246
41.2k
            (clip_rect.p.y >= clip_rect.q.y))
247
24
            goto exit;
248
74.6k
        for (i = 0; i < count; ++i) {
249
41.2k
            gs_fixed_point p, q;
250
41.2k
            gs_fixed_rect draw_rect;
251
252
41.2k
            if (gs_point_transform2fixed(&pgs->ctm, pr[i].p.x, pr[i].p.y, &p) < 0 ||
253
41.2k
                gs_point_transform2fixed(&pgs->ctm, pr[i].q.x, pr[i].q.y, &q) < 0
254
41.2k
                ) {   /* Switch to the slow algorithm. */
255
7.28k
                goto slow;
256
7.28k
            }
257
33.9k
            draw_rect.p.x = min(p.x, q.x);
258
33.9k
            draw_rect.p.y = min(p.y, q.y);
259
33.9k
            draw_rect.q.x = max(p.x, q.x);
260
33.9k
            draw_rect.q.y = max(p.y, q.y);
261
33.9k
            if (hl_color) {
262
7.98k
                rect_intersect(draw_rect, clip_rect);
263
                /* We do pass on 0 extant rectangles to high level
264
                   devices.  It isn't clear how a client and an output
265
                   device should interact if one uses a center of
266
                   pixel algorithm and the other uses any part of
267
                   pixel.  For now we punt and just pass the high
268
                   level rectangle on without adjustment. */
269
7.98k
                if (draw_rect.p.x <= draw_rect.q.x &&
270
7.98k
                    draw_rect.p.y <= draw_rect.q.y) {
271
7.31k
                    code = dev_proc(pdev, fill_rectangle_hl_color)(pdev,
272
7.31k
                             &draw_rect, pgs2, pdc, pcpath);
273
7.31k
                    if (code < 0)
274
0
                        goto exit;
275
7.31k
                }
276
25.9k
            } else {
277
25.9k
                int x, y, w, h;
278
279
25.9k
                rect_intersect(draw_rect, clip_rect);
280
25.9k
                if (center_of_pixel) {
281
0
                    draw_rect.p.x = fixed_rounded(draw_rect.p.x);
282
0
                    draw_rect.p.y = fixed_rounded(draw_rect.p.y);
283
0
                    draw_rect.q.x = fixed_rounded(draw_rect.q.x);
284
0
                    draw_rect.q.y = fixed_rounded(draw_rect.q.y);
285
25.9k
                } else { /* any part of pixel rule - touched */
286
25.9k
                    draw_rect.p.x = fixed_floor(draw_rect.p.x);
287
25.9k
                    draw_rect.p.y = fixed_floor(draw_rect.p.y);
288
25.9k
                    draw_rect.q.x = fixed_ceiling(draw_rect.q.x);
289
25.9k
                    draw_rect.q.y = fixed_ceiling(draw_rect.q.y);
290
25.9k
                }
291
25.9k
                x = fixed2int(draw_rect.p.x);
292
25.9k
                y = fixed2int(draw_rect.p.y);
293
25.9k
                w = fixed2int(draw_rect.q.x) - x;
294
25.9k
                h = fixed2int(draw_rect.q.y) - y;
295
                /* clients that use the "any part of pixel" rule also
296
                   fill 0 areas.  This is true of current graphics
297
                   library clients but not a general rule.  */
298
25.9k
                if (!center_of_pixel) {
299
25.9k
                    if (w == 0)
300
5.39k
                        w = 1;
301
                    /* yes Adobe Acrobat 8, seems to back up the y
302
                       coordinate when the width is 0, sigh. */
303
25.9k
                    if (h == 0) {
304
2.44k
                        y--;
305
2.44k
                        h = 1;
306
2.44k
                    }
307
25.9k
                }
308
25.9k
                if (gx_fill_rectangle(x, y, w, h, pdc, pgs) < 0)
309
593
                    goto slow;
310
25.9k
            }
311
33.9k
        }
312
33.3k
        code = 0;
313
33.3k
        goto exit;
314
7.87k
      slow:rlist = pr + i;
315
7.87k
        rcount = count - i;
316
8.61k
    } {
317
8.61k
        bool do_save = !gx_path_is_null(pgs->path);
318
319
8.61k
        if (do_save) {
320
6
            if ((code = gs_gsave(pgs)) < 0)
321
0
                goto exit;
322
6
            code = gs_newpath(pgs);
323
6
        }
324
8.61k
        if ((code >= 0) &&
325
8.61k
            (((code = gs_rectappend(pgs, rlist, rcount)) < 0) ||
326
8.61k
            ((code = gs_fill(pgs)) < 0))
327
8.61k
            )
328
8.61k
            DO_NOTHING;
329
8.61k
        if (do_save)
330
6
            gs_grestore(pgs);
331
8.61k
        else if (code < 0)
332
0
            gs_newpath(pgs);
333
8.61k
    }
334
335
42.0k
exit:
336
42.0k
    if (black_vector) {
337
        /* Restore color */
338
0
        gsicc_restore_blacktextvec(pgs, false);
339
0
    }
340
42.0k
    return code;
341
8.61k
}
342
343
/* Stroke a list of rectangles. */
344
/* (We could do this a lot more efficiently.) */
345
int
346
gs_rectstroke(gs_gstate * pgs, const gs_rect * pr, uint count,
347
              const gs_matrix * pmat)
348
1.91k
{
349
1.91k
    bool do_save = pmat != NULL || !gx_path_is_null(pgs->path);
350
1.91k
    int code;
351
352
1.91k
    if (do_save) {
353
14
        if ((code = gs_gsave(pgs)) < 0)
354
0
            return code;
355
14
        gs_newpath(pgs);
356
14
    }
357
1.91k
    if ((code = gs_rectappend(pgs, pr, count)) < 0 ||
358
1.91k
        (pmat != NULL && (code = gs_concat(pgs, pmat)) < 0) ||
359
1.91k
        (code = gs_stroke(pgs)) < 0
360
1.91k
        )
361
1.91k
        DO_NOTHING;
362
1.91k
    if (do_save)
363
14
        gs_grestore(pgs);
364
1.90k
    else if (code < 0)
365
0
        gs_newpath(pgs);
366
1.91k
    return code;
367
1.91k
}