Subversion Repositories Kolibri OS

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
4349 Serge 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 
36
 *	Chris Wilson 
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
}