1
/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
2
/* cairo - a vector graphics library with display and print output
3
 *
4
 * Copyright © 2002 University of Southern California
5
 *
6
 * This library is free software; you can redistribute it and/or
7
 * modify it either under the terms of the GNU Lesser General Public
8
 * License version 2.1 as published by the Free Software Foundation
9
 * (the "LGPL") or, at your option, under the terms of the Mozilla
10
 * Public License Version 1.1 (the "MPL"). If you do not alter this
11
 * notice, a recipient may use your version of this file under either
12
 * the MPL or the LGPL.
13
 *
14
 * You should have received a copy of the LGPL along with this library
15
 * in the file COPYING-LGPL-2.1; if not, write to the Free Software
16
 * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
17
 * You should have received a copy of the MPL along with this library
18
 * in the file COPYING-MPL-1.1
19
 *
20
 * The contents of this file are subject to the Mozilla Public License
21
 * Version 1.1 (the "License"); you may not use this file except in
22
 * compliance with the License. You may obtain a copy of the License at
23
 * http://www.mozilla.org/MPL/
24
 *
25
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
26
 * OF ANY KIND, either express or implied. See the LGPL or the MPL for
27
 * the specific language governing rights and limitations.
28
 *
29
 * The Original Code is the cairo graphics library.
30
 *
31
 * The Initial Developer of the Original Code is University of Southern
32
 * California.
33
 *
34
 * Contributor(s):
35
 *	Carl D. Worth <cworth@cworth.org>
36
 *	Chris Wilson <chris@chris-wilson.co.uk>
37
 */
38

            
39
#define _DEFAULT_SOURCE /* for hypot() */
40
#include "cairoint.h"
41

            
42
#include "cairo-box-inline.h"
43
#include "cairo-boxes-private.h"
44
#include "cairo-error-private.h"
45
#include "cairo-path-fixed-private.h"
46
#include "cairo-slope-private.h"
47
#include "cairo-stroke-dash-private.h"
48

            
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
1206
_cairo_rectilinear_stroker_limit (cairo_rectilinear_stroker_t *stroker,
81
				  const cairo_box_t *boxes,
82
				  int num_boxes)
83
{
84
1206
    stroker->has_bounds = TRUE;
85
1206
    _cairo_boxes_get_extents (boxes, num_boxes, &stroker->bounds);
86

            
87
1206
    stroker->bounds.p1.x -= stroker->half_line_x;
88
1206
    stroker->bounds.p2.x += stroker->half_line_x;
89

            
90
1206
    stroker->bounds.p1.y -= stroker->half_line_y;
91
1206
    stroker->bounds.p2.y += stroker->half_line_y;
92
1206
}
93

            
94
static cairo_bool_t
95
2997
_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
2997
    if (stroke_style->line_join	!= CAIRO_LINE_JOIN_MITER)
112
186
	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
2811
    if (stroke_style->miter_limit < M_SQRT2)
119
3
	return FALSE;
120

            
121
2808
    if (! (stroke_style->line_cap == CAIRO_LINE_CAP_BUTT ||
122
465
	   stroke_style->line_cap == CAIRO_LINE_CAP_SQUARE))
123
    {
124
360
	return FALSE;
125
    }
126

            
127
2448
    if (! _cairo_matrix_is_scale (ctm))
128
12
	return FALSE;
129

            
130
2436
    stroker->stroke_style = stroke_style;
131
2436
    stroker->ctm = ctm;
132
2436
    stroker->antialias = antialias;
133

            
134
2436
    stroker->half_line_x =
135
2436
	_cairo_fixed_from_double (fabs(ctm->xx) * stroke_style->line_width / 2.0);
136
2436
    stroker->half_line_y =
137
2436
	_cairo_fixed_from_double (fabs(ctm->yy) * stroke_style->line_width / 2.0);
138

            
139
2436
    stroker->open_sub_path = FALSE;
140
2436
    stroker->segments = stroker->segments_embedded;
141
2436
    stroker->segments_size = ARRAY_LENGTH (stroker->segments_embedded);
142
2436
    stroker->num_segments = 0;
143

            
144
2436
    _cairo_stroker_dash_init (&stroker->dash, stroke_style);
145

            
146
2436
    stroker->has_bounds = FALSE;
147

            
148
2436
    stroker->boxes = boxes;
149

            
150
2436
    return TRUE;
151
}
152

            
153
static void
154
2436
_cairo_rectilinear_stroker_fini (cairo_rectilinear_stroker_t	*stroker)
155
{
156
2436
    if (stroker->segments != stroker->segments_embedded)
157
171
	free (stroker->segments);
158
2436
}
159

            
160
static cairo_status_t
161
8628
_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
8628
    if (stroker->num_segments == stroker->segments_size) {
170
186
	int new_size = stroker->segments_size * 2;
171
	segment_t *new_segments;
172

            
173
186
	if (stroker->segments == stroker->segments_embedded) {
174
171
	    new_segments = _cairo_malloc_ab (new_size, sizeof (segment_t));
175
171
	    if (unlikely (new_segments == NULL))
176
		return _cairo_error (CAIRO_STATUS_NO_MEMORY);
177

            
178
171
	    memcpy (new_segments, stroker->segments,
179
171
		    stroker->num_segments * sizeof (segment_t));
180
	} else {
181
15
	    new_segments = _cairo_realloc_ab (stroker->segments,
182
					      new_size, sizeof (segment_t));
183
15
	    if (unlikely (new_segments == NULL))
184
		return _cairo_error (CAIRO_STATUS_NO_MEMORY);
185
	}
186

            
187
186
	stroker->segments_size = new_size;
188
186
	stroker->segments = new_segments;
189
    }
190

            
191
8628
    stroker->segments[stroker->num_segments].p1 = *p1;
192
8628
    stroker->segments[stroker->num_segments].p2 = *p2;
193
8628
    stroker->segments[stroker->num_segments].flags = flags;
194
8628
    stroker->num_segments++;
195

            
196
8628
    return CAIRO_STATUS_SUCCESS;
197
}
198

            
199
static cairo_status_t
200
4578
_cairo_rectilinear_stroker_emit_segments (cairo_rectilinear_stroker_t *stroker)
201
{
202
4578
    cairo_line_cap_t line_cap = stroker->stroke_style->line_cap;
203
4578
    cairo_fixed_t half_line_x = stroker->half_line_x;
204
4578
    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
8865
    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
4287
	a = &stroker->segments[i].p1;
219
4287
	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
4287
	j = i == 0 ? stroker->num_segments - 1 : i-1;
233
4287
	lengthen_initial = (stroker->segments[i].flags ^ stroker->segments[j].flags) & HORIZONTAL;
234
4287
	j = i == stroker->num_segments - 1 ? 0 : i+1;
235
4287
	lengthen_final = (stroker->segments[i].flags ^ stroker->segments[j].flags) & HORIZONTAL;
236
4287
	if (stroker->open_sub_path) {
237
1923
	    if (i == 0)
238
1608
		lengthen_initial = line_cap != CAIRO_LINE_CAP_BUTT;
239

            
240
1923
	    if (i == stroker->num_segments - 1)
241
1608
		lengthen_final = line_cap != CAIRO_LINE_CAP_BUTT;
242
	}
243

            
244
	/* Perform the adjustments of the endpoints. */
245
4287
	if (lengthen_initial | lengthen_final) {
246
2982
	    if (a->y == b->y) {
247
1494
		if (a->x < b->x) {
248
942
		    if (lengthen_initial)
249
900
			a->x -= half_line_x;
250
942
		    if (lengthen_final)
251
744
			b->x += half_line_x;
252
		} else {
253
552
		    if (lengthen_initial)
254
513
			a->x += half_line_x;
255
552
		    if (lengthen_final)
256
546
			b->x -= half_line_x;
257
		}
258
	    } else {
259
1488
		if (a->y < b->y) {
260
750
		    if (lengthen_initial)
261
549
			a->y -= half_line_y;
262
750
		    if (lengthen_final)
263
711
			b->y += half_line_y;
264
		} else {
265
738
		    if (lengthen_initial)
266
738
			a->y += half_line_y;
267
738
		    if (lengthen_final)
268
699
			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
4287
	if (a->y == b->y) {
276
2361
	    a->y -= half_line_y;
277
2361
	    b->y += half_line_y;
278
	} else {
279
1926
	    a->x -= half_line_x;
280
1926
	    b->x += half_line_x;
281
	}
282

            
283
4287
	if (a->x < b->x) {
284
3732
	    box.p1.x = a->x;
285
3732
	    box.p2.x = b->x;
286
	} else {
287
555
	    box.p1.x = b->x;
288
555
	    box.p2.x = a->x;
289
	}
290
4287
	if (a->y < b->y) {
291
3549
	    box.p1.y = a->y;
292
3549
	    box.p2.y = b->y;
293
	} else {
294
738
	    box.p1.y = b->y;
295
738
	    box.p2.y = a->y;
296
	}
297

            
298
4287
	status = _cairo_boxes_add (stroker->boxes, stroker->antialias, &box);
299
4287
	if (unlikely (status))
300
	    return status;
301
    }
302

            
303
4578
    stroker->num_segments = 0;
304
4578
    return CAIRO_STATUS_SUCCESS;
305
}
306

            
307
static cairo_status_t
308
1788
_cairo_rectilinear_stroker_emit_segments_dashed (cairo_rectilinear_stroker_t *stroker)
309
{
310
    cairo_status_t status;
311
1788
    cairo_line_cap_t line_cap = stroker->stroke_style->line_cap;
312
1788
    cairo_fixed_t half_line_x = stroker->half_line_x;
313
1788
    cairo_fixed_t half_line_y = stroker->half_line_y;
314
    int i;
315

            
316
6129
    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
4341
	a = &stroker->segments[i].p1;
322
4341
	b = &stroker->segments[i].p2;
323

            
324
4341
	is_horizontal = stroker->segments[i].flags & HORIZONTAL;
325

            
326
	/* Handle the joins for a potentially degenerate segment. */
327
4341
	if (line_cap == CAIRO_LINE_CAP_BUTT &&
328
3459
	    stroker->segments[i].flags & JOIN &&
329
918
	    (i != stroker->num_segments - 1 ||
330
621
	     (! stroker->open_sub_path && stroker->dash.dash_starts_on)))
331
	{
332
	    cairo_slope_t out_slope;
333
342
	    int j = (i + 1) % stroker->num_segments;
334
342
	    cairo_bool_t forwards = !!(stroker->segments[i].flags & FORWARDS);
335

            
336
342
	    _cairo_slope_init (&out_slope,
337
342
			       &stroker->segments[j].p1,
338
342
			       &stroker->segments[j].p2);
339
342
	    box.p2 = box.p1 = stroker->segments[i].p2;
340

            
341
342
	    if (is_horizontal) {
342
165
		if (forwards)
343
87
		    box.p2.x += half_line_x;
344
		else
345
78
		    box.p1.x -= half_line_x;
346

            
347
165
		if (out_slope.dy > 0)
348
87
		    box.p1.y -= half_line_y;
349
		else
350
78
		    box.p2.y += half_line_y;
351
	    } else {
352
177
		if (forwards)
353
90
		    box.p2.y += half_line_y;
354
		else
355
87
		    box.p1.y -= half_line_y;
356

            
357
177
		if (out_slope.dx > 0)
358
87
		    box.p1.x -= half_line_x;
359
		else
360
90
		    box.p2.x += half_line_x;
361
	    }
362

            
363
342
	    status = _cairo_boxes_add (stroker->boxes, stroker->antialias, &box);
364
342
	    if (unlikely (status))
365
		return status;
366
	}
367

            
368
	/* Perform the adjustments of the endpoints. */
369
4341
	if (is_horizontal) {
370
3297
	    if (line_cap == CAIRO_LINE_CAP_SQUARE) {
371
444
		if (a->x <= b->x) {
372
228
		    a->x -= half_line_x;
373
228
		    b->x += half_line_x;
374
		} else {
375
216
		    a->x += half_line_x;
376
216
		    b->x -= half_line_x;
377
		}
378
	    }
379

            
380
3297
	    a->y += half_line_y;
381
3297
	    b->y -= half_line_y;
382
	} else {
383
1044
	    if (line_cap == CAIRO_LINE_CAP_SQUARE) {
384
438
		if (a->y <= b->y) {
385
222
		    a->y -= half_line_y;
386
222
		    b->y += half_line_y;
387
		} else {
388
216
		    a->y += half_line_y;
389
216
		    b->y -= half_line_y;
390
		}
391
	    }
392

            
393
1044
	    a->x += half_line_x;
394
1044
	    b->x -= half_line_x;
395
	}
396

            
397
4341
	if (a->x == b->x && a->y == b->y)
398
	    continue;
399

            
400
4341
	if (a->x < b->x) {
401
2583
	    box.p1.x = a->x;
402
2583
	    box.p2.x = b->x;
403
	} else {
404
1758
	    box.p1.x = b->x;
405
1758
	    box.p2.x = a->x;
406
	}
407
4341
	if (a->y < b->y) {
408
513
	    box.p1.y = a->y;
409
513
	    box.p2.y = b->y;
410
	} else {
411
3828
	    box.p1.y = b->y;
412
3828
	    box.p2.y = a->y;
413
	}
414

            
415
4341
	status = _cairo_boxes_add (stroker->boxes, stroker->antialias, &box);
416
4341
	if (unlikely (status))
417
	    return status;
418
    }
419

            
420
1788
    stroker->num_segments = 0;
421

            
422
1788
    return CAIRO_STATUS_SUCCESS;
423
}
424

            
425
static cairo_status_t
426
3654
_cairo_rectilinear_stroker_move_to (void		*closure,
427
				    const cairo_point_t	*point)
428
{
429
3654
    cairo_rectilinear_stroker_t *stroker = closure;
430
    cairo_status_t status;
431

            
432
3654
    if (stroker->dash.dashed)
433
1152
	status = _cairo_rectilinear_stroker_emit_segments_dashed (stroker);
434
    else
435
2502
	status = _cairo_rectilinear_stroker_emit_segments (stroker);
436
3654
    if (unlikely (status))
437
	return status;
438

            
439
    /* reset the dash pattern for new sub paths */
440
3654
    _cairo_stroker_dash_start (&stroker->dash);
441

            
442
3654
    stroker->current_point = *point;
443
3654
    stroker->first_point = *point;
444

            
445
3654
    return CAIRO_STATUS_SUCCESS;
446
}
447

            
448
static cairo_status_t
449
4335
_cairo_rectilinear_stroker_line_to (void		*closure,
450
				    const cairo_point_t	*b)
451
{
452
4335
    cairo_rectilinear_stroker_t *stroker = closure;
453
4335
    cairo_point_t *a = &stroker->current_point;
454
    cairo_status_t status;
455

            
456
    /* We only support horizontal or vertical elements. */
457
4335
    assert (a->x == b->x || a->y == b->y);
458

            
459
    /* We don't draw anything for degenerate paths. */
460
4335
    if (a->x == b->x && a->y == b->y)
461
48
	return CAIRO_STATUS_SUCCESS;
462

            
463
4287
    status = _cairo_rectilinear_stroker_add_segment (stroker, a, b,
464
4287
						     (a->y == b->y) | JOIN);
465

            
466
4287
    stroker->current_point = *b;
467
4287
    stroker->open_sub_path = TRUE;
468

            
469
4287
    return status;
470
}
471

            
472
static cairo_status_t
473
1614
_cairo_rectilinear_stroker_line_to_dashed (void		*closure,
474
					   const cairo_point_t	*point)
475
{
476
1614
    cairo_rectilinear_stroker_t *stroker = closure;
477
1614
    const cairo_point_t *a = &stroker->current_point;
478
1614
    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
1614
    cairo_bool_t dash_on = FALSE;
485
    unsigned is_horizontal;
486

            
487
    /* We don't draw anything for degenerate paths. */
488
1614
    if (a->x == b->x && a->y == b->y)
489
12
	return CAIRO_STATUS_SUCCESS;
490

            
491
    /* We only support horizontal or vertical elements. */
492
1602
    assert (a->x == b->x || a->y == b->y);
493

            
494
1602
    fully_in_bounds = TRUE;
495
1692
    if (stroker->has_bounds &&
496
144
	(! _cairo_box_contains_point (&stroker->bounds, a) ||
497
54
	 ! _cairo_box_contains_point (&stroker->bounds, b)))
498
    {
499
48
	fully_in_bounds = FALSE;
500
    }
501

            
502
1602
    is_horizontal = a->y == b->y;
503
1602
    if (is_horizontal) {
504
972
	mag = b->x - a->x;
505
972
	sf = fabs (stroker->ctm->xx);
506
    } else {
507
630
	mag = b->y - a->y;
508
630
	sf = fabs (stroker->ctm->yy);
509
    }
510
1602
    if (mag < 0) {
511
636
	remain = _cairo_fixed_to_double (-mag);
512
636
	sign = 1.;
513
    } else {
514
966
	remain = _cairo_fixed_to_double (mag);
515
966
	is_horizontal |= FORWARDS;
516
966
	sign = -1.;
517
    }
518

            
519
1602
    segment.p2 = segment.p1 = *a;
520
9372
    while (remain > 0.) {
521
	double step_length;
522

            
523
7770
	step_length = MIN (sf * stroker->dash.dash_remain, remain);
524
7770
	remain -= step_length;
525

            
526
7770
	mag = _cairo_fixed_from_double (sign*remain);
527
7770
	if (is_horizontal & 0x1)
528
6015
	    segment.p2.x = b->x + mag;
529
	else
530
1755
	    segment.p2.y = b->y + mag;
531

            
532
7770
	if (stroker->dash.dash_on &&
533
480
	    (fully_in_bounds ||
534
480
	     _cairo_box_intersects_line_segment (&stroker->bounds, &segment)))
535
	{
536
4182
	    status = _cairo_rectilinear_stroker_add_segment (stroker,
537
							     &segment.p1,
538
							     &segment.p2,
539
4182
							     is_horizontal | (remain <= 0.) << 2);
540
4182
	    if (unlikely (status))
541
		return status;
542

            
543
4182
	    dash_on = TRUE;
544
	}
545
	else
546
	{
547
3588
	    dash_on = FALSE;
548
	}
549

            
550
7770
	_cairo_stroker_dash_step (&stroker->dash, step_length / sf);
551
7770
	segment.p1 = segment.p2;
552
    }
553

            
554
1602
    if (stroker->dash.dash_on && ! dash_on &&
555
48
	(fully_in_bounds ||
556
48
	 _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
159
	status = _cairo_rectilinear_stroker_add_segment (stroker,
564
							 &segment.p1,
565
							 &segment.p1,
566
							 is_horizontal | JOIN);
567
159
	if (unlikely (status))
568
	    return status;
569
    }
570

            
571
1602
    stroker->current_point = *point;
572
1602
    stroker->open_sub_path = TRUE;
573

            
574
1602
    return CAIRO_STATUS_SUCCESS;
575
}
576

            
577
static cairo_status_t
578
540
_cairo_rectilinear_stroker_close_path (void *closure)
579
{
580
540
    cairo_rectilinear_stroker_t *stroker = closure;
581
    cairo_status_t status;
582

            
583
    /* We don't draw anything for degenerate paths. */
584
540
    if (! stroker->open_sub_path)
585
21
	return CAIRO_STATUS_SUCCESS;
586

            
587
519
    if (stroker->dash.dashed) {
588
96
	status = _cairo_rectilinear_stroker_line_to_dashed (stroker,
589
96
							    &stroker->first_point);
590
    } else {
591
423
	status = _cairo_rectilinear_stroker_line_to (stroker,
592
423
						     &stroker->first_point);
593
    }
594
519
    if (unlikely (status))
595
	return status;
596

            
597
519
    stroker->open_sub_path = FALSE;
598

            
599
519
    if (stroker->dash.dashed)
600
96
	status = _cairo_rectilinear_stroker_emit_segments_dashed (stroker);
601
    else
602
423
	status = _cairo_rectilinear_stroker_emit_segments (stroker);
603
519
    if (unlikely (status))
604
	return status;
605

            
606
519
    return CAIRO_STATUS_SUCCESS;
607
}
608

            
609
cairo_int_status_t
610
2997
_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
2997
    assert (_cairo_path_fixed_stroke_is_rectilinear (path));
621

            
622
2997
    if (! _cairo_rectilinear_stroker_init (&rectilinear_stroker,
623
					   stroke_style, ctm, antialias,
624
					   boxes))
625
    {
626
561
	return CAIRO_INT_STATUS_UNSUPPORTED;
627
    }
628

            
629
4332
    if (! rectilinear_stroker.dash.dashed &&
630
1896
	_cairo_path_fixed_is_stroke_box (path, &box) &&
631
	/* if the segments overlap we need to feed them into the tessellator */
632
279
	box.p2.x - box.p1.x > 2* rectilinear_stroker.half_line_x &&
633
243
	box.p2.y - box.p1.y > 2* rectilinear_stroker.half_line_y)
634
    {
635
	cairo_box_t b;
636

            
637
	/* top */
638
243
	b.p1.x = box.p1.x - rectilinear_stroker.half_line_x;
639
243
	b.p2.x = box.p2.x + rectilinear_stroker.half_line_x;
640
243
	b.p1.y = box.p1.y - rectilinear_stroker.half_line_y;
641
243
	b.p2.y = box.p1.y + rectilinear_stroker.half_line_y;
642
243
	status = _cairo_boxes_add (boxes, antialias, &b);
643
243
	assert (status == CAIRO_INT_STATUS_SUCCESS);
644

            
645
	/* left  (excluding top/bottom) */
646
243
	b.p1.x = box.p1.x - rectilinear_stroker.half_line_x;
647
243
	b.p2.x = box.p1.x + rectilinear_stroker.half_line_x;
648
243
	b.p1.y = box.p1.y + rectilinear_stroker.half_line_y;
649
243
	b.p2.y = box.p2.y - rectilinear_stroker.half_line_y;
650
243
	status = _cairo_boxes_add (boxes, antialias, &b);
651
243
	assert (status == CAIRO_INT_STATUS_SUCCESS);
652

            
653
	/* right  (excluding top/bottom) */
654
243
	b.p1.x = box.p2.x - rectilinear_stroker.half_line_x;
655
243
	b.p2.x = box.p2.x + rectilinear_stroker.half_line_x;
656
243
	b.p1.y = box.p1.y + rectilinear_stroker.half_line_y;
657
243
	b.p2.y = box.p2.y - rectilinear_stroker.half_line_y;
658
243
	status = _cairo_boxes_add (boxes, antialias, &b);
659
243
	assert (status == CAIRO_INT_STATUS_SUCCESS);
660

            
661
	/* bottom */
662
243
	b.p1.x = box.p1.x - rectilinear_stroker.half_line_x;
663
243
	b.p2.x = box.p2.x + rectilinear_stroker.half_line_x;
664
243
	b.p1.y = box.p2.y - rectilinear_stroker.half_line_y;
665
243
	b.p2.y = box.p2.y + rectilinear_stroker.half_line_y;
666
243
	status = _cairo_boxes_add (boxes, antialias, &b);
667
243
	assert (status == CAIRO_INT_STATUS_SUCCESS);
668

            
669
243
	goto done;
670
    }
671

            
672
2193
    if (boxes->num_limits) {
673
1206
	_cairo_rectilinear_stroker_limit (&rectilinear_stroker,
674
					  boxes->limits,
675
					  boxes->num_limits);
676
    }
677

            
678
2193
    status = _cairo_path_fixed_interpret (path,
679
					  _cairo_rectilinear_stroker_move_to,
680
2193
					  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
2193
    if (unlikely (status))
687
	goto BAIL;
688

            
689
2193
    if (rectilinear_stroker.dash.dashed)
690
540
	status = _cairo_rectilinear_stroker_emit_segments_dashed (&rectilinear_stroker);
691
    else
692
1653
	status = _cairo_rectilinear_stroker_emit_segments (&rectilinear_stroker);
693
2193
    if (unlikely (status))
694
	goto BAIL;
695

            
696
    /* As we incrementally tessellate, we do not eliminate self-intersections */
697
2193
    status = _cairo_bentley_ottmann_tessellate_boxes (boxes,
698
						      CAIRO_FILL_RULE_WINDING,
699
						      boxes);
700
2193
    if (unlikely (status))
701
	goto BAIL;
702

            
703
2193
done:
704
2436
    _cairo_rectilinear_stroker_fini (&rectilinear_stroker);
705
2436
    return CAIRO_STATUS_SUCCESS;
706

            
707
BAIL:
708
    _cairo_rectilinear_stroker_fini (&rectilinear_stroker);
709
    _cairo_boxes_clear (boxes);
710
    return status;
711
}