[cairo-commit] 3 commits - src/cairoint.h src/cairo-path-fill.c src/cairo-path-stroke-boxes.c src/cairo-path-stroke.c src/cairo-stroke-dash.c src/cairo-stroke-dash-private.h src/cairo-surface-fallback.c src/Makefile.sources test/line-width.c test/line-width-overlap.c test/line-width-overlap-flipped.ref.png test/line-width-overlap-flopped.ref.png test/line-width-overlap-offset.ref.png test/line-width-overlap-rotated.ref.png test/Makefile.refs

Chris Wilson ickle at kemper.freedesktop.org
Sat Aug 13 08:44:42 PDT 2011


 src/Makefile.sources                    |    3 
 src/cairo-path-fill.c                   |  245 ---------
 src/cairo-path-stroke-boxes.c           |  658 +++++++++++++++++++++++++
 src/cairo-path-stroke.c                 |  812 --------------------------------
 src/cairo-stroke-dash-private.h         |   70 ++
 src/cairo-stroke-dash.c                 |   96 +++
 src/cairo-surface-fallback.c            |  116 ++--
 src/cairoint.h                          |   13 
 test/Makefile.refs                      |    4 
 test/line-width-overlap-flipped.ref.png |binary
 test/line-width-overlap-flopped.ref.png |binary
 test/line-width-overlap-offset.ref.png  |binary
 test/line-width-overlap-rotated.ref.png |binary
 test/line-width-overlap.c               |   81 +++
 test/line-width.c                       |   27 -
 15 files changed, 970 insertions(+), 1155 deletions(-)

New commits:
commit ba406866be320c3a344b4e4a8d4bd19f48fa158d
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Sat Aug 13 15:20:03 2011 +0100

    stroke: Rely on the tessellator to remove self-intersections
    
    As handling joins/caps between line segments shorter than
    half_line_width is tricky.
    
    Rather than also fixing the bug in traps, remove that code. The plan is
    to avoiding hitting the traps code, short-circuiting several steps along
    the fast rectangular paths.
    
    Fixes line-width-overlap.
    
    Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>

diff --git a/src/Makefile.sources b/src/Makefile.sources
index 5b116c6..c007d18 100644
--- a/src/Makefile.sources
+++ b/src/Makefile.sources
@@ -96,6 +96,7 @@ cairo_private = \
 	cairo-scaled-font-private.h \
 	cairo-slope-private.h \
 	cairo-spans-private.h \
+	cairo-stroke-dash-private.h \
 	cairo-surface-fallback-private.h \
 	cairo-surface-private.h \
 	cairo-surface-clipper-private.h \
@@ -160,6 +161,7 @@ cairo_sources = \
 	cairo-path-fixed.c \
 	cairo-path-in-fill.c \
 	cairo-path-stroke.c \
+	cairo-path-stroke-boxes.c \
 	cairo-pattern.c \
 	cairo-pen.c \
 	cairo-polygon.c \
@@ -174,6 +176,7 @@ cairo_sources = \
 	cairo-slope.c \
 	cairo-spans.c \
 	cairo-spline.c \
+	cairo-stroke-dash.c \
 	cairo-stroke-style.c \
 	cairo-surface.c \
 	cairo-surface-fallback.c \
diff --git a/src/cairo-path-fill.c b/src/cairo-path-fill.c
index b15b1a4..ce4c86a 100644
--- a/src/cairo-path-fill.c
+++ b/src/cairo-path-fill.c
@@ -235,251 +235,6 @@ _cairo_path_fixed_fill_to_traps (const cairo_path_fixed_t *path,
     return status;
 }
 
-static cairo_region_t *
-_cairo_path_fixed_fill_rectilinear_tessellate_to_region (const cairo_path_fixed_t	*path,
-							 cairo_fill_rule_t	 fill_rule,
-							 const cairo_rectangle_int_t *extents)
-{
-    cairo_box_t box;
-    cairo_polygon_t polygon;
-    cairo_traps_t traps;
-    cairo_status_t status;
-    cairo_region_t *region;
-
-    /* first try to bypass fill-to-polygon */
-    _cairo_traps_init (&traps);
-    status = _cairo_path_fixed_fill_rectilinear_to_traps (path,
-							  fill_rule,
-							  CAIRO_ANTIALIAS_NONE,
-							  &traps);
-    if (_cairo_status_is_error (status))
-	goto CLEANUP_TRAPS;
-
-    if (status == CAIRO_STATUS_SUCCESS) {
-	status = _cairo_traps_extract_region (&traps,
-					      CAIRO_ANTIALIAS_NONE,
-					      &region);
-	goto CLEANUP_TRAPS;
-    }
-
-    /* path is not rectangular, try extracting clipped rectilinear edges */
-    if (extents != NULL) {
-	_cairo_box_from_rectangle (&box, extents);
-	_cairo_polygon_init (&polygon, &box, 1);
-    } else {
-	_cairo_polygon_init (&polygon, NULL, 0);
-    }
-
-    /* tolerance will be ignored as the path is rectilinear */
-    status = _cairo_path_fixed_fill_to_polygon (path, 0., &polygon);
-    if (unlikely (status))
-	goto CLEANUP_POLYGON;
-
-    if (polygon.num_edges == 0) {
-	region = cairo_region_create ();
-    } else {
-	status =
-	    _cairo_bentley_ottmann_tessellate_rectilinear_polygon (&traps,
-								   &polygon,
-								   fill_rule);
-	if (likely (status == CAIRO_STATUS_SUCCESS))
-	    status = _cairo_traps_extract_region (&traps,
-						  CAIRO_ANTIALIAS_NONE,
-						  &region);
-    }
-
-  CLEANUP_POLYGON:
-    _cairo_polygon_fini (&polygon);
-
-  CLEANUP_TRAPS:
-    _cairo_traps_fini (&traps);
-
-    if (unlikely (status))
-	region = _cairo_region_create_in_error (status);
-
-    return region;
-}
-
-/* This special-case filler supports only a path that describes a
- * device-axis aligned rectangle. It exists to avoid the overhead of
- * the general tessellator when drawing very common rectangles.
- *
- * If the path described anything but a device-axis aligned rectangle,
- * this function will abort.
- */
-cairo_region_t *
-_cairo_path_fixed_fill_rectilinear_to_region (const cairo_path_fixed_t	*path,
-					      cairo_fill_rule_t	 fill_rule,
-					      const cairo_rectangle_int_t *extents)
-{
-    cairo_rectangle_int_t rectangle_stack[CAIRO_STACK_ARRAY_LENGTH (cairo_rectangle_int_t)];
-    cairo_box_t box;
-    cairo_region_t *region = NULL;
-
-    assert (_cairo_path_fixed_fill_maybe_region (path));
-    assert (! _cairo_path_fixed_fill_is_empty (path));
-
-    if (_cairo_path_fixed_is_box (path, &box)) {
-	rectangle_stack[0].x = _cairo_fixed_integer_part (box.p1.x);
-	rectangle_stack[0].y = _cairo_fixed_integer_part (box.p1.y);
-	rectangle_stack[0].width = _cairo_fixed_integer_part (box.p2.x) -
-	                            rectangle_stack[0].x;
-	rectangle_stack[0].height = _cairo_fixed_integer_part (box.p2.y) -
-	                            rectangle_stack[0].y;
-	if (! _cairo_rectangle_intersect (&rectangle_stack[0], extents))
-	    region = cairo_region_create ();
-	else
-	    region = cairo_region_create_rectangle (&rectangle_stack[0]);
-    } else if (fill_rule == CAIRO_FILL_RULE_WINDING) {
-	cairo_rectangle_int_t *rects = rectangle_stack;
-	cairo_path_fixed_iter_t iter;
-	int last_cw = -1;
-	int size = ARRAY_LENGTH (rectangle_stack);
-	int count = 0;
-
-	/* Support a series of rectangles as can be expected to describe a
-	 * GdkRegion clip region during exposes.
-	 */
-	_cairo_path_fixed_iter_init (&iter, path);
-	while (_cairo_path_fixed_iter_is_fill_box (&iter, &box)) {
-	    int cw = 0;
-
-	    if (box.p1.x > box.p2.x) {
-		cairo_fixed_t t;
-
-		t = box.p1.x;
-		box.p1.x = box.p2.x;
-		box.p2.x = t;
-
-		cw = ! cw;
-	    }
-
-	    if (box.p1.y > box.p2.y) {
-		cairo_fixed_t t;
-
-		t = box.p1.y;
-		box.p1.y = box.p2.y;
-		box.p2.y = t;
-
-		cw = ! cw;
-	    }
-
-	    if (last_cw < 0)
-		last_cw = cw;
-	    else if (last_cw != cw)
-		goto TESSELLATE;
-
-	    if (count == size) {
-		cairo_rectangle_int_t *new_rects;
-
-		size *= 4;
-		if (rects == rectangle_stack) {
-		    new_rects = _cairo_malloc_ab (size,
-						  sizeof (cairo_rectangle_int_t));
-		    if (unlikely (new_rects == NULL)) {
-			/* XXX _cairo_region_nil */
-			break;
-		    }
-		    memcpy (new_rects, rects, sizeof (rectangle_stack));
-		} else {
-		    new_rects = _cairo_realloc_ab (rects, size,
-						   sizeof (cairo_rectangle_int_t));
-		    if (unlikely (new_rects == NULL)) {
-			/* XXX _cairo_region_nil */
-			break;
-		    }
-		}
-		rects = new_rects;
-	    }
-
-	    rects[count].x = _cairo_fixed_integer_part (box.p1.x);
-	    rects[count].y = _cairo_fixed_integer_part (box.p1.y);
-	    rects[count].width = _cairo_fixed_integer_part (box.p2.x) - rects[count].x;
-	    rects[count].height = _cairo_fixed_integer_part (box.p2.y) - rects[count].y;
-	    if (_cairo_rectangle_intersect (&rects[count], extents))
-		count++;
-	}
-
-	if (_cairo_path_fixed_iter_at_end (&iter))
-	    region = cairo_region_create_rectangles (rects, count);
-
-TESSELLATE:
-	if (rects != rectangle_stack)
-	    free (rects);
-    }
-
-    if (region == NULL) {
-	/* Hmm, complex polygon */
-	region = _cairo_path_fixed_fill_rectilinear_tessellate_to_region (path,
-									  fill_rule,
-									  extents);
-
-
-    }
-
-    return region;
-}
-
-cairo_int_status_t
-_cairo_path_fixed_fill_rectilinear_to_traps (const cairo_path_fixed_t *path,
-					     cairo_fill_rule_t fill_rule,
-					     cairo_antialias_t antialias,
-					     cairo_traps_t *traps)
-{
-    cairo_box_t box;
-    cairo_status_t status;
-
-    traps->is_rectilinear = TRUE;
-    traps->is_rectangular = TRUE;
-
-    if (_cairo_path_fixed_is_box (path, &box)) {
-	if (antialias == CAIRO_ANTIALIAS_NONE) {
-	    box.p1.x = _cairo_fixed_round_down (box.p1.x);
-	    box.p1.y = _cairo_fixed_round_down (box.p1.y);
-	    box.p2.x = _cairo_fixed_round_down (box.p2.x);
-	    box.p2.y = _cairo_fixed_round_down (box.p2.y);
-	}
-	return _cairo_traps_tessellate_rectangle (traps, &box.p1, &box.p2);
-    } else {
-	cairo_path_fixed_iter_t iter;
-
-	_cairo_path_fixed_iter_init (&iter, path);
-	while (_cairo_path_fixed_iter_is_fill_box (&iter, &box)) {
-	    if (box.p1.y > box.p2.y) {
-		cairo_fixed_t t;
-
-		t = box.p1.y;
-		box.p1.y = box.p2.y;
-		box.p2.y = t;
-
-		t = box.p1.x;
-		box.p1.x = box.p2.x;
-		box.p2.x = t;
-	    }
-
-	    if (antialias == CAIRO_ANTIALIAS_NONE) {
-		box.p1.x = _cairo_fixed_round_down (box.p1.x);
-		box.p1.y = _cairo_fixed_round_down (box.p1.y);
-		box.p2.x = _cairo_fixed_round_down (box.p2.x);
-		box.p2.y = _cairo_fixed_round_down (box.p2.y);
-	    }
-
-	    status = _cairo_traps_tessellate_rectangle (traps,
-							&box.p1, &box.p2);
-	    if (unlikely (status)) {
-		_cairo_traps_clear (traps);
-		return status;
-	    }
-	}
-
-	if (_cairo_path_fixed_iter_at_end (&iter))
-	    return _cairo_bentley_ottmann_tessellate_rectangular_traps (traps, fill_rule);
-
-	_cairo_traps_clear (traps);
-	return CAIRO_INT_STATUS_UNSUPPORTED;
-    }
-}
-
 static cairo_status_t
 _cairo_path_fixed_fill_rectilinear_tessellate_to_boxes (const cairo_path_fixed_t *path,
 							cairo_fill_rule_t fill_rule,
diff --git a/src/cairo-path-stroke-boxes.c b/src/cairo-path-stroke-boxes.c
new file mode 100644
index 0000000..de3a628
--- /dev/null
+++ b/src/cairo-path-stroke-boxes.c
@@ -0,0 +1,658 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2002 University of Southern California
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ *	Carl D. Worth <cworth at cworth.org>
+ *	Chris Wilson <chris at chris-wilson.co.uk>
+ */
+
+#define _BSD_SOURCE /* for hypot() */
+#include "cairoint.h"
+
+#include "cairo-box-private.h"
+#include "cairo-boxes-private.h"
+#include "cairo-error-private.h"
+#include "cairo-path-fixed-private.h"
+#include "cairo-slope-private.h"
+#include "cairo-stroke-dash-private.h"
+
+typedef struct _segment_t {
+    cairo_point_t p1, p2;
+    cairo_bool_t is_horizontal;
+    cairo_bool_t has_join;
+} segment_t;
+
+typedef struct _cairo_rectilinear_stroker {
+    const cairo_stroke_style_t *stroke_style;
+    const cairo_matrix_t *ctm;
+    cairo_antialias_t antialias;
+
+    cairo_fixed_t half_line_width;
+    cairo_boxes_t *boxes;
+    cairo_point_t current_point;
+    cairo_point_t first_point;
+    cairo_bool_t open_sub_path;
+
+    cairo_stroker_dash_t dash;
+
+    cairo_bool_t has_bounds;
+    cairo_box_t bounds;
+
+    int num_segments;
+    int segments_size;
+    segment_t *segments;
+    segment_t segments_embedded[8]; /* common case is a single rectangle */
+} cairo_rectilinear_stroker_t;
+
+static void
+_cairo_rectilinear_stroker_limit (cairo_rectilinear_stroker_t *stroker,
+				  const cairo_box_t *boxes,
+				  int num_boxes)
+{
+    stroker->has_bounds = TRUE;
+    _cairo_boxes_get_extents (boxes, num_boxes, &stroker->bounds);
+
+    stroker->bounds.p1.x -= stroker->half_line_width;
+    stroker->bounds.p2.x += stroker->half_line_width;
+
+    stroker->bounds.p1.y -= stroker->half_line_width;
+    stroker->bounds.p2.y += stroker->half_line_width;
+}
+
+static cairo_bool_t
+_cairo_rectilinear_stroker_init (cairo_rectilinear_stroker_t	*stroker,
+				 const cairo_stroke_style_t	*stroke_style,
+				 const cairo_matrix_t		*ctm,
+				 cairo_antialias_t		 antialias,
+				 cairo_boxes_t			*boxes)
+{
+    /* This special-case rectilinear stroker only supports
+     * miter-joined lines (not curves) and a translation-only matrix
+     * (though it could probably be extended to support a matrix with
+     * uniform, integer scaling).
+     *
+     * It also only supports horizontal and vertical line_to
+     * elements. But we don't catch that here, but instead return
+     * UNSUPPORTED from _cairo_rectilinear_stroker_line_to if any
+     * non-rectilinear line_to is encountered.
+     */
+    if (stroke_style->line_join	!= CAIRO_LINE_JOIN_MITER)
+	return FALSE;
+
+    /* If the miter limit turns right angles into bevels, then we
+     * can't use this optimization. Remember, the ratio is
+     * 1/sin(ɸ/2). So the cutoff is 1/sin(π/4.0) or ⎷2,
+     * which we round for safety. */
+    if (stroke_style->miter_limit < M_SQRT2)
+	return FALSE;
+
+    if (! (stroke_style->line_cap == CAIRO_LINE_CAP_BUTT ||
+	   stroke_style->line_cap == CAIRO_LINE_CAP_SQUARE))
+    {
+	return FALSE;
+    }
+
+    if (! _cairo_matrix_has_unity_scale (ctm))
+	return FALSE;
+
+    stroker->stroke_style = stroke_style;
+    stroker->ctm = ctm;
+    stroker->antialias = antialias;
+
+    stroker->half_line_width =
+	_cairo_fixed_from_double (stroke_style->line_width / 2.0);
+    stroker->open_sub_path = FALSE;
+    stroker->segments = stroker->segments_embedded;
+    stroker->segments_size = ARRAY_LENGTH (stroker->segments_embedded);
+    stroker->num_segments = 0;
+
+    _cairo_stroker_dash_init (&stroker->dash, stroke_style);
+
+    stroker->has_bounds = FALSE;
+
+    stroker->boxes = boxes;
+
+    return TRUE;
+}
+
+static void
+_cairo_rectilinear_stroker_fini (cairo_rectilinear_stroker_t	*stroker)
+{
+    if (stroker->segments != stroker->segments_embedded)
+	free (stroker->segments);
+}
+
+static cairo_status_t
+_cairo_rectilinear_stroker_add_segment (cairo_rectilinear_stroker_t *stroker,
+					const cairo_point_t	*p1,
+					const cairo_point_t	*p2,
+					cairo_bool_t		 is_horizontal,
+					cairo_bool_t		 has_join)
+{
+    if (CAIRO_INJECT_FAULT ())
+	return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    if (stroker->num_segments == stroker->segments_size) {
+	int new_size = stroker->segments_size * 2;
+	segment_t *new_segments;
+
+	if (stroker->segments == stroker->segments_embedded) {
+	    new_segments = _cairo_malloc_ab (new_size, sizeof (segment_t));
+	    if (unlikely (new_segments == NULL))
+		return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+	    memcpy (new_segments, stroker->segments,
+		    stroker->num_segments * sizeof (segment_t));
+	} else {
+	    new_segments = _cairo_realloc_ab (stroker->segments,
+					      new_size, sizeof (segment_t));
+	    if (unlikely (new_segments == NULL))
+		return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+	}
+
+	stroker->segments_size = new_size;
+	stroker->segments = new_segments;
+    }
+
+    stroker->segments[stroker->num_segments].p1 = *p1;
+    stroker->segments[stroker->num_segments].p2 = *p2;
+    stroker->segments[stroker->num_segments].has_join = has_join;
+    stroker->segments[stroker->num_segments].is_horizontal = is_horizontal;
+    stroker->num_segments++;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_rectilinear_stroker_emit_segments (cairo_rectilinear_stroker_t *stroker)
+{
+    cairo_line_cap_t line_cap = stroker->stroke_style->line_cap;
+    cairo_fixed_t half_line_width = stroker->half_line_width;
+    cairo_status_t status;
+    int i;
+
+    /* For each segment we generate a single rectangle.
+     * This rectangle is based on a perpendicular extension (by half the
+     * line width) of the segment endpoints * after some adjustments of the
+     * endpoints to account for caps and joins.
+     */
+    for (i = 0; i < stroker->num_segments; i++) {
+	cairo_bool_t lengthen_initial, lengthen_final;
+	cairo_point_t *a, *b;
+	cairo_box_t box;
+
+	a = &stroker->segments[i].p1;
+	b = &stroker->segments[i].p2;
+
+	/* We adjust the initial point of the segment to extend the
+	 * rectangle to include the previous cap or join, (this
+	 * adjustment applies to all segments except for the first
+	 * segment of open, butt-capped paths).
+	 *
+	 * Overlapping segments will be eliminated by the tessellation.
+	 * Ideally, we would not emit these self-intersections at all,
+	 * but that is tricky with segments shorter than half_line_width.
+	 */
+	lengthen_initial = TRUE;
+	lengthen_final = TRUE;
+	if (stroker->open_sub_path && line_cap == CAIRO_LINE_CAP_BUTT) {
+	    if (i == 0)
+		lengthen_initial = FALSE;
+
+	    if (i == stroker->num_segments - 1)
+		lengthen_final = FALSE;
+	}
+
+	/* Perform the adjustments of the endpoints. */
+	if (a->y == b->y) {
+	    if (a->x < b->x) {
+		if (lengthen_initial)
+		    a->x -= half_line_width;
+		if (lengthen_final)
+		    b->x += half_line_width;
+	    } else {
+		if (lengthen_initial)
+		    a->x += half_line_width;
+		if (lengthen_final)
+		    b->x -= half_line_width;
+	    }
+	} else {
+	    if (a->y < b->y) {
+		if (lengthen_initial)
+		    a->y -= half_line_width;
+		if (lengthen_final)
+		    b->y += half_line_width;
+	    } else {
+		if (lengthen_initial)
+		    a->y += half_line_width;
+		if (lengthen_final)
+		    b->y -= half_line_width;
+	    }
+	}
+
+	/* Form the rectangle by expanding by half the line width in
+	 * either perpendicular direction. */
+	if (a->y == b->y) {
+	    a->y -= half_line_width;
+	    b->y += half_line_width;
+	} else {
+	    a->x -= half_line_width;
+	    b->x += half_line_width;
+	}
+
+	if (a->x < b->x) {
+	    box.p1.x = a->x;
+	    box.p2.x = b->x;
+	} else {
+	    box.p1.x = b->x;
+	    box.p2.x = a->x;
+	}
+	if (a->y < b->y) {
+	    box.p1.y = a->y;
+	    box.p2.y = b->y;
+	} else {
+	    box.p1.y = b->y;
+	    box.p2.y = a->y;
+	}
+
+	status = _cairo_boxes_add (stroker->boxes, stroker->antialias, &box);
+	if (unlikely (status))
+	    return status;
+    }
+
+    stroker->num_segments = 0;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_rectilinear_stroker_emit_segments_dashed (cairo_rectilinear_stroker_t *stroker)
+{
+    cairo_status_t status;
+    cairo_line_cap_t line_cap = stroker->stroke_style->line_cap;
+    cairo_fixed_t half_line_width = stroker->half_line_width;
+    int i;
+
+    for (i = 0; i < stroker->num_segments; i++) {
+	cairo_point_t *a, *b;
+	cairo_bool_t is_horizontal;
+	cairo_box_t box;
+
+	a = &stroker->segments[i].p1;
+	b = &stroker->segments[i].p2;
+
+	is_horizontal = stroker->segments[i].is_horizontal;
+
+	/* Handle the joins for a potentially degenerate segment. */
+	if (line_cap == CAIRO_LINE_CAP_BUTT &&
+	    stroker->segments[i].has_join &&
+	    (i != stroker->num_segments - 1 ||
+	     (! stroker->open_sub_path && stroker->dash.dash_starts_on)))
+	{
+	    cairo_slope_t out_slope;
+	    int j = (i + 1) % stroker->num_segments;
+
+	    box.p1 = stroker->segments[i].p1;
+	    box.p2 = stroker->segments[i].p2;
+	    _cairo_slope_init (&out_slope,
+			       &stroker->segments[j].p1,
+			       &stroker->segments[j].p2);
+
+	    if (is_horizontal) {
+		if (box.p1.x <= box.p2.x) {
+		    box.p1.x = box.p2.x;
+		    box.p2.x += half_line_width;
+		} else {
+		    box.p1.x = box.p2.x - half_line_width;
+		}
+		if (out_slope.dy >= 0)
+		    box.p1.y -= half_line_width;
+		if (out_slope.dy <= 0)
+		    box.p2.y += half_line_width;
+	    } else {
+		if (box.p1.y <= box.p2.y) {
+		    box.p1.y = box.p2.y;
+		    box.p2.y += half_line_width;
+		} else {
+		    box.p1.y = box.p2.y - half_line_width;
+		}
+		if (out_slope.dx >= 0)
+		    box.p1.x -= half_line_width;
+		if (out_slope.dx <= 0)
+		    box.p2.x += half_line_width;
+	    }
+
+	    status = _cairo_boxes_add (stroker->boxes, stroker->antialias, &box);
+	    if (unlikely (status))
+		return status;
+	}
+
+	/* Perform the adjustments of the endpoints. */
+	if (is_horizontal) {
+	    if (line_cap == CAIRO_LINE_CAP_SQUARE) {
+		if (a->x <= b->x) {
+		    a->x -= half_line_width;
+		    b->x += half_line_width;
+		} else {
+		    a->x += half_line_width;
+		    b->x -= half_line_width;
+		}
+	    }
+
+	    a->y += half_line_width;
+	    b->y -= half_line_width;
+	} else {
+	    if (line_cap == CAIRO_LINE_CAP_SQUARE) {
+		if (a->y <= b->y) {
+		    a->y -= half_line_width;
+		    b->y += half_line_width;
+		} else {
+		    a->y += half_line_width;
+		    b->y -= half_line_width;
+		}
+	    }
+
+	    a->x += half_line_width;
+	    b->x -= half_line_width;
+	}
+
+	if (a->x == b->x && a->y == b->y)
+	    continue;
+
+	if (a->x < b->x) {
+	    box.p1.x = a->x;
+	    box.p2.x = b->x;
+	} else {
+	    box.p1.x = b->x;
+	    box.p2.x = a->x;
+	}
+	if (a->y < b->y) {
+	    box.p1.y = a->y;
+	    box.p2.y = b->y;
+	} else {
+	    box.p1.y = b->y;
+	    box.p2.y = a->y;
+	}
+
+	status = _cairo_boxes_add (stroker->boxes, stroker->antialias, &box);
+	if (unlikely (status))
+	    return status;
+    }
+
+    stroker->num_segments = 0;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_rectilinear_stroker_move_to (void		*closure,
+				    const cairo_point_t	*point)
+{
+    cairo_rectilinear_stroker_t *stroker = closure;
+    cairo_status_t status;
+
+    if (stroker->dash.dashed)
+	status = _cairo_rectilinear_stroker_emit_segments_dashed (stroker);
+    else
+	status = _cairo_rectilinear_stroker_emit_segments (stroker);
+    if (unlikely (status))
+	return status;
+
+    /* reset the dash pattern for new sub paths */
+    _cairo_stroker_dash_start (&stroker->dash);
+
+    stroker->current_point = *point;
+    stroker->first_point = *point;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_rectilinear_stroker_line_to (void		*closure,
+				    const cairo_point_t	*b)
+{
+    cairo_rectilinear_stroker_t *stroker = closure;
+    cairo_point_t *a = &stroker->current_point;
+    cairo_status_t status;
+
+    /* We only support horizontal or vertical elements. */
+    assert (a->x == b->x || a->y == b->y);
+
+    /* We don't draw anything for degenerate paths. */
+    if (a->x == b->x && a->y == b->y)
+	return CAIRO_STATUS_SUCCESS;
+
+    status = _cairo_rectilinear_stroker_add_segment (stroker, a, b,
+						     a->y == b->y,
+						     TRUE);
+
+    stroker->current_point = *b;
+    stroker->open_sub_path = TRUE;
+
+    return status;
+}
+
+static cairo_status_t
+_cairo_rectilinear_stroker_line_to_dashed (void		*closure,
+					   const cairo_point_t	*point)
+{
+    cairo_rectilinear_stroker_t *stroker = closure;
+    const cairo_point_t *a = &stroker->current_point;
+    const cairo_point_t *b = point;
+    cairo_bool_t fully_in_bounds;
+    double sign, remain;
+    cairo_fixed_t mag;
+    cairo_status_t status;
+    cairo_line_t segment;
+    cairo_bool_t dash_on = FALSE;
+    cairo_bool_t is_horizontal;
+
+    /* We don't draw anything for degenerate paths. */
+    if (a->x == b->x && a->y == b->y)
+	return CAIRO_STATUS_SUCCESS;
+
+    /* We only support horizontal or vertical elements. */
+    assert (a->x == b->x || a->y == b->y);
+
+    fully_in_bounds = TRUE;
+    if (stroker->has_bounds &&
+	(! _cairo_box_contains_point (&stroker->bounds, a) ||
+	 ! _cairo_box_contains_point (&stroker->bounds, b)))
+    {
+	fully_in_bounds = FALSE;
+    }
+
+    is_horizontal = a->y == b->y;
+    if (is_horizontal)
+	mag = b->x - a->x;
+    else
+	mag = b->y - a->y;
+    if (mag < 0) {
+	remain = _cairo_fixed_to_double (-mag);
+	sign = 1.;
+    } else {
+	remain = _cairo_fixed_to_double (mag);
+	sign = -1.;
+    }
+
+    segment.p2 = segment.p1 = *a;
+    while (remain > 0.) {
+	double step_length;
+
+	step_length = MIN (stroker->dash.dash_remain, remain);
+	remain -= step_length;
+
+	mag = _cairo_fixed_from_double (sign*remain);
+	if (is_horizontal)
+	    segment.p2.x = b->x + mag;
+	else
+	    segment.p2.y = b->y + mag;
+
+	if (stroker->dash.dash_on &&
+	    (fully_in_bounds ||
+	     _cairo_box_intersects_line_segment (&stroker->bounds, &segment)))
+	{
+	    status = _cairo_rectilinear_stroker_add_segment (stroker,
+							     &segment.p1,
+							     &segment.p2,
+							     is_horizontal,
+							     remain <= 0.);
+	    if (unlikely (status))
+		return status;
+
+	    dash_on = TRUE;
+	}
+	else
+	{
+	    dash_on = FALSE;
+	}
+
+	_cairo_stroker_dash_step (&stroker->dash, step_length);
+	segment.p1 = segment.p2;
+    }
+
+    if (stroker->dash.dash_on && ! dash_on &&
+	(fully_in_bounds ||
+	 _cairo_box_intersects_line_segment (&stroker->bounds, &segment)))
+    {
+
+	/* This segment ends on a transition to dash_on, compute a new face
+	 * and add cap for the beginning of the next dash_on step.
+	 */
+
+	status = _cairo_rectilinear_stroker_add_segment (stroker,
+							 &segment.p1,
+							 &segment.p1,
+							 is_horizontal,
+							 TRUE);
+	if (unlikely (status))
+	    return status;
+    }
+
+    stroker->current_point = *point;
+    stroker->open_sub_path = TRUE;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_rectilinear_stroker_close_path (void *closure)
+{
+    cairo_rectilinear_stroker_t *stroker = closure;
+    cairo_status_t status;
+
+    /* We don't draw anything for degenerate paths. */
+    if (! stroker->open_sub_path)
+	return CAIRO_STATUS_SUCCESS;
+
+    if (stroker->dash.dashed) {
+	status = _cairo_rectilinear_stroker_line_to_dashed (stroker,
+							    &stroker->first_point);
+    } else {
+	status = _cairo_rectilinear_stroker_line_to (stroker,
+						     &stroker->first_point);
+    }
+    if (unlikely (status))
+	return status;
+
+    stroker->open_sub_path = FALSE;
+
+    if (stroker->dash.dashed)
+	status = _cairo_rectilinear_stroker_emit_segments_dashed (stroker);
+    else
+	status = _cairo_rectilinear_stroker_emit_segments (stroker);
+    if (unlikely (status))
+	return status;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_int_status_t
+_cairo_path_fixed_stroke_rectilinear_to_boxes (const cairo_path_fixed_t	*path,
+					       const cairo_stroke_style_t	*stroke_style,
+					       const cairo_matrix_t	*ctm,
+					       cairo_antialias_t	 antialias,
+					       cairo_boxes_t		*boxes)
+{
+    cairo_rectilinear_stroker_t rectilinear_stroker;
+    cairo_int_status_t status;
+
+    assert (_cairo_path_fixed_stroke_is_rectilinear (path));
+
+    if (! _cairo_rectilinear_stroker_init (&rectilinear_stroker,
+					   stroke_style, ctm, antialias,
+					   boxes))
+    {
+	return CAIRO_INT_STATUS_UNSUPPORTED;
+    }
+
+    if (boxes->num_limits) {
+	_cairo_rectilinear_stroker_limit (&rectilinear_stroker,
+					  boxes->limits,
+					  boxes->num_limits);
+    }
+
+    status = _cairo_path_fixed_interpret (path,
+					  _cairo_rectilinear_stroker_move_to,
+					  rectilinear_stroker.dash.dashed ?
+					  _cairo_rectilinear_stroker_line_to_dashed :
+					  _cairo_rectilinear_stroker_line_to,
+					  NULL,
+					  _cairo_rectilinear_stroker_close_path,
+					  &rectilinear_stroker);
+    if (unlikely (status))
+	goto BAIL;
+
+    if (rectilinear_stroker.dash.dashed)
+	status = _cairo_rectilinear_stroker_emit_segments_dashed (&rectilinear_stroker);
+    else
+	status = _cairo_rectilinear_stroker_emit_segments (&rectilinear_stroker);
+    if (unlikely (status))
+	goto BAIL;
+
+    /* As we incrementally tessellate, we do not eliminate self-intersections */
+    status = _cairo_bentley_ottmann_tessellate_boxes (boxes,
+						      CAIRO_FILL_RULE_WINDING,
+						      boxes);
+    if (unlikely (status))
+	goto BAIL;
+
+    _cairo_rectilinear_stroker_fini (&rectilinear_stroker);
+
+    return CAIRO_STATUS_SUCCESS;
+
+BAIL:
+    _cairo_rectilinear_stroker_fini (&rectilinear_stroker);
+    _cairo_boxes_clear (boxes);
+    return status;
+}
diff --git a/src/cairo-path-stroke.c b/src/cairo-path-stroke.c
index 31ce1ac..02f6ba2 100644
--- a/src/cairo-path-stroke.c
+++ b/src/cairo-path-stroke.c
@@ -44,18 +44,7 @@
 #include "cairo-error-private.h"
 #include "cairo-path-fixed-private.h"
 #include "cairo-slope-private.h"
-
-typedef struct _cairo_stroker_dash {
-    cairo_bool_t dashed;
-    unsigned int dash_index;
-    cairo_bool_t dash_on;
-    cairo_bool_t dash_starts_on;
-    double dash_remain;
-
-    double dash_offset;
-    const double *dashes;
-    unsigned int num_dashes;
-} cairo_stroker_dash_t;
+#include "cairo-stroke-dash-private.h"
 
 typedef struct cairo_stroker {
     cairo_stroke_style_t style;
@@ -98,61 +87,6 @@ typedef struct cairo_stroker {
     cairo_box_t bounds;
 } cairo_stroker_t;
 
-static void
-_cairo_stroker_dash_start (cairo_stroker_dash_t *dash)
-{
-    double offset;
-    cairo_bool_t on = TRUE;
-    unsigned int i = 0;
-
-    if (! dash->dashed)
-	return;
-
-    offset = dash->dash_offset;
-
-    /* We stop searching for a starting point as soon as the
-       offset reaches zero.  Otherwise when an initial dash
-       segment shrinks to zero it will be skipped over. */
-    while (offset > 0.0 && offset >= dash->dashes[i]) {
-	offset -= dash->dashes[i];
-	on = !on;
-	if (++i == dash->num_dashes)
-	    i = 0;
-    }
-
-    dash->dash_index = i;
-    dash->dash_on = dash->dash_starts_on = on;
-    dash->dash_remain = dash->dashes[i] - offset;
-}
-
-static void
-_cairo_stroker_dash_step (cairo_stroker_dash_t *dash, double step)
-{
-    dash->dash_remain -= step;
-    if (dash->dash_remain <= 0.) {
-	if (++dash->dash_index == dash->num_dashes)
-	    dash->dash_index = 0;
-
-	dash->dash_on = ! dash->dash_on;
-	dash->dash_remain = dash->dashes[dash->dash_index];
-    }
-}
-
-static void
-_cairo_stroker_dash_init (cairo_stroker_dash_t *dash,
-			  const cairo_stroke_style_t *style)
-{
-    dash->dashed = style->dash != NULL;
-    if (! dash->dashed)
-	return;
-
-    dash->dashes = style->dash;
-    dash->num_dashes = style->num_dashes;
-    dash->dash_offset = style->dash_offset;
-
-    _cairo_stroker_dash_start (dash);
-}
-
 static cairo_status_t
 _cairo_stroker_init (cairo_stroker_t		*stroker,
 		     const cairo_stroke_style_t	*stroke_style,
@@ -1393,21 +1327,6 @@ _cairo_path_fixed_stroke_to_traps (const cairo_path_fixed_t	*path,
     cairo_int_status_t status;
     cairo_polygon_t polygon;
 
-    /* Before we do anything else, we attempt the rectilinear
-     * stroker. It's careful to generate trapezoids that align to
-     * device-pixel boundaries when possible. Many backends can render
-     * those much faster than non-aligned trapezoids, (by using clip
-     * regions, etc.) */
-    if (_cairo_path_fixed_stroke_is_rectilinear (path)) {
-	status = _cairo_path_fixed_stroke_rectilinear_to_traps (path,
-								stroke_style,
-								ctm,
-								CAIRO_ANTIALIAS_DEFAULT,
-								traps);
-	if (status != CAIRO_INT_STATUS_UNSUPPORTED)
-	    return status;
-    }
-
     _cairo_polygon_init (&polygon, traps->limits, traps->num_limits);
 
     status = _cairo_path_fixed_stroke_to_polygon (path,
@@ -1431,732 +1350,3 @@ BAIL:
 
     return status;
 }
-
-typedef struct _segment_t {
-    cairo_point_t p1, p2;
-    cairo_bool_t is_horizontal;
-    cairo_bool_t has_join;
-} segment_t;
-
-typedef struct _cairo_rectilinear_stroker {
-    const cairo_stroke_style_t *stroke_style;
-    const cairo_matrix_t *ctm;
-    cairo_antialias_t antialias;
-
-    cairo_fixed_t half_line_width;
-    cairo_bool_t do_traps;
-    void *container;
-    cairo_point_t current_point;
-    cairo_point_t first_point;
-    cairo_bool_t open_sub_path;
-
-    cairo_stroker_dash_t dash;
-
-    cairo_bool_t has_bounds;
-    cairo_box_t bounds;
-
-    int num_segments;
-    int segments_size;
-    segment_t *segments;
-    segment_t segments_embedded[8]; /* common case is a single rectangle */
-} cairo_rectilinear_stroker_t;
-
-static void
-_cairo_rectilinear_stroker_limit (cairo_rectilinear_stroker_t *stroker,
-				  const cairo_box_t *boxes,
-				  int num_boxes)
-{
-    stroker->has_bounds = TRUE;
-    _cairo_boxes_get_extents (boxes, num_boxes, &stroker->bounds);
-
-    stroker->bounds.p1.x -= stroker->half_line_width;
-    stroker->bounds.p2.x += stroker->half_line_width;
-
-    stroker->bounds.p1.y -= stroker->half_line_width;
-    stroker->bounds.p2.y += stroker->half_line_width;
-}
-
-static cairo_bool_t
-_cairo_rectilinear_stroker_init (cairo_rectilinear_stroker_t	*stroker,
-				 const cairo_stroke_style_t	*stroke_style,
-				 const cairo_matrix_t		*ctm,
-				 cairo_antialias_t		 antialias,
-				 cairo_bool_t			 do_traps,
-				 void				*container)
-{
-    /* This special-case rectilinear stroker only supports
-     * miter-joined lines (not curves) and a translation-only matrix
-     * (though it could probably be extended to support a matrix with
-     * uniform, integer scaling).
-     *
-     * It also only supports horizontal and vertical line_to
-     * elements. But we don't catch that here, but instead return
-     * UNSUPPORTED from _cairo_rectilinear_stroker_line_to if any
-     * non-rectilinear line_to is encountered.
-     */
-    if (stroke_style->line_join	!= CAIRO_LINE_JOIN_MITER)
-	return FALSE;
-
-    /* If the miter limit turns right angles into bevels, then we
-     * can't use this optimization. Remember, the ratio is
-     * 1/sin(ɸ/2). So the cutoff is 1/sin(π/4.0) or ⎷2,
-     * which we round for safety. */
-    if (stroke_style->miter_limit < M_SQRT2)
-	return FALSE;
-
-    if (! (stroke_style->line_cap == CAIRO_LINE_CAP_BUTT ||
-	   stroke_style->line_cap == CAIRO_LINE_CAP_SQUARE))
-    {
-	return FALSE;
-    }
-
-    if (! _cairo_matrix_has_unity_scale (ctm))
-	return FALSE;
-
-    stroker->stroke_style = stroke_style;
-    stroker->ctm = ctm;
-    stroker->antialias = antialias;
-
-    stroker->half_line_width =
-	_cairo_fixed_from_double (stroke_style->line_width / 2.0);
-    stroker->open_sub_path = FALSE;
-    stroker->segments = stroker->segments_embedded;
-    stroker->segments_size = ARRAY_LENGTH (stroker->segments_embedded);
-    stroker->num_segments = 0;
-
-    _cairo_stroker_dash_init (&stroker->dash, stroke_style);
-
-    stroker->has_bounds = FALSE;
-
-    stroker->do_traps = do_traps;
-    stroker->container = container;
-
-    return TRUE;
-}
-
-static void
-_cairo_rectilinear_stroker_fini (cairo_rectilinear_stroker_t	*stroker)
-{
-    if (stroker->segments != stroker->segments_embedded)
-	free (stroker->segments);
-}
-
-static cairo_status_t
-_cairo_rectilinear_stroker_add_segment (cairo_rectilinear_stroker_t *stroker,
-					const cairo_point_t	*p1,
-					const cairo_point_t	*p2,
-					cairo_bool_t		 is_horizontal,
-					cairo_bool_t		 has_join)
-{
-    if (CAIRO_INJECT_FAULT ())
-	return _cairo_error (CAIRO_STATUS_NO_MEMORY);
-
-    if (stroker->num_segments == stroker->segments_size) {
-	int new_size = stroker->segments_size * 2;
-	segment_t *new_segments;
-
-	if (stroker->segments == stroker->segments_embedded) {
-	    new_segments = _cairo_malloc_ab (new_size, sizeof (segment_t));
-	    if (unlikely (new_segments == NULL))
-		return _cairo_error (CAIRO_STATUS_NO_MEMORY);
-
-	    memcpy (new_segments, stroker->segments,
-		    stroker->num_segments * sizeof (segment_t));
-	} else {
-	    new_segments = _cairo_realloc_ab (stroker->segments,
-					      new_size, sizeof (segment_t));
-	    if (unlikely (new_segments == NULL))
-		return _cairo_error (CAIRO_STATUS_NO_MEMORY);
-	}
-
-	stroker->segments_size = new_size;
-	stroker->segments = new_segments;
-    }
-
-    stroker->segments[stroker->num_segments].p1 = *p1;
-    stroker->segments[stroker->num_segments].p2 = *p2;
-    stroker->segments[stroker->num_segments].has_join = has_join;
-    stroker->segments[stroker->num_segments].is_horizontal = is_horizontal;
-    stroker->num_segments++;
-
-    return CAIRO_STATUS_SUCCESS;
-}
-
-static cairo_status_t
-_cairo_rectilinear_stroker_emit_segments (cairo_rectilinear_stroker_t *stroker)
-{
-    cairo_status_t status;
-    cairo_line_cap_t line_cap = stroker->stroke_style->line_cap;
-    cairo_fixed_t half_line_width = stroker->half_line_width;
-    int i;
-
-    for (i = 0; i < stroker->num_segments; i++) {
-	cairo_point_t *a, *b;
-	cairo_bool_t lengthen_initial, shorten_final, lengthen_final;
-
-	a = &stroker->segments[i].p1;
-	b = &stroker->segments[i].p2;
-
-	/* For each segment we generate a single rectangular
-	 * trapezoid. This rectangle is based on a perpendicular
-	 * extension (by half the line width) of the segment endpoints
-	 * after some adjustments of the endpoints to account for caps
-	 * and joins.
-	 */
-
-	/* We adjust the initial point of the segment to extend the
-	 * rectangle to include the previous cap or join, (this
-	 * adjustment applies to all segments except for the first
-	 * segment of open, butt-capped paths).
-	 */
-	lengthen_initial = TRUE;
-	if (i == 0 && stroker->open_sub_path && line_cap == CAIRO_LINE_CAP_BUTT)
-	    lengthen_initial = FALSE;
-
-	/* The adjustment of the final point is trickier. For all but
-	 * the last segment we shorten the segment at the final
-	 * endpoint to not overlap with the subsequent join. For the
-	 * last segment we do the same shortening if the path is
-	 * closed. If the path is open and butt-capped we do no
-	 * adjustment, while if it's open and square-capped we do a
-	 * lengthening adjustment instead to include the cap.
-	 */
-	shorten_final = TRUE;
-	lengthen_final = FALSE;
-	if (i == stroker->num_segments - 1 && stroker->open_sub_path) {
-	    shorten_final = FALSE;
-	    if (line_cap == CAIRO_LINE_CAP_SQUARE)
-		lengthen_final = TRUE;
-	}
-
-	/* Perform the adjustments of the endpoints. */
-	if (a->y == b->y) {
-	    if (a->x < b->x) {
-		if (lengthen_initial)
-		    a->x -= half_line_width;
-		if (shorten_final)
-		    b->x -= half_line_width;
-		else if (lengthen_final)
-		    b->x += half_line_width;
-	    } else {
-		if (lengthen_initial)
-		    a->x += half_line_width;
-		if (shorten_final)
-		    b->x += half_line_width;
-		else if (lengthen_final)
-		    b->x -= half_line_width;
-	    }
-
-	    if (a->x > b->x) {
-		cairo_point_t *t;
-
-		t = a;
-		a = b;
-		b = t;
-	    }
-	} else {
-	    if (a->y < b->y) {
-		if (lengthen_initial)
-		    a->y -= half_line_width;
-		if (shorten_final)
-		    b->y -= half_line_width;
-		else if (lengthen_final)
-		    b->y += half_line_width;
-	    } else {
-		if (lengthen_initial)
-		    a->y += half_line_width;
-		if (shorten_final)
-		    b->y += half_line_width;
-		else if (lengthen_final)
-		    b->y -= half_line_width;
-	    }
-
-	    if (a->y > b->y) {
-		cairo_point_t *t;
-
-		t = a;
-		a = b;
-		b = t;
-	    }
-	}
-
-	/* Form the rectangle by expanding by half the line width in
-	 * either perpendicular direction. */
-	if (a->y == b->y) {
-	    a->y -= half_line_width;
-	    b->y += half_line_width;
-	} else {
-	    a->x -= half_line_width;
-	    b->x += half_line_width;
-	}
-
-	if (stroker->do_traps) {
-	    if (stroker->antialias == CAIRO_ANTIALIAS_NONE) {
-		a->x = _cairo_fixed_round_down (a->x);
-		a->y = _cairo_fixed_round_down (a->y);
-		b->x = _cairo_fixed_round_down (b->x);
-		b->y = _cairo_fixed_round_down (b->y);
-	    }
-	    status = _cairo_traps_tessellate_rectangle (stroker->container, a, b);
-	} else {
-	    cairo_box_t box;
-
-	    box.p1 = *a;
-	    box.p2 = *b;
-
-	    status = _cairo_boxes_add (stroker->container, stroker->antialias, &box);
-	}
-	if (unlikely (status))
-	    return status;
-    }
-
-    stroker->num_segments = 0;
-
-    return CAIRO_STATUS_SUCCESS;
-}
-
-static cairo_status_t
-_cairo_rectilinear_stroker_emit_segments_dashed (cairo_rectilinear_stroker_t *stroker)
-{
-    cairo_status_t status;
-    cairo_line_cap_t line_cap = stroker->stroke_style->line_cap;
-    cairo_fixed_t half_line_width = stroker->half_line_width;
-    int i;
-
-    for (i = 0; i < stroker->num_segments; i++) {
-	cairo_point_t *a, *b;
-	cairo_bool_t is_horizontal;
-
-	a = &stroker->segments[i].p1;
-	b = &stroker->segments[i].p2;
-
-	is_horizontal = stroker->segments[i].is_horizontal;
-
-	/* Handle the joins for a potentially degenerate segment. */
-	if (line_cap == CAIRO_LINE_CAP_BUTT &&
-	    stroker->segments[i].has_join &&
-	    (i != stroker->num_segments - 1 ||
-	     (! stroker->open_sub_path && stroker->dash.dash_starts_on)))
-	{
-	    cairo_point_t p1 = stroker->segments[i].p1;
-	    cairo_point_t p2 = stroker->segments[i].p2;
-	    cairo_slope_t out_slope;
-	    int j = (i + 1) % stroker->num_segments;
-
-	    _cairo_slope_init (&out_slope,
-			       &stroker->segments[j].p1,
-			       &stroker->segments[j].p2);
-
-	    if (is_horizontal) {
-		if (p1.x <= p2.x) {
-		    p1.x = p2.x;
-		    p2.x += half_line_width;
-		} else {
-		    p1.x = p2.x - half_line_width;
-		}
-		if (out_slope.dy >= 0)
-		    p1.y -= half_line_width;
-		if (out_slope.dy <= 0)
-		    p2.y += half_line_width;
-	    } else {
-		if (p1.y <= p2.y) {
-		    p1.y = p2.y;
-		    p2.y += half_line_width;
-		} else {
-		    p1.y = p2.y - half_line_width;
-		}
-		if (out_slope.dx >= 0)
-		    p1.x -= half_line_width;
-		if (out_slope.dx <= 0)
-		    p2.x += half_line_width;
-	    }
-
-	    if (stroker->do_traps) {
-		if (stroker->antialias == CAIRO_ANTIALIAS_NONE) {
-		    p1.x = _cairo_fixed_round_down (p1.x);
-		    p1.y = _cairo_fixed_round_down (p1.y);
-		    p2.x = _cairo_fixed_round_down (p2.x);
-		    p2.y = _cairo_fixed_round_down (p2.y);
-		}
-		status = _cairo_traps_tessellate_rectangle (stroker->container, &p1, &p2);
-	    } else {
-		cairo_box_t box;
-
-		box.p1 = p1;
-		box.p2 = p2;
-
-		status = _cairo_boxes_add (stroker->container, stroker->antialias, &box);
-	    }
-	    if (unlikely (status))
-		return status;
-	}
-
-	/* Perform the adjustments of the endpoints. */
-	if (is_horizontal) {
-	    if (line_cap == CAIRO_LINE_CAP_SQUARE) {
-		if (a->x <= b->x) {
-		    a->x -= half_line_width;
-		    b->x += half_line_width;
-		} else {
-		    a->x += half_line_width;
-		    b->x -= half_line_width;
-		}
-	    }
-
-	    if (a->x > b->x) {
-		cairo_point_t *t;
-
-		t = a;
-		a = b;
-		b = t;
-	    }
-
-	    a->y -= half_line_width;
-	    b->y += half_line_width;
-	} else {
-	    if (line_cap == CAIRO_LINE_CAP_SQUARE) {
-		if (a->y <= b->y) {
-		    a->y -= half_line_width;
-		    b->y += half_line_width;
-		} else {
-		    a->y += half_line_width;
-		    b->y -= half_line_width;
-		}
-	    }
-
-	    if (a->y > b->y) {
-		cairo_point_t *t;
-
-		t = a;
-		a = b;
-		b = t;
-	    }
-
-	    a->x -= half_line_width;
-	    b->x += half_line_width;
-	}
-
-	if (a->x == b->x && a->y == b->y)
-	    continue;
-
-	if (stroker->do_traps) {
-	    if (stroker->antialias == CAIRO_ANTIALIAS_NONE) {
-		a->x = _cairo_fixed_round_down (a->x);
-		a->y = _cairo_fixed_round_down (a->y);
-		b->x = _cairo_fixed_round_down (b->x);
-		b->y = _cairo_fixed_round_down (b->y);
-	    }
-	    status = _cairo_traps_tessellate_rectangle (stroker->container, a, b);
-	} else {
-	    cairo_box_t box;
-
-	    box.p1 = *a;
-	    box.p2 = *b;
-
-	    status = _cairo_boxes_add (stroker->container, stroker->antialias, &box);
-	}
-	if (unlikely (status))
-	    return status;
-    }
-
-    stroker->num_segments = 0;
-
-    return CAIRO_STATUS_SUCCESS;
-}
-
-static cairo_status_t
-_cairo_rectilinear_stroker_move_to (void		*closure,
-				    const cairo_point_t	*point)
-{
-    cairo_rectilinear_stroker_t *stroker = closure;
-    cairo_status_t status;
-
-    if (stroker->dash.dashed)
-	status = _cairo_rectilinear_stroker_emit_segments_dashed (stroker);
-    else
-	status = _cairo_rectilinear_stroker_emit_segments (stroker);
-    if (unlikely (status))
-	return status;
-
-    /* reset the dash pattern for new sub paths */
-    _cairo_stroker_dash_start (&stroker->dash);
-
-    stroker->current_point = *point;
-    stroker->first_point = *point;
-
-    return CAIRO_STATUS_SUCCESS;
-}
-
-static cairo_status_t
-_cairo_rectilinear_stroker_line_to (void		*closure,
-				    const cairo_point_t	*b)
-{
-    cairo_rectilinear_stroker_t *stroker = closure;
-    cairo_point_t *a = &stroker->current_point;
-    cairo_status_t status;
-
-    /* We only support horizontal or vertical elements. */
-    assert (a->x == b->x || a->y == b->y);
-
-    /* We don't draw anything for degenerate paths. */
-    if (a->x == b->x && a->y == b->y)
-	return CAIRO_STATUS_SUCCESS;
-
-    status = _cairo_rectilinear_stroker_add_segment (stroker, a, b,
-						     a->y == b->y,
-						     TRUE);
-
-    stroker->current_point = *b;
-    stroker->open_sub_path = TRUE;
-
-    return status;
-}
-
-static cairo_status_t
-_cairo_rectilinear_stroker_line_to_dashed (void		*closure,
-					   const cairo_point_t	*point)
-{
-    cairo_rectilinear_stroker_t *stroker = closure;
-    const cairo_point_t *a = &stroker->current_point;
-    const cairo_point_t *b = point;
-    cairo_bool_t fully_in_bounds;
-    double sign, remain;
-    cairo_fixed_t mag;
-    cairo_status_t status;
-    cairo_line_t segment;
-    cairo_bool_t dash_on = FALSE;
-    cairo_bool_t is_horizontal;
-
-    /* We don't draw anything for degenerate paths. */
-    if (a->x == b->x && a->y == b->y)
-	return CAIRO_STATUS_SUCCESS;
-
-    /* We only support horizontal or vertical elements. */
-    assert (a->x == b->x || a->y == b->y);
-
-    fully_in_bounds = TRUE;
-    if (stroker->has_bounds &&
-	(! _cairo_box_contains_point (&stroker->bounds, a) ||
-	 ! _cairo_box_contains_point (&stroker->bounds, b)))
-    {
-	fully_in_bounds = FALSE;
-    }
-
-    is_horizontal = a->y == b->y;
-    if (is_horizontal)
-	mag = b->x - a->x;
-    else
-	mag = b->y - a->y;
-    if (mag < 0) {
-	remain = _cairo_fixed_to_double (-mag);
-	sign = 1.;
-    } else {
-	remain = _cairo_fixed_to_double (mag);
-	sign = -1.;
-    }
-
-    segment.p2 = segment.p1 = *a;
-    while (remain > 0.) {
-	double step_length;
-
-	step_length = MIN (stroker->dash.dash_remain, remain);
-	remain -= step_length;
-
-	mag = _cairo_fixed_from_double (sign*remain);
-	if (is_horizontal)
-	    segment.p2.x = b->x + mag;
-	else
-	    segment.p2.y = b->y + mag;
-
-	if (stroker->dash.dash_on &&
-	    (fully_in_bounds ||
-	     _cairo_box_intersects_line_segment (&stroker->bounds, &segment)))
-	{
-	    status = _cairo_rectilinear_stroker_add_segment (stroker,
-							     &segment.p1,
-							     &segment.p2,
-							     is_horizontal,
-							     remain <= 0.);
-	    if (unlikely (status))
-		return status;
-
-	    dash_on = TRUE;
-	}
-	else
-	{
-	    dash_on = FALSE;
-	}
-
-	_cairo_stroker_dash_step (&stroker->dash, step_length);
-	segment.p1 = segment.p2;
-    }
-
-    if (stroker->dash.dash_on && ! dash_on &&
-	(fully_in_bounds ||
-	 _cairo_box_intersects_line_segment (&stroker->bounds, &segment)))
-    {
-
-	/* This segment ends on a transition to dash_on, compute a new face
-	 * and add cap for the beginning of the next dash_on step.
-	 */
-
-	status = _cairo_rectilinear_stroker_add_segment (stroker,
-							 &segment.p1,
-							 &segment.p1,
-							 is_horizontal,
-							 TRUE);
-	if (unlikely (status))
-	    return status;
-    }
-
-    stroker->current_point = *point;
-    stroker->open_sub_path = TRUE;
-
-    return CAIRO_STATUS_SUCCESS;
-}
-
-static cairo_status_t
-_cairo_rectilinear_stroker_close_path (void *closure)
-{
-    cairo_rectilinear_stroker_t *stroker = closure;
-    cairo_status_t status;
-
-    /* We don't draw anything for degenerate paths. */
-    if (! stroker->open_sub_path)
-	return CAIRO_STATUS_SUCCESS;
-
-    if (stroker->dash.dashed) {
-	status = _cairo_rectilinear_stroker_line_to_dashed (stroker,
-							    &stroker->first_point);
-    } else {
-	status = _cairo_rectilinear_stroker_line_to (stroker,
-						     &stroker->first_point);
-    }
-    if (unlikely (status))
-	return status;
-
-    stroker->open_sub_path = FALSE;
-
-    if (stroker->dash.dashed)
-	status = _cairo_rectilinear_stroker_emit_segments_dashed (stroker);
-    else
-	status = _cairo_rectilinear_stroker_emit_segments (stroker);
-    if (unlikely (status))
-	return status;
-
-    return CAIRO_STATUS_SUCCESS;
-}
-
-cairo_int_status_t
-_cairo_path_fixed_stroke_rectilinear_to_traps (const cairo_path_fixed_t	*path,
-					       const cairo_stroke_style_t	*stroke_style,
-					       const cairo_matrix_t	*ctm,
-					       cairo_antialias_t	 antialias,
-					       cairo_traps_t		*traps)
-{
-    cairo_rectilinear_stroker_t rectilinear_stroker;
-    cairo_int_status_t status;
-
-    assert (_cairo_path_fixed_stroke_is_rectilinear (path));
-
-    if (! _cairo_rectilinear_stroker_init (&rectilinear_stroker,
-					   stroke_style, ctm, antialias,
-					   TRUE, traps))
-    {
-	return CAIRO_INT_STATUS_UNSUPPORTED;
-    }
-
-    if (traps->num_limits) {
-	_cairo_rectilinear_stroker_limit (&rectilinear_stroker,
-					  traps->limits,
-					  traps->num_limits);
-    }
-
-    status = _cairo_path_fixed_interpret (path,
-					  _cairo_rectilinear_stroker_move_to,
-					  rectilinear_stroker.dash.dashed ?
-					  _cairo_rectilinear_stroker_line_to_dashed :
-					  _cairo_rectilinear_stroker_line_to,
-					  NULL,
-					  _cairo_rectilinear_stroker_close_path,
-					  &rectilinear_stroker);
-    if (unlikely (status))
-	goto BAIL;
-
-    if (rectilinear_stroker.dash.dashed)
-	status = _cairo_rectilinear_stroker_emit_segments_dashed (&rectilinear_stroker);
-    else
-	status = _cairo_rectilinear_stroker_emit_segments (&rectilinear_stroker);
-
-    traps->is_rectilinear = 1;
-    traps->is_rectangular = 1;
-    /* As we incrementally tessellate, we do not eliminate self-intersections */
-    traps->has_intersections = traps->num_traps > 1;
-BAIL:
-    _cairo_rectilinear_stroker_fini (&rectilinear_stroker);
-
-    if (unlikely (status))
-	_cairo_traps_clear (traps);
-
-    return status;
-}
-
-cairo_int_status_t
-_cairo_path_fixed_stroke_rectilinear_to_boxes (const cairo_path_fixed_t	*path,
-					       const cairo_stroke_style_t	*stroke_style,
-					       const cairo_matrix_t	*ctm,
-					       cairo_antialias_t	 antialias,
-					       cairo_boxes_t		*boxes)
-{
-    cairo_rectilinear_stroker_t rectilinear_stroker;
-    cairo_int_status_t status;
-
-    assert (_cairo_path_fixed_stroke_is_rectilinear (path));
-
-    if (! _cairo_rectilinear_stroker_init (&rectilinear_stroker,
-					   stroke_style, ctm, antialias,
-					   FALSE, boxes))
-    {
-	return CAIRO_INT_STATUS_UNSUPPORTED;
-    }
-
-    if (boxes->num_limits) {
-	_cairo_rectilinear_stroker_limit (&rectilinear_stroker,
-					  boxes->limits,
-					  boxes->num_limits);
-    }
-
-    status = _cairo_path_fixed_interpret (path,
-					  _cairo_rectilinear_stroker_move_to,
-					  rectilinear_stroker.dash.dashed ?
-					  _cairo_rectilinear_stroker_line_to_dashed :
-					  _cairo_rectilinear_stroker_line_to,
-					  NULL,
-					  _cairo_rectilinear_stroker_close_path,
-					  &rectilinear_stroker);
-    if (unlikely (status))
-	goto BAIL;
-
-    if (rectilinear_stroker.dash.dashed)
-	status = _cairo_rectilinear_stroker_emit_segments_dashed (&rectilinear_stroker);
-    else
-	status = _cairo_rectilinear_stroker_emit_segments (&rectilinear_stroker);
-    if (unlikely (status))
-	goto BAIL;
-
-    /* As we incrementally tessellate, we do not eliminate self-intersections */
-    status = _cairo_bentley_ottmann_tessellate_boxes (boxes,
-						      CAIRO_FILL_RULE_WINDING,
-						      boxes);
-    if (unlikely (status))
-	goto BAIL;
-
-    _cairo_rectilinear_stroker_fini (&rectilinear_stroker);
-
-    return CAIRO_STATUS_SUCCESS;
-
-BAIL:
-    _cairo_rectilinear_stroker_fini (&rectilinear_stroker);
-    _cairo_boxes_clear (boxes);
-    return status;
-}
diff --git a/src/cairo-stroke-dash-private.h b/src/cairo-stroke-dash-private.h
new file mode 100644
index 0000000..75c000c
--- /dev/null
+++ b/src/cairo-stroke-dash-private.h
@@ -0,0 +1,70 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2002 University of Southern California
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ *	Carl D. Worth <cworth at cworth.org>
+ *	Chris Wilson <chris at chris-wilson.co.uk>
+ */
+
+#ifndef CAIRO_STROKE_DASH_PRIVATE_H
+#define CAIRO_STROKE_DASH_PRIVATE_H
+
+#include "cairoint.h"
+
+CAIRO_BEGIN_DECLS
+
+typedef struct _cairo_stroker_dash {
+    cairo_bool_t dashed;
+    unsigned int dash_index;
+    cairo_bool_t dash_on;
+    cairo_bool_t dash_starts_on;
+    double dash_remain;
+
+    double dash_offset;
+    const double *dashes;
+    unsigned int num_dashes;
+} cairo_stroker_dash_t;
+
+cairo_private void
+_cairo_stroker_dash_init (cairo_stroker_dash_t *dash,
+			  const cairo_stroke_style_t *style);
+
+cairo_private void
+_cairo_stroker_dash_start (cairo_stroker_dash_t *dash);
+
+cairo_private void
+_cairo_stroker_dash_step (cairo_stroker_dash_t *dash, double step);
+
+CAIRO_END_DECLS
+
+#endif /* CAIRO_STROKE_DASH_PRIVATE_H */
diff --git a/src/cairo-stroke-dash.c b/src/cairo-stroke-dash.c
new file mode 100644
index 0000000..d581bdc
--- /dev/null
+++ b/src/cairo-stroke-dash.c
@@ -0,0 +1,96 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2002 University of Southern California
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ *	Carl D. Worth <cworth at cworth.org>
+ *	Chris Wilson <chris at chris-wilson.co.uk>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-stroke-dash-private.h"
+
+void
+_cairo_stroker_dash_start (cairo_stroker_dash_t *dash)
+{
+    double offset;
+    cairo_bool_t on = TRUE;
+    unsigned int i = 0;
+
+    if (! dash->dashed)
+	return;
+
+    offset = dash->dash_offset;
+
+    /* We stop searching for a starting point as soon as the
+       offset reaches zero.  Otherwise when an initial dash
+       segment shrinks to zero it will be skipped over. */
+    while (offset > 0.0 && offset >= dash->dashes[i]) {
+	offset -= dash->dashes[i];
+	on = !on;
+	if (++i == dash->num_dashes)
+	    i = 0;
+    }
+
+    dash->dash_index = i;
+    dash->dash_on = dash->dash_starts_on = on;
+    dash->dash_remain = dash->dashes[i] - offset;
+}
+
+void
+_cairo_stroker_dash_step (cairo_stroker_dash_t *dash, double step)
+{
+    dash->dash_remain -= step;
+    if (dash->dash_remain <= 0.) {
+	if (++dash->dash_index == dash->num_dashes)
+	    dash->dash_index = 0;
+
+	dash->dash_on = ! dash->dash_on;
+	dash->dash_remain = dash->dashes[dash->dash_index];
+    }
+}
+
+void
+_cairo_stroker_dash_init (cairo_stroker_dash_t *dash,
+			  const cairo_stroke_style_t *style)
+{
+    dash->dashed = style->dash != NULL;
+    if (! dash->dashed)
+	return;
+
+    dash->dashes = style->dash;
+    dash->num_dashes = style->num_dashes;
+    dash->dash_offset = style->dash_offset;
+
+    _cairo_stroker_dash_start (dash);
+}
diff --git a/src/cairo-surface-fallback.c b/src/cairo-surface-fallback.c
index b65b2bf..ec6946b 100644
--- a/src/cairo-surface-fallback.c
+++ b/src/cairo-surface-fallback.c
@@ -1038,7 +1038,7 @@ _cairo_surface_fallback_stroke (cairo_surface_t		*surface,
     cairo_traps_t traps;
     cairo_composite_rectangles_t extents;
     cairo_rectangle_int_t rect;
-    cairo_status_t status;
+    cairo_int_status_t status;
 
     if (!_cairo_surface_get_extents (surface, &rect))
         ASSERT_NOT_REACHED;
@@ -1053,44 +1053,47 @@ _cairo_surface_fallback_stroke (cairo_surface_t		*surface,
     _cairo_polygon_init_with_clip (&polygon, extents.clip);
     _cairo_traps_init_with_clip (&traps, extents.clip);
 
+    status = CAIRO_INT_STATUS_UNSUPPORTED;
     if (_cairo_path_fixed_stroke_is_rectilinear (path)) {
-	status = _cairo_path_fixed_stroke_rectilinear_to_traps (path,
+	cairo_boxes_t boxes;
+
+	/* Did I mention, that I need to rewrite surface-fallback? */
+	_cairo_boxes_init_with_clip (&boxes, extents.clip);
+	status = _cairo_path_fixed_stroke_rectilinear_to_boxes (path,
 								stroke_style,
 								ctm,
 								antialias,
-								&traps);
-	if (likely (status == CAIRO_STATUS_SUCCESS))
-	    goto DO_TRAPS;
+								&boxes);
+	if (status == CAIRO_INT_STATUS_SUCCESS)
+	    status = _cairo_traps_init_boxes (&traps, &boxes);
+	_cairo_boxes_fini (&boxes);
 
-	if (_cairo_status_is_error (status))
+	if (unlikely (_cairo_int_status_is_error (status)))
 	    goto CLEANUP;
     }
 
-    status = _cairo_path_fixed_stroke_to_polygon (path,
-						  stroke_style,
-						  ctm, ctm_inverse,
-						  tolerance,
-						  &polygon);
-    if (unlikely (status))
-	goto CLEANUP;
+    /* Fall back to trapezoid fills. */
+    if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
+	status = _cairo_path_fixed_stroke_to_polygon (path,
+						      stroke_style,
+						      ctm, ctm_inverse,
+						      tolerance,
+						      &polygon);
+	if (unlikely (status))
+	    goto CLEANUP;
 
-    if (polygon.num_edges == 0)
-	goto DO_TRAPS;
+	status = _cairo_composite_rectangles_intersect_mask_extents (&extents,
+								     &polygon.extents);
+	if (unlikely (status))
+	    goto CLEANUP;
 
-    if (_cairo_operator_bounded_by_mask (op)) {
-	_cairo_box_round_to_rectangle (&polygon.extents, &extents.mask);
-	if (! _cairo_rectangle_intersect (&extents.bounded, &extents.mask))
+	status = _cairo_bentley_ottmann_tessellate_polygon (&traps,
+							    &polygon,
+							    CAIRO_FILL_RULE_WINDING);
+	if (unlikely (status))
 	    goto CLEANUP;
     }
 
-    /* Fall back to trapezoid fills. */
-    status = _cairo_bentley_ottmann_tessellate_polygon (&traps,
-							&polygon,
-							CAIRO_FILL_RULE_WINDING);
-    if (unlikely (status))
-	goto CLEANUP;
-
-  DO_TRAPS:
     status = _clip_and_composite_trapezoids (source, op, surface,
 					     &traps, antialias,
 					     extents.clip,
@@ -1116,10 +1119,9 @@ _cairo_surface_fallback_fill (cairo_surface_t		*surface,
 {
     cairo_polygon_t polygon;
     cairo_traps_t traps;
-    cairo_bool_t is_rectilinear;
     cairo_composite_rectangles_t extents;
     cairo_rectangle_int_t rect;
-    cairo_status_t status;
+    cairo_int_status_t status;
 
     if (!_cairo_surface_get_extents (surface, &rect))
         ASSERT_NOT_REACHED;
@@ -1136,50 +1138,44 @@ _cairo_surface_fallback_fill (cairo_surface_t		*surface,
     if (_cairo_path_fixed_fill_is_empty (path))
 	goto DO_TRAPS;
 
-    is_rectilinear = _cairo_path_fixed_fill_is_rectilinear (path);
-    if (is_rectilinear) {
-	status = _cairo_path_fixed_fill_rectilinear_to_traps (path,
+    status = CAIRO_INT_STATUS_UNSUPPORTED;
+    if (_cairo_path_fixed_fill_is_rectilinear (path)) {
+	cairo_boxes_t boxes;
+
+	/* meh, surface-fallback is dying anyway... */
+	_cairo_boxes_init_with_clip (&boxes, extents.clip);
+	status = _cairo_path_fixed_fill_rectilinear_to_boxes (path,
 							      fill_rule,
 							      antialias,
-							      &traps);
-	if (likely (status == CAIRO_STATUS_SUCCESS))
-	    goto DO_TRAPS;
+							      &boxes);
+	if (unlikely (status))
+	    goto CLEANUP;
 
-	if (_cairo_status_is_error (status))
+	if (status == CAIRO_INT_STATUS_SUCCESS)
+	    status = _cairo_traps_init_boxes (&traps, &boxes);
+	_cairo_boxes_fini (&boxes);
+	if (unlikely (_cairo_int_status_is_error (status)))
 	    goto CLEANUP;
     }
 
-    status = _cairo_path_fixed_fill_to_polygon (path, tolerance, &polygon);
-    if (unlikely (status))
-	goto CLEANUP;
-
-    if (polygon.num_edges == 0)
-	goto DO_TRAPS;
-
-    if (_cairo_operator_bounded_by_mask (op)) {
-	_cairo_box_round_to_rectangle (&polygon.extents, &extents.mask);
-	if (! _cairo_rectangle_intersect (&extents.bounded, &extents.mask))
+    /* Fall back to trapezoid fills. */
+    if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
+	status = _cairo_path_fixed_fill_to_polygon (path, tolerance, &polygon);
+	if (unlikely (status))
 	    goto CLEANUP;
-    }
 
-    if (is_rectilinear) {
-	status = _cairo_bentley_ottmann_tessellate_rectilinear_polygon (&traps,
-									&polygon,
-									fill_rule);
-	if (likely (status == CAIRO_STATUS_SUCCESS))
-	    goto DO_TRAPS;
+	status = _cairo_composite_rectangles_intersect_mask_extents (&extents,
+								     &polygon.extents);
+	if (unlikely (status))
+	    goto CLEANUP;
 
-	if (unlikely (_cairo_status_is_error (status)))
+	status = _cairo_bentley_ottmann_tessellate_polygon (&traps,
+							    &polygon,
+							    fill_rule);
+	if (unlikely (status))
 	    goto CLEANUP;
     }
 
-    /* Fall back to trapezoid fills. */
-    status = _cairo_bentley_ottmann_tessellate_polygon (&traps,
-							&polygon,
-							fill_rule);
-    if (unlikely (status))
-	goto CLEANUP;
-
   DO_TRAPS:
     status = _clip_and_composite_trapezoids (source, op, surface,
 					     &traps, antialias,
diff --git a/src/cairoint.h b/src/cairoint.h
index 8d87aa5..9a76557 100644
--- a/src/cairoint.h
+++ b/src/cairoint.h
@@ -1371,12 +1371,6 @@ _cairo_path_fixed_fill_rectilinear_to_polygon (const cairo_path_fixed_t *path,
 					       cairo_antialias_t antialias,
 					       cairo_polygon_t *polygon);
 
-cairo_private cairo_int_status_t
-_cairo_path_fixed_fill_rectilinear_to_traps (const cairo_path_fixed_t *path,
-					     cairo_fill_rule_t fill_rule,
-					     cairo_antialias_t antialias,
-					     cairo_traps_t *traps);
-
 cairo_private cairo_status_t
 _cairo_path_fixed_fill_rectilinear_to_boxes (const cairo_path_fixed_t *path,
 					     cairo_fill_rule_t fill_rule,
@@ -1404,13 +1398,6 @@ _cairo_path_fixed_stroke_to_polygon (const cairo_path_fixed_t	*path,
 				     cairo_polygon_t	*polygon);
 
 cairo_private cairo_int_status_t
-_cairo_path_fixed_stroke_rectilinear_to_traps (const cairo_path_fixed_t	*path,
-					       const cairo_stroke_style_t	*stroke_style,
-					       const cairo_matrix_t	*ctm,
-					       cairo_antialias_t	 antialias,
-					       cairo_traps_t		*traps);
-
-cairo_private cairo_int_status_t
 _cairo_path_fixed_stroke_rectilinear_to_boxes (const cairo_path_fixed_t	*path,
 					       const cairo_stroke_style_t	*stroke_style,
 					       const cairo_matrix_t	*ctm,
commit 54c8e8ccfc242fd17144c64202f628c87edbb6f4
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Sat Aug 13 13:24:52 2011 +0100

    test: Add a couple of variants to line-width-overlap
    
    The bug may be in only the fast-path, but future bugs may lie elsewhere.
    
    Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>

diff --git a/test/Makefile.refs b/test/Makefile.refs
index 5a73386..c435bf6 100644
--- a/test/Makefile.refs
+++ b/test/Makefile.refs
@@ -690,6 +690,10 @@ REFERENCE_IMAGES = \
 	leaky-polygon.image16.ref.png \
 	leaky-polygon.ps.ref.png \
 	leaky-polygon.ref.png \
+	line-width-overlap-flipped.ref.png \
+	line-width-overlap-flopped.ref.png \
+	line-width-overlap-offset.ref.png \
+	line-width-overlap-rotated.ref.png \
 	line-width-overlap.ref.png \
 	line-width-scale.image16.ref.png \
 	line-width-scale.ps2.ref.png \
diff --git a/test/line-width-overlap-flipped.ref.png b/test/line-width-overlap-flipped.ref.png
new file mode 100644
index 0000000..09911bc
Binary files /dev/null and b/test/line-width-overlap-flipped.ref.png differ
diff --git a/test/line-width-overlap-flopped.ref.png b/test/line-width-overlap-flopped.ref.png
new file mode 100644
index 0000000..09911bc
Binary files /dev/null and b/test/line-width-overlap-flopped.ref.png differ
diff --git a/test/line-width-overlap-offset.ref.png b/test/line-width-overlap-offset.ref.png
new file mode 100644
index 0000000..eafa50b
Binary files /dev/null and b/test/line-width-overlap-offset.ref.png differ
diff --git a/test/line-width-overlap-rotated.ref.png b/test/line-width-overlap-rotated.ref.png
new file mode 100644
index 0000000..cd89a85
Binary files /dev/null and b/test/line-width-overlap-rotated.ref.png differ
diff --git a/test/line-width-overlap.c b/test/line-width-overlap.c
index cd23d32..ac8c234 100644
--- a/test/line-width-overlap.c
+++ b/test/line-width-overlap.c
@@ -54,9 +54,9 @@ draw (cairo_t *cr, int width, int height)
 
     /* rectangle that is smaller than the line width in center of image */
     cairo_rectangle (cr,
-                     (SIZE - RECT_SIZE) / 2, 
                      (SIZE - RECT_SIZE) / 2,
-                     RECT_SIZE, 
+                     (SIZE - RECT_SIZE) / 2,
+                     RECT_SIZE,
                      RECT_SIZE);
 
     cairo_stroke (cr);
@@ -64,9 +64,86 @@ draw (cairo_t *cr, int width, int height)
     return CAIRO_TEST_SUCCESS;
 }
 
+/* and again slightly offset to trigger another path */
+static cairo_test_status_t
+draw_offset (cairo_t *cr, int width, int height)
+{
+    cairo_translate (cr, .5, .5);
+    return draw (cr, width, height);
+}
+
+static cairo_test_status_t
+draw_rotated (cairo_t *cr, int width, int height)
+{
+    cairo_translate (cr, SIZE/2, SIZE/2);
+    cairo_rotate (cr, M_PI/4);
+    cairo_translate (cr, -SIZE/2, -SIZE/2);
+
+    return draw (cr, width, height);
+}
+
+static cairo_test_status_t
+draw_flipped (cairo_t *cr, int width, int height)
+{
+    cairo_translate (cr, SIZE/2, SIZE/2);
+    cairo_scale (cr, -1, 1);
+    cairo_translate (cr, -SIZE/2, -SIZE/2);
+
+    return draw (cr, width, height);
+}
+
+static cairo_test_status_t
+draw_flopped (cairo_t *cr, int width, int height)
+{
+    cairo_translate (cr, SIZE/2, SIZE/2);
+    cairo_scale (cr, 1, -1);
+    cairo_translate (cr, -SIZE/2, -SIZE/2);
+
+    return draw (cr, width, height);
+}
+
+static cairo_test_status_t
+draw_dashed (cairo_t *cr, int width, int height)
+{
+    const double dashes[] = { 4 };
+    cairo_set_dash (cr, dashes, 1, 0);
+    cairo_set_line_cap (cr, CAIRO_LINE_CAP_BUTT);
+    return draw (cr, width, height);
+}
+
 CAIRO_TEST (line_width_overlap,
 	    "Test overlapping lines due to large line width",
 	    "stroke", /* keywords */
 	    NULL, /* requirements */
 	    SIZE, SIZE,
 	    NULL, draw)
+CAIRO_TEST (line_width_overlap_offset,
+	    "Test overlapping lines due to large line width",
+	    "stroke", /* keywords */
+	    NULL, /* requirements */
+	    SIZE, SIZE,
+	    NULL, draw_offset)
+CAIRO_TEST (line_width_overlap_rotated,
+	    "Test overlapping lines due to large line width",
+	    "stroke", /* keywords */
+	    NULL, /* requirements */
+	    SIZE, SIZE,
+	    NULL, draw_rotated)
+CAIRO_TEST (line_width_overlap_flipped,
+	    "Test overlapping lines due to large line width",
+	    "stroke", /* keywords */
+	    NULL, /* requirements */
+	    SIZE, SIZE,
+	    NULL, draw_flipped)
+CAIRO_TEST (line_width_overlap_flopped,
+	    "Test overlapping lines due to large line width",
+	    "stroke", /* keywords */
+	    NULL, /* requirements */
+	    SIZE, SIZE,
+	    NULL, draw_flopped)
+CAIRO_TEST (line_width_overlap_dashed,
+	    "Test overlapping lines due to large line width",
+	    "stroke", /* keywords */
+	    NULL, /* requirements */
+	    SIZE, SIZE,
+	    NULL, draw_dashed)
commit 829eabfc9531a3e4490760b6bbd33286cd280e95
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Sat Aug 13 16:13:41 2011 +0100

    test/line-width: Refactor and tidy
    
    Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>

diff --git a/test/line-width.c b/test/line-width.c
index 66924f1..283e41f 100644
--- a/test/line-width.c
+++ b/test/line-width.c
@@ -31,7 +31,7 @@
 #define IMAGE_HEIGHT ((LINES+4)*LINES)/2 + 2
 
 static cairo_test_status_t
-draw_a8 (cairo_t *cr, int width, int height)
+draw (cairo_t *cr, int width, int height)
 {
     int i;
 
@@ -59,29 +59,8 @@ draw_a8 (cairo_t *cr, int width, int height)
 static cairo_test_status_t
 draw_a1 (cairo_t *cr, int width, int height)
 {
-    int i;
-
-    /* We draw in black, so paint white first. */
-    cairo_set_source_rgb (cr, 1.0, 1.0, 1.0); /* white */
-    cairo_paint (cr);
-
-    cairo_set_source_rgb (cr, 0, 0, 0);
-    cairo_translate (cr, 2, 2);
-
     cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE);
-
-    for (i=0; i < LINES; i++) {
-	cairo_set_line_width (cr, i+1);
-	cairo_move_to (cr, 0, 0);
-	cairo_rel_line_to (cr, LINE_LENGTH, 0);
-	cairo_stroke (cr);
-	cairo_move_to (cr, LINE_LENGTH + 2, 0.5);
-	cairo_rel_line_to (cr, LINE_LENGTH, 0);
-	cairo_stroke (cr);
-	cairo_translate (cr, 0, i+3);
-    }
-
-    return CAIRO_TEST_SUCCESS;
+    return draw (cr, width, height);
 }
 
 CAIRO_TEST (line_width,
@@ -89,7 +68,7 @@ CAIRO_TEST (line_width,
 	    "stroke", /* keywords */
 	    NULL, /* requirements */
 	    IMAGE_WIDTH, IMAGE_HEIGHT,
-	    NULL, draw_a8)
+	    NULL, draw)
 CAIRO_TEST (a1_line_width,
 	    "Tests cairo_set_line_width",
 	    "stroke", /* keywords */


More information about the cairo-commit mailing list