Coverage Report

Created: 2025-08-28 07:06

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