Coverage Report

Created: 2025-06-10 07:26

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