Coverage Report

Created: 2022-10-31 07:00

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