1 /* Copyright (C) 1995, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
2
3 This software is provided AS-IS with no warranty, either express or
4 implied.
5
6 This software is distributed under license and may not be copied,
7 modified or distributed except as expressly authorized under the terms
8 of the license contained in the file LICENSE in this distribution.
9
10 For more information about licensing, please refer to
11 http://www.ghostscript.com/licensing/. For information on
12 commercial licensing, go to http://www.artifex.com/licensing/ or
13 contact Artifex Software, Inc., 101 Lucas Valley Road #110,
14 San Rafael, CA 94903, U.S.A., +1(415)492-9861.
15 */
16
17 /* $Id: gxpdash.c,v 1.6 2004/10/18 15:23:20 igor Exp $ */
18 /* Dash expansion for paths */
19 #include "math_.h"
20 #include "gx.h"
21 #include "gsmatrix.h" /* for gscoord.h */
22 #include "gscoord.h"
23 #include "gxfixed.h"
24 #include "gsline.h"
25 #include "gzline.h"
26 #include "gzpath.h"
27
28 /* Expand a dashed path into explicit segments. */
29 /* The path contains no curves. */
30 private int subpath_expand_dashes(const subpath *, gx_path *,
31 const gs_imager_state *,
32 const gx_dash_params *);
33 int
gx_path_add_dash_expansion(const gx_path * ppath_old,gx_path * ppath,const gs_imager_state * pis)34 gx_path_add_dash_expansion(const gx_path * ppath_old, gx_path * ppath,
35 const gs_imager_state * pis)
36 {
37 const subpath *psub;
38 const gx_dash_params *dash = &gs_currentlineparams(pis)->dash;
39 int code = 0;
40
41 if (dash->pattern_size == 0)
42 return gx_path_copy(ppath_old, ppath);
43 for (psub = ppath_old->first_subpath; psub != 0 && code >= 0;
44 psub = (const subpath *)psub->last->next
45 )
46 code = subpath_expand_dashes(psub, ppath, pis, dash);
47 return code;
48 }
49
50 private int
subpath_expand_dashes(const subpath * psub,gx_path * ppath,const gs_imager_state * pis,const gx_dash_params * dash)51 subpath_expand_dashes(const subpath * psub, gx_path * ppath,
52 const gs_imager_state * pis, const gx_dash_params * dash)
53 {
54 const float *pattern = dash->pattern;
55 int count, index;
56 bool ink_on;
57 double elt_length;
58 fixed x0 = psub->pt.x, y0 = psub->pt.y;
59 fixed x, y;
60 const segment *pseg;
61 int wrap = (dash->init_ink_on && psub->is_closed ? -1 : 0);
62 int drawing = wrap;
63 segment_notes notes = ~sn_not_first;
64 int code;
65
66 if ((code = gx_path_add_point(ppath, x0, y0)) < 0)
67 return code;
68 /*
69 * To do the right thing at the beginning of a closed path, we have
70 * to skip any initial line, and then redo it at the end of the
71 * path. Drawing = -1 while skipping, 0 while drawing normally, and
72 * 1 on the second round. Note that drawing != 0 implies ink_on.
73 */
74 top:count = dash->pattern_size;
75 ink_on = dash->init_ink_on;
76 index = dash->init_index;
77 elt_length = dash->init_dist_left;
78 x = x0, y = y0;
79 pseg = (const segment *)psub;
80 while ((pseg = pseg->next) != 0 && pseg->type != s_start) {
81 fixed sx = pseg->pt.x, sy = pseg->pt.y;
82 fixed udx = sx - x, udy = sy - y;
83 double length, dx, dy;
84 double scale = 1;
85 double left;
86
87 if (!(udx | udy)) { /* degenerate */
88 if (gs_currentlinecap((const gs_state *)pis) != gs_cap_round) {
89 /* From PLRM, stroke operator :
90 If a subpath is degenerate (consists of a single-point closed path
91 or of two or more points at the same coordinates),
92 stroke paints it only if round line caps have been specified */
93 continue;
94 }
95 dx = 0, dy = 0, length = 0;
96 } else {
97 gs_point d;
98
99 dx = udx, dy = udy; /* scaled as fixed */
100 gs_imager_idtransform(pis, dx, dy, &d);
101 length = hypot(d.x, d.y) * (1.0 / fixed_1);
102 if (gs_imager_currentdashadapt(pis)) {
103 double reps = length / dash->pattern_length;
104
105 scale = reps / ceil(reps);
106 /* Ensure we're starting at the start of a */
107 /* repetition. (This shouldn't be necessary, */
108 /* but it is.) */
109 count = dash->pattern_size;
110 ink_on = dash->init_ink_on;
111 index = dash->init_index;
112 elt_length = dash->init_dist_left * scale;
113 }
114 }
115 left = length;
116 while (left > elt_length) { /* We are using up the line segment. */
117 double fraction = elt_length / length;
118 fixed nx = x + (fixed) (dx * fraction);
119 fixed ny = y + (fixed) (dy * fraction);
120
121 if (ink_on) {
122 if (drawing >= 0)
123 code = gx_path_add_line_notes(ppath, nx, ny,
124 notes & pseg->notes);
125 notes |= sn_not_first;
126 } else {
127 if (drawing > 0) /* done */
128 return 0;
129 code = gx_path_add_point(ppath, nx, ny);
130 notes &= ~sn_not_first;
131 drawing = 0;
132 }
133 if (code < 0)
134 return code;
135 left -= elt_length;
136 ink_on = !ink_on;
137 if (++index == count)
138 index = 0;
139 elt_length = pattern[index] * scale;
140 x = nx, y = ny;
141 }
142 elt_length -= left;
143 /* Handle the last dash of a segment. */
144 on:if (ink_on) {
145 if (drawing >= 0) {
146 code =
147 (pseg->type == s_line_close && drawing > 0 ?
148 gx_path_close_subpath_notes(ppath,
149 notes & pseg->notes) :
150 gx_path_add_line_notes(ppath, sx, sy,
151 notes & pseg->notes));
152 notes |= sn_not_first;
153 }
154 } else {
155 code = gx_path_add_point(ppath, sx, sy);
156 notes &= ~sn_not_first;
157 if (elt_length < fixed2float(fixed_epsilon) &&
158 (pseg->next == 0 || pseg->next->type == s_start)
159 ) { /*
160 * Ink is off, but we're within epsilon of the end
161 * of the dash element, and at the end of the
162 * subpath. "Stretch" a little so we get a dot.
163 */
164 if (code < 0)
165 return code;
166 elt_length = 0;
167 ink_on = true;
168 if (++index == count)
169 index = 0;
170 elt_length = pattern[index] * scale;
171 goto on;
172 }
173 if (drawing > 0) /* done */
174 return code;
175 drawing = 0;
176 }
177 if (code < 0)
178 return code;
179 x = sx, y = sy;
180 }
181 /* Check for wraparound. */
182 if (wrap && drawing <= 0) { /* We skipped some initial lines. */
183 /* Go back and do them now. */
184 drawing = 1;
185 goto top;
186 }
187 return 0;
188 }
189