Coverage Report

Created: 2025-06-10 07:17

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