Coverage Report

Created: 2026-07-24 07:44

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ghostpdl/base/gxpdash.c
Line
Count
Source
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
/* Dash expansion for paths */
18
#include "math_.h"
19
#include "gx.h"
20
#include "gsmatrix.h"   /* for gscoord.h */
21
#include "gscoord.h"
22
#include "gxfixed.h"
23
#include "gxarith.h"
24
#include "gxgstate.h"
25
#include "gsline.h"
26
#include "gzline.h"
27
#include "gzpath.h"
28
29
/* Expand a dashed path into explicit segments. */
30
/* The path contains no curves. */
31
static int subpath_expand_dashes(const subpath *, gx_path *,
32
                                  const gs_gstate *,
33
                                  const gx_dash_params *);
34
int
35
gx_path_add_dash_expansion(const gx_path * ppath_old, gx_path * ppath,
36
                           const gs_gstate * pgs)
37
21.9k
{
38
21.9k
    const subpath *psub;
39
21.9k
    const gx_dash_params *dash = &gs_currentlineparams(pgs)->dash;
40
21.9k
    int code = 0;
41
42
21.9k
    if (dash->pattern_size == 0)
43
0
        return gx_path_copy(ppath_old, ppath);
44
45.5k
    for (psub = ppath_old->first_subpath; psub != 0 && code >= 0;
45
23.6k
         psub = (const subpath *)psub->last->next
46
21.9k
        )
47
23.6k
        code = subpath_expand_dashes(psub, ppath, pgs, dash);
48
21.9k
    return code;
49
21.9k
}
50
51
static int
52
subpath_expand_dashes(const subpath * psub, gx_path * ppath,
53
                   const gs_gstate * pgs, const gx_dash_params * dash)
54
23.6k
{
55
23.6k
    const float *pattern = dash->pattern;
56
23.6k
    int count, index;
57
23.6k
    bool ink_on;
58
23.6k
    double elt_length;
59
23.6k
    fixed x0 = psub->pt.x, y0 = psub->pt.y;
60
23.6k
    fixed x, y;
61
23.6k
    const segment *pseg;
62
23.6k
    int wrap = (dash->init_ink_on && psub->is_closed ? -1 : 0);
63
23.6k
    int drawing = wrap;
64
23.6k
    segment_notes notes = ~sn_not_first;
65
23.6k
    const gx_line_params *pgs_lp = gs_currentlineparams_inline(pgs);
66
23.6k
    bool zero_length = true;
67
23.6k
    int code;
68
23.6k
    gs_line_cap cap;
69
23.6k
    segment_notes start_notes, end_notes;
70
71
23.6k
    if (wrap) {
72
        /* If we are wrapping around, then we use dash caps throughout */
73
3.80k
        cap         = pgs_lp->dash_cap;
74
3.80k
        start_notes = sn_dash_head;
75
19.8k
    } else {
76
        /* Otherwise, start off with a start cap */
77
19.8k
        cap         = pgs_lp->start_cap;
78
19.8k
        start_notes = 0;
79
19.8k
    }
80
81
23.6k
    if ((code = gx_path_add_point(ppath, x0, y0)) < 0)
82
0
        return code;
83
    /*
84
     * To do the right thing at the beginning of a closed path, we have
85
     * to skip any initial line, and then redo it at the end of the
86
     * path.  Drawing = -1 while skipping, 0 while drawing normally, and
87
     * 1 on the second round.  Note that drawing != 0 implies ink_on.
88
     */
89
27.4k
  top:count = dash->pattern_size;
90
27.4k
    ink_on = dash->init_ink_on;
91
27.4k
    index = dash->init_index;
92
27.4k
    elt_length = dash->init_dist_left;
93
27.4k
    x = x0, y = y0;
94
27.4k
    pseg = (const segment *)psub;
95
227k
    while ((pseg = pseg->next) != 0 && pseg->type != s_start) {
96
202k
        fixed sx = pseg->pt.x, sy = pseg->pt.y;
97
202k
        fixed udx = sx - x, udy = sy - y;
98
202k
        double length, dx, dy;
99
202k
        double scale = 1;
100
202k
        double left;
101
202k
        int gap = pseg->type == s_gap;
102
103
202k
        if (!(udx | udy)) { /* degenerate */
104
7.46k
            if (pgs_lp->dot_length == 0 &&
105
7.46k
                cap != gs_cap_round) {
106
                /* From PLRM, stroke operator :
107
                   If a subpath is degenerate (consists of a single-point closed path
108
                   or of two or more points at the same coordinates),
109
                   stroke paints it only if round line caps have been specified */
110
7.25k
                if (zero_length || pseg->type != s_line_close)
111
7.15k
                    continue;
112
7.25k
            }
113
312
            dx = 0, dy = 0, length = 0;
114
195k
        } else {
115
195k
            gs_point d;
116
117
195k
            zero_length = false;
118
195k
            dx = udx, dy = udy; /* scaled as fixed */
119
195k
            code = gs_gstate_idtransform(pgs, dx, dy, &d);
120
195k
            if (code < 0) {
121
0
                d.x = 0; d.y = 0;
122
                /* Swallow the error */
123
0
                code = 0;
124
0
            }
125
195k
            length = hypot(d.x, d.y) * (1.0 / fixed_1);
126
195k
            if (gs_gstate_currentdashadapt(pgs)) {
127
0
                double reps = length / dash->pattern_length;
128
129
0
                scale = reps / ceil(reps);
130
                /* Ensure we're starting at the start of a */
131
                /* repetition.  (This shouldn't be necessary, */
132
                /* but it is.) */
133
0
                count = dash->pattern_size;
134
0
                ink_on = dash->init_ink_on;
135
0
                index = dash->init_index;
136
0
                elt_length = dash->init_dist_left * scale;
137
0
            }
138
195k
        }
139
195k
        left = length;
140
17.2M
        while (left > elt_length) { /* We are using up the line segment. */
141
17.0M
            double fraction = elt_length / length;
142
17.0M
            fixed fx = (fixed) (dx * fraction);
143
17.0M
            fixed fy = (fixed) (dy * fraction);
144
17.0M
            fixed nx = x + fx;
145
17.0M
            fixed ny = y + fy;
146
147
17.0M
            if (ink_on && !gap) {
148
8.51M
                if (drawing >= 0) {
149
8.51M
                    if (left >= elt_length && any_abs(fx) + any_abs(fy) < fixed_half)
150
5.97M
                        code = gx_path_add_dash_notes(ppath, nx, ny, udx, udy,
151
5.97M
                                                      ((notes & pseg->notes)|
152
5.97M
                                                       start_notes|
153
5.97M
                                                       sn_dash_tail));
154
2.53M
                    else
155
2.53M
                        code = gx_path_add_line_notes(ppath, nx, ny,
156
2.53M
                                                      ((notes & pseg->notes)|
157
2.53M
                                                       start_notes|
158
2.53M
                                                       sn_dash_tail));
159
8.51M
                }
160
8.51M
                notes |= sn_not_first;
161
8.51M
            } else {
162
8.50M
                if (drawing > 0)  /* done */
163
2.60k
                    return 0;
164
8.50M
                code = gx_path_add_point(ppath, nx, ny);
165
8.50M
                notes &= ~sn_not_first;
166
8.50M
                drawing = 0;
167
8.50M
            }
168
17.0M
            if (code < 0)
169
1
                return code;
170
17.0M
            left -= elt_length;
171
17.0M
            ink_on = !ink_on;
172
17.0M
            start_notes = sn_dash_head;
173
17.0M
            if (++index == count)
174
7.56M
                index = 0;
175
17.0M
            elt_length = pattern[index] * scale;
176
17.0M
            x = nx, y = ny;
177
17.0M
        }
178
192k
        elt_length -= left;
179
        /* Handle the last dash of a segment. */
180
192k
        if (wrap) {
181
            /* We are wrapping, therefore we always use the dash cap */
182
13.9k
            end_notes = sn_dash_tail;
183
178k
        } else {
184
            /* Look ahead to see if we have any more non-degenerate segments
185
             * before the next move or end of subpath. (i.e. should we use an
186
             * end cap or a dash cap?) */
187
178k
            const segment *pseg2 = pseg->next;
188
189
178k
            end_notes = 0;
190
178k
            while (pseg2 != 0 && pseg2->type != s_start)
191
159k
            {
192
159k
                if ((pseg2->pt.x != sx) || (pseg2->pt.x != sy)) {
193
                    /* Non degenerate. We aren't the last one */
194
159k
                    end_notes = sn_dash_tail;
195
159k
                    break;
196
159k
                }
197
0
                pseg2 = pseg2->next;
198
0
            }
199
178k
        }
200
193k
      on:if (ink_on && !gap) {
201
90.7k
            if (drawing >= 0) {
202
89.5k
                if (pseg->type == s_line_close && drawing > 0)
203
590
                    code = gx_path_close_subpath_notes(ppath,
204
590
                                                       ((notes & pseg->notes)|
205
590
                                                        start_notes |
206
590
                                                        end_notes));
207
88.9k
                else if ((any_abs(sx - x) + any_abs(sy - y) < fixed_half) &&
208
2.89k
                         (udx | udy))
209
                    /* If we only need to move a short distance, then output
210
                     * dash notes to ensure that the stroke tangent remains
211
                     * accurate. There is no point in outputting such dash
212
                     * notes if we don't have any useful information to put
213
                     * in the note though (if udx == 0 && udy == 0). */
214
2.68k
                    code = gx_path_add_dash_notes(ppath, sx, sy, udx, udy,
215
2.68k
                                                  ((notes & pseg->notes)|
216
2.68k
                                                   start_notes | end_notes));
217
86.2k
                else
218
86.2k
                    code = gx_path_add_line_notes(ppath, sx, sy,
219
86.2k
                                                  ((notes & pseg->notes)|
220
86.2k
                                                   start_notes | end_notes));
221
89.5k
                notes |= sn_not_first;
222
89.5k
            }
223
102k
        } else {
224
102k
            code = gx_path_add_point(ppath, sx, sy);
225
102k
            notes &= ~sn_not_first;
226
102k
            if (elt_length < fixed2float(fixed_epsilon) &&
227
284
                (pseg->next == 0 ||
228
216
                 pseg->next->type == s_start ||
229
9
                 pseg->next->type == s_gap ||
230
284
                 elt_length == 0)) {
231
                /*
232
                 * Ink is off, but we're within epsilon of the end
233
                 * of the dash element.
234
                 * "Stretch" a little so we get a dot.
235
                 * Also if the next dash pattern is zero length,
236
                 * use the last segment orientation.
237
                 */
238
284
                double elt_length1;
239
240
284
                if (code < 0)
241
0
                    return code;
242
284
                if (++index == count)
243
284
                    index = 0;
244
284
                elt_length1 = pattern[index] * scale;
245
284
                if (pseg->next == 0 ||
246
216
                    pseg->next->type == s_start ||
247
275
                    pseg->next->type == s_gap) {
248
275
                    elt_length = elt_length1;
249
275
                    left = 0;
250
275
                    ink_on = true;
251
275
                    goto on;
252
275
                }
253
                /* Looking ahead one dash pattern element.
254
                   If it is zero length, apply to the current segment
255
                   (at its end). */
256
9
                if (elt_length1 == 0) {
257
0
                    left = 0;
258
0
                    code = gx_path_add_dash_notes(ppath, sx, sy, udx, udy,
259
0
                                                  ((notes & pseg->notes)|
260
0
                                                  start_notes | end_notes));
261
0
                    if (++index == count)
262
0
                        index = 0;
263
0
                    elt_length = pattern[index] * scale;
264
0
                    ink_on = false;
265
9
                } else if (--index == 0) {
266
                    /* Revert lookahead. */
267
0
                    index = count - 1;
268
0
                }
269
9
            }
270
102k
            if (drawing > 0)  /* done */
271
16
                return code;
272
102k
            drawing = 0;
273
102k
        }
274
192k
        if (code < 0)
275
0
            return code;
276
192k
        x = sx, y = sy;
277
192k
        cap = pgs_lp->dash_cap;
278
192k
    }
279
    /* Check for wraparound. */
280
24.8k
    if (wrap && drawing <= 0) { /* We skipped some initial lines. */
281
        /* Go back and do them now. */
282
3.80k
        drawing = 1;
283
3.80k
        goto top;
284
3.80k
    }
285
21.0k
    return 0;
286
24.8k
}