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
4 * Copyright © 2002 University of Southern California
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.
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
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/
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.
29 * The Original Code is the cairo graphics library.
31 * The Initial Developer of the Original Code is University of Southern
35 * Carl D. Worth <cworth@cworth.org>
36 * Chris Wilson <chris@chris-wilson.co.uk>
39 #define _BSD_SOURCE /* for hypot() */
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"
50 typedef struct cairo_stroker
{
51 cairo_stroke_style_t style
;
53 const cairo_matrix_t
*ctm
;
54 const cairo_matrix_t
*ctm_inverse
;
55 double half_line_width
;
57 double spline_cusp_tolerance
;
58 double ctm_determinant
;
59 cairo_bool_t ctm_det_positive
;
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
,
71 cairo_status_t (*add_convex_quad
) (void *closure
,
72 const cairo_point_t quad
[4]);
76 cairo_point_t current_point
;
77 cairo_point_t first_point
;
79 cairo_bool_t has_initial_sub_path
;
81 cairo_bool_t has_current_face
;
82 cairo_stroke_face_t current_face
;
84 cairo_bool_t has_first_face
;
85 cairo_stroke_face_t first_face
;
87 cairo_stroker_dash_t dash
;
89 cairo_bool_t has_bounds
;
94 _cairo_stroker_limit (cairo_stroker_t
*stroker
,
95 const cairo_path_fixed_t
*path
,
96 const cairo_box_t
*boxes
,
100 cairo_fixed_t fdx
, fdy
;
102 stroker
->has_bounds
= TRUE
;
103 _cairo_boxes_get_extents (boxes
, num_boxes
, &stroker
->bounds
);
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.
110 _cairo_stroke_style_max_distance_from_path (&stroker
->style
, path
,
111 stroker
->ctm
, &dx
, &dy
);
113 fdx
= _cairo_fixed_from_double (dx
);
114 fdy
= _cairo_fixed_from_double (dy
);
116 stroker
->bounds
.p1
.x
-= fdx
;
117 stroker
->bounds
.p2
.x
+= fdx
;
119 stroker
->bounds
.p1
.y
-= fdy
;
120 stroker
->bounds
.p2
.y
+= fdy
;
123 static cairo_status_t
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
,
130 const cairo_box_t
*limits
,
133 cairo_status_t status
;
135 stroker
->style
= *stroke_style
;
137 stroker
->ctm_inverse
= ctm_inverse
;
138 stroker
->tolerance
= tolerance
;
139 stroker
->half_line_width
= stroke_style
->line_width
/ 2.0;
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
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;
153 stroker
->ctm_determinant
= _cairo_matrix_compute_determinant (stroker
->ctm
);
154 stroker
->ctm_det_positive
= stroker
->ctm_determinant
>= 0.0;
156 status
= _cairo_pen_init (&stroker
->pen
,
157 stroker
->half_line_width
, tolerance
, ctm
);
158 if (unlikely (status
))
161 stroker
->has_current_face
= FALSE
;
162 stroker
->has_first_face
= FALSE
;
163 stroker
->has_initial_sub_path
= FALSE
;
165 _cairo_stroker_dash_init (&stroker
->dash
, stroke_style
);
167 stroker
->add_external_edge
= NULL
;
169 stroker
->has_bounds
= FALSE
;
171 _cairo_stroker_limit (stroker
, path
, limits
, num_limits
);
173 return CAIRO_STATUS_SUCCESS
;
177 _cairo_stroker_fini (cairo_stroker_t
*stroker
)
179 _cairo_pen_fini (&stroker
->pen
);
183 _translate_point (cairo_point_t
*point
, const cairo_point_t
*offset
)
185 point
->x
+= offset
->x
;
186 point
->y
+= offset
->y
;
190 _cairo_stroker_join_is_clockwise (const cairo_stroke_face_t
*in
,
191 const cairo_stroke_face_t
*out
)
193 cairo_slope_t in_slope
, out_slope
;
195 _cairo_slope_init (&in_slope
, &in
->point
, &in
->cw
);
196 _cairo_slope_init (&out_slope
, &out
->point
, &out
->cw
);
198 return _cairo_slope_compare (&in_slope
, &out_slope
) < 0;
202 * _cairo_slope_compare_sgn:
204 * Return -1, 0 or 1 depending on the relative slopes of
208 _cairo_slope_compare_sgn (double dx1
, double dy1
, double dx2
, double dy2
)
210 double c
= (dx1
* dy2
- dx2
* dy1
);
213 if (c
< 0) return -1;
218 _range_step (int i
, int step
, int max
)
229 * Construct a fan around the midpoint using the vertices from pen between
232 static cairo_status_t
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
)
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
;
246 if (stroker
->has_bounds
&&
247 ! _cairo_box_contains_point (&stroker
->bounds
, midpt
))
250 assert (stroker
->pen
.num_vertices
);
253 _cairo_pen_find_active_ccw_vertices (pen
,
254 in_vector
, out_vector
,
256 if (stroker
->add_external_edge
) {
259 while (start
!= stop
) {
260 cairo_point_t p
= *midpt
;
261 _translate_point (&p
, &pen
->vertices
[start
].point
);
263 status
= stroker
->add_external_edge (stroker
->closure
,
265 if (unlikely (status
))
270 start
+= pen
->num_vertices
;
272 status
= stroker
->add_external_edge (stroker
->closure
,
278 num_points
= stop
- start
;
280 num_points
+= pen
->num_vertices
;
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
);
290 while (start
!= stop
) {
291 points
[num_points
] = *midpt
;
292 _translate_point (&points
[num_points
], &pen
->vertices
[start
].point
);
296 start
+= pen
->num_vertices
;
298 points
[num_points
++] = *outpt
;
301 _cairo_pen_find_active_cw_vertices (pen
,
302 in_vector
, out_vector
,
304 if (stroker
->add_external_edge
) {
307 while (start
!= stop
) {
308 cairo_point_t p
= *midpt
;
309 _translate_point (&p
, &pen
->vertices
[start
].point
);
311 status
= stroker
->add_external_edge (stroker
->closure
,
313 if (unlikely (status
))
317 if (++start
== pen
->num_vertices
)
320 status
= stroker
->add_external_edge (stroker
->closure
,
326 num_points
= stop
- start
;
328 num_points
+= pen
->num_vertices
;
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
);
338 while (start
!= stop
) {
339 points
[num_points
] = *midpt
;
340 _translate_point (&points
[num_points
], &pen
->vertices
[start
].point
);
343 if (++start
== pen
->num_vertices
)
346 points
[num_points
++] = *outpt
;
351 status
= stroker
->add_triangle_fan (stroker
->closure
,
352 midpt
, points
, num_points
);
355 if (points
!= stack_points
)
361 /* Ensure a leak free connection... */
362 if (stroker
->add_external_edge
!= NULL
) {
364 return stroker
->add_external_edge (stroker
->closure
, inpt
, outpt
);
366 return stroker
->add_external_edge (stroker
->closure
, outpt
, inpt
);
368 stack_points
[0] = *midpt
;
369 stack_points
[1] = *inpt
;
370 stack_points
[2] = *outpt
;
371 return stroker
->add_triangle (stroker
->closure
, stack_points
);
375 static cairo_status_t
376 _cairo_stroker_join (cairo_stroker_t
*stroker
,
377 const cairo_stroke_face_t
*in
,
378 const cairo_stroke_face_t
*out
)
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
;
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
)
388 return CAIRO_STATUS_SUCCESS
;
392 if (stroker
->add_external_edge
!= NULL
) {
393 status
= stroker
->add_external_edge (stroker
->closure
,
394 &out
->cw
, &in
->point
);
395 if (unlikely (status
))
398 status
= stroker
->add_external_edge (stroker
->closure
,
399 &in
->point
, &in
->cw
);
400 if (unlikely (status
))
407 if (stroker
->add_external_edge
!= NULL
) {
408 status
= stroker
->add_external_edge (stroker
->closure
,
409 &in
->ccw
, &in
->point
);
410 if (unlikely (status
))
413 status
= stroker
->add_external_edge (stroker
->closure
,
414 &in
->point
, &out
->ccw
);
415 if (unlikely (status
))
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
,
429 &in
->point
, inpt
, outpt
,
432 case CAIRO_LINE_JOIN_MITER
:
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
;
439 /* Check the miter limit -- lines meeting at an acute angle
440 * can generate long miters, the limit converts them to bevel
442 * Consider the miter join formed when two line segments
443 * meet at an angle psi:
450 * We can zoom in on the right half of that to see:
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
473 * 1 /sin (psi/2) = miter_length / line_width
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:
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))
487 * in · out = |in| |out| cos (psi)
489 * in and out are both unit vectors, so:
491 * in · out = cos (psi)
493 * 2 <= ml² (1 - in · out)
496 if (2 <= ml
* ml
* (1 - in_dot_out
)) {
497 double x1
, y1
, x2
, y2
;
499 double dx1
, dx2
, dy1
, dy2
;
501 double fdx1
, fdy1
, fdx2
, fdy2
;
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
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
);
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
);
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.
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
;
537 mx
= (my
- y2
) * dx2
/ dy2
+ x2
;
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.
548 ix
= _cairo_fixed_to_double (in
->point
.x
);
549 iy
= _cairo_fixed_to_double (in
->point
.y
);
551 /* slope of one face */
552 fdx1
= x1
- ix
; fdy1
= y1
- iy
;
554 /* slope of the other face */
555 fdx2
= x2
- ix
; fdy2
= y2
- iy
;
557 /* slope from the intersection to the miter point */
558 mdx
= mx
- ix
; mdy
= my
- iy
;
561 * Make sure the miter point line lies between the two
562 * faces by comparing the slopes
564 if (_cairo_slope_compare_sgn (fdx1
, fdy1
, mdx
, mdy
) !=
565 _cairo_slope_compare_sgn (fdx2
, fdy2
, mdx
, mdy
))
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
);
572 status
= stroker
->add_external_edge (stroker
->closure
,
574 if (unlikely (status
))
577 status
= stroker
->add_external_edge (stroker
->closure
,
579 if (unlikely (status
))
582 status
= stroker
->add_external_edge (stroker
->closure
,
584 if (unlikely (status
))
587 status
= stroker
->add_external_edge (stroker
->closure
,
589 if (unlikely (status
))
593 return CAIRO_STATUS_SUCCESS
;
595 points
[0] = in
->point
;
597 points
[2].x
= _cairo_fixed_from_double (mx
);
598 points
[2].y
= _cairo_fixed_from_double (my
);
601 return stroker
->add_convex_quad (stroker
->closure
, points
);
607 /* fall through ... */
609 case CAIRO_LINE_JOIN_BEVEL
:
610 if (stroker
->add_external_edge
!= NULL
) {
612 return stroker
->add_external_edge (stroker
->closure
,
615 return stroker
->add_external_edge (stroker
->closure
,
619 points
[0] = in
->point
;
623 return stroker
->add_triangle (stroker
->closure
, points
);
628 static cairo_status_t
629 _cairo_stroker_add_cap (cairo_stroker_t
*stroker
,
630 const cairo_stroke_face_t
*f
)
632 switch (stroker
->style
.line_cap
) {
633 case CAIRO_LINE_CAP_ROUND
: {
636 slope
.dx
= -f
->dev_vector
.dx
;
637 slope
.dy
= -f
->dev_vector
.dy
;
639 return _tessellate_fan (stroker
,
642 &f
->point
, &f
->cw
, &f
->ccw
,
647 case CAIRO_LINE_CAP_SQUARE
: {
649 cairo_slope_t fvector
;
650 cairo_point_t quad
[4];
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
);
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
;
667 if (stroker
->add_external_edge
!= NULL
) {
668 cairo_status_t status
;
670 status
= stroker
->add_external_edge (stroker
->closure
,
672 if (unlikely (status
))
675 status
= stroker
->add_external_edge (stroker
->closure
,
677 if (unlikely (status
))
680 status
= stroker
->add_external_edge (stroker
->closure
,
682 if (unlikely (status
))
685 return CAIRO_STATUS_SUCCESS
;
687 return stroker
->add_convex_quad (stroker
->closure
, quad
);
691 case CAIRO_LINE_CAP_BUTT
:
693 if (stroker
->add_external_edge
!= NULL
) {
694 return stroker
->add_external_edge (stroker
->closure
,
697 return CAIRO_STATUS_SUCCESS
;
702 static cairo_status_t
703 _cairo_stroker_add_leading_cap (cairo_stroker_t
*stroker
,
704 const cairo_stroke_face_t
*face
)
706 cairo_stroke_face_t reversed
;
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
;
717 reversed
.cw
= reversed
.ccw
;
720 return _cairo_stroker_add_cap (stroker
, &reversed
);
723 static cairo_status_t
724 _cairo_stroker_add_trailing_cap (cairo_stroker_t
*stroker
,
725 const cairo_stroke_face_t
*face
)
727 return _cairo_stroker_add_cap (stroker
, face
);
730 static inline cairo_bool_t
731 _compute_normalized_device_slope (double *dx
, double *dy
,
732 const cairo_matrix_t
*ctm_inverse
,
735 double dx0
= *dx
, dy0
= *dy
;
738 cairo_matrix_transform_distance (ctm_inverse
, &dx0
, &dy0
);
740 if (dx0
== 0.0 && dy0
== 0.0) {
755 } else if (dy0
== 0.0) {
765 mag
= hypot (dx0
, dy0
);
777 _compute_face (const cairo_point_t
*point
,
778 const cairo_slope_t
*dev_slope
,
781 cairo_stroker_t
*stroker
,
782 cairo_stroke_face_t
*face
)
784 double face_dx
, face_dy
;
785 cairo_point_t offset_ccw
, offset_cw
;
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.
794 if (stroker
->ctm_det_positive
)
796 face_dx
= - slope_dy
* stroker
->half_line_width
;
797 face_dy
= slope_dx
* stroker
->half_line_width
;
801 face_dx
= slope_dy
* stroker
->half_line_width
;
802 face_dy
= - slope_dx
* stroker
->half_line_width
;
805 /* back to device space */
806 cairo_matrix_transform_distance (stroker
->ctm
, &face_dx
, &face_dy
);
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
;
814 _translate_point (&face
->ccw
, &offset_ccw
);
816 face
->point
= *point
;
819 _translate_point (&face
->cw
, &offset_cw
);
821 face
->usr_vector
.x
= slope_dx
;
822 face
->usr_vector
.y
= slope_dy
;
824 face
->dev_vector
= *dev_slope
;
827 static cairo_status_t
828 _cairo_stroker_add_caps (cairo_stroker_t
*stroker
)
830 cairo_status_t status
;
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
)
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
;
843 _compute_normalized_device_slope (&dx
, &dy
,
844 stroker
->ctm_inverse
, NULL
);
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
);
850 status
= _cairo_stroker_add_leading_cap (stroker
, &face
);
851 if (unlikely (status
))
854 status
= _cairo_stroker_add_trailing_cap (stroker
, &face
);
855 if (unlikely (status
))
859 if (stroker
->has_first_face
) {
860 status
= _cairo_stroker_add_leading_cap (stroker
,
861 &stroker
->first_face
);
862 if (unlikely (status
))
866 if (stroker
->has_current_face
) {
867 status
= _cairo_stroker_add_trailing_cap (stroker
,
868 &stroker
->current_face
);
869 if (unlikely (status
))
873 return CAIRO_STATUS_SUCCESS
;
876 static cairo_status_t
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
)
885 _compute_face (p1
, dev_slope
, slope_dx
, slope_dy
, stroker
, start
);
888 if (p1
->x
== p2
->x
&& p1
->y
== p2
->y
)
889 return CAIRO_STATUS_SUCCESS
;
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
;
897 if (stroker
->add_external_edge
!= NULL
) {
898 cairo_status_t status
;
900 status
= stroker
->add_external_edge (stroker
->closure
,
901 &end
->cw
, &start
->cw
);
902 if (unlikely (status
))
905 status
= stroker
->add_external_edge (stroker
->closure
,
906 &start
->ccw
, &end
->ccw
);
907 if (unlikely (status
))
910 return CAIRO_STATUS_SUCCESS
;
912 cairo_point_t quad
[4];
917 quad
[3] = start
->ccw
;
919 return stroker
->add_convex_quad (stroker
->closure
, quad
);
923 static cairo_status_t
924 _cairo_stroker_move_to (void *closure
,
925 const cairo_point_t
*point
)
927 cairo_stroker_t
*stroker
= closure
;
928 cairo_status_t status
;
930 /* reset the dash pattern for new sub paths */
931 _cairo_stroker_dash_start (&stroker
->dash
);
933 /* Cap the start and end of the previous sub path as needed */
934 status
= _cairo_stroker_add_caps (stroker
);
935 if (unlikely (status
))
938 stroker
->first_point
= *point
;
939 stroker
->current_point
= *point
;
941 stroker
->has_first_face
= FALSE
;
942 stroker
->has_current_face
= FALSE
;
943 stroker
->has_initial_sub_path
= FALSE
;
945 return CAIRO_STATUS_SUCCESS
;
948 static cairo_status_t
949 _cairo_stroker_line_to (void *closure
,
950 const cairo_point_t
*point
)
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
;
959 stroker
->has_initial_sub_path
= TRUE
;
961 if (p1
->x
== point
->x
&& p1
->y
== point
->y
)
962 return CAIRO_STATUS_SUCCESS
;
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
);
970 status
= _cairo_stroker_add_sub_edge (stroker
,
975 if (unlikely (status
))
978 if (stroker
->has_current_face
) {
979 /* Join with final face from previous segment */
980 status
= _cairo_stroker_join (stroker
,
981 &stroker
->current_face
,
983 if (unlikely (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
;
990 stroker
->current_face
= end
;
991 stroker
->has_current_face
= TRUE
;
993 stroker
->current_point
= *point
;
995 return CAIRO_STATUS_SUCCESS
;
998 static cairo_status_t
999 _cairo_stroker_spline_to (void *closure
,
1000 const cairo_point_t
*point
,
1001 const cairo_slope_t
*tangent
)
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
;
1009 stroker
->has_initial_sub_path
= TRUE
;
1011 if (stroker
->current_point
.x
== point
->x
&&
1012 stroker
->current_point
.y
== point
->y
)
1013 return CAIRO_STATUS_SUCCESS
;
1015 slope_dx
= _cairo_fixed_to_double (tangent
->dx
);
1016 slope_dy
= _cairo_fixed_to_double (tangent
->dy
);
1018 if (! _compute_normalized_device_slope (&slope_dx
, &slope_dy
,
1019 stroker
->ctm_inverse
, NULL
))
1020 return CAIRO_STATUS_SUCCESS
;
1022 _compute_face (point
, tangent
,
1024 stroker
, &new_face
);
1026 assert (stroker
->has_current_face
);
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
) {
1031 const cairo_point_t
*inpt
, *outpt
;
1032 int clockwise
= _cairo_stroker_join_is_clockwise (&new_face
,
1033 &stroker
->current_face
);
1036 inpt
= &stroker
->current_face
.cw
;
1037 outpt
= &new_face
.cw
;
1039 inpt
= &stroker
->current_face
.ccw
;
1040 outpt
= &new_face
.ccw
;
1043 _tessellate_fan (stroker
,
1044 &stroker
->current_face
.dev_vector
,
1045 &new_face
.dev_vector
,
1046 &stroker
->current_face
.point
,
1051 if (_slow_segment_intersection (&stroker
->current_face
.cw
,
1052 &stroker
->current_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
);
1061 points
[0] = stroker
->current_face
.cw
;
1062 points
[1] = new_face
.cw
;
1063 stroker
->add_triangle (stroker
->closure
, points
);
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
);
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
);
1076 stroker
->current_face
= new_face
;
1077 stroker
->has_current_face
= TRUE
;
1078 stroker
->current_point
= *point
;
1080 return CAIRO_STATUS_SUCCESS
;
1084 * Dashed lines. Cap each dash end, join around turns when on
1086 static cairo_status_t
1087 _cairo_stroker_line_to_dashed (void *closure
,
1088 const cairo_point_t
*p2
)
1090 cairo_stroker_t
*stroker
= closure
;
1091 double mag
, remain
, step_length
= 0;
1092 double slope_dx
, slope_dy
;
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
;
1101 stroker
->has_initial_sub_path
= stroker
->dash
.dash_starts_on
;
1103 if (p1
->x
== p2
->x
&& p1
->y
== p2
->y
)
1104 return CAIRO_STATUS_SUCCESS
;
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
)))
1111 fully_in_bounds
= FALSE
;
1114 _cairo_slope_init (&dev_slope
, p1
, p2
);
1116 slope_dx
= _cairo_fixed_to_double (p2
->x
- p1
->x
);
1117 slope_dy
= _cairo_fixed_to_double (p2
->y
- p1
->y
);
1119 if (! _compute_normalized_device_slope (&slope_dx
, &slope_dy
,
1120 stroker
->ctm_inverse
, &mag
))
1122 return CAIRO_STATUS_SUCCESS
;
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
;
1136 if (stroker
->dash
.dash_on
&&
1138 (! stroker
->has_first_face
&& stroker
->dash
.dash_starts_on
) ||
1139 _cairo_box_intersects_line_segment (&stroker
->bounds
, &segment
)))
1141 status
= _cairo_stroker_add_sub_edge (stroker
,
1142 &segment
.p1
, &segment
.p2
,
1145 &sub_start
, &sub_end
);
1146 if (unlikely (status
))
1149 if (stroker
->has_current_face
)
1151 /* Join with final face from previous segment */
1152 status
= _cairo_stroker_join (stroker
,
1153 &stroker
->current_face
,
1155 if (unlikely (status
))
1158 stroker
->has_current_face
= FALSE
;
1160 else if (! stroker
->has_first_face
&&
1161 stroker
->dash
.dash_starts_on
)
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
;
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
))
1176 /* Cap dash end if not at end of segment */
1177 status
= _cairo_stroker_add_trailing_cap (stroker
, &sub_end
);
1178 if (unlikely (status
))
1181 stroker
->current_face
= sub_end
;
1182 stroker
->has_current_face
= TRUE
;
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
))
1192 stroker
->has_current_face
= FALSE
;
1196 _cairo_stroker_dash_step (&stroker
->dash
, step_length
);
1197 segment
.p1
= segment
.p2
;
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.
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
1208 * On the other hand, Acroread 7 also produces the degenerate caps.
1210 _compute_face (p2
, &dev_slope
,
1213 &stroker
->current_face
);
1215 status
= _cairo_stroker_add_leading_cap (stroker
,
1216 &stroker
->current_face
);
1217 if (unlikely (status
))
1220 stroker
->has_current_face
= TRUE
;
1223 stroker
->current_point
= *p2
;
1225 return CAIRO_STATUS_SUCCESS
;
1228 static cairo_status_t
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
)
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
;
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
;
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
;
1252 if (! _cairo_spline_init (&spline
,
1255 &stroker
->current_point
, b
, c
, d
))
1257 cairo_slope_t fallback_slope
;
1258 _cairo_slope_init (&fallback_slope
, &stroker
->current_point
, d
);
1259 return line_to (closure
, d
, &fallback_slope
);
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
;
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
))
1274 _compute_face (&stroker
->current_point
,
1275 &spline
.initial_slope
,
1279 if (stroker
->has_current_face
) {
1280 status
= _cairo_stroker_join (stroker
,
1281 &stroker
->current_face
, &face
);
1282 if (unlikely (status
))
1284 } else if (! stroker
->has_first_face
) {
1285 stroker
->first_face
= face
;
1286 stroker
->has_first_face
= TRUE
;
1289 stroker
->current_face
= face
;
1290 stroker
->has_current_face
= TRUE
;
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
;
1298 status
= _cairo_spline_decompose (&spline
, stroker
->tolerance
);
1299 if (unlikely (status
))
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
))
1309 _compute_face (&stroker
->current_point
,
1310 &spline
.final_slope
,
1315 status
= _cairo_stroker_join (stroker
, &stroker
->current_face
, &face
);
1316 if (unlikely (status
))
1319 stroker
->current_face
= face
;
1322 stroker
->style
.line_join
= line_join_save
;
1324 return CAIRO_STATUS_SUCCESS
;
1327 static cairo_status_t
1328 _cairo_stroker_close_path (void *closure
)
1330 cairo_stroker_t
*stroker
= closure
;
1331 cairo_status_t status
;
1333 if (stroker
->dash
.dashed
)
1334 status
= _cairo_stroker_line_to_dashed (stroker
, &stroker
->first_point
);
1336 status
= _cairo_stroker_line_to (stroker
, &stroker
->first_point
);
1337 if (unlikely (status
))
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
))
1348 /* Cap the start and end of the sub path as needed */
1349 status
= _cairo_stroker_add_caps (stroker
);
1350 if (unlikely (status
))
1354 stroker
->has_initial_sub_path
= FALSE
;
1355 stroker
->has_first_face
= FALSE
;
1356 stroker
->has_current_face
= FALSE
;
1358 return CAIRO_STATUS_SUCCESS
;
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
,
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
,
1373 cairo_status_t (*add_convex_quad
) (void *closure
,
1374 const cairo_point_t quad
[4]),
1377 cairo_stroker_t stroker
;
1378 cairo_status_t status
;
1380 status
= _cairo_stroker_init (&stroker
, path
, stroke_style
,
1381 ctm
, ctm_inverse
, tolerance
,
1383 if (unlikely (status
))
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
;
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
,
1400 if (unlikely (status
))
1403 /* Cap the start and end of the final sub path as needed */
1404 status
= _cairo_stroker_add_caps (&stroker
);
1407 _cairo_stroker_fini (&stroker
);
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
,
1418 cairo_polygon_t
*polygon
)
1420 cairo_stroker_t stroker
;
1421 cairo_status_t status
;
1423 status
= _cairo_stroker_init (&stroker
, path
, stroke_style
,
1424 ctm
, ctm_inverse
, tolerance
,
1425 polygon
->limits
, polygon
->num_limits
);
1426 if (unlikely (status
))
1429 stroker
.add_external_edge
= _cairo_polygon_add_external_edge
,
1430 stroker
.closure
= polygon
;
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
,
1441 if (unlikely (status
))
1444 /* Cap the start and end of the final sub path as needed */
1445 status
= _cairo_stroker_add_caps (&stroker
);
1448 _cairo_stroker_fini (&stroker
);
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
,
1459 cairo_traps_t
*traps
)
1461 cairo_int_status_t status
;
1462 cairo_polygon_t polygon
;
1464 _cairo_polygon_init (&polygon
, traps
->limits
, traps
->num_limits
);
1465 status
= _cairo_path_fixed_stroke_to_polygon (path
,
1471 if (unlikely (status
))
1474 status
= _cairo_polygon_status (&polygon
);
1475 if (unlikely (status
))
1478 status
= _cairo_bentley_ottmann_tessellate_polygon (traps
, &polygon
,
1479 CAIRO_FILL_RULE_WINDING
);
1482 _cairo_polygon_fini (&polygon
);