xref: /plan9/sys/src/cmd/gs/src/gxpdash.c (revision 593dc095aefb2a85c828727bbfa9da139a49bdf4)
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