Coverage Report

Created: 2025-06-24 07:01

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