xref: /plan9/sys/src/cmd/gs/src/gsline.c (revision 593dc095aefb2a85c828727bbfa9da139a49bdf4)
1 /* Copyright (C) 1989, 1995, 1996, 1997, 1999 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: gsline.c,v 1.4 2002/02/21 22:24:52 giles Exp $ */
18 /* Line parameter operators for Ghostscript library */
19 #include "math_.h"
20 #include "memory_.h"
21 #include "gx.h"
22 #include "gserrors.h"
23 #include "gxfixed.h"		/* ditto */
24 #include "gxmatrix.h"		/* for gzstate */
25 #include "gzstate.h"
26 #include "gscoord.h"		/* for currentmatrix, setmatrix */
27 #include "gsline.h"		/* for prototypes */
28 #include "gzline.h"
29 
30 /* ------ Device-independent parameters ------ */
31 
32 #define pgs_lp gs_currentlineparams_inline(pgs)
33 
34 /* setlinewidth */
35 int
gs_setlinewidth(gs_state * pgs,floatp width)36 gs_setlinewidth(gs_state * pgs, floatp width)
37 {
38     gx_set_line_width(pgs_lp, width);
39     return 0;
40 }
41 
42 /* currentlinewidth */
43 float
gs_currentlinewidth(const gs_state * pgs)44 gs_currentlinewidth(const gs_state * pgs)
45 {
46     return gx_current_line_width(pgs_lp);
47 }
48 
49 /* setlinecap */
50 int
gs_setlinecap(gs_state * pgs,gs_line_cap cap)51 gs_setlinecap(gs_state * pgs, gs_line_cap cap)
52 {
53     if ((uint) cap > gs_line_cap_max)
54 	return_error(gs_error_rangecheck);
55     pgs_lp->cap = cap;
56     return 0;
57 }
58 
59 /* currentlinecap */
60 gs_line_cap
gs_currentlinecap(const gs_state * pgs)61 gs_currentlinecap(const gs_state * pgs)
62 {
63     return pgs_lp->cap;
64 }
65 
66 /* setlinejoin */
67 int
gs_setlinejoin(gs_state * pgs,gs_line_join join)68 gs_setlinejoin(gs_state * pgs, gs_line_join join)
69 {
70     if ((uint) join > gs_line_join_max)
71 	return_error(gs_error_rangecheck);
72     pgs_lp->join = join;
73     return 0;
74 }
75 
76 /* currentlinejoin */
77 gs_line_join
gs_currentlinejoin(const gs_state * pgs)78 gs_currentlinejoin(const gs_state * pgs)
79 {
80     return pgs_lp->join;
81 }
82 
83 /* setmiterlimit */
84 int
gx_set_miter_limit(gx_line_params * plp,floatp limit)85 gx_set_miter_limit(gx_line_params * plp, floatp limit)
86 {
87     if (limit < 1.0)
88 	return_error(gs_error_rangecheck);
89     plp->miter_limit = limit;
90     /*
91      * Compute the miter check value.  The supplied miter limit is an
92      * upper bound on 1/sin(phi/2); we convert this to a lower bound on
93      * tan(phi).  Note that if phi > pi/2, this is negative.  We use the
94      * half-angle and angle-sum formulas here to avoid the trig functions.
95      * We also need a special check for phi/2 close to pi/4.
96      * Some C compilers can't handle this as a conditional expression....
97      */
98     {
99 	double limit_squared = limit * limit;
100 
101 	if (limit_squared < 2.0001 && limit_squared > 1.9999)
102 	    plp->miter_check = 1.0e6;
103 	else
104 	    plp->miter_check =
105 		sqrt(limit_squared - 1) * 2 / (limit_squared - 2);
106     }
107     return 0;
108 }
109 int
gs_setmiterlimit(gs_state * pgs,floatp limit)110 gs_setmiterlimit(gs_state * pgs, floatp limit)
111 {
112     return gx_set_miter_limit(pgs_lp, limit);
113 }
114 
115 /* currentmiterlimit */
116 float
gs_currentmiterlimit(const gs_state * pgs)117 gs_currentmiterlimit(const gs_state * pgs)
118 {
119     return pgs_lp->miter_limit;
120 }
121 
122 /* setdash */
123 int
gx_set_dash(gx_dash_params * dash,const float * pattern,uint length,floatp offset,gs_memory_t * mem)124 gx_set_dash(gx_dash_params * dash, const float *pattern, uint length,
125 	    floatp offset, gs_memory_t * mem)
126 {
127     uint n = length;
128     const float *dfrom = pattern;
129     bool ink = true;
130     int index = 0;
131     float pattern_length = 0.0;
132     float dist_left;
133     float *ppat = dash->pattern;
134 
135     /* Check the dash pattern. */
136     while (n--) {
137 	float elt = *dfrom++;
138 
139 	if (elt < 0)
140 	    return_error(gs_error_rangecheck);
141 	pattern_length += elt;
142     }
143     if (length == 0) {		/* empty pattern */
144 	dist_left = 0.0;
145 	if (mem && ppat) {
146 	    gs_free_object(mem, ppat, "gx_set_dash(old pattern)");
147 	    ppat = 0;
148 	}
149     } else {
150 	uint size = length * sizeof(float);
151 
152 	if (pattern_length == 0)
153 	    return_error(gs_error_rangecheck);
154 	/* Compute the initial index, ink_on, and distance left */
155 	/* in the pattern, according to the offset. */
156 #define f_mod(a, b) ((a) - floor((a) / (b)) * (b))
157 	if (length & 1) {	/* Odd and even repetitions of the pattern */
158 	    /* have opposite ink values! */
159 	    float length2 = pattern_length * 2;
160 
161 	    dist_left = f_mod(offset, length2);
162 	    if (dist_left >= pattern_length)
163 		dist_left -= pattern_length, ink = !ink;
164 	} else
165 	    dist_left = f_mod(offset, pattern_length);
166 	while ((dist_left -= pattern[index]) >= 0 &&
167 	       (dist_left > 0 || pattern[index] != 0)
168 	    )
169 	    ink = !ink, index++;
170 	if (mem) {
171 	    if (ppat == 0)
172 		ppat = (float *)gs_alloc_bytes(mem, size,
173 					       "gx_set_dash(pattern)");
174 	    else if (length != dash->pattern_size)
175 		ppat = gs_resize_object(mem, ppat, size,
176 					"gx_set_dash(pattern)");
177 	    if (ppat == 0)
178 		return_error(gs_error_VMerror);
179 	}
180 	memcpy(ppat, pattern, length * sizeof(float));
181     }
182     dash->pattern = ppat;
183     dash->pattern_size = length;
184     dash->offset = offset;
185     dash->pattern_length = pattern_length;
186     dash->init_ink_on = ink;
187     dash->init_index = index;
188     dash->init_dist_left = -dist_left;
189     return 0;
190 }
191 int
gs_setdash(gs_state * pgs,const float * pattern,uint length,floatp offset)192 gs_setdash(gs_state * pgs, const float *pattern, uint length, floatp offset)
193 {
194     return gx_set_dash(&pgs_lp->dash, pattern, length, offset,
195 		       pgs->memory);
196 }
197 
198 /* currentdash */
199 uint
gs_currentdash_length(const gs_state * pgs)200 gs_currentdash_length(const gs_state * pgs)
201 {
202     return pgs_lp->dash.pattern_size;
203 }
204 const float *
gs_currentdash_pattern(const gs_state * pgs)205 gs_currentdash_pattern(const gs_state * pgs)
206 {
207     return pgs_lp->dash.pattern;
208 }
209 float
gs_currentdash_offset(const gs_state * pgs)210 gs_currentdash_offset(const gs_state * pgs)
211 {
212     return pgs_lp->dash.offset;
213 }
214 
215 /* Internal accessor for line parameters */
216 const gx_line_params *
gs_currentlineparams(const gs_imager_state * pis)217 gs_currentlineparams(const gs_imager_state * pis)
218 {
219     return gs_currentlineparams_inline(pis);
220 }
221 
222 /* ------ Device-dependent parameters ------ */
223 
224 /* setflat */
225 int
gs_imager_setflat(gs_imager_state * pis,floatp flat)226 gs_imager_setflat(gs_imager_state * pis, floatp flat)
227 {
228     if (flat <= 0.2)
229 	flat = 0.2;
230     else if (flat > 100)
231 	flat = 100;
232     pis->flatness = flat;
233     return 0;
234 }
235 int
gs_setflat(gs_state * pgs,floatp flat)236 gs_setflat(gs_state * pgs, floatp flat)
237 {
238     return gs_imager_setflat((gs_imager_state *) pgs, flat);
239 }
240 
241 /* currentflat */
242 float
gs_currentflat(const gs_state * pgs)243 gs_currentflat(const gs_state * pgs)
244 {
245     return pgs->flatness;
246 }
247 
248 /* setstrokeadjust */
249 int
gs_setstrokeadjust(gs_state * pgs,bool stroke_adjust)250 gs_setstrokeadjust(gs_state * pgs, bool stroke_adjust)
251 {
252     pgs->stroke_adjust = stroke_adjust;
253     return 0;
254 }
255 
256 /* currentstrokeadjust */
257 bool
gs_currentstrokeadjust(const gs_state * pgs)258 gs_currentstrokeadjust(const gs_state * pgs)
259 {
260     return pgs->stroke_adjust;
261 }
262 
263 /* ------ Extensions ------ */
264 
265 /* Device-independent */
266 
267 /* setdashadapt */
268 void
gs_setdashadapt(gs_state * pgs,bool adapt)269 gs_setdashadapt(gs_state * pgs, bool adapt)
270 {
271     pgs_lp->dash.adapt = adapt;
272 }
273 
274 /* currentdashadapt */
275 bool
gs_imager_currentdashadapt(const gs_imager_state * pis)276 gs_imager_currentdashadapt(const gs_imager_state * pis)
277 {
278     return gs_currentlineparams_inline(pis)->dash.adapt;
279 }
280 bool
gs_currentdashadapt(const gs_state * pgs)281 gs_currentdashadapt(const gs_state * pgs)
282 {
283     return gs_imager_currentdashadapt((const gs_imager_state *)pgs);
284 }
285 
286 /* setcurvejoin */
287 int
gs_setcurvejoin(gs_state * pgs,int join)288 gs_setcurvejoin(gs_state * pgs, int join)
289 {
290     if (join < -1 || join > gs_line_join_max)
291 	return_error(gs_error_rangecheck);
292     pgs_lp->curve_join = join;
293     return 0;
294 }
295 
296 /* currentcurvejoin */
297 int
gs_currentcurvejoin(const gs_state * pgs)298 gs_currentcurvejoin(const gs_state * pgs)
299 {
300     return pgs_lp->curve_join;
301 }
302 
303 /* Device-dependent */
304 
305 /* setaccuratecurves */
306 void
gs_setaccuratecurves(gs_state * pgs,bool accurate)307 gs_setaccuratecurves(gs_state * pgs, bool accurate)
308 {
309     pgs->accurate_curves = accurate;
310 }
311 
312 /* currentaccuratecurves */
313 bool
gs_imager_currentaccuratecurves(const gs_imager_state * pis)314 gs_imager_currentaccuratecurves(const gs_imager_state * pis)
315 {
316     return pis->accurate_curves;
317 }
318 bool
gs_currentaccuratecurves(const gs_state * pgs)319 gs_currentaccuratecurves(const gs_state * pgs)
320 {
321     return gs_imager_currentaccuratecurves((const gs_imager_state *)pgs);
322 }
323 
324 /* setdotlength */
325 int
gx_set_dot_length(gx_line_params * plp,floatp length,bool absolute)326 gx_set_dot_length(gx_line_params * plp, floatp length, bool absolute)
327 {
328     if (length < 0)
329 	return_error(gs_error_rangecheck);
330     plp->dot_length = length;
331     plp->dot_length_absolute = absolute;
332     return 0;
333 }
334 int
gs_setdotlength(gs_state * pgs,floatp length,bool absolute)335 gs_setdotlength(gs_state * pgs, floatp length, bool absolute)
336 {
337     return gx_set_dot_length(pgs_lp, length, absolute);
338 }
339 
340 /* currentdotlength */
341 float
gs_currentdotlength(const gs_state * pgs)342 gs_currentdotlength(const gs_state * pgs)
343 {
344     return pgs_lp->dot_length;
345 }
346 bool
gs_currentdotlength_absolute(const gs_state * pgs)347 gs_currentdotlength_absolute(const gs_state * pgs)
348 {
349     return pgs_lp->dot_length_absolute;
350 }
351 
352 /* setdotorientation */
353 int
gs_setdotorientation(gs_state * pgs)354 gs_setdotorientation(gs_state *pgs)
355 {
356     if (is_xxyy(&pgs->ctm) || is_xyyx(&pgs->ctm))
357 	return gs_currentmatrix(pgs, &pgs_lp->dot_orientation);
358     return_error(gs_error_rangecheck);
359 }
360 
361 /* dotorientation */
362 int
gs_dotorientation(gs_state * pgs)363 gs_dotorientation(gs_state *pgs)
364 {
365     return gs_setmatrix(pgs, &pgs_lp->dot_orientation);
366 }
367