Tizen 2.0 Release
[framework/graphics/cairo.git] / src / cairo-path-stroke-boxes.c
1 /* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
2 /* cairo - a vector graphics library with display and print output
3  *
4  * Copyright © 2002 University of Southern California
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it either under the terms of the GNU Lesser General Public
8  * License version 2.1 as published by the Free Software Foundation
9  * (the "LGPL") or, at your option, under the terms of the Mozilla
10  * Public License Version 1.1 (the "MPL"). If you do not alter this
11  * notice, a recipient may use your version of this file under either
12  * the MPL or the LGPL.
13  *
14  * You should have received a copy of the LGPL along with this library
15  * in the file COPYING-LGPL-2.1; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
17  * You should have received a copy of the MPL along with this library
18  * in the file COPYING-MPL-1.1
19  *
20  * The contents of this file are subject to the Mozilla Public License
21  * Version 1.1 (the "License"); you may not use this file except in
22  * compliance with the License. You may obtain a copy of the License at
23  * http://www.mozilla.org/MPL/
24  *
25  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
26  * OF ANY KIND, either express or implied. See the LGPL or the MPL for
27  * the specific language governing rights and limitations.
28  *
29  * The Original Code is the cairo graphics library.
30  *
31  * The Initial Developer of the Original Code is University of Southern
32  * California.
33  *
34  * Contributor(s):
35  *      Carl D. Worth <cworth@cworth.org>
36  *      Chris Wilson <chris@chris-wilson.co.uk>
37  */
38
39 #define _BSD_SOURCE /* for hypot() */
40 #include "cairoint.h"
41
42 #include "cairo-box-inline.h"
43 #include "cairo-boxes-private.h"
44 #include "cairo-error-private.h"
45 #include "cairo-path-fixed-private.h"
46 #include "cairo-slope-private.h"
47 #include "cairo-stroke-dash-private.h"
48
49 typedef struct _segment_t {
50     cairo_point_t p1, p2;
51     unsigned flags;
52 #define HORIZONTAL 0x1
53 #define FORWARDS 0x2
54 #define JOIN 0x4
55 } segment_t;
56
57 typedef struct _cairo_rectilinear_stroker {
58     const cairo_stroke_style_t *stroke_style;
59     const cairo_matrix_t *ctm;
60     cairo_antialias_t antialias;
61
62     cairo_fixed_t half_line_x, half_line_y;
63     cairo_boxes_t *boxes;
64     cairo_point_t current_point;
65     cairo_point_t first_point;
66     cairo_bool_t open_sub_path;
67
68     cairo_stroker_dash_t dash;
69
70     cairo_bool_t has_bounds;
71     cairo_box_t bounds;
72
73     int num_segments;
74     int segments_size;
75     segment_t *segments;
76     segment_t segments_embedded[8]; /* common case is a single rectangle */
77 } cairo_rectilinear_stroker_t;
78
79 static void
80 _cairo_rectilinear_stroker_limit (cairo_rectilinear_stroker_t *stroker,
81                                   const cairo_box_t *boxes,
82                                   int num_boxes)
83 {
84     stroker->has_bounds = TRUE;
85     _cairo_boxes_get_extents (boxes, num_boxes, &stroker->bounds);
86
87     stroker->bounds.p1.x -= stroker->half_line_x;
88     stroker->bounds.p2.x += stroker->half_line_x;
89
90     stroker->bounds.p1.y -= stroker->half_line_y;
91     stroker->bounds.p2.y += stroker->half_line_y;
92 }
93
94 static cairo_bool_t
95 _cairo_rectilinear_stroker_init (cairo_rectilinear_stroker_t    *stroker,
96                                  const cairo_stroke_style_t     *stroke_style,
97                                  const cairo_matrix_t           *ctm,
98                                  cairo_antialias_t               antialias,
99                                  cairo_boxes_t                  *boxes)
100 {
101     /* This special-case rectilinear stroker only supports
102      * miter-joined lines (not curves) and a translation-only matrix
103      * (though it could probably be extended to support a matrix with
104      * uniform, integer scaling).
105      *
106      * It also only supports horizontal and vertical line_to
107      * elements. But we don't catch that here, but instead return
108      * UNSUPPORTED from _cairo_rectilinear_stroker_line_to if any
109      * non-rectilinear line_to is encountered.
110      */
111     if (stroke_style->line_join != CAIRO_LINE_JOIN_MITER)
112         return FALSE;
113
114     /* If the miter limit turns right angles into bevels, then we
115      * can't use this optimization. Remember, the ratio is
116      * 1/sin(ɸ/2). So the cutoff is 1/sin(π/4.0) or ⎷2,
117      * which we round for safety. */
118     if (stroke_style->miter_limit < M_SQRT2)
119         return FALSE;
120
121     if (! (stroke_style->line_cap == CAIRO_LINE_CAP_BUTT ||
122            stroke_style->line_cap == CAIRO_LINE_CAP_SQUARE))
123     {
124         return FALSE;
125     }
126
127     if (! _cairo_matrix_is_scale (ctm))
128         return FALSE;
129
130     stroker->stroke_style = stroke_style;
131     stroker->ctm = ctm;
132     stroker->antialias = antialias;
133
134     stroker->half_line_x =
135         _cairo_fixed_from_double (fabs(ctm->xx) * stroke_style->line_width / 2.0);
136     stroker->half_line_y =
137         _cairo_fixed_from_double (fabs(ctm->yy) * stroke_style->line_width / 2.0);
138
139     stroker->open_sub_path = FALSE;
140     stroker->segments = stroker->segments_embedded;
141     stroker->segments_size = ARRAY_LENGTH (stroker->segments_embedded);
142     stroker->num_segments = 0;
143
144     _cairo_stroker_dash_init (&stroker->dash, stroke_style);
145
146     stroker->has_bounds = FALSE;
147
148     stroker->boxes = boxes;
149
150     return TRUE;
151 }
152
153 static void
154 _cairo_rectilinear_stroker_fini (cairo_rectilinear_stroker_t    *stroker)
155 {
156     if (stroker->segments != stroker->segments_embedded)
157         free (stroker->segments);
158 }
159
160 static cairo_status_t
161 _cairo_rectilinear_stroker_add_segment (cairo_rectilinear_stroker_t *stroker,
162                                         const cairo_point_t     *p1,
163                                         const cairo_point_t     *p2,
164                                         unsigned                 flags)
165 {
166     if (CAIRO_INJECT_FAULT ())
167         return _cairo_error (CAIRO_STATUS_NO_MEMORY);
168
169     if (stroker->num_segments == stroker->segments_size) {
170         int new_size = stroker->segments_size * 2;
171         segment_t *new_segments;
172
173         if (stroker->segments == stroker->segments_embedded) {
174             new_segments = _cairo_malloc_ab (new_size, sizeof (segment_t));
175             if (unlikely (new_segments == NULL))
176                 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
177
178             memcpy (new_segments, stroker->segments,
179                     stroker->num_segments * sizeof (segment_t));
180         } else {
181             new_segments = _cairo_realloc_ab (stroker->segments,
182                                               new_size, sizeof (segment_t));
183             if (unlikely (new_segments == NULL))
184                 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
185         }
186
187         stroker->segments_size = new_size;
188         stroker->segments = new_segments;
189     }
190
191     stroker->segments[stroker->num_segments].p1 = *p1;
192     stroker->segments[stroker->num_segments].p2 = *p2;
193     stroker->segments[stroker->num_segments].flags = flags;
194     stroker->num_segments++;
195
196     return CAIRO_STATUS_SUCCESS;
197 }
198
199 static cairo_status_t
200 _cairo_rectilinear_stroker_emit_segments (cairo_rectilinear_stroker_t *stroker)
201 {
202     cairo_line_cap_t line_cap = stroker->stroke_style->line_cap;
203     cairo_fixed_t half_line_x = stroker->half_line_x;
204     cairo_fixed_t half_line_y = stroker->half_line_y;
205     cairo_status_t status;
206     int i, j;
207
208     /* For each segment we generate a single rectangle.
209      * This rectangle is based on a perpendicular extension (by half the
210      * line width) of the segment endpoints * after some adjustments of the
211      * endpoints to account for caps and joins.
212      */
213     for (i = 0; i < stroker->num_segments; i++) {
214         cairo_bool_t lengthen_initial, lengthen_final;
215         cairo_point_t *a, *b;
216         cairo_box_t box;
217
218         a = &stroker->segments[i].p1;
219         b = &stroker->segments[i].p2;
220
221         /* We adjust the initial point of the segment to extend the
222          * rectangle to include the previous cap or join, (this
223          * adjustment applies to all segments except for the first
224          * segment of open, butt-capped paths). However, we must be
225          * careful not to emit a miter join across a degenerate segment
226          * which has been elided.
227          *
228          * Overlapping segments will be eliminated by the tessellation.
229          * Ideally, we would not emit these self-intersections at all,
230          * but that is tricky with segments shorter than half_line_width.
231          */
232         j = i == 0 ? stroker->num_segments - 1 : i-1;
233         lengthen_initial = (stroker->segments[i].flags ^ stroker->segments[j].flags) & HORIZONTAL;
234         j = i == stroker->num_segments - 1 ? 0 : i+1;
235         lengthen_final = (stroker->segments[i].flags ^ stroker->segments[j].flags) & HORIZONTAL;
236         if (stroker->open_sub_path) {
237             if (i == 0)
238                 lengthen_initial = line_cap != CAIRO_LINE_CAP_BUTT;
239
240             if (i == stroker->num_segments - 1)
241                 lengthen_final = line_cap != CAIRO_LINE_CAP_BUTT;
242         }
243
244         /* Perform the adjustments of the endpoints. */
245         if (lengthen_initial | lengthen_final) {
246             if (a->y == b->y) {
247                 if (a->x < b->x) {
248                     if (lengthen_initial)
249                         a->x -= half_line_x;
250                     if (lengthen_final)
251                         b->x += half_line_x;
252                 } else {
253                     if (lengthen_initial)
254                         a->x += half_line_x;
255                     if (lengthen_final)
256                         b->x -= half_line_x;
257                 }
258             } else {
259                 if (a->y < b->y) {
260                     if (lengthen_initial)
261                         a->y -= half_line_y;
262                     if (lengthen_final)
263                         b->y += half_line_y;
264                 } else {
265                     if (lengthen_initial)
266                         a->y += half_line_y;
267                     if (lengthen_final)
268                         b->y -= half_line_y;
269                 }
270             }
271         }
272
273         /* Form the rectangle by expanding by half the line width in
274          * either perpendicular direction. */
275         if (a->y == b->y) {
276             a->y -= half_line_y;
277             b->y += half_line_y;
278         } else {
279             a->x -= half_line_x;
280             b->x += half_line_x;
281         }
282
283         if (a->x < b->x) {
284             box.p1.x = a->x;
285             box.p2.x = b->x;
286         } else {
287             box.p1.x = b->x;
288             box.p2.x = a->x;
289         }
290         if (a->y < b->y) {
291             box.p1.y = a->y;
292             box.p2.y = b->y;
293         } else {
294             box.p1.y = b->y;
295             box.p2.y = a->y;
296         }
297
298         status = _cairo_boxes_add (stroker->boxes, stroker->antialias, &box);
299         if (unlikely (status))
300             return status;
301     }
302
303     stroker->num_segments = 0;
304     return CAIRO_STATUS_SUCCESS;
305 }
306
307 static cairo_status_t
308 _cairo_rectilinear_stroker_emit_segments_dashed (cairo_rectilinear_stroker_t *stroker)
309 {
310     cairo_status_t status;
311     cairo_line_cap_t line_cap = stroker->stroke_style->line_cap;
312     cairo_fixed_t half_line_x = stroker->half_line_x;
313     cairo_fixed_t half_line_y = stroker->half_line_y;
314     int i;
315
316     for (i = 0; i < stroker->num_segments; i++) {
317         cairo_point_t *a, *b;
318         cairo_bool_t is_horizontal;
319         cairo_box_t box;
320
321         a = &stroker->segments[i].p1;
322         b = &stroker->segments[i].p2;
323
324         is_horizontal = stroker->segments[i].flags & HORIZONTAL;
325
326         /* Handle the joins for a potentially degenerate segment. */
327         if (line_cap == CAIRO_LINE_CAP_BUTT &&
328             stroker->segments[i].flags & JOIN &&
329             (i != stroker->num_segments - 1 ||
330              (! stroker->open_sub_path && stroker->dash.dash_starts_on)))
331         {
332             cairo_slope_t out_slope;
333             int j = (i + 1) % stroker->num_segments;
334             cairo_bool_t forwards = !!(stroker->segments[i].flags & FORWARDS);
335
336             _cairo_slope_init (&out_slope,
337                                &stroker->segments[j].p1,
338                                &stroker->segments[j].p2);
339             box.p2 = box.p1 = stroker->segments[i].p2;
340
341             if (is_horizontal) {
342                 if (forwards)
343                     box.p2.x += half_line_x;
344                 else
345                     box.p1.x -= half_line_x;
346
347                 if (out_slope.dy > 0)
348                     box.p1.y -= half_line_y;
349                 else
350                     box.p2.y += half_line_y;
351             } else {
352                 if (forwards)
353                     box.p2.y += half_line_y;
354                 else
355                     box.p1.y -= half_line_y;
356
357                 if (out_slope.dx > 0)
358                     box.p1.x -= half_line_x;
359                 else
360                     box.p2.x += half_line_x;
361             }
362
363             status = _cairo_boxes_add (stroker->boxes, stroker->antialias, &box);
364             if (unlikely (status))
365                 return status;
366         }
367
368         /* Perform the adjustments of the endpoints. */
369         if (is_horizontal) {
370             if (line_cap == CAIRO_LINE_CAP_SQUARE) {
371                 if (a->x <= b->x) {
372                     a->x -= half_line_x;
373                     b->x += half_line_x;
374                 } else {
375                     a->x += half_line_x;
376                     b->x -= half_line_x;
377                 }
378             }
379
380             a->y += half_line_y;
381             b->y -= half_line_y;
382         } else {
383             if (line_cap == CAIRO_LINE_CAP_SQUARE) {
384                 if (a->y <= b->y) {
385                     a->y -= half_line_y;
386                     b->y += half_line_y;
387                 } else {
388                     a->y += half_line_y;
389                     b->y -= half_line_y;
390                 }
391             }
392
393             a->x += half_line_x;
394             b->x -= half_line_x;
395         }
396
397         if (a->x == b->x && a->y == b->y)
398             continue;
399
400         if (a->x < b->x) {
401             box.p1.x = a->x;
402             box.p2.x = b->x;
403         } else {
404             box.p1.x = b->x;
405             box.p2.x = a->x;
406         }
407         if (a->y < b->y) {
408             box.p1.y = a->y;
409             box.p2.y = b->y;
410         } else {
411             box.p1.y = b->y;
412             box.p2.y = a->y;
413         }
414
415         status = _cairo_boxes_add (stroker->boxes, stroker->antialias, &box);
416         if (unlikely (status))
417             return status;
418     }
419
420     stroker->num_segments = 0;
421
422     return CAIRO_STATUS_SUCCESS;
423 }
424
425 static cairo_status_t
426 _cairo_rectilinear_stroker_move_to (void                *closure,
427                                     const cairo_point_t *point)
428 {
429     cairo_rectilinear_stroker_t *stroker = closure;
430     cairo_status_t status;
431
432     if (stroker->dash.dashed)
433         status = _cairo_rectilinear_stroker_emit_segments_dashed (stroker);
434     else
435         status = _cairo_rectilinear_stroker_emit_segments (stroker);
436     if (unlikely (status))
437         return status;
438
439     /* reset the dash pattern for new sub paths */
440     _cairo_stroker_dash_start (&stroker->dash);
441
442     stroker->current_point = *point;
443     stroker->first_point = *point;
444
445     return CAIRO_STATUS_SUCCESS;
446 }
447
448 static cairo_status_t
449 _cairo_rectilinear_stroker_line_to (void                *closure,
450                                     const cairo_point_t *b)
451 {
452     cairo_rectilinear_stroker_t *stroker = closure;
453     cairo_point_t *a = &stroker->current_point;
454     cairo_status_t status;
455
456     /* We only support horizontal or vertical elements. */
457     assert (a->x == b->x || a->y == b->y);
458
459     /* We don't draw anything for degenerate paths. */
460     if (a->x == b->x && a->y == b->y)
461         return CAIRO_STATUS_SUCCESS;
462
463     status = _cairo_rectilinear_stroker_add_segment (stroker, a, b,
464                                                      (a->y == b->y) | JOIN);
465
466     stroker->current_point = *b;
467     stroker->open_sub_path = TRUE;
468
469     return status;
470 }
471
472 static cairo_status_t
473 _cairo_rectilinear_stroker_line_to_dashed (void         *closure,
474                                            const cairo_point_t  *point)
475 {
476     cairo_rectilinear_stroker_t *stroker = closure;
477     const cairo_point_t *a = &stroker->current_point;
478     const cairo_point_t *b = point;
479     cairo_bool_t fully_in_bounds;
480     double sf, sign, remain;
481     cairo_fixed_t mag;
482     cairo_status_t status;
483     cairo_line_t segment;
484     cairo_bool_t dash_on = FALSE;
485     unsigned is_horizontal;
486
487     /* We don't draw anything for degenerate paths. */
488     if (a->x == b->x && a->y == b->y)
489         return CAIRO_STATUS_SUCCESS;
490
491     /* We only support horizontal or vertical elements. */
492     assert (a->x == b->x || a->y == b->y);
493
494     fully_in_bounds = TRUE;
495     if (stroker->has_bounds &&
496         (! _cairo_box_contains_point (&stroker->bounds, a) ||
497          ! _cairo_box_contains_point (&stroker->bounds, b)))
498     {
499         fully_in_bounds = FALSE;
500     }
501
502     is_horizontal = a->y == b->y;
503     if (is_horizontal) {
504         mag = b->x - a->x;
505         sf = fabs (stroker->ctm->xx);
506     } else {
507         mag = b->y - a->y;
508         sf = fabs (stroker->ctm->yy);
509     }
510     if (mag < 0) {
511         remain = _cairo_fixed_to_double (-mag);
512         sign = 1.;
513     } else {
514         remain = _cairo_fixed_to_double (mag);
515         is_horizontal |= FORWARDS;
516         sign = -1.;
517     }
518
519     segment.p2 = segment.p1 = *a;
520     while (remain > 0.) {
521         double step_length;
522
523         step_length = MIN (sf * stroker->dash.dash_remain, remain);
524         remain -= step_length;
525
526         mag = _cairo_fixed_from_double (sign*remain);
527         if (is_horizontal & 0x1)
528             segment.p2.x = b->x + mag;
529         else
530             segment.p2.y = b->y + mag;
531
532         if (stroker->dash.dash_on &&
533             (fully_in_bounds ||
534              _cairo_box_intersects_line_segment (&stroker->bounds, &segment)))
535         {
536             status = _cairo_rectilinear_stroker_add_segment (stroker,
537                                                              &segment.p1,
538                                                              &segment.p2,
539                                                              is_horizontal | (remain <= 0.) << 2);
540             if (unlikely (status))
541                 return status;
542
543             dash_on = TRUE;
544         }
545         else
546         {
547             dash_on = FALSE;
548         }
549
550         _cairo_stroker_dash_step (&stroker->dash, step_length / sf);
551         segment.p1 = segment.p2;
552     }
553
554     if (stroker->dash.dash_on && ! dash_on &&
555         (fully_in_bounds ||
556          _cairo_box_intersects_line_segment (&stroker->bounds, &segment)))
557     {
558
559         /* This segment ends on a transition to dash_on, compute a new face
560          * and add cap for the beginning of the next dash_on step.
561          */
562
563         status = _cairo_rectilinear_stroker_add_segment (stroker,
564                                                          &segment.p1,
565                                                          &segment.p1,
566                                                          is_horizontal | JOIN);
567         if (unlikely (status))
568             return status;
569     }
570
571     stroker->current_point = *point;
572     stroker->open_sub_path = TRUE;
573
574     return CAIRO_STATUS_SUCCESS;
575 }
576
577 static cairo_status_t
578 _cairo_rectilinear_stroker_close_path (void *closure)
579 {
580     cairo_rectilinear_stroker_t *stroker = closure;
581     cairo_status_t status;
582
583     /* We don't draw anything for degenerate paths. */
584     if (! stroker->open_sub_path)
585         return CAIRO_STATUS_SUCCESS;
586
587     if (stroker->dash.dashed) {
588         status = _cairo_rectilinear_stroker_line_to_dashed (stroker,
589                                                             &stroker->first_point);
590     } else {
591         status = _cairo_rectilinear_stroker_line_to (stroker,
592                                                      &stroker->first_point);
593     }
594     if (unlikely (status))
595         return status;
596
597     stroker->open_sub_path = FALSE;
598
599     if (stroker->dash.dashed)
600         status = _cairo_rectilinear_stroker_emit_segments_dashed (stroker);
601     else
602         status = _cairo_rectilinear_stroker_emit_segments (stroker);
603     if (unlikely (status))
604         return status;
605
606     return CAIRO_STATUS_SUCCESS;
607 }
608
609 cairo_int_status_t
610 _cairo_path_fixed_stroke_rectilinear_to_boxes (const cairo_path_fixed_t *path,
611                                                const cairo_stroke_style_t       *stroke_style,
612                                                const cairo_matrix_t     *ctm,
613                                                cairo_antialias_t         antialias,
614                                                cairo_boxes_t            *boxes)
615 {
616     cairo_rectilinear_stroker_t rectilinear_stroker;
617     cairo_int_status_t status;
618     cairo_box_t box;
619
620     assert (_cairo_path_fixed_stroke_is_rectilinear (path));
621
622     if (! _cairo_rectilinear_stroker_init (&rectilinear_stroker,
623                                            stroke_style, ctm, antialias,
624                                            boxes))
625     {
626         return CAIRO_INT_STATUS_UNSUPPORTED;
627     }
628
629     if (! rectilinear_stroker.dash.dashed &&
630         _cairo_path_fixed_is_stroke_box (path, &box) &&
631         /* if the segments overlap we need to feed them into the tessellator */
632         box.p2.x - box.p1.x > 2* rectilinear_stroker.half_line_x &&
633         box.p2.y - box.p1.y > 2* rectilinear_stroker.half_line_y)
634     {
635         cairo_box_t b;
636
637         /* top */
638         b.p1.x = box.p1.x - rectilinear_stroker.half_line_x;
639         b.p2.x = box.p2.x + rectilinear_stroker.half_line_x;
640         b.p1.y = box.p1.y - rectilinear_stroker.half_line_y;
641         b.p2.y = box.p1.y + rectilinear_stroker.half_line_y;
642         status = _cairo_boxes_add (boxes, antialias, &b);
643         assert (status == CAIRO_INT_STATUS_SUCCESS);
644
645         /* left  (excluding top/bottom) */
646         b.p1.x = box.p1.x - rectilinear_stroker.half_line_x;
647         b.p2.x = box.p1.x + rectilinear_stroker.half_line_x;
648         b.p1.y = box.p1.y + rectilinear_stroker.half_line_y;
649         b.p2.y = box.p2.y - rectilinear_stroker.half_line_y;
650         status = _cairo_boxes_add (boxes, antialias, &b);
651         assert (status == CAIRO_INT_STATUS_SUCCESS);
652
653         /* right  (excluding top/bottom) */
654         b.p1.x = box.p2.x - rectilinear_stroker.half_line_x;
655         b.p2.x = box.p2.x + rectilinear_stroker.half_line_x;
656         b.p1.y = box.p1.y + rectilinear_stroker.half_line_y;
657         b.p2.y = box.p2.y - rectilinear_stroker.half_line_y;
658         status = _cairo_boxes_add (boxes, antialias, &b);
659         assert (status == CAIRO_INT_STATUS_SUCCESS);
660
661         /* bottom */
662         b.p1.x = box.p1.x - rectilinear_stroker.half_line_x;
663         b.p2.x = box.p2.x + rectilinear_stroker.half_line_x;
664         b.p1.y = box.p2.y - rectilinear_stroker.half_line_y;
665         b.p2.y = box.p2.y + rectilinear_stroker.half_line_y;
666         status = _cairo_boxes_add (boxes, antialias, &b);
667         assert (status == CAIRO_INT_STATUS_SUCCESS);
668
669         goto done;
670     }
671
672     if (boxes->num_limits) {
673         _cairo_rectilinear_stroker_limit (&rectilinear_stroker,
674                                           boxes->limits,
675                                           boxes->num_limits);
676     }
677
678     status = _cairo_path_fixed_interpret (path,
679                                           _cairo_rectilinear_stroker_move_to,
680                                           rectilinear_stroker.dash.dashed ?
681                                           _cairo_rectilinear_stroker_line_to_dashed :
682                                           _cairo_rectilinear_stroker_line_to,
683                                           NULL,
684                                           _cairo_rectilinear_stroker_close_path,
685                                           &rectilinear_stroker);
686     if (unlikely (status))
687         goto BAIL;
688
689     if (rectilinear_stroker.dash.dashed)
690         status = _cairo_rectilinear_stroker_emit_segments_dashed (&rectilinear_stroker);
691     else
692         status = _cairo_rectilinear_stroker_emit_segments (&rectilinear_stroker);
693     if (unlikely (status))
694         goto BAIL;
695
696     /* As we incrementally tessellate, we do not eliminate self-intersections */
697     status = _cairo_bentley_ottmann_tessellate_boxes (boxes,
698                                                       CAIRO_FILL_RULE_WINDING,
699                                                       boxes);
700     if (unlikely (status))
701         goto BAIL;
702
703 done:
704     _cairo_rectilinear_stroker_fini (&rectilinear_stroker);
705     return CAIRO_STATUS_SUCCESS;
706
707 BAIL:
708     _cairo_rectilinear_stroker_fini (&rectilinear_stroker);
709     _cairo_boxes_clear (boxes);
710     return status;
711 }