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