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