Coverage Report

Created: 2025-11-16 07:40

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ghostpdl/base/gsdps1.c
Line
Count
Source
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
22
{
54
22
    gs_rect ubox, dbox;
55
22
    gs_fixed_rect obox, bbox;
56
22
    gx_path *ppath = pgs->path;
57
22
    int code;
58
59
22
    if (llx > urx || lly > ury)
60
8
        return_error(gs_error_rangecheck);
61
    /* Transform box to device coordinates. */
62
14
    ubox.p.x = llx;
63
14
    ubox.p.y = lly;
64
14
    ubox.q.x = urx;
65
14
    ubox.q.y = ury;
66
14
    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
14
    if (dbox.p.x < fixed2float(min_fixed + box_rounding_slop_fixed) ||
72
12
        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
9
        dbox.q.y >= fixed2float(max_fixed - box_rounding_slop_fixed + fixed_epsilon)
75
14
        )
76
6
        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
1
        ppath->bbox.p.x = min(obox.p.x, bbox.p.x);
87
1
        ppath->bbox.p.y = min(obox.p.y, bbox.p.y);
88
1
        ppath->bbox.q.x = max(obox.q.x, bbox.q.x);
89
1
        ppath->bbox.q.y = max(obox.q.y, bbox.q.y);
90
7
    } else {     /* empty path *//* Just set the bbox. */
91
7
        ppath->bbox = bbox;
92
7
    }
93
8
    ppath->bbox_set = 1;
94
8
    return 0;
95
14
}
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
503k
{
103
503k
    bool CPSI_mode = gs_currentcpsimode(pgs->memory);
104
105
1.00M
    for (; count != 0; count--, pr++) {
106
503k
        double px = pr->p.x, py = pr->p.y, qx = pr->q.x, qy = pr->q.y;
107
503k
        int code;
108
109
503k
        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
503k
        } else {
144
            /* Ensure counter-clockwise drawing. */
145
503k
            if ((qx >= px) != (qy >= py))
146
11.2k
                qx = px, px = pr->q.x; /* swap x values */
147
503k
            if ((code = gs_moveto(pgs, px, py)) < 0 ||
148
503k
                (code = gs_lineto(pgs, qx, py)) < 0 ||
149
503k
                (code = gs_lineto(pgs, qx, qy)) < 0 ||
150
503k
                (code = gs_lineto(pgs, px, qy)) < 0 ||
151
503k
                (code = gs_closepath(pgs)) < 0
152
503k
                )
153
0
                return code;
154
503k
        }
155
503k
    }
156
503k
    return 0;
157
503k
}
158
int
159
gs_rectappend(gs_gstate * pgs, const gs_rect * pr, uint count)
160
14.6k
{
161
14.6k
    return gs_rectappend_compat(pgs, pr, count, false);
162
14.6k
}
163
164
/* Clip to a list of rectangles. */
165
int
166
gs_rectclip(gs_gstate * pgs, const gs_rect * pr, uint count)
167
489k
{
168
489k
    int code;
169
489k
    gx_path save;
170
171
489k
    gx_path_init_local(&save, pgs->memory);
172
489k
    gx_path_assign_preserve(&save, pgs->path);
173
489k
    gs_newpath(pgs);
174
489k
    if ((code = gs_rectappend_compat(pgs, pr, count, true)) < 0 ||
175
489k
        (code = gs_clip(pgs)) < 0
176
489k
        ) {
177
0
        gx_path_assign_free(pgs->path, &save);
178
0
        return code;
179
0
    }
180
489k
    gx_path_free(&save, "gs_rectclip");
181
489k
    gs_newpath(pgs);
182
489k
    return 0;
183
489k
}
184
185
/* Setup for black vector handling */
186
static inline bool black_vectors(gs_gstate *pgs, gx_device *dev)
187
47.0k
{
188
47.0k
    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
47.0k
    return false;
193
47.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
47.0k
{
200
47.0k
    const gs_rect *rlist = pr;
201
47.0k
    gx_clip_path *pcpath;
202
47.0k
    uint rcount = count;
203
47.0k
    int code;
204
47.0k
    gx_device * pdev = pgs->device;
205
47.0k
    gx_device_color *pdc = gs_currentdevicecolor_inline(pgs);
206
47.0k
    const gs_gstate *pgs2 = (const gs_gstate *)pgs;
207
47.0k
    bool hl_color_available = gx_hld_is_hl_color_available(pgs2, pdc);
208
47.0k
    bool hl_color = (hl_color_available &&
209
47.0k
                dev_proc(pdev, dev_spec_op)(pdev, gxdso_supports_hlcolor,
210
47.0k
                                  NULL, 0));
211
47.0k
    bool center_of_pixel = (pgs->fill_adjust.x == 0 && pgs->fill_adjust.y == 0);
212
47.0k
    bool black_vector = false;
213
214
    /* Processing a fill object operation */
215
47.0k
    ensure_tag_is_set(pgs, pgs->device, GS_VECTOR_TAG); /* NB: may unset_dev_color */
216
217
47.0k
    black_vector = black_vectors(pgs, pgs->device); /* Set vector fill to black */
218
219
47.0k
    code = gx_set_dev_color(pgs);
220
47.0k
    if (code != 0)
221
3
        goto exit;
222
223
47.0k
    if ( !(pgs->device->page_uses_transparency ||
224
47.0k
          dev_proc(pgs->device, dev_spec_op)(pgs->device,
225
47.0k
              gxdso_is_pdf14_device, &(pgs->device),
226
47.0k
              sizeof(pgs->device))) &&
227
46.2k
        (is_fzero2(pgs->ctm.xy, pgs->ctm.yx) ||
228
12
         is_fzero2(pgs->ctm.xx, pgs->ctm.yy)) &&
229
46.1k
        gx_effective_clip_path(pgs, &pcpath) >= 0 &&
230
46.1k
        clip_list_is_rectangle(gx_cpath_list(pcpath)) &&
231
46.0k
        (hl_color ||
232
40.2k
         pdc->type == gx_dc_type_pure ||
233
15.0k
         pdc->type == gx_dc_type_ht_binary ||
234
9.01k
         pdc->type == gx_dc_type_ht_colored) &&
235
43.9k
        gs_gstate_color_load(pgs) >= 0 &&
236
43.9k
        (*dev_proc(pdev, get_alpha_bits)) (pdev, go_graphics)
237
43.9k
        <= 1 &&
238
43.9k
        (!pgs->overprint || !gs_currentcolor_eopm(pgs))
239
47.0k
        ) {
240
43.9k
        uint i;
241
43.9k
        gs_fixed_rect clip_rect;
242
243
43.9k
        gx_cpath_inner_box(pcpath, &clip_rect);
244
        /* We should never plot anything for an empty clip rectangle */
245
43.9k
        if ((clip_rect.p.x >= clip_rect.q.x) &&
246
43
            (clip_rect.p.y >= clip_rect.q.y))
247
43
            goto exit;
248
79.7k
        for (i = 0; i < count; ++i) {
249
43.8k
            gs_fixed_point p, q;
250
43.8k
            gs_fixed_rect draw_rect;
251
252
43.8k
            if (gs_point_transform2fixed(&pgs->ctm, pr[i].p.x, pr[i].p.y, &p) < 0 ||
253
41.6k
                gs_point_transform2fixed(&pgs->ctm, pr[i].q.x, pr[i].q.y, &q) < 0
254
43.8k
                ) {   /* Switch to the slow algorithm. */
255
7.37k
                goto slow;
256
7.37k
            }
257
36.4k
            draw_rect.p.x = min(p.x, q.x);
258
36.4k
            draw_rect.p.y = min(p.y, q.y);
259
36.4k
            draw_rect.q.x = max(p.x, q.x);
260
36.4k
            draw_rect.q.y = max(p.y, q.y);
261
36.4k
            if (hl_color) {
262
4.93k
                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
4.93k
                if (draw_rect.p.x <= draw_rect.q.x &&
270
4.75k
                    draw_rect.p.y <= draw_rect.q.y) {
271
4.67k
                    code = dev_proc(pdev, fill_rectangle_hl_color)(pdev,
272
4.67k
                             &draw_rect, pgs2, pdc, pcpath);
273
4.67k
                    if (code < 0)
274
0
                        goto exit;
275
4.67k
                }
276
31.5k
            } else {
277
31.5k
                int x, y, w, h;
278
279
31.5k
                rect_intersect(draw_rect, clip_rect);
280
31.5k
                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
31.5k
                } else { /* any part of pixel rule - touched */
286
31.5k
                    draw_rect.p.x = fixed_floor(draw_rect.p.x);
287
31.5k
                    draw_rect.p.y = fixed_floor(draw_rect.p.y);
288
31.5k
                    draw_rect.q.x = fixed_ceiling(draw_rect.q.x);
289
31.5k
                    draw_rect.q.y = fixed_ceiling(draw_rect.q.y);
290
31.5k
                }
291
31.5k
                x = fixed2int(draw_rect.p.x);
292
31.5k
                y = fixed2int(draw_rect.p.y);
293
31.5k
                w = fixed2int(draw_rect.q.x) - x;
294
31.5k
                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
31.5k
                if (!center_of_pixel) {
299
31.5k
                    if (w == 0)
300
5.96k
                        w = 1;
301
                    /* yes Adobe Acrobat 8, seems to back up the y
302
                       coordinate when the width is 0, sigh. */
303
31.5k
                    if (h == 0) {
304
2.97k
                        y--;
305
2.97k
                        h = 1;
306
2.97k
                    }
307
31.5k
                }
308
31.5k
                if (gx_fill_rectangle(x, y, w, h, pdc, pgs) < 0)
309
635
                    goto slow;
310
31.5k
            }
311
36.4k
        }
312
35.8k
        code = 0;
313
35.8k
        goto exit;
314
8.01k
      slow:rlist = pr + i;
315
8.01k
        rcount = count - i;
316
11.1k
    } {
317
11.1k
        bool do_save = !gx_path_is_null(pgs->path);
318
319
11.1k
        if (do_save) {
320
13
            if ((code = gs_gsave(pgs)) < 0)
321
0
                goto exit;
322
13
            code = gs_newpath(pgs);
323
13
        }
324
11.1k
        if ((code >= 0) &&
325
11.1k
            (((code = gs_rectappend(pgs, rlist, rcount)) < 0) ||
326
11.1k
            ((code = gs_fill(pgs)) < 0))
327
11.1k
            )
328
11.1k
            DO_NOTHING;
329
11.1k
        if (do_save)
330
13
            gs_grestore(pgs);
331
11.1k
        else if (code < 0)
332
0
            gs_newpath(pgs);
333
11.1k
    }
334
335
47.0k
exit:
336
47.0k
    if (black_vector) {
337
        /* Restore color */
338
0
        gsicc_restore_blacktextvec(pgs, false);
339
0
    }
340
47.0k
    return code;
341
11.1k
}
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
3.48k
{
349
3.48k
    bool do_save = pmat != NULL || !gx_path_is_null(pgs->path);
350
3.48k
    int code;
351
352
3.48k
    if (do_save) {
353
22
        if ((code = gs_gsave(pgs)) < 0)
354
0
            return code;
355
22
        gs_newpath(pgs);
356
22
    }
357
3.48k
    if ((code = gs_rectappend(pgs, pr, count)) < 0 ||
358
3.48k
        (pmat != NULL && (code = gs_concat(pgs, pmat)) < 0) ||
359
3.48k
        (code = gs_stroke(pgs)) < 0
360
3.48k
        )
361
3.48k
        DO_NOTHING;
362
3.48k
    if (do_save)
363
22
        gs_grestore(pgs);
364
3.46k
    else if (code < 0)
365
0
        gs_newpath(pgs);
366
3.48k
    return code;
367
3.48k
}