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