Coverage Report

Created: 2025-06-10 06:59

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