Commit | Line | Data |
---|---|---|
51a81c37 WJ |
1 | /* Copyright (C) 1989, 1992 Aladdin Enterprises. All rights reserved. |
2 | Distributed by Free Software Foundation, Inc. | |
3 | ||
4 | This file is part of Ghostscript. | |
5 | ||
6 | Ghostscript is distributed in the hope that it will be useful, but | |
7 | WITHOUT ANY WARRANTY. No author or distributor accepts responsibility | |
8 | to anyone for the consequences of using it or for whether it serves any | |
9 | particular purpose or works at all, unless he says so in writing. Refer | |
10 | to the Ghostscript General Public License for full details. | |
11 | ||
12 | Everyone is granted permission to copy, modify and redistribute | |
13 | Ghostscript, but only under the conditions described in the Ghostscript | |
14 | General Public License. A copy of this license is supposed to have been | |
15 | given to you along with Ghostscript so you can know your rights and | |
16 | responsibilities. It should be in a file named COPYING. Among other | |
17 | things, the copyright notice and this notice must be preserved on all | |
18 | copies. */ | |
19 | ||
20 | /* gxstroke.c */ | |
21 | /* Path stroking procedures for Ghostscript library */ | |
22 | #include "math_.h" | |
23 | #include "gx.h" | |
24 | #include "gserrors.h" | |
25 | #include "gxfixed.h" | |
26 | #include "gxarith.h" | |
27 | #include "gxmatrix.h" | |
28 | #include "gzstate.h" | |
29 | #include "gzdevice.h" | |
30 | #include "gzcolor.h" /* requires gxdevice.h */ | |
31 | #include "gzline.h" | |
32 | #include "gzpath.h" | |
33 | ||
34 | /* stroke_add uses the following global for its path: */ | |
35 | private gx_path stroke_path_body; | |
36 | private gx_path *stroke_path; | |
37 | ||
38 | /* | |
39 | * Structure for a partial line (passed to the drawing routine). | |
40 | * Two of these are required to do joins right. | |
41 | * Each endpoint includes the two ends of the cap as well, | |
42 | * and the deltas for square and round cap computation. | |
43 | * | |
44 | * The deltas (co, cdelta, ce) are in clockwise order in device space | |
45 | * around the endpoint p: they are one-half the line width (suitably | |
46 | * transformed) at 90 degrees counter-clockwise, straight ahead, | |
47 | * and 90 degrees clockwise from the oriented line o->e, | |
48 | * where "90 degrees" is measured in *user* coordinates. | |
49 | * Note that the values at o are the negatives of the values at e. | |
50 | * | |
51 | * Initially, only o.p, e.p, o.cdelta, width, and thin are set. | |
52 | * compute_caps fills in the rest when needed. | |
53 | */ | |
54 | typedef gs_fixed_point _ss *p_ptr; | |
55 | typedef struct endpoint_s { | |
56 | gs_fixed_point p; /* the end of the line */ | |
57 | gs_fixed_point co, ce; /* ends of the cap, p +/- width */ | |
58 | gs_fixed_point cdelta; /* +/- cap length */ | |
59 | } endpoint; | |
60 | typedef endpoint _ss *ep_ptr; | |
61 | typedef struct partial_line_s { | |
62 | endpoint o; /* starting coordinate */ | |
63 | endpoint e; /* ending coordinate */ | |
64 | gs_fixed_point width; /* one-half line width, see above */ | |
65 | int thin; /* true if minimum-width line */ | |
66 | } partial_line; | |
67 | typedef partial_line _ss *pl_ptr; | |
68 | ||
69 | /* Procedures that stroke a partial_line (the first argument). */ | |
70 | /* If both partial_lines are non-null, the procedure creates */ | |
71 | /* an appropriate join; otherwise, the procedure creates an */ | |
72 | /* end cap. If the first int is 0, the procedure also starts with */ | |
73 | /* an appropriate cap. */ | |
74 | private int stroke_add(P4(int, pl_ptr, pl_ptr, gs_state *)); | |
75 | private int stroke_fill(P4(int, pl_ptr, pl_ptr, gs_state *)); | |
76 | ||
77 | /* Other forward declarations */ | |
78 | private int stroke(P3(gx_path *, | |
79 | int (*)(P4(int, pl_ptr, pl_ptr, gs_state *)), | |
80 | gs_state *)); | |
81 | private int expand_dashes(P3(subpath *, gx_path *, gs_state *)); | |
82 | private void compute_caps(P1(pl_ptr)); | |
83 | private int add_capped(P4(gx_path *, gs_line_cap, | |
84 | int (*)(P3(gx_path *, fixed, fixed)), | |
85 | ep_ptr)); | |
86 | ||
87 | /* Stroke a path for drawing or saving */ | |
88 | int | |
89 | gx_stroke_fill(gx_path *ppath, gs_state *pgs) | |
90 | { int code; | |
91 | stroke_path = 0; | |
92 | code = stroke(ppath, stroke_fill, pgs); | |
93 | if ( stroke_path ) /* set if filling needed */ | |
94 | { if ( code >= 0 ) | |
95 | code = gx_fill_path(stroke_path, pgs->dev_color, pgs, | |
96 | gx_rule_winding_number, (fixed)0); | |
97 | gx_path_release(stroke_path); | |
98 | } | |
99 | return code; | |
100 | } | |
101 | int | |
102 | gx_stroke_add(gx_path *ppath, gx_path *topath, gs_state *pgs) | |
103 | { stroke_path = topath; | |
104 | return stroke(ppath, stroke_add, pgs); | |
105 | } | |
106 | ||
107 | /* Stroke a path. Call line_proc (stroke_add or stroke_fill) */ | |
108 | /* for each line segment. */ | |
109 | private int | |
110 | stroke(gx_path *ppath, | |
111 | int (*line_proc)(P4(int, pl_ptr, pl_ptr, gs_state *)), | |
112 | gs_state *pgs) | |
113 | { subpath *psub; | |
114 | subpath *save_psub = 0; | |
115 | int code = 0; | |
116 | line_params *lp = pgs->line_params; | |
117 | int dash_count = lp->dash.pattern_size; | |
118 | gx_path fpath, dpath; | |
119 | float xx = pgs->ctm.xx, xy = pgs->ctm.xy; | |
120 | float yx = pgs->ctm.yx, yy = pgs->ctm.yy; | |
121 | int skewed = !is_fzero2(xy, yx); | |
122 | int uniform = (skewed ? 0 : xx == yy ? 1 : xx == -yy ? -1 : 0); | |
123 | /* | |
124 | * We are dealing with a reflected coordinate system | |
125 | * if (1,0) is counter-clockwise from (0,1). | |
126 | * See the note in stroke_add for the algorithm. | |
127 | */ | |
128 | int reflected = | |
129 | (uniform ? uniform > 0 : | |
130 | skewed ? xy * yx < xx * yy : | |
131 | (xx < 0) == (yy < 0)); | |
132 | float line_width = lp->width; /* this is *half* the line width! */ | |
133 | int always_thin; | |
134 | float line_width_and_scale; | |
135 | #ifdef DEBUG | |
136 | if ( gs_debug['o'] ) | |
137 | { int count = lp->dash.pattern_size; | |
138 | int i; | |
139 | dprintf3("[o]half_width=%f, cap=%d, join=%d,\n", | |
140 | lp->width, (int)lp->cap, (int)lp->join); | |
141 | dprintf2(" miter_limit=%f, miter_check=%f,\n", | |
142 | lp->miter_limit, lp->miter_check); | |
143 | dprintf1(" dash pattern=%d", count); | |
144 | for ( i = 0; i < count; i++ ) | |
145 | dprintf1(",%f", lp->dash.pattern[i]); | |
146 | dprintf4(",\n offset=%f, init(ink_on=%d, index=%d, dist_left=%f)\n", | |
147 | lp->dash.offset, lp->dash.init_ink_on, lp->dash.init_index, | |
148 | lp->dash.init_dist_left); | |
149 | } | |
150 | #endif | |
151 | if ( line_width < 0 ) line_width = -line_width; | |
152 | if ( is_fzero(line_width) ) | |
153 | always_thin = 1; | |
154 | else if ( !skewed ) | |
155 | { float xxa = xx, yya = yy; | |
156 | if ( xxa < 0 ) xxa = -xxa; | |
157 | if ( yya < 0 ) yya = -yya; | |
158 | always_thin = (max(xxa, yya) * line_width < 0.5); | |
159 | } | |
160 | else | |
161 | { /* The check is more complicated, but it's worth it. */ | |
162 | float xsq = xx * xx + xy * xy; | |
163 | float ysq = yx * yx + yy * yy; | |
164 | float cross = xx * yx + xy * yy; | |
165 | if ( cross < 0 ) cross = 0; | |
166 | always_thin = | |
167 | ((max(xsq, ysq) + cross) * line_width * line_width < 0.5); | |
168 | } | |
169 | line_width_and_scale = line_width * (float)int2fixed(1); | |
170 | #ifdef DEBUG | |
171 | if ( gs_debug['o'] ) | |
172 | dprintf5("[o]ctm=(%g,%g,%g,%g) thin=%d\n", | |
173 | xx, xy, yx, yy, always_thin); | |
174 | #endif | |
175 | /* Start by flattening the path. We should do this on-the-fly.... */ | |
176 | if ( !ppath->curve_count ) /* don't need to flatten */ | |
177 | { psub = ppath->first_subpath; | |
178 | if ( !psub ) return 0; | |
179 | } | |
180 | else | |
181 | { if ( (code = gx_path_flatten(ppath, &fpath, pgs->flatness)) < 0 ) return code; | |
182 | psub = fpath.first_subpath; | |
183 | } | |
184 | if ( dash_count ) | |
185 | gx_path_init(&dpath, &ppath->memory_procs); | |
186 | for ( ; ; ) | |
187 | { line_segment *pline; | |
188 | fixed x, y; | |
189 | partial_line pl, pl_prev, pl_first; | |
190 | int first = 0; | |
191 | int index = 0; | |
192 | if ( !psub ) | |
193 | { /* Might just be the end of a dash expansion. */ | |
194 | if ( save_psub ) | |
195 | { gx_path_release(&dpath); | |
196 | psub = (subpath *)save_psub->last->next; | |
197 | if ( !psub ) break; | |
198 | gx_path_init(&dpath, &ppath->memory_procs); | |
199 | save_psub = 0; | |
200 | } | |
201 | else /* all done */ | |
202 | break; | |
203 | } | |
204 | if ( dash_count && !save_psub ) | |
205 | { code = expand_dashes(psub, &dpath, pgs); | |
206 | if ( code < 0 ) goto exit; | |
207 | save_psub = (subpath *)psub; | |
208 | psub = dpath.first_subpath; | |
209 | continue; /* psub might be null */ | |
210 | } | |
211 | pline = (line_segment *)(psub->next); | |
212 | x = psub->pt.x; | |
213 | y = psub->pt.y; | |
214 | while ( pline != 0 && pline->type != s_start ) | |
215 | { fixed sx = pline->pt.x; | |
216 | fixed sy = pline->pt.y; | |
217 | /* Compute the width parameters in device space. */ | |
218 | /* We work with unscaled values, for speed. */ | |
219 | pl.o.p.x = x, pl.o.p.y = y; | |
220 | pl.e.p.x = sx, pl.e.p.y = sy; | |
221 | if ( !always_thin ) | |
222 | { fixed udx = sx - x, udy = sy - y; | |
223 | if ( !(udx | udy) ) /* degenerate */ | |
224 | { /* Only consider a degenerate segment */ | |
225 | /* if the entire subpath is degenerate and */ | |
226 | /* we are using round caps or joins. */ | |
227 | if ( index != 0 || (pline->next != 0 && | |
228 | pline->next->type != s_start) || | |
229 | (lp->cap != gs_cap_round && | |
230 | lp->join != gs_join_round) | |
231 | ) | |
232 | goto nd; | |
233 | /* Pick an arbitrary orientation. */ | |
234 | udx = int2fixed(1); | |
235 | } | |
236 | if ( uniform != 0 ) | |
237 | { /* We can save a lot of work in this case. */ | |
238 | float dpx = udx, dpy = udy; | |
239 | float wl = line_width_and_scale * xx / | |
240 | hypot(dpx, dpy); | |
241 | if ( wl < 0 ) wl = -wl; | |
242 | pl.e.cdelta.x = (fixed)(dpx * wl); | |
243 | pl.e.cdelta.y = (fixed)(dpy * wl); | |
244 | pl.width.x = -pl.e.cdelta.y; | |
245 | pl.width.y = pl.e.cdelta.x; | |
246 | pl.thin = 0; /* if not always_thin, */ | |
247 | /* then never thin. */ | |
248 | } | |
249 | else | |
250 | { gs_point dpt; /* unscaled */ | |
251 | float wl; | |
252 | if ( skewed ) | |
253 | gs_idtransform(pgs, | |
254 | (float)udx, (float)udy, &dpt); | |
255 | else /* shortcut */ | |
256 | dpt.x = udx / xx, | |
257 | dpt.y = udy / yy; | |
258 | wl = line_width_and_scale / | |
259 | hypot(dpt.x, dpt.y); | |
260 | /* Construct the width vector in */ | |
261 | /* user space, still unscaled. */ | |
262 | dpt.x *= wl; | |
263 | dpt.y *= wl; | |
264 | /* | |
265 | * We now compute both perpendicular | |
266 | * and (optionally) parallel half-widths, | |
267 | * as deltas in device space. We use | |
268 | * a fixed-point, unscaled version of | |
269 | * gs_dtransform. The second computation | |
270 | * folds in a 90-degree rotation (in user | |
271 | * space, before transforming) in the | |
272 | * direction that corresponds to clockwise | |
273 | * in device space. | |
274 | */ | |
275 | pl.e.cdelta.x = (fixed)(dpt.x * xx); | |
276 | pl.e.cdelta.y = (fixed)(dpt.y * yy); | |
277 | if ( skewed ) | |
278 | pl.e.cdelta.x += (fixed)(dpt.y * yx), | |
279 | pl.e.cdelta.y += (fixed)(dpt.x * xy); | |
280 | if ( reflected ) | |
281 | dpt.x = -dpt.x, dpt.y = -dpt.y; | |
282 | pl.width.x = (fixed)(dpt.y * xx), | |
283 | pl.width.y = -(fixed)(dpt.x * yy); | |
284 | if ( skewed ) | |
285 | pl.width.x -= (fixed)(dpt.x * yx), | |
286 | pl.width.y += (fixed)(dpt.y * xy); | |
287 | pl.thin = | |
288 | any_abs(pl.width.x) + any_abs(pl.width.y) < | |
289 | float2fixed(0.75); | |
290 | } | |
291 | if ( !pl.thin ) compute_caps(&pl); | |
292 | } | |
293 | else | |
294 | pl.e.cdelta.x = pl.e.cdelta.y = 0, | |
295 | pl.width.x = pl.width.y = 0, | |
296 | pl.thin = 1; | |
297 | if ( first++ == 0 ) pl_first = pl; | |
298 | if ( index++ ) (*line_proc)(index - 2, &pl_prev, &pl, pgs); | |
299 | pl_prev = pl; | |
300 | x = sx, y = sy; | |
301 | nd: pline = (line_segment *)(pline->next); | |
302 | } | |
303 | if ( index ) | |
304 | { /* If closed, join back to start, else cap */ | |
305 | (*line_proc)(index - 1, &pl_prev, | |
306 | (psub->closed ? &pl_first : (pl_ptr)0), pgs); | |
307 | } | |
308 | psub = (subpath *)pline; | |
309 | if ( stroke_path == &stroke_path_body ) | |
310 | { /* Fill and release the accumulated path */ | |
311 | gx_fill_path(stroke_path, pgs->dev_color, pgs, | |
312 | gx_rule_winding_number, (fixed)0); | |
313 | gx_path_release(stroke_path); | |
314 | stroke_path = 0; | |
315 | } | |
316 | } | |
317 | exit: if ( dash_count ) gx_path_release(&dpath); | |
318 | if ( ppath->curve_count ) gx_path_release(&fpath); | |
319 | return code; | |
320 | } | |
321 | ||
322 | /* ------ Internal routines ------ */ | |
323 | ||
324 | /* Expand a dashed subpath into explicit segments. */ | |
325 | /* The subpath contains no curves. */ | |
326 | private int | |
327 | expand_dashes(subpath *psub, gx_path *ppath, gs_state *pgs) | |
328 | { int skewed = is_skewed(&pgs->ctm); | |
329 | dash_params *dash = &pgs->line_params->dash; | |
330 | float *pattern = dash->pattern; | |
331 | int count, ink_on, index; | |
332 | float dist_left; | |
333 | fixed x0 = psub->pt.x, y0 = psub->pt.y; | |
334 | fixed x, y; | |
335 | segment *pseg; | |
336 | int wrap = (dash->init_ink_on && psub->closed ? -1 : 0); | |
337 | int drawing = wrap; | |
338 | int code; | |
339 | if ( (code = gx_path_add_point(ppath, x0, y0)) < 0 ) | |
340 | return code; | |
341 | /* To do the right thing at the beginning of a closed path, */ | |
342 | /* we have to skip any initial line, and then redo it at */ | |
343 | /* the end of the path. Drawing = -1 while skipping, */ | |
344 | /* 0 while drawing normally, and 1 on the second round. */ | |
345 | top: count = dash->pattern_size; | |
346 | ink_on = dash->init_ink_on; | |
347 | index = dash->init_index; | |
348 | dist_left = dash->init_dist_left; | |
349 | x = x0, y = y0; | |
350 | pseg = (segment *)psub; | |
351 | while ( (pseg = pseg->next) != 0 && pseg->type != s_start ) | |
352 | { fixed sx = pseg->pt.x, sy = pseg->pt.y; | |
353 | fixed udx = sx - x, udy = sy - y; | |
354 | float length, dx, dy; | |
355 | float dist; | |
356 | if ( !(udx | udy) ) /* degenerate */ | |
357 | dx = 0, dy = 0, length = 0; | |
358 | else | |
359 | { gs_point d; | |
360 | dx = udx, dy = udy; /* scaled as fixed */ | |
361 | if ( skewed ) | |
362 | gs_idtransform(pgs, dx, dy, &d); | |
363 | else /* shortcut */ | |
364 | d.x = dx / pgs->ctm.xx, | |
365 | d.y = dy / pgs->ctm.yy; | |
366 | length = sqrt(d.x * d.x + d.y * d.y) * | |
367 | (1 / (float)int2fixed(1)); | |
368 | } | |
369 | dist = length; | |
370 | while ( dist > dist_left ) | |
371 | { /* We are using up the dash element */ | |
372 | float fraction = dist_left / length; | |
373 | fixed nx = x + (fixed)(dx * fraction); | |
374 | fixed ny = y + (fixed)(dy * fraction); | |
375 | if ( ink_on ) | |
376 | { if ( drawing >= 0 ) | |
377 | code = gx_path_add_line(ppath, nx, ny); | |
378 | } | |
379 | else | |
380 | { if ( drawing > 0 ) return 0; /* done */ | |
381 | code = gx_path_add_point(ppath, nx, ny); | |
382 | drawing = 0; | |
383 | } | |
384 | if ( code < 0 ) return code; | |
385 | dist -= dist_left; | |
386 | ink_on = !ink_on; | |
387 | if ( ++index == count ) index = 0; | |
388 | dist_left = pattern[index]; | |
389 | x = nx, y = ny; | |
390 | } | |
391 | dist_left -= dist; | |
392 | /* Handle the last dash of a segment. */ | |
393 | if ( ink_on ) | |
394 | { if ( drawing >= 0 ) | |
395 | code = | |
396 | (pseg->type == s_line_close && drawing > 0 ? | |
397 | gx_path_close_subpath(ppath) : | |
398 | gx_path_add_line(ppath, sx, sy)); | |
399 | } | |
400 | else | |
401 | { if ( drawing > 0 ) return 0; /* done */ | |
402 | code = gx_path_add_point(ppath, sx, sy); | |
403 | drawing = 0; | |
404 | } | |
405 | if ( code < 0 ) return code; | |
406 | x = sx, y = sy; | |
407 | } | |
408 | /* Check for wraparound. */ | |
409 | if ( wrap && drawing <= 0 ) | |
410 | { /* We skipped some initial lines. */ | |
411 | /* Go back and do them now. */ | |
412 | drawing = 1; | |
413 | goto top; | |
414 | } | |
415 | return 0; | |
416 | } | |
417 | ||
418 | /* Compute the intersection of two lines. This is a messy algorithm */ | |
419 | /* that somehow ought to be useful in more places than just here.... */ | |
420 | private void | |
421 | line_intersect( | |
422 | p_ptr pp1, /* point on 1st line */ | |
423 | p_ptr pd1, /* slope of 1st line (dx,dy) */ | |
424 | p_ptr pp2, /* point on 2nd line */ | |
425 | p_ptr pd2, /* slope of 2nd line */ | |
426 | p_ptr pi) /* return intersection here */ | |
427 | { /* We don't have to do any scaling, the factors all work out right. */ | |
428 | float u1 = pd1->x, v1 = pd1->y; | |
429 | float u2 = pd2->x, v2 = pd2->y; | |
430 | double denom = u1 * v2 - u2 * v1; | |
431 | double num1 = v1 * pp1->x - u1 * pp1->y; | |
432 | double num2 = v2 * pp2->x - u2 * pp2->y; | |
433 | double xnum = u1 * num2 - u2 * num1; | |
434 | double ynum = v1 * num2 - v2 * num1; | |
435 | double max_result = any_abs(denom) * (double)max_fixed; | |
436 | #ifdef DEBUG | |
437 | if ( gs_debug['o'] ) | |
438 | { dprintf4("[o]Intersect %f,%f(%f/%f)", | |
439 | fixed2float(pp1->x), fixed2float(pp1->y), | |
440 | fixed2float(pd1->x), fixed2float(pd1->y)); | |
441 | dprintf4(" & %f,%f(%f/%f),\n", | |
442 | fixed2float(pp2->x), fixed2float(pp2->y), | |
443 | fixed2float(pd2->x), fixed2float(pd2->y)); | |
444 | dprintf4("\txnum=%f ynum=%f denom=%f max_result=%f ->\n", | |
445 | xnum, ynum, denom, max_result); | |
446 | } | |
447 | #endif | |
448 | /* Check for degenerate result. */ | |
449 | if ( denom == 0 || any_abs(xnum) > max_result || any_abs(ynum) > max_result ) | |
450 | { /* The lines are nearly parallel, */ | |
451 | /* or one of them has zero length. Punt. */ | |
452 | *pi = *pp1; | |
453 | #ifdef DEBUG | |
454 | if ( gs_debug['o'] ) | |
455 | dprintf("\tdegenerate!\n"); | |
456 | #endif | |
457 | } | |
458 | else | |
459 | { pi->x = (fixed)(xnum / denom); | |
460 | pi->y = (fixed)(ynum / denom); | |
461 | #ifdef DEBUG | |
462 | if ( gs_debug['o'] ) | |
463 | dprintf2("\t%f,%f\n", fixed2float(pi->x), fixed2float(pi->y)); | |
464 | #endif | |
465 | } | |
466 | } | |
467 | ||
468 | #define lix plp->o.p.x | |
469 | #define liy plp->o.p.y | |
470 | #define litox plp->e.p.x | |
471 | #define litoy plp->e.p.y | |
472 | ||
473 | /* Draw a line on the device. */ | |
474 | private int | |
475 | stroke_fill(int first, register pl_ptr plp, pl_ptr nplp, gs_state *pgs) | |
476 | { if ( plp->thin ) | |
477 | { /* Minimum-width line, don't have to be careful. */ | |
478 | /* We do have to check for the entire line being */ | |
479 | /* within the clipping rectangle, allowing for some */ | |
480 | /* slop at the ends. */ | |
481 | fixed dx = litox - lix, dy = litoy - liy; | |
482 | #define trsign(v, c) (v >= 0 ? c : -c) | |
483 | #define slop int2fixed(2) | |
484 | fixed xslop = trsign(dx, slop); | |
485 | fixed yslop = trsign(dy, slop); | |
486 | if ( gx_cpath_includes_rectangle(pgs->clip_path, | |
487 | lix - xslop, liy - yslop, | |
488 | litox + xslop, litoy + yslop) ) | |
489 | return gz_draw_line_fixed(lix, liy, litox, litoy, | |
490 | pgs->dev_color, pgs); | |
491 | #undef slop | |
492 | /* We didn't set up the endpoint parameters before, */ | |
493 | /* because the line was thin. Do it now. */ | |
494 | /* We only approximate the width and height. */ | |
495 | if ( any_abs(dx) > any_abs(dy) ) | |
496 | { plp->width.x = plp->e.cdelta.y = 0; | |
497 | plp->width.y = -(plp->e.cdelta.x = | |
498 | trsign(dx, float2fixed(-0.5))); | |
499 | } | |
500 | else | |
501 | { plp->width.y = plp->e.cdelta.x = 0; | |
502 | plp->width.x = -(plp->e.cdelta.y = | |
503 | trsign(dy, float2fixed(-0.5))); | |
504 | } | |
505 | #undef trsign | |
506 | compute_caps(plp); | |
507 | } | |
508 | { /* General case. */ | |
509 | /* Construct a path and hand it to the fill algorithm. */ | |
510 | if ( stroke_path == 0 ) | |
511 | { /* We are rendering, and haven't run into the */ | |
512 | /* general case yet. Initialize the path. */ | |
513 | stroke_path = &stroke_path_body; /* set global for stroke_add */ | |
514 | gx_path_init(stroke_path, &pgs->memory_procs); | |
515 | } | |
516 | stroke_add(first, plp, nplp, pgs); | |
517 | { /****** PATCH ******/ | |
518 | if ( stroke_path == &stroke_path_body ) | |
519 | { gx_fill_path(stroke_path, pgs->dev_color, pgs, | |
520 | gx_rule_winding_number, (fixed)0); | |
521 | gx_path_release(stroke_path); | |
522 | stroke_path = 0; | |
523 | } | |
524 | } | |
525 | } | |
526 | return 0; | |
527 | } | |
528 | ||
529 | #undef lix | |
530 | #undef liy | |
531 | #undef litox | |
532 | #undef litoy | |
533 | ||
534 | /* Add a segment to the path. This handles all the complex cases. */ | |
535 | private int add_capped(P4(gx_path *, gs_line_cap, int (*)(P3(gx_path *, fixed, fixed)), ep_ptr)); | |
536 | private int | |
537 | stroke_add(int first, register pl_ptr plp, pl_ptr nplp, gs_state *pgs) | |
538 | { gx_path *ppath = stroke_path; | |
539 | int code; | |
540 | if ( ppath == 0 ) return 0; /****** strokepath is NYI ******/ | |
541 | if ( plp->thin ) | |
542 | { /* We didn't set up the endpoint parameters before, */ | |
543 | /* because the line was thin. Do it now. */ | |
544 | compute_caps(plp); | |
545 | } | |
546 | if ( (code = add_capped(ppath, (first == 0 ? pgs->line_params->cap : gs_cap_butt), gx_path_add_point, &plp->o)) < 0 ) | |
547 | return code; | |
548 | if ( nplp == 0 ) | |
549 | { code = add_capped(ppath, pgs->line_params->cap, gx_path_add_line, &plp->e); | |
550 | } | |
551 | else if ( pgs->line_params->join == gs_join_round ) | |
552 | { code = add_capped(ppath, gs_cap_round, gx_path_add_line, &plp->e); | |
553 | } | |
554 | else if ( nplp->thin ) /* no join */ | |
555 | { code = add_capped(ppath, gs_cap_butt, gx_path_add_line, &plp->e); | |
556 | } | |
557 | else /* join_bevel or join_miter */ | |
558 | { gs_fixed_point jp1, jp2; | |
559 | /* | |
560 | * Set np to whichever of nplp->o.co or .ce | |
561 | * is outside the current line. | |
562 | * We use the interesting observation that | |
563 | * point (x2,y2) is counter-clockwise from (x1,y1) | |
564 | * relative to the origin iff x1*y2 < x2*y1. | |
565 | * In this case x1,y1 are plp->width, | |
566 | * x2,y2 are nplp->width, and the origin is | |
567 | * their common point (plp->e.p, nplp->o.p). | |
568 | */ | |
569 | float wx1 = plp->width.x, wy1 = plp->width.y; | |
570 | float wx2 = nplp->width.x, wy2 = nplp->width.y; | |
571 | int ccw = wx1 * wy2 < wx2 * wy1; | |
572 | p_ptr outp, np, np1, np2; | |
573 | /* Initialize for a bevel join. */ | |
574 | jp1.x = plp->e.co.x, jp1.y = plp->e.co.y; | |
575 | jp2.x = plp->e.ce.x, jp2.y = plp->e.ce.y; | |
576 | if ( ccw ) | |
577 | outp = &jp2, np2 = np = &nplp->o.co, np1 = &plp->e.p; | |
578 | else | |
579 | outp = &jp1, np1 = np = &nplp->o.ce, np2 = &plp->e.p; | |
580 | #ifdef DEBUG | |
581 | if ( gs_debug['o'] ) | |
582 | dprintf1("[o]use %s\n", (ccw ? "co (ccw)" : "ce (cw)")); | |
583 | #endif | |
584 | /* Don't bother with the miter check if the two */ | |
585 | /* points to be joined are very close together, */ | |
586 | /* namely, in the same square half-pixel. */ | |
587 | if ( pgs->line_params->join == gs_join_miter && | |
588 | !(fixed2long(outp->x << 1) == fixed2long(np->x << 1) && | |
589 | fixed2long(outp->y << 1) == fixed2long(np->y << 1)) | |
590 | ) | |
591 | { /* | |
592 | * Check whether a miter join is appropriate. | |
593 | * Let a, b be the angles of the two lines. | |
594 | * We check tan(a-b) against the miter_check | |
595 | * by using the following formula: | |
596 | * If tan(a)=u1/v1 and tan(b)=u2/v2, then | |
597 | * tan(a-b) = (u1*v2 - u2*v1) / (u1*u2 + v1*v2). | |
598 | * We can do all the computations unscaled, | |
599 | * because we're only concerned with ratios. | |
600 | */ | |
601 | float u1 = plp->e.cdelta.x, v1 = plp->e.cdelta.y; | |
602 | float u2 = nplp->o.cdelta.x, v2 = nplp->o.cdelta.y; | |
603 | float num = u1 * v2 - u2 * v1; | |
604 | float denom = u1 * u2 + v1 * v2; | |
605 | float check = pgs->line_params->miter_check; | |
606 | /* | |
607 | * We will want either tan(a-b) or tan(b-a) | |
608 | * depending on the orientations of the lines. | |
609 | * Fortunately we know the relative orientations already. | |
610 | */ | |
611 | if ( !ccw ) /* have plp - nplp, want vice versa */ | |
612 | num = -num; | |
613 | #ifdef DEBUG | |
614 | if ( gs_debug['o'] ) | |
615 | { dprintf4("[o]Miter check: u1/v1=%f/%f, u2/v2=%f/%f,\n", | |
616 | u1, v1, u2, v2); | |
617 | dprintf3(" num=%f, denom=%f, check=%f\n", | |
618 | num, denom, check); | |
619 | } | |
620 | #endif | |
621 | /* Use a miter if num / denom >= check. */ | |
622 | /* If check > 0, num < 0 always passes; */ | |
623 | /* if check < 0, num >= 0 always fails. */ | |
624 | if ( denom < 0 ) num = -num, denom = -denom; | |
625 | if ( check > 0 ? | |
626 | (num < 0 || num >= denom * check) : | |
627 | (num < 0 && num >= denom * check) | |
628 | ) | |
629 | { /* OK to use a miter join. */ | |
630 | #ifdef DEBUG | |
631 | if ( gs_debug['o'] ) | |
632 | dputs(" ... passes.\n"); | |
633 | #endif | |
634 | /* Compute the intersection of */ | |
635 | /* the extended edge lines. */ | |
636 | line_intersect(outp, &plp->e.cdelta, np, | |
637 | &nplp->o.cdelta, outp); | |
638 | } | |
639 | } | |
640 | if ( (code = gx_path_add_line(ppath, jp1.x, jp1.y)) < 0 || | |
641 | (code = gx_path_add_line(ppath, np1->x, np1->y)) < 0 || | |
642 | (code = gx_path_add_line(ppath, np2->x, np2->y)) < 0 || | |
643 | (code = gx_path_add_line(ppath, jp2.x, jp2.y)) < 0 | |
644 | ) | |
645 | return code; | |
646 | } | |
647 | if ( code < 0 || (code = gx_path_close_subpath(ppath)) < 0 ) | |
648 | return code; | |
649 | return 0; | |
650 | } | |
651 | ||
652 | /* Routines for cap computations */ | |
653 | ||
654 | /* Compute the endpoints of the two caps of a segment. */ | |
655 | private void | |
656 | compute_caps(register pl_ptr plp) | |
657 | { fixed wx2 = plp->width.x; | |
658 | fixed wy2 = plp->width.y; | |
659 | plp->o.co.x = plp->o.p.x + wx2, plp->o.co.y = plp->o.p.y + wy2; | |
660 | plp->o.cdelta.x = -plp->e.cdelta.x, | |
661 | plp->o.cdelta.y = -plp->e.cdelta.y; | |
662 | plp->o.ce.x = plp->o.p.x - wx2, plp->o.ce.y = plp->o.p.y - wy2; | |
663 | plp->e.co.x = plp->e.p.x - wx2, plp->e.co.y = plp->e.p.y - wy2; | |
664 | plp->e.ce.x = plp->e.p.x + wx2, plp->e.ce.y = plp->e.p.y + wy2; | |
665 | #ifdef DEBUG | |
666 | if ( gs_debug['o'] ) | |
667 | dprintf4("[o]Stroke o=(%f,%f) e=(%f,%f)\n", | |
668 | fixed2float(plp->o.p.x), fixed2float(plp->o.p.y), | |
669 | fixed2float(plp->e.p.x), fixed2float(plp->e.p.y)), | |
670 | dprintf4("\twxy=(%f,%f) lxy=(%f,%f)\n", | |
671 | fixed2float(wx2), fixed2float(wy2), | |
672 | fixed2float(plp->e.cdelta.x), fixed2float(plp->e.cdelta.y)); | |
673 | #endif | |
674 | } | |
675 | ||
676 | /* Add a properly capped line endpoint to the path. */ | |
677 | /* The first point may require either moveto or lineto. */ | |
678 | private int | |
679 | add_capped(gx_path *ppath, gs_line_cap type, | |
680 | int (*add_proc)(P3(gx_path *, fixed, fixed)), /* gx_path_add_point/line */ | |
681 | register ep_ptr endp) | |
682 | { int code; | |
683 | #define px endp->p.x | |
684 | #define py endp->p.y | |
685 | #define xo endp->co.x | |
686 | #define yo endp->co.y | |
687 | #define xe endp->ce.x | |
688 | #define ye endp->ce.y | |
689 | #define cdx endp->cdelta.x | |
690 | #define cdy endp->cdelta.y | |
691 | #ifdef DEBUG | |
692 | if ( gs_debug['o'] ) | |
693 | dprintf4("[o]cap: p=(%g,%g), co=(%g,%g),\n", | |
694 | fixed2float(px), fixed2float(py), | |
695 | fixed2float(xo), fixed2float(yo)), | |
696 | dprintf4("[o]\tce=(%g,%g), cd=(%g,%g)\n", | |
697 | fixed2float(xe), fixed2float(ye), | |
698 | fixed2float(cdx), fixed2float(cdy)); | |
699 | #endif | |
700 | switch ( type ) | |
701 | { | |
702 | case gs_cap_round: | |
703 | { fixed xm = px + cdx; | |
704 | fixed ym = py + cdy; | |
705 | if ( (code = (*add_proc)(ppath, xo, yo)) < 0 || | |
706 | (code = gx_path_add_arc(ppath, xo, yo, xm, ym, | |
707 | xo + cdx, yo + cdy, quarter_arc_fraction)) < 0 || | |
708 | (code = gx_path_add_arc(ppath, xm, ym, xe, ye, | |
709 | xe + cdx, ye + cdy, quarter_arc_fraction)) < 0 | |
710 | ) return code; | |
711 | } | |
712 | break; | |
713 | case gs_cap_square: | |
714 | if ( (code = (*add_proc)(ppath, xo + cdx, yo + cdy)) < 0 || | |
715 | (code = gx_path_add_line(ppath, xe + cdx, ye + cdy)) < 0 | |
716 | ) return code; | |
717 | break; | |
718 | case gs_cap_butt: | |
719 | if ( (code = (*add_proc)(ppath, xo, yo)) < 0 || | |
720 | (code = gx_path_add_line(ppath, xe, ye)) < 0 | |
721 | ) return code; | |
722 | } | |
723 | return code; | |
724 | } |