/work/workdir/UnpackedTarball/cairo/src/cairo-path-stroke-polygon.c
Line | Count | Source |
1 | | /* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ |
2 | | /* cairo - a vector graphics library with display and print output |
3 | | * |
4 | | * Copyright © 2002 University of Southern California |
5 | | * Copyright © 2011 Intel Corporation |
6 | | * |
7 | | * This library is free software; you can redistribute it and/or |
8 | | * modify it either under the terms of the GNU Lesser General Public |
9 | | * License version 2.1 as published by the Free Software Foundation |
10 | | * (the "LGPL") or, at your option, under the terms of the Mozilla |
11 | | * Public License Version 1.1 (the "MPL"). If you do not alter this |
12 | | * notice, a recipient may use your version of this file under either |
13 | | * the MPL or the LGPL. |
14 | | * |
15 | | * You should have received a copy of the LGPL along with this library |
16 | | * in the file COPYING-LGPL-2.1; if not, write to the Free Software |
17 | | * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA |
18 | | * You should have received a copy of the MPL along with this library |
19 | | * in the file COPYING-MPL-1.1 |
20 | | * |
21 | | * The contents of this file are subject to the Mozilla Public License |
22 | | * Version 1.1 (the "License"); you may not use this file except in |
23 | | * compliance with the License. You may obtain a copy of the License at |
24 | | * http://www.mozilla.org/MPL/ |
25 | | * |
26 | | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY |
27 | | * OF ANY KIND, either express or implied. See the LGPL or the MPL for |
28 | | * the specific language governing rights and limitations. |
29 | | * |
30 | | * The Original Code is the cairo graphics library. |
31 | | * |
32 | | * The Initial Developer of the Original Code is University of Southern |
33 | | * California. |
34 | | * |
35 | | * Contributor(s): |
36 | | * Carl D. Worth <cworth@cworth.org> |
37 | | * Chris Wilson <chris@chris-wilson.co.uk> |
38 | | */ |
39 | | |
40 | | #define _DEFAULT_SOURCE /* for hypot() */ |
41 | | #include "cairoint.h" |
42 | | |
43 | | #include "cairo-box-inline.h" |
44 | | #include "cairo-boxes-private.h" |
45 | | #include "cairo-contour-inline.h" |
46 | | #include "cairo-contour-private.h" |
47 | | #include "cairo-error-private.h" |
48 | | #include "cairo-path-fixed-private.h" |
49 | | #include "cairo-slope-private.h" |
50 | | |
51 | | #define DEBUG 0 |
52 | | |
53 | | struct stroker { |
54 | | cairo_stroke_style_t style; |
55 | | |
56 | | #if DEBUG |
57 | | cairo_contour_t path; |
58 | | #endif |
59 | | |
60 | | struct stroke_contour { |
61 | | /* Note that these are not strictly contours as they may intersect */ |
62 | | cairo_contour_t contour; |
63 | | } cw, ccw; |
64 | | cairo_uint64_t contour_tolerance; |
65 | | cairo_polygon_t *polygon; |
66 | | |
67 | | const cairo_matrix_t *ctm; |
68 | | const cairo_matrix_t *ctm_inverse; |
69 | | double tolerance; |
70 | | double spline_cusp_tolerance; |
71 | | double half_line_width; |
72 | | cairo_bool_t ctm_det_positive; |
73 | | |
74 | | cairo_pen_t pen; |
75 | | |
76 | | cairo_point_t first_point; |
77 | | |
78 | | cairo_bool_t has_initial_sub_path; |
79 | | |
80 | | cairo_bool_t has_current_face; |
81 | | cairo_stroke_face_t current_face; |
82 | | |
83 | | cairo_bool_t has_first_face; |
84 | | cairo_stroke_face_t first_face; |
85 | | |
86 | | cairo_bool_t has_bounds; |
87 | | cairo_box_t bounds; |
88 | | }; |
89 | | |
90 | | static inline double |
91 | | normalize_slope (double *dx, double *dy); |
92 | | |
93 | | static void |
94 | | compute_face (const cairo_point_t *point, |
95 | | const cairo_slope_t *dev_slope, |
96 | | struct stroker *stroker, |
97 | | cairo_stroke_face_t *face); |
98 | | |
99 | | static cairo_uint64_t |
100 | | point_distance_sq (const cairo_point_t *p1, |
101 | | const cairo_point_t *p2) |
102 | 0 | { |
103 | 0 | int32_t dx = p1->x - p2->x; |
104 | 0 | int32_t dy = p1->y - p2->y; |
105 | 0 | return _cairo_int32x32_64_mul (dx, dx) + _cairo_int32x32_64_mul (dy, dy); |
106 | 0 | } |
107 | | |
108 | | static cairo_bool_t |
109 | | within_tolerance (const cairo_point_t *p1, |
110 | | const cairo_point_t *p2, |
111 | | cairo_uint64_t tolerance) |
112 | 3.18M | { |
113 | 3.18M | return FALSE; |
114 | 0 | return _cairo_int64_lt (point_distance_sq (p1, p2), tolerance); |
115 | 3.18M | } |
116 | | |
117 | | static void |
118 | | contour_add_point (struct stroker *stroker, |
119 | | struct stroke_contour *c, |
120 | | const cairo_point_t *point) |
121 | 3.11M | { |
122 | 3.11M | if (! within_tolerance (point, _cairo_contour_last_point (&c->contour), |
123 | 3.11M | stroker->contour_tolerance)) |
124 | 3.11M | _cairo_contour_add_point (&c->contour, point); |
125 | | //*_cairo_contour_last_point (&c->contour) = *point; |
126 | 3.11M | } |
127 | | |
128 | | static void |
129 | | translate_point (cairo_point_t *point, const cairo_point_t *offset) |
130 | 2.19M | { |
131 | 2.19M | point->x += offset->x; |
132 | 2.19M | point->y += offset->y; |
133 | 2.19M | } |
134 | | |
135 | | static int |
136 | | slope_compare_sgn (double dx1, double dy1, double dx2, double dy2) |
137 | 37.2k | { |
138 | 37.2k | double c = (dx1 * dy2 - dx2 * dy1); |
139 | | |
140 | 37.2k | if (c > 0) return 1; |
141 | 19.8k | if (c < 0) return -1; |
142 | 1.65k | return 0; |
143 | 19.8k | } |
144 | | |
145 | | /* |
146 | | * Construct a fan around the midpoint using the vertices from pen between |
147 | | * inpt and outpt. |
148 | | */ |
149 | | static void |
150 | | add_fan (struct stroker *stroker, |
151 | | const cairo_slope_t *in_vector, |
152 | | const cairo_slope_t *out_vector, |
153 | | const cairo_point_t *midpt, |
154 | | cairo_bool_t clockwise, |
155 | | struct stroke_contour *c) |
156 | 530 | { |
157 | 530 | cairo_pen_t *pen = &stroker->pen; |
158 | 530 | int start, stop; |
159 | | |
160 | 530 | if (stroker->has_bounds && |
161 | 530 | ! _cairo_box_contains_point (&stroker->bounds, midpt)) |
162 | 320 | return; |
163 | | |
164 | 530 | assert (stroker->pen.num_vertices); |
165 | | |
166 | 210 | if (clockwise) { |
167 | 89 | _cairo_pen_find_active_cw_vertices (pen, |
168 | 89 | in_vector, out_vector, |
169 | 89 | &start, &stop); |
170 | 1.11k | while (start != stop) { |
171 | 1.02k | cairo_point_t p = *midpt; |
172 | 1.02k | translate_point (&p, &pen->vertices[start].point); |
173 | 1.02k | contour_add_point (stroker, c, &p); |
174 | | |
175 | 1.02k | if (++start == pen->num_vertices) |
176 | 15 | start = 0; |
177 | 1.02k | } |
178 | 121 | } else { |
179 | 121 | _cairo_pen_find_active_ccw_vertices (pen, |
180 | 121 | in_vector, out_vector, |
181 | 121 | &start, &stop); |
182 | 2.00k | while (start != stop) { |
183 | 1.88k | cairo_point_t p = *midpt; |
184 | 1.88k | translate_point (&p, &pen->vertices[start].point); |
185 | 1.88k | contour_add_point (stroker, c, &p); |
186 | | |
187 | 1.88k | if (start-- == 0) |
188 | 23 | start += pen->num_vertices; |
189 | 1.88k | } |
190 | 121 | } |
191 | 210 | } |
192 | | |
193 | | static int |
194 | | join_is_clockwise (const cairo_stroke_face_t *in, |
195 | | const cairo_stroke_face_t *out) |
196 | 1.44k | { |
197 | 1.44k | return _cairo_slope_compare (&in->dev_vector, &out->dev_vector) < 0; |
198 | 1.44k | } |
199 | | |
200 | | static void |
201 | | inner_join (struct stroker *stroker, |
202 | | const cairo_stroke_face_t *in, |
203 | | const cairo_stroke_face_t *out, |
204 | | int clockwise) |
205 | 72.7k | { |
206 | | #if 0 |
207 | | cairo_point_t last; |
208 | | const cairo_point_t *p, *outpt; |
209 | | struct stroke_contour *inner; |
210 | | cairo_int64_t d_p, d_last; |
211 | | cairo_int64_t half_line_width; |
212 | | cairo_bool_t negate; |
213 | | |
214 | | /* XXX line segments shorter than line-width */ |
215 | | |
216 | | if (clockwise) { |
217 | | inner = &stroker->ccw; |
218 | | outpt = &out->ccw; |
219 | | negate = 1; |
220 | | } else { |
221 | | inner = &stroker->cw; |
222 | | outpt = &out->cw; |
223 | | negate = 0; |
224 | | } |
225 | | |
226 | | half_line_width = CAIRO_FIXED_ONE*CAIRO_FIXED_ONE/2 * stroker->style.line_width * out->length + .5; |
227 | | |
228 | | /* On the inside, the previous end-point is always |
229 | | * closer to the new face by definition. |
230 | | */ |
231 | | last = *_cairo_contour_last_point (&inner->contour); |
232 | | d_last = distance_from_face (out, &last, negate); |
233 | | _cairo_contour_remove_last_point (&inner->contour); |
234 | | |
235 | | prev: |
236 | | if (inner->contour.chain.num_points == 0) { |
237 | | contour_add_point (stroker, inner, outpt); |
238 | | return; |
239 | | } |
240 | | p = _cairo_contour_last_point (&inner->contour); |
241 | | d_p = distance_from_face (out, p, negate); |
242 | | if (_cairo_int64_lt (d_p, half_line_width) && |
243 | | !_cairo_int64_negative (distance_along_face (out, p))) |
244 | | { |
245 | | last = *p; |
246 | | d_last = d_p; |
247 | | _cairo_contour_remove_last_point (&inner->contour); |
248 | | goto prev; |
249 | | } |
250 | | |
251 | | compute_inner_joint (&last, d_last, p, d_p, half_line_width); |
252 | | contour_add_point (stroker, inner, &last); |
253 | | #else |
254 | 72.7k | const cairo_point_t *outpt; |
255 | 72.7k | struct stroke_contour *inner; |
256 | | |
257 | 72.7k | if (clockwise) { |
258 | 14.0k | inner = &stroker->ccw; |
259 | 14.0k | outpt = &out->ccw; |
260 | 58.6k | } else { |
261 | 58.6k | inner = &stroker->cw; |
262 | 58.6k | outpt = &out->cw; |
263 | 58.6k | } |
264 | 72.7k | contour_add_point (stroker, inner, &in->point); |
265 | 72.7k | contour_add_point (stroker, inner, outpt); |
266 | 72.7k | #endif |
267 | 72.7k | } |
268 | | |
269 | | static void |
270 | | inner_close (struct stroker *stroker, |
271 | | const cairo_stroke_face_t *in, |
272 | | cairo_stroke_face_t *out) |
273 | 721 | { |
274 | | #if 0 |
275 | | cairo_point_t last; |
276 | | const cairo_point_t *p, *outpt, *inpt; |
277 | | struct stroke_contour *inner; |
278 | | struct _cairo_contour_chain *chain; |
279 | | |
280 | | /* XXX line segments shorter than line-width */ |
281 | | |
282 | | if (join_is_clockwise (in, out)) { |
283 | | inner = &stroker->ccw; |
284 | | outpt = &in->ccw; |
285 | | inpt = &out->ccw; |
286 | | } else { |
287 | | inner = &stroker->cw; |
288 | | outpt = &in->cw; |
289 | | inpt = &out->cw; |
290 | | } |
291 | | |
292 | | if (inner->contour.chain.num_points == 0) { |
293 | | contour_add_point (stroker, inner, &in->point); |
294 | | contour_add_point (stroker, inner, inpt); |
295 | | *_cairo_contour_first_point (&inner->contour) = |
296 | | *_cairo_contour_last_point (&inner->contour); |
297 | | return; |
298 | | } |
299 | | |
300 | | line_width = stroker->style.line_width/2; |
301 | | line_width *= CAIRO_FIXED_ONE; |
302 | | |
303 | | d_last = sign * distance_from_face (out, outpt); |
304 | | last = *outpt; |
305 | | |
306 | | for (chain = &inner->contour.chain; chain; chain = chain->next) { |
307 | | for (i = 0; i < chain->num_points; i++) { |
308 | | p = &chain->points[i]; |
309 | | if ((d_p = sign * distance_from_face (in, p)) >= line_width && |
310 | | distance_from_edge (stroker, inpt, &last, p) < line_width) |
311 | | { |
312 | | goto out; |
313 | | } |
314 | | |
315 | | if (p->x != last.x || p->y != last.y) { |
316 | | last = *p; |
317 | | d_last = d_p; |
318 | | } |
319 | | } |
320 | | } |
321 | | out: |
322 | | |
323 | | if (d_p != d_last) { |
324 | | double dot = (line_width - d_last) / (d_p - d_last); |
325 | | last.x += dot * (p->x - last.x); |
326 | | last.y += dot * (p->y - last.y); |
327 | | } |
328 | | *_cairo_contour_last_point (&inner->contour) = last; |
329 | | |
330 | | for (chain = &inner->contour.chain; chain; chain = chain->next) { |
331 | | for (i = 0; i < chain->num_points; i++) { |
332 | | cairo_point_t *pp = &chain->points[i]; |
333 | | if (pp == p) |
334 | | return; |
335 | | *pp = last; |
336 | | } |
337 | | } |
338 | | #else |
339 | 721 | const cairo_point_t *inpt; |
340 | 721 | struct stroke_contour *inner; |
341 | | |
342 | 721 | if (join_is_clockwise (in, out)) { |
343 | 82 | inner = &stroker->ccw; |
344 | 82 | inpt = &out->ccw; |
345 | 639 | } else { |
346 | 639 | inner = &stroker->cw; |
347 | 639 | inpt = &out->cw; |
348 | 639 | } |
349 | | |
350 | 721 | contour_add_point (stroker, inner, &in->point); |
351 | 721 | contour_add_point (stroker, inner, inpt); |
352 | 721 | *_cairo_contour_first_point (&inner->contour) = |
353 | 721 | *_cairo_contour_last_point (&inner->contour); |
354 | 721 | #endif |
355 | 721 | } |
356 | | |
357 | | static void |
358 | | outer_close (struct stroker *stroker, |
359 | | const cairo_stroke_face_t *in, |
360 | | const cairo_stroke_face_t *out) |
361 | 721 | { |
362 | 721 | const cairo_point_t *inpt, *outpt; |
363 | 721 | struct stroke_contour *outer; |
364 | 721 | int clockwise; |
365 | | |
366 | 721 | if (in->cw.x == out->cw.x && in->cw.y == out->cw.y && |
367 | 6 | in->ccw.x == out->ccw.x && in->ccw.y == out->ccw.y) |
368 | 6 | { |
369 | 6 | return; |
370 | 6 | } |
371 | | |
372 | 715 | clockwise = join_is_clockwise (in, out); |
373 | 715 | if (clockwise) { |
374 | 81 | inpt = &in->cw; |
375 | 81 | outpt = &out->cw; |
376 | 81 | outer = &stroker->cw; |
377 | 634 | } else { |
378 | 634 | inpt = &in->ccw; |
379 | 634 | outpt = &out->ccw; |
380 | 634 | outer = &stroker->ccw; |
381 | 634 | } |
382 | | |
383 | 715 | if (within_tolerance (inpt, outpt, stroker->contour_tolerance)) { |
384 | 0 | *_cairo_contour_first_point (&outer->contour) = |
385 | 0 | *_cairo_contour_last_point (&outer->contour); |
386 | 0 | return; |
387 | 0 | } |
388 | | |
389 | 715 | switch (stroker->style.line_join) { |
390 | 11 | case CAIRO_LINE_JOIN_ROUND: |
391 | 11 | if ((in->dev_slope.x * out->dev_slope.x + |
392 | 11 | in->dev_slope.y * out->dev_slope.y) < stroker->spline_cusp_tolerance) |
393 | 8 | { |
394 | | /* construct a fan around the common midpoint */ |
395 | 8 | add_fan (stroker, |
396 | 8 | &in->dev_vector, &out->dev_vector, &in->point, |
397 | 8 | clockwise, outer); |
398 | 8 | } /* else: bevel join */ |
399 | 11 | break; |
400 | | |
401 | 704 | case CAIRO_LINE_JOIN_MITER: |
402 | 704 | default: { |
403 | | /* dot product of incoming slope vector with outgoing slope vector */ |
404 | 704 | double in_dot_out = in->dev_slope.x * out->dev_slope.x + |
405 | 704 | in->dev_slope.y * out->dev_slope.y; |
406 | 704 | double ml = stroker->style.miter_limit; |
407 | | |
408 | | /* Check the miter limit -- lines meeting at an acute angle |
409 | | * can generate long miters, the limit converts them to bevel |
410 | | * |
411 | | * Consider the miter join formed when two line segments |
412 | | * meet at an angle psi: |
413 | | * |
414 | | * /.\ |
415 | | * /. .\ |
416 | | * /./ \.\ |
417 | | * /./psi\.\ |
418 | | * |
419 | | * We can zoom in on the right half of that to see: |
420 | | * |
421 | | * |\ |
422 | | * | \ psi/2 |
423 | | * | \ |
424 | | * | \ |
425 | | * | \ |
426 | | * | \ |
427 | | * miter \ |
428 | | * length \ |
429 | | * | \ |
430 | | * | .\ |
431 | | * | . \ |
432 | | * |. line \ |
433 | | * \ width \ |
434 | | * \ \ |
435 | | * |
436 | | * |
437 | | * The right triangle in that figure, (the line-width side is |
438 | | * shown faintly with three '.' characters), gives us the |
439 | | * following expression relating miter length, angle and line |
440 | | * width: |
441 | | * |
442 | | * 1 /sin (psi/2) = miter_length / line_width |
443 | | * |
444 | | * The right-hand side of this relationship is the same ratio |
445 | | * in which the miter limit (ml) is expressed. We want to know |
446 | | * when the miter length is within the miter limit. That is |
447 | | * when the following condition holds: |
448 | | * |
449 | | * 1/sin(psi/2) <= ml |
450 | | * 1 <= ml sin(psi/2) |
451 | | * 1 <= ml² sin²(psi/2) |
452 | | * 2 <= ml² 2 sin²(psi/2) |
453 | | * 2·sin²(psi/2) = 1-cos(psi) |
454 | | * 2 <= ml² (1-cos(psi)) |
455 | | * |
456 | | * in · out = |in| |out| cos (psi) |
457 | | * |
458 | | * in and out are both unit vectors, so: |
459 | | * |
460 | | * in · out = cos (psi) |
461 | | * |
462 | | * 2 <= ml² (1 - in · out) |
463 | | * |
464 | | */ |
465 | 704 | if (2 <= ml * ml * (1 + in_dot_out)) { |
466 | 250 | double x1, y1, x2, y2; |
467 | 250 | double mx, my; |
468 | 250 | double dx1, dx2, dy1, dy2; |
469 | 250 | double ix, iy; |
470 | 250 | double fdx1, fdy1, fdx2, fdy2; |
471 | 250 | double mdx, mdy; |
472 | | |
473 | | /* |
474 | | * we've got the points already transformed to device |
475 | | * space, but need to do some computation with them and |
476 | | * also need to transform the slope from user space to |
477 | | * device space |
478 | | */ |
479 | | /* outer point of incoming line face */ |
480 | 250 | x1 = _cairo_fixed_to_double (inpt->x); |
481 | 250 | y1 = _cairo_fixed_to_double (inpt->y); |
482 | 250 | dx1 = in->dev_slope.x; |
483 | 250 | dy1 = in->dev_slope.y; |
484 | | |
485 | | /* outer point of outgoing line face */ |
486 | 250 | x2 = _cairo_fixed_to_double (outpt->x); |
487 | 250 | y2 = _cairo_fixed_to_double (outpt->y); |
488 | 250 | dx2 = out->dev_slope.x; |
489 | 250 | dy2 = out->dev_slope.y; |
490 | | |
491 | | /* |
492 | | * Compute the location of the outer corner of the miter. |
493 | | * That's pretty easy -- just the intersection of the two |
494 | | * outer edges. We've got slopes and points on each |
495 | | * of those edges. Compute my directly, then compute |
496 | | * mx by using the edge with the larger dy; that avoids |
497 | | * dividing by values close to zero. |
498 | | */ |
499 | 250 | my = (((x2 - x1) * dy1 * dy2 - y2 * dx2 * dy1 + y1 * dx1 * dy2) / |
500 | 250 | (dx1 * dy2 - dx2 * dy1)); |
501 | 250 | if (fabs (dy1) >= fabs (dy2)) |
502 | 137 | mx = (my - y1) * dx1 / dy1 + x1; |
503 | 113 | else |
504 | 113 | mx = (my - y2) * dx2 / dy2 + x2; |
505 | | |
506 | | /* |
507 | | * When the two outer edges are nearly parallel, slight |
508 | | * perturbations in the position of the outer points of the lines |
509 | | * caused by representing them in fixed point form can cause the |
510 | | * intersection point of the miter to move a large amount. If |
511 | | * that moves the miter intersection from between the two faces, |
512 | | * then draw a bevel instead. |
513 | | */ |
514 | | |
515 | 250 | ix = _cairo_fixed_to_double (in->point.x); |
516 | 250 | iy = _cairo_fixed_to_double (in->point.y); |
517 | | |
518 | | /* slope of one face */ |
519 | 250 | fdx1 = x1 - ix; fdy1 = y1 - iy; |
520 | | |
521 | | /* slope of the other face */ |
522 | 250 | fdx2 = x2 - ix; fdy2 = y2 - iy; |
523 | | |
524 | | /* slope from the intersection to the miter point */ |
525 | 250 | mdx = mx - ix; mdy = my - iy; |
526 | | |
527 | | /* |
528 | | * Make sure the miter point line lies between the two |
529 | | * faces by comparing the slopes |
530 | | */ |
531 | 250 | if (slope_compare_sgn (fdx1, fdy1, mdx, mdy) != |
532 | 250 | slope_compare_sgn (fdx2, fdy2, mdx, mdy)) |
533 | 250 | { |
534 | 250 | cairo_point_t p; |
535 | | |
536 | 250 | p.x = _cairo_fixed_from_double (mx); |
537 | 250 | p.y = _cairo_fixed_from_double (my); |
538 | | |
539 | 250 | *_cairo_contour_last_point (&outer->contour) = p; |
540 | 250 | *_cairo_contour_first_point (&outer->contour) = p; |
541 | 250 | return; |
542 | 250 | } |
543 | 250 | } |
544 | 454 | break; |
545 | 704 | } |
546 | | |
547 | 454 | case CAIRO_LINE_JOIN_BEVEL: |
548 | 0 | break; |
549 | 715 | } |
550 | 465 | contour_add_point (stroker, outer, outpt); |
551 | 465 | } |
552 | | |
553 | | static void |
554 | | outer_join (struct stroker *stroker, |
555 | | const cairo_stroke_face_t *in, |
556 | | const cairo_stroke_face_t *out, |
557 | | int clockwise) |
558 | 72.7k | { |
559 | 72.7k | const cairo_point_t *inpt, *outpt; |
560 | 72.7k | struct stroke_contour *outer; |
561 | | |
562 | 72.7k | if (in->cw.x == out->cw.x && in->cw.y == out->cw.y && |
563 | 50.7k | in->ccw.x == out->ccw.x && in->ccw.y == out->ccw.y) |
564 | 50.7k | { |
565 | 50.7k | return; |
566 | 50.7k | } |
567 | 22.0k | if (clockwise) { |
568 | 7.43k | inpt = &in->cw; |
569 | 7.43k | outpt = &out->cw; |
570 | 7.43k | outer = &stroker->cw; |
571 | 14.5k | } else { |
572 | 14.5k | inpt = &in->ccw; |
573 | 14.5k | outpt = &out->ccw; |
574 | 14.5k | outer = &stroker->ccw; |
575 | 14.5k | } |
576 | | |
577 | 22.0k | switch (stroker->style.line_join) { |
578 | 1.19k | case CAIRO_LINE_JOIN_ROUND: |
579 | 1.19k | if ((in->dev_slope.x * out->dev_slope.x + |
580 | 1.19k | in->dev_slope.y * out->dev_slope.y) < stroker->spline_cusp_tolerance) |
581 | 518 | { |
582 | | /* construct a fan around the common midpoint */ |
583 | 518 | add_fan (stroker, |
584 | 518 | &in->dev_vector, &out->dev_vector, &in->point, |
585 | 518 | clockwise, outer); |
586 | 518 | } /* else: bevel join */ |
587 | 1.19k | break; |
588 | | |
589 | 20.8k | case CAIRO_LINE_JOIN_MITER: |
590 | 20.8k | default: { |
591 | | /* dot product of incoming slope vector with outgoing slope vector */ |
592 | 20.8k | double in_dot_out = in->dev_slope.x * out->dev_slope.x + |
593 | 20.8k | in->dev_slope.y * out->dev_slope.y; |
594 | 20.8k | double ml = stroker->style.miter_limit; |
595 | | |
596 | | /* Check the miter limit -- lines meeting at an acute angle |
597 | | * can generate long miters, the limit converts them to bevel |
598 | | * |
599 | | * Consider the miter join formed when two line segments |
600 | | * meet at an angle psi: |
601 | | * |
602 | | * /.\ |
603 | | * /. .\ |
604 | | * /./ \.\ |
605 | | * /./psi\.\ |
606 | | * |
607 | | * We can zoom in on the right half of that to see: |
608 | | * |
609 | | * |\ |
610 | | * | \ psi/2 |
611 | | * | \ |
612 | | * | \ |
613 | | * | \ |
614 | | * | \ |
615 | | * miter \ |
616 | | * length \ |
617 | | * | \ |
618 | | * | .\ |
619 | | * | . \ |
620 | | * |. line \ |
621 | | * \ width \ |
622 | | * \ \ |
623 | | * |
624 | | * |
625 | | * The right triangle in that figure, (the line-width side is |
626 | | * shown faintly with three '.' characters), gives us the |
627 | | * following expression relating miter length, angle and line |
628 | | * width: |
629 | | * |
630 | | * 1 /sin (psi/2) = miter_length / line_width |
631 | | * |
632 | | * The right-hand side of this relationship is the same ratio |
633 | | * in which the miter limit (ml) is expressed. We want to know |
634 | | * when the miter length is within the miter limit. That is |
635 | | * when the following condition holds: |
636 | | * |
637 | | * 1/sin(psi/2) <= ml |
638 | | * 1 <= ml sin(psi/2) |
639 | | * 1 <= ml² sin²(psi/2) |
640 | | * 2 <= ml² 2 sin²(psi/2) |
641 | | * 2·sin²(psi/2) = 1-cos(psi) |
642 | | * 2 <= ml² (1-cos(psi)) |
643 | | * |
644 | | * in · out = |in| |out| cos (psi) |
645 | | * |
646 | | * in and out are both unit vectors, so: |
647 | | * |
648 | | * in · out = cos (psi) |
649 | | * |
650 | | * 2 <= ml² (1 - in · out) |
651 | | * |
652 | | */ |
653 | 20.8k | if (2 <= ml * ml * (1 + in_dot_out)) { |
654 | 18.3k | double x1, y1, x2, y2; |
655 | 18.3k | double mx, my; |
656 | 18.3k | double dx1, dx2, dy1, dy2; |
657 | 18.3k | double ix, iy; |
658 | 18.3k | double fdx1, fdy1, fdx2, fdy2; |
659 | 18.3k | double mdx, mdy; |
660 | | |
661 | | /* |
662 | | * we've got the points already transformed to device |
663 | | * space, but need to do some computation with them and |
664 | | * also need to transform the slope from user space to |
665 | | * device space |
666 | | */ |
667 | | /* outer point of incoming line face */ |
668 | 18.3k | x1 = _cairo_fixed_to_double (inpt->x); |
669 | 18.3k | y1 = _cairo_fixed_to_double (inpt->y); |
670 | 18.3k | dx1 = in->dev_slope.x; |
671 | 18.3k | dy1 = in->dev_slope.y; |
672 | | |
673 | | /* outer point of outgoing line face */ |
674 | 18.3k | x2 = _cairo_fixed_to_double (outpt->x); |
675 | 18.3k | y2 = _cairo_fixed_to_double (outpt->y); |
676 | 18.3k | dx2 = out->dev_slope.x; |
677 | 18.3k | dy2 = out->dev_slope.y; |
678 | | |
679 | | /* |
680 | | * Compute the location of the outer corner of the miter. |
681 | | * That's pretty easy -- just the intersection of the two |
682 | | * outer edges. We've got slopes and points on each |
683 | | * of those edges. Compute my directly, then compute |
684 | | * mx by using the edge with the larger dy; that avoids |
685 | | * dividing by values close to zero. |
686 | | */ |
687 | 18.3k | my = (((x2 - x1) * dy1 * dy2 - y2 * dx2 * dy1 + y1 * dx1 * dy2) / |
688 | 18.3k | (dx1 * dy2 - dx2 * dy1)); |
689 | 18.3k | if (fabs (dy1) >= fabs (dy2)) |
690 | 9.17k | mx = (my - y1) * dx1 / dy1 + x1; |
691 | 9.21k | else |
692 | 9.21k | mx = (my - y2) * dx2 / dy2 + x2; |
693 | | |
694 | | /* |
695 | | * When the two outer edges are nearly parallel, slight |
696 | | * perturbations in the position of the outer points of the lines |
697 | | * caused by representing them in fixed point form can cause the |
698 | | * intersection point of the miter to move a large amount. If |
699 | | * that moves the miter intersection from between the two faces, |
700 | | * then draw a bevel instead. |
701 | | */ |
702 | | |
703 | 18.3k | ix = _cairo_fixed_to_double (in->point.x); |
704 | 18.3k | iy = _cairo_fixed_to_double (in->point.y); |
705 | | |
706 | | /* slope of one face */ |
707 | 18.3k | fdx1 = x1 - ix; fdy1 = y1 - iy; |
708 | | |
709 | | /* slope of the other face */ |
710 | 18.3k | fdx2 = x2 - ix; fdy2 = y2 - iy; |
711 | | |
712 | | /* slope from the intersection to the miter point */ |
713 | 18.3k | mdx = mx - ix; mdy = my - iy; |
714 | | |
715 | | /* |
716 | | * Make sure the miter point line lies between the two |
717 | | * faces by comparing the slopes |
718 | | */ |
719 | 18.3k | if (slope_compare_sgn (fdx1, fdy1, mdx, mdy) != |
720 | 18.3k | slope_compare_sgn (fdx2, fdy2, mdx, mdy)) |
721 | 7.49k | { |
722 | 7.49k | cairo_point_t p; |
723 | | |
724 | 7.49k | p.x = _cairo_fixed_from_double (mx); |
725 | 7.49k | p.y = _cairo_fixed_from_double (my); |
726 | | |
727 | 7.49k | *_cairo_contour_last_point (&outer->contour) = p; |
728 | 7.49k | return; |
729 | 7.49k | } |
730 | 18.3k | } |
731 | 13.3k | break; |
732 | 20.8k | } |
733 | | |
734 | 13.3k | case CAIRO_LINE_JOIN_BEVEL: |
735 | 0 | break; |
736 | 22.0k | } |
737 | 14.5k | contour_add_point (stroker,outer, outpt); |
738 | 14.5k | } |
739 | | |
740 | | static void |
741 | | add_cap (struct stroker *stroker, |
742 | | const cairo_stroke_face_t *f, |
743 | | struct stroke_contour *c) |
744 | 591k | { |
745 | 591k | switch (stroker->style.line_cap) { |
746 | 0 | case CAIRO_LINE_CAP_ROUND: { |
747 | 0 | cairo_slope_t slope; |
748 | |
|
749 | 0 | slope.dx = -f->dev_vector.dx; |
750 | 0 | slope.dy = -f->dev_vector.dy; |
751 | |
|
752 | 0 | add_fan (stroker, &f->dev_vector, &slope, &f->point, FALSE, c); |
753 | 0 | break; |
754 | 0 | } |
755 | | |
756 | 0 | case CAIRO_LINE_CAP_SQUARE: { |
757 | 0 | cairo_slope_t fvector; |
758 | 0 | cairo_point_t p; |
759 | 0 | double dx, dy; |
760 | |
|
761 | 0 | dx = f->usr_vector.x; |
762 | 0 | dy = f->usr_vector.y; |
763 | 0 | dx *= stroker->half_line_width; |
764 | 0 | dy *= stroker->half_line_width; |
765 | 0 | cairo_matrix_transform_distance (stroker->ctm, &dx, &dy); |
766 | 0 | fvector.dx = _cairo_fixed_from_double (dx); |
767 | 0 | fvector.dy = _cairo_fixed_from_double (dy); |
768 | |
|
769 | 0 | p.x = f->ccw.x + fvector.dx; |
770 | 0 | p.y = f->ccw.y + fvector.dy; |
771 | 0 | contour_add_point (stroker, c, &p); |
772 | |
|
773 | 0 | p.x = f->cw.x + fvector.dx; |
774 | 0 | p.y = f->cw.y + fvector.dy; |
775 | 0 | contour_add_point (stroker, c, &p); |
776 | 0 | } |
777 | |
|
778 | 591k | case CAIRO_LINE_CAP_BUTT: |
779 | 591k | default: |
780 | 591k | break; |
781 | 591k | } |
782 | 591k | contour_add_point (stroker, c, &f->cw); |
783 | 591k | } |
784 | | |
785 | | static void |
786 | | add_leading_cap (struct stroker *stroker, |
787 | | const cairo_stroke_face_t *face, |
788 | | struct stroke_contour *c) |
789 | 295k | { |
790 | 295k | cairo_stroke_face_t reversed; |
791 | 295k | cairo_point_t t; |
792 | | |
793 | 295k | reversed = *face; |
794 | | |
795 | | /* The initial cap needs an outward facing vector. Reverse everything */ |
796 | 295k | reversed.usr_vector.x = -reversed.usr_vector.x; |
797 | 295k | reversed.usr_vector.y = -reversed.usr_vector.y; |
798 | 295k | reversed.dev_vector.dx = -reversed.dev_vector.dx; |
799 | 295k | reversed.dev_vector.dy = -reversed.dev_vector.dy; |
800 | | |
801 | 295k | t = reversed.cw; |
802 | 295k | reversed.cw = reversed.ccw; |
803 | 295k | reversed.ccw = t; |
804 | | |
805 | 295k | add_cap (stroker, &reversed, c); |
806 | 295k | } |
807 | | |
808 | | static void |
809 | | add_trailing_cap (struct stroker *stroker, |
810 | | const cairo_stroke_face_t *face, |
811 | | struct stroke_contour *c) |
812 | 295k | { |
813 | 295k | add_cap (stroker, face, c); |
814 | 295k | } |
815 | | |
816 | | static inline double |
817 | | normalize_slope (double *dx, double *dy) |
818 | 2.19M | { |
819 | 2.19M | double dx0 = *dx, dy0 = *dy; |
820 | 2.19M | double mag; |
821 | | |
822 | 2.19M | assert (dx0 != 0.0 || dy0 != 0.0); |
823 | | |
824 | 2.19M | if (dx0 == 0.0) { |
825 | 444k | *dx = 0.0; |
826 | 444k | if (dy0 > 0.0) { |
827 | 292k | mag = dy0; |
828 | 292k | *dy = 1.0; |
829 | 292k | } else { |
830 | 152k | mag = -dy0; |
831 | 152k | *dy = -1.0; |
832 | 152k | } |
833 | 1.75M | } else if (dy0 == 0.0) { |
834 | 438k | *dy = 0.0; |
835 | 438k | if (dx0 > 0.0) { |
836 | 149k | mag = dx0; |
837 | 149k | *dx = 1.0; |
838 | 289k | } else { |
839 | 289k | mag = -dx0; |
840 | 289k | *dx = -1.0; |
841 | 289k | } |
842 | 1.31M | } else { |
843 | 1.31M | mag = hypot (dx0, dy0); |
844 | 1.31M | *dx = dx0 / mag; |
845 | 1.31M | *dy = dy0 / mag; |
846 | 1.31M | } |
847 | | |
848 | 2.19M | return mag; |
849 | 2.19M | } |
850 | | |
851 | | static void |
852 | | compute_face (const cairo_point_t *point, |
853 | | const cairo_slope_t *dev_slope, |
854 | | struct stroker *stroker, |
855 | | cairo_stroke_face_t *face) |
856 | 1.09M | { |
857 | 1.09M | double face_dx, face_dy; |
858 | 1.09M | cairo_point_t offset_ccw, offset_cw; |
859 | 1.09M | double slope_dx, slope_dy; |
860 | | |
861 | 1.09M | slope_dx = _cairo_fixed_to_double (dev_slope->dx); |
862 | 1.09M | slope_dy = _cairo_fixed_to_double (dev_slope->dy); |
863 | 1.09M | face->length = normalize_slope (&slope_dx, &slope_dy); |
864 | 1.09M | face->dev_slope.x = slope_dx; |
865 | 1.09M | face->dev_slope.y = slope_dy; |
866 | | |
867 | | /* |
868 | | * rotate to get a line_width/2 vector along the face, note that |
869 | | * the vector must be rotated the right direction in device space, |
870 | | * but by 90° in user space. So, the rotation depends on |
871 | | * whether the ctm reflects or not, and that can be determined |
872 | | * by looking at the determinant of the matrix. |
873 | | */ |
874 | 1.09M | if (! _cairo_matrix_is_identity (stroker->ctm_inverse)) { |
875 | | /* Normalize the matrix! */ |
876 | 1.09M | cairo_matrix_transform_distance (stroker->ctm_inverse, |
877 | 1.09M | &slope_dx, &slope_dy); |
878 | 1.09M | normalize_slope (&slope_dx, &slope_dy); |
879 | | |
880 | 1.09M | if (stroker->ctm_det_positive) { |
881 | 1.09M | face_dx = - slope_dy * stroker->half_line_width; |
882 | 1.09M | face_dy = slope_dx * stroker->half_line_width; |
883 | 1.09M | } else { |
884 | 0 | face_dx = slope_dy * stroker->half_line_width; |
885 | 0 | face_dy = - slope_dx * stroker->half_line_width; |
886 | 0 | } |
887 | | |
888 | | /* back to device space */ |
889 | 1.09M | cairo_matrix_transform_distance (stroker->ctm, &face_dx, &face_dy); |
890 | 1.09M | } else { |
891 | 0 | face_dx = - slope_dy * stroker->half_line_width; |
892 | 0 | face_dy = slope_dx * stroker->half_line_width; |
893 | 0 | } |
894 | | |
895 | 1.09M | offset_ccw.x = _cairo_fixed_from_double (face_dx); |
896 | 1.09M | offset_ccw.y = _cairo_fixed_from_double (face_dy); |
897 | 1.09M | offset_cw.x = -offset_ccw.x; |
898 | 1.09M | offset_cw.y = -offset_ccw.y; |
899 | | |
900 | 1.09M | face->ccw = *point; |
901 | 1.09M | translate_point (&face->ccw, &offset_ccw); |
902 | | |
903 | 1.09M | face->point = *point; |
904 | | |
905 | 1.09M | face->cw = *point; |
906 | 1.09M | translate_point (&face->cw, &offset_cw); |
907 | | |
908 | 1.09M | face->usr_vector.x = slope_dx; |
909 | 1.09M | face->usr_vector.y = slope_dy; |
910 | | |
911 | 1.09M | face->dev_vector = *dev_slope; |
912 | 1.09M | } |
913 | | |
914 | | static void |
915 | | add_caps (struct stroker *stroker) |
916 | 374k | { |
917 | | /* check for a degenerative sub_path */ |
918 | 374k | if (stroker->has_initial_sub_path && |
919 | 81.6k | ! stroker->has_first_face && |
920 | 1.20k | ! stroker->has_current_face && |
921 | 1.20k | stroker->style.line_cap == CAIRO_LINE_CAP_ROUND) |
922 | 0 | { |
923 | | /* pick an arbitrary slope to use */ |
924 | 0 | cairo_slope_t slope = { CAIRO_FIXED_ONE, 0 }; |
925 | 0 | cairo_stroke_face_t face; |
926 | | |
927 | | /* arbitrarily choose first_point */ |
928 | 0 | compute_face (&stroker->first_point, &slope, stroker, &face); |
929 | |
|
930 | 0 | add_leading_cap (stroker, &face, &stroker->ccw); |
931 | 0 | add_trailing_cap (stroker, &face, &stroker->ccw); |
932 | | |
933 | | /* ensure the circle is complete */ |
934 | 0 | _cairo_contour_add_point (&stroker->ccw.contour, |
935 | 0 | _cairo_contour_first_point (&stroker->ccw.contour)); |
936 | |
|
937 | 0 | _cairo_polygon_add_contour (stroker->polygon, &stroker->ccw.contour); |
938 | 0 | _cairo_contour_reset (&stroker->ccw.contour); |
939 | 374k | } else { |
940 | 374k | if (stroker->has_current_face) |
941 | 295k | add_trailing_cap (stroker, &stroker->current_face, &stroker->ccw); |
942 | | |
943 | | #if DEBUG |
944 | | { |
945 | | FILE *file = fopen ("contours.txt", "a"); |
946 | | _cairo_debug_print_contour (file, &stroker->path); |
947 | | _cairo_debug_print_contour (file, &stroker->cw.contour); |
948 | | _cairo_debug_print_contour (file, &stroker->ccw.contour); |
949 | | fclose (file); |
950 | | _cairo_contour_reset (&stroker->path); |
951 | | } |
952 | | #endif |
953 | | |
954 | 374k | _cairo_polygon_add_contour (stroker->polygon, &stroker->ccw.contour); |
955 | 374k | _cairo_contour_reset (&stroker->ccw.contour); |
956 | | |
957 | 374k | if (stroker->has_first_face) { |
958 | 295k | _cairo_contour_add_point (&stroker->ccw.contour, |
959 | 295k | &stroker->first_face.cw); |
960 | 295k | add_leading_cap (stroker, &stroker->first_face, &stroker->ccw); |
961 | | #if DEBUG |
962 | | { |
963 | | FILE *file = fopen ("contours.txt", "a"); |
964 | | _cairo_debug_print_contour (file, &stroker->ccw.contour); |
965 | | fclose (file); |
966 | | } |
967 | | #endif |
968 | | |
969 | 295k | _cairo_polygon_add_contour (stroker->polygon, |
970 | 295k | &stroker->ccw.contour); |
971 | 295k | _cairo_contour_reset (&stroker->ccw.contour); |
972 | 295k | } |
973 | | |
974 | 374k | _cairo_polygon_add_contour (stroker->polygon, &stroker->cw.contour); |
975 | 374k | _cairo_contour_reset (&stroker->cw.contour); |
976 | 374k | } |
977 | 374k | } |
978 | | |
979 | | static cairo_status_t |
980 | | close_path (void *closure); |
981 | | |
982 | | static cairo_status_t |
983 | | move_to (void *closure, |
984 | | const cairo_point_t *point) |
985 | 298k | { |
986 | 298k | struct stroker *stroker = closure; |
987 | | |
988 | | /* Cap the start and end of the previous sub path as needed */ |
989 | 298k | add_caps (stroker); |
990 | | |
991 | 298k | stroker->has_first_face = FALSE; |
992 | 298k | stroker->has_current_face = FALSE; |
993 | 298k | stroker->has_initial_sub_path = FALSE; |
994 | | |
995 | 298k | stroker->first_point = *point; |
996 | | |
997 | | #if DEBUG |
998 | | _cairo_contour_add_point (&stroker->path, point); |
999 | | #endif |
1000 | | |
1001 | 298k | stroker->current_face.point = *point; |
1002 | | |
1003 | 298k | return CAIRO_STATUS_SUCCESS; |
1004 | 298k | } |
1005 | | |
1006 | | static cairo_status_t |
1007 | | line_to (void *closure, |
1008 | | const cairo_point_t *point) |
1009 | 155k | { |
1010 | 155k | struct stroker *stroker = closure; |
1011 | 155k | cairo_stroke_face_t start; |
1012 | 155k | cairo_point_t *p1 = &stroker->current_face.point; |
1013 | 155k | cairo_slope_t dev_slope; |
1014 | | |
1015 | 155k | stroker->has_initial_sub_path = TRUE; |
1016 | | |
1017 | 155k | if (p1->x == point->x && p1->y == point->y) |
1018 | 1.20k | return CAIRO_STATUS_SUCCESS; |
1019 | | |
1020 | | #if DEBUG |
1021 | | _cairo_contour_add_point (&stroker->path, point); |
1022 | | #endif |
1023 | | |
1024 | 153k | _cairo_slope_init (&dev_slope, p1, point); |
1025 | 153k | compute_face (p1, &dev_slope, stroker, &start); |
1026 | | |
1027 | 153k | if (stroker->has_current_face) { |
1028 | 72.7k | int clockwise = _cairo_slope_compare (&stroker->current_face.dev_vector, |
1029 | 72.7k | &start.dev_vector); |
1030 | 72.7k | if (clockwise) { |
1031 | 72.7k | clockwise = clockwise < 0; |
1032 | | /* Join with final face from previous segment */ |
1033 | 72.7k | if (! within_tolerance (&stroker->current_face.ccw, &start.ccw, |
1034 | 72.7k | stroker->contour_tolerance) || |
1035 | 0 | ! within_tolerance (&stroker->current_face.cw, &start.cw, |
1036 | 0 | stroker->contour_tolerance)) |
1037 | 72.7k | { |
1038 | 72.7k | outer_join (stroker, &stroker->current_face, &start, clockwise); |
1039 | 72.7k | inner_join (stroker, &stroker->current_face, &start, clockwise); |
1040 | 72.7k | } |
1041 | 72.7k | } |
1042 | 81.1k | } else { |
1043 | 81.1k | if (! stroker->has_first_face) { |
1044 | | /* Save sub path's first face in case needed for closing join */ |
1045 | 81.1k | stroker->first_face = start; |
1046 | 81.1k | stroker->has_first_face = TRUE; |
1047 | 81.1k | } |
1048 | 81.1k | stroker->has_current_face = TRUE; |
1049 | | |
1050 | 81.1k | contour_add_point (stroker, &stroker->cw, &start.cw); |
1051 | 81.1k | contour_add_point (stroker, &stroker->ccw, &start.ccw); |
1052 | 81.1k | } |
1053 | | |
1054 | 153k | stroker->current_face = start; |
1055 | 153k | stroker->current_face.point = *point; |
1056 | 153k | stroker->current_face.ccw.x += dev_slope.dx; |
1057 | 153k | stroker->current_face.ccw.y += dev_slope.dy; |
1058 | 153k | stroker->current_face.cw.x += dev_slope.dx; |
1059 | 153k | stroker->current_face.cw.y += dev_slope.dy; |
1060 | | |
1061 | 153k | contour_add_point (stroker, &stroker->cw, &stroker->current_face.cw); |
1062 | 153k | contour_add_point (stroker, &stroker->ccw, &stroker->current_face.ccw); |
1063 | | |
1064 | 153k | return CAIRO_STATUS_SUCCESS; |
1065 | 155k | } |
1066 | | |
1067 | | static cairo_status_t |
1068 | | spline_to (void *closure, |
1069 | | const cairo_point_t *point, |
1070 | | const cairo_slope_t *tangent) |
1071 | 729k | { |
1072 | 729k | struct stroker *stroker = closure; |
1073 | 729k | cairo_stroke_face_t face; |
1074 | | |
1075 | | #if DEBUG |
1076 | | _cairo_contour_add_point (&stroker->path, point); |
1077 | | #endif |
1078 | 729k | if ((tangent->dx | tangent->dy) == 0) { |
1079 | 0 | struct stroke_contour *outer; |
1080 | 0 | cairo_point_t t; |
1081 | 0 | int clockwise; |
1082 | |
|
1083 | 0 | face = stroker->current_face; |
1084 | |
|
1085 | 0 | face.usr_vector.x = -face.usr_vector.x; |
1086 | 0 | face.usr_vector.y = -face.usr_vector.y; |
1087 | 0 | face.dev_vector.dx = -face.dev_vector.dx; |
1088 | 0 | face.dev_vector.dy = -face.dev_vector.dy; |
1089 | |
|
1090 | 0 | t = face.cw; |
1091 | 0 | face.cw = face.ccw; |
1092 | 0 | face.ccw = t; |
1093 | |
|
1094 | 0 | clockwise = join_is_clockwise (&stroker->current_face, &face); |
1095 | 0 | if (clockwise) { |
1096 | 0 | outer = &stroker->cw; |
1097 | 0 | } else { |
1098 | 0 | outer = &stroker->ccw; |
1099 | 0 | } |
1100 | |
|
1101 | 0 | add_fan (stroker, |
1102 | 0 | &stroker->current_face.dev_vector, |
1103 | 0 | &face.dev_vector, |
1104 | 0 | &stroker->current_face.point, |
1105 | 0 | clockwise, outer); |
1106 | 729k | } else { |
1107 | 729k | compute_face (point, tangent, stroker, &face); |
1108 | | |
1109 | 729k | if ((face.dev_slope.x * stroker->current_face.dev_slope.x + |
1110 | 729k | face.dev_slope.y * stroker->current_face.dev_slope.y) < stroker->spline_cusp_tolerance) |
1111 | 4 | { |
1112 | 4 | struct stroke_contour *outer; |
1113 | 4 | int clockwise = join_is_clockwise (&stroker->current_face, &face); |
1114 | | |
1115 | 4 | stroker->current_face.cw.x += face.point.x - stroker->current_face.point.x; |
1116 | 4 | stroker->current_face.cw.y += face.point.y - stroker->current_face.point.y; |
1117 | 4 | contour_add_point (stroker, &stroker->cw, &stroker->current_face.cw); |
1118 | | |
1119 | 4 | stroker->current_face.ccw.x += face.point.x - stroker->current_face.point.x; |
1120 | 4 | stroker->current_face.ccw.y += face.point.y - stroker->current_face.point.y; |
1121 | 4 | contour_add_point (stroker, &stroker->ccw, &stroker->current_face.ccw); |
1122 | | |
1123 | 4 | if (clockwise) { |
1124 | 2 | outer = &stroker->cw; |
1125 | 2 | } else { |
1126 | 2 | outer = &stroker->ccw; |
1127 | 2 | } |
1128 | 4 | add_fan (stroker, |
1129 | 4 | &stroker->current_face.dev_vector, |
1130 | 4 | &face.dev_vector, |
1131 | 4 | &stroker->current_face.point, |
1132 | 4 | clockwise, outer); |
1133 | 4 | } |
1134 | | |
1135 | 729k | contour_add_point (stroker, &stroker->cw, &face.cw); |
1136 | 729k | contour_add_point (stroker, &stroker->ccw, &face.ccw); |
1137 | 729k | } |
1138 | | |
1139 | 729k | stroker->current_face = face; |
1140 | | |
1141 | 729k | return CAIRO_STATUS_SUCCESS; |
1142 | 729k | } |
1143 | | |
1144 | | static cairo_status_t |
1145 | | curve_to (void *closure, |
1146 | | const cairo_point_t *b, |
1147 | | const cairo_point_t *c, |
1148 | | const cairo_point_t *d) |
1149 | 220k | { |
1150 | 220k | struct stroker *stroker = closure; |
1151 | 220k | cairo_spline_t spline; |
1152 | 220k | cairo_stroke_face_t face; |
1153 | | |
1154 | 220k | if (stroker->has_bounds && |
1155 | 220k | ! _cairo_spline_intersects (&stroker->current_face.point, b, c, d, |
1156 | 220k | &stroker->bounds)) |
1157 | 5.33k | return line_to (closure, d); |
1158 | | |
1159 | 215k | if (! _cairo_spline_init (&spline, spline_to, stroker, |
1160 | 215k | &stroker->current_face.point, b, c, d)) |
1161 | 0 | return line_to (closure, d); |
1162 | | |
1163 | 215k | compute_face (&stroker->current_face.point, &spline.initial_slope, |
1164 | 215k | stroker, &face); |
1165 | | |
1166 | 215k | if (stroker->has_current_face) { |
1167 | 0 | int clockwise = join_is_clockwise (&stroker->current_face, &face); |
1168 | | /* Join with final face from previous segment */ |
1169 | 0 | outer_join (stroker, &stroker->current_face, &face, clockwise); |
1170 | 0 | inner_join (stroker, &stroker->current_face, &face, clockwise); |
1171 | 215k | } else { |
1172 | 215k | if (! stroker->has_first_face) { |
1173 | | /* Save sub path's first face in case needed for closing join */ |
1174 | 215k | stroker->first_face = face; |
1175 | 215k | stroker->has_first_face = TRUE; |
1176 | 215k | } |
1177 | 215k | stroker->has_current_face = TRUE; |
1178 | | |
1179 | 215k | contour_add_point (stroker, &stroker->cw, &face.cw); |
1180 | 215k | contour_add_point (stroker, &stroker->ccw, &face.ccw); |
1181 | 215k | } |
1182 | 215k | stroker->current_face = face; |
1183 | | |
1184 | 215k | return _cairo_spline_decompose (&spline, stroker->tolerance); |
1185 | 215k | } |
1186 | | |
1187 | | static cairo_status_t |
1188 | | close_path (void *closure) |
1189 | 1.46k | { |
1190 | 1.46k | struct stroker *stroker = closure; |
1191 | 1.46k | cairo_status_t status; |
1192 | | |
1193 | 1.46k | status = line_to (stroker, &stroker->first_point); |
1194 | 1.46k | if (unlikely (status)) |
1195 | 0 | return status; |
1196 | | |
1197 | 1.46k | if (stroker->has_first_face && stroker->has_current_face) { |
1198 | | /* Join first and final faces of sub path */ |
1199 | 721 | outer_close (stroker, &stroker->current_face, &stroker->first_face); |
1200 | 721 | inner_close (stroker, &stroker->current_face, &stroker->first_face); |
1201 | | #if 0 |
1202 | | *_cairo_contour_first_point (&stroker->ccw.contour) = |
1203 | | *_cairo_contour_last_point (&stroker->ccw.contour); |
1204 | | |
1205 | | *_cairo_contour_first_point (&stroker->cw.contour) = |
1206 | | *_cairo_contour_last_point (&stroker->cw.contour); |
1207 | | #endif |
1208 | | |
1209 | 721 | _cairo_polygon_add_contour (stroker->polygon, &stroker->cw.contour); |
1210 | 721 | _cairo_polygon_add_contour (stroker->polygon, &stroker->ccw.contour); |
1211 | | |
1212 | | #if DEBUG |
1213 | | { |
1214 | | FILE *file = fopen ("contours.txt", "a"); |
1215 | | _cairo_debug_print_contour (file, &stroker->path); |
1216 | | _cairo_debug_print_contour (file, &stroker->cw.contour); |
1217 | | _cairo_debug_print_contour (file, &stroker->ccw.contour); |
1218 | | fclose (file); |
1219 | | |
1220 | | _cairo_contour_reset (&stroker->path); |
1221 | | } |
1222 | | #endif |
1223 | 721 | _cairo_contour_reset (&stroker->cw.contour); |
1224 | 721 | _cairo_contour_reset (&stroker->ccw.contour); |
1225 | 746 | } else { |
1226 | | /* Cap the start and end of the sub path as needed */ |
1227 | 746 | add_caps (stroker); |
1228 | 746 | } |
1229 | | |
1230 | 1.46k | stroker->has_initial_sub_path = FALSE; |
1231 | 1.46k | stroker->has_first_face = FALSE; |
1232 | 1.46k | stroker->has_current_face = FALSE; |
1233 | | |
1234 | 1.46k | return CAIRO_STATUS_SUCCESS; |
1235 | 1.46k | } |
1236 | | |
1237 | | cairo_status_t |
1238 | | _cairo_path_fixed_stroke_to_polygon (const cairo_path_fixed_t *path, |
1239 | | const cairo_stroke_style_t *style, |
1240 | | const cairo_matrix_t *ctm, |
1241 | | const cairo_matrix_t *ctm_inverse, |
1242 | | double tolerance, |
1243 | | cairo_polygon_t *polygon) |
1244 | 75.1k | { |
1245 | 75.1k | struct stroker stroker; |
1246 | 75.1k | cairo_status_t status; |
1247 | | |
1248 | 75.1k | if (style->num_dashes) { |
1249 | 0 | return _cairo_path_fixed_stroke_dashed_to_polygon (path, |
1250 | 0 | style, |
1251 | 0 | ctm, |
1252 | 0 | ctm_inverse, |
1253 | 0 | tolerance, |
1254 | 0 | polygon); |
1255 | 0 | } |
1256 | | |
1257 | 75.1k | stroker.has_bounds = polygon->num_limits; |
1258 | 75.1k | if (stroker.has_bounds) { |
1259 | | /* Extend the bounds in each direction to account for the maximum area |
1260 | | * we might generate trapezoids, to capture line segments that are |
1261 | | * outside of the bounds but which might generate rendering that's |
1262 | | * within bounds. |
1263 | | */ |
1264 | 75.1k | double dx, dy; |
1265 | 75.1k | cairo_fixed_t fdx, fdy; |
1266 | 75.1k | int i; |
1267 | | |
1268 | 75.1k | stroker.bounds = polygon->limits[0]; |
1269 | 75.1k | for (i = 1; i < polygon->num_limits; i++) |
1270 | 0 | _cairo_box_add_box (&stroker.bounds, &polygon->limits[i]); |
1271 | | |
1272 | 75.1k | _cairo_stroke_style_max_distance_from_path (style, path, ctm, &dx, &dy); |
1273 | 75.1k | fdx = _cairo_fixed_from_double (dx); |
1274 | 75.1k | fdy = _cairo_fixed_from_double (dy); |
1275 | | |
1276 | 75.1k | stroker.bounds.p1.x -= fdx; |
1277 | 75.1k | stroker.bounds.p2.x += fdx; |
1278 | 75.1k | stroker.bounds.p1.y -= fdy; |
1279 | 75.1k | stroker.bounds.p2.y += fdy; |
1280 | 75.1k | } |
1281 | | |
1282 | 75.1k | stroker.style = *style; |
1283 | 75.1k | stroker.ctm = ctm; |
1284 | 75.1k | stroker.ctm_inverse = ctm_inverse; |
1285 | 75.1k | stroker.tolerance = tolerance; |
1286 | 75.1k | stroker.half_line_width = style->line_width / 2.; |
1287 | | |
1288 | | /* If `CAIRO_LINE_JOIN_ROUND` is selected and a joint's `arc height` |
1289 | | * is greater than `tolerance` then two segments are joined with |
1290 | | * round-join, otherwise bevel-join is used. |
1291 | | * |
1292 | | * (See https://gitlab.freedesktop.org/cairo/cairo/-/merge_requests/372#note_1698225 |
1293 | | * for an illustration.) |
1294 | | * |
1295 | | * `Arc height` is the distance from the center of arc's chord to |
1296 | | * the center of the arc. It is also the difference of arc's radius |
1297 | | * and the "distance from a point where segments are joined to the |
1298 | | * chord" (distance to the chord). Arc's radius is the half of a line |
1299 | | * width and the "distance to the chord" is equal to "half of a line width" |
1300 | | * times `cos(half the angle between segment vectors)`. So |
1301 | | * |
1302 | | * arc_height = w/2 - w/2 * cos(phi/2), |
1303 | | * |
1304 | | * where `w/2` is the "half of a line width". |
1305 | | * |
1306 | | * Using the double angle cosine formula we can express the `cos(phi/2)` |
1307 | | * with just `cos(phi)` which is also the dot product of segments' |
1308 | | * unit vectors. |
1309 | | * |
1310 | | * cos(phi/2) = sqrt ( (1 + cos(phi)) / 2 ); |
1311 | | * cos(phi/2) is in [0; 1] range, cannot be negative; |
1312 | | * |
1313 | | * cos(phi) = a . b = (ax * bx + ay * by), |
1314 | | * |
1315 | | * where `a` and `b` are unit vectors of the segments to be joined. |
1316 | | * |
1317 | | * Since `arc height` should be greater than the `tolerance` to produce |
1318 | | * a round-join we can write |
1319 | | * |
1320 | | * w/2 * (1 - cos(phi/2)) > tolerance; |
1321 | | * 1 - tolerance / (w/2) > cos(phi/2); [!] |
1322 | | * |
1323 | | * which can be rewritten with the above double angle formula to |
1324 | | * |
1325 | | * cos(phi) < 2 * ( 1 - tolerance / (w/2) )^2 - 1, |
1326 | | * |
1327 | | * [!] Note that `w/2` is in [tolerance; +inf] range, since `cos(phi/2)` |
1328 | | * cannot be negative. The left part of the above inequality is the |
1329 | | * dot product and the right part is the `spline_cusp_tolerance`: |
1330 | | * |
1331 | | * (ax * bx + ay * by) < spline_cusp_tolerance. |
1332 | | * |
1333 | | * In the code below only the `spline_cusp_tolerance` is calculated. |
1334 | | * The dot product is calculated later, in the condition expression |
1335 | | * itself. "Half of a line width" must be scaled with CTM for tolerance |
1336 | | * condition to be properly met. Also, since `arch height` cannot exceed |
1337 | | * the "half of a line width" and since `cos(phi/2)` cannot be negative, |
1338 | | * when `tolerance` is greater than the "half of a line width" the |
1339 | | * bevel-join should be produced. |
1340 | | */ |
1341 | 75.1k | double scaled_hlw = hypot(stroker.half_line_width * ctm->xx, |
1342 | 75.1k | stroker.half_line_width * ctm->yx); |
1343 | | |
1344 | 75.1k | if (scaled_hlw <= tolerance) { |
1345 | 0 | stroker.spline_cusp_tolerance = -1.0; |
1346 | 75.1k | } else { |
1347 | 75.1k | stroker.spline_cusp_tolerance = 1 - tolerance / scaled_hlw; |
1348 | 75.1k | stroker.spline_cusp_tolerance *= stroker.spline_cusp_tolerance; |
1349 | 75.1k | stroker.spline_cusp_tolerance *= 2; |
1350 | 75.1k | stroker.spline_cusp_tolerance -= 1; |
1351 | 75.1k | } |
1352 | | |
1353 | 75.1k | stroker.ctm_det_positive = |
1354 | 75.1k | _cairo_matrix_compute_determinant (ctm) >= 0.0; |
1355 | | |
1356 | 75.1k | stroker.pen.num_vertices = 0; |
1357 | 75.1k | if (path->has_curve_to || |
1358 | 1.85k | style->line_join == CAIRO_LINE_JOIN_ROUND || |
1359 | 73.3k | style->line_cap == CAIRO_LINE_CAP_ROUND) { |
1360 | 73.3k | status = _cairo_pen_init (&stroker.pen, |
1361 | 73.3k | stroker.half_line_width, |
1362 | 73.3k | tolerance, ctm); |
1363 | 73.3k | if (unlikely (status)) |
1364 | 0 | return status; |
1365 | | |
1366 | | /* If the line width is so small that the pen is reduced to a |
1367 | | single point, then we have nothing to do. */ |
1368 | 73.3k | if (stroker.pen.num_vertices <= 1) |
1369 | 0 | return CAIRO_STATUS_SUCCESS; |
1370 | 73.3k | } |
1371 | | |
1372 | 75.1k | stroker.has_current_face = FALSE; |
1373 | 75.1k | stroker.has_first_face = FALSE; |
1374 | 75.1k | stroker.has_initial_sub_path = FALSE; |
1375 | | |
1376 | | #if DEBUG |
1377 | | remove ("contours.txt"); |
1378 | | remove ("polygons.txt"); |
1379 | | _cairo_contour_init (&stroker.path, 0); |
1380 | | #endif |
1381 | 75.1k | _cairo_contour_init (&stroker.cw.contour, 1); |
1382 | 75.1k | _cairo_contour_init (&stroker.ccw.contour, -1); |
1383 | 75.1k | tolerance *= CAIRO_FIXED_ONE; |
1384 | 75.1k | tolerance *= tolerance; |
1385 | 75.1k | stroker.contour_tolerance = tolerance; |
1386 | 75.1k | stroker.polygon = polygon; |
1387 | | |
1388 | 75.1k | status = _cairo_path_fixed_interpret (path, |
1389 | 75.1k | move_to, |
1390 | 75.1k | line_to, |
1391 | 75.1k | curve_to, |
1392 | 75.1k | close_path, |
1393 | 75.1k | &stroker); |
1394 | | /* Cap the start and end of the final sub path as needed */ |
1395 | 75.1k | if (likely (status == CAIRO_STATUS_SUCCESS)) |
1396 | 75.1k | add_caps (&stroker); |
1397 | | |
1398 | 75.1k | _cairo_contour_fini (&stroker.cw.contour); |
1399 | 75.1k | _cairo_contour_fini (&stroker.ccw.contour); |
1400 | 75.1k | if (stroker.pen.num_vertices) |
1401 | 73.3k | _cairo_pen_fini (&stroker.pen); |
1402 | | |
1403 | | #if DEBUG |
1404 | | { |
1405 | | FILE *file = fopen ("polygons.txt", "a"); |
1406 | | _cairo_debug_print_polygon (file, polygon); |
1407 | | fclose (file); |
1408 | | } |
1409 | | #endif |
1410 | | |
1411 | 75.1k | return status; |
1412 | 75.1k | } |