/src/libspectre/ghostscript/base/gxstroke.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* Copyright (C) 2001-2020 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., 1305 Grant Avenue - Suite 200, Novato, |
13 | | CA 94945, U.S.A., +1(415)492-9861, for further information. |
14 | | */ |
15 | | |
16 | | |
17 | | /* Path stroking procedures for Ghostscript library */ |
18 | | #include "math_.h" |
19 | | #include <stdlib.h> /* abs() */ |
20 | | #include "gx.h" |
21 | | #include "gpcheck.h" |
22 | | #include "gserrors.h" |
23 | | #include "gsdcolor.h" |
24 | | #include "gsptype1.h" |
25 | | #include "gxfixed.h" |
26 | | #include "gxfarith.h" |
27 | | #include "gxmatrix.h" |
28 | | #include "gscoord.h" |
29 | | #include "gsdevice.h" |
30 | | #include "gxdevice.h" |
31 | | #include "gxhttile.h" |
32 | | #include "gxgstate.h" |
33 | | #include "gzline.h" |
34 | | #include "gzpath.h" |
35 | | #include "gzcpath.h" |
36 | | #include "gxpaint.h" |
37 | | #include "gsstate.h" /* for gs_currentcpsimode */ |
38 | | |
39 | | /* RJW: There appears to be a difference in the xps and postscript models |
40 | | * (at least in as far as Microsofts implementation of xps and Acrobats of |
41 | | * postscript). Acrobat (and ghostscript) are happy to join a line segment |
42 | | * around a corner, even when the next segment is a dash gap. Microsofts |
43 | | * implementation of XPS does not. |
44 | | * |
45 | | * A test file that shows this up is tests_private/comparefiles/298-05.ps |
46 | | * |
47 | | * Enabling the following define would emulate xps behaviour here. |
48 | | */ |
49 | | #undef AVOID_JOINING_TO_DASH_GAPS |
50 | | |
51 | | /* |
52 | | * We don't really know whether it's a good idea to take fill adjustment |
53 | | * into account for stroking. Disregarding it means that strokes |
54 | | * come out thinner than fills; observing it produces heavy-looking |
55 | | * strokes at low resolutions. But in any case, we must disregard it |
56 | | * when stroking zero-width lines. |
57 | | */ |
58 | | #define USE_FILL_ADJUSTMENT |
59 | | |
60 | | #ifdef USE_FILL_ADJUSTMENT |
61 | | # define STROKE_ADJUSTMENT(thin, pgs, xy)\ |
62 | 0 | (thin ? fixed_0 : (pgs)->fill_adjust.xy) |
63 | | #else |
64 | | # define STROKE_ADJUSTMENT(thin, pgs, xy) fixed_0 |
65 | | #endif |
66 | | |
67 | | /* |
68 | | * For some reason, we commented out the optimization for portrait, |
69 | | * landscape, and uniform (non-scaled) transformations. We have no record |
70 | | * of why we did this, and we don't know what bugs re-enabling it may |
71 | | * introduce. |
72 | | */ |
73 | | #define OPTIMIZE_ORIENTATION |
74 | | |
75 | | /* |
76 | | * Compute the amount by which to expand a stroked bounding box to account |
77 | | * for line width, caps and joins. Return 0 if the result is exact, 1 if |
78 | | * it may be conservative, or gs_error_limitcheck if the result is too |
79 | | * large to fit in a gs_fixed_point. |
80 | | * |
81 | | * Because of square caps and miter and triangular joins, the maximum |
82 | | * expansion on each side (in user space) is |
83 | | * K * line_width/2 |
84 | | * where K is determined as follows: |
85 | | * For round or butt caps, E = 1 |
86 | | * For square caps, E = sqrt(2) |
87 | | * If the path is only a single line segment, K = E; |
88 | | * if triangular joins, K = 2; |
89 | | * if miter joins, K = max(miter_limit, E); |
90 | | * otherwise, K = E. |
91 | | * |
92 | | * If the following conditions apply, K = E yields an exact result: |
93 | | * - The CTM is of the form [X 0 0 Y] or [0 X Y 0]. |
94 | | * - Square or round caps are used, or all subpaths are closed. |
95 | | * - All segments (including the implicit segment created by |
96 | | * closepath) are vertical or horizontal lines. |
97 | | * |
98 | | * Note that these conditions are sufficient, but not necessary, to get an |
99 | | * exact result. We choose this set of conditions because it is easy to |
100 | | * check and covers many common cases. Clients that care always have the |
101 | | * option of using strokepath to get an exact result. |
102 | | */ |
103 | | static float join_expansion_factor(const gs_gstate *, gs_line_join); |
104 | | int |
105 | | gx_stroke_path_expansion(const gs_gstate * pgs, const gx_path * ppath, |
106 | | gs_fixed_point * ppt) |
107 | 0 | { |
108 | 0 | const subpath *psub; |
109 | 0 | const segment *pseg; |
110 | 0 | double cx = fabs(pgs->ctm.xx) + fabs(pgs->ctm.yx); |
111 | 0 | double cy = fabs(pgs->ctm.xy) + fabs(pgs->ctm.yy); |
112 | 0 | double expand = pgs->line_params.half_width; |
113 | 0 | int result = 1; |
114 | |
|
115 | 0 | if (ppath == NULL) { |
116 | 0 | ppt->x = ppt->y = 0; |
117 | 0 | return 0; /* no expansion */ |
118 | 0 | } |
119 | 0 | psub = ppath->first_subpath; |
120 | | /* Adjust the expansion (E) for square caps, if needed */ |
121 | 0 | if (pgs->line_params.start_cap == gs_cap_square || |
122 | 0 | pgs->line_params.end_cap == gs_cap_square) |
123 | 0 | expand *= 1.414213562; |
124 | | |
125 | | /* Check for whether an exact result can be computed easily. */ |
126 | 0 | if (is_fzero2(pgs->ctm.xy, pgs->ctm.yx) || |
127 | 0 | is_fzero2(pgs->ctm.xx, pgs->ctm.yy) |
128 | 0 | ) { |
129 | 0 | bool must_be_closed = |
130 | 0 | !(pgs->line_params.start_cap == gs_cap_square || |
131 | 0 | pgs->line_params.start_cap == gs_cap_round || |
132 | 0 | pgs->line_params.end_cap == gs_cap_square || |
133 | 0 | pgs->line_params.end_cap == gs_cap_round || |
134 | 0 | pgs->line_params.dash_cap == gs_cap_square || |
135 | 0 | pgs->line_params.dash_cap == gs_cap_round); |
136 | 0 | gs_fixed_point prev; |
137 | |
|
138 | 0 | prev.x = prev.y = 0; /* Quiet gcc warning. */ |
139 | 0 | for (pseg = (const segment *)psub; pseg; |
140 | 0 | prev = pseg->pt, pseg = pseg->next |
141 | 0 | ) |
142 | 0 | switch (pseg->type) { |
143 | 0 | case s_start: |
144 | 0 | if (((const subpath *)pseg)->curve_count || |
145 | 0 | (must_be_closed && !((const subpath *)pseg)->is_closed) |
146 | 0 | ) |
147 | 0 | goto not_exact; |
148 | 0 | break; |
149 | 0 | case s_line: |
150 | 0 | case s_dash: |
151 | 0 | case s_line_close: |
152 | 0 | if (!(pseg->pt.x == prev.x || pseg->pt.y == prev.y)) |
153 | 0 | goto not_exact; |
154 | 0 | break; |
155 | 0 | case s_gap: |
156 | 0 | default: /* other/unknown segment type */ |
157 | 0 | goto not_exact; |
158 | 0 | } |
159 | 0 | result = 0; /* exact result */ |
160 | 0 | } |
161 | 0 | not_exact: |
162 | 0 | if (result) { |
163 | 0 | if (!gx_path_has_curves(ppath) && gx_path_subpath_count(ppath) <= 1 && |
164 | 0 | (psub == 0 || (pseg = psub->next) == 0 || |
165 | 0 | (pseg = pseg->next) == 0 || pseg->type == s_line_close)) |
166 | 0 | DO_NOTHING; |
167 | 0 | else { |
168 | 0 | float factor = join_expansion_factor(pgs, pgs->line_params.join); |
169 | |
|
170 | 0 | if (pgs->line_params.curve_join >= 0) |
171 | 0 | factor = max(factor, join_expansion_factor(pgs, |
172 | 0 | (gs_line_join)pgs->line_params.curve_join)); |
173 | 0 | expand *= factor; |
174 | 0 | } |
175 | 0 | } |
176 | | |
177 | | /* Short-cut gs_bbox_transform. */ |
178 | 0 | { |
179 | 0 | float exx = expand * cx; |
180 | 0 | float exy = expand * cy; |
181 | 0 | int code = set_float2fixed_vars(ppt->x, exx); |
182 | |
|
183 | 0 | if (code < 0) |
184 | 0 | return code; |
185 | 0 | code = set_float2fixed_vars(ppt->y, exy); |
186 | 0 | if (code < 0) |
187 | 0 | return code; |
188 | 0 | } |
189 | | |
190 | 0 | return result; |
191 | 0 | } |
192 | | static float |
193 | | join_expansion_factor(const gs_gstate *pgs, gs_line_join join) |
194 | 0 | { |
195 | 0 | switch (join) { |
196 | 0 | case gs_join_miter: return pgs->line_params.miter_limit; |
197 | 0 | case gs_join_triangle: return 2.0; |
198 | 0 | default: return 1.0; |
199 | 0 | } |
200 | 0 | } |
201 | | |
202 | | /* |
203 | | * Structure for a partial line (passed to the drawing routine). |
204 | | * Two of these are required to do joins right. |
205 | | * Each endpoint includes the two ends of the cap as well, |
206 | | * and the deltas for square, round, and triangular cap computation. |
207 | | * |
208 | | * The two base values for computing the caps of a partial line are the |
209 | | * width and the end cap delta. The width value is one-half the line |
210 | | * width (suitably transformed) at 90 degrees counter-clockwise |
211 | | * (in device space, but with "90 degrees" interpreted in *user* |
212 | | * coordinates) at the end (as opposed to the origin) of the line. |
213 | | * The cdelta value is one-half the transformed line width in the same |
214 | | * direction as the line. From these, we compute two other values at each |
215 | | * end of the line: co and ce, which are the ends of the cap. |
216 | | * Note that the cdelta values at o are the negatives of the values at e, |
217 | | * as are the offsets from p to co and ce. |
218 | | * |
219 | | * Initially, only o.p, e.p, e.cdelta, width, and thin are set. |
220 | | * compute_caps fills in the rest. |
221 | | */ |
222 | | typedef gs_fixed_point *p_ptr; |
223 | | typedef struct endpoint_s { |
224 | | gs_fixed_point p; /* the end of the line */ |
225 | | gs_fixed_point co, ce; /* ends of the cap, p +/- width */ |
226 | | gs_fixed_point cdelta; /* +/- cap length */ |
227 | | } endpoint; |
228 | | typedef endpoint *ep_ptr; |
229 | | typedef const endpoint *const_ep_ptr; |
230 | | typedef struct partial_line_s { |
231 | | endpoint o; /* starting coordinate */ |
232 | | endpoint e; /* ending coordinate */ |
233 | | gs_fixed_point width; /* one-half line width, see above */ |
234 | | gs_fixed_point vector; /* The line segment direction */ |
235 | | bool thin; /* true if minimum-width line */ |
236 | | } partial_line; |
237 | | typedef partial_line *pl_ptr; |
238 | | |
239 | | /* As we stroke a path, we run through the line segments that make it up. |
240 | | * We gather each line segment together with any degenerate line segments |
241 | | * that follow it (call this set "prev"), and then 'join them' to the next |
242 | | * line segment (and any degenerate line segments that follow it) (if there |
243 | | * is one) (call this "current"). |
244 | | * |
245 | | * In order to get the joins right we need to keep flags about both |
246 | | * prev and current, and whether they originally came from arcs. |
247 | | */ |
248 | | typedef enum note_flags { |
249 | | |
250 | | /* If set, all the line segments that make up current come from arcs. */ |
251 | | nf_all_from_arc = 1, |
252 | | |
253 | | /* If set, at least one of the line segments that make up current, come |
254 | | * from arcs. */ |
255 | | nf_some_from_arc = 2, |
256 | | |
257 | | /* If set then this segment should have a dash cap on the start rather |
258 | | * than a start cap. */ |
259 | | nf_dash_head = 4, |
260 | | |
261 | | /* If set then this segment should have a dash cap on the end rather |
262 | | * than an end cap. */ |
263 | | nf_dash_tail = 8, |
264 | | |
265 | | /* If set, all the line segments that make up prev come from arcs. */ |
266 | | nf_prev_all_from_arc = 16, |
267 | | |
268 | | /* If set, at least one of the line segment that make up prev, come from |
269 | | * arcs. */ |
270 | | nf_prev_some_from_arc = 32, |
271 | | |
272 | | /* If set then prev should have a dash cap on the start rather |
273 | | * than a start cap. */ |
274 | | nf_prev_dash_head = 64, |
275 | | |
276 | | /* If set then prev should have a dash cap on the end rather |
277 | | * than an end cap. */ |
278 | | nf_prev_dash_tail = 128 |
279 | | |
280 | | } note_flags; |
281 | | |
282 | | /* Macro to combine the prev and current arc_flags. After applying this |
283 | | * macro, the bits in the result have the following meanings: |
284 | | * nf_all_from_arc set if all the components of current and prev |
285 | | * come from an Arc. |
286 | | * nf_some_from_arc set if any of the components of current and |
287 | | * prev come from an Arc. |
288 | | * nf_dash_head set if prev should have a dash cap rather than |
289 | | * a start cap. |
290 | | * nf_dash_tail set if prev should have a dash cap rather than |
291 | | * an end cap. |
292 | | */ |
293 | | #define COMBINE_FLAGS(F) \ |
294 | 0 | (((F>>4) | ((F) & nf_some_from_arc)) & \ |
295 | 0 | (((F) & nf_all_from_arc) ? ~0 : ~nf_all_from_arc)) |
296 | | |
297 | | /* Assign a point. Some compilers would do this with very slow code */ |
298 | | /* if we simply implemented it as an assignment. */ |
299 | | #define ASSIGN_POINT(pp, p)\ |
300 | 0 | ((pp)->x = (p).x, (pp)->y = (p).y) |
301 | | |
302 | | /* Other forward declarations */ |
303 | | static bool width_is_thin(pl_ptr); |
304 | | static void adjust_stroke(gx_device *, pl_ptr, const gs_gstate *, bool, bool, note_flags); |
305 | | static int line_join_points(const gx_line_params * pgs_lp, |
306 | | pl_ptr plp, pl_ptr nplp, |
307 | | gs_fixed_point * join_points, |
308 | | const gs_matrix * pmat, gs_line_join join, |
309 | | bool reflected); |
310 | | static int line_join_points_fast_cw(const gx_line_params * pgs_lp, |
311 | | pl_ptr plp, pl_ptr nplp, |
312 | | gs_fixed_point * rjoin_points, |
313 | | const gs_matrix * pmat, |
314 | | gs_line_join join); |
315 | | static int line_join_points_fast_ccw(const gx_line_params * pgs_lp, |
316 | | pl_ptr plp, pl_ptr nplp, |
317 | | gs_fixed_point * join_points, |
318 | | const gs_matrix * pmat, |
319 | | gs_line_join join); |
320 | | static void compute_caps(pl_ptr); |
321 | | static int add_points(gx_path *, const gs_fixed_point *, |
322 | | int, bool); |
323 | | static int add_pie_join(gx_path *, pl_ptr, pl_ptr, bool, bool); |
324 | | static int add_pie_join_fast_cw(gx_path *, pl_ptr, pl_ptr, bool); |
325 | | static int add_pie_join_fast_ccw(gx_path *, pl_ptr, pl_ptr, bool); |
326 | | static int add_round_cap(gx_path *, const_ep_ptr); |
327 | | static int add_pie_cap(gx_path *, const_ep_ptr); |
328 | | static int cap_points(gs_line_cap, const_ep_ptr, |
329 | | gs_fixed_point * /*[3] */ ); |
330 | | static int join_under_pie(gx_path *, pl_ptr, pl_ptr, bool); |
331 | | |
332 | | /* Define the default implementation of the device stroke_path procedure. */ |
333 | | int |
334 | | gx_default_stroke_path(gx_device * dev, const gs_gstate * pgs, |
335 | | gx_path * ppath, const gx_stroke_params * params, |
336 | | const gx_drawing_color * pdcolor, |
337 | | const gx_clip_path * pcpath) |
338 | 0 | { |
339 | 0 | return gx_stroke_path_only(ppath, (gx_path *) 0, dev, pgs, params, |
340 | 0 | pdcolor, pcpath); |
341 | 0 | } |
342 | | |
343 | | /* Fill a partial stroked path. Free variables: */ |
344 | | /* to_path, stroke_path_body, fill_params, always_thin, pgs, dev, pdevc, */ |
345 | | /* code, ppath, exit(label). */ |
346 | | #define FILL_STROKE_PATH(dev, thin, pcpath, final)\ |
347 | 0 | if(to_path==&stroke_path_body && !gx_path_is_void(&stroke_path_body) &&\ |
348 | 0 | (final || lop_is_idempotent(pgs->log_op))) {\ |
349 | 0 | fill_params.adjust.x = STROKE_ADJUSTMENT(thin, pgs, x);\ |
350 | 0 | fill_params.adjust.y = STROKE_ADJUSTMENT(thin, pgs, y);\ |
351 | 0 | if (to_path_reverse != NULL) {\ |
352 | 0 | code = gx_join_path_and_reverse(to_path, to_path_reverse);\ |
353 | 0 | if(code < 0) goto exit;\ |
354 | 0 | }\ |
355 | 0 | code = gx_fill_path_only(to_path, dev, pgs, &fill_params, pdevc, pcpath);\ |
356 | 0 | gx_path_free(&stroke_path_body, "fill_stroke_path");\ |
357 | 0 | if ( code < 0 ) goto exit;\ |
358 | 0 | gx_path_init_local(&stroke_path_body, ppath->memory);\ |
359 | 0 | } |
360 | | |
361 | | /* |
362 | | * Define the internal procedures that stroke a partial_line |
363 | | * (the first pl_ptr argument). If both partial_lines are non-null, |
364 | | * the procedure creates an appropriate join; otherwise, the procedure |
365 | | * creates an end cap. If the first int is 0, the procedure also starts |
366 | | * with an appropriate cap. |
367 | | */ |
368 | | #define stroke_line_proc(proc)\ |
369 | | int proc(gx_path *, gx_path *, bool ensure_closed, int, pl_ptr, pl_ptr,\ |
370 | | const gx_device_color *, gx_device *, const gs_gstate *,\ |
371 | | const gx_stroke_params *, const gs_fixed_rect *, int,\ |
372 | | gs_line_join, bool, note_flags) |
373 | | typedef stroke_line_proc((*stroke_line_proc_t)); |
374 | | |
375 | | static stroke_line_proc(stroke_add); |
376 | | static stroke_line_proc(stroke_add_compat); |
377 | | static stroke_line_proc(stroke_add_fast); |
378 | | static stroke_line_proc(stroke_fill); |
379 | | static int stroke_add_initial_cap_compat(gx_path * ppath, pl_ptr plp, bool adlust_longitude, |
380 | | const gx_device_color * pdevc, gx_device * dev, |
381 | | const gs_gstate * pgs); |
382 | | |
383 | | /* Define the orientations we handle specially. */ |
384 | | typedef enum { |
385 | | orient_other = 0, |
386 | | orient_portrait, /* [xx 0 0 yy tx ty] */ |
387 | | orient_landscape /* [0 xy yx 0 tx ty] */ |
388 | | } orientation; |
389 | | |
390 | | /* |
391 | | * Internal function used to merge the 2 sides of a stroked path. |
392 | | * path contains the 'forward' side, rpath contains the 'reversed' side. |
393 | | * Reverse rpath, then append it to path. |
394 | | * |
395 | | * If path is closed, then rpath should be too. If path is open, then the |
396 | | * starting and ending points of both paths should be the same, so as to |
397 | | * guarantee a closed path. |
398 | | */ |
399 | | static int |
400 | | gx_join_path_and_reverse(gx_path * path, gx_path * rpath) |
401 | 0 | { |
402 | 0 | int code; |
403 | |
|
404 | 0 | if (gx_path_is_void(rpath)) |
405 | 0 | return 0; |
406 | 0 | code = gx_path_append_reversed(rpath, path); |
407 | 0 | if (code < 0) |
408 | 0 | return code; |
409 | | |
410 | 0 | gx_path_free(rpath, "gx_join_path_and_reverse"); |
411 | 0 | gx_path_init_local(rpath, path->memory); |
412 | |
|
413 | 0 | return gx_path_close_subpath(path); |
414 | 0 | } |
415 | | |
416 | | /* |
417 | | * Stroke a path. If to_path != 0, append the stroke outline to it; |
418 | | * if to_path == 0, draw the strokes on pdev. |
419 | | * |
420 | | * Note that gx_stroke_path_only with to_path != NULL may clip the path to |
421 | | * the clipping path, as for to_path == NULL. This is almost never |
422 | | * what is wanted. |
423 | | */ |
424 | | static int |
425 | | gx_stroke_path_only_aux(gx_path * ppath, gx_path * to_path, gx_device * pdev, |
426 | | const gs_gstate * pgs, const gx_stroke_params * params, |
427 | | const gx_device_color * pdevc, const gx_clip_path * pcpath) |
428 | 0 | { |
429 | 0 | bool CPSI_mode = gs_currentcpsimode(pgs->memory); |
430 | 0 | bool traditional = CPSI_mode | params->traditional; |
431 | 0 | stroke_line_proc_t line_proc = |
432 | 0 | ((to_path == 0 && !gx_dc_is_pattern1_color_clist_based(pdevc)) |
433 | 0 | ? (lop_is_idempotent(pgs->log_op) ? stroke_fill : stroke_add) : |
434 | 0 | (traditional ? stroke_add_compat : stroke_add_fast)); |
435 | 0 | gs_fixed_rect ibox, cbox; |
436 | 0 | gx_device_clip cdev; |
437 | 0 | gx_device *dev = pdev; |
438 | 0 | int code = 0; |
439 | 0 | gx_fill_params fill_params; |
440 | 0 | const gx_line_params *pgs_lp = gs_currentlineparams_inline(pgs); |
441 | 0 | int dash_count = pgs_lp->dash.pattern_size; |
442 | 0 | gx_path fpath, dpath; |
443 | 0 | gx_path stroke_path_body; |
444 | 0 | gx_path stroke_path_reverse; |
445 | 0 | gx_path *to_path_reverse = NULL; |
446 | 0 | const gx_path *spath; |
447 | 0 | float xx = pgs->ctm.xx, xy = pgs->ctm.xy; |
448 | 0 | float yx = pgs->ctm.yx, yy = pgs->ctm.yy; |
449 | | /* |
450 | | * We are dealing with a reflected coordinate system |
451 | | * if transform(1,0) is counter-clockwise from transform(0,1). |
452 | | * See the note in stroke_add for the algorithm. |
453 | | */ |
454 | 0 | int uniform; |
455 | 0 | bool reflected; |
456 | 0 | orientation orient = |
457 | 0 | ( |
458 | 0 | #ifdef OPTIMIZE_ORIENTATION |
459 | 0 | is_fzero2(xy, yx) ? |
460 | 0 | (uniform = (xx == yy ? 1 : xx == -yy ? -1 : 0), |
461 | 0 | reflected = (uniform ? uniform < 0 : (xx < 0) != (yy < 0)), |
462 | 0 | orient_portrait) : |
463 | 0 | is_fzero2(xx, yy) ? |
464 | 0 | (uniform = (xy == yx ? -1 : xy == -yx ? 1 : 0), |
465 | 0 | reflected = (uniform ? uniform < 0 : (xy < 0) == (yx < 0)), |
466 | 0 | orient_landscape) : |
467 | | /* We should optimize uniform rotated coordinate systems */ |
468 | | /* here as well, but we don't. */ |
469 | 0 | #endif |
470 | 0 | (uniform = 0, |
471 | 0 | reflected = xy * yx > xx * yy, |
472 | 0 | orient_other)); |
473 | 0 | const segment_notes not_first = sn_not_first; |
474 | 0 | gs_line_join curve_join = |
475 | 0 | (pgs_lp->curve_join >= 0 ? (gs_line_join)pgs_lp->curve_join : |
476 | 0 | pgs_lp->join == gs_join_none || pgs_lp->join == gs_join_round ? |
477 | 0 | gs_join_bevel : pgs_lp->join); |
478 | 0 | float line_width = pgs_lp->half_width; /* (*half* the line width) */ |
479 | 0 | bool always_thin; |
480 | 0 | double line_width_and_scale; |
481 | 0 | double device_line_width_scale = 0; /* Quiet compiler. */ |
482 | 0 | double device_dot_length = pgs_lp->dot_length * fixed_1; |
483 | 0 | const subpath *psub; |
484 | 0 | gs_matrix initial_matrix; |
485 | 0 | bool initial_matrix_reflected; |
486 | 0 | note_flags flags; |
487 | |
|
488 | 0 | (*dev_proc(pdev, get_initial_matrix)) (pdev, &initial_matrix); |
489 | 0 | initial_matrix_reflected = initial_matrix.xy * initial_matrix.yx > |
490 | 0 | initial_matrix.xx * initial_matrix.yy; |
491 | |
|
492 | | #ifdef DEBUG |
493 | | if (gs_debug_c('o')) { |
494 | | int i; |
495 | | |
496 | | dmlprintf4(ppath->memory, "[o]half_width=%f, start_cap=%d, end_cap=%d, dash_cap=%d,\n", |
497 | | pgs_lp->half_width, (int)pgs_lp->start_cap, |
498 | | (int)pgs_lp->end_cap, (int)pgs_lp->dash_cap); |
499 | | dmlprintf3(ppath->memory, " join=%d, miter_limit=%f, miter_check=%f,\n", |
500 | | (int)pgs_lp->join, pgs_lp->miter_limit, |
501 | | pgs_lp->miter_check); |
502 | | dmlprintf1(ppath->memory, " dash pattern=%d", dash_count); |
503 | | for (i = 0; i < dash_count; i++) |
504 | | dmprintf1(ppath->memory, ",%f", pgs_lp->dash.pattern[i]); |
505 | | dmputs(ppath->memory, ",\n"); |
506 | | dmlprintf4(ppath->memory, "\toffset=%f, init(ink_on=%d, index=%d, dist_left=%f)\n", |
507 | | pgs_lp->dash.offset, pgs_lp->dash.init_ink_on, |
508 | | pgs_lp->dash.init_index, pgs_lp->dash.init_dist_left); |
509 | | } |
510 | | #endif |
511 | |
|
512 | 0 | gx_path_bbox(ppath, &ibox); |
513 | | /* Expand the path bounding box by the scaled line width. */ |
514 | 0 | { |
515 | 0 | gs_fixed_point expansion; |
516 | |
|
517 | 0 | if (gx_stroke_path_expansion(pgs, ppath, &expansion) < 0) { |
518 | | /* The expansion is so large it caused a limitcheck. */ |
519 | 0 | ibox.p.x = ibox.p.y = min_fixed; |
520 | 0 | ibox.q.x = ibox.q.y = max_fixed; |
521 | 0 | } else { |
522 | 0 | expansion.x += pgs->fill_adjust.x; |
523 | 0 | expansion.y += pgs->fill_adjust.y; |
524 | | /* |
525 | | * It's theoretically possible for the following computations to |
526 | | * overflow, so we need to check for this. |
527 | | */ |
528 | 0 | ibox.p.x = (ibox.p.x < min_fixed + expansion.x ? min_fixed : |
529 | 0 | ibox.p.x - expansion.x); |
530 | 0 | ibox.p.y = (ibox.p.y < min_fixed + expansion.y ? min_fixed : |
531 | 0 | ibox.p.y - expansion.y); |
532 | 0 | ibox.q.x = (ibox.q.x > max_fixed - expansion.x ? max_fixed : |
533 | 0 | ibox.q.x + expansion.x); |
534 | 0 | ibox.q.y = (ibox.q.y > max_fixed - expansion.y ? max_fixed : |
535 | 0 | ibox.q.y + expansion.y); |
536 | 0 | } |
537 | 0 | } |
538 | | /* Check the expanded bounding box against the clipping regions. */ |
539 | 0 | if (pcpath) |
540 | 0 | gx_cpath_inner_box(pcpath, &cbox); |
541 | 0 | else if (pdevc) |
542 | 0 | (*dev_proc(pdev, get_clipping_box)) (pdev, &cbox); |
543 | 0 | else { |
544 | | /* This is strokepath, not stroke. Don't clip. */ |
545 | 0 | cbox = ibox; |
546 | 0 | } |
547 | 0 | if (!rect_within(ibox, cbox)) { |
548 | | /* Intersect the path box and the clip bounding box. */ |
549 | | /* If the intersection is empty, this call is a no-op. */ |
550 | 0 | gs_fixed_rect bbox; |
551 | |
|
552 | 0 | if (pcpath) { |
553 | 0 | gx_cpath_outer_box(pcpath, &bbox); |
554 | 0 | if_debug4m('f', ppath->memory, " outer_box=(%g,%g),(%g,%g)\n", |
555 | 0 | fixed2float(bbox.p.x), fixed2float(bbox.p.y), |
556 | 0 | fixed2float(bbox.q.x), fixed2float(bbox.q.y)); |
557 | 0 | rect_intersect(ibox, bbox); |
558 | 0 | } else |
559 | 0 | rect_intersect(ibox, cbox); |
560 | 0 | if (ibox.p.x >= ibox.q.x || ibox.p.y >= ibox.q.y) { |
561 | | /* Intersection of boxes is empty! */ |
562 | 0 | return 0; |
563 | 0 | } |
564 | | /* |
565 | | * The path is neither entirely inside the inner clip box |
566 | | * nor entirely outside the outer clip box. |
567 | | * If we had to flatten the path, this is where we would |
568 | | * recompute its bbox and make the tests again, |
569 | | * but we don't bother right now. |
570 | | */ |
571 | | /* |
572 | | * If there is a clipping path, set up a clipping device. |
573 | | * for stroke_fill because, because the latter uses low level methods |
574 | | * which don't accept a clipping path. |
575 | | * Note that in some cases stroke_fill appends the path to stroke_path_body |
576 | | * instead a real painting, and it is painted with FILL_STROKE_PATH. |
577 | | * |
578 | | * Contrary to that, FILL_STROKE_PATH paints a path with |
579 | | * the fill_path method, which handles a clipping path, |
580 | | * so we don't pass the clipper device to FILL_STROKE_PATH |
581 | | * to prevent an appearence of superposing clippers. |
582 | | */ |
583 | 0 | if (pcpath && line_proc == stroke_fill) { |
584 | 0 | gx_make_clip_device_on_stack(&cdev, pcpath, pdev); |
585 | 0 | cdev.max_fill_band = pdev->max_fill_band; |
586 | 0 | dev = (gx_device *)&cdev; |
587 | 0 | } |
588 | 0 | } |
589 | 0 | fill_params.rule = gx_rule_winding_number; |
590 | 0 | fill_params.flatness = pgs->flatness; |
591 | 0 | if (line_width < 0) |
592 | 0 | line_width = -line_width; |
593 | 0 | line_width_and_scale = line_width * (double)int2fixed(1); |
594 | 0 | if (is_fzero(line_width)) |
595 | 0 | always_thin = true; |
596 | 0 | else { |
597 | 0 | float xa, ya; |
598 | |
|
599 | 0 | switch (orient) { |
600 | 0 | case orient_portrait: |
601 | 0 | xa = xx, ya = yy; |
602 | 0 | goto sat; |
603 | 0 | case orient_landscape: |
604 | 0 | xa = xy, ya = yx; |
605 | 0 | sat: |
606 | 0 | if (xa < 0) |
607 | 0 | xa = -xa; |
608 | 0 | if (ya < 0) |
609 | 0 | ya = -ya; |
610 | 0 | always_thin = (max(xa, ya) * line_width < 0.5); |
611 | 0 | if (!always_thin && uniform) { /* Precompute a value we'll need later. */ |
612 | 0 | device_line_width_scale = line_width_and_scale * xa; |
613 | 0 | } |
614 | 0 | break; |
615 | 0 | default: |
616 | 0 | { |
617 | | /* The check is more complicated, but it's worth it. */ |
618 | | /* Compute radii of the transformed round brush. */ |
619 | | /* Let x = [a, sqrt(1-a^2)]' |
620 | | radius^2 is an extremum of : |
621 | | rr(a)=(CTM*x)^2 = (a*xx + sqrt(1 - a^2)*xy)^2 + (a*yx + sqrt(1 - a^2)*yy)^2 |
622 | | With solving D(rr(a),a)==0, got : |
623 | | max_rr = (xx^2 + xy^2 + yx^2 + yy^2 + sqrt(((xy + yx)^2 + (xx - yy)^2)*((xy - yx)^2 + (xx + yy)^2)))/2. |
624 | | r = sqrt(max_rr); |
625 | | Well we could use eigenvalues of the quadratic form, |
626 | | but it gives same result with a bigger calculus. |
627 | | */ |
628 | 0 | double max_rr = ((double)(xx*xx + xy*xy + yx*yx + yy*yy) + |
629 | 0 | sqrt((double)((xy + yx)*(xy + yx) + (xx - yy)*(xx - yy)) * |
630 | 0 | ((xy - yx)*(xy - yx) + (xx + yy)*(xx + yy)) |
631 | 0 | ) |
632 | 0 | )/2; |
633 | |
|
634 | 0 | always_thin = max_rr * line_width * line_width < 0.25; |
635 | 0 | } |
636 | 0 | } |
637 | 0 | } |
638 | 0 | if_debug7m('o', ppath->memory, "[o]ctm=(%g,%g,%g,%g,%g,%g) thin=%d\n", |
639 | 0 | xx, xy, yx, yy, pgs->ctm.tx, pgs->ctm.ty, always_thin); |
640 | 0 | if (device_dot_length != 0) { |
641 | | /* |
642 | | * Compute the dot length in device space. We can't do this |
643 | | * quite right for non-uniform coordinate systems; too bad. |
644 | | */ |
645 | 0 | gs_matrix mat; |
646 | 0 | const gs_matrix *pmat; |
647 | |
|
648 | 0 | if (pgs_lp->dot_length_absolute) { |
649 | 0 | gs_deviceinitialmatrix(pdev, &mat); |
650 | 0 | pmat = &mat; |
651 | 0 | } else |
652 | 0 | pmat = (const gs_matrix *)&pgs->ctm; |
653 | 0 | device_dot_length *= fabs(pmat->xy) + fabs(pmat->yy); |
654 | 0 | } |
655 | | /* Start by flattening the path. We should do this on-the-fly.... */ |
656 | 0 | if (!gx_path_has_curves(ppath) && !gx_path_has_long_segments(ppath)) { |
657 | | /* don't need to flatten */ |
658 | 0 | if (!ppath->first_subpath) |
659 | 0 | return 0; |
660 | 0 | spath = ppath; |
661 | 0 | } else { |
662 | 0 | gx_path_init_local(&fpath, ppath->memory); |
663 | 0 | if ((code = gx_path_add_flattened_for_stroke(ppath, &fpath, |
664 | 0 | params->flatness, pgs)) < 0 |
665 | 0 | ) |
666 | 0 | return code; |
667 | 0 | spath = &fpath; |
668 | 0 | } |
669 | 0 | if (dash_count) { |
670 | 0 | float max_dash_len = 0; |
671 | 0 | float expand_squared; |
672 | 0 | int i; |
673 | 0 | float adjust = (float)pgs->fill_adjust.x; |
674 | 0 | if (adjust > (float)pgs->fill_adjust.y) |
675 | 0 | adjust = (float)pgs->fill_adjust.y; |
676 | 0 | for (i = 0; i < dash_count; i++) { |
677 | 0 | if (max_dash_len < pgs_lp->dash.pattern[i]) |
678 | 0 | max_dash_len = pgs_lp->dash.pattern[i]; |
679 | 0 | } |
680 | 0 | expand_squared = pgs->ctm.xx * pgs->ctm.yy - pgs->ctm.xy * pgs->ctm.yx; |
681 | 0 | if (expand_squared < 0) |
682 | 0 | expand_squared = -expand_squared; |
683 | 0 | expand_squared *= max_dash_len * max_dash_len; |
684 | | /* Wide lines in curves can show dashes up, so fudge to allow for |
685 | | * this. */ |
686 | 0 | if (pgs->line_params.half_width > 1) |
687 | 0 | adjust /= pgs->line_params.half_width; |
688 | 0 | if (expand_squared*65536.0f >= (float)(adjust*adjust)) { |
689 | 0 | gx_path_init_local(&dpath, ppath->memory); |
690 | 0 | code = gx_path_add_dash_expansion(spath, &dpath, pgs); |
691 | 0 | if (code < 0) |
692 | 0 | goto exf; |
693 | 0 | spath = &dpath; |
694 | 0 | } else { |
695 | 0 | dash_count = 0; |
696 | 0 | } |
697 | 0 | } |
698 | 0 | if (to_path == 0) { |
699 | | /* We might try to defer this if it's expensive.... */ |
700 | 0 | to_path = &stroke_path_body; |
701 | 0 | gx_path_init_local(&stroke_path_body, ppath->memory); |
702 | 0 | } |
703 | 0 | if (line_proc == stroke_add_fast) { |
704 | 0 | to_path_reverse = &stroke_path_reverse; |
705 | 0 | gx_path_init_local(&stroke_path_reverse, ppath->memory); |
706 | 0 | } |
707 | 0 | for (psub = spath->first_subpath; psub != 0;) { |
708 | 0 | int index = 0; |
709 | 0 | const segment *pseg = (const segment *)psub; |
710 | 0 | fixed x = pseg->pt.x; |
711 | 0 | fixed y = pseg->pt.y; |
712 | 0 | bool is_closed = ((const subpath *)pseg)->is_closed; |
713 | 0 | partial_line pl, pl_prev, pl_first; |
714 | 0 | bool zero_length = true; |
715 | 0 | int pseg_notes = pseg->notes; |
716 | |
|
717 | 0 | flags = nf_all_from_arc; |
718 | | |
719 | | /* Run through each segment in the current path, drawing each segment |
720 | | * delayed by 1 - that is, when we're looking at segment n, we draw |
721 | | * (or not) segment n-1. This delay allows us to always know whether |
722 | | * to join or cap the line. */ |
723 | 0 | while ((pseg = pseg->next) != 0 && |
724 | 0 | pseg->type != s_start |
725 | 0 | ) { |
726 | | /* Compute the width parameters in device space. */ |
727 | | /* We work with unscaled values, for speed. */ |
728 | 0 | fixed sx, udx, sy, udy; |
729 | 0 | bool is_dash_segment; |
730 | |
|
731 | 0 | pseg_notes = pseg->notes; |
732 | |
|
733 | 0 | d2:is_dash_segment = false; |
734 | 0 | d1:if (pseg->type == s_dash) { |
735 | 0 | dash_segment *pd = (dash_segment *)pseg; |
736 | |
|
737 | 0 | sx = pd->pt.x; |
738 | 0 | sy = pd->pt.y; |
739 | 0 | udx = pd->tangent.x; |
740 | 0 | udy = pd->tangent.y; |
741 | 0 | is_dash_segment = true; |
742 | 0 | } else if (pseg->type == s_gap) { |
743 | 0 | sx = pseg->pt.x; |
744 | 0 | sy = pseg->pt.y; |
745 | 0 | udx = sx - x; |
746 | 0 | udy = sy - y; |
747 | 0 | is_dash_segment = true; |
748 | 0 | } else { |
749 | 0 | sx = pseg->pt.x; |
750 | 0 | sy = pseg->pt.y; |
751 | 0 | udx = sx - x; |
752 | 0 | udy = sy - y; |
753 | 0 | } |
754 | 0 | zero_length &= ((udx | udy) == 0); |
755 | 0 | pl.o.p.x = x, pl.o.p.y = y; |
756 | 0 | d:flags = (((pseg_notes & sn_not_first) ? |
757 | 0 | ((flags & nf_all_from_arc) | nf_some_from_arc) : 0) | |
758 | 0 | ((pseg_notes & sn_dash_head) ? nf_dash_head : 0) | |
759 | 0 | ((pseg_notes & sn_dash_tail) ? nf_dash_tail : 0) | |
760 | 0 | (flags & ~nf_all_from_arc)); |
761 | 0 | pl.e.p.x = sx, pl.e.p.y = sy; |
762 | 0 | if (!(udx | udy) || pseg->type == s_dash || pseg->type == s_gap) { /* degenerate or short */ |
763 | | /* |
764 | | * If this is the first segment of the subpath, |
765 | | * check the entire subpath for degeneracy. |
766 | | * Otherwise, ignore the degenerate segment. |
767 | | */ |
768 | 0 | if (index != 0 && pseg->type != s_dash && pseg->type != s_gap) |
769 | 0 | { |
770 | 0 | if (pseg->next == NULL || pseg->next->type == s_start) |
771 | 0 | continue; |
772 | 0 | pseg = pseg->next; |
773 | | /* We're skipping a degenerate path segment; if it was |
774 | | * labelled as being the first from a curve, then make |
775 | | * sure the one we're skipping to is also labelled as |
776 | | * being the first from a curve, otherwise we can get |
777 | | * improper joins being used. See Bug 696466. */ |
778 | 0 | pseg_notes = (((pseg_notes & sn_not_first) == 0) ? |
779 | 0 | (pseg->notes & ~sn_not_first) : |
780 | 0 | pseg->notes); |
781 | 0 | goto d2; |
782 | 0 | } |
783 | | /* Check for a degenerate subpath. */ |
784 | 0 | while ((pseg = pseg->next) != 0 && |
785 | 0 | pseg->type != s_start |
786 | 0 | ) { |
787 | 0 | if (is_dash_segment) |
788 | 0 | break; |
789 | 0 | if (pseg->type == s_dash || pseg->type == s_gap) |
790 | 0 | goto d1; |
791 | 0 | sx = pseg->pt.x, udx = sx - x; |
792 | 0 | sy = pseg->pt.y, udy = sy - y; |
793 | 0 | if (udx | udy) { |
794 | 0 | zero_length = false; |
795 | 0 | goto d; |
796 | 0 | } |
797 | 0 | } |
798 | 0 | if (pgs_lp->dot_length == 0 && |
799 | 0 | pgs_lp->start_cap != gs_cap_round && |
800 | 0 | pgs_lp->end_cap != gs_cap_round && |
801 | 0 | !is_dash_segment) { |
802 | | /* From PLRM, stroke operator : |
803 | | If a subpath is degenerate (consists of a single-point closed path |
804 | | or of two or more points at the same coordinates), |
805 | | stroke paints it only if round line caps have been specified */ |
806 | 0 | break; |
807 | 0 | } |
808 | | /* |
809 | | * If the subpath is a dash, take the orientation from the dash segment. |
810 | | * Otherwise orient the dot according to the previous segment if |
811 | | * any, or else the next segment if any, or else |
812 | | * according to the specified dot orientation. |
813 | | */ |
814 | 0 | { |
815 | | /* When passing here, either pseg == NULL or it points to the |
816 | | start of the next subpaph. So we can't use pseg |
817 | | for determining the segment direction. |
818 | | In same time, psub->last may help, so use it. */ |
819 | 0 | const segment *end = psub->last; |
820 | |
|
821 | 0 | if (is_dash_segment) { |
822 | | /* Nothing. */ |
823 | 0 | } else if (end != 0 && (end->pt.x != x || end->pt.y != y)) |
824 | 0 | sx = end->pt.x, sy = end->pt.y, udx = sx - x, udy = sy - y; |
825 | 0 | } |
826 | | /* |
827 | | * Compute the properly oriented dot length, and then |
828 | | * draw the dot like a very short line. |
829 | | */ |
830 | 0 | if ((udx | udy) == 0) { |
831 | 0 | if (is_fzero(pgs_lp->dot_orientation.xy)) { |
832 | | /* Portrait orientation, dot length = X */ |
833 | 0 | udx = fixed_1; |
834 | 0 | } else { |
835 | | /* Landscape orientation, dot length = Y */ |
836 | 0 | udy = fixed_1; |
837 | 0 | } |
838 | 0 | } |
839 | 0 | if (sx == x && sy == y && (pseg == NULL || pseg->type == s_start)) { |
840 | 0 | double scale = device_dot_length / |
841 | 0 | hypot((double)udx, (double)udy); |
842 | 0 | fixed udx1, udy1; |
843 | | /* |
844 | | * If we're using butt caps, make sure the "line" is |
845 | | * long enough to show up. |
846 | | * Don't apply this with always_thin, becase |
847 | | * draw thin line always rounds the length up. |
848 | | */ |
849 | 0 | if (!always_thin && (pgs_lp->start_cap == gs_cap_butt || |
850 | 0 | pgs_lp->end_cap == gs_cap_butt || |
851 | 0 | pgs_lp->dash_cap == gs_cap_butt)) { |
852 | 0 | fixed dmax = max(any_abs(udx), any_abs(udy)); |
853 | |
|
854 | 0 | if (dmax * scale < fixed_1) |
855 | 0 | scale = (float)fixed_1 / dmax; |
856 | 0 | } |
857 | 0 | udx1 = (fixed) (udx * scale); |
858 | 0 | udy1 = (fixed) (udy * scale); |
859 | 0 | sx = x + udx1; |
860 | 0 | sy = y + udy1; |
861 | 0 | } |
862 | | /* |
863 | | * Back up 1 segment to keep the bookkeeping straight. |
864 | | */ |
865 | 0 | pseg = (pseg != 0 ? pseg->prev : psub->last); |
866 | 0 | if (!is_dash_segment) |
867 | 0 | goto d; |
868 | 0 | pl.e.p.x = sx; |
869 | 0 | pl.e.p.y = sy; |
870 | 0 | } |
871 | 0 | pl.vector.x = udx; |
872 | 0 | pl.vector.y = udy; |
873 | 0 | if (always_thin) { |
874 | 0 | pl.e.cdelta.x = pl.e.cdelta.y = 0; |
875 | 0 | pl.width.x = pl.width.y = 0; |
876 | 0 | pl.thin = true; |
877 | 0 | } else { |
878 | 0 | if (uniform != 0) { |
879 | | /* We can save a lot of work in this case. */ |
880 | | /* We know orient != orient_other. */ |
881 | 0 | double dpx = udx, dpy = udy; |
882 | 0 | double wl = device_line_width_scale / |
883 | 0 | hypot(dpx, dpy); |
884 | |
|
885 | 0 | pl.e.cdelta.x = (fixed) (dpx * wl); |
886 | 0 | pl.e.cdelta.y = (fixed) (dpy * wl); |
887 | | /* The width is the cap delta rotated by */ |
888 | | /* 90 degrees. */ |
889 | 0 | if (initial_matrix_reflected) |
890 | 0 | pl.width.x = pl.e.cdelta.y, pl.width.y = -pl.e.cdelta.x; |
891 | 0 | else |
892 | 0 | pl.width.x = -pl.e.cdelta.y, pl.width.y = pl.e.cdelta.x; |
893 | 0 | pl.thin = false; /* if not always_thin, */ |
894 | | /* then never thin. */ |
895 | |
|
896 | 0 | } else { |
897 | 0 | gs_point dpt; /* unscaled */ |
898 | 0 | float wl; |
899 | |
|
900 | 0 | code = gs_gstate_idtransform(pgs, |
901 | 0 | (float)udx, (float)udy, |
902 | 0 | &dpt); |
903 | 0 | if (code < 0) { |
904 | 0 | dpt.x = 0; dpt.y = 0; |
905 | | /* Swallow the error */ |
906 | 0 | code = 0; |
907 | 0 | } else { |
908 | 0 | wl = line_width_and_scale / |
909 | 0 | hypot(dpt.x, dpt.y); |
910 | | /* Construct the width vector in */ |
911 | | /* user space, still unscaled. */ |
912 | 0 | dpt.x *= wl; |
913 | 0 | dpt.y *= wl; |
914 | 0 | } |
915 | | |
916 | | /* |
917 | | * We now compute both perpendicular |
918 | | * and (optionally) parallel half-widths, |
919 | | * as deltas in device space. We use |
920 | | * a fixed-point, unscaled version of |
921 | | * gs_dtransform. The second computation |
922 | | * folds in a 90-degree rotation (in user |
923 | | * space, before transforming) in the |
924 | | * direction that corresponds to counter- |
925 | | * clockwise in device space. |
926 | | */ |
927 | 0 | pl.e.cdelta.x = (fixed) (dpt.x * xx); |
928 | 0 | pl.e.cdelta.y = (fixed) (dpt.y * yy); |
929 | 0 | if (orient != orient_portrait) |
930 | 0 | pl.e.cdelta.x += (fixed) (dpt.y * yx), |
931 | 0 | pl.e.cdelta.y += (fixed) (dpt.x * xy); |
932 | 0 | if (!reflected ^ initial_matrix_reflected) |
933 | 0 | dpt.x = -dpt.x, dpt.y = -dpt.y; |
934 | 0 | pl.width.x = (fixed) (dpt.y * xx), |
935 | 0 | pl.width.y = -(fixed) (dpt.x * yy); |
936 | 0 | if (orient != orient_portrait) |
937 | 0 | pl.width.x -= (fixed) (dpt.x * yx), |
938 | 0 | pl.width.y += (fixed) (dpt.y * xy); |
939 | 0 | pl.thin = width_is_thin(&pl); |
940 | 0 | } |
941 | 0 | if (!pl.thin) { |
942 | 0 | if (index) |
943 | 0 | dev->sgr.stroke_stored = false; |
944 | 0 | adjust_stroke(dev, &pl, pgs, false, |
945 | 0 | (pseg->prev == 0 || pseg->prev->type == s_start) && |
946 | 0 | (pseg->next == 0 || pseg->next->type == s_start) && |
947 | 0 | (zero_length || !is_closed), |
948 | 0 | COMBINE_FLAGS(flags)); |
949 | 0 | compute_caps(&pl); |
950 | 0 | } |
951 | 0 | } |
952 | 0 | if (index++) { |
953 | 0 | gs_line_join join = |
954 | 0 | (pseg_notes & not_first ? curve_join : pgs_lp->join); |
955 | 0 | int first; |
956 | 0 | pl_ptr lptr; |
957 | 0 | bool ensure_closed; |
958 | |
|
959 | 0 | if (join == gs_join_none) { |
960 | | /* Fake the end of a subpath so we get */ |
961 | | /* caps instead of joins. */ |
962 | 0 | first = 0; |
963 | 0 | lptr = 0; |
964 | 0 | index = 1; |
965 | 0 | } else { |
966 | 0 | first = (is_closed ? 1 : index - 2); |
967 | 0 | lptr = &pl; |
968 | 0 | } |
969 | | #ifdef AVOID_JOINING_TO_DASH_GAPS |
970 | | if (is_dash_segment) /* Never join to a dash segment */ |
971 | | lptr = NULL; |
972 | | #endif |
973 | 0 | if (pseg->type == s_gap) |
974 | 0 | { |
975 | 0 | lptr = NULL; |
976 | | /* We are always drawing one line segment behind, so make |
977 | | * sure we don't draw the next one. */ |
978 | 0 | index = 0; |
979 | 0 | } |
980 | |
|
981 | 0 | ensure_closed = ((to_path == &stroke_path_body && |
982 | 0 | lop_is_idempotent(pgs->log_op)) || |
983 | 0 | (lptr == NULL ? true : lptr->thin)); |
984 | | /* Draw the PREVIOUS line segment, joining it to lptr (or |
985 | | * capping if lptr == NULL. */ |
986 | 0 | code = (*line_proc) (to_path, to_path_reverse, ensure_closed, |
987 | 0 | first, &pl_prev, lptr, |
988 | 0 | pdevc, dev, pgs, params, &cbox, |
989 | 0 | uniform, join, initial_matrix_reflected, |
990 | 0 | COMBINE_FLAGS(flags)); |
991 | 0 | if (code < 0) |
992 | 0 | goto exit; |
993 | 0 | FILL_STROKE_PATH(pdev, always_thin, pcpath, false); |
994 | 0 | } else if (pseg->type == s_gap) { |
995 | | /* If this segment is a gap, then we don't want to draw it |
996 | | * next time! */ |
997 | 0 | index = 0; |
998 | 0 | } else |
999 | 0 | pl_first = pl; |
1000 | 0 | pl_prev = pl; |
1001 | 0 | x = sx, y = sy; |
1002 | 0 | flags = (flags<<4) | nf_all_from_arc; |
1003 | 0 | } |
1004 | 0 | if (index) { |
1005 | | /* If closed, join back to start, else cap. */ |
1006 | 0 | segment_notes notes = (pseg == 0 ? |
1007 | 0 | (const segment *)spath->first_subpath : |
1008 | 0 | pseg)->notes; |
1009 | 0 | gs_line_join join = (notes & not_first ? curve_join : |
1010 | 0 | pgs_lp->join); |
1011 | 0 | gs_line_cap cap; |
1012 | | /* For some reason, the Borland compiler requires the cast */ |
1013 | | /* in the following statement. */ |
1014 | 0 | pl_ptr lptr = |
1015 | 0 | (!is_closed || join == gs_join_none || zero_length ? |
1016 | 0 | (pl_ptr) 0 : (pl_ptr) & pl_first); |
1017 | |
|
1018 | | #ifdef AVOID_JOINING_TO_DASH_GAPS |
1019 | | if (lptr && psub->type == s_dash) |
1020 | | lptr = NULL; |
1021 | | #endif |
1022 | | /* If the subpath starts with a gap, then cap, don't join! */ |
1023 | 0 | if (lptr && psub->type == s_start && psub->next && psub->next->type == s_gap) |
1024 | 0 | lptr = NULL; |
1025 | |
|
1026 | 0 | flags = (((notes & sn_not_first) ? |
1027 | 0 | ((flags & nf_all_from_arc) | nf_some_from_arc) : 0) | |
1028 | 0 | ((notes & sn_dash_head) ? nf_dash_head : 0) | |
1029 | 0 | ((notes & sn_dash_tail) ? nf_dash_tail : 0) | |
1030 | 0 | (flags & ~nf_all_from_arc)); |
1031 | 0 | code = (*line_proc) (to_path, to_path_reverse, true, |
1032 | 0 | index - 1, &pl_prev, lptr, pdevc, |
1033 | 0 | dev, pgs, params, &cbox, uniform, join, |
1034 | 0 | initial_matrix_reflected, |
1035 | 0 | COMBINE_FLAGS(flags)); |
1036 | 0 | if (code < 0) |
1037 | 0 | goto exit; |
1038 | 0 | FILL_STROKE_PATH(pdev, always_thin, pcpath, false); |
1039 | 0 | cap = ((flags & nf_prev_dash_head) ? |
1040 | 0 | pgs_lp->start_cap : pgs_lp->dash_cap); |
1041 | 0 | if (traditional && lptr == 0 && cap != gs_cap_butt) { |
1042 | | /* Create the initial cap at last. */ |
1043 | 0 | code = stroke_add_initial_cap_compat(to_path, &pl_first, index == 1, pdevc, dev, pgs); |
1044 | 0 | if (code < 0) |
1045 | 0 | goto exit; |
1046 | 0 | FILL_STROKE_PATH(pdev, always_thin, pcpath, false); |
1047 | 0 | } |
1048 | 0 | } |
1049 | 0 | psub = (const subpath *)pseg; |
1050 | 0 | } |
1051 | 0 | if (to_path_reverse != NULL) |
1052 | 0 | code = gx_join_path_and_reverse(to_path, to_path_reverse); |
1053 | 0 | FILL_STROKE_PATH(pdev, always_thin, pcpath, true); |
1054 | 0 | exit: |
1055 | 0 | if (dev == (gx_device *)&cdev) |
1056 | 0 | cdev.target->sgr = cdev.sgr; |
1057 | 0 | if (to_path == &stroke_path_body) |
1058 | 0 | gx_path_free(&stroke_path_body, "gx_stroke_path_only error"); /* (only needed if error) */ |
1059 | 0 | if (to_path_reverse == &stroke_path_reverse) |
1060 | 0 | gx_path_free(&stroke_path_reverse, "gx_stroke_path_only error"); |
1061 | 0 | exf: |
1062 | 0 | if (dash_count) |
1063 | 0 | gx_path_free(&dpath, "gx_stroke_path exit(dash path)"); |
1064 | 0 | if (ppath->curve_count) |
1065 | 0 | gx_path_free(&fpath, "gx_stroke_path exit(flattened path)"); |
1066 | 0 | return code; |
1067 | 0 | } |
1068 | | |
1069 | | int |
1070 | | gx_stroke_path_only(gx_path * ppath, gx_path * to_path, gx_device * pdev, |
1071 | | const gs_gstate * pgs, const gx_stroke_params * params, |
1072 | | const gx_device_color * pdevc, const gx_clip_path * pcpath) |
1073 | 0 | { |
1074 | 0 | return gx_stroke_path_only_aux(ppath, to_path, pdev, pgs, params, pdevc, pcpath); |
1075 | 0 | } |
1076 | | |
1077 | | /* ------ Internal routines ------ */ |
1078 | | |
1079 | | /* |
1080 | | * Test whether a line is thin, i.e., whether the half-width, measured |
1081 | | * perpendicular to the line in device space, is less than 0.5 pixel. |
1082 | | * Unfortunately, the width values we computed are perpendicular to the |
1083 | | * line in *user* space, so we may have to do some extra work. |
1084 | | */ |
1085 | | static bool |
1086 | | width_is_thin(pl_ptr plp) |
1087 | 0 | { |
1088 | 0 | fixed dx, dy, wx = plp->width.x, wy = plp->width.y; |
1089 | | |
1090 | | /* If the line is horizontal or vertical, things are easy. */ |
1091 | 0 | if ((dy = plp->vector.y) == 0) |
1092 | 0 | return any_abs(wy) < fixed_half; |
1093 | 0 | if ((dx = plp->vector.x) == 0) |
1094 | 0 | return any_abs(wx) < fixed_half; |
1095 | | |
1096 | | /* For the longest time, we used to have a test here that |
1097 | | * attempted to trivially accept diagonal lines as being |
1098 | | * thin based on the components of the perpendicular |
1099 | | * width vector in device space as both being less than 0.5. |
1100 | | * Bug 702196 showed some examples where this was clearly |
1101 | | * wrong. |
1102 | | * |
1103 | | * The cause for this bug was that the 0.5 figure was wrong. |
1104 | | * For the point to be less than 1/2 a pixel perpendicular |
1105 | | * distant from the line, we'd need x^2 + y^2 < .5^2. |
1106 | | * For a 45 degree line, that'd be 2(x^2) < 1/4 = x^2 < 1/8 |
1107 | | * or x < sqr(1/8). 45 degree line is the "worst case", so |
1108 | | * if both horizontal and vertical widths are less than |
1109 | | * sqr(1/8), the line is thin. sqr(1/8) = 0.35355339059. |
1110 | | * So, we should be using sqr(1/8) rather than 0.5. |
1111 | | * |
1112 | | * Fixing this did indeed produce many many progressions, |
1113 | | * but left just the odd file still showing problems. |
1114 | | * |
1115 | | * Further investigations show that those cases were due to |
1116 | | * the use of "non-uniform" scaling matrices, for example |
1117 | | * (83 0 0 51 0 0). With such matrices, it's possible for |
1118 | | * nearly horizontal lines to be thin, but nearly vertical |
1119 | | * ones to be thick (or vice versa). Having the style of |
1120 | | * line "pop" between thick and thin in a single stroke |
1121 | | * looks very noticeable. |
1122 | | * |
1123 | | * We could change the trivial optimisation below to only |
1124 | | * apply in the 'uniform' case, but that would never actually |
1125 | | * trigger (as tested on the cluster), because all such |
1126 | | * cases are caught by the "always_thin" condition in the |
1127 | | * caller. |
1128 | | * |
1129 | | * Just removing the trivial test and leaving the 'complicated' |
1130 | | * test below us would leave us vulnerable to "popping", |
1131 | | * so we disable both. In practice this makes no difference |
1132 | | * to the number of tests showing diffs in the cluster. |
1133 | | */ |
1134 | | #if 0 /* DISABLED TEST, see above */ |
1135 | | { |
1136 | | /* thin_threshold = fixed sqr(1/8) - see above. */ |
1137 | | const fixed thin_threshold = float2fixed(0.35355339059f); |
1138 | | if (any_abs(wx) < thin_threshold && any_abs(wy) < thin_threshold) |
1139 | | return true; |
1140 | | } |
1141 | | |
1142 | | /* |
1143 | | * We have to do this the hard way, by actually computing the |
1144 | | * perpendicular distance. The distance from the point (U,V) |
1145 | | * from a line from (0,0) to (C,D) is |
1146 | | * abs(C*V - D*U) / sqrt(C^2 + D^2) |
1147 | | * In this case, (U,V) is plp->width, and (C,D) is (dx,dy). |
1148 | | */ |
1149 | | { |
1150 | | double C = dx, D = dy; |
1151 | | double num = C * wy - D * wx; |
1152 | | double denom = hypot(C, D); |
1153 | | |
1154 | | /* both num and denom are scaled by fixed_scale^2, */ |
1155 | | /* so we don't need to do any de-scaling for the test. */ |
1156 | | return fabs(num) < denom * 0.5; |
1157 | | } |
1158 | | #else |
1159 | 0 | return false; |
1160 | 0 | #endif |
1161 | 0 | } |
1162 | | |
1163 | | /* Adjust the endpoints and width of a stroke segment along a specified axis */ |
1164 | | static void |
1165 | | adjust_stroke_transversal(pl_ptr plp, const gs_gstate * pgs, bool thin, bool horiz) |
1166 | 0 | { |
1167 | 0 | fixed *pw; |
1168 | 0 | fixed *pov; |
1169 | 0 | fixed *pev; |
1170 | 0 | fixed w, w2; |
1171 | 0 | fixed adj2; |
1172 | |
|
1173 | 0 | if (horiz) { |
1174 | | /* More horizontal stroke */ |
1175 | 0 | pw = &plp->width.y, pov = &plp->o.p.y, pev = &plp->e.p.y; |
1176 | 0 | adj2 = STROKE_ADJUSTMENT(thin, pgs, y) << 1; |
1177 | 0 | } else { |
1178 | | /* More vertical stroke */ |
1179 | 0 | pw = &plp->width.x, pov = &plp->o.p.x, pev = &plp->e.p.x; |
1180 | 0 | adj2 = STROKE_ADJUSTMENT(thin, pgs, x) << 1; |
1181 | 0 | } |
1182 | | /* Round the larger component of the width up or down, */ |
1183 | | /* whichever way produces a result closer to the correct width. */ |
1184 | | /* Note that just rounding the larger component */ |
1185 | | /* may not produce the correct result. */ |
1186 | 0 | w = *pw; |
1187 | 0 | if (w > 0) |
1188 | 0 | w2 = fixed_rounded(w << 1); /* full line width */ |
1189 | 0 | else |
1190 | 0 | w2 = -fixed_rounded(-w << 1); /* full line width */ |
1191 | 0 | if (w2 == 0 && *pw != 0) { |
1192 | | /* Make sure thin lines don't disappear. */ |
1193 | 0 | w2 = (*pw < 0 ? -fixed_1 + adj2 : fixed_1 - adj2); |
1194 | 0 | *pw = arith_rshift_1(w2); |
1195 | 0 | } |
1196 | | /* Only adjust the endpoints if the line is horizontal or vertical. */ |
1197 | 0 | if (*pov == *pev) { |
1198 | | /* We're going to round the endpoint coordinates, so */ |
1199 | | /* take the fill adjustment into account now. */ |
1200 | 0 | if (w >= 0) |
1201 | 0 | w2 += adj2; |
1202 | 0 | else |
1203 | 0 | w2 = adj2 - w2; |
1204 | 0 | if (w2 & fixed_1) /* odd width, move to half-pixel */ |
1205 | 0 | *pov = *pev = fixed_floor(*pov) + fixed_half; |
1206 | 0 | else /* even width, move to pixel */ |
1207 | 0 | *pov = *pev = fixed_rounded(*pov); |
1208 | |
|
1209 | 0 | } |
1210 | 0 | } |
1211 | | |
1212 | | static void |
1213 | | adjust_stroke_longitude(pl_ptr plp, const gs_gstate * pgs, |
1214 | | bool thin, bool horiz, |
1215 | | gs_line_cap start_cap, gs_line_cap end_cap) |
1216 | 0 | { |
1217 | |
|
1218 | 0 | fixed *pow = (horiz ? &plp->o.p.y : &plp->o.p.x); |
1219 | 0 | fixed *pew = (horiz ? &plp->e.p.y : &plp->e.p.x); |
1220 | | |
1221 | | /* Only adjust the endpoints if the line is horizontal or vertical. |
1222 | | Debugged with pdfwrite->ppmraw 72dpi file2.pdf */ |
1223 | 0 | if (*pow == *pew) { |
1224 | 0 | fixed *pov = (horiz ? &plp->o.p.x : &plp->o.p.y); |
1225 | 0 | fixed *pev = (horiz ? &plp->e.p.x : &plp->e.p.y); |
1226 | 0 | fixed length = any_abs(*pov - *pev); |
1227 | 0 | fixed length_r, length_r_2; |
1228 | 0 | fixed mv = (*pov + *pev) / 2, mv_r; |
1229 | 0 | fixed adj2 = (horiz ? STROKE_ADJUSTMENT(thin, pgs, x) |
1230 | 0 | : STROKE_ADJUSTMENT(thin, pgs, y)) << 1; |
1231 | | |
1232 | | /* fixme : |
1233 | | The best value for adjust_longitude is whether |
1234 | | the dash is isolated and doesn't cover entire segment. |
1235 | | The current data structure can't pass this info. |
1236 | | Therefore we restrict adjust_stroke_longitude with 1 pixel length. |
1237 | | */ |
1238 | 0 | if (length > fixed_1) /* comparefiles/file2.pdf */ |
1239 | 0 | return; |
1240 | 0 | if (start_cap == gs_cap_butt || end_cap == gs_cap_butt) { |
1241 | 0 | length_r = fixed_rounded(length); |
1242 | 0 | if (length_r < fixed_1) |
1243 | 0 | length_r = fixed_1; |
1244 | 0 | length_r_2 = length_r / 2; |
1245 | 0 | } else { |
1246 | | /* Account width for proper placing cap centers. */ |
1247 | 0 | fixed width = any_abs(horiz ? plp->width.y : plp->width.x); |
1248 | |
|
1249 | 0 | length_r = fixed_rounded(length + width * 2 + adj2); |
1250 | 0 | length_r_2 = fixed_rounded(length) / 2; |
1251 | 0 | } |
1252 | 0 | if (length_r & fixed_1) |
1253 | 0 | mv_r = fixed_floor(mv) + fixed_half; |
1254 | 0 | else |
1255 | 0 | mv_r = fixed_floor(mv); |
1256 | 0 | if (*pov < *pev) { |
1257 | 0 | *pov = mv_r - length_r_2; |
1258 | 0 | *pev = mv_r + length_r_2; |
1259 | 0 | } else { |
1260 | 0 | *pov = mv_r + length_r_2; |
1261 | 0 | *pev = mv_r - length_r_2; |
1262 | 0 | } |
1263 | 0 | } |
1264 | 0 | } |
1265 | | |
1266 | | /* Adjust the endpoints and width of a stroke segment */ |
1267 | | /* to achieve more uniform rendering. */ |
1268 | | /* Only o.p, e.p, e.cdelta, and width have been set. */ |
1269 | | static void |
1270 | | adjust_stroke(gx_device *dev, pl_ptr plp, const gs_gstate * pgs, |
1271 | | bool thin, bool adjust_longitude, note_flags flags) |
1272 | 0 | { |
1273 | 0 | bool horiz, adjust = true; |
1274 | 0 | gs_line_cap start_cap = (flags & nf_dash_head ? |
1275 | 0 | pgs->line_params.dash_cap : |
1276 | 0 | pgs->line_params.start_cap); |
1277 | 0 | gs_line_cap end_cap = (flags & nf_dash_tail ? |
1278 | 0 | pgs->line_params.dash_cap : |
1279 | 0 | pgs->line_params.end_cap); |
1280 | | |
1281 | | /* If stroke_adjustment is disabled, or this isn't a horizontal or |
1282 | | * vertical line, then bale. */ |
1283 | 0 | if (!pgs->stroke_adjust || (plp->width.x != 0 && plp->width.y != 0)) { |
1284 | 0 | dev->sgr.stroke_stored = false; |
1285 | 0 | return; /* don't adjust */ |
1286 | 0 | } |
1287 | | /* Recognizing gradients, which some obsolete software |
1288 | | represent as a set of parallel strokes. |
1289 | | Such strokes must not be adjusted - bug 687974. */ |
1290 | 0 | if (dev->sgr.stroke_stored && |
1291 | 0 | (start_cap == gs_cap_butt || end_cap == gs_cap_butt) && |
1292 | 0 | dev->sgr.orig[3].x == plp->vector.x && dev->sgr.orig[3].y == plp->vector.y) { |
1293 | | /* Parallel. */ |
1294 | 0 | if ((int64_t)(plp->o.p.x - dev->sgr.orig[0].x) * plp->vector.x == |
1295 | 0 | (int64_t)(plp->o.p.y - dev->sgr.orig[0].y) * plp->vector.y && |
1296 | 0 | (int64_t)(plp->e.p.x - dev->sgr.orig[1].x) * plp->vector.x == |
1297 | 0 | (int64_t)(plp->e.p.y - dev->sgr.orig[1].y) * plp->vector.y) { |
1298 | | /* Transversal shift. */ |
1299 | 0 | if (any_abs(plp->o.p.x - dev->sgr.orig[0].x) <= any_abs(plp->width.x + dev->sgr.orig[2].x) && |
1300 | 0 | any_abs(plp->o.p.y - dev->sgr.orig[0].y) <= any_abs(plp->width.y + dev->sgr.orig[2].y) && |
1301 | 0 | any_abs(plp->e.p.x - dev->sgr.orig[1].x) <= any_abs(plp->width.x + dev->sgr.orig[2].x) && |
1302 | 0 | any_abs(plp->e.p.y - dev->sgr.orig[1].y) <= any_abs(plp->width.y + dev->sgr.orig[2].y)) { |
1303 | | /* The strokes were contacting or overlapping. */ |
1304 | 0 | if (any_abs(plp->o.p.x - dev->sgr.orig[0].x) >= any_abs(plp->width.x + dev->sgr.orig[2].x) / 2 && |
1305 | 0 | any_abs(plp->o.p.y - dev->sgr.orig[0].y) >= any_abs(plp->width.y + dev->sgr.orig[2].y) / 2 && |
1306 | 0 | any_abs(plp->e.p.x - dev->sgr.orig[1].x) >= any_abs(plp->width.x + dev->sgr.orig[2].x) / 2 && |
1307 | 0 | any_abs(plp->e.p.y - dev->sgr.orig[1].y) >= any_abs(plp->width.y + dev->sgr.orig[2].y) / 2) { |
1308 | | /* The strokes were not much overlapping. */ |
1309 | 0 | if (!(any_abs(plp->o.p.x - dev->sgr.adjusted[0].x) <= any_abs(plp->width.x + dev->sgr.adjusted[2].x) && |
1310 | 0 | any_abs(plp->o.p.y - dev->sgr.adjusted[0].y) <= any_abs(plp->width.y + dev->sgr.adjusted[2].y) && |
1311 | 0 | any_abs(plp->e.p.x - dev->sgr.adjusted[1].x) <= any_abs(plp->width.x + dev->sgr.adjusted[2].x) && |
1312 | 0 | any_abs(plp->e.p.y - dev->sgr.adjusted[1].y) <= any_abs(plp->width.y + dev->sgr.adjusted[2].y))) { |
1313 | | /* they became not contacting. |
1314 | | We should not have adjusted the last stroke. Since if we did, |
1315 | | lets change the current one to restore the contact, |
1316 | | so that we don't leave gaps when rasterising. See bug 687974. |
1317 | | */ |
1318 | 0 | fixed delta_w_x = (dev->sgr.adjusted[2].x - dev->sgr.orig[2].x); |
1319 | 0 | fixed delta_w_y = (dev->sgr.adjusted[2].y - dev->sgr.orig[2].y); |
1320 | 0 | fixed shift_o_x = (dev->sgr.adjusted[0].x - dev->sgr.orig[0].x); |
1321 | 0 | fixed shift_o_y = (dev->sgr.adjusted[0].y - dev->sgr.orig[0].y); |
1322 | 0 | fixed shift_e_x = (dev->sgr.adjusted[1].x - dev->sgr.orig[1].x); /* Must be same, but we prefer clarity. */ |
1323 | 0 | fixed shift_e_y = (dev->sgr.adjusted[1].y - dev->sgr.orig[1].y); |
1324 | |
|
1325 | 0 | if (plp->o.p.x < dev->sgr.orig[0].x || |
1326 | 0 | (plp->o.p.x == dev->sgr.orig[0].x && plp->o.p.y < dev->sgr.orig[0].y)) { |
1327 | | /* Left contact, adjust to keep the contact. */ |
1328 | 0 | if_debug4m('O', dev->memory, "[O]don't adjust {{%f,%f},{%f,%f}}\n", |
1329 | 0 | fixed2float(plp->o.p.x), fixed2float(plp->o.p.y), |
1330 | 0 | fixed2float(plp->e.p.x), fixed2float(plp->e.p.y)); |
1331 | 0 | plp->width.x += (shift_o_x - delta_w_x) / 2; |
1332 | 0 | plp->width.y += (shift_o_y - delta_w_y) / 2; |
1333 | 0 | plp->o.p.x += (shift_o_x - delta_w_x) / 2; |
1334 | 0 | plp->o.p.y += (shift_o_y - delta_w_y) / 2; |
1335 | 0 | plp->e.p.x += (shift_e_x - delta_w_x) / 2; |
1336 | 0 | plp->e.p.y += (shift_e_y - delta_w_y) / 2; |
1337 | 0 | adjust = false; |
1338 | 0 | } else { |
1339 | | /* Right contact, adjust to keep the contact. */ |
1340 | 0 | if_debug4m('O', dev->memory, "[O]don't adjust {{%f,%f},{%f,%f}}\n", |
1341 | 0 | fixed2float(plp->o.p.x), fixed2float(plp->o.p.y), |
1342 | 0 | fixed2float(plp->e.p.x), fixed2float(plp->e.p.y)); |
1343 | 0 | plp->width.x -= (shift_o_x + delta_w_x) / 2; |
1344 | 0 | plp->width.y -= (shift_o_y + delta_w_y) / 2; |
1345 | 0 | plp->o.p.x += (shift_o_x + delta_w_x) / 2; |
1346 | 0 | plp->o.p.y += (shift_o_y + delta_w_y) / 2; |
1347 | 0 | plp->e.p.x += (shift_e_x + delta_w_x) / 2; |
1348 | 0 | plp->e.p.y += (shift_e_y + delta_w_y) / 2; |
1349 | 0 | adjust = false; |
1350 | 0 | } |
1351 | 0 | } |
1352 | 0 | } |
1353 | 0 | } |
1354 | 0 | } |
1355 | 0 | } |
1356 | 0 | if ((start_cap == gs_cap_butt) || (end_cap == gs_cap_butt)) { |
1357 | 0 | dev->sgr.stroke_stored = true; |
1358 | 0 | dev->sgr.orig[0] = plp->o.p; |
1359 | 0 | dev->sgr.orig[1] = plp->e.p; |
1360 | 0 | dev->sgr.orig[2] = plp->width; |
1361 | 0 | dev->sgr.orig[3] = plp->vector; |
1362 | 0 | } else |
1363 | 0 | dev->sgr.stroke_stored = false; |
1364 | 0 | if (adjust) { |
1365 | 0 | horiz = (any_abs(plp->width.x) <= any_abs(plp->width.y)); |
1366 | 0 | adjust_stroke_transversal(plp, pgs, thin, horiz); |
1367 | 0 | if (adjust_longitude) |
1368 | 0 | adjust_stroke_longitude(plp, pgs, thin, horiz, start_cap, end_cap); |
1369 | 0 | } |
1370 | 0 | if ((start_cap == gs_cap_butt) || (end_cap == gs_cap_butt)) { |
1371 | 0 | dev->sgr.adjusted[0] = plp->o.p; |
1372 | 0 | dev->sgr.adjusted[1] = plp->e.p; |
1373 | 0 | dev->sgr.adjusted[2] = plp->width; |
1374 | 0 | dev->sgr.adjusted[3] = plp->vector; |
1375 | 0 | } |
1376 | 0 | } |
1377 | | |
1378 | | /* Compute the intersection of two lines. This is a messy algorithm */ |
1379 | | /* that somehow ought to be useful in more places than just here.... */ |
1380 | | /* If the lines are (nearly) parallel, return -1 without setting *pi; */ |
1381 | | /* otherwise, return 0 if the intersection is beyond *pp1 and *pp2 in */ |
1382 | | /* the direction determined by *pd1 and *pd2, and 1 otherwise. */ |
1383 | | static int |
1384 | | line_intersect( |
1385 | | p_ptr pp1, /* point on 1st line */ |
1386 | | p_ptr pd1, /* slope of 1st line (dx,dy) */ |
1387 | | p_ptr pp2, /* point on 2nd line */ |
1388 | | p_ptr pd2, /* slope of 2nd line */ |
1389 | | p_ptr pi) |
1390 | 0 | { /* return intersection here */ |
1391 | | /* We don't have to do any scaling, the factors all work out right. */ |
1392 | 0 | double u1 = pd1->x, v1 = pd1->y; |
1393 | 0 | double u2 = pd2->x, v2 = pd2->y; |
1394 | 0 | double denom = u1 * v2 - u2 * v1; |
1395 | 0 | double xdiff = pp2->x - pp1->x; |
1396 | 0 | double ydiff = pp2->y - pp1->y; |
1397 | 0 | double f1; |
1398 | 0 | double max_result = any_abs(denom) * (double)max_fixed; |
1399 | |
|
1400 | | #if defined(DEBUG) && !defined(GS_THREADSAFE) |
1401 | | if (gs_debug_c('O')) { |
1402 | | dlprintf4("[o]Intersect %f,%f(%f/%f)", |
1403 | | fixed2float(pp1->x), fixed2float(pp1->y), |
1404 | | fixed2float(pd1->x), fixed2float(pd1->y)); |
1405 | | dlprintf4(" & %f,%f(%f/%f),\n", |
1406 | | fixed2float(pp2->x), fixed2float(pp2->y), |
1407 | | fixed2float(pd2->x), fixed2float(pd2->y)); |
1408 | | dlprintf3("\txdiff=%f ydiff=%f denom=%f ->\n", |
1409 | | xdiff, ydiff, denom); |
1410 | | } |
1411 | | #endif |
1412 | | /* Check for degenerate result. */ |
1413 | 0 | if (any_abs(xdiff) >= max_result || any_abs(ydiff) >= max_result) { |
1414 | | /* The lines are nearly parallel, */ |
1415 | | /* or one of them has zero length. Punt. */ |
1416 | 0 | if_debug0('O', "\tdegenerate!\n"); |
1417 | 0 | return -1; |
1418 | 0 | } |
1419 | 0 | f1 = (v2 * xdiff - u2 * ydiff) / denom; |
1420 | 0 | pi->x = pp1->x + (fixed) (f1 * u1); |
1421 | 0 | pi->y = pp1->y + (fixed) (f1 * v1); |
1422 | 0 | if_debug2('O', "\t%f,%f\n", |
1423 | 0 | fixed2float(pi->x), fixed2float(pi->y)); |
1424 | 0 | return (f1 >= 0 && (v1 * xdiff >= u1 * ydiff ? denom >= 0 : denom < 0) ? 0 : 1); |
1425 | 0 | } |
1426 | | |
1427 | | /* Set up the width and delta parameters for a thin line. */ |
1428 | | /* We only approximate the width and height. */ |
1429 | | static void |
1430 | | set_thin_widths(register pl_ptr plp) |
1431 | 0 | { |
1432 | 0 | fixed dx = plp->e.p.x - plp->o.p.x, dy = plp->e.p.y - plp->o.p.y; |
1433 | |
|
1434 | 0 | #define TRSIGN(v, c) ((v) >= 0 ? (c) : -(c)) |
1435 | 0 | if (any_abs(dx) > any_abs(dy)) { |
1436 | 0 | plp->width.x = plp->e.cdelta.y = 0; |
1437 | 0 | plp->width.y = plp->e.cdelta.x = TRSIGN(dx, fixed_half); |
1438 | 0 | } else { |
1439 | 0 | plp->width.y = plp->e.cdelta.x = 0; |
1440 | 0 | plp->width.x = -(plp->e.cdelta.y = TRSIGN(dy, fixed_half)); |
1441 | 0 | } |
1442 | 0 | #undef TRSIGN |
1443 | 0 | } |
1444 | | |
1445 | | /* Draw a line on the device. */ |
1446 | | /* Treat no join the same as a bevel join. */ |
1447 | | /* rpath should always be NULL, hence ensure_closed can be ignored */ |
1448 | | static int |
1449 | | stroke_fill(gx_path * ppath, gx_path * rpath, bool ensure_closed, int first, |
1450 | | register pl_ptr plp, pl_ptr nplp, const gx_device_color * pdevc, |
1451 | | gx_device * dev, const gs_gstate * pgs, |
1452 | | const gx_stroke_params * params, const gs_fixed_rect * pbbox, |
1453 | | int uniform, gs_line_join join, bool reflected, |
1454 | | note_flags flags) |
1455 | 0 | { |
1456 | 0 | const fixed lix = plp->o.p.x; |
1457 | 0 | const fixed liy = plp->o.p.y; |
1458 | 0 | const fixed litox = plp->e.p.x; |
1459 | 0 | const fixed litoy = plp->e.p.y; |
1460 | | |
1461 | | /* assert(lop_is_idempotent(pgs->log_op)); */ |
1462 | 0 | if (plp->thin) { |
1463 | | /* Minimum-width line, don't have to be careful with caps/joins. */ |
1464 | 0 | return (*dev_proc(dev, draw_thin_line))(dev, lix, liy, litox, litoy, |
1465 | 0 | pdevc, pgs->log_op, |
1466 | 0 | pgs->fill_adjust.x, |
1467 | 0 | pgs->fill_adjust.y); |
1468 | 0 | } |
1469 | | /* Check for being able to fill directly. */ |
1470 | 0 | { |
1471 | 0 | const gx_line_params *pgs_lp = gs_currentlineparams_inline(pgs); |
1472 | 0 | gs_line_cap start_cap = (flags & nf_dash_head ? |
1473 | 0 | pgs_lp->dash_cap : pgs_lp->start_cap); |
1474 | 0 | gs_line_cap end_cap = (flags & nf_dash_tail ? |
1475 | 0 | pgs_lp->dash_cap : pgs_lp->end_cap); |
1476 | |
|
1477 | 0 | if (first != 0) |
1478 | 0 | start_cap = gs_cap_butt; |
1479 | 0 | if (nplp != 0) |
1480 | 0 | end_cap = gs_cap_butt; |
1481 | 0 | if (!plp->thin && (nplp == 0 || !nplp->thin) |
1482 | 0 | && (start_cap == gs_cap_butt || start_cap == gs_cap_square) |
1483 | 0 | && (end_cap == gs_cap_butt || end_cap == gs_cap_square) |
1484 | 0 | && (join == gs_join_bevel || join == gs_join_miter || |
1485 | 0 | join == gs_join_none) |
1486 | 0 | && (pgs->fill_adjust.x | pgs->fill_adjust.y) == 0 |
1487 | 0 | ) { |
1488 | 0 | gs_fixed_point points[6]; |
1489 | 0 | int npoints, code; |
1490 | 0 | fixed ax, ay, bx, by; |
1491 | |
|
1492 | 0 | npoints = cap_points(start_cap, &plp->o, points); |
1493 | 0 | if (nplp == 0) |
1494 | 0 | code = cap_points(end_cap, &plp->e, points + npoints); |
1495 | 0 | else |
1496 | 0 | code = line_join_points(pgs_lp, plp, nplp, points + npoints, |
1497 | 0 | (uniform ? (gs_matrix *) 0 : |
1498 | 0 | &ctm_only(pgs)), join, reflected); |
1499 | 0 | if (code < 0) |
1500 | 0 | goto general; |
1501 | | /* Make sure the parallelogram fill won't overflow. */ |
1502 | 0 | #define SUB_OVERFLOWS(r, u, v)\ |
1503 | 0 | (((r = u - v) ^ u) < 0 && (u ^ v) < 0) |
1504 | 0 | if (SUB_OVERFLOWS(ax, points[0].x, points[1].x) || |
1505 | 0 | SUB_OVERFLOWS(ay, points[0].y, points[1].y) || |
1506 | 0 | SUB_OVERFLOWS(bx, points[2].x, points[1].x) || |
1507 | 0 | SUB_OVERFLOWS(by, points[2].y, points[1].y) |
1508 | 0 | ) |
1509 | 0 | goto general; |
1510 | 0 | #undef SUB_OVERFLOWS |
1511 | 0 | if (nplp != 0) { |
1512 | 0 | if (join == gs_join_miter) { |
1513 | | /* Make sure we have a bevel and not a miter. */ |
1514 | 0 | if (!(points[2].x == plp->e.co.x && |
1515 | 0 | points[2].y == plp->e.co.y && |
1516 | 0 | points[5].x == plp->e.ce.x && |
1517 | 0 | points[5].y == plp->e.ce.y) |
1518 | 0 | ) |
1519 | 0 | goto fill; |
1520 | 0 | } { |
1521 | 0 | const gs_fixed_point *bevel = points + 2; |
1522 | | |
1523 | | /* Identify which 3 points define the bevel triangle. */ |
1524 | 0 | if (points[3].x == nplp->o.p.x && |
1525 | 0 | points[3].y == nplp->o.p.y |
1526 | 0 | ) |
1527 | 0 | ++bevel; |
1528 | | /* Fill the bevel. */ |
1529 | 0 | code = (*dev_proc(dev, fill_triangle)) (dev, |
1530 | 0 | bevel->x, bevel->y, |
1531 | 0 | bevel[1].x - bevel->x, bevel[1].y - bevel->y, |
1532 | 0 | bevel[2].x - bevel->x, bevel[2].y - bevel->y, |
1533 | 0 | pdevc, pgs->log_op); |
1534 | 0 | if (code < 0) |
1535 | 0 | return code; |
1536 | 0 | } |
1537 | 0 | } |
1538 | | /* Fill the body of the stroke. */ |
1539 | 0 | return (*dev_proc(dev, fill_parallelogram)) (dev, |
1540 | 0 | points[1].x, points[1].y, |
1541 | 0 | ax, ay, bx, by, |
1542 | 0 | pdevc, pgs->log_op); |
1543 | 0 | fill: |
1544 | 0 | code = add_points(ppath, points, npoints + code, true); |
1545 | 0 | if (code < 0) |
1546 | 0 | return code; |
1547 | 0 | return gx_path_close_subpath(ppath); |
1548 | 0 | } |
1549 | 0 | } |
1550 | | /* General case: construct a path for the fill algorithm. */ |
1551 | 0 | general: |
1552 | 0 | return stroke_add(ppath, rpath, ensure_closed, first, plp, nplp, pdevc, |
1553 | 0 | dev, pgs, params, pbbox, uniform, join, reflected, |
1554 | 0 | flags); |
1555 | 0 | } |
1556 | | |
1557 | | /* Add a segment to the path. This handles all the complex cases. */ |
1558 | | static int |
1559 | | stroke_add(gx_path * ppath, gx_path * rpath, bool ensure_closed, int first, |
1560 | | pl_ptr plp, pl_ptr nplp, const gx_device_color * pdevc, |
1561 | | gx_device * dev, const gs_gstate * pgs, |
1562 | | const gx_stroke_params * params, |
1563 | | const gs_fixed_rect * ignore_pbbox, int uniform, |
1564 | | gs_line_join join, bool reflected, note_flags flags) |
1565 | 0 | { |
1566 | 0 | const gx_line_params *pgs_lp = gs_currentlineparams_inline(pgs); |
1567 | 0 | gs_fixed_point points[8]; |
1568 | 0 | int npoints; |
1569 | 0 | int code; |
1570 | 0 | bool moveto_first = true; |
1571 | 0 | gs_line_cap start_cap = (flags & nf_dash_head ? |
1572 | 0 | pgs_lp->dash_cap : pgs_lp->start_cap); |
1573 | 0 | gs_line_cap end_cap = (flags & nf_dash_tail ? |
1574 | 0 | pgs_lp->dash_cap : pgs_lp->end_cap); |
1575 | |
|
1576 | 0 | if (plp->thin) { |
1577 | | /* We didn't set up the endpoint parameters before, */ |
1578 | | /* because the line was thin. Do it now. */ |
1579 | 0 | set_thin_widths(plp); |
1580 | 0 | adjust_stroke(dev, plp, pgs, true, first == 0 && nplp == 0, flags); |
1581 | 0 | compute_caps(plp); |
1582 | 0 | } |
1583 | | /* Create an initial cap if desired. */ |
1584 | 0 | if (first == 0 && start_cap == gs_cap_round) { |
1585 | 0 | if ((code = gx_path_add_point(ppath, plp->o.co.x, plp->o.co.y)) < 0 || |
1586 | 0 | (code = add_pie_cap(ppath, &plp->o)) < 0) |
1587 | 0 | return code; |
1588 | 0 | npoints = 0; |
1589 | 0 | moveto_first = false; |
1590 | 0 | } else { |
1591 | 0 | if ((npoints = cap_points((first == 0 ? start_cap : gs_cap_butt), |
1592 | 0 | &plp->o, points)) < 0) |
1593 | 0 | return npoints; |
1594 | 0 | } |
1595 | 0 | if (nplp == 0) { |
1596 | | /* Add a final cap. */ |
1597 | 0 | if (end_cap == gs_cap_round) { |
1598 | 0 | ASSIGN_POINT(&points[npoints], plp->e.co); |
1599 | 0 | ++npoints; |
1600 | 0 | if ((code = add_points(ppath, points, npoints, moveto_first)) < 0) |
1601 | 0 | return code; |
1602 | 0 | code = add_pie_cap(ppath, &plp->e); |
1603 | 0 | goto done; |
1604 | 0 | } |
1605 | 0 | code = cap_points(end_cap, &plp->e, points + npoints); |
1606 | 0 | } else if (nplp->thin) /* no join */ |
1607 | 0 | code = cap_points(gs_cap_butt, &plp->e, points + npoints); |
1608 | 0 | else if (join == gs_join_round) { |
1609 | 0 | ASSIGN_POINT(&points[npoints], plp->e.co); |
1610 | 0 | ++npoints; |
1611 | 0 | if ((code = add_points(ppath, points, npoints, moveto_first)) < 0) |
1612 | 0 | return code; |
1613 | 0 | code = add_pie_join(ppath, plp, nplp, reflected, true); |
1614 | 0 | goto done; |
1615 | 0 | } else if (flags & nf_all_from_arc) { |
1616 | | /* If all the segments in 'prev' and 'current' are from a curve |
1617 | | * then the join should actually be a round one, because it would |
1618 | | * have been round if we had flattened it enough. */ |
1619 | 0 | ASSIGN_POINT(&points[npoints], plp->e.co); |
1620 | 0 | ++npoints; |
1621 | 0 | if ((code = add_points(ppath, points, npoints, moveto_first)) < 0) |
1622 | 0 | return code; |
1623 | 0 | code = add_pie_join(ppath, plp, nplp, reflected, false); |
1624 | 0 | goto done; |
1625 | 0 | } else /* non-round join */ |
1626 | 0 | code = line_join_points(pgs_lp, plp, nplp, points + npoints, |
1627 | 0 | (uniform ? (gs_matrix *) 0 : &ctm_only(pgs)), |
1628 | 0 | join, reflected); |
1629 | 0 | if (code < 0) |
1630 | 0 | return code; |
1631 | 0 | code = add_points(ppath, points, npoints + code, moveto_first); |
1632 | 0 | done: |
1633 | 0 | if (code < 0) |
1634 | 0 | return code; |
1635 | 0 | if ((flags & nf_some_from_arc) && (!plp->thin) && |
1636 | 0 | (nplp != NULL) && (!nplp->thin)) |
1637 | 0 | code = join_under_pie(ppath, plp, nplp, reflected); |
1638 | 0 | return gx_path_close_subpath(ppath); |
1639 | 0 | } |
1640 | | |
1641 | | /* When painting the 'underjoin' (the 'inside' of a join), we |
1642 | | * need to take special care if the curve is particularly wide as |
1643 | | * the leading edge of the underside of the first stroked segment |
1644 | | * may be beyond the leading edge of the underside of the second |
1645 | | * stroked segment. Similarly, the trailing edge of the second |
1646 | | * stroked segment may be behing the trailing edge of the first |
1647 | | * stroked segment. We detect those cases here. |
1648 | | * |
1649 | | * We detect the first case by projecting plp.width onto nplp.vector. |
1650 | | * If the projected vector is longer then nplp.vector, we have a |
1651 | | * problem. |
1652 | | * |
1653 | | * len_vector_squared = nplp.vector.x * nplp.vector.x + nplp.vector.y * nplp.nvector.y |
1654 | | * len_vector = sqr(len_vector_squared) |
1655 | | * len_projection_unnormalised = plp.width.x * nplp.vector.x + plp.width.y * nplp.vector.y |
1656 | | * len_projection = len_projection_unnormalised / len_vector |
1657 | | * |
1658 | | * len_projection > len_vector === len_projection_unnormalised > len_vector * len_vector |
1659 | | * === len_projection_unnormalised > len_vector_squared |
1660 | | */ |
1661 | | |
1662 | | #ifdef SLOWER_BUT_MORE_ACCURATE_STROKING |
1663 | | static bool |
1664 | | wide_underjoin(pl_ptr plp, pl_ptr nplp) |
1665 | | { |
1666 | | double h_squared = (double)nplp->vector.x * nplp->vector.x + (double)nplp->vector.y * nplp->vector.y; |
1667 | | double dot = (double)plp->width.x * nplp->vector.x + (double)plp->width.y * nplp->vector.y; |
1668 | | |
1669 | | if (dot < 0) |
1670 | | dot = -dot; |
1671 | | if (dot > h_squared) |
1672 | | return 1; |
1673 | | |
1674 | | h_squared = (double)plp->vector.x * plp->vector.x + (double)plp->vector.y * plp->vector.y; |
1675 | | dot = (double)nplp->width.x * plp->vector.x + (double)nplp->width.y * plp->vector.y; |
1676 | | if (dot < 0) |
1677 | | dot = -dot; |
1678 | | if (dot > h_squared) |
1679 | | return 1; |
1680 | | |
1681 | | return 0; |
1682 | | } |
1683 | | #endif |
1684 | | |
1685 | | static int |
1686 | | check_miter(const gx_line_params * pgs_lp, pl_ptr plp, pl_ptr nplp, |
1687 | | const gs_matrix * pmat, p_ptr outp, p_ptr np, p_ptr mpt, |
1688 | | bool ccw0) |
1689 | 0 | { |
1690 | | /* |
1691 | | * Check whether a miter join is appropriate. |
1692 | | * Let a, b be the angles of the two lines. |
1693 | | * We check tan(a-b) against the miter_check |
1694 | | * by using the following formula: |
1695 | | * If tan(a)=u1/v1 and tan(b)=u2/v2, then |
1696 | | * tan(a-b) = (u1*v2 - u2*v1) / (u1*u2 + v1*v2). |
1697 | | * |
1698 | | * We can do all the computations unscaled, |
1699 | | * because we're only concerned with ratios. |
1700 | | * However, if we have a non-uniform coordinate |
1701 | | * system (indicated by pmat != 0), we must do the |
1702 | | * computations in user space. |
1703 | | */ |
1704 | 0 | float check; |
1705 | 0 | double u1, v1, u2, v2; |
1706 | 0 | double num, denom; |
1707 | 0 | int code; |
1708 | | |
1709 | | /* |
1710 | | * Don't bother with the miter check if the two |
1711 | | * points to be joined are very close together, |
1712 | | * namely, in the same square half-pixel. |
1713 | | */ |
1714 | 0 | if (fixed2long(outp->x << 1) == fixed2long(np->x << 1) && |
1715 | 0 | fixed2long(outp->y << 1) == fixed2long(np->y << 1)) |
1716 | 0 | return 1; |
1717 | | |
1718 | 0 | check = pgs_lp->miter_check; |
1719 | 0 | u1 = plp->vector.y, v1 = plp->vector.x; |
1720 | 0 | u2 = -nplp->vector.y, v2 = -nplp->vector.x; |
1721 | |
|
1722 | 0 | if (pmat) { |
1723 | 0 | gs_point pt; |
1724 | |
|
1725 | 0 | code = gs_distance_transform_inverse(v1, u1, pmat, &pt); |
1726 | 0 | if (code < 0) |
1727 | 0 | return code; |
1728 | 0 | v1 = pt.x, u1 = pt.y; |
1729 | 0 | code = gs_distance_transform_inverse(v2, u2, pmat, &pt); |
1730 | 0 | if (code < 0) |
1731 | 0 | return code; |
1732 | 0 | v2 = pt.x, u2 = pt.y; |
1733 | | /* |
1734 | | * We need to recompute ccw according to the |
1735 | | * relative positions of the lines in user space. |
1736 | | * We repeat the computation described above, |
1737 | | * using the cdelta values instead of the widths. |
1738 | | * Because the definition of ccw above is inverted |
1739 | | * from the intuitive one (for historical reasons), |
1740 | | * we actually have to do the test backwards. |
1741 | | */ |
1742 | 0 | ccw0 = v1 * u2 < v2 * u1; |
1743 | | #if defined(DEBUG) && !defined(GS_THREADSAFE) |
1744 | | { |
1745 | | double a1 = atan2(u1, v1), a2 = atan2(u2, v2), dif = a1 - a2; |
1746 | | |
1747 | | if (dif < 0) |
1748 | | dif += 2 * M_PI; |
1749 | | else if (dif >= 2 * M_PI) |
1750 | | dif -= 2 * M_PI; |
1751 | | if (dif != 0 && (dif < M_PI) != ccw0) |
1752 | | lprintf8("ccw wrong: tan(a1=%g)=%g/%g, tan(a2=%g)=%g,%g, dif=%g, ccw0=%d\n", |
1753 | | a1, u1, v1, a2, u2, v2, dif, ccw0); |
1754 | | } |
1755 | | #endif |
1756 | 0 | } |
1757 | 0 | num = u1 * v2 - u2 * v1; |
1758 | 0 | denom = u1 * u2 + v1 * v2; |
1759 | | /* |
1760 | | * We will want either tan(a-b) or tan(b-a) |
1761 | | * depending on the orientations of the lines. |
1762 | | * Fortunately we know the relative orientations already. |
1763 | | */ |
1764 | 0 | if (!ccw0) /* have plp - nplp, want vice versa */ |
1765 | 0 | num = -num; |
1766 | | #if defined(DEBUG) && !defined(GS_THREADSAFE) |
1767 | | if (gs_debug_c('O')) { |
1768 | | dlprintf4("[o]Miter check: u1/v1=%f/%f, u2/v2=%f/%f,\n", |
1769 | | u1, v1, u2, v2); |
1770 | | dlprintf3(" num=%f, denom=%f, check=%f\n", |
1771 | | num, denom, check); |
1772 | | } |
1773 | | #endif |
1774 | | /* |
1775 | | * If we define T = num / denom, then we want to use |
1776 | | * a miter join iff arctan(T) >= arctan(check). |
1777 | | * We know that both of these angles are in the 1st |
1778 | | * or 2nd quadrant, and since arctan is monotonic |
1779 | | * within each quadrant, we can do the comparisons |
1780 | | * on T and check directly, taking signs into account |
1781 | | * as follows: |
1782 | | * sign(T) sign(check) atan(T) >= atan(check) |
1783 | | * ------- ----------- ---------------------- |
1784 | | * + + T >= check |
1785 | | * - + true |
1786 | | * + - false |
1787 | | * - - T >= check |
1788 | | */ |
1789 | 0 | if (num == 0 && denom == 0) |
1790 | 0 | return_error(gs_error_unregistered); /* Must not happen. */ |
1791 | 0 | if (denom < 0) |
1792 | 0 | num = -num, denom = -denom; |
1793 | | /* Now denom >= 0, so sign(num) = sign(T). */ |
1794 | 0 | if (check > 0 ? |
1795 | 0 | (num < 0 || num >= denom * check) : |
1796 | 0 | (num < 0 && num >= denom * check) |
1797 | 0 | ) { |
1798 | | /* OK to use a miter join. */ |
1799 | 0 | gs_fixed_point dirn1, dirn2; |
1800 | |
|
1801 | 0 | dirn1.x = plp->e.cdelta.x; |
1802 | 0 | dirn1.y = plp->e.cdelta.y; |
1803 | | /* If this direction is small enough that we might have |
1804 | | * underflowed and the vector record is suitable for us |
1805 | | * to use to calculate a better one, then do so. */ |
1806 | 0 | if ((abs(dirn1.x) + abs(dirn1.y) < 16) && |
1807 | 0 | ((plp->vector.x != 0) || (plp->vector.y != 0))) |
1808 | 0 | { |
1809 | 0 | float scale = 65536.0; |
1810 | 0 | if (abs(plp->vector.x) > abs(plp->vector.y)) |
1811 | 0 | scale /= abs(plp->vector.x); |
1812 | 0 | else |
1813 | 0 | scale /= abs(plp->vector.y); |
1814 | 0 | dirn1.x = (fixed)(plp->vector.x*scale); |
1815 | 0 | dirn1.y = (fixed)(plp->vector.y*scale); |
1816 | 0 | } |
1817 | 0 | dirn2.x = nplp->o.cdelta.x; |
1818 | 0 | dirn2.y = nplp->o.cdelta.y; |
1819 | | /* If this direction is small enough that we might have |
1820 | | * underflowed and the vector record is suitable for us |
1821 | | * to use to calculate a better one, then do so. */ |
1822 | 0 | if ((abs(dirn2.x) + abs(dirn2.y) < 16) && |
1823 | 0 | ((nplp->vector.x != 0) || (nplp->vector.y != 0))) |
1824 | 0 | { |
1825 | 0 | float scale = 65536.0; |
1826 | 0 | if (abs(nplp->vector.x) > abs(nplp->vector.y)) |
1827 | 0 | scale /= abs(nplp->vector.x); |
1828 | 0 | else |
1829 | 0 | scale /= abs(nplp->vector.y); |
1830 | 0 | dirn2.x = (fixed)(-nplp->vector.x*scale); |
1831 | 0 | dirn2.y = (fixed)(-nplp->vector.y*scale); |
1832 | 0 | } |
1833 | 0 | if_debug0('O', " ... passes.\n"); |
1834 | | /* Compute the intersection of the extended edge lines. */ |
1835 | 0 | if (line_intersect(outp, &dirn1, np, &dirn2, mpt) == 0) |
1836 | 0 | return 0; |
1837 | 0 | } |
1838 | 0 | return 1; |
1839 | 0 | } |
1840 | | |
1841 | | /* Add a segment to the path. |
1842 | | * This works by crafting 2 paths, one for each edge, that will later be |
1843 | | * merged together. */ |
1844 | | static int |
1845 | | stroke_add_fast(gx_path * ppath, gx_path * rpath, bool ensure_closed, int first, |
1846 | | pl_ptr plp, pl_ptr nplp, const gx_device_color * pdevc, |
1847 | | gx_device * dev, const gs_gstate * pgs, |
1848 | | const gx_stroke_params * params, |
1849 | | const gs_fixed_rect * ignore_pbbox, int uniform, |
1850 | | gs_line_join join, bool reflected, note_flags flags) |
1851 | 0 | { |
1852 | 0 | const gx_line_params *pgs_lp = gs_currentlineparams_inline(pgs); |
1853 | 0 | gs_fixed_point points[8]; |
1854 | 0 | gs_fixed_point rpoints[8]; |
1855 | 0 | int npoints = 0; |
1856 | 0 | int nrpoints = 0; |
1857 | 0 | int code; |
1858 | 0 | bool moveto_first = false; |
1859 | 0 | bool rmoveto_first = false; |
1860 | 0 | gs_line_cap start_cap, end_cap; |
1861 | 0 | const gs_matrix *pmat = (uniform ? (const gs_matrix *)NULL : &ctm_only(pgs)); |
1862 | 0 | enum { |
1863 | 0 | joinsense_cap = 0, |
1864 | 0 | joinsense_cw = 1, |
1865 | 0 | joinsense_ccw = 2, |
1866 | 0 | joinsense_over = 4, |
1867 | 0 | joinsense_under = 8, |
1868 | 0 | } joinsense = joinsense_cap; |
1869 | |
|
1870 | 0 | if (plp->thin) { |
1871 | | /* We didn't set up the endpoint parameters before, */ |
1872 | | /* because the line was thin. Do it now. */ |
1873 | 0 | set_thin_widths(plp); |
1874 | 0 | adjust_stroke(dev, plp, pgs, true, first == 0 && nplp == 0, flags); |
1875 | 0 | compute_caps(plp); |
1876 | 0 | } |
1877 | 0 | start_cap = (flags & nf_dash_head ? |
1878 | 0 | pgs_lp->dash_cap : pgs_lp->start_cap); |
1879 | 0 | end_cap = (flags & nf_dash_tail ? |
1880 | 0 | pgs_lp->dash_cap : pgs_lp->end_cap); |
1881 | | /* If we're starting a new rpath here, we need to fake a new cap. |
1882 | | * Don't interfere if we would have been doing a cap anyway. */ |
1883 | 0 | if (gx_path_is_void(rpath) && (first != 0)) { |
1884 | 0 | first = 0; |
1885 | 0 | start_cap = gs_cap_butt; |
1886 | 0 | end_cap = gs_cap_butt; |
1887 | 0 | moveto_first = true; |
1888 | 0 | rmoveto_first = true; |
1889 | 0 | } |
1890 | 0 | if (first == 0) { |
1891 | | /* Create an initial cap. */ |
1892 | 0 | if (start_cap == gs_cap_round) { |
1893 | 0 | if ((code = gx_path_add_point(ppath, plp->o.co.x, plp->o.co.y)) < 0 || |
1894 | 0 | (code = add_pie_cap(ppath, &plp->o)) < 0) |
1895 | 0 | return code; |
1896 | 0 | moveto_first = false; |
1897 | 0 | } else { |
1898 | 0 | if ((npoints = cap_points(start_cap, &plp->o, points)) < 0) |
1899 | 0 | return npoints; |
1900 | 0 | moveto_first = true; |
1901 | 0 | } |
1902 | 0 | rmoveto_first = true; |
1903 | 0 | ASSIGN_POINT(&rpoints[0], plp->o.co); |
1904 | 0 | nrpoints = 1; |
1905 | 0 | } |
1906 | | /* Add points to move us along the edges of this stroke */ |
1907 | 0 | ASSIGN_POINT(&points [npoints ], plp->e.co); |
1908 | 0 | ASSIGN_POINT(&rpoints[nrpoints], plp->e.ce); |
1909 | 0 | npoints++; |
1910 | 0 | nrpoints++; |
1911 | |
|
1912 | 0 | if (nplp != NULL && !nplp->thin) { |
1913 | | /* We need to do a join. What sense is it it? */ |
1914 | 0 | double l, r; |
1915 | |
|
1916 | 0 | l = (double)(plp->width.x) /* x1 */ * (nplp->width.y) /* y2 */; |
1917 | 0 | r = (double)(nplp->width.x) /* x2 */ * (plp->width.y) /* y1 */; |
1918 | |
|
1919 | 0 | if ((l == r) && (join == gs_join_round)) |
1920 | 0 | joinsense = joinsense_cap; |
1921 | 0 | else if ((l > r) ^ reflected) |
1922 | 0 | joinsense = joinsense_ccw | joinsense_over | joinsense_under; |
1923 | 0 | else |
1924 | 0 | joinsense = joinsense_cw | joinsense_over | joinsense_under; |
1925 | |
|
1926 | 0 | if (joinsense != joinsense_cap && join == gs_join_miter) { |
1927 | | /* We need to do a miter line join. Miters are 'special' |
1928 | | * in that we'd like to do them by adjusting the existing |
1929 | | * points, rather than adding new ones. */ |
1930 | 0 | gs_fixed_point mpt; |
1931 | 0 | if (joinsense & joinsense_ccw) { |
1932 | | /* Underjoin (in reverse path): |
1933 | | * A = plp->o.co, B = plp->e.ce, C = nplp->o.co, D = nplp->e.ce */ |
1934 | 0 | double xa = plp->o.co.x, ya = plp->o.co.y; |
1935 | 0 | double xb = plp->e.ce.x, yb = plp->e.ce.y; |
1936 | 0 | double xc = nplp->o.co.x, yc = nplp->o.co.y; |
1937 | 0 | double xd = nplp->e.ce.x, yd = nplp->e.ce.y; |
1938 | 0 | double xab = xa-xb, xac = xa-xc, xcd = xc-xd; |
1939 | 0 | double yab = ya-yb, yac = ya-yc, ycd = yc-yd; |
1940 | 0 | double t_num = xac * ycd - yac * xcd; |
1941 | 0 | double t_den = xab * ycd - yab * xcd; |
1942 | 0 | code = check_miter(pgs_lp, plp, nplp, pmat, &plp->e.co, |
1943 | 0 | &nplp->o.ce, &mpt, true); |
1944 | 0 | if (code < 0) |
1945 | 0 | return code; |
1946 | 0 | if (code == 0) { |
1947 | 0 | points[npoints-1].x = mpt.x; |
1948 | 0 | points[npoints-1].y = mpt.y; |
1949 | 0 | if (ensure_closed) { |
1950 | 0 | points[npoints].x = nplp->o.ce.x; |
1951 | 0 | points[npoints].y = nplp->o.ce.y; |
1952 | 0 | npoints++; |
1953 | 0 | } |
1954 | 0 | joinsense &= ~joinsense_over; |
1955 | 0 | } else |
1956 | 0 | join = gs_join_bevel; |
1957 | 0 | if (t_den != 0 && |
1958 | 0 | ((t_num >= 0 && t_num <= t_den) || |
1959 | 0 | (t_num <= 0 && t_num >= t_den))) { |
1960 | 0 | double x = xa - xab * t_num / t_den; |
1961 | 0 | double y = ya - yab * t_num / t_den; |
1962 | 0 | rpoints[nrpoints-1].x = (fixed)x; |
1963 | 0 | rpoints[nrpoints-1].y = (fixed)y; |
1964 | 0 | joinsense &= ~joinsense_under; |
1965 | 0 | } |
1966 | 0 | } else { |
1967 | | /* Underjoin (in fwd path): |
1968 | | * A = plp->o.ce, B = plp->e.co, C = nplp->o.ce, D = nplp->e.co */ |
1969 | 0 | double xa = plp->o.ce.x, ya = plp->o.ce.y; |
1970 | 0 | double xb = plp->e.co.x, yb = plp->e.co.y; |
1971 | 0 | double xc = nplp->o.ce.x, yc = nplp->o.ce.y; |
1972 | 0 | double xd = nplp->e.co.x, yd = nplp->e.co.y; |
1973 | 0 | double xab = xa-xb, xac = xa-xc, xcd = xc-xd; |
1974 | 0 | double yab = ya-yb, yac = ya-yc, ycd = yc-yd; |
1975 | 0 | double t_num = xac * ycd - yac * xcd; |
1976 | 0 | double t_den = xab * ycd - yab * xcd; |
1977 | 0 | code = check_miter(pgs_lp, plp, nplp, pmat, &plp->e.ce, |
1978 | 0 | &nplp->o.co, &mpt, false); |
1979 | 0 | if (code < 0) |
1980 | 0 | return code; |
1981 | 0 | if (code == 0) { |
1982 | 0 | rpoints[nrpoints-1].x = mpt.x; |
1983 | 0 | rpoints[nrpoints-1].y = mpt.y; |
1984 | 0 | if (ensure_closed) { |
1985 | 0 | rpoints[nrpoints].x = nplp->o.co.x; |
1986 | 0 | rpoints[nrpoints].y = nplp->o.co.y; |
1987 | 0 | nrpoints++; |
1988 | 0 | } |
1989 | 0 | joinsense &= ~joinsense_over; |
1990 | 0 | } else |
1991 | 0 | join = gs_join_bevel; |
1992 | 0 | if (t_den != 0 && |
1993 | 0 | ((t_num >= 0 && t_num <= t_den) || |
1994 | 0 | (t_num <= 0 && t_num >= t_den))) { |
1995 | 0 | double x = xa - xab * t_num / t_den; |
1996 | 0 | double y = ya - yab * t_num / t_den; |
1997 | 0 | points[npoints-1].x = (fixed)x; |
1998 | 0 | points[npoints-1].y = (fixed)y; |
1999 | 0 | joinsense &= ~joinsense_under; |
2000 | 0 | } |
2001 | 0 | } |
2002 | 0 | } |
2003 | 0 | } |
2004 | | |
2005 | 0 | if ((code = add_points(ppath, points, npoints, moveto_first)) < 0) |
2006 | 0 | return code; |
2007 | 0 | if ((code = add_points(rpath, rpoints, nrpoints, rmoveto_first)) < 0) |
2008 | 0 | return code; |
2009 | 0 | npoints = 0; |
2010 | 0 | nrpoints = 0; |
2011 | |
|
2012 | 0 | if (nplp == 0) { /* Add a final cap. */ |
2013 | 0 | if (end_cap == gs_cap_round) { |
2014 | 0 | code = add_pie_cap(ppath, &plp->e); |
2015 | 0 | } else { |
2016 | 0 | code = cap_points(end_cap, &plp->e, points); |
2017 | 0 | npoints = code; |
2018 | 0 | } |
2019 | 0 | } else if (nplp->thin) { /* no join */ |
2020 | 0 | code = cap_points(gs_cap_butt, &plp->e, points); |
2021 | 0 | npoints = code; |
2022 | 0 | } else if (joinsense == joinsense_cap) { |
2023 | | /* Do a cap */ |
2024 | 0 | code = add_pie_cap(ppath, &plp->e); |
2025 | 0 | if (code >= 0) { |
2026 | | /* If the next line is in the opposite direction as the current one |
2027 | | * we want to leave the point on the same side as it was |
2028 | | * originally. This is required for paths that come to a stop |
2029 | | * and then reverse themselves, but may produce more complexity |
2030 | | * than we'd really like at the ends of smooth beziers. */ |
2031 | 0 | if ((double)(plp->width.x) * nplp->width.x + (double)plp->width.y * nplp->width.y >= 0) |
2032 | 0 | code = gx_path_add_line(ppath, plp->e.co.x, plp->e.co.y); |
2033 | 0 | } |
2034 | 0 | } else if (joinsense & joinsense_ccw) { |
2035 | | /* CCW rotation. Join in the forward path. "Underjoin" in the |
2036 | | * reverse path. */ |
2037 | 0 | if (joinsense & joinsense_over) { |
2038 | | /* RJW: Ideally we should include the "|| flags" clause in |
2039 | | * the following condition. This forces all joins between |
2040 | | * line segments generated from arcs to be round. This would |
2041 | | * solve some flatness issues, but makes some pathological |
2042 | | * cases incredibly slow. */ |
2043 | 0 | if (join == gs_join_round /* || (flags & nf_all_from_arc) */) { |
2044 | 0 | code = add_pie_join_fast_ccw(ppath, plp, nplp, reflected); |
2045 | 0 | } else { /* non-round join */ |
2046 | 0 | code = line_join_points_fast_ccw(pgs_lp, plp, nplp, |
2047 | 0 | points, pmat, join); |
2048 | 0 | npoints = code; |
2049 | 0 | } |
2050 | 0 | if (code < 0) |
2051 | 0 | return code; |
2052 | 0 | } |
2053 | 0 | if (joinsense & joinsense_under) { |
2054 | | /* The underjoin */ |
2055 | 0 | #ifndef SLOWER_BUT_MORE_ACCURATE_STROKING |
2056 | 0 | if ((flags & (nf_some_from_arc | nf_prev_some_from_arc)) == 0) { |
2057 | | /* RJW: This is an approximation. We ought to draw a line |
2058 | | * back to nplp->o.p, and then independently fill any exposed |
2059 | | * region under the curve with a round join. Sadly, that's |
2060 | | * a) really hard to do, and b) makes certain pathological |
2061 | | * filling cases MUCH slower due to the greater number of |
2062 | | * "cross-segment" line segments this produces. Instead, |
2063 | | * we just skip the line to the middle, and join across the |
2064 | | * bottom instead. This is akin to what other graphics libs |
2065 | | * do (such as fitz, libart, etc). It's not perfect but in |
2066 | | * most cases it's close, and results in faster to fill |
2067 | | * paths. |
2068 | | */ |
2069 | | /* RJW: This goes wrong for some paths, as the 'underjoin' wind |
2070 | | * will be the wrong way. See bug 694971 */ |
2071 | 0 | code = gx_path_add_line(rpath, nplp->o.p.x, nplp->o.p.y); |
2072 | 0 | if (code < 0) |
2073 | 0 | return code; |
2074 | 0 | } |
2075 | | #else |
2076 | | if (wide_underjoin(plp, nplp)) |
2077 | | { |
2078 | | code = gx_path_add_line(rpath, nplp->o.p.x, nplp->o.p.y); |
2079 | | if (code < 0) |
2080 | | return code; |
2081 | | if ((flags & (nf_some_from_arc | nf_prev_some_from_arc)) != 0) { |
2082 | | code = gx_path_add_line(rpath, nplp->o.co.x, nplp->o.co.y); |
2083 | | if (code < 0) |
2084 | | return code; |
2085 | | code = gx_path_add_line(rpath, plp->e.ce.x, plp->e.ce.y); |
2086 | | if (code < 0) |
2087 | | return code; |
2088 | | code = gx_path_add_line(rpath, nplp->o.p.x, nplp->o.p.y); |
2089 | | if (code < 0) |
2090 | | return code; |
2091 | | } |
2092 | | } |
2093 | | #endif |
2094 | 0 | code = gx_path_add_line(rpath, nplp->o.co.x, nplp->o.co.y); |
2095 | 0 | } |
2096 | 0 | } else if (joinsense & joinsense) { |
2097 | | /* CW rotation. Join in the reverse path. "Underjoin" in the |
2098 | | * forward path. */ |
2099 | 0 | if (joinsense & joinsense_over) { |
2100 | | /* RJW: Ideally we should include the "|| flags" clause in |
2101 | | * the following condition. This forces all joins between |
2102 | | * line segments generated from arcs to be round. This would |
2103 | | * solve some flatness issues, but makes some pathological |
2104 | | * cases incredibly slow. */ |
2105 | 0 | if (join == gs_join_round /* || (flags & nf_all_from_arc) */) { |
2106 | 0 | code = add_pie_join_fast_cw(rpath, plp, nplp, reflected); |
2107 | 0 | } else { /* non-round join */ |
2108 | 0 | code = line_join_points_fast_cw(pgs_lp, plp, nplp, |
2109 | 0 | rpoints, pmat, join); |
2110 | 0 | nrpoints = code; |
2111 | 0 | } |
2112 | 0 | if (code < 0) |
2113 | 0 | return code; |
2114 | 0 | } |
2115 | 0 | if (joinsense & joinsense_under) { |
2116 | | /* The underjoin */ |
2117 | 0 | #ifndef SLOWER_BUT_MORE_ACCURATE_STROKING |
2118 | 0 | if ((flags & (nf_some_from_arc | nf_prev_some_from_arc)) == 0 && |
2119 | 0 | join != gs_join_miter) { |
2120 | | /* RJW: This is an approximation. We ought to draw a line |
2121 | | * back to nplp->o.p, and then independently fill any exposed |
2122 | | * region under the curve with a round join. Sadly, that's |
2123 | | * a) really hard to do, and b) makes certain pathological |
2124 | | * filling cases MUCH slower due to the greater number of |
2125 | | * "cross-segment" line segments this produces. Instead, |
2126 | | * we just skip the line to the middle, and join across the |
2127 | | * bottom instead. This is akin to what other graphics libs |
2128 | | * do (such as fitz, libart, etc). It's not perfect but in |
2129 | | * most cases it's close, and results in faster to fill |
2130 | | * paths. |
2131 | | */ |
2132 | | /* RJW: This goes wrong for some paths, as the 'underjoin' wind |
2133 | | * will be the wrong way. See bug 694971 */ |
2134 | 0 | code = gx_path_add_line(ppath, nplp->o.p.x, nplp->o.p.y); |
2135 | 0 | if (code < 0) |
2136 | 0 | return code; |
2137 | 0 | } |
2138 | | #else |
2139 | | if (wide_underjoin(plp, nplp)) |
2140 | | { |
2141 | | code = gx_path_add_line(ppath, nplp->o.p.x, nplp->o.p.y); |
2142 | | if (code < 0) |
2143 | | return code; |
2144 | | if ((flags & (nf_some_from_arc | nf_prev_some_from_arc)) != 0) { |
2145 | | code = gx_path_add_line(ppath, nplp->o.ce.x, nplp->o.ce.y); |
2146 | | if (code < 0) |
2147 | | return code; |
2148 | | code = gx_path_add_line(ppath, plp->e.co.x, plp->e.co.y); |
2149 | | if (code < 0) |
2150 | | return code; |
2151 | | code = gx_path_add_line(ppath, nplp->o.p.x, nplp->o.p.y); |
2152 | | if (code < 0) |
2153 | | return code; |
2154 | | } |
2155 | | } |
2156 | | #endif |
2157 | 0 | code = gx_path_add_line(ppath, nplp->o.ce.x, nplp->o.ce.y); |
2158 | 0 | } |
2159 | 0 | } |
2160 | 0 | if (code < 0) |
2161 | 0 | return code; |
2162 | 0 | if (npoints > 0) { |
2163 | 0 | code = add_points(ppath, points, npoints, false); |
2164 | 0 | if (code < 0) |
2165 | 0 | return code; |
2166 | 0 | } |
2167 | 0 | if (nrpoints > 0) { |
2168 | 0 | code = add_points(rpath, rpoints, nrpoints, false); |
2169 | 0 | if (code < 0) |
2170 | 0 | return code; |
2171 | 0 | } |
2172 | 0 | if (ensure_closed) |
2173 | 0 | return gx_join_path_and_reverse(ppath, rpath); |
2174 | 0 | return 0; |
2175 | 0 | } |
2176 | | |
2177 | | /* Add a CPSI-compatible segment to the path. This handles all the complex |
2178 | | * cases. |
2179 | | * |
2180 | | * This method doesn't support start/end/dash caps, but it's only used from |
2181 | | * postscript, so it doesn't need to. |
2182 | | */ |
2183 | | static int |
2184 | | stroke_add_compat(gx_path * ppath, gx_path *rpath, bool ensure_closed, |
2185 | | int first, pl_ptr plp, pl_ptr nplp, |
2186 | | const gx_device_color * pdevc, gx_device * dev, |
2187 | | const gs_gstate * pgs, |
2188 | | const gx_stroke_params * params, |
2189 | | const gs_fixed_rect * ignore_pbbox, int uniform, |
2190 | | gs_line_join join, bool reflected, note_flags flags) |
2191 | 0 | { |
2192 | | /* Actually it adds 2 contours : one for the segment itself, |
2193 | | and another one for line join or for the ending cap. |
2194 | | Note CPSI creates negative contours. */ |
2195 | 0 | const gx_line_params *pgs_lp = gs_currentlineparams_inline(pgs); |
2196 | 0 | gs_fixed_point points[5]; |
2197 | 0 | int npoints; |
2198 | 0 | bool const moveto_first = true; /* Keeping this code closer to "stroke_add". */ |
2199 | 0 | int code; |
2200 | |
|
2201 | 0 | if (plp->thin) { |
2202 | | /* We didn't set up the endpoint parameters before, */ |
2203 | | /* because the line was thin. Do it now. */ |
2204 | 0 | set_thin_widths(plp); |
2205 | 0 | adjust_stroke(dev, plp, pgs, true, first == 0 && nplp == 0, flags); |
2206 | 0 | compute_caps(plp); |
2207 | 0 | } |
2208 | | /* The segment itself : */ |
2209 | 0 | ASSIGN_POINT(&points[0], plp->o.ce); |
2210 | 0 | ASSIGN_POINT(&points[1], plp->e.co); |
2211 | 0 | ASSIGN_POINT(&points[2], plp->e.ce); |
2212 | 0 | ASSIGN_POINT(&points[3], plp->o.co); |
2213 | 0 | code = add_points(ppath, points, 4, moveto_first); |
2214 | 0 | if (code < 0) |
2215 | 0 | return code; |
2216 | 0 | code = gx_path_close_subpath(ppath); |
2217 | 0 | if (code < 0) |
2218 | 0 | return code; |
2219 | 0 | npoints = 0; |
2220 | 0 | if (nplp == 0) { |
2221 | | /* Add a final cap. */ |
2222 | 0 | if (pgs_lp->start_cap == gs_cap_butt) |
2223 | 0 | return 0; |
2224 | 0 | if (pgs_lp->start_cap == gs_cap_round) { |
2225 | 0 | ASSIGN_POINT(&points[npoints], plp->e.co); |
2226 | 0 | ++npoints; |
2227 | 0 | if ((code = add_points(ppath, points, npoints, moveto_first)) < 0) |
2228 | 0 | return code; |
2229 | 0 | return add_round_cap(ppath, &plp->e); |
2230 | 0 | } |
2231 | 0 | ASSIGN_POINT(&points[0], plp->e.ce); |
2232 | 0 | ++npoints; |
2233 | 0 | ASSIGN_POINT(&points[npoints], plp->e.co); |
2234 | 0 | ++npoints; |
2235 | 0 | code = cap_points(pgs_lp->start_cap, &plp->e, points + npoints); |
2236 | 0 | if (code < 0) |
2237 | 0 | return code; |
2238 | 0 | npoints += code; |
2239 | 0 | } else if (join == gs_join_round) { |
2240 | 0 | ASSIGN_POINT(&points[npoints], plp->e.co); |
2241 | 0 | ++npoints; |
2242 | 0 | if ((code = add_points(ppath, points, npoints, moveto_first)) < 0) |
2243 | 0 | return code; |
2244 | 0 | return add_round_cap(ppath, &plp->e); |
2245 | 0 | } else if (nplp->thin) { /* no join */ |
2246 | 0 | npoints = 0; |
2247 | 0 | } else { /* non-round join */ |
2248 | 0 | bool ccw = |
2249 | 0 | (double)(plp->width.x) /* x1 */ * (nplp->width.y) /* y2 */ > |
2250 | 0 | (double)(nplp->width.x) /* x2 */ * (plp->width.y) /* y1 */; |
2251 | |
|
2252 | 0 | if (ccw ^ reflected) { |
2253 | 0 | ASSIGN_POINT(&points[0], plp->e.co); |
2254 | 0 | ++npoints; |
2255 | 0 | code = line_join_points(pgs_lp, plp, nplp, points + npoints, |
2256 | 0 | (uniform ? (gs_matrix *) 0 : &ctm_only(pgs)), |
2257 | 0 | join, reflected); |
2258 | 0 | if (code < 0) |
2259 | 0 | return code; |
2260 | 0 | code--; /* Drop the last point of the non-compatible mode. */ |
2261 | 0 | npoints += code; |
2262 | 0 | } else { |
2263 | 0 | code = line_join_points(pgs_lp, plp, nplp, points, |
2264 | 0 | (uniform ? (gs_matrix *) 0 : &ctm_only(pgs)), |
2265 | 0 | join, reflected); |
2266 | 0 | if (code < 0) |
2267 | 0 | return code; |
2268 | 0 | ASSIGN_POINT(&points[0], plp->e.ce); /* Replace the starting point of the non-compatible mode. */ |
2269 | 0 | npoints = code; |
2270 | 0 | } |
2271 | 0 | } |
2272 | 0 | code = add_points(ppath, points, npoints, moveto_first); |
2273 | 0 | if (code < 0) |
2274 | 0 | return code; |
2275 | 0 | code = gx_path_close_subpath(ppath); |
2276 | 0 | return code; |
2277 | 0 | } |
2278 | | |
2279 | | /* Add a CPSI-compatible segment to the path. This handles all the complex |
2280 | | * cases. |
2281 | | * |
2282 | | * This method doesn't support start/end/dash caps, but it's only used from |
2283 | | * postscript, so it doesn't need to. |
2284 | | */ |
2285 | | static int |
2286 | | stroke_add_initial_cap_compat(gx_path * ppath, pl_ptr plp, bool adlust_longitude, |
2287 | | const gx_device_color * pdevc, gx_device * dev, |
2288 | | const gs_gstate * pgs) |
2289 | 0 | { |
2290 | 0 | const gx_line_params *pgs_lp = gs_currentlineparams_inline(pgs); |
2291 | 0 | gs_fixed_point points[5]; |
2292 | 0 | int npoints = 0; |
2293 | 0 | int code; |
2294 | |
|
2295 | 0 | if (pgs_lp->start_cap == gs_cap_butt) |
2296 | 0 | return 0; |
2297 | 0 | if (plp->thin) { |
2298 | | /* We didn't set up the endpoint parameters before, */ |
2299 | | /* because the line was thin. Do it now. */ |
2300 | 0 | set_thin_widths(plp); |
2301 | 0 | adjust_stroke(dev, plp, pgs, true, adlust_longitude, 0); |
2302 | 0 | compute_caps(plp); |
2303 | 0 | } |
2304 | | /* Create an initial cap if desired. */ |
2305 | 0 | if (pgs_lp->start_cap == gs_cap_round) { |
2306 | 0 | if ((code = gx_path_add_point(ppath, plp->o.co.x, plp->o.co.y)) < 0 || |
2307 | 0 | (code = add_round_cap(ppath, &plp->o)) < 0 |
2308 | 0 | ) |
2309 | 0 | return code; |
2310 | 0 | return 0; |
2311 | 0 | } else { |
2312 | 0 | ASSIGN_POINT(&points[0], plp->o.co); |
2313 | 0 | ++npoints; |
2314 | 0 | if ((code = cap_points(pgs_lp->start_cap, &plp->o, points + npoints)) < 0) |
2315 | 0 | return npoints; |
2316 | 0 | npoints += code; |
2317 | 0 | ASSIGN_POINT(&points[npoints], plp->o.ce); |
2318 | 0 | ++npoints; |
2319 | 0 | code = add_points(ppath, points, npoints, true); |
2320 | 0 | if (code < 0) |
2321 | 0 | return code; |
2322 | 0 | return gx_path_close_subpath(ppath); |
2323 | 0 | } |
2324 | 0 | } |
2325 | | |
2326 | | /* Add lines with a possible initial moveto. */ |
2327 | | static int |
2328 | | add_points(gx_path * ppath, const gs_fixed_point * points, int npoints, |
2329 | | bool moveto_first) |
2330 | 0 | { |
2331 | 0 | int code; |
2332 | |
|
2333 | 0 | if (moveto_first) { |
2334 | 0 | code = gx_path_add_point(ppath, points[0].x, points[0].y); |
2335 | 0 | if (code < 0) |
2336 | 0 | return code; |
2337 | 0 | return gx_path_add_lines(ppath, points + 1, npoints - 1); |
2338 | 0 | } else { |
2339 | 0 | return gx_path_add_lines(ppath, points, npoints); |
2340 | 0 | } |
2341 | 0 | } |
2342 | | |
2343 | | /* ---------------- Join computation ---------------- */ |
2344 | | |
2345 | | /* Compute the points for a bevel, miter, or triangle join. */ |
2346 | | /* Treat no join the same as a bevel join. */ |
2347 | | /* If pmat != 0, we must inverse-transform the distances for */ |
2348 | | /* the miter check. */ |
2349 | | static int |
2350 | | line_join_points(const gx_line_params * pgs_lp, pl_ptr plp, pl_ptr nplp, |
2351 | | gs_fixed_point * join_points, const gs_matrix * pmat, |
2352 | | gs_line_join join, bool reflected) |
2353 | 0 | { |
2354 | 0 | #define jp1 join_points[0] |
2355 | 0 | #define np1 join_points[1] |
2356 | 0 | #define np2 join_points[2] |
2357 | 0 | #define jp2 join_points[3] |
2358 | 0 | #define jpx join_points[4] |
2359 | | /* |
2360 | | * Set np to whichever of nplp->o.co or .ce is outside |
2361 | | * the current line. We observe that the point (x2,y2) |
2362 | | * is counter-clockwise from (x1,y1), relative to the origin, |
2363 | | * iff |
2364 | | * (arctan(y2/x2) - arctan(y1/x1)) mod 2*pi < pi, |
2365 | | * taking the signs of xi and yi into account to determine |
2366 | | * the quadrants of the results. It turns out that |
2367 | | * even though arctan is monotonic only in the 4th/1st |
2368 | | * quadrants and the 2nd/3rd quadrants, case analysis on |
2369 | | * the signs of xi and yi demonstrates that this test |
2370 | | * is equivalent to the much less expensive test |
2371 | | * x1 * y2 > x2 * y1 |
2372 | | * in all cases. |
2373 | | * |
2374 | | * In the present instance, x1,y1 are plp->width, |
2375 | | * x2,y2 are nplp->width, and the origin is |
2376 | | * their common point (plp->e.p, nplp->o.p). |
2377 | | * ccw will be true iff nplp.o.co (nplp.o.p + width) is |
2378 | | * counter-clockwise from plp.e.ce (plp.e.p + width), |
2379 | | * in which case we want tan(a-b) rather than tan(b-a). |
2380 | | * |
2381 | | * We make the test using double arithmetic only because |
2382 | | * the !@#&^*% C language doesn't give us access to |
2383 | | * the double-width-result multiplication operation |
2384 | | * that almost all CPUs provide! |
2385 | | */ |
2386 | 0 | bool ccw = |
2387 | 0 | (double)(plp->width.x) /* x1 */ * (nplp->width.y) /* y2 */ > |
2388 | 0 | (double)(nplp->width.x) /* x2 */ * (plp->width.y) /* y1 */; |
2389 | 0 | bool ccw0 = ccw; |
2390 | 0 | p_ptr outp, np; |
2391 | 0 | int code; |
2392 | 0 | gs_fixed_point mpt; |
2393 | |
|
2394 | 0 | ccw ^= reflected; |
2395 | | |
2396 | | /* Initialize for a bevel join. */ |
2397 | 0 | ASSIGN_POINT(&jp1, plp->e.co); |
2398 | 0 | ASSIGN_POINT(&jp2, plp->e.ce); |
2399 | | |
2400 | | /* |
2401 | | * Because of stroke adjustment, it is possible that |
2402 | | * plp->e.p != nplp->o.p. For that reason, we must use |
2403 | | * nplp->o.p as np1 or np2. |
2404 | | */ |
2405 | 0 | if (!ccw) { |
2406 | 0 | outp = &jp2; |
2407 | 0 | ASSIGN_POINT(&np2, nplp->o.co); |
2408 | 0 | ASSIGN_POINT(&np1, nplp->o.p); |
2409 | 0 | np = &np2; |
2410 | 0 | } else { |
2411 | 0 | outp = &jp1; |
2412 | 0 | ASSIGN_POINT(&np1, nplp->o.ce); |
2413 | 0 | ASSIGN_POINT(&np2, nplp->o.p); |
2414 | 0 | np = &np1; |
2415 | 0 | } |
2416 | 0 | if_debug1('O', "[O]use %s\n", (ccw ? "co (ccw)" : "ce (cw)")); |
2417 | | |
2418 | | /* Handle triangular joins now. */ |
2419 | 0 | if (join == gs_join_triangle) { |
2420 | 0 | fixed tpx = outp->x - nplp->o.p.x + np->x; |
2421 | 0 | fixed tpy = outp->y - nplp->o.p.y + np->y; |
2422 | |
|
2423 | 0 | ASSIGN_POINT(&jpx, jp2); |
2424 | 0 | if (!ccw) { |
2425 | | /* Insert tp between np2 and jp2. */ |
2426 | 0 | jp2.x = tpx, jp2.y = tpy; |
2427 | 0 | } else { |
2428 | | /* Insert tp between jp1 and np1. */ |
2429 | 0 | ASSIGN_POINT(&jp2, np2); |
2430 | 0 | ASSIGN_POINT(&np2, np1); |
2431 | 0 | np1.x = tpx, np1.y = tpy; |
2432 | 0 | } |
2433 | 0 | return 5; |
2434 | 0 | } |
2435 | 0 | if (join == gs_join_miter && |
2436 | 0 | (code = check_miter(pgs_lp, plp, nplp, pmat, outp, np, &mpt, ccw0)) <= 0) { |
2437 | 0 | if (code < 0) |
2438 | 0 | return code; |
2439 | 0 | ASSIGN_POINT(outp, mpt); |
2440 | 0 | } |
2441 | 0 | return 4; |
2442 | 0 | } |
2443 | | |
2444 | | static int |
2445 | | line_join_points_fast_cw(const gx_line_params * pgs_lp, |
2446 | | pl_ptr plp, pl_ptr nplp, |
2447 | | gs_fixed_point * rjoin_points, |
2448 | | const gs_matrix * pmat, |
2449 | | gs_line_join join) |
2450 | 0 | { |
2451 | | /* rjoin_points will be added to a path that is currently at plp->e.ce. |
2452 | | */ |
2453 | | |
2454 | | /* Join will be between plp->e.ce and nplp->o.co */ |
2455 | 0 | if (join == gs_join_triangle) |
2456 | 0 | { |
2457 | 0 | gs_fixed_point tp; |
2458 | |
|
2459 | 0 | tp.x = plp->e.ce.x - nplp->o.p.x + nplp->o.co.x; |
2460 | 0 | tp.y = plp->e.ce.y - nplp->o.p.y + nplp->o.co.y; |
2461 | 0 | ASSIGN_POINT(&rjoin_points[0], tp); |
2462 | 0 | ASSIGN_POINT(&rjoin_points[1], nplp->o.co); |
2463 | 0 | return 2; |
2464 | 0 | } |
2465 | | |
2466 | | /* Set up for a Bevel join */ |
2467 | 0 | ASSIGN_POINT(&rjoin_points[0], nplp->o.co); |
2468 | |
|
2469 | 0 | return 1; |
2470 | 0 | } |
2471 | | |
2472 | | static int |
2473 | | line_join_points_fast_ccw(const gx_line_params * pgs_lp, |
2474 | | pl_ptr plp, pl_ptr nplp, |
2475 | | gs_fixed_point * join_points, |
2476 | | const gs_matrix * pmat, |
2477 | | gs_line_join join) |
2478 | 0 | { |
2479 | | /* join_points will be added to a path that is currently at plp->e.co. |
2480 | | */ |
2481 | | /* Join will be between plp->e.co and nplp->o.ce */ |
2482 | 0 | if (join == gs_join_triangle) |
2483 | 0 | { |
2484 | 0 | gs_fixed_point tp; |
2485 | |
|
2486 | 0 | tp.x = plp->e.co.x - nplp->o.p.x + nplp->o.ce.x; |
2487 | 0 | tp.y = plp->e.co.y - nplp->o.p.y + nplp->o.ce.y; |
2488 | 0 | ASSIGN_POINT(&join_points[0], tp); |
2489 | 0 | ASSIGN_POINT(&join_points[1], nplp->o.ce); |
2490 | 0 | return 2; |
2491 | 0 | } |
2492 | | |
2493 | | /* Set up for a Bevel join */ |
2494 | 0 | ASSIGN_POINT(&join_points[0], nplp->o.ce); |
2495 | |
|
2496 | 0 | return 1; |
2497 | 0 | } |
2498 | | /* ---------------- Cap computations ---------------- */ |
2499 | | |
2500 | | /* Compute the endpoints of the two caps of a segment. */ |
2501 | | /* Only o.p, e.p, width, and cdelta have been set. */ |
2502 | | static void |
2503 | | compute_caps(pl_ptr plp) |
2504 | 0 | { |
2505 | 0 | fixed wx2 = plp->width.x; |
2506 | 0 | fixed wy2 = plp->width.y; |
2507 | |
|
2508 | 0 | plp->o.co.x = plp->o.p.x + wx2, plp->o.co.y = plp->o.p.y + wy2; |
2509 | 0 | plp->o.cdelta.x = -plp->e.cdelta.x, |
2510 | 0 | plp->o.cdelta.y = -plp->e.cdelta.y; |
2511 | 0 | plp->o.ce.x = plp->o.p.x - wx2, plp->o.ce.y = plp->o.p.y - wy2; |
2512 | 0 | plp->e.co.x = plp->e.p.x - wx2, plp->e.co.y = plp->e.p.y - wy2; |
2513 | 0 | plp->e.ce.x = plp->e.p.x + wx2, plp->e.ce.y = plp->e.p.y + wy2; |
2514 | | #if defined(DEBUG) && !defined(GS_THREADSAFE) |
2515 | | if (gs_debug_c('O')) { |
2516 | | dlprintf4("[o]Stroke o=(%f,%f) e=(%f,%f)\n", |
2517 | | fixed2float(plp->o.p.x), fixed2float(plp->o.p.y), |
2518 | | fixed2float(plp->e.p.x), fixed2float(plp->e.p.y)); |
2519 | | dlprintf4("\twxy=(%f,%f) lxy=(%f,%f)\n", |
2520 | | fixed2float(wx2), fixed2float(wy2), |
2521 | | fixed2float(plp->e.cdelta.x), |
2522 | | fixed2float(plp->e.cdelta.y)); |
2523 | | } |
2524 | | #endif |
2525 | 0 | } |
2526 | | |
2527 | | #define px endp->p.x |
2528 | | #define py endp->p.y |
2529 | | #define xo endp->co.x |
2530 | | #define yo endp->co.y |
2531 | | #define xe endp->ce.x |
2532 | | #define ye endp->ce.y |
2533 | | #define cdx endp->cdelta.x |
2534 | | #define cdy endp->cdelta.y |
2535 | | |
2536 | | /* Add a round cap to a path. */ |
2537 | | /* Assume the current point is the cap origin (endp->co). */ |
2538 | | static int |
2539 | | add_round_cap(gx_path * ppath, const_ep_ptr endp) |
2540 | 0 | { |
2541 | 0 | int code; |
2542 | | |
2543 | | /* |
2544 | | * Per the Red Book, we draw a full circle, even though a semicircle |
2545 | | * is sufficient for the join. |
2546 | | */ |
2547 | 0 | if ((code = gx_path_add_partial_arc(ppath, px + cdx, py + cdy, |
2548 | 0 | xo + cdx, yo + cdy, |
2549 | 0 | quarter_arc_fraction)) < 0 || |
2550 | 0 | (code = gx_path_add_partial_arc(ppath, xe, ye, xe + cdx, ye + cdy, |
2551 | 0 | quarter_arc_fraction)) < 0 || |
2552 | 0 | (code = gx_path_add_partial_arc(ppath, px - cdx, py - cdy, |
2553 | 0 | xe - cdx, ye - cdy, |
2554 | 0 | quarter_arc_fraction)) < 0 || |
2555 | 0 | (code = gx_path_add_partial_arc(ppath, xo, yo, xo - cdx, yo - cdy, |
2556 | 0 | quarter_arc_fraction)) < 0 || |
2557 | | /* The final point must be (xe,ye). */ |
2558 | 0 | (code = gx_path_add_line(ppath, xe, ye)) < 0 |
2559 | 0 | ) |
2560 | 0 | return code; |
2561 | 0 | return 0; |
2562 | 0 | } |
2563 | | |
2564 | | /* Add a semicircular cap to a path. */ |
2565 | | /* Assume the current point is the cap origin (endp->co). */ |
2566 | | static int |
2567 | | add_pie_cap(gx_path * ppath, const_ep_ptr endp) |
2568 | 0 | { |
2569 | 0 | int code; |
2570 | |
|
2571 | 0 | if ((code = gx_path_add_partial_arc(ppath, px + cdx, py + cdy, |
2572 | 0 | xo + cdx, yo + cdy, |
2573 | 0 | quarter_arc_fraction)) < 0 || |
2574 | 0 | (code = gx_path_add_partial_arc(ppath, xe, ye, xe + cdx, ye + cdy, |
2575 | 0 | quarter_arc_fraction)) < 0 || |
2576 | 0 | (code = gx_path_add_line(ppath, xe, ye)) < 0) |
2577 | 0 | return code; |
2578 | 0 | return 0; |
2579 | 0 | } |
2580 | | |
2581 | | static int |
2582 | | do_pie_join(gx_path * ppath, gs_fixed_point *centre, |
2583 | | gs_fixed_point *current_orig, gs_fixed_point *current_tangent, |
2584 | | gs_fixed_point *final, gs_fixed_point *final_tangent, bool ccw, |
2585 | | gs_fixed_point *width) |
2586 | 0 | { |
2587 | 0 | int code; |
2588 | 0 | double rad_squared, dist_squared, F; |
2589 | 0 | gs_fixed_point current, tangent, tangmeet; |
2590 | |
|
2591 | 0 | tangent.x = current_tangent->x; |
2592 | 0 | tangent.y = current_tangent->y; |
2593 | 0 | current.x = current_orig->x; |
2594 | 0 | current.y = current_orig->y; |
2595 | | |
2596 | | /* Is the join more than 90 degrees? */ |
2597 | 0 | if ((double)tangent.x * (double)final_tangent->x + |
2598 | 0 | (double)tangent.y * (double)final_tangent->y > 0) { |
2599 | | /* Yes, so do a quarter turn. */ |
2600 | 0 | code = gx_path_add_partial_arc(ppath, |
2601 | 0 | centre->x + tangent.x, |
2602 | 0 | centre->y + tangent.y, |
2603 | | /* Point where tangents meet */ |
2604 | 0 | current.x + tangent.x, |
2605 | 0 | current.y + tangent.y, |
2606 | 0 | quarter_arc_fraction); |
2607 | 0 | if (code < 0) |
2608 | 0 | return code; |
2609 | 0 | current.x = centre->x + tangent.x; |
2610 | 0 | current.y = centre->y + tangent.y; |
2611 | 0 | if (ccw) { |
2612 | 0 | int tmp = tangent.x; |
2613 | 0 | tangent.x = -tangent.y; |
2614 | 0 | tangent.y = tmp; |
2615 | 0 | } else { |
2616 | 0 | int tmp = tangent.x; |
2617 | 0 | tangent.x = tangent.y; |
2618 | 0 | tangent.y = -tmp; |
2619 | 0 | } |
2620 | 0 | } |
2621 | | |
2622 | | /* Now we are guaranteed that the remaining arc is 90 degrees or |
2623 | | * less. Find where the tangents meet for this final section. */ |
2624 | 0 | if (line_intersect(¤t, &tangent, |
2625 | 0 | final, final_tangent, &tangmeet) != 0) { |
2626 | 0 | return gx_path_add_line(ppath, final->x, final->y); |
2627 | 0 | } |
2628 | 0 | current.x -= tangmeet.x; |
2629 | 0 | current.y -= tangmeet.y; |
2630 | 0 | dist_squared = ((double)current.x) * current.x + |
2631 | 0 | ((double)current.y) * current.y; |
2632 | 0 | rad_squared = ((double)width->x) * width->x + |
2633 | 0 | ((double)width->y) * width->y; |
2634 | 0 | dist_squared /= rad_squared; |
2635 | 0 | F = (4.0/3.0)*(1/(1+sqrt(1+dist_squared))); |
2636 | 0 | return gx_path_add_partial_arc(ppath, final->x, final->y, |
2637 | 0 | tangmeet.x, tangmeet.y, F); |
2638 | 0 | } |
2639 | | |
2640 | | /* Add a pie shaped join to a path. */ |
2641 | | /* Assume the current point is the cap origin (endp->co). */ |
2642 | | static int |
2643 | | add_pie_join(gx_path * ppath, pl_ptr plp, pl_ptr nplp, bool reflected, |
2644 | | bool cap) |
2645 | 0 | { |
2646 | 0 | int code; |
2647 | 0 | gs_fixed_point *current, *final, *tangent, *final_tangent; |
2648 | 0 | double l, r; |
2649 | 0 | bool ccw; |
2650 | |
|
2651 | 0 | l = (double)(plp->width.x) /* x1 */ * (nplp->width.y) /* y2 */; |
2652 | 0 | r = (double)(nplp->width.x) /* x2 */ * (plp->width.y) /* y1 */; |
2653 | |
|
2654 | 0 | if (l == r) { |
2655 | 0 | if (cap) |
2656 | 0 | return add_pie_cap(ppath, &plp->e); |
2657 | 0 | else |
2658 | 0 | return gx_path_add_line(ppath, plp->e.ce.x, plp->e.ce.y); |
2659 | 0 | } |
2660 | | |
2661 | 0 | ccw = (l > r); |
2662 | |
|
2663 | 0 | ccw ^= reflected; |
2664 | | |
2665 | | /* At this point, the current point is plp->e.co */ |
2666 | 0 | if (ccw) { |
2667 | 0 | current = & plp->e.co; |
2668 | 0 | final = &nplp->o.ce; |
2669 | 0 | tangent = & plp->e.cdelta; |
2670 | 0 | final_tangent = &nplp->o.cdelta; |
2671 | | /* Check for no join required */ |
2672 | 0 | if (current->x == final->x && current->y == final->y) { |
2673 | 0 | return gx_path_add_line(ppath, plp->e.ce.x, plp->e.ce.y); |
2674 | 0 | } |
2675 | 0 | } else { |
2676 | 0 | current = &nplp->o.co; |
2677 | 0 | final = & plp->e.ce; |
2678 | 0 | tangent = &nplp->o.cdelta; |
2679 | 0 | final_tangent = & plp->e.cdelta; |
2680 | 0 | code = gx_path_add_line(ppath, plp->e.p.x, plp->e.p.y); |
2681 | 0 | if (code < 0) |
2682 | 0 | return code; |
2683 | 0 | code = gx_path_add_line(ppath, current->x, current->y); |
2684 | 0 | if (code < 0) |
2685 | 0 | return code; |
2686 | 0 | if (current->x == final->x && current->y == final->y) |
2687 | 0 | return 0; |
2688 | 0 | } |
2689 | | |
2690 | 0 | if ((code = do_pie_join(ppath, &plp->e.p, current, tangent, |
2691 | 0 | final, final_tangent, !reflected, &plp->width)) < 0) |
2692 | 0 | return code; |
2693 | 0 | if (ccw && |
2694 | 0 | ((code = gx_path_add_line(ppath, plp->e.p.x, plp->e.p.y)) < 0 || |
2695 | 0 | (code = gx_path_add_line(ppath, plp->e.ce.x, plp->e.ce.y)) < 0)) |
2696 | 0 | return code; |
2697 | | |
2698 | 0 | return 0; |
2699 | 0 | } |
2700 | | |
2701 | | /* Add a pie shaped join to a path. */ |
2702 | | static int |
2703 | | add_pie_join_fast_cw(gx_path * rpath, pl_ptr plp, pl_ptr nplp, bool reflected) |
2704 | 0 | { |
2705 | | /* At this point, the current point is plp->e.ce */ |
2706 | 0 | if (plp->e.ce.x == nplp->o.co.x && plp->e.ce.y == nplp->o.co.y) |
2707 | 0 | return 0; |
2708 | | |
2709 | 0 | return do_pie_join(rpath, &plp->e.p, &plp->e.ce, &plp->e.cdelta, |
2710 | 0 | &nplp->o.co, &nplp->o.cdelta, reflected, &plp->width); |
2711 | 0 | } |
2712 | | |
2713 | | static int |
2714 | | add_pie_join_fast_ccw(gx_path * ppath, pl_ptr plp, pl_ptr nplp, bool reflected) |
2715 | 0 | { |
2716 | | /* At this point, the current point is plp->e.co */ |
2717 | | /* Check for no join required */ |
2718 | 0 | if (plp->e.co.x == nplp->o.ce.x && plp->e.co.y == nplp->o.ce.y) |
2719 | 0 | return 0; |
2720 | | |
2721 | 0 | return do_pie_join(ppath, &plp->e.p, &plp->e.co, &plp->e.cdelta, |
2722 | 0 | &nplp->o.ce, &nplp->o.cdelta, !reflected, &plp->width); |
2723 | 0 | } |
2724 | | |
2725 | | static int |
2726 | | join_under_pie(gx_path * ppath, pl_ptr plp, pl_ptr nplp, bool reflected) |
2727 | 0 | { |
2728 | 0 | int code; |
2729 | 0 | gs_fixed_point dirn1, dirn2, tangmeet; |
2730 | 0 | double l, r; |
2731 | 0 | bool ccw; |
2732 | |
|
2733 | 0 | l = (double)(plp->width.x) /* x1 */ * (nplp->width.y) /* y2 */; |
2734 | 0 | r = (double)(nplp->width.x) /* x2 */ * (plp->width.y) /* y1 */; |
2735 | |
|
2736 | 0 | if (l == r) |
2737 | 0 | return 0; |
2738 | | |
2739 | 0 | ccw = (l > r); |
2740 | |
|
2741 | 0 | ccw ^= reflected; |
2742 | |
|
2743 | 0 | if (ccw) { |
2744 | 0 | dirn1.x = - plp->width.x; |
2745 | 0 | dirn1.y = - plp->width.y; |
2746 | 0 | dirn2.x = -nplp->width.x; |
2747 | 0 | dirn2.y = -nplp->width.y; |
2748 | 0 | if (line_intersect(& plp->o.co, &dirn1, |
2749 | 0 | &nplp->e.ce, &dirn2, &tangmeet) != 0) |
2750 | 0 | return 0; |
2751 | 0 | if ((code = gx_path_close_subpath(ppath)) < 0 || |
2752 | 0 | (code = gx_path_add_point(ppath, tangmeet.x, tangmeet.y)) < 0 || |
2753 | 0 | (code = gx_path_add_line(ppath,plp->o.co.x,plp->o.co.y)) < 0 || |
2754 | 0 | (code = do_pie_join(ppath, &plp->e.p, &plp->o.co, &plp->o.cdelta, |
2755 | 0 | &nplp->e.ce, &nplp->e.cdelta, !reflected, |
2756 | 0 | &plp->width))) |
2757 | 0 | return code; |
2758 | 0 | } else { |
2759 | 0 | if (line_intersect(& plp->o.ce, & plp->width, |
2760 | 0 | &nplp->e.co, &nplp->width, &tangmeet) != 0) |
2761 | 0 | return 0; |
2762 | 0 | if ((code = gx_path_close_subpath(ppath)) < 0 || |
2763 | 0 | (code = gx_path_add_point(ppath, tangmeet.x, tangmeet.y)) < 0 || |
2764 | 0 | (code = gx_path_add_line(ppath,nplp->e.co.x,nplp->e.co.y)) < 0 || |
2765 | 0 | (code = do_pie_join(ppath, &plp->e.p,&nplp->e.co,&nplp->e.cdelta, |
2766 | 0 | &plp->o.ce, &plp->o.cdelta, !reflected, |
2767 | 0 | &plp->width))) |
2768 | 0 | return code; |
2769 | 0 | } |
2770 | 0 | return 0; |
2771 | 0 | } |
2772 | | |
2773 | | /* Compute the points for a non-round cap. */ |
2774 | | /* Return the number of points. */ |
2775 | | static int |
2776 | | cap_points(gs_line_cap type, const_ep_ptr endp, gs_fixed_point *pts /*[3]*/) |
2777 | 0 | { |
2778 | 0 | #define PUT_POINT(i, px, py)\ |
2779 | 0 | pts[i].x = (px), pts[i].y = (py) |
2780 | 0 | switch (type) { |
2781 | 0 | case gs_cap_butt: |
2782 | 0 | PUT_POINT(0, xo, yo); |
2783 | 0 | PUT_POINT(1, xe, ye); |
2784 | 0 | return 2; |
2785 | 0 | case gs_cap_square: |
2786 | 0 | PUT_POINT(0, xo + cdx, yo + cdy); |
2787 | 0 | PUT_POINT(1, xe + cdx, ye + cdy); |
2788 | 0 | return 2; |
2789 | 0 | case gs_cap_triangle: /* (not supported by PostScript) */ |
2790 | 0 | PUT_POINT(0, xo, yo); |
2791 | 0 | PUT_POINT(1, px + cdx, py + cdy); |
2792 | 0 | PUT_POINT(2, xe, ye); |
2793 | 0 | return 3; |
2794 | 0 | default: /* can't happen */ |
2795 | 0 | return_error(gs_error_unregistered); |
2796 | 0 | } |
2797 | 0 | #undef PUT_POINT |
2798 | 0 | } |