Rev 1892 | Go to most recent revision | Show entire file | Regard whitespace | Details | Blame | Last modification | View Log | RSS feed
Rev 1892 | Rev 3959 | ||
---|---|---|---|
Line 37... | Line 37... | ||
37 | */ |
37 | */ |
Line 38... | Line 38... | ||
38 | 38 | ||
39 | #define _BSD_SOURCE /* for hypot() */ |
39 | #define _BSD_SOURCE /* for hypot() */ |
Line -... | Line 40... | ||
- | 40 | #include "cairoint.h" |
|
40 | #include "cairoint.h" |
41 | |
41 | 42 | #include "cairo-box-inline.h" |
|
42 | #include "cairo-boxes-private.h" |
43 | #include "cairo-boxes-private.h" |
43 | #include "cairo-error-private.h" |
44 | #include "cairo-error-private.h" |
44 | #include "cairo-path-fixed-private.h" |
- | |
45 | #include "cairo-slope-private.h" |
45 | #include "cairo-path-fixed-private.h" |
46 | - | ||
47 | typedef struct _cairo_stroker_dash { |
- | |
48 | cairo_bool_t dashed; |
- | |
49 | unsigned int dash_index; |
- | |
50 | cairo_bool_t dash_on; |
- | |
51 | cairo_bool_t dash_starts_on; |
- | |
52 | double dash_remain; |
- | |
53 | - | ||
54 | double dash_offset; |
- | |
55 | const double *dashes; |
46 | #include "cairo-slope-private.h" |
Line 56... | Line 47... | ||
56 | unsigned int num_dashes; |
47 | #include "cairo-stroke-dash-private.h" |
57 | } cairo_stroker_dash_t; |
48 | #include "cairo-traps-private.h" |
Line 58... | Line 49... | ||
58 | 49 | ||
59 | typedef struct cairo_stroker { |
50 | typedef struct cairo_stroker { |
- | 51 | cairo_stroke_style_t style; |
|
60 | cairo_stroke_style_t style; |
52 | |
- | 53 | const cairo_matrix_t *ctm; |
|
61 | 54 | const cairo_matrix_t *ctm_inverse; |
|
62 | const cairo_matrix_t *ctm; |
55 | double half_line_width; |
Line 63... | Line 56... | ||
63 | const cairo_matrix_t *ctm_inverse; |
56 | double tolerance; |
64 | double tolerance; |
57 | double spline_cusp_tolerance; |
Line 96... | Line 89... | ||
96 | cairo_bool_t has_bounds; |
89 | cairo_bool_t has_bounds; |
97 | cairo_box_t bounds; |
90 | cairo_box_t bounds; |
98 | } cairo_stroker_t; |
91 | } cairo_stroker_t; |
Line 99... | Line 92... | ||
99 | 92 | ||
100 | static void |
93 | static void |
- | 94 | _cairo_stroker_limit (cairo_stroker_t *stroker, |
|
- | 95 | const cairo_path_fixed_t *path, |
|
- | 96 | const cairo_box_t *boxes, |
|
101 | _cairo_stroker_dash_start (cairo_stroker_dash_t *dash) |
97 | int num_boxes) |
102 | { |
98 | { |
103 | double offset; |
99 | double dx, dy; |
104 | cairo_bool_t on = TRUE; |
- | |
105 | unsigned int i = 0; |
- | |
106 | - | ||
107 | if (! dash->dashed) |
- | |
108 | return; |
- | |
109 | - | ||
110 | offset = dash->dash_offset; |
- | |
111 | - | ||
112 | /* We stop searching for a starting point as soon as the |
- | |
113 | offset reaches zero. Otherwise when an initial dash |
- | |
114 | segment shrinks to zero it will be skipped over. */ |
- | |
115 | while (offset > 0.0 && offset >= dash->dashes[i]) { |
- | |
116 | offset -= dash->dashes[i]; |
- | |
117 | on = !on; |
- | |
118 | if (++i == dash->num_dashes) |
- | |
119 | i = 0; |
- | |
Line 120... | Line 100... | ||
120 | } |
100 | cairo_fixed_t fdx, fdy; |
121 | - | ||
122 | dash->dash_index = i; |
101 | |
123 | dash->dash_on = dash->dash_starts_on = on; |
- | |
Line 124... | Line -... | ||
124 | dash->dash_remain = dash->dashes[i] - offset; |
- | |
125 | } |
102 | stroker->has_bounds = TRUE; |
126 | - | ||
127 | static void |
- | |
128 | _cairo_stroker_dash_step (cairo_stroker_dash_t *dash, double step) |
103 | _cairo_boxes_get_extents (boxes, num_boxes, &stroker->bounds); |
129 | { |
104 | |
130 | dash->dash_remain -= step; |
105 | /* Extend the bounds in each direction to account for the maximum area |
Line 131... | Line 106... | ||
131 | if (dash->dash_remain <= 0.) { |
106 | * we might generate trapezoids, to capture line segments that are outside |
132 | if (++dash->dash_index == dash->num_dashes) |
107 | * of the bounds but which might generate rendering that's within bounds. |
133 | dash->dash_index = 0; |
- | |
134 | - | ||
Line 135... | Line -... | ||
135 | dash->dash_on = ! dash->dash_on; |
- | |
136 | dash->dash_remain = dash->dashes[dash->dash_index]; |
- | |
137 | } |
108 | */ |
138 | } |
- | |
139 | 109 | ||
140 | static void |
- | |
141 | _cairo_stroker_dash_init (cairo_stroker_dash_t *dash, |
- | |
142 | const cairo_stroke_style_t *style) |
- | |
143 | { |
- | |
144 | dash->dashed = style->dash != NULL; |
- | |
145 | if (! dash->dashed) |
- | |
Line -... | Line 110... | ||
- | 110 | _cairo_stroke_style_max_distance_from_path (&stroker->style, path, |
|
- | 111 | stroker->ctm, &dx, &dy); |
|
- | 112 | ||
146 | return; |
113 | fdx = _cairo_fixed_from_double (dx); |
- | 114 | fdy = _cairo_fixed_from_double (dy); |
|
147 | 115 | ||
Line 148... | Line 116... | ||
148 | dash->dashes = style->dash; |
116 | stroker->bounds.p1.x -= fdx; |
149 | dash->num_dashes = style->num_dashes; |
117 | stroker->bounds.p2.x += fdx; |
- | 118 | ||
150 | dash->dash_offset = style->dash_offset; |
119 | stroker->bounds.p1.y -= fdy; |
151 | 120 | stroker->bounds.p2.y += fdy; |
|
152 | _cairo_stroker_dash_start (dash); |
121 | } |
153 | } |
122 | |
- | 123 | static cairo_status_t |
|
- | 124 | _cairo_stroker_init (cairo_stroker_t *stroker, |
|
154 | 125 | const cairo_path_fixed_t *path, |
|
155 | static cairo_status_t |
126 | const cairo_stroke_style_t *stroke_style, |
Line 156... | Line 127... | ||
156 | _cairo_stroker_init (cairo_stroker_t *stroker, |
127 | const cairo_matrix_t *ctm, |
157 | const cairo_stroke_style_t *stroke_style, |
128 | const cairo_matrix_t *ctm_inverse, |
158 | const cairo_matrix_t *ctm, |
129 | double tolerance, |
159 | const cairo_matrix_t *ctm_inverse, |
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 |
|
Line 160... | Line 144... | ||
160 | double tolerance) |
144 | * (half-line-width times the cosine of the bisection angle) and the |
161 | { |
145 | * half-line-width itself is greater than tolerance then we need to |
Line 162... | Line 146... | ||
162 | cairo_status_t status; |
146 | * inject a point. |
163 | 147 | */ |
|
164 | stroker->style = *stroke_style; |
- | |
165 | stroker->ctm = ctm; |
148 | stroker->spline_cusp_tolerance = 1 - tolerance / stroker->half_line_width; |
166 | stroker->ctm_inverse = ctm_inverse; |
149 | stroker->spline_cusp_tolerance *= stroker->spline_cusp_tolerance; |
Line 167... | Line -... | ||
167 | stroker->tolerance = tolerance; |
- | |
168 | - | ||
169 | stroker->ctm_determinant = _cairo_matrix_compute_determinant (stroker->ctm); |
150 | stroker->spline_cusp_tolerance *= 2; |
170 | stroker->ctm_det_positive = stroker->ctm_determinant >= 0.0; |
151 | stroker->spline_cusp_tolerance -= 1; |
171 | 152 | ||
Line 172... | Line 153... | ||
172 | status = _cairo_pen_init (&stroker->pen, |
153 | stroker->ctm_determinant = _cairo_matrix_compute_determinant (stroker->ctm); |
Line 173... | Line 154... | ||
173 | stroke_style->line_width / 2.0, |
154 | stroker->ctm_det_positive = stroker->ctm_determinant >= 0.0; |
Line 174... | Line -... | ||
174 | tolerance, ctm); |
- | |
175 | if (unlikely (status)) |
- | |
176 | return status; |
- | |
177 | - | ||
178 | stroker->has_bounds = FALSE; |
- | |
179 | - | ||
180 | stroker->has_current_face = FALSE; |
- | |
181 | stroker->has_first_face = FALSE; |
- | |
182 | stroker->has_initial_sub_path = FALSE; |
- | |
183 | - | ||
184 | _cairo_stroker_dash_init (&stroker->dash, stroke_style); |
- | |
185 | 155 | ||
186 | stroker->add_external_edge = NULL; |
- | |
187 | - | ||
188 | return CAIRO_STATUS_SUCCESS; |
- | |
189 | } |
- | |
190 | - | ||
191 | static void |
156 | status = _cairo_pen_init (&stroker->pen, |
192 | _cairo_stroker_limit (cairo_stroker_t *stroker, |
- | |
193 | const cairo_box_t *boxes, |
157 | stroker->half_line_width, tolerance, ctm); |
194 | int num_boxes) |
- | |
195 | { |
- | |
196 | double dx, dy; |
- | |
197 | cairo_fixed_t fdx, fdy; |
- | |
198 | - | ||
199 | stroker->has_bounds = TRUE; |
- | |
200 | _cairo_boxes_get_extents (boxes, num_boxes, &stroker->bounds); |
- | |
Line 201... | Line -... | ||
201 | - | ||
202 | /* Extend the bounds in each direction to account for the maximum area |
158 | if (unlikely (status)) |
203 | * we might generate trapezoids, to capture line segments that are outside |
159 | return status; |
Line 204... | Line 160... | ||
204 | * of the bounds but which might generate rendering that's within bounds. |
160 | |
205 | */ |
161 | stroker->has_current_face = FALSE; |
206 | 162 | stroker->has_first_face = FALSE; |
|
Line 241... | Line 197... | ||
241 | 197 | ||
242 | return _cairo_slope_compare (&in_slope, &out_slope) < 0; |
198 | return _cairo_slope_compare (&in_slope, &out_slope) < 0; |
Line 243... | Line 199... | ||
243 | } |
199 | } |
244 | 200 | ||
245 | /** |
201 | /** |
246 | * _cairo_slope_compare_sgn |
202 | * _cairo_slope_compare_sgn: |
247 | * |
203 | * |
248 | * Return -1, 0 or 1 depending on the relative slopes of |
204 | * Return -1, 0 or 1 depending on the relative slopes of |
249 | * two lines. |
205 | * two lines. |
250 | */ |
206 | **/ |
251 | static int |
207 | static int |
252 | _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) |
Line 281... | Line 237... | ||
281 | const cairo_point_t *inpt, |
237 | const cairo_point_t *inpt, |
282 | const cairo_point_t *outpt, |
238 | const cairo_point_t *outpt, |
283 | cairo_bool_t clockwise) |
239 | cairo_bool_t clockwise) |
284 | { |
240 | { |
285 | cairo_point_t stack_points[64], *points = stack_points; |
241 | cairo_point_t stack_points[64], *points = stack_points; |
- | 242 | cairo_pen_t *pen = &stroker->pen; |
|
286 | int start, stop, step, i, npoints; |
243 | int start, stop, num_points = 0; |
287 | cairo_status_t status; |
244 | cairo_status_t status; |
Line -... | Line 245... | ||
- | 245 | ||
- | 246 | if (stroker->has_bounds && |
|
- | 247 | ! _cairo_box_contains_point (&stroker->bounds, midpt)) |
|
- | 248 | goto BEVEL; |
|
- | 249 | ||
- | 250 | assert (stroker->pen.num_vertices); |
|
288 | 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) { |
|
289 | if (clockwise) { |
257 | cairo_point_t last; |
- | 258 | last = *inpt; |
|
- | 259 | while (start != stop) { |
|
- | 260 | cairo_point_t p = *midpt; |
|
Line 290... | Line 261... | ||
290 | step = -1; |
261 | _translate_point (&p, &pen->vertices[start].point); |
291 | 262 | ||
292 | start = _cairo_pen_find_active_ccw_vertex_index (&stroker->pen, |
- | |
293 | in_vector); |
263 | status = stroker->add_external_edge (stroker->closure, |
294 | if (_cairo_slope_compare (&stroker->pen.vertices[start].slope_ccw, |
- | |
295 | in_vector) < 0) |
- | |
296 | start = _range_step (start, -1, stroker->pen.num_vertices); |
- | |
297 | 264 | &last, &p); |
|
298 | stop = _cairo_pen_find_active_ccw_vertex_index (&stroker->pen, |
- | |
299 | out_vector); |
- | |
300 | if (_cairo_slope_compare (&stroker->pen.vertices[stop].slope_cw, |
- | |
301 | out_vector) > 0) |
- | |
302 | { |
- | |
303 | stop = _range_step (stop, 1, stroker->pen.num_vertices); |
- | |
304 | if (_cairo_slope_compare (&stroker->pen.vertices[stop].slope_ccw, |
- | |
305 | in_vector) < 0) |
265 | if (unlikely (status)) |
306 | { |
- | |
307 | goto BEVEL; |
- | |
Line 308... | Line 266... | ||
308 | } |
266 | return status; |
- | 267 | last = p; |
|
- | 268 | ||
- | 269 | if (start-- == 0) |
|
- | 270 | start += pen->num_vertices; |
|
309 | } |
271 | } |
310 | - | ||
311 | npoints = start - stop; |
- | |
312 | } else { |
- | |
313 | step = 1; |
- | |
314 | - | ||
315 | start = _cairo_pen_find_active_cw_vertex_index (&stroker->pen, |
272 | status = stroker->add_external_edge (stroker->closure, |
316 | in_vector); |
- | |
317 | if (_cairo_slope_compare (&stroker->pen.vertices[start].slope_cw, |
- | |
318 | in_vector) < 0) |
- | |
319 | start = _range_step (start, 1, stroker->pen.num_vertices); |
- | |
320 | - | ||
321 | stop = _cairo_pen_find_active_cw_vertex_index (&stroker->pen, |
- | |
322 | out_vector); |
- | |
323 | if (_cairo_slope_compare (&stroker->pen.vertices[stop].slope_ccw, |
- | |
324 | out_vector) > 0) |
- | |
325 | { |
- | |
326 | stop = _range_step (stop, -1, stroker->pen.num_vertices); |
- | |
327 | if (_cairo_slope_compare (&stroker->pen.vertices[stop].slope_cw, |
273 | &last, outpt); |
328 | in_vector) < 0) |
- | |
329 | { |
- | |
Line 330... | Line 274... | ||
330 | goto BEVEL; |
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; |
|
331 | } |
282 | if (num_points > ARRAY_LENGTH(stack_points)) { |
332 | } |
- | |
Line -... | Line 283... | ||
- | 283 | points = _cairo_malloc_ab (num_points, sizeof (cairo_point_t)); |
|
333 | 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) { |
|
334 | npoints = stop - start; |
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 | } |
|
335 | } |
300 | } else { |
- | 301 | _cairo_pen_find_active_cw_vertices (pen, |
|
- | 302 | in_vector, out_vector, |
|
- | 303 | &start, &stop); |
|
- | 304 | if (stroker->add_external_edge) { |
|
Line -... | Line 305... | ||
- | 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 | ||
336 | stop = _range_step (stop, step, stroker->pen.num_vertices); |
317 | if (++start == pen->num_vertices) |
337 | 318 | start = 0; |
|
Line -... | Line 319... | ||
- | 319 | } |
|
- | 320 | status = stroker->add_external_edge (stroker->closure, |
|
- | 321 | outpt, &last); |
|
- | 322 | } else { |
|
338 | if (npoints < 0) |
323 | if (start == stop) |
339 | npoints += stroker->pen.num_vertices; |
324 | goto BEVEL; |
340 | npoints += 3; |
325 | |
341 | 326 | num_points = stop - start; |
|
342 | if (npoints <= 1) |
327 | if (num_points < 0) |
Line -... | Line 328... | ||
- | 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); |
|
Line 343... | Line 334... | ||
343 | goto BEVEL; |
334 | } |
344 | 335 | ||
345 | if (npoints > ARRAY_LENGTH (stack_points)) { |
- | |
346 | points = _cairo_malloc_ab (npoints, sizeof (cairo_point_t)); |
- | |
347 | if (unlikely (points == NULL)) |
- | |
348 | return _cairo_error (CAIRO_STATUS_NO_MEMORY); |
- | |
349 | } |
- | |
350 | - | ||
351 | - | ||
352 | /* Construct the fan. */ |
- | |
353 | npoints = 0; |
336 | points[0] = *inpt; |
354 | points[npoints++] = *inpt; |
337 | num_points = 1; |
355 | for (i = start; |
- | |
356 | i != stop; |
- | |
357 | i = _range_step (i, step, stroker->pen.num_vertices)) |
- | |
358 | { |
- | |
359 | points[npoints] = *midpt; |
- | |
360 | _translate_point (&points[npoints], &stroker->pen.vertices[i].point); |
- | |
361 | npoints++; |
- | |
362 | } |
- | |
363 | points[npoints++] = *outpt; |
- | |
364 | 338 | while (start != stop) { |
|
365 | if (stroker->add_external_edge != NULL) { |
- | |
366 | for (i = 0; i < npoints - 1; i++) { |
- | |
367 | if (clockwise) { |
339 | points[num_points] = *midpt; |
- | 340 | _translate_point (&points[num_points], &pen->vertices[start].point); |
|
368 | status = stroker->add_external_edge (stroker->closure, |
341 | num_points++; |
369 | &points[i], &points[i+1]); |
342 | |
370 | } else { |
343 | if (++start == pen->num_vertices) |
371 | status = stroker->add_external_edge (stroker->closure, |
344 | start = 0; |
Line 372... | Line 345... | ||
372 | &points[i+1], &points[i]); |
345 | } |
373 | } |
346 | points[num_points++] = *outpt; |
Line 676... | Line 649... | ||
676 | cairo_slope_t fvector; |
649 | cairo_slope_t fvector; |
677 | cairo_point_t quad[4]; |
650 | cairo_point_t quad[4]; |
Line 678... | Line 651... | ||
678 | 651 | ||
679 | dx = f->usr_vector.x; |
652 | dx = f->usr_vector.x; |
680 | dy = f->usr_vector.y; |
653 | dy = f->usr_vector.y; |
681 | dx *= stroker->style.line_width / 2.0; |
654 | dx *= stroker->half_line_width; |
682 | dy *= stroker->style.line_width / 2.0; |
655 | dy *= stroker->half_line_width; |
683 | cairo_matrix_transform_distance (stroker->ctm, &dx, &dy); |
656 | cairo_matrix_transform_distance (stroker->ctm, &dx, &dy); |
684 | fvector.dx = _cairo_fixed_from_double (dx); |
657 | fvector.dx = _cairo_fixed_from_double (dx); |
Line 685... | Line 658... | ||
685 | fvector.dy = _cairo_fixed_from_double (dy); |
658 | fvector.dy = _cairo_fixed_from_double (dy); |
Line 799... | Line 772... | ||
799 | 772 | ||
800 | return TRUE; |
773 | return TRUE; |
Line 801... | Line 774... | ||
801 | } |
774 | } |
802 | 775 | ||
- | 776 | static void |
|
803 | static void |
777 | _compute_face (const cairo_point_t *point, |
- | 778 | const cairo_slope_t *dev_slope, |
|
- | 779 | double slope_dx, |
|
804 | _compute_face (const cairo_point_t *point, cairo_slope_t *dev_slope, |
780 | double slope_dy, |
805 | double slope_dx, double slope_dy, |
781 | cairo_stroker_t *stroker, |
806 | cairo_stroker_t *stroker, cairo_stroke_face_t *face) |
782 | cairo_stroke_face_t *face) |
807 | { |
783 | { |
Line 808... | Line 784... | ||
808 | double face_dx, face_dy; |
784 | double face_dx, face_dy; |
Line 815... | Line 791... | ||
815 | * whether the ctm reflects or not, and that can be determined |
791 | * whether the ctm reflects or not, and that can be determined |
816 | * by looking at the determinant of the matrix. |
792 | * by looking at the determinant of the matrix. |
817 | */ |
793 | */ |
818 | if (stroker->ctm_det_positive) |
794 | if (stroker->ctm_det_positive) |
819 | { |
795 | { |
820 | face_dx = - slope_dy * (stroker->style.line_width / 2.0); |
796 | face_dx = - slope_dy * stroker->half_line_width; |
821 | face_dy = slope_dx * (stroker->style.line_width / 2.0); |
797 | face_dy = slope_dx * stroker->half_line_width; |
822 | } |
798 | } |
823 | else |
799 | else |
824 | { |
800 | { |
825 | face_dx = slope_dy * (stroker->style.line_width / 2.0); |
801 | face_dx = slope_dy * stroker->half_line_width; |
826 | face_dy = - slope_dx * (stroker->style.line_width / 2.0); |
802 | face_dy = - slope_dx * stroker->half_line_width; |
827 | } |
803 | } |
Line 828... | Line 804... | ||
828 | 804 | ||
829 | /* back to device space */ |
805 | /* back to device space */ |
Line 855... | Line 831... | ||
855 | 831 | ||
856 | /* check for a degenerative sub_path */ |
832 | /* check for a degenerative sub_path */ |
857 | if (stroker->has_initial_sub_path |
833 | if (stroker->has_initial_sub_path |
858 | && ! stroker->has_first_face |
834 | && ! stroker->has_first_face |
859 | && ! stroker->has_current_face |
835 | && ! stroker->has_current_face |
860 | && stroker->style.line_cap == CAIRO_LINE_JOIN_ROUND) |
836 | && stroker->style.line_cap == CAIRO_LINE_CAP_ROUND) |
861 | { |
837 | { |
862 | /* pick an arbitrary slope to use */ |
838 | /* pick an arbitrary slope to use */ |
863 | double dx = 1.0, dy = 0.0; |
839 | double dx = 1.0, dy = 0.0; |
864 | cairo_slope_t slope = { CAIRO_FIXED_ONE, 0 }; |
840 | cairo_slope_t slope = { CAIRO_FIXED_ONE, 0 }; |
Line 1017... | Line 993... | ||
1017 | stroker->current_point = *point; |
993 | stroker->current_point = *point; |
Line 1018... | Line 994... | ||
1018 | 994 | ||
1019 | return CAIRO_STATUS_SUCCESS; |
995 | return CAIRO_STATUS_SUCCESS; |
Line -... | Line 996... | ||
- | 996 | } |
|
- | 997 | ||
- | 998 | static cairo_status_t |
|
- | 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; |
|
1020 | } |
1081 | } |
1021 | 1082 | ||
1022 | /* |
1083 | /* |
1023 | * Dashed lines. Cap each dash end, join around turns when on |
1084 | * Dashed lines. Cap each dash end, join around turns when on |
1024 | */ |
1085 | */ |
Line 1173... | Line 1234... | ||
1173 | cairo_stroker_t *stroker = closure; |
1234 | cairo_stroker_t *stroker = closure; |
1174 | cairo_spline_t spline; |
1235 | cairo_spline_t spline; |
1175 | cairo_line_join_t line_join_save; |
1236 | cairo_line_join_t line_join_save; |
1176 | cairo_stroke_face_t face; |
1237 | cairo_stroke_face_t face; |
1177 | double slope_dx, slope_dy; |
1238 | double slope_dx, slope_dy; |
1178 | cairo_path_fixed_line_to_func_t *line_to; |
1239 | cairo_spline_add_point_func_t line_to; |
- | 1240 | cairo_spline_add_point_func_t spline_to; |
|
1179 | cairo_status_t status = CAIRO_STATUS_SUCCESS; |
1241 | cairo_status_t status = CAIRO_STATUS_SUCCESS; |
Line 1180... | Line 1242... | ||
1180 | 1242 | ||
1181 | line_to = stroker->dash.dashed ? |
1243 | line_to = stroker->dash.dashed ? |
1182 | _cairo_stroker_line_to_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 : |
|
Line 1183... | Line 1250... | ||
1183 | _cairo_stroker_line_to; |
1250 | (cairo_spline_add_point_func_t) _cairo_stroker_spline_to; |
- | 1251 | ||
1184 | 1252 | if (! _cairo_spline_init (&spline, |
|
1185 | if (! _cairo_spline_init (&spline, |
1253 | spline_to, |
1186 | line_to, stroker, |
1254 | stroker, |
- | 1255 | &stroker->current_point, b, c, d)) |
|
- | 1256 | { |
|
1187 | &stroker->current_point, b, c, d)) |
1257 | cairo_slope_t fallback_slope; |
1188 | { |
1258 | _cairo_slope_init (&fallback_slope, &stroker->current_point, d); |
Line 1189... | Line 1259... | ||
1189 | return line_to (closure, d); |
1259 | return line_to (closure, d, &fallback_slope); |
1190 | } |
1260 | } |
1191 | 1261 | ||
Line 1305... | Line 1375... | ||
1305 | void *closure) |
1375 | void *closure) |
1306 | { |
1376 | { |
1307 | cairo_stroker_t stroker; |
1377 | cairo_stroker_t stroker; |
1308 | cairo_status_t status; |
1378 | cairo_status_t status; |
Line 1309... | Line 1379... | ||
1309 | 1379 | ||
1310 | status = _cairo_stroker_init (&stroker, stroke_style, |
1380 | status = _cairo_stroker_init (&stroker, path, stroke_style, |
- | 1381 | ctm, ctm_inverse, tolerance, |
|
1311 | ctm, ctm_inverse, tolerance); |
1382 | NULL, 0); |
1312 | if (unlikely (status)) |
1383 | if (unlikely (status)) |
Line 1313... | Line 1384... | ||
1313 | return status; |
1384 | return status; |
1314 | 1385 | ||
1315 | stroker.add_triangle = add_triangle; |
1386 | stroker.add_triangle = add_triangle; |
1316 | stroker.add_triangle_fan = add_triangle_fan; |
1387 | stroker.add_triangle_fan = add_triangle_fan; |
Line 1317... | Line 1388... | ||
1317 | stroker.add_convex_quad = add_convex_quad; |
1388 | stroker.add_convex_quad = add_convex_quad; |
1318 | stroker.closure = closure; |
- | |
1319 | 1389 | stroker.closure = closure; |
|
1320 | status = _cairo_path_fixed_interpret (path, |
1390 | |
1321 | CAIRO_DIRECTION_FORWARD, |
1391 | status = _cairo_path_fixed_interpret (path, |
1322 | _cairo_stroker_move_to, |
1392 | _cairo_stroker_move_to, |
1323 | stroker.dash.dashed ? |
1393 | stroker.dash.dashed ? |
Line 1338... | Line 1408... | ||
1338 | 1408 | ||
1339 | return status; |
1409 | return status; |
Line 1340... | Line 1410... | ||
1340 | } |
1410 | } |
1341 | 1411 | ||
1342 | cairo_status_t |
1412 | cairo_status_t |
1343 | _cairo_path_fixed_stroke_to_polygon (const cairo_path_fixed_t *path, |
1413 | _cairo_path_fixed_stroke_dashed_to_polygon (const cairo_path_fixed_t *path, |
1344 | const cairo_stroke_style_t *stroke_style, |
1414 | const cairo_stroke_style_t *stroke_style, |
1345 | const cairo_matrix_t *ctm, |
1415 | const cairo_matrix_t *ctm, |
1346 | const cairo_matrix_t *ctm_inverse, |
1416 | const cairo_matrix_t *ctm_inverse, |
1347 | double tolerance, |
1417 | double tolerance, |
1348 | cairo_polygon_t *polygon) |
1418 | cairo_polygon_t *polygon) |
1349 | { |
1419 | { |
Line 1350... | Line 1420... | ||
1350 | cairo_stroker_t stroker; |
1420 | cairo_stroker_t stroker; |
1351 | cairo_status_t status; |
1421 | cairo_status_t status; |
- | 1422 | ||
1352 | 1423 | status = _cairo_stroker_init (&stroker, path, stroke_style, |
|
1353 | status = _cairo_stroker_init (&stroker, stroke_style, |
1424 | ctm, ctm_inverse, tolerance, |
Line 1354... | Line 1425... | ||
1354 | ctm, ctm_inverse, tolerance); |
1425 | polygon->limits, polygon->num_limits); |
1355 | if (unlikely (status)) |
1426 | if (unlikely (status)) |
Line 1356... | Line -... | ||
1356 | return status; |
- | |
1357 | - | ||
1358 | stroker.add_external_edge = _cairo_polygon_add_external_edge, |
- | |
1359 | stroker.closure = polygon; |
1427 | return status; |
1360 | - | ||
1361 | if (polygon->num_limits) |
1428 | |
1362 | _cairo_stroker_limit (&stroker, polygon->limits, polygon->num_limits); |
1429 | stroker.add_external_edge = _cairo_polygon_add_external_edge, |
1363 | 1430 | stroker.closure = polygon; |
|
1364 | status = _cairo_path_fixed_interpret (path, |
1431 | |
1365 | CAIRO_DIRECTION_FORWARD, |
1432 | status = _cairo_path_fixed_interpret (path, |
Line 1381... | Line 1448... | ||
1381 | _cairo_stroker_fini (&stroker); |
1448 | _cairo_stroker_fini (&stroker); |
Line 1382... | Line 1449... | ||
1382 | 1449 | ||
1383 | return status; |
1450 | return status; |
Line 1384... | Line 1451... | ||
1384 | } |
1451 | } |
1385 | 1452 | ||
1386 | cairo_status_t |
1453 | cairo_int_status_t |
1387 | _cairo_path_fixed_stroke_to_traps (const cairo_path_fixed_t *path, |
1454 | _cairo_path_fixed_stroke_polygon_to_traps (const cairo_path_fixed_t *path, |
1388 | const cairo_stroke_style_t *stroke_style, |
1455 | const cairo_stroke_style_t *stroke_style, |
1389 | const cairo_matrix_t *ctm, |
1456 | const cairo_matrix_t *ctm, |
1390 | const cairo_matrix_t *ctm_inverse, |
1457 | const cairo_matrix_t *ctm_inverse, |
1391 | double tolerance, |
1458 | double tolerance, |
1392 | cairo_traps_t *traps) |
1459 | cairo_traps_t *traps) |
1393 | { |
1460 | { |
Line 1394... | Line -... | ||
1394 | cairo_status_t status; |
- | |
1395 | cairo_polygon_t polygon; |
- | |
1396 | - | ||
1397 | /* Before we do anything else, we attempt the rectilinear |
- | |
1398 | * stroker. It's careful to generate trapezoids that align to |
- | |
1399 | * device-pixel boundaries when possible. Many backends can render |
- | |
1400 | * those much faster than non-aligned trapezoids, (by using clip |
- | |
1401 | * regions, etc.) */ |
- | |
1402 | if (path->is_rectilinear) { |
- | |
1403 | status = _cairo_path_fixed_stroke_rectilinear_to_traps (path, |
- | |
1404 | stroke_style, |
- | |
1405 | ctm, |
- | |
1406 | traps); |
- | |
1407 | if (status != CAIRO_INT_STATUS_UNSUPPORTED) |
- | |
1408 | return status; |
- | |
1409 | } |
- | |
1410 | 1461 | cairo_int_status_t status; |
|
1411 | _cairo_polygon_init (&polygon); |
- | |
1412 | if (traps->num_limits) |
1462 | cairo_polygon_t polygon; |
1413 | _cairo_polygon_limit (&polygon, traps->limits, traps->num_limits); |
1463 | |
1414 | 1464 | _cairo_polygon_init (&polygon, traps->limits, traps->num_limits); |
|
1415 | status = _cairo_path_fixed_stroke_to_polygon (path, |
1465 | status = _cairo_path_fixed_stroke_to_polygon (path, |
1416 | stroke_style, |
1466 | stroke_style, |
Line 1431... | Line 1481... | ||
1431 | BAIL: |
1481 | BAIL: |
1432 | _cairo_polygon_fini (&polygon); |
1482 | _cairo_polygon_fini (&polygon); |
Line 1433... | Line 1483... | ||
1433 | 1483 | ||
1434 | return status; |
1484 | return status; |
1435 | } |
- | |
1436 | - | ||
1437 | typedef struct _segment_t { |
- | |
1438 | cairo_point_t p1, p2; |
- | |
1439 | cairo_bool_t is_horizontal; |
- | |
1440 | cairo_bool_t has_join; |
- | |
1441 | } segment_t; |
- | |
1442 | - | ||
1443 | typedef struct _cairo_rectilinear_stroker { |
- | |
1444 | const cairo_stroke_style_t *stroke_style; |
- | |
1445 | const cairo_matrix_t *ctm; |
- | |
1446 | - | ||
1447 | cairo_fixed_t half_line_width; |
- | |
1448 | cairo_bool_t do_traps; |
- | |
1449 | void *container; |
- | |
1450 | cairo_point_t current_point; |
- | |
1451 | cairo_point_t first_point; |
- | |
1452 | cairo_bool_t open_sub_path; |
- | |
1453 | - | ||
1454 | cairo_stroker_dash_t dash; |
- | |
1455 | - | ||
1456 | cairo_bool_t has_bounds; |
- | |
1457 | cairo_box_t bounds; |
- | |
1458 | - | ||
1459 | int num_segments; |
- | |
1460 | int segments_size; |
- | |
1461 | segment_t *segments; |
- | |
1462 | segment_t segments_embedded[8]; /* common case is a single rectangle */ |
- | |
1463 | } cairo_rectilinear_stroker_t; |
- | |
1464 | - | ||
1465 | static void |
- | |
1466 | _cairo_rectilinear_stroker_limit (cairo_rectilinear_stroker_t *stroker, |
- | |
1467 | const cairo_box_t *boxes, |
- | |
1468 | int num_boxes) |
- | |
1469 | { |
- | |
1470 | stroker->has_bounds = TRUE; |
- | |
1471 | _cairo_boxes_get_extents (boxes, num_boxes, &stroker->bounds); |
- | |
1472 | - | ||
1473 | stroker->bounds.p1.x -= stroker->half_line_width; |
- | |
1474 | stroker->bounds.p2.x += stroker->half_line_width; |
- | |
1475 | - | ||
1476 | stroker->bounds.p1.y -= stroker->half_line_width; |
- | |
1477 | stroker->bounds.p2.y += stroker->half_line_width; |
- | |
1478 | } |
- | |
1479 | - | ||
1480 | static cairo_bool_t |
- | |
1481 | _cairo_rectilinear_stroker_init (cairo_rectilinear_stroker_t *stroker, |
- | |
1482 | const cairo_stroke_style_t *stroke_style, |
- | |
1483 | const cairo_matrix_t *ctm, |
- | |
1484 | cairo_bool_t do_traps, |
- | |
1485 | void *container) |
- | |
1486 | { |
- | |
1487 | /* This special-case rectilinear stroker only supports |
- | |
1488 | * miter-joined lines (not curves) and a translation-only matrix |
- | |
1489 | * (though it could probably be extended to support a matrix with |
- | |
1490 | * uniform, integer scaling). |
- | |
1491 | * |
- | |
1492 | * It also only supports horizontal and vertical line_to |
- | |
1493 | * elements. But we don't catch that here, but instead return |
- | |
1494 | * UNSUPPORTED from _cairo_rectilinear_stroker_line_to if any |
- | |
1495 | * non-rectilinear line_to is encountered. |
- | |
1496 | */ |
- | |
1497 | if (stroke_style->line_join != CAIRO_LINE_JOIN_MITER) |
- | |
1498 | return FALSE; |
- | |
1499 | - | ||
1500 | /* If the miter limit turns right angles into bevels, then we |
- | |
1501 | * can't use this optimization. Remember, the ratio is |
- | |
1502 | * 1/sin(ɸ/2). So the cutoff is 1/sin(π/4.0) or ⎷2, |
- | |
1503 | * which we round for safety. */ |
- | |
1504 | if (stroke_style->miter_limit < M_SQRT2) |
- | |
1505 | return FALSE; |
- | |
1506 | - | ||
1507 | if (! (stroke_style->line_cap == CAIRO_LINE_CAP_BUTT || |
- | |
1508 | stroke_style->line_cap == CAIRO_LINE_CAP_SQUARE)) |
- | |
1509 | { |
- | |
1510 | return FALSE; |
- | |
1511 | } |
- | |
1512 | - | ||
1513 | if (! _cairo_matrix_has_unity_scale (ctm)) |
- | |
1514 | return FALSE; |
- | |
1515 | - | ||
1516 | stroker->stroke_style = stroke_style; |
- | |
1517 | stroker->ctm = ctm; |
- | |
1518 | - | ||
1519 | stroker->half_line_width = |
- | |
1520 | _cairo_fixed_from_double (stroke_style->line_width / 2.0); |
- | |
1521 | stroker->open_sub_path = FALSE; |
- | |
1522 | stroker->segments = stroker->segments_embedded; |
- | |
1523 | stroker->segments_size = ARRAY_LENGTH (stroker->segments_embedded); |
- | |
1524 | stroker->num_segments = 0; |
- | |
1525 | - | ||
1526 | _cairo_stroker_dash_init (&stroker->dash, stroke_style); |
- | |
1527 | - | ||
1528 | stroker->has_bounds = FALSE; |
- | |
1529 | - | ||
1530 | stroker->do_traps = do_traps; |
- | |
1531 | stroker->container = container; |
- | |
1532 | - | ||
1533 | return TRUE; |
- | |
1534 | } |
- | |
1535 | - | ||
1536 | static void |
- | |
1537 | _cairo_rectilinear_stroker_fini (cairo_rectilinear_stroker_t *stroker) |
- | |
1538 | { |
- | |
1539 | if (stroker->segments != stroker->segments_embedded) |
- | |
1540 | free (stroker->segments); |
- | |
1541 | } |
- | |
1542 | - | ||
1543 | static cairo_status_t |
- | |
1544 | _cairo_rectilinear_stroker_add_segment (cairo_rectilinear_stroker_t *stroker, |
- | |
1545 | const cairo_point_t *p1, |
- | |
1546 | const cairo_point_t *p2, |
- | |
1547 | cairo_bool_t is_horizontal, |
- | |
1548 | cairo_bool_t has_join) |
- | |
1549 | { |
- | |
1550 | if (CAIRO_INJECT_FAULT ()) |
- | |
1551 | return _cairo_error (CAIRO_STATUS_NO_MEMORY); |
- | |
1552 | - | ||
1553 | if (stroker->num_segments == stroker->segments_size) { |
- | |
1554 | int new_size = stroker->segments_size * 2; |
- | |
1555 | segment_t *new_segments; |
- | |
1556 | - | ||
1557 | if (stroker->segments == stroker->segments_embedded) { |
- | |
1558 | new_segments = _cairo_malloc_ab (new_size, sizeof (segment_t)); |
- | |
1559 | if (unlikely (new_segments == NULL)) |
- | |
1560 | return _cairo_error (CAIRO_STATUS_NO_MEMORY); |
- | |
1561 | - | ||
1562 | memcpy (new_segments, stroker->segments, |
- | |
1563 | stroker->num_segments * sizeof (segment_t)); |
- | |
1564 | } else { |
- | |
1565 | new_segments = _cairo_realloc_ab (stroker->segments, |
- | |
1566 | new_size, sizeof (segment_t)); |
- | |
1567 | if (unlikely (new_segments == NULL)) |
- | |
1568 | return _cairo_error (CAIRO_STATUS_NO_MEMORY); |
- | |
1569 | } |
- | |
1570 | - | ||
1571 | stroker->segments_size = new_size; |
- | |
1572 | stroker->segments = new_segments; |
- | |
1573 | } |
- | |
1574 | - | ||
1575 | stroker->segments[stroker->num_segments].p1 = *p1; |
- | |
1576 | stroker->segments[stroker->num_segments].p2 = *p2; |
- | |
1577 | stroker->segments[stroker->num_segments].has_join = has_join; |
- | |
1578 | stroker->segments[stroker->num_segments].is_horizontal = is_horizontal; |
- | |
1579 | stroker->num_segments++; |
- | |
1580 | - | ||
1581 | return CAIRO_STATUS_SUCCESS; |
- | |
1582 | } |
- | |
1583 | - | ||
1584 | static cairo_status_t |
- | |
1585 | _cairo_rectilinear_stroker_emit_segments (cairo_rectilinear_stroker_t *stroker) |
- | |
1586 | { |
- | |
1587 | cairo_status_t status; |
- | |
1588 | cairo_line_cap_t line_cap = stroker->stroke_style->line_cap; |
- | |
1589 | cairo_fixed_t half_line_width = stroker->half_line_width; |
- | |
1590 | int i; |
- | |
1591 | - | ||
1592 | for (i = 0; i < stroker->num_segments; i++) { |
- | |
1593 | cairo_point_t *a, *b; |
- | |
1594 | cairo_bool_t lengthen_initial, shorten_final, lengthen_final; |
- | |
1595 | - | ||
1596 | a = &stroker->segments[i].p1; |
- | |
1597 | b = &stroker->segments[i].p2; |
- | |
1598 | - | ||
1599 | /* For each segment we generate a single rectangular |
- | |
1600 | * trapezoid. This rectangle is based on a perpendicular |
- | |
1601 | * extension (by half the line width) of the segment endpoints |
- | |
1602 | * after some adjustments of the endpoints to account for caps |
- | |
1603 | * and joins. |
- | |
1604 | */ |
- | |
1605 | - | ||
1606 | /* We adjust the initial point of the segment to extend the |
- | |
1607 | * rectangle to include the previous cap or join, (this |
- | |
1608 | * adjustment applies to all segments except for the first |
- | |
1609 | * segment of open, butt-capped paths). |
- | |
1610 | */ |
- | |
1611 | lengthen_initial = TRUE; |
- | |
1612 | if (i == 0 && stroker->open_sub_path && line_cap == CAIRO_LINE_CAP_BUTT) |
- | |
1613 | lengthen_initial = FALSE; |
- | |
1614 | - | ||
1615 | /* The adjustment of the final point is trickier. For all but |
- | |
1616 | * the last segment we shorten the segment at the final |
- | |
1617 | * endpoint to not overlap with the subsequent join. For the |
- | |
1618 | * last segment we do the same shortening if the path is |
- | |
1619 | * closed. If the path is open and butt-capped we do no |
- | |
1620 | * adjustment, while if it's open and square-capped we do a |
- | |
1621 | * lengthening adjustment instead to include the cap. |
- | |
1622 | */ |
- | |
1623 | shorten_final = TRUE; |
- | |
1624 | lengthen_final = FALSE; |
- | |
1625 | if (i == stroker->num_segments - 1 && stroker->open_sub_path) { |
- | |
1626 | shorten_final = FALSE; |
- | |
1627 | if (line_cap == CAIRO_LINE_CAP_SQUARE) |
- | |
1628 | lengthen_final = TRUE; |
- | |
1629 | } |
- | |
1630 | - | ||
1631 | /* Perform the adjustments of the endpoints. */ |
- | |
1632 | if (a->y == b->y) { |
- | |
1633 | if (a->x < b->x) { |
- | |
1634 | if (lengthen_initial) |
- | |
1635 | a->x -= half_line_width; |
- | |
1636 | if (shorten_final) |
- | |
1637 | b->x -= half_line_width; |
- | |
1638 | else if (lengthen_final) |
- | |
1639 | b->x += half_line_width; |
- | |
1640 | } else { |
- | |
1641 | if (lengthen_initial) |
- | |
1642 | a->x += half_line_width; |
- | |
1643 | if (shorten_final) |
- | |
1644 | b->x += half_line_width; |
- | |
1645 | else if (lengthen_final) |
- | |
1646 | b->x -= half_line_width; |
- | |
1647 | } |
- | |
1648 | - | ||
1649 | if (a->x > b->x) { |
- | |
1650 | cairo_point_t *t; |
- | |
1651 | - | ||
1652 | t = a; |
- | |
1653 | a = b; |
- | |
1654 | b = t; |
- | |
1655 | } |
- | |
1656 | } else { |
- | |
1657 | if (a->y < b->y) { |
- | |
1658 | if (lengthen_initial) |
- | |
1659 | a->y -= half_line_width; |
- | |
1660 | if (shorten_final) |
- | |
1661 | b->y -= half_line_width; |
- | |
1662 | else if (lengthen_final) |
- | |
1663 | b->y += half_line_width; |
- | |
1664 | } else { |
- | |
1665 | if (lengthen_initial) |
- | |
1666 | a->y += half_line_width; |
- | |
1667 | if (shorten_final) |
- | |
1668 | b->y += half_line_width; |
- | |
1669 | else if (lengthen_final) |
- | |
1670 | b->y -= half_line_width; |
- | |
1671 | } |
- | |
1672 | - | ||
1673 | if (a->y > b->y) { |
- | |
1674 | cairo_point_t *t; |
- | |
1675 | - | ||
1676 | t = a; |
- | |
1677 | a = b; |
- | |
1678 | b = t; |
- | |
1679 | } |
- | |
1680 | } |
- | |
1681 | - | ||
1682 | /* Form the rectangle by expanding by half the line width in |
- | |
1683 | * either perpendicular direction. */ |
- | |
1684 | if (a->y == b->y) { |
- | |
1685 | a->y -= half_line_width; |
- | |
1686 | b->y += half_line_width; |
- | |
1687 | } else { |
- | |
1688 | a->x -= half_line_width; |
- | |
1689 | b->x += half_line_width; |
- | |
1690 | } |
- | |
1691 | - | ||
1692 | if (stroker->do_traps) { |
- | |
1693 | status = _cairo_traps_tessellate_rectangle (stroker->container, a, b); |
- | |
1694 | } else { |
- | |
1695 | cairo_box_t box; |
- | |
1696 | - | ||
1697 | box.p1 = *a; |
- | |
1698 | box.p2 = *b; |
- | |
1699 | - | ||
1700 | status = _cairo_boxes_add (stroker->container, &box); |
- | |
1701 | } |
- | |
1702 | if (unlikely (status)) |
- | |
1703 | return status; |
- | |
1704 | } |
- | |
1705 | - | ||
1706 | stroker->num_segments = 0; |
- | |
1707 | - | ||
1708 | return CAIRO_STATUS_SUCCESS; |
- | |
1709 | } |
- | |
1710 | - | ||
1711 | static cairo_status_t |
- | |
1712 | _cairo_rectilinear_stroker_emit_segments_dashed (cairo_rectilinear_stroker_t *stroker) |
- | |
1713 | { |
- | |
1714 | cairo_status_t status; |
- | |
1715 | cairo_line_cap_t line_cap = stroker->stroke_style->line_cap; |
- | |
1716 | cairo_fixed_t half_line_width = stroker->half_line_width; |
- | |
1717 | int i; |
- | |
1718 | - | ||
1719 | for (i = 0; i < stroker->num_segments; i++) { |
- | |
1720 | cairo_point_t *a, *b; |
- | |
1721 | cairo_bool_t is_horizontal; |
- | |
1722 | - | ||
1723 | a = &stroker->segments[i].p1; |
- | |
1724 | b = &stroker->segments[i].p2; |
- | |
1725 | - | ||
1726 | is_horizontal = stroker->segments[i].is_horizontal; |
- | |
1727 | - | ||
1728 | /* Handle the joins for a potentially degenerate segment. */ |
- | |
1729 | if (line_cap == CAIRO_LINE_CAP_BUTT && |
- | |
1730 | stroker->segments[i].has_join && |
- | |
1731 | (i != stroker->num_segments - 1 || |
- | |
1732 | (! stroker->open_sub_path && stroker->dash.dash_starts_on))) |
- | |
1733 | { |
- | |
1734 | cairo_point_t p1 = stroker->segments[i].p1; |
- | |
1735 | cairo_point_t p2 = stroker->segments[i].p2; |
- | |
1736 | cairo_slope_t out_slope; |
- | |
1737 | int j = (i + 1) % stroker->num_segments; |
- | |
1738 | - | ||
1739 | _cairo_slope_init (&out_slope, |
- | |
1740 | &stroker->segments[j].p1, |
- | |
1741 | &stroker->segments[j].p2); |
- | |
1742 | - | ||
1743 | if (is_horizontal) { |
- | |
1744 | if (p1.x <= p2.x) { |
- | |
1745 | p1.x = p2.x; |
- | |
1746 | p2.x += half_line_width; |
- | |
1747 | } else { |
- | |
1748 | p1.x = p2.x - half_line_width; |
- | |
1749 | } |
- | |
1750 | if (out_slope.dy >= 0) |
- | |
1751 | p1.y -= half_line_width; |
- | |
1752 | if (out_slope.dy <= 0) |
- | |
1753 | p2.y += half_line_width; |
- | |
1754 | } else { |
- | |
1755 | if (p1.y <= p2.y) { |
- | |
1756 | p1.y = p2.y; |
- | |
1757 | p2.y += half_line_width; |
- | |
1758 | } else { |
- | |
1759 | p1.y = p2.y - half_line_width; |
- | |
1760 | } |
- | |
1761 | if (out_slope.dx >= 0) |
- | |
1762 | p1.x -= half_line_width; |
- | |
1763 | if (out_slope.dx <= 0) |
- | |
1764 | p2.x += half_line_width; |
- | |
1765 | } |
- | |
1766 | - | ||
1767 | if (stroker->do_traps) { |
- | |
1768 | status = _cairo_traps_tessellate_rectangle (stroker->container, &p1, &p2); |
- | |
1769 | } else { |
- | |
1770 | cairo_box_t box; |
- | |
1771 | - | ||
1772 | box.p1 = p1; |
- | |
1773 | box.p2 = p2; |
- | |
1774 | - | ||
1775 | status = _cairo_boxes_add (stroker->container, &box); |
- | |
1776 | } |
- | |
1777 | if (unlikely (status)) |
- | |
1778 | return status; |
- | |
1779 | } |
- | |
1780 | - | ||
1781 | /* Perform the adjustments of the endpoints. */ |
- | |
1782 | if (is_horizontal) { |
- | |
1783 | if (line_cap == CAIRO_LINE_CAP_SQUARE) { |
- | |
1784 | if (a->x <= b->x) { |
- | |
1785 | a->x -= half_line_width; |
- | |
1786 | b->x += half_line_width; |
- | |
1787 | } else { |
- | |
1788 | a->x += half_line_width; |
- | |
1789 | b->x -= half_line_width; |
- | |
1790 | } |
- | |
1791 | } |
- | |
1792 | - | ||
1793 | if (a->x > b->x) { |
- | |
1794 | cairo_point_t *t; |
- | |
1795 | - | ||
1796 | t = a; |
- | |
1797 | a = b; |
- | |
1798 | b = t; |
- | |
1799 | } |
- | |
1800 | - | ||
1801 | a->y -= half_line_width; |
- | |
1802 | b->y += half_line_width; |
- | |
1803 | } else { |
- | |
1804 | if (line_cap == CAIRO_LINE_CAP_SQUARE) { |
- | |
1805 | if (a->y <= b->y) { |
- | |
1806 | a->y -= half_line_width; |
- | |
1807 | b->y += half_line_width; |
- | |
1808 | } else { |
- | |
1809 | a->y += half_line_width; |
- | |
1810 | b->y -= half_line_width; |
- | |
1811 | } |
- | |
1812 | } |
- | |
1813 | - | ||
1814 | if (a->y > b->y) { |
- | |
1815 | cairo_point_t *t; |
- | |
1816 | - | ||
1817 | t = a; |
- | |
1818 | a = b; |
- | |
1819 | b = t; |
- | |
1820 | } |
- | |
1821 | - | ||
1822 | a->x -= half_line_width; |
- | |
1823 | b->x += half_line_width; |
- | |
1824 | } |
- | |
1825 | - | ||
1826 | if (a->x == b->x && a->y == b->y) |
- | |
1827 | continue; |
- | |
1828 | - | ||
1829 | if (stroker->do_traps) { |
- | |
1830 | status = _cairo_traps_tessellate_rectangle (stroker->container, a, b); |
- | |
1831 | } else { |
- | |
1832 | cairo_box_t box; |
- | |
1833 | - | ||
1834 | box.p1 = *a; |
- | |
1835 | box.p2 = *b; |
- | |
1836 | - | ||
1837 | status = _cairo_boxes_add (stroker->container, &box); |
- | |
1838 | } |
- | |
1839 | if (unlikely (status)) |
- | |
1840 | return status; |
- | |
1841 | } |
- | |
1842 | - | ||
1843 | stroker->num_segments = 0; |
- | |
1844 | - | ||
1845 | return CAIRO_STATUS_SUCCESS; |
- | |
1846 | } |
- | |
1847 | - | ||
1848 | static cairo_status_t |
- | |
1849 | _cairo_rectilinear_stroker_move_to (void *closure, |
- | |
1850 | const cairo_point_t *point) |
- | |
1851 | { |
- | |
1852 | cairo_rectilinear_stroker_t *stroker = closure; |
- | |
1853 | cairo_status_t status; |
- | |
1854 | - | ||
1855 | if (stroker->dash.dashed) |
- | |
1856 | status = _cairo_rectilinear_stroker_emit_segments_dashed (stroker); |
- | |
1857 | else |
- | |
1858 | status = _cairo_rectilinear_stroker_emit_segments (stroker); |
- | |
1859 | if (unlikely (status)) |
- | |
1860 | return status; |
- | |
1861 | - | ||
1862 | /* reset the dash pattern for new sub paths */ |
- | |
1863 | _cairo_stroker_dash_start (&stroker->dash); |
- | |
1864 | - | ||
1865 | stroker->current_point = *point; |
- | |
1866 | stroker->first_point = *point; |
- | |
1867 | - | ||
1868 | return CAIRO_STATUS_SUCCESS; |
- | |
1869 | } |
- | |
1870 | - | ||
1871 | static cairo_status_t |
- | |
1872 | _cairo_rectilinear_stroker_line_to (void *closure, |
- | |
1873 | const cairo_point_t *b) |
- | |
1874 | { |
- | |
1875 | cairo_rectilinear_stroker_t *stroker = closure; |
- | |
1876 | cairo_point_t *a = &stroker->current_point; |
- | |
1877 | cairo_status_t status; |
- | |
1878 | - | ||
1879 | /* We only support horizontal or vertical elements. */ |
- | |
1880 | assert (a->x == b->x || a->y == b->y); |
- | |
1881 | - | ||
1882 | /* We don't draw anything for degenerate paths. */ |
- | |
1883 | if (a->x == b->x && a->y == b->y) |
- | |
1884 | return CAIRO_STATUS_SUCCESS; |
- | |
1885 | - | ||
1886 | status = _cairo_rectilinear_stroker_add_segment (stroker, a, b, |
- | |
1887 | a->y == b->y, |
- | |
1888 | TRUE); |
- | |
1889 | - | ||
1890 | stroker->current_point = *b; |
- | |
1891 | stroker->open_sub_path = TRUE; |
- | |
1892 | - | ||
1893 | return status; |
- | |
1894 | } |
- | |
1895 | - | ||
1896 | static cairo_status_t |
- | |
1897 | _cairo_rectilinear_stroker_line_to_dashed (void *closure, |
- | |
1898 | const cairo_point_t *point) |
- | |
1899 | { |
- | |
1900 | cairo_rectilinear_stroker_t *stroker = closure; |
- | |
1901 | const cairo_point_t *a = &stroker->current_point; |
- | |
1902 | const cairo_point_t *b = point; |
- | |
1903 | cairo_bool_t fully_in_bounds; |
- | |
1904 | double sign, remain; |
- | |
1905 | cairo_fixed_t mag; |
- | |
1906 | cairo_status_t status; |
- | |
1907 | cairo_line_t segment; |
- | |
1908 | cairo_bool_t dash_on = FALSE; |
- | |
1909 | cairo_bool_t is_horizontal; |
- | |
1910 | - | ||
1911 | /* We don't draw anything for degenerate paths. */ |
- | |
1912 | if (a->x == b->x && a->y == b->y) |
- | |
1913 | return CAIRO_STATUS_SUCCESS; |
- | |
1914 | - | ||
1915 | /* We only support horizontal or vertical elements. */ |
- | |
1916 | assert (a->x == b->x || a->y == b->y); |
- | |
1917 | - | ||
1918 | fully_in_bounds = TRUE; |
- | |
1919 | if (stroker->has_bounds && |
- | |
1920 | (! _cairo_box_contains_point (&stroker->bounds, a) || |
- | |
1921 | ! _cairo_box_contains_point (&stroker->bounds, b))) |
- | |
1922 | { |
- | |
1923 | fully_in_bounds = FALSE; |
- | |
1924 | } |
- | |
1925 | - | ||
1926 | is_horizontal = a->y == b->y; |
- | |
1927 | if (is_horizontal) |
- | |
1928 | mag = b->x - a->x; |
- | |
1929 | else |
- | |
1930 | mag = b->y - a->y; |
- | |
1931 | if (mag < 0) { |
- | |
1932 | remain = _cairo_fixed_to_double (-mag); |
- | |
1933 | sign = 1.; |
- | |
1934 | } else { |
- | |
1935 | remain = _cairo_fixed_to_double (mag); |
- | |
1936 | sign = -1.; |
- | |
1937 | } |
- | |
1938 | - | ||
1939 | segment.p2 = segment.p1 = *a; |
- | |
1940 | while (remain > 0.) { |
- | |
1941 | double step_length; |
- | |
1942 | - | ||
1943 | step_length = MIN (stroker->dash.dash_remain, remain); |
- | |
1944 | remain -= step_length; |
- | |
1945 | - | ||
1946 | mag = _cairo_fixed_from_double (sign*remain); |
- | |
1947 | if (is_horizontal) |
- | |
1948 | segment.p2.x = b->x + mag; |
- | |
1949 | else |
- | |
1950 | segment.p2.y = b->y + mag; |
- | |
1951 | - | ||
1952 | if (stroker->dash.dash_on && |
- | |
1953 | (fully_in_bounds || |
- | |
1954 | _cairo_box_intersects_line_segment (&stroker->bounds, &segment))) |
- | |
1955 | { |
- | |
1956 | status = _cairo_rectilinear_stroker_add_segment (stroker, |
- | |
1957 | &segment.p1, |
- | |
1958 | &segment.p2, |
- | |
1959 | is_horizontal, |
- | |
1960 | remain <= 0.); |
- | |
1961 | if (unlikely (status)) |
- | |
1962 | return status; |
- | |
1963 | - | ||
1964 | dash_on = TRUE; |
- | |
1965 | } |
- | |
1966 | else |
- | |
1967 | { |
- | |
1968 | dash_on = FALSE; |
- | |
1969 | } |
- | |
1970 | - | ||
1971 | _cairo_stroker_dash_step (&stroker->dash, step_length); |
- | |
1972 | segment.p1 = segment.p2; |
- | |
1973 | } |
- | |
1974 | - | ||
1975 | if (stroker->dash.dash_on && ! dash_on && |
- | |
1976 | (fully_in_bounds || |
- | |
1977 | _cairo_box_intersects_line_segment (&stroker->bounds, &segment))) |
- | |
1978 | { |
- | |
1979 | - | ||
1980 | /* This segment ends on a transition to dash_on, compute a new face |
- | |
1981 | * and add cap for the beginning of the next dash_on step. |
- | |
1982 | */ |
- | |
1983 | - | ||
1984 | status = _cairo_rectilinear_stroker_add_segment (stroker, |
- | |
1985 | &segment.p1, |
- | |
1986 | &segment.p1, |
- | |
1987 | is_horizontal, |
- | |
1988 | TRUE); |
- | |
1989 | if (unlikely (status)) |
- | |
1990 | return status; |
- | |
1991 | } |
- | |
1992 | - | ||
1993 | stroker->current_point = *point; |
- | |
1994 | stroker->open_sub_path = TRUE; |
- | |
1995 | - | ||
1996 | return CAIRO_STATUS_SUCCESS; |
- | |
1997 | } |
- | |
1998 | - | ||
1999 | static cairo_status_t |
- | |
2000 | _cairo_rectilinear_stroker_close_path (void *closure) |
- | |
2001 | { |
- | |
2002 | cairo_rectilinear_stroker_t *stroker = closure; |
- | |
2003 | cairo_status_t status; |
- | |
2004 | - | ||
2005 | /* We don't draw anything for degenerate paths. */ |
- | |
2006 | if (! stroker->open_sub_path) |
- | |
2007 | return CAIRO_STATUS_SUCCESS; |
- | |
2008 | - | ||
2009 | if (stroker->dash.dashed) { |
- | |
2010 | status = _cairo_rectilinear_stroker_line_to_dashed (stroker, |
- | |
2011 | &stroker->first_point); |
- | |
2012 | } else { |
- | |
2013 | status = _cairo_rectilinear_stroker_line_to (stroker, |
- | |
2014 | &stroker->first_point); |
- | |
2015 | } |
- | |
2016 | if (unlikely (status)) |
- | |
2017 | return status; |
- | |
2018 | - | ||
2019 | stroker->open_sub_path = FALSE; |
- | |
2020 | - | ||
2021 | if (stroker->dash.dashed) |
- | |
2022 | status = _cairo_rectilinear_stroker_emit_segments_dashed (stroker); |
- | |
2023 | else |
- | |
2024 | status = _cairo_rectilinear_stroker_emit_segments (stroker); |
- | |
2025 | if (unlikely (status)) |
- | |
2026 | return status; |
- | |
2027 | - | ||
2028 | return CAIRO_STATUS_SUCCESS; |
- | |
2029 | } |
- | |
2030 | - | ||
2031 | cairo_int_status_t |
- | |
2032 | _cairo_path_fixed_stroke_rectilinear_to_traps (const cairo_path_fixed_t *path, |
- | |
2033 | const cairo_stroke_style_t *stroke_style, |
- | |
2034 | const cairo_matrix_t *ctm, |
- | |
2035 | cairo_traps_t *traps) |
- | |
2036 | { |
- | |
2037 | cairo_rectilinear_stroker_t rectilinear_stroker; |
- | |
2038 | cairo_int_status_t status; |
- | |
2039 | - | ||
2040 | assert (path->is_rectilinear); |
- | |
2041 | - | ||
2042 | if (! _cairo_rectilinear_stroker_init (&rectilinear_stroker, |
- | |
2043 | stroke_style, ctm, |
- | |
2044 | TRUE, traps)) |
- | |
2045 | { |
- | |
2046 | return CAIRO_INT_STATUS_UNSUPPORTED; |
- | |
2047 | } |
- | |
2048 | - | ||
2049 | if (traps->num_limits) { |
- | |
2050 | _cairo_rectilinear_stroker_limit (&rectilinear_stroker, |
- | |
2051 | traps->limits, |
- | |
2052 | traps->num_limits); |
- | |
2053 | } |
- | |
2054 | - | ||
2055 | status = _cairo_path_fixed_interpret (path, |
- | |
2056 | CAIRO_DIRECTION_FORWARD, |
- | |
2057 | _cairo_rectilinear_stroker_move_to, |
- | |
2058 | rectilinear_stroker.dash.dashed ? |
- | |
2059 | _cairo_rectilinear_stroker_line_to_dashed : |
- | |
2060 | _cairo_rectilinear_stroker_line_to, |
- | |
2061 | NULL, |
- | |
2062 | _cairo_rectilinear_stroker_close_path, |
- | |
2063 | &rectilinear_stroker); |
- | |
2064 | if (unlikely (status)) |
- | |
2065 | goto BAIL; |
- | |
2066 | - | ||
2067 | if (rectilinear_stroker.dash.dashed) |
- | |
2068 | status = _cairo_rectilinear_stroker_emit_segments_dashed (&rectilinear_stroker); |
- | |
2069 | else |
- | |
2070 | status = _cairo_rectilinear_stroker_emit_segments (&rectilinear_stroker); |
- | |
2071 | - | ||
2072 | traps->is_rectilinear = 1; |
- | |
2073 | traps->is_rectangular = 1; |
- | |
2074 | /* As we incrementally tessellate, we do not eliminate self-intersections */ |
- | |
2075 | traps->has_intersections = traps->num_traps > 1; |
- | |
2076 | BAIL: |
- | |
2077 | _cairo_rectilinear_stroker_fini (&rectilinear_stroker); |
- | |
2078 | - | ||
2079 | if (unlikely (status)) |
- | |
2080 | _cairo_traps_clear (traps); |
- | |
2081 | - | ||
2082 | return status; |
- | |
2083 | } |
- | |
2084 | - | ||
2085 | cairo_int_status_t |
- | |
2086 | _cairo_path_fixed_stroke_rectilinear_to_boxes (const cairo_path_fixed_t *path, |
- | |
2087 | const cairo_stroke_style_t *stroke_style, |
- | |
2088 | const cairo_matrix_t *ctm, |
- | |
2089 | cairo_boxes_t *boxes) |
- | |
2090 | { |
- | |
2091 | cairo_rectilinear_stroker_t rectilinear_stroker; |
- | |
2092 | cairo_int_status_t status; |
- | |
2093 | - | ||
2094 | assert (path->is_rectilinear); |
- | |
2095 | - | ||
2096 | if (! _cairo_rectilinear_stroker_init (&rectilinear_stroker, |
- | |
2097 | stroke_style, ctm, |
- | |
2098 | FALSE, boxes)) |
- | |
2099 | { |
- | |
2100 | return CAIRO_INT_STATUS_UNSUPPORTED; |
- | |
2101 | } |
- | |
2102 | - | ||
2103 | if (boxes->num_limits) { |
- | |
2104 | _cairo_rectilinear_stroker_limit (&rectilinear_stroker, |
- | |
2105 | boxes->limits, |
- | |
2106 | boxes->num_limits); |
- | |
2107 | } |
- | |
2108 | - | ||
2109 | status = _cairo_path_fixed_interpret (path, |
- | |
2110 | CAIRO_DIRECTION_FORWARD, |
- | |
2111 | _cairo_rectilinear_stroker_move_to, |
- | |
2112 | rectilinear_stroker.dash.dashed ? |
- | |
2113 | _cairo_rectilinear_stroker_line_to_dashed : |
- | |
2114 | _cairo_rectilinear_stroker_line_to, |
- | |
2115 | NULL, |
- | |
2116 | _cairo_rectilinear_stroker_close_path, |
- | |
2117 | &rectilinear_stroker); |
- | |
2118 | if (unlikely (status)) |
- | |
2119 | goto BAIL; |
- | |
2120 | - | ||
2121 | if (rectilinear_stroker.dash.dashed) |
- | |
2122 | status = _cairo_rectilinear_stroker_emit_segments_dashed (&rectilinear_stroker); |
- | |
2123 | else |
- | |
2124 | status = _cairo_rectilinear_stroker_emit_segments (&rectilinear_stroker); |
- | |
2125 | if (unlikely (status)) |
- | |
2126 | goto BAIL; |
- | |
2127 | - | ||
2128 | /* As we incrementally tessellate, we do not eliminate self-intersections */ |
- | |
2129 | status = _cairo_bentley_ottmann_tessellate_boxes (boxes, |
- | |
2130 | CAIRO_FILL_RULE_WINDING, |
- | |
2131 | boxes); |
- | |
2132 | if (unlikely (status)) |
- | |
2133 | goto BAIL; |
- | |
2134 | - | ||
2135 | _cairo_rectilinear_stroker_fini (&rectilinear_stroker); |
- | |
2136 | - | ||
2137 | return CAIRO_STATUS_SUCCESS; |
- | |
2138 | - | ||
2139 | BAIL: |
- | |
2140 | _cairo_rectilinear_stroker_fini (&rectilinear_stroker); |
- | |
2141 | _cairo_boxes_clear (boxes); |
- | |
2142 | return status; |
- |