[cairo-commit] 22 commits - boilerplate/cairo-boilerplate.c boilerplate/cairo-boilerplate-canvas.c boilerplate/Makefile.sources boilerplate/Makefile.win32.features build/configure.ac.features build/Makefile.win32.features build/Makefile.win32.features-h configure.ac perf/micro src/cairo-backend-private.h src/cairo-bentley-ottmann.c src/cairo-bentley-ottmann-rectangular.c src/cairo-bentley-ottmann-rectilinear.c src/cairo-botor-scan-converter.c src/cairo-canvas-context.c src/cairo-canvas-device.c src/cairo-canvas.h src/cairo-canvas-private.h src/cairo-canvas-surface.c src/cairo-clip-private.h src/cairo-clip-tor-scan-converter.c src/cairo-contour.c src/cairo-contour-private.h src/cairo-debug.c src/cairo-default-context.c src/cairo-default-context-private.h src/cairo-error-private.h src/cairo-fast-image-surface.c src/cairo-ft-font.c src/cairo-gstate.c src/cairo.h src/cairo-image-surface.c src/cairo-image-surface-private.h src/cairoint.h src/cairo-mime-surface.c src/cairo-paginated-surf ace.c src/cairo-path-bounds.c src/cairo-path-fill.c src/cairo-path-fixed.c src/cairo-path-fixed-private.h src/cairo-path-in-fill.c src/cairo-path-stroke-boxes.c src/cairo-path-stroke.c src/cairo-path-stroke-polygon.c src/cairo-path-stroke-traps.c src/cairo-pattern.c src/cairo-pattern-private.h src/cairo-pdf-surface.c src/cairo-png.c src/cairo-polygon.c src/cairo-ps-surface.c src/cairo-recording-surface.c src/cairo-rectangular-scan-converter.c src/cairo-scaled-font.c src/cairo-skia.h src/cairo-slope.c src/cairo-slope-private.h src/cairo-spans-private.h src/cairo-spline.c src/cairo-stroke-dash.c src/cairo-stroke-dash-private.h src/cairo-surface.c src/cairo-surface-fallback.c src/cairo-surface-snapshot.c src/cairo-surface-subsurface.c src/cairo-svg-surface.c src/cairo-tor33-scan-converter.c src/cairo-tor-scan-converter.c src/cairo-traps.c src/cairo-traps-private.h src/cairo-type3-glyph-surface.c src/cairo-types-private.h src/cairo-xcb-private.h src/cairo-xcb-surface.c src/cairo -xcb-surface-core.c src/cairo-xcb-surface-render.c src/cairo-xlib-surface.c src/Makefile.sources src/Makefile.win32.features src/skia util/.gitignore util/Makefile.am util/show-contour.c

Chris Wilson ickle at kemper.freedesktop.org
Wed Aug 10 02:35:47 PDT 2011


 boilerplate/Makefile.sources            |    1 
 boilerplate/Makefile.win32.features     |   22 
 boilerplate/cairo-boilerplate-canvas.c  |  172 +
 boilerplate/cairo-boilerplate.c         |   92 
 build/Makefile.win32.features           |    1 
 build/Makefile.win32.features-h         |    4 
 build/configure.ac.features             |    2 
 configure.ac                            |    4 
 perf/micro/many-strokes.c               |    2 
 src/Makefile.sources                    |   26 
 src/Makefile.win32.features             |   26 
 src/cairo-backend-private.h             |    2 
 src/cairo-bentley-ottmann-rectangular.c |    1 
 src/cairo-bentley-ottmann-rectilinear.c |    1 
 src/cairo-bentley-ottmann.c             |    1 
 src/cairo-botor-scan-converter.c        |    3 
 src/cairo-canvas-context.c              | 1296 +++++++++++
 src/cairo-canvas-device.c               |  245 ++
 src/cairo-canvas-private.h              |  102 
 src/cairo-canvas-surface.c              |  187 +
 src/cairo-canvas.h                      |   69 
 src/cairo-clip-private.h                |    1 
 src/cairo-clip-tor-scan-converter.c     | 1842 ++++++++++++++++
 src/cairo-contour-private.h             |  158 +
 src/cairo-contour.c                     |  455 ++++
 src/cairo-debug.c                       |    6 
 src/cairo-default-context-private.h     |    7 
 src/cairo-default-context.c             |   37 
 src/cairo-error-private.h               |    8 
 src/cairo-fast-image-surface.c          | 3626 ++++++++++++++++++++++++++++++++
 src/cairo-ft-font.c                     |    1 
 src/cairo-gstate.c                      |    1 
 src/cairo-image-surface-private.h       |   84 
 src/cairo-image-surface.c               | 1244 ++++------
 src/cairo-mime-surface.c                |  348 +++
 src/cairo-paginated-surface.c           |    1 
 src/cairo-path-bounds.c                 |    2 
 src/cairo-path-fill.c                   |    1 
 src/cairo-path-fixed-private.h          |    3 
 src/cairo-path-fixed.c                  |   16 
 src/cairo-path-in-fill.c                |    2 
 src/cairo-path-stroke-boxes.c           |  776 ++++++
 src/cairo-path-stroke-polygon.c         | 1351 +++++++++++
 src/cairo-path-stroke-traps.c           | 1732 +++++++++++++++
 src/cairo-path-stroke.c                 |  865 -------
 src/cairo-pattern-private.h             |    5 
 src/cairo-pattern.c                     |   29 
 src/cairo-pdf-surface.c                 |    1 
 src/cairo-png.c                         |    1 
 src/cairo-polygon.c                     |   48 
 src/cairo-ps-surface.c                  |    1 
 src/cairo-recording-surface.c           |    2 
 src/cairo-rectangular-scan-converter.c  |   49 
 src/cairo-scaled-font.c                 |    1 
 src/cairo-skia.h                        |   18 
 src/cairo-slope-private.h               |   10 
 src/cairo-slope.c                       |   27 
 src/cairo-spans-private.h               |   37 
 src/cairo-spline.c                      |   25 
 src/cairo-stroke-dash-private.h         |   65 
 src/cairo-stroke-dash.c                 |   93 
 src/cairo-surface-fallback.c            |    4 
 src/cairo-surface-snapshot.c            |    1 
 src/cairo-surface-subsurface.c          |    1 
 src/cairo-surface.c                     |    1 
 src/cairo-svg-surface.c                 |    1 
 src/cairo-tor-scan-converter.c          |   75 
 src/cairo-tor33-scan-converter.c        | 2035 +++++++++++++++++
 src/cairo-traps-private.h               |  122 +
 src/cairo-traps.c                       |  250 ++
 src/cairo-type3-glyph-surface.c         |    1 
 src/cairo-types-private.h               |   17 
 src/cairo-xcb-private.h                 |    2 
 src/cairo-xcb-surface-core.c            |    1 
 src/cairo-xcb-surface-render.c          |  187 +
 src/cairo-xcb-surface.c                 |  143 -
 src/cairo-xlib-surface.c                |    1 
 src/cairo.h                             |  105 
 src/cairoint.h                          |  110 
 src/skia/cairo-skia-context.cpp         | 1711 +++++++++++++++
 src/skia/cairo-skia-private.h           |  110 
 src/skia/cairo-skia-surface.cpp         |  505 ++++
 util/.gitignore                         |    1 
 util/Makefile.am                        |    7 
 util/show-contour.c                     |  660 +++++
 85 files changed, 19333 insertions(+), 1956 deletions(-)

New commits:
commit ae530627977342a3ce49e6c4b26d75bf2f59a581
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Tue Aug 9 22:21:33 2011 +0100

    tor: update is-vertical along with min-height
    
    Similar to the minimum height property, is-vertical can only change
    after an insertion or deletion event. So we only need to update the
    flags after one of those events, so simply update it along side
    min-height.
    
    Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>

diff --git a/src/cairo-tor-scan-converter.c b/src/cairo-tor-scan-converter.c
index 3f709ee..e49aca4 100644
--- a/src/cairo-tor-scan-converter.c
+++ b/src/cairo-tor-scan-converter.c
@@ -483,6 +483,7 @@ struct active_list {
      * scan conversion by a full pixel row if an edge ends somewhere
      * within it. */
     grid_scaled_y_t min_height;
+    int is_vertical;
 };
 
 struct glitter_scan_converter {
@@ -1110,6 +1111,7 @@ active_list_reset (struct active_list *active)
     active->tail.height_left = INT_MAX;
     active->tail.vertical = 1;
     active->min_height = 0;
+    active->is_vertical = 1;
 }
 
 static void
@@ -1251,14 +1253,17 @@ active_list_can_step_full_row (struct active_list *active)
      * list if we have been dropping edges. */
     if (active->min_height <= 0) {
 	int min_height = INT_MAX;
+	int is_vertical = 1;
 
 	e = active->head.next;
 	while (NULL != e) {
 	    if (e->height_left < min_height)
 		min_height = e->height_left;
+	    is_vertical &= e->vertical;
 	    e = e->next;
 	}
 
+	active->is_vertical = is_vertical;
 	active->min_height = min_height;
     }
 
@@ -1301,6 +1306,7 @@ polygon_fill_buckets (struct active_list *active,
 		      struct edge **buckets)
 {
     grid_scaled_y_t min_height = active->min_height;
+    int is_vertical = active->is_vertical;
 
     while (edge) {
 	struct edge *next = edge->next;
@@ -1312,9 +1318,11 @@ polygon_fill_buckets (struct active_list *active,
 	buckets[suby] = edge;
 	if (edge->height_left < min_height)
 	    min_height = edge->height_left;
+	is_vertical &= edge->vertical;
 	edge = next;
     }
 
+    active->is_vertical = is_vertical;
     active->min_height = min_height;
 }
 
@@ -1642,19 +1650,6 @@ glitter_scan_converter_add_edge (glitter_scan_converter_t *converter,
     polygon_add_edge (converter->polygon, &e);
 }
 
-static cairo_bool_t
-active_list_is_vertical (struct active_list *active)
-{
-    struct edge *e;
-
-    for (e = active->head.next; e != &active->tail; e = e->next) {
-	if (! e->vertical)
-	    return FALSE;
-    }
-
-    return TRUE;
-}
-
 static void
 step_edges (struct active_list *active, int count)
 {
@@ -1702,6 +1697,7 @@ glitter_scan_converter_render(
 	if (! polygon->y_buckets[i]) {
 	    if (active->head.next == &active->tail) {
 		active->min_height = INT_MAX;
+		active->is_vertical = 1;
 		for (; j < h && ! polygon->y_buckets[j]; j++)
 		    ;
 		continue;
@@ -1717,7 +1713,7 @@ glitter_scan_converter_render(
 	    else
 		apply_evenodd_fill_rule_and_step_edges (active, coverages);
 
-	    if (active_list_is_vertical (active)) {
+	    if (active->is_vertical) {
 		while (j < h &&
 		       polygon->y_buckets[j] == NULL &&
 		       active->min_height >= 2*GRID_Y)
commit 49db2eb10449a5277c1def96f7efbf9738527bb2
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Mon Aug 1 19:31:43 2011 +0100

    xcb: stroke-to-traps
    
    The xserver will render to an intermediate mask and should be correct
    with respect to self-intersections...
    
    Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>

diff --git a/src/Makefile.sources b/src/Makefile.sources
index 2c505fc..16b27c0 100644
--- a/src/Makefile.sources
+++ b/src/Makefile.sources
@@ -167,6 +167,7 @@ cairo_sources = \
 	cairo-path-stroke.c \
 	cairo-path-stroke-boxes.c \
 	cairo-path-stroke-polygon.c \
+	cairo-path-stroke-traps.c \
 	cairo-pattern.c \
 	cairo-pen.c \
 	cairo-polygon.c \
@@ -196,6 +197,7 @@ cairo_sources = \
 	cairo-clip-tor-scan-converter.c \
 	cairo-toy-font-face.c \
 	cairo-traps.c \
+	cairo-traps-private.h \
 	cairo-unicode.c \
 	cairo-user-font.c \
 	cairo-version.c \
diff --git a/src/cairo-bentley-ottmann-rectangular.c b/src/cairo-bentley-ottmann-rectangular.c
index 7baec13..33c3a48 100644
--- a/src/cairo-bentley-ottmann-rectangular.c
+++ b/src/cairo-bentley-ottmann-rectangular.c
@@ -42,6 +42,7 @@
 #include "cairo-error-private.h"
 #include "cairo-combsort-private.h"
 #include "cairo-list-private.h"
+#include "cairo-traps-private.h"
 
 #include <setjmp.h>
 
diff --git a/src/cairo-bentley-ottmann-rectilinear.c b/src/cairo-bentley-ottmann-rectilinear.c
index a3eb490..28aee32 100644
--- a/src/cairo-bentley-ottmann-rectilinear.c
+++ b/src/cairo-bentley-ottmann-rectilinear.c
@@ -41,6 +41,7 @@
 #include "cairo-boxes-private.h"
 #include "cairo-combsort-private.h"
 #include "cairo-error-private.h"
+#include "cairo-traps-private.h"
 
 typedef struct _cairo_bo_edge cairo_bo_edge_t;
 typedef struct _cairo_bo_trap cairo_bo_trap_t;
diff --git a/src/cairo-bentley-ottmann.c b/src/cairo-bentley-ottmann.c
index d7b017a..47fcc90 100644
--- a/src/cairo-bentley-ottmann.c
+++ b/src/cairo-bentley-ottmann.c
@@ -41,6 +41,7 @@
 #include "cairo-error-private.h"
 #include "cairo-freelist-private.h"
 #include "cairo-combsort-private.h"
+#include "cairo-traps-private.h"
 
 #define DEBUG_PRINT_STATE 0
 #define DEBUG_EVENTS 0
diff --git a/src/cairo-gstate.c b/src/cairo-gstate.c
index 1d59fc5..9036d3a 100644
--- a/src/cairo-gstate.c
+++ b/src/cairo-gstate.c
@@ -41,6 +41,7 @@
 #include "cairo-error-private.h"
 #include "cairo-gstate-private.h"
 #include "cairo-pattern-private.h"
+#include "cairo-traps-private.h"
 
 #if _XOPEN_SOURCE >= 600 || defined (_ISOC99_SOURCE)
 #define ISFINITE(x) isfinite (x)
diff --git a/src/cairo-image-surface.c b/src/cairo-image-surface.c
index 9ba0d7b..c9061f2 100644
--- a/src/cairo-image-surface.c
+++ b/src/cairo-image-surface.c
@@ -52,6 +52,7 @@
 #include "cairo-scaled-font-private.h"
 #include "cairo-surface-snapshot-private.h"
 #include "cairo-surface-subsurface-private.h"
+#include "cairo-traps-private.h"
 
 /* Limit on the width / height of an image surface in pixels.  This is
  * mainly determined by coordinates of things sent to pixman at the
diff --git a/src/cairo-path-bounds.c b/src/cairo-path-bounds.c
index 1fc9a06..f336098 100644
--- a/src/cairo-path-bounds.c
+++ b/src/cairo-path-bounds.c
@@ -39,7 +39,7 @@
 #include "cairo-box-private.h"
 #include "cairo-error-private.h"
 #include "cairo-path-fixed-private.h"
-
+#include "cairo-traps-private.h"
 
 typedef struct _cairo_path_bounder {
     cairo_point_t current_point;
diff --git a/src/cairo-path-fill.c b/src/cairo-path-fill.c
index b15b1a4..e065a01 100644
--- a/src/cairo-path-fill.c
+++ b/src/cairo-path-fill.c
@@ -40,6 +40,7 @@
 #include "cairo-error-private.h"
 #include "cairo-path-fixed-private.h"
 #include "cairo-region-private.h"
+#include "cairo-traps-private.h"
 
 typedef struct cairo_filler {
     cairo_polygon_t *polygon;
diff --git a/src/cairo-path-fixed.c b/src/cairo-path-fixed.c
index 8d661f4..0a901a4 100644
--- a/src/cairo-path-fixed.c
+++ b/src/cairo-path-fixed.c
@@ -42,6 +42,7 @@
 #include "cairo-error-private.h"
 #include "cairo-path-fixed-private.h"
 #include "cairo-slope-private.h"
+#include "cairo-traps-private.h"
 
 static cairo_status_t
 _cairo_path_fixed_add (cairo_path_fixed_t  *path,
diff --git a/src/cairo-path-stroke-traps.c b/src/cairo-path-stroke-traps.c
new file mode 100644
index 0000000..87f92b0
--- /dev/null
+++ b/src/cairo-path-stroke-traps.c
@@ -0,0 +1,1732 @@
+/* -*- 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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>
+ */
+
+#include "cairoint.h"
+#include "cairo-box-private.h"
+#include "cairo-error-private.h"
+#include "cairo-path-fixed-private.h"
+#include "cairo-slope-private.h"
+#include "cairo-traps-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;
+
+typedef struct cairo_stroker {
+    cairo_stroke_style_t style;
+
+    const cairo_matrix_t *ctm;
+    const cairo_matrix_t *ctm_inverse;
+    double tolerance;
+    double ctm_determinant;
+    cairo_bool_t ctm_det_positive;
+
+    cairo_traps_t *traps;
+
+    cairo_pen_t	  pen;
+
+    cairo_point_t current_point;
+    cairo_point_t first_point;
+
+    cairo_bool_t has_initial_sub_path;
+
+    cairo_bool_t has_current_face;
+    cairo_stroke_face_t current_face;
+
+    cairo_bool_t has_first_face;
+    cairo_stroke_face_t first_face;
+
+    cairo_stroker_dash_t dash;
+
+    cairo_bool_t has_bounds;
+    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,
+		     const cairo_matrix_t	*ctm,
+		     const cairo_matrix_t	*ctm_inverse,
+		     double			 tolerance,
+		     cairo_traps_t		*traps)
+{
+    cairo_status_t status;
+
+    stroker->style = *stroke_style;
+    stroker->ctm = ctm;
+    stroker->ctm_inverse = ctm_inverse;
+    stroker->tolerance = tolerance;
+    stroker->traps = traps;
+
+    _cairo_stroker_dash_init (&stroker->dash, stroke_style);
+
+    stroker->ctm_determinant = _cairo_matrix_compute_determinant (stroker->ctm);
+    stroker->ctm_det_positive = stroker->ctm_determinant >= 0.0;
+
+    status = _cairo_pen_init (&stroker->pen,
+		              stroke_style->line_width / 2.0,
+			      tolerance, ctm);
+    if (status)
+	return status;
+
+    stroker->has_current_face = FALSE;
+    stroker->has_first_face = FALSE;
+    stroker->has_initial_sub_path = FALSE;
+
+    stroker->has_bounds = traps->num_limits;
+    if (stroker->has_bounds) {
+	/* Extend the bounds in each direction to account for the maximum area
+	 * we might generate trapezoids, to capture line segments that are outside
+	 * of the bounds but which might generate rendering that's within bounds.
+	 */
+	double dx, dy;
+	cairo_fixed_t fdx, fdy;
+
+	_cairo_stroke_style_max_distance_from_path (&stroker->style,
+						    stroker->ctm,
+						    &dx, &dy);
+
+	fdx = _cairo_fixed_from_double (dx);
+	fdy = _cairo_fixed_from_double (dy);
+
+	stroker->bounds.p1.x -= fdx;
+	stroker->bounds.p2.x += fdx;
+
+	stroker->bounds.p1.y -= fdy;
+	stroker->bounds.p2.y += fdy;
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_stroker_fini (cairo_stroker_t *stroker)
+{
+    _cairo_pen_fini (&stroker->pen);
+}
+
+static void
+_translate_point (cairo_point_t *point, cairo_point_t *offset)
+{
+    point->x += offset->x;
+    point->y += offset->y;
+}
+
+static int
+_cairo_stroker_face_clockwise (cairo_stroke_face_t *in, cairo_stroke_face_t *out)
+{
+    cairo_slope_t in_slope, out_slope;
+
+    _cairo_slope_init (&in_slope, &in->point, &in->cw);
+    _cairo_slope_init (&out_slope, &out->point, &out->cw);
+
+    return _cairo_slope_compare (&in_slope, &out_slope) < 0;
+}
+
+/**
+ * _cairo_slope_compare_sgn
+ *
+ * Return -1, 0 or 1 depending on the relative slopes of
+ * two lines.
+ */
+static int
+_cairo_slope_compare_sgn (double dx1, double dy1, double dx2, double dy2)
+{
+    double  c = (dx1 * dy2 - dx2 * dy1);
+
+    if (c > 0) return 1;
+    if (c < 0) return -1;
+    return 0;
+}
+
+static cairo_status_t
+_cairo_stroker_join (cairo_stroker_t *stroker, cairo_stroke_face_t *in, cairo_stroke_face_t *out)
+{
+    int			clockwise = _cairo_stroker_face_clockwise (out, in);
+    cairo_point_t	*inpt, *outpt;
+    cairo_status_t status;
+
+    if (in->cw.x == out->cw.x
+	&& in->cw.y == out->cw.y
+	&& in->ccw.x == out->ccw.x
+	&& in->ccw.y == out->ccw.y)
+    {
+	return CAIRO_STATUS_SUCCESS;
+    }
+
+    if (clockwise) {
+    	inpt = &in->ccw;
+    	outpt = &out->ccw;
+    } else {
+    	inpt = &in->cw;
+    	outpt = &out->cw;
+    }
+
+    switch (stroker->style.line_join) {
+    case CAIRO_LINE_JOIN_ROUND: {
+	int i;
+	int start, step, stop;
+	cairo_point_t tri[3];
+	cairo_pen_t *pen = &stroker->pen;
+
+	tri[0] = in->point;
+	if (clockwise) {
+	    start = _cairo_pen_find_active_ccw_vertex_index (pen, &in->dev_vector);
+	    stop = _cairo_pen_find_active_ccw_vertex_index (pen, &out->dev_vector);
+	    step = -1;
+	} else {
+	    start = _cairo_pen_find_active_cw_vertex_index (pen, &in->dev_vector);
+	    stop = _cairo_pen_find_active_cw_vertex_index (pen, &out->dev_vector);
+	    step = +1;
+	}
+
+	i = start;
+	tri[1] = *inpt;
+	while (i != stop) {
+	    tri[2] = in->point;
+	    _translate_point (&tri[2], &pen->vertices[i].point);
+	    status = _cairo_traps_tessellate_triangle (stroker->traps, tri);
+	    if (status)
+		return status;
+	    tri[1] = tri[2];
+	    i += step;
+	    if (i < 0)
+		i = pen->num_vertices - 1;
+	    if (i >= pen->num_vertices)
+		i = 0;
+	}
+
+	tri[2] = *outpt;
+
+	return _cairo_traps_tessellate_triangle (stroker->traps, tri);
+    }
+    case CAIRO_LINE_JOIN_MITER:
+    default: {
+	/* dot product of incoming slope vector with outgoing slope vector */
+	double	in_dot_out = ((-in->usr_vector.x * out->usr_vector.x)+
+			      (-in->usr_vector.y * out->usr_vector.y));
+	double	ml = stroker->style.miter_limit;
+
+	/* Check the miter limit -- lines meeting at an acute angle
+	 * can generate long miters, the limit converts them to bevel
+	 *
+	 * Consider the miter join formed when two line segments
+	 * meet at an angle psi:
+	 *
+	 *	   /.\
+	 *	  /. .\
+	 *	 /./ \.\
+	 *	/./psi\.\
+	 *
+	 * We can zoom in on the right half of that to see:
+	 *
+	 *	    |\
+	 *	    | \ psi/2
+	 *	    |  \
+	 *	    |   \
+	 *	    |    \
+	 *	    |     \
+	 * 	  miter    \
+	 *	 length     \
+	 *	    |        \
+	 *	    |        .\
+	 *	    |    .     \
+	 *	    |.   line   \
+	 *	     \    width  \
+	 *	      \           \
+	 *
+	 *
+	 * The right triangle in that figure, (the line-width side is
+	 * shown faintly with three '.' characters), gives us the
+	 * following expression relating miter length, angle and line
+	 * width:
+	 *
+	 *	1 /sin (psi/2) = miter_length / line_width
+	 *
+	 * The right-hand side of this relationship is the same ratio
+	 * in which the miter limit (ml) is expressed. We want to know
+	 * when the miter length is within the miter limit. That is
+	 * when the following condition holds:
+	 *
+	 *	1/sin(psi/2) <= ml
+	 *	1 <= ml sin(psi/2)
+	 *	1 <= ml² sin²(psi/2)
+	 *	2 <= ml² 2 sin²(psi/2)
+	 *				2·sin²(psi/2) = 1-cos(psi)
+	 *	2 <= ml² (1-cos(psi))
+	 *
+	 *				in · out = |in| |out| cos (psi)
+	 *
+	 * in and out are both unit vectors, so:
+	 *
+	 *				in · out = cos (psi)
+	 *
+	 *	2 <= ml² (1 - in · out)
+	 *
+	 */
+	if (2 <= ml * ml * (1 - in_dot_out))
+	{
+	    double		x1, y1, x2, y2;
+	    double		mx, my;
+	    double		dx1, dx2, dy1, dy2;
+	    cairo_point_t	outer;
+	    cairo_point_t	quad[4];
+	    double		ix, iy;
+	    double		fdx1, fdy1, fdx2, fdy2;
+	    double		mdx, mdy;
+
+	    /*
+	     * we've got the points already transformed to device
+	     * space, but need to do some computation with them and
+	     * also need to transform the slope from user space to
+	     * device space
+	     */
+	    /* outer point of incoming line face */
+	    x1 = _cairo_fixed_to_double (inpt->x);
+	    y1 = _cairo_fixed_to_double (inpt->y);
+	    dx1 = in->usr_vector.x;
+	    dy1 = in->usr_vector.y;
+	    cairo_matrix_transform_distance (stroker->ctm, &dx1, &dy1);
+
+	    /* outer point of outgoing line face */
+	    x2 = _cairo_fixed_to_double (outpt->x);
+	    y2 = _cairo_fixed_to_double (outpt->y);
+	    dx2 = out->usr_vector.x;
+	    dy2 = out->usr_vector.y;
+	    cairo_matrix_transform_distance (stroker->ctm, &dx2, &dy2);
+
+	    /*
+	     * Compute the location of the outer corner of the miter.
+	     * That's pretty easy -- just the intersection of the two
+	     * outer edges.  We've got slopes and points on each
+	     * of those edges.  Compute my directly, then compute
+	     * mx by using the edge with the larger dy; that avoids
+	     * dividing by values close to zero.
+	     */
+	    my = (((x2 - x1) * dy1 * dy2 - y2 * dx2 * dy1 + y1 * dx1 * dy2) /
+		  (dx1 * dy2 - dx2 * dy1));
+	    if (fabs (dy1) >= fabs (dy2))
+		mx = (my - y1) * dx1 / dy1 + x1;
+	    else
+		mx = (my - y2) * dx2 / dy2 + x2;
+
+	    /*
+	     * When the two outer edges are nearly parallel, slight
+	     * perturbations in the position of the outer points of the lines
+	     * caused by representing them in fixed point form can cause the
+	     * intersection point of the miter to move a large amount. If
+	     * that moves the miter intersection from between the two faces,
+	     * then draw a bevel instead.
+	     */
+
+	    ix = _cairo_fixed_to_double (in->point.x);
+	    iy = _cairo_fixed_to_double (in->point.y);
+
+	    /* slope of one face */
+	    fdx1 = x1 - ix; fdy1 = y1 - iy;
+
+	    /* slope of the other face */
+	    fdx2 = x2 - ix; fdy2 = y2 - iy;
+
+	    /* slope from the intersection to the miter point */
+	    mdx = mx - ix; mdy = my - iy;
+
+	    /*
+	     * Make sure the miter point line lies between the two
+	     * faces by comparing the slopes
+	     */
+	    if (_cairo_slope_compare_sgn (fdx1, fdy1, mdx, mdy) !=
+		_cairo_slope_compare_sgn (fdx2, fdy2, mdx, mdy))
+	    {
+		/*
+		 * Draw the quadrilateral
+		 */
+		outer.x = _cairo_fixed_from_double (mx);
+		outer.y = _cairo_fixed_from_double (my);
+
+		quad[0] = in->point;
+		quad[1] = *inpt;
+		quad[2] = outer;
+		quad[3] = *outpt;
+
+		return _cairo_traps_tessellate_convex_quad (stroker->traps, quad);
+	    }
+	}
+	/* fall through ... */
+    }
+    case CAIRO_LINE_JOIN_BEVEL: {
+	cairo_point_t tri[3];
+	tri[0] = in->point;
+	tri[1] = *inpt;
+	tri[2] = *outpt;
+
+	return _cairo_traps_tessellate_triangle (stroker->traps, tri);
+    }
+    }
+}
+
+static cairo_status_t
+_cairo_stroker_add_cap (cairo_stroker_t *stroker, cairo_stroke_face_t *f)
+{
+    cairo_status_t	    status;
+
+    if (stroker->style.line_cap == CAIRO_LINE_CAP_BUTT)
+	return CAIRO_STATUS_SUCCESS;
+
+    switch (stroker->style.line_cap) {
+    case CAIRO_LINE_CAP_ROUND: {
+	int i;
+	int start, stop;
+	cairo_slope_t slope;
+	cairo_point_t tri[3];
+	cairo_pen_t *pen = &stroker->pen;
+
+	slope = f->dev_vector;
+	start = _cairo_pen_find_active_cw_vertex_index (pen, &slope);
+	slope.dx = -slope.dx;
+	slope.dy = -slope.dy;
+	stop = _cairo_pen_find_active_cw_vertex_index (pen, &slope);
+
+	tri[0] = f->point;
+	tri[1] = f->cw;
+	for (i=start; i != stop; i = (i+1) % pen->num_vertices) {
+	    tri[2] = f->point;
+	    _translate_point (&tri[2], &pen->vertices[i].point);
+	    status = _cairo_traps_tessellate_triangle (stroker->traps, tri);
+	    if (status)
+		return status;
+	    tri[1] = tri[2];
+	}
+	tri[2] = f->ccw;
+
+	return _cairo_traps_tessellate_triangle (stroker->traps, tri);
+    }
+    case CAIRO_LINE_CAP_SQUARE: {
+	double dx, dy;
+	cairo_slope_t	fvector;
+	cairo_point_t q[4];
+
+	dx = f->usr_vector.x;
+	dy = f->usr_vector.y;
+	dx *= stroker->style.line_width / 2.0;
+	dy *= stroker->style.line_width / 2.0;
+	cairo_matrix_transform_distance (stroker->ctm, &dx, &dy);
+	fvector.dx = _cairo_fixed_from_double (dx);
+	fvector.dy = _cairo_fixed_from_double (dy);
+	q[0] = f->cw;
+	q[1].x = f->cw.x + fvector.dx;
+	q[1].y = f->cw.y + fvector.dy;
+	q[2].x = f->ccw.x + fvector.dx;
+	q[2].y = f->ccw.y + fvector.dy;
+	q[3] = f->ccw;
+
+	return _cairo_traps_tessellate_convex_quad (stroker->traps, q);
+    }
+    case CAIRO_LINE_CAP_BUTT:
+    default:
+	return CAIRO_STATUS_SUCCESS;
+    }
+}
+
+static cairo_status_t
+_cairo_stroker_add_leading_cap (cairo_stroker_t     *stroker,
+				cairo_stroke_face_t *face)
+{
+    cairo_stroke_face_t reversed;
+    cairo_point_t t;
+
+    reversed = *face;
+
+    /* The initial cap needs an outward facing vector. Reverse everything */
+    reversed.usr_vector.x = -reversed.usr_vector.x;
+    reversed.usr_vector.y = -reversed.usr_vector.y;
+    reversed.dev_vector.dx = -reversed.dev_vector.dx;
+    reversed.dev_vector.dy = -reversed.dev_vector.dy;
+    t = reversed.cw;
+    reversed.cw = reversed.ccw;
+    reversed.ccw = t;
+
+    return _cairo_stroker_add_cap (stroker, &reversed);
+}
+
+static cairo_status_t
+_cairo_stroker_add_trailing_cap (cairo_stroker_t     *stroker,
+				 cairo_stroke_face_t *face)
+{
+    return _cairo_stroker_add_cap (stroker, face);
+}
+
+static inline cairo_bool_t
+_compute_normalized_device_slope (double *dx, double *dy,
+				  const cairo_matrix_t *ctm_inverse,
+				  double *mag_out)
+{
+    double dx0 = *dx, dy0 = *dy;
+    double mag;
+
+    cairo_matrix_transform_distance (ctm_inverse, &dx0, &dy0);
+
+    if (dx0 == 0.0 && dy0 == 0.0) {
+	if (mag_out)
+	    *mag_out = 0.0;
+	return FALSE;
+    }
+
+    if (dx0 == 0.0) {
+	*dx = 0.0;
+	if (dy0 > 0.0) {
+	    mag = dy0;
+	    *dy = 1.0;
+	} else {
+	    mag = -dy0;
+	    *dy = -1.0;
+	}
+    } else if (dy0 == 0.0) {
+	*dy = 0.0;
+	if (dx0 > 0.0) {
+	    mag = dx0;
+	    *dx = 1.0;
+	} else {
+	    mag = -dx0;
+	    *dx = -1.0;
+	}
+    } else {
+	mag = sqrt (dx0 * dx0 + dy0 * dy0);
+	*dx = dx0 / mag;
+	*dy = dy0 / mag;
+    }
+
+    if (mag_out)
+	*mag_out = mag;
+
+    return TRUE;
+}
+
+static void
+_compute_face (const cairo_point_t *point,
+	       cairo_slope_t *dev_slope,
+	       double slope_dx, double slope_dy,
+	       cairo_stroker_t *stroker, cairo_stroke_face_t *face);
+
+static cairo_status_t
+_cairo_stroker_add_caps (cairo_stroker_t *stroker)
+{
+    cairo_status_t status;
+    /* check for a degenerative sub_path */
+    if (stroker->has_initial_sub_path
+	&& !stroker->has_first_face
+	&& !stroker->has_current_face
+	&& stroker->style.line_cap == CAIRO_LINE_CAP_ROUND)
+    {
+	/* pick an arbitrary slope to use */
+	double dx = 1.0, dy = 0.0;
+	cairo_slope_t slope = { CAIRO_FIXED_ONE, 0 };
+	cairo_stroke_face_t face;
+
+	_compute_normalized_device_slope (&dx, &dy, stroker->ctm_inverse, NULL);
+
+	/* arbitrarily choose first_point
+	 * first_point and current_point should be the same */
+	_compute_face (&stroker->first_point, &slope, dx, dy, stroker, &face);
+
+	status = _cairo_stroker_add_leading_cap (stroker, &face);
+	if (status)
+	    return status;
+	status = _cairo_stroker_add_trailing_cap (stroker, &face);
+	if (status)
+	    return status;
+    }
+
+    if (stroker->has_first_face) {
+	status = _cairo_stroker_add_leading_cap (stroker, &stroker->first_face);
+	if (status)
+	    return status;
+    }
+
+    if (stroker->has_current_face) {
+	status = _cairo_stroker_add_trailing_cap (stroker, &stroker->current_face);
+	if (status)
+	    return status;
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_compute_face (const cairo_point_t *point,
+	       cairo_slope_t *dev_slope,
+	       double slope_dx, double slope_dy,
+	       cairo_stroker_t *stroker, cairo_stroke_face_t *face)
+{
+    double face_dx, face_dy;
+    cairo_point_t offset_ccw, offset_cw;
+
+    /*
+     * rotate to get a line_width/2 vector along the face, note that
+     * the vector must be rotated the right direction in device space,
+     * but by 90° in user space. So, the rotation depends on
+     * whether the ctm reflects or not, and that can be determined
+     * by looking at the determinant of the matrix.
+     */
+    if (stroker->ctm_det_positive)
+    {
+	face_dx = - slope_dy * (stroker->style.line_width / 2.0);
+	face_dy = slope_dx * (stroker->style.line_width / 2.0);
+    }
+    else
+    {
+	face_dx = slope_dy * (stroker->style.line_width / 2.0);
+	face_dy = - slope_dx * (stroker->style.line_width / 2.0);
+    }
+
+    /* back to device space */
+    cairo_matrix_transform_distance (stroker->ctm, &face_dx, &face_dy);
+
+    offset_ccw.x = _cairo_fixed_from_double (face_dx);
+    offset_ccw.y = _cairo_fixed_from_double (face_dy);
+    offset_cw.x = -offset_ccw.x;
+    offset_cw.y = -offset_ccw.y;
+
+    face->ccw = *point;
+    _translate_point (&face->ccw, &offset_ccw);
+
+    face->point = *point;
+
+    face->cw = *point;
+    _translate_point (&face->cw, &offset_cw);
+
+    face->usr_vector.x = slope_dx;
+    face->usr_vector.y = slope_dy;
+
+    face->dev_vector = *dev_slope;
+}
+
+static cairo_status_t
+_cairo_stroker_add_sub_edge (cairo_stroker_t *stroker,
+			     const cairo_point_t *p1,
+			     const cairo_point_t *p2,
+			     cairo_slope_t *dev_slope, double slope_dx, double slope_dy,
+			     cairo_stroke_face_t *start, cairo_stroke_face_t *end)
+{
+    cairo_point_t rectangle[4];
+
+    _compute_face (p1, dev_slope, slope_dx, slope_dy, stroker, start);
+
+    /* XXX: This could be optimized slightly by not calling
+       _compute_face again but rather  translating the relevant
+       fields from start. */
+    _compute_face (p2, dev_slope, slope_dx, slope_dy, stroker, end);
+
+    if (p1->x == p2->x && p1->y == p2->y)
+	return CAIRO_STATUS_SUCCESS;
+
+    rectangle[0] = start->cw;
+    rectangle[1] = start->ccw;
+    rectangle[2] = end->ccw;
+    rectangle[3] = end->cw;
+
+    return _cairo_traps_tessellate_convex_quad (stroker->traps, rectangle);
+}
+
+static cairo_status_t
+_cairo_stroker_move_to (void *closure,
+		       	const cairo_point_t *point)
+{
+    cairo_status_t status;
+    cairo_stroker_t *stroker = closure;
+
+    /* Cap the start and end of the previous sub path as needed */
+    status = _cairo_stroker_add_caps (stroker);
+    if (status)
+	return status;
+
+    stroker->first_point = *point;
+    stroker->current_point = *point;
+
+    stroker->has_first_face = FALSE;
+    stroker->has_current_face = FALSE;
+    stroker->has_initial_sub_path = FALSE;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_stroker_move_to_dashed (void *closure,
+			       const cairo_point_t *point)
+{
+    /* reset the dash pattern for new sub paths */
+    cairo_stroker_t *stroker = closure;
+    _cairo_stroker_dash_start (&stroker->dash);
+
+    return _cairo_stroker_move_to (closure, point);
+}
+
+static cairo_status_t
+_cairo_stroker_line_to (void *closure,
+		       	const cairo_point_t *point)
+{
+    cairo_status_t status;
+    cairo_stroker_t *stroker = closure;
+    cairo_stroke_face_t start, end;
+    const cairo_point_t *p1 = &stroker->current_point;
+    const cairo_point_t *p2 = point;
+    cairo_slope_t dev_slope;
+    double slope_dx, slope_dy;
+
+    stroker->has_initial_sub_path = TRUE;
+
+    if (p1->x == p2->x && p1->y == p2->y)
+	return CAIRO_STATUS_SUCCESS;
+
+    _cairo_slope_init (&dev_slope, p1, p2);
+    slope_dx = _cairo_fixed_to_double (p2->x - p1->x);
+    slope_dy = _cairo_fixed_to_double (p2->y - p1->y);
+    _compute_normalized_device_slope (&slope_dx, &slope_dy, stroker->ctm_inverse, NULL);
+
+    status = _cairo_stroker_add_sub_edge (stroker, p1, p2, &dev_slope, slope_dx, slope_dy, &start, &end);
+    if (status)
+	return status;
+
+    if (stroker->has_current_face) {
+	/* Join with final face from previous segment */
+	status = _cairo_stroker_join (stroker, &stroker->current_face, &start);
+	if (status)
+	    return status;
+    } else if (!stroker->has_first_face) {
+	/* Save sub path's first face in case needed for closing join */
+	stroker->first_face = start;
+	stroker->has_first_face = TRUE;
+    }
+    stroker->current_face = end;
+    stroker->has_current_face = TRUE;
+
+    stroker->current_point = *point;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+/*
+ * Dashed lines.  Cap each dash end, join around turns when on
+ */
+static cairo_status_t
+_cairo_stroker_line_to_dashed (void *closure,
+			       const cairo_point_t *point)
+{
+    cairo_status_t status = CAIRO_STATUS_SUCCESS;
+    cairo_stroker_t *stroker = closure;
+    double mag, remain, step_length = 0;
+    double slope_dx, slope_dy;
+    double dx2, dy2;
+    cairo_stroke_face_t sub_start, sub_end;
+    const cairo_point_t *p1 = &stroker->current_point;
+    const cairo_point_t *p2 = point;
+    cairo_slope_t dev_slope;
+    cairo_bool_t fully_in_bounds = TRUE;
+    cairo_line_t segment;
+
+    stroker->has_initial_sub_path = stroker->dash.dash_starts_on;
+
+    if (p1->x == p2->x && p1->y == p2->y)
+	return CAIRO_STATUS_SUCCESS;
+
+    if (stroker->has_bounds &&
+	(!_cairo_box_contains_point (&stroker->bounds, p1) ||
+	 !_cairo_box_contains_point (&stroker->bounds, p2)))
+    {
+	fully_in_bounds = FALSE;
+    }
+
+    _cairo_slope_init (&dev_slope, p1, p2);
+
+    slope_dx = _cairo_fixed_to_double (p2->x - p1->x);
+    slope_dy = _cairo_fixed_to_double (p2->y - p1->y);
+
+    if (!_compute_normalized_device_slope (&slope_dx, &slope_dy, stroker->ctm_inverse, &mag))
+	return CAIRO_STATUS_SUCCESS;
+
+    remain = mag;
+    segment.p1 = *p1;
+    while (remain) {
+	step_length = MIN (stroker->dash.dash_remain, remain);
+	remain -= step_length;
+	dx2 = slope_dx * (mag - remain);
+	dy2 = slope_dy * (mag - remain);
+	cairo_matrix_transform_distance (stroker->ctm, &dx2, &dy2);
+	segment.p2.x = _cairo_fixed_from_double (dx2) + p1->x;
+	segment.p2.y = _cairo_fixed_from_double (dy2) + p1->y;
+
+	if (fully_in_bounds ||
+	    _cairo_box_intersects_line_segment (&stroker->bounds, &segment))
+	{
+	    if (stroker->dash.dash_on) {
+		status = _cairo_stroker_add_sub_edge (stroker, &segment.p1, &segment.p2, &dev_slope, slope_dx, slope_dy, &sub_start, &sub_end);
+		if (status)
+		    return status;
+
+		if (stroker->has_current_face) {
+		    /* Join with final face from previous segment */
+		    status = _cairo_stroker_join (stroker, &stroker->current_face, &sub_start);
+		    stroker->has_current_face = FALSE;
+		    if (status)
+			return status;
+		} else if (!stroker->has_first_face && stroker->dash.dash_starts_on) {
+		    /* Save sub path's first face in case needed for closing join */
+		    stroker->first_face = sub_start;
+		    stroker->has_first_face = TRUE;
+		} else {
+		    /* Cap dash start if not connecting to a previous segment */
+		    status = _cairo_stroker_add_leading_cap (stroker, &sub_start);
+		    if (status)
+			return status;
+		}
+
+		if (remain) {
+		    /* Cap dash end if not at end of segment */
+		    status = _cairo_stroker_add_trailing_cap (stroker, &sub_end);
+		    if (status)
+			return status;
+		} else {
+		    stroker->current_face = sub_end;
+		    stroker->has_current_face = TRUE;
+		}
+	    } else {
+		if (stroker->has_current_face) {
+		    /* Cap final face from previous segment */
+		    status = _cairo_stroker_add_trailing_cap (stroker, &stroker->current_face);
+		    if (status)
+			return status;
+		    stroker->has_current_face = FALSE;
+		}
+	    }
+	} else {
+	    if (stroker->has_current_face) {
+		/* Cap final face from previous segment */
+		status = _cairo_stroker_add_trailing_cap (stroker, &stroker->current_face);
+		if (status)
+		    return status;
+		stroker->has_current_face = FALSE;
+	    }
+	}
+
+	_cairo_stroker_dash_step (&stroker->dash, step_length);
+	segment.p1 = segment.p2;
+    }
+
+    if (stroker->dash.dash_on && !stroker->has_current_face) {
+	/* This segment ends on a transition to dash_on, compute a new face
+	 * and add cap for the begining of the next dash_on step.
+	 *
+	 * Note: this will create a degenerate cap if this is not the last line
+	 * in the path. Whether this behaviour is desirable or not is debatable.
+	 * On one side these degnerate caps can not be reproduced with regular path stroking.
+	 * On the other side Acroread 7 also produces the degenerate caps. */
+	_compute_face (point, &dev_slope, slope_dx, slope_dy, stroker, &stroker->current_face);
+	stroker->has_current_face = TRUE;
+	status = _cairo_stroker_add_leading_cap (stroker, &stroker->current_face);
+	if (status)
+	    return status;
+    }
+
+    stroker->current_point = *point;
+
+    return status;
+}
+
+static cairo_status_t
+_cairo_stroker_curve_to (void *closure,
+			 const cairo_point_t *b,
+			 const cairo_point_t *c,
+			 const cairo_point_t *d)
+{
+    cairo_stroker_t *stroker = closure;
+    cairo_spline_t spline;
+    cairo_line_join_t line_join_save;
+    cairo_stroke_face_t face;
+    double slope_dx, slope_dy;
+    cairo_path_fixed_line_to_func_t *line_to;
+    cairo_status_t status = CAIRO_STATUS_SUCCESS;
+
+    line_to = stroker->dash.dashed ?
+	_cairo_stroker_line_to_dashed :
+	_cairo_stroker_line_to;
+
+    if (! _cairo_spline_init (&spline,
+			      line_to, stroker,
+			      &stroker->current_point, b, c, d))
+    {
+	return line_to (closure, d);
+    }
+
+    /* If the line width is so small that the pen is reduced to a
+       single point, then we have nothing to do. */
+    if (stroker->pen.num_vertices <= 1)
+	return CAIRO_STATUS_SUCCESS;
+
+    /* Compute the initial face */
+    if (! stroker->dash.dashed || stroker->dash.dash_on) {
+	slope_dx = _cairo_fixed_to_double (spline.initial_slope.dx);
+	slope_dy = _cairo_fixed_to_double (spline.initial_slope.dy);
+	if (_compute_normalized_device_slope (&slope_dx, &slope_dy,
+					      stroker->ctm_inverse, NULL))
+	{
+	    _compute_face (&stroker->current_point,
+			   &spline.initial_slope,
+			   slope_dx, slope_dy,
+			   stroker, &face);
+	}
+	if (stroker->has_current_face) {
+	    status = _cairo_stroker_join (stroker,
+					  &stroker->current_face, &face);
+	    if (unlikely (status))
+		return status;
+	} else if (! stroker->has_first_face) {
+	    stroker->first_face = face;
+	    stroker->has_first_face = TRUE;
+	}
+
+	stroker->current_face = face;
+	stroker->has_current_face = TRUE;
+    }
+
+    /* Temporarily modify the stroker to use round joins to guarantee
+     * smooth stroked curves. */
+    line_join_save = stroker->style.line_join;
+    stroker->style.line_join = CAIRO_LINE_JOIN_ROUND;
+
+    status = _cairo_spline_decompose (&spline, stroker->tolerance);
+    if (unlikely (status))
+	return status;
+
+    /* And join the final face */
+    if (! stroker->dash.dashed || stroker->dash.dash_on) {
+	slope_dx = _cairo_fixed_to_double (spline.final_slope.dx);
+	slope_dy = _cairo_fixed_to_double (spline.final_slope.dy);
+	if (_compute_normalized_device_slope (&slope_dx, &slope_dy,
+					      stroker->ctm_inverse, NULL))
+	{
+	    _compute_face (&stroker->current_point,
+			   &spline.final_slope,
+			   slope_dx, slope_dy,
+			   stroker, &face);
+	}
+
+	status = _cairo_stroker_join (stroker, &stroker->current_face, &face);
+	if (unlikely (status))
+	    return status;
+
+	stroker->current_face = face;
+    }
+
+    stroker->style.line_join = line_join_save;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_stroker_close_path (void *closure)
+{
+    cairo_status_t status;
+    cairo_stroker_t *stroker = closure;
+
+    if (stroker->dash.dashed)
+	status = _cairo_stroker_line_to_dashed (stroker, &stroker->first_point);
+    else
+	status = _cairo_stroker_line_to (stroker, &stroker->first_point);
+    if (status)
+	return status;
+
+    if (stroker->has_first_face && stroker->has_current_face) {
+	/* Join first and final faces of sub path */
+	status = _cairo_stroker_join (stroker, &stroker->current_face, &stroker->first_face);
+	if (status)
+	    return status;
+    } else {
+	/* Cap the start and end of the sub path as needed */
+	status = _cairo_stroker_add_caps (stroker);
+	if (status)
+	    return status;
+    }
+
+    stroker->has_initial_sub_path = FALSE;
+    stroker->has_first_face = FALSE;
+    stroker->has_current_face = FALSE;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_int_status_t
+_cairo_path_fixed_stroke_to_traps (const cairo_path_fixed_t	*path,
+				   const cairo_stroke_style_t	*stroke_style,
+				   const cairo_matrix_t	*ctm,
+				   const cairo_matrix_t	*ctm_inverse,
+				   double		 tolerance,
+				   cairo_traps_t	*traps)
+{
+    cairo_int_status_t status;
+    cairo_stroker_t stroker;
+
+    status = _cairo_stroker_init (&stroker, stroke_style,
+			          ctm, ctm_inverse, tolerance,
+				  traps);
+    if (unlikely (status))
+	return status;
+
+    if (stroker.style.dash)
+	status = _cairo_path_fixed_interpret (path,
+					      _cairo_stroker_move_to_dashed,
+					      _cairo_stroker_line_to_dashed,
+					      _cairo_stroker_curve_to,
+					      _cairo_stroker_close_path,
+					      &stroker);
+    else
+	status = _cairo_path_fixed_interpret (path,
+					      _cairo_stroker_move_to,
+					      _cairo_stroker_line_to,
+					      _cairo_stroker_curve_to,
+					      _cairo_stroker_close_path,
+					      &stroker);
+    /* Cap the start and end of the final sub path as needed */
+    if (likely (status == CAIRO_INT_STATUS_SUCCESS))
+	    status = _cairo_stroker_add_caps (&stroker);
+
+    _cairo_stroker_fini (&stroker);
+
+    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_traps_t *traps;
+    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_traps_t			*traps)
+{
+    /* 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->traps = traps;
+
+    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->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->traps, a, b);
+	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->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->traps, &p1, &p2);
+	    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->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->traps, a, b);
+	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,
+					   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->maybe_region = antialias == CAIRO_ANTIALIAS_NONE || path->fill_maybe_region;
+    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;
+}
diff --git a/src/cairo-path-stroke.c b/src/cairo-path-stroke.c
index 56eb97d..10d4963 100644
--- a/src/cairo-path-stroke.c
+++ b/src/cairo-path-stroke.c
@@ -1315,722 +1315,3 @@ BAIL:
 
     return status;
 }
-
-cairo_int_status_t
-_cairo_path_fixed_stroke_to_traps (const cairo_path_fixed_t	*path,
-				   const cairo_stroke_style_t	*stroke_style,
-				   const cairo_matrix_t	*ctm,
-				   const cairo_matrix_t	*ctm_inverse,
-				   double		 tolerance,
-				   cairo_traps_t	*traps)
-{
-    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,
-						  stroke_style,
-						  ctm,
-						  ctm_inverse,
-						  tolerance,
-						  &polygon);
-    if (unlikely (status))
-	goto BAIL;
-
-    status = _cairo_polygon_status (&polygon);
-    if (unlikely (status))
-	goto BAIL;
-
-    status = _cairo_bentley_ottmann_tessellate_polygon (traps, &polygon,
-							CAIRO_FILL_RULE_WINDING);
-
-BAIL:
-    _cairo_polygon_fini (&polygon);
-
-    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;
-}
diff --git a/src/cairo-recording-surface.c b/src/cairo-recording-surface.c
index be803ec..254768c 100644
--- a/src/cairo-recording-surface.c
+++ b/src/cairo-recording-surface.c
@@ -86,6 +86,7 @@
 #include "cairo-image-surface-private.h"
 #include "cairo-recording-surface-private.h"
 #include "cairo-surface-wrapper-private.h"
+#include "cairo-traps-private.h"
 
 typedef enum {
     CAIRO_RECORDING_REPLAY,
diff --git a/src/cairo-surface-fallback.c b/src/cairo-surface-fallback.c
index b65b2bf..824a64d 100644
--- a/src/cairo-surface-fallback.c
+++ b/src/cairo-surface-fallback.c
@@ -49,6 +49,7 @@
 #include "cairo-region-private.h"
 #include "cairo-spans-private.h"
 #include "cairo-surface-fallback-private.h"
+#include "cairo-traps-private.h"
 
 typedef struct {
     cairo_surface_t *dst;
@@ -1053,6 +1054,7 @@ _cairo_surface_fallback_stroke (cairo_surface_t		*surface,
     _cairo_polygon_init_with_clip (&polygon, extents.clip);
     _cairo_traps_init_with_clip (&traps, extents.clip);
 
+#if 0
     if (_cairo_path_fixed_stroke_is_rectilinear (path)) {
 	status = _cairo_path_fixed_stroke_rectilinear_to_traps (path,
 								stroke_style,
@@ -1065,6 +1067,7 @@ _cairo_surface_fallback_stroke (cairo_surface_t		*surface,
 	if (_cairo_status_is_error (status))
 	    goto CLEANUP;
     }
+#endif
 
     status = _cairo_path_fixed_stroke_to_polygon (path,
 						  stroke_style,
diff --git a/src/cairo-traps-private.h b/src/cairo-traps-private.h
new file mode 100644
index 0000000..1d52121
--- /dev/null
+++ b/src/cairo-traps-private.h
@@ -0,0 +1,122 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2002 University of Southern California
+ * Copyright © 2005 Red Hat, Inc.
+ *
+ * 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>
+ */
+
+#ifndef CAIRO_TRAPS_H
+#define CAIRO_TRAPS_H
+
+#include "cairo-types-private.h"
+#include "cairo-compiler-private.h"
+
+
+/* cairo-traps.c */
+cairo_private void
+_cairo_traps_init (cairo_traps_t *traps);
+
+cairo_private void
+_cairo_traps_init_with_clip (cairo_traps_t *traps,
+			     const cairo_clip_t *clip);
+
+cairo_private void
+_cairo_traps_init_with_clip (cairo_traps_t *traps,
+			     const cairo_clip_t *clip);
+
+cairo_private void
+_cairo_traps_limit (cairo_traps_t	*traps,
+		    const cairo_box_t	*boxes,
+		    int			 num_boxes);
+
+cairo_private cairo_status_t
+_cairo_traps_init_boxes (cairo_traps_t	    *traps,
+		         const cairo_boxes_t *boxes);
+
+cairo_private void
+_cairo_traps_clear (cairo_traps_t *traps);
+
+cairo_private void
+_cairo_traps_fini (cairo_traps_t *traps);
+
+#define _cairo_traps_status(T) (T)->status
+
+cairo_private void
+_cairo_traps_translate (cairo_traps_t *traps, int x, int y);
+
+cairo_private cairo_status_t
+_cairo_traps_tessellate_rectangle (cairo_traps_t *traps,
+				   const cairo_point_t *top_left,
+				   const cairo_point_t *bottom_right);
+
+cairo_private cairo_status_t
+_cairo_traps_tessellate_convex_quad (cairo_traps_t *traps,
+				     const cairo_point_t q[4]);
+
+cairo_private cairo_status_t
+_cairo_traps_tessellate_triangle (cairo_traps_t *traps,
+				  const cairo_point_t t[3]);
+
+cairo_private void
+_cairo_traps_add_trap (cairo_traps_t *traps,
+		       cairo_fixed_t top, cairo_fixed_t bottom,
+		       cairo_line_t *left, cairo_line_t *right);
+
+
+cairo_private cairo_int_status_t
+_cairo_path_fixed_stroke_to_traps (const cairo_path_fixed_t	*path,
+				   const cairo_stroke_style_t	*stroke_style,
+				   const cairo_matrix_t	*ctm,
+				   const cairo_matrix_t	*ctm_inverse,
+				   double		 tolerance,
+				   cairo_traps_t	*traps);
+
+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_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_bool_t
+_cairo_traps_to_boxes (cairo_traps_t *traps,
+		       cairo_antialias_t antialias,
+		       cairo_boxes_t *boxes);
+
+#endif /* CAIRO_TRAPS_H */
diff --git a/src/cairo-traps.c b/src/cairo-traps.c
index 42e2eb5..2b9afd3 100644
--- a/src/cairo-traps.c
+++ b/src/cairo-traps.c
@@ -43,6 +43,7 @@
 #include "cairo-error-private.h"
 #include "cairo-region-private.h"
 #include "cairo-slope-private.h"
+#include "cairo-traps-private.h"
 
 /* private functions */
 
@@ -67,15 +68,6 @@ _cairo_traps_init (cairo_traps_t *traps)
 }
 
 void
-_cairo_traps_limit (cairo_traps_t	*traps,
-		    const cairo_box_t	*limits,
-		    int			 num_limits)
-{
-    traps->limits = limits;
-    traps->num_limits = num_limits;
-}
-
-void
 _cairo_traps_init_with_clip (cairo_traps_t *traps,
 			     const cairo_clip_t *clip)
 {
@@ -85,6 +77,15 @@ _cairo_traps_init_with_clip (cairo_traps_t *traps,
 }
 
 void
+_cairo_traps_limit (cairo_traps_t	*traps,
+		    const cairo_box_t	*limits,
+		    int			 num_limits)
+{
+    traps->limits = limits;
+    traps->num_limits = num_limits;
+}
+
+void
 _cairo_traps_clear (cairo_traps_t *traps)
 {
     traps->status = CAIRO_STATUS_SUCCESS;
@@ -305,6 +306,177 @@ _cairo_traps_tessellate_rectangle (cairo_traps_t *traps,
     return traps->status;
 }
 
+static int
+_compare_point_fixed_by_y (const void *av, const void *bv)
+{
+    const cairo_point_t	*a = av, *b = bv;
+
+    int ret = a->y - b->y;
+    if (ret == 0)
+	ret = a->x - b->x;
+    return ret;
+}
+
+cairo_status_t
+_cairo_traps_tessellate_convex_quad (cairo_traps_t *traps,
+				     const cairo_point_t q[4])
+{
+    int a, b, c, d;
+    int i;
+    cairo_slope_t ab, ad;
+    cairo_bool_t b_left_of_d;
+    cairo_line_t left;
+    cairo_line_t right;
+
+    /* Choose a as a point with minimal y */
+    a = 0;
+    for (i = 1; i < 4; i++)
+	if (_compare_point_fixed_by_y (&q[i], &q[a]) < 0)
+	    a = i;
+
+    /* b and d are adjacent to a, while c is opposite */
+    b = (a + 1) % 4;
+    c = (a + 2) % 4;
+    d = (a + 3) % 4;
+
+    /* Choose between b and d so that b.y is less than d.y */
+    if (_compare_point_fixed_by_y (&q[d], &q[b]) < 0) {
+	b = (a + 3) % 4;
+	d = (a + 1) % 4;
+    }
+
+    /* Without freedom left to choose anything else, we have four
+     * cases to tessellate.
+     *
+     * First, we have to determine the Y-axis sort of the four
+     * vertices, (either abcd or abdc). After that we need to detemine
+     * which edges will be "left" and which will be "right" in the
+     * resulting trapezoids. This can be determined by computing a
+     * slope comparison of ab and ad to determine if b is left of d or
+     * not.
+     *
+     * Note that "left of" here is in the sense of which edges should
+     * be the left vs. right edges of the trapezoid. In particular, b
+     * left of d does *not* mean that b.x is less than d.x.
+     *
+     * This should hopefully be made clear in the lame ASCII art
+     * below. Since the same slope comparison is used in all cases, we
+     * compute it before testing for the Y-value sort. */
+
+    /* Note: If a == b then the ab slope doesn't give us any
+     * information. In that case, we can replace it with the ac (or
+     * equivalenly the bc) slope which gives us exactly the same
+     * information we need. At worst the names of the identifiers ab
+     * and b_left_of_d are inaccurate in this case, (would be ac, and
+     * c_left_of_d). */
+    if (q[a].x == q[b].x && q[a].y == q[b].y)
+	_cairo_slope_init (&ab, &q[a], &q[c]);
+    else
+	_cairo_slope_init (&ab, &q[a], &q[b]);
+
+    _cairo_slope_init (&ad, &q[a], &q[d]);
+
+    b_left_of_d = (_cairo_slope_compare (&ab, &ad) > 0);
+
+    if (q[c].y <= q[d].y) {
+	if (b_left_of_d) {
+	    /* Y-sort is abcd and b is left of d, (slope(ab) > slope (ad))
+	     *
+	     *                      top bot left right
+	     *        _a  a  a
+	     *      / /  /|  |\      a.y b.y  ab   ad
+	     *     b /  b |  b \
+	     *    / /   | |   \ \    b.y c.y  bc   ad
+	     *   c /    c |    c \
+	     *  | /      \|     \ \  c.y d.y  cd   ad
+	     *  d         d       d
+	     */
+	    left.p1  = q[a]; left.p2  = q[b];
+	    right.p1 = q[a]; right.p2 = q[d];
+	    _cairo_traps_add_trap (traps, q[a].y, q[b].y, &left, &right);
+	    left.p1  = q[b]; left.p2  = q[c];
+	    _cairo_traps_add_trap (traps, q[b].y, q[c].y, &left, &right);
+	    left.p1  = q[c]; left.p2  = q[d];
+	    _cairo_traps_add_trap (traps, q[c].y, q[d].y, &left, &right);
+	} else {
+	    /* Y-sort is abcd and b is right of d, (slope(ab) <= slope (ad))
+	     *
+	     *       a  a  a_
+	     *      /|  |\  \ \     a.y b.y  ad  ab
+	     *     / b  | b  \ b
+	     *    / /   | |   \ \   b.y c.y  ad  bc
+	     *   / c    | c    \ c
+	     *  / /     |/      \ | c.y d.y  ad  cd
+	     *  d       d         d
+	     */
+	    left.p1  = q[a]; left.p2  = q[d];
+	    right.p1 = q[a]; right.p2 = q[b];
+	    _cairo_traps_add_trap (traps, q[a].y, q[b].y, &left, &right);
+	    right.p1 = q[b]; right.p2 = q[c];
+	    _cairo_traps_add_trap (traps, q[b].y, q[c].y, &left, &right);
+	    right.p1 = q[c]; right.p2 = q[d];
+	    _cairo_traps_add_trap (traps, q[c].y, q[d].y, &left, &right);
+	}
+    } else {
+	if (b_left_of_d) {
+	    /* Y-sort is abdc and b is left of d, (slope (ab) > slope (ad))
+	     *
+	     *        a   a     a
+	     *       //  / \    |\     a.y b.y  ab  ad
+	     *     /b/  b   \   b \
+	     *    / /    \   \   \ \   b.y d.y  bc  ad
+	     *   /d/      \   d   \ d
+	     *  //         \ /     \|  d.y c.y  bc  dc
+	     *  c           c       c
+	     */
+	    left.p1  = q[a]; left.p2  = q[b];
+	    right.p1 = q[a]; right.p2 = q[d];
+	    _cairo_traps_add_trap (traps, q[a].y, q[b].y, &left, &right);
+	    left.p1  = q[b]; left.p2  = q[c];
+	    _cairo_traps_add_trap (traps, q[b].y, q[d].y, &left, &right);
+	    right.p1 = q[d]; right.p2 = q[c];
+	    _cairo_traps_add_trap (traps, q[d].y, q[c].y, &left, &right);
+	} else {
+	    /* Y-sort is abdc and b is right of d, (slope (ab) <= slope (ad))
+	     *
+	     *      a     a   a
+	     *     /|    / \  \\       a.y b.y  ad  ab
+	     *    / b   /   b  \b\
+	     *   / /   /   /    \ \    b.y d.y  ad  bc
+	     *  d /   d   /	 \d\
+	     *  |/     \ /         \\  d.y c.y  dc  bc
+	     *  c       c	   c
+	     */
+	    left.p1  = q[a]; left.p2  = q[d];
+	    right.p1 = q[a]; right.p2 = q[b];
+	    _cairo_traps_add_trap (traps, q[a].y, q[b].y, &left, &right);
+	    right.p1 = q[b]; right.p2 = q[c];
+	    _cairo_traps_add_trap (traps, q[b].y, q[d].y, &left, &right);
+	    left.p1  = q[d]; left.p2  = q[c];
+	    _cairo_traps_add_trap (traps, q[d].y, q[c].y, &left, &right);
+	}
+    }
+
+    return traps->status;
+}
+
+/* A triangle is simply a degenerate case of a convex
+ * quadrilateral. We would not benefit from having any distinct
+ * implementation of triangle vs. quadrilateral tessellation here. */
+cairo_status_t
+_cairo_traps_tessellate_triangle (cairo_traps_t *traps,
+				  const cairo_point_t t[3])
+{
+    cairo_point_t quad[4];
+
+    quad[0] = t[0];
+    quad[1] = t[0];
+    quad[2] = t[1];
+    quad[3] = t[2];
+
+    return _cairo_traps_tessellate_convex_quad (traps, quad);
+}
+
 void
 _cairo_traps_translate (cairo_traps_t *traps, int x, int y)
 {
@@ -608,6 +780,66 @@ _cairo_traps_extract_region (cairo_traps_t   *traps,
     return status;
 }
 
+cairo_bool_t
+_cairo_traps_to_boxes (cairo_traps_t *traps,
+		       cairo_antialias_t antialias,
+		       cairo_boxes_t *boxes)
+{
+    int i;
+
+    for (i = 0; i < traps->num_traps; i++) {
+	if (traps->traps[i].left.p1.x  != traps->traps[i].left.p2.x ||
+	    traps->traps[i].right.p1.x != traps->traps[i].right.p2.x)
+	    return FALSE;
+    }
+
+    _cairo_boxes_init (boxes);
+
+    boxes->num_boxes    = traps->num_traps;
+    boxes->chunks.base  = (cairo_box_t *) traps->traps;
+    boxes->chunks.count = traps->num_traps;
+    boxes->chunks.size  = traps->num_traps;
+
+    if (antialias != CAIRO_ANTIALIAS_NONE) {
+	for (i = 0; i < traps->num_traps; i++) {
+	    /* Note the traps and boxes alias so we need to take the local copies first. */
+	    cairo_fixed_t x1 = traps->traps[i].left.p1.x;
+	    cairo_fixed_t x2 = traps->traps[i].right.p1.x;
+	    cairo_fixed_t y1 = traps->traps[i].top;
+	    cairo_fixed_t y2 = traps->traps[i].bottom;
+
+	    boxes->chunks.base[i].p1.x = x1;
+	    boxes->chunks.base[i].p1.y = y1;
+	    boxes->chunks.base[i].p2.x = x2;
+	    boxes->chunks.base[i].p2.y = y2;
+
+	    if (boxes->is_pixel_aligned) {
+		boxes->is_pixel_aligned =
+		    _cairo_fixed_is_integer (x1) && _cairo_fixed_is_integer (y1) &&
+		    _cairo_fixed_is_integer (x2) && _cairo_fixed_is_integer (y2);
+	    }
+	}
+    } else {
+	boxes->is_pixel_aligned = TRUE;
+
+	for (i = 0; i < traps->num_traps; i++) {
+	    /* Note the traps and boxes alias so we need to take the local copies first. */
+	    cairo_fixed_t x1 = traps->traps[i].left.p1.x;
+	    cairo_fixed_t x2 = traps->traps[i].right.p1.x;
+	    cairo_fixed_t y1 = traps->traps[i].top;
+	    cairo_fixed_t y2 = traps->traps[i].bottom;
+
+	    /* round down here to match Pixman's behavior when using traps. */
+	    boxes->chunks.base[i].p1.x = _cairo_fixed_round_down (x1);
+	    boxes->chunks.base[i].p1.y = _cairo_fixed_round_down (y1);
+	    boxes->chunks.base[i].p2.x = _cairo_fixed_round_down (x2);
+	    boxes->chunks.base[i].p2.y = _cairo_fixed_round_down (y2);
+	}
+    }
+
+    return TRUE;
+}
+
 /* moves trap points such that they become the actual corners of the trapezoid */
 static void
 _sanitize_trap (cairo_trapezoid_t *t)
diff --git a/src/cairo-xcb-surface-render.c b/src/cairo-xcb-surface-render.c
index b42795e..e603341 100644
--- a/src/cairo-xcb-surface-render.c
+++ b/src/cairo-xcb-surface-render.c
@@ -39,6 +39,7 @@
 #include "cairo-surface-offset-private.h"
 #include "cairo-surface-snapshot-private.h"
 #include "cairo-surface-subsurface-private.h"
+#include "cairo-traps-private.h"
 #include "cairo-xcb-private.h"
 
 #define PIXMAN_MAX_INT ((pixman_fixed_1 >> 1) - pixman_fixed_e) /* need to ensure deltas also fit */
@@ -2891,21 +2892,12 @@ _composite_polygon (cairo_xcb_surface_t *dst,
 
     _cairo_traps_init (&traps.traps);
 
-    status = _cairo_bentley_ottmann_tessellate_polygon (&traps.traps, polygon, fill_rule);
+    status = _cairo_bentley_ottmann_tessellate_polygon (&traps.traps,
+						       	polygon,
+						       	fill_rule);
     if (unlikely (status))
 	goto CLEANUP_TRAPS;
 
-    if (traps.traps.has_intersections) {
-	if (traps.traps.is_rectangular)
-	    status = _cairo_bentley_ottmann_tessellate_rectangular_traps (&traps.traps, CAIRO_FILL_RULE_WINDING);
-	else if (traps.traps.is_rectilinear)
-	    status = _cairo_bentley_ottmann_tessellate_rectilinear_traps (&traps.traps, CAIRO_FILL_RULE_WINDING);
-	else
-	    status = _cairo_bentley_ottmann_tessellate_traps (&traps.traps, CAIRO_FILL_RULE_WINDING);
-	if (unlikely (status))
-	    goto CLEANUP_TRAPS;
-    }
-
     /* Use a fast path if the trapezoids consist of a simple region,
      * but we can only do this if we do not have a clip surface, or can
      * substitute the mask with the clip.
@@ -3246,19 +3238,6 @@ _composite_mask_clip (void				*closure,
     if (unlikely (status))
 	return status;
 
-    if (info.traps.has_intersections) {
-	if (info.traps.is_rectangular)
-	    status = _cairo_bentley_ottmann_tessellate_rectangular_traps (&info.traps, CAIRO_FILL_RULE_WINDING);
-	else if (info.traps.is_rectilinear)
-	    status = _cairo_bentley_ottmann_tessellate_rectilinear_traps (&info.traps, CAIRO_FILL_RULE_WINDING);
-	else
-	    status = _cairo_bentley_ottmann_tessellate_traps (&info.traps, CAIRO_FILL_RULE_WINDING);
-	if (unlikely (status)) {
-	    _cairo_traps_fini (&info.traps);
-	    return status;
-	}
-    }
-
     dst->deferred_clear = FALSE; /* assert(trap extents == extents); */
 
     status = _composite_traps (&info,
@@ -3484,33 +3463,97 @@ _cairo_xcb_surface_render_mask (cairo_xcb_surface_t	*surface,
 }
 
 static cairo_int_status_t
-_cairo_xcb_surface_render_stroke_as_polygon (cairo_xcb_surface_t	*dst,
-					     cairo_operator_t		 op,
-					     const cairo_pattern_t	*source,
-					     const cairo_path_fixed_t		*path,
-					     const cairo_stroke_style_t	*stroke_style,
-					     const cairo_matrix_t	*ctm,
-					     const cairo_matrix_t	*ctm_inverse,
-					     double			 tolerance,
-					     cairo_antialias_t		 antialias,
-					     cairo_composite_rectangles_t *extents)
+_composite_traps_as_boxes (cairo_xcb_surface_t *dst,
+			   cairo_operator_t op,
+			   const cairo_pattern_t *src,
+			   composite_traps_info_t *info,
+			   cairo_composite_rectangles_t *extents)
 {
-    cairo_polygon_t polygon;
-    cairo_status_t status;
+    cairo_boxes_t boxes;
 
-    _cairo_polygon_init_with_clip (&polygon, extents->clip);
-    status = _cairo_path_fixed_stroke_to_polygon (path,
-						  stroke_style,
-						  ctm, ctm_inverse,
-						  tolerance,
-						  &polygon);
-    if (likely (status == CAIRO_STATUS_SUCCESS)) {
-	status = _composite_polygon (dst, op, source,
-				     &polygon, antialias,
-				     CAIRO_FILL_RULE_WINDING,
-				     extents);
+    if (! _cairo_traps_to_boxes (&info->traps, info->antialias, &boxes))
+	return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    return _clip_and_composite_boxes (dst, op, src, &boxes, extents);
+}
+
+static cairo_int_status_t
+_clip_and_composite_traps (cairo_xcb_surface_t *dst,
+			   cairo_operator_t op,
+			   const cairo_pattern_t *src,
+			   composite_traps_info_t *info,
+			   cairo_composite_rectangles_t *extents)
+{
+    cairo_int_status_t status;
+
+    status = trim_extents_to_traps (extents, &info->traps);
+    if (unlikely (status != CAIRO_INT_STATUS_SUCCESS))
+	return status;
+
+    status = _composite_traps_as_boxes (dst, op, src, info, extents);
+    if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
+	unsigned int flags = 0;
+
+	/* For unbounded operations, the X11 server will estimate the
+	 * affected rectangle and apply the operation to that. However,
+	 * there are cases where this is an overestimate (e.g. the
+	 * clip-fill-{eo,nz}-unbounded test).
+	 *
+	 * The clip will trim that overestimate to our expectations.
+	 */
+	if (! extents->is_bounded)
+	    flags |= FORCE_CLIP_REGION;
+
+	status = _clip_and_composite (dst, op, src,
+				      _composite_traps, NULL,
+				      info, extents,
+				      need_unbounded_clip (extents) | flags);
     }
-    _cairo_polygon_fini (&polygon);
+
+    return status;
+}
+
+static cairo_int_status_t
+_cairo_xcb_surface_render_stroke_as_traps (cairo_xcb_surface_t	*dst,
+					   cairo_operator_t		 op,
+					   const cairo_pattern_t	*source,
+					   const cairo_path_fixed_t		*path,
+					   const cairo_stroke_style_t	*stroke_style,
+					   const cairo_matrix_t	*ctm,
+					   const cairo_matrix_t	*ctm_inverse,
+					   double			 tolerance,
+					   cairo_antialias_t		 antialias,
+					   cairo_composite_rectangles_t *extents)
+{
+    composite_traps_info_t info;
+    cairo_int_status_t status;
+
+    info.antialias = antialias;
+    _cairo_traps_init_with_clip (&info.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,
+								stroke_style,
+								ctm,
+								antialias,
+								&info.traps);
+	if (likely (status == CAIRO_INT_STATUS_SUCCESS))
+	    status = _clip_and_composite_traps (dst, op, source,
+						&info, extents);
+    }
+
+    if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
+	status = _cairo_path_fixed_stroke_to_traps (path,
+						    stroke_style,
+						    ctm, ctm_inverse,
+						    tolerance,
+						    &info.traps);
+	if (likely (status == CAIRO_INT_STATUS_SUCCESS))
+	    status = _clip_and_composite_traps (dst, op, source,
+						&info, extents);
+    }
+    _cairo_traps_fini (&info.traps);
 
     return status;
 }
@@ -3612,30 +3655,31 @@ _cairo_xcb_surface_render_stroke (cairo_xcb_surface_t	*surface,
 	return status;
 
     status = CAIRO_INT_STATUS_UNSUPPORTED;
-    if (_cairo_path_fixed_stroke_is_rectilinear (path)) {
-	cairo_boxes_t boxes;
-
-	_cairo_boxes_init_with_clip (&boxes, extents.clip);
-	status = _cairo_path_fixed_stroke_rectilinear_to_boxes (path,
-								style,
-								ctm,
-								antialias,
-								&boxes);
-	if (likely (status == CAIRO_INT_STATUS_SUCCESS)) {
-	    status = _clip_and_composite_boxes (surface, op, source,
-						&boxes, &extents);
-	}
-	_cairo_boxes_fini (&boxes);
-    }
+    if (surface->connection->flags & CAIRO_XCB_RENDER_HAS_COMPOSITE_TRAPEZOIDS) {
+	    status = _cairo_xcb_surface_render_stroke_as_traps (surface, op, source,
+								path, style,
+								ctm, ctm_inverse,
+								tolerance, antialias,
+								&extents);
+    } 
 
     if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
-	if (surface->connection->flags & CAIRO_XCB_RENDER_HAS_COMPOSITE_TRAPEZOIDS) {
-	    status = _cairo_xcb_surface_render_stroke_as_polygon (surface, op, source,
-								  path, style,
-								  ctm, ctm_inverse,
-								  tolerance, antialias,
-								  &extents);
-	} else if (surface->connection->flags & CAIRO_XCB_RENDER_HAS_COMPOSITE) {
+	if (_cairo_path_fixed_stroke_is_rectilinear (path)) {
+	    cairo_boxes_t boxes;
+
+	    _cairo_boxes_init_with_clip (&boxes, extents.clip);
+	    status = _cairo_path_fixed_stroke_rectilinear_to_boxes (path,
+								    style,
+								    ctm,
+								    antialias,
+								    &boxes);
+	    if (likely (status == CAIRO_INT_STATUS_SUCCESS)) {
+		status = _clip_and_composite_boxes (surface, op, source,
+						    &boxes, &extents);
+	    }
+	    _cairo_boxes_fini (&boxes);
+	}
+	if (surface->connection->flags & CAIRO_XCB_RENDER_HAS_COMPOSITE) {
 	    status = _cairo_xcb_surface_render_stroke_via_mask (surface, op, source,
 								path, style,
 								ctm, ctm_inverse,
diff --git a/src/cairo-xcb-surface.c b/src/cairo-xcb-surface.c
index abf8fe3..c966794 100644
--- a/src/cairo-xcb-surface.c
+++ b/src/cairo-xcb-surface.c
@@ -628,15 +628,14 @@ _cairo_xcb_surface_flush (void *abstract_surface)
     if (status == CAIRO_STATUS_SUCCESS && ! surface->base.finished) {
 	status = cairo_surface_status (surface->fallback);
 
-	if (status == CAIRO_STATUS_SUCCESS) {
-	    status = _put_image (surface, (cairo_image_surface_t *)surface->fallback);
-	}
+	if (status == CAIRO_STATUS_SUCCESS)
+	    status = _put_image (surface,
+				 (cairo_image_surface_t *) surface->fallback);
 
-	if (status == CAIRO_STATUS_SUCCESS) {
+	if (status == CAIRO_STATUS_SUCCESS)
 	    _cairo_surface_attach_snapshot (&surface->base,
 					    surface->fallback,
 					    cairo_surface_finish);
-	}
     }
 
     cairo_surface_destroy (surface->fallback);
@@ -696,15 +695,19 @@ _cairo_xcb_surface_unmap (void *abstract_surface,
 static cairo_surface_t *
 _cairo_xcb_surface_fallback (cairo_xcb_surface_t *surface)
 {
-    cairo_surface_t *image;
+    if (surface->fallback == NULL) {
+	cairo_surface_t *image;
 
-    image = _get_image (surface, TRUE, 0, 0, surface->width, surface->height);
+	image = _get_image (surface, TRUE,
+			    0, 0, surface->width, surface->height);
 
-    /* If there was a deferred clear, _get_image applied it */
-    if (image->status == CAIRO_STATUS_SUCCESS)
-	surface->deferred_clear = FALSE;
+	/* If there was a deferred clear, _get_image applied it */
+	if (image->status == CAIRO_STATUS_SUCCESS)
+	    surface->deferred_clear = FALSE;
 
-    return image;
+	surface->fallback = image;
+    }
+    return surface->fallback;
 }
 
 static cairo_int_status_t
@@ -714,9 +717,10 @@ _cairo_xcb_surface_paint (void			*abstract_surface,
 			  const cairo_clip_t	*clip)
 {
     cairo_xcb_surface_t *surface = abstract_surface;
-    cairo_int_status_t status;
 
     if (surface->fallback == NULL) {
+	cairo_int_status_t status;
+
 	status = _cairo_xcb_surface_cairo_paint (surface, op, source, clip);
 	if (status != CAIRO_INT_STATUS_UNSUPPORTED)
 	    return status;
@@ -724,11 +728,10 @@ _cairo_xcb_surface_paint (void			*abstract_surface,
 	status = _cairo_xcb_surface_render_paint (surface, op, source, clip);
 	if (status != CAIRO_INT_STATUS_UNSUPPORTED)
 	    return status;
-
-	surface->fallback = _cairo_xcb_surface_fallback (surface);
     }
 
-    return _cairo_surface_paint (surface->fallback, op, source, clip);
+    return _cairo_surface_paint (_cairo_xcb_surface_fallback (surface),
+				 op, source, clip);
 }
 
 static cairo_int_status_t
@@ -739,9 +742,10 @@ _cairo_xcb_surface_mask (void			*abstract_surface,
 			 const cairo_clip_t	*clip)
 {
     cairo_xcb_surface_t *surface = abstract_surface;
-    cairo_int_status_t status;
 
     if (surface->fallback == NULL) {
+	cairo_int_status_t status;
+
 	status =  _cairo_xcb_surface_cairo_mask (surface,
 						 op, source, mask, clip);
 	if (status != CAIRO_INT_STATUS_UNSUPPORTED)
@@ -751,11 +755,9 @@ _cairo_xcb_surface_mask (void			*abstract_surface,
 						  op, source, mask, clip);
 	if (status != CAIRO_INT_STATUS_UNSUPPORTED)
 	    return status;
-
-	surface->fallback = _cairo_xcb_surface_fallback (surface);
     }
 
-    return _cairo_surface_mask (surface->fallback,
+    return _cairo_surface_mask (_cairo_xcb_surface_fallback (surface),
 				op, source, mask,
 				clip);
 }
@@ -773,9 +775,10 @@ _cairo_xcb_surface_stroke (void				*abstract_surface,
 			   const cairo_clip_t		*clip)
 {
     cairo_xcb_surface_t *surface = abstract_surface;
-    cairo_int_status_t status;
 
     if (surface->fallback == NULL) {
+	cairo_int_status_t status;
+
 	status = _cairo_xcb_surface_cairo_stroke (surface, op, source,
 						  path, style,
 						  ctm, ctm_inverse,
@@ -793,11 +796,9 @@ _cairo_xcb_surface_stroke (void				*abstract_surface,
 
 	if (status != CAIRO_INT_STATUS_UNSUPPORTED)
 	    return status;
-
-	surface->fallback = _cairo_xcb_surface_fallback (surface);
     }
 
-    return _cairo_surface_stroke (surface->fallback,
+    return _cairo_surface_stroke (_cairo_xcb_surface_fallback (surface),
 				  op, source,
 				  path, style,
 				  ctm, ctm_inverse,
@@ -816,9 +817,10 @@ _cairo_xcb_surface_fill (void			*abstract_surface,
 			 const cairo_clip_t	*clip)
 {
     cairo_xcb_surface_t *surface = abstract_surface;
-    cairo_int_status_t status;
 
     if (surface->fallback == NULL) {
+	cairo_int_status_t status;
+
 	status = _cairo_xcb_surface_cairo_fill (surface, op, source,
 						path, fill_rule,
 						tolerance, antialias,
@@ -832,11 +834,9 @@ _cairo_xcb_surface_fill (void			*abstract_surface,
 						 clip);
 	if (status != CAIRO_INT_STATUS_UNSUPPORTED)
 	    return status;
-
-	surface->fallback = _cairo_xcb_surface_fallback (surface);
     }
 
-    return _cairo_surface_fill (surface->fallback,
+    return _cairo_surface_fill (_cairo_xcb_surface_fallback (surface),
 				op, source,
 				path, fill_rule,
 				tolerance, antialias,
@@ -854,11 +854,12 @@ _cairo_xcb_surface_glyphs (void				*abstract_surface,
 			   int *num_remaining)
 {
     cairo_xcb_surface_t *surface = abstract_surface;
-    cairo_int_status_t status;
 
     *num_remaining = 0;
 
     if (surface->fallback == NULL) {
+	cairo_int_status_t status;
+
 	status = _cairo_xcb_surface_cairo_glyphs (surface,
 						  op, source,
 						  scaled_font, glyphs, num_glyphs,
@@ -872,11 +873,9 @@ _cairo_xcb_surface_glyphs (void				*abstract_surface,
 						   clip);
 	if (status != CAIRO_INT_STATUS_UNSUPPORTED)
 	    return status;
-
-	surface->fallback = _cairo_xcb_surface_fallback (surface);
     }
 
-    return _cairo_surface_show_text_glyphs (surface->fallback,
+    return _cairo_surface_show_text_glyphs (_cairo_xcb_surface_fallback (surface),
 					    op, source,
 					    NULL, 0,
 					    glyphs, num_glyphs,
diff --git a/src/cairoint.h b/src/cairoint.h
index 1976a7a..9fef9c5 100644
--- a/src/cairoint.h
+++ b/src/cairoint.h
@@ -1376,12 +1376,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,
@@ -1417,13 +1411,6 @@ _cairo_path_fixed_stroke_dashed_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,
@@ -2206,44 +2193,6 @@ _cairo_matrix_to_pixman_matrix_offset (const cairo_matrix_t	*matrix,
 				       int                      *out_x_offset,
 				       int                      *out_y_offset);
 
-/* cairo-traps.c */
-cairo_private void
-_cairo_traps_init (cairo_traps_t *traps);
-
-cairo_private void
-_cairo_traps_init_with_clip (cairo_traps_t *traps,
-			     const cairo_clip_t *clip);
-
-cairo_private void
-_cairo_traps_limit (cairo_traps_t	*traps,
-		    const cairo_box_t	*boxes,
-		    int			 num_boxes);
-
-cairo_private cairo_status_t
-_cairo_traps_init_boxes (cairo_traps_t	    *traps,
-		         const cairo_boxes_t *boxes);
-
-cairo_private void
-_cairo_traps_clear (cairo_traps_t *traps);
-
-cairo_private void
-_cairo_traps_fini (cairo_traps_t *traps);
-
-#define _cairo_traps_status(T) (T)->status
-
-cairo_private void
-_cairo_traps_translate (cairo_traps_t *traps, int x, int y);
-
-cairo_private cairo_status_t
-_cairo_traps_tessellate_rectangle (cairo_traps_t *traps,
-				   const cairo_point_t *top_left,
-				   const cairo_point_t *bottom_right);
-
-cairo_private void
-_cairo_traps_add_trap (cairo_traps_t *traps,
-		       cairo_fixed_t top, cairo_fixed_t bottom,
-		       cairo_line_t *left, cairo_line_t *right);
-
 cairo_private cairo_status_t
 _cairo_bentley_ottmann_tessellate_rectilinear_polygon (cairo_traps_t	 *traps,
 						       const cairo_polygon_t *polygon,
commit b546c0a98d53854433cedabec619ad63ba598b79
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Tue Aug 9 18:53:05 2011 +0100

    types

diff --git a/src/cairo-contour-private.h b/src/cairo-contour-private.h
index ef9ad9d..0898db8 100644
--- a/src/cairo-contour-private.h
+++ b/src/cairo-contour-private.h
@@ -49,15 +49,23 @@ CAIRO_BEGIN_DECLS
  * contains no holes, but maybe either concave or convex.
  */
 
+struct _cairo_contour_chain {
+    cairo_point_t *points;
+    int num_points, size_points;
+    struct _cairo_contour_chain *next;
+};
+
+struct _cairo_contour_iter {
+    cairo_point_t *point;
+    cairo_contour_chain_t *chain;
+};
+
 struct _cairo_contour {
+    cairo_list_t next;
     int direction;
-    struct _cairo_contour_chain {
-	cairo_point_t *points;
-	int num_points, size_points;
-	struct _cairo_contour_chain *next;
-    } chain, *tail;
+    cairo_contour_chain_t chain, *tail;
+
     cairo_point_t embedded_points[64];
-    cairo_list_t next;
 };
 
 /* Initial definition of a shape is a set of contours (some representing holes) */
diff --git a/src/cairo-contour.c b/src/cairo-contour.c
index aa3ad6d..603a49a 100644
--- a/src/cairo-contour.c
+++ b/src/cairo-contour.c
@@ -60,14 +60,14 @@ cairo_int_status_t
 __cairo_contour_add_point (cairo_contour_t *contour,
 			  const cairo_point_t *point)
 {
-    struct _cairo_contour_chain *tail = contour->tail;
-    struct _cairo_contour_chain *next;
+    cairo_contour_chain_t *tail = contour->tail;
+    cairo_contour_chain_t *next;
 
     assert (tail->next == NULL);
 
     next = _cairo_malloc_ab_plus_c (tail->size_points*2,
 				    sizeof (cairo_point_t),
-				    sizeof (struct _cairo_contour_chain));
+				    sizeof (cairo_contour_chain_t));
     if (unlikely (next == NULL))
 	return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
@@ -85,7 +85,7 @@ __cairo_contour_add_point (cairo_contour_t *contour,
 static void
 first_inc (cairo_contour_t *contour,
 	   cairo_point_t **p,
-	   struct _cairo_contour_chain **chain)
+	   cairo_contour_chain_t **chain)
 {
     if (*p == (*chain)->points + (*chain)->num_points) {
 	assert ((*chain)->next);
@@ -98,10 +98,10 @@ first_inc (cairo_contour_t *contour,
 static void
 last_dec (cairo_contour_t *contour,
 	  cairo_point_t **p,
-	  struct _cairo_contour_chain **chain)
+	  cairo_contour_chain_t **chain)
 {
     if (*p == (*chain)->points) {
-	struct _cairo_contour_chain *prev;
+	cairo_contour_chain_t *prev;
 	assert (*chain != &contour->chain);
 	for (prev = &contour->chain; prev->next != *chain; prev = prev->next)
 	    ;
@@ -114,7 +114,7 @@ last_dec (cairo_contour_t *contour,
 void
 _cairo_contour_reverse (cairo_contour_t *contour)
 {
-    struct _cairo_contour_chain *first_chain, *last_chain;
+    cairo_contour_chain_t *first_chain, *last_chain;
     cairo_point_t *first, *last;
 
     contour->direction = -contour->direction;
@@ -144,7 +144,7 @@ cairo_int_status_t
 _cairo_contour_add (cairo_contour_t *dst,
 		    const cairo_contour_t *src)
 {
-    const struct _cairo_contour_chain *chain;
+    const cairo_contour_chain_t *chain;
     cairo_int_status_t status;
     int i;
 
@@ -159,13 +159,8 @@ _cairo_contour_add (cairo_contour_t *dst,
     return CAIRO_INT_STATUS_SUCCESS;
 }
 
-struct _cairo_contour_iter {
-    struct _cairo_contour_chain *chain;
-    cairo_point_t *point;
-};
-
 static inline cairo_bool_t
-iter_next (struct _cairo_contour_iter *iter)
+iter_next (cairo_contour_iter_t *iter)
 {
     if (iter->point == &iter->chain->points[iter->chain->size_points-1]) {
 	iter->chain = iter->chain->next;
@@ -181,30 +176,30 @@ iter_next (struct _cairo_contour_iter *iter)
 }
 
 static cairo_bool_t
-iter_equal (const struct _cairo_contour_iter *i1,
-	    const struct _cairo_contour_iter *i2)
+iter_equal (const cairo_contour_iter_t *i1,
+	    const cairo_contour_iter_t *i2)
 {
     return i1->chain == i2->chain && i1->point == i2->point;
 }
 
 static void
-iter_init (struct _cairo_contour_iter *iter, cairo_contour_t *contour)
+iter_init (cairo_contour_iter_t *iter, cairo_contour_t *contour)
 {
     iter->chain = &contour->chain;
     iter->point = &contour->chain.points[0];
 }
 
 static void
-iter_init_last (struct _cairo_contour_iter *iter, cairo_contour_t *contour)
+iter_init_last (cairo_contour_iter_t *iter, cairo_contour_t *contour)
 {
     iter->chain = contour->tail;
     iter->point = &contour->tail->points[contour->tail->num_points-1];
 }
 
-static const struct _cairo_contour_chain *prev_const_chain(const cairo_contour_t *contour,
-							   const struct _cairo_contour_chain *chain)
+static const cairo_contour_chain_t *prev_const_chain(const cairo_contour_t *contour,
+						     const cairo_contour_chain_t *chain)
 {
-    const struct _cairo_contour_chain *prev;
+    const cairo_contour_chain_t *prev;
 
     if (chain == &contour->chain)
 	return NULL;
@@ -219,7 +214,7 @@ cairo_int_status_t
 _cairo_contour_add_reversed (cairo_contour_t *dst,
 			     const cairo_contour_t *src)
 {
-    const struct _cairo_contour_chain *last;
+    const cairo_contour_chain_t *last;
     cairo_int_status_t status;
     int i;
 
@@ -251,10 +246,10 @@ _cairo_point_distance2 (const cairo_point_t *p1, const cairo_point_t *p2)
 
 static cairo_bool_t
 _cairo_contour_simplify_chain (cairo_contour_t *contour, const double tolerance,
-			       const struct _cairo_contour_iter *first,
-			       const struct _cairo_contour_iter *last)
+			       const cairo_contour_iter_t *first,
+			       const cairo_contour_iter_t *last)
 {
-    struct _cairo_contour_iter iter, furthest;
+    cairo_contour_iter_t iter, furthest;
     uint64_t max_error;
     int x0, y0;
     int nx, ny;
@@ -311,9 +306,9 @@ _cairo_contour_simplify_chain (cairo_contour_t *contour, const double tolerance,
 void
 _cairo_contour_simplify (cairo_contour_t *contour, double tolerance)
 {
-    struct _cairo_contour_chain *chain;
+    cairo_contour_chain_t *chain;
     cairo_point_t *last = NULL;
-    struct _cairo_contour_iter iter, furthest;
+    cairo_contour_iter_t iter, furthest;
     cairo_bool_t simplified;
     uint64_t max = 0;
     int i;
@@ -386,7 +381,7 @@ _cairo_contour_simplify (cairo_contour_t *contour, double tolerance)
     }
 
     if (iter.chain) {
-	struct _cairo_contour_chain *next;
+	cairo_contour_chain_t *next;
 
 	for (chain = iter.chain->next; chain; chain = next) {
 	    next = chain->next;
@@ -408,7 +403,7 @@ _cairo_contour_reset (cairo_contour_t *contour)
 void
 _cairo_contour_fini (cairo_contour_t *contour)
 {
-    struct _cairo_contour_chain *chain, *next;
+    cairo_contour_chain_t *chain, *next;
 
     for (chain = contour->chain.next; chain; chain = next) {
 	next = chain->next;
@@ -419,7 +414,7 @@ _cairo_contour_fini (cairo_contour_t *contour)
 void
 _cairo_debug_print_contour (FILE *file, cairo_contour_t *contour)
 {
-    struct _cairo_contour_chain *chain;
+    cairo_contour_chain_t *chain;
     int num_points, size_points;
     int i;
 
@@ -447,7 +442,7 @@ _cairo_debug_print_contour (FILE *file, cairo_contour_t *contour)
 void
 __cairo_contour_remove_last_chain (cairo_contour_t *contour)
 {
-    struct _cairo_contour_chain *chain;
+    cairo_contour_chain_t *chain;
 
     if (contour->tail == &contour->chain)
 	return;
diff --git a/src/cairo-types-private.h b/src/cairo-types-private.h
index a1d4863..1fa25fd 100644
--- a/src/cairo-types-private.h
+++ b/src/cairo-types-private.h
@@ -64,6 +64,8 @@ typedef struct _cairo_clip_path cairo_clip_path_t;
 typedef struct _cairo_color cairo_color_t;
 typedef struct _cairo_color_stop cairo_color_stop_t;
 typedef struct _cairo_contour cairo_contour_t;
+typedef struct _cairo_contour_chain cairo_contour_chain_t;
+typedef struct _cairo_contour_iter cairo_contour_iter_t;
 typedef struct _cairo_device_backend cairo_device_backend_t;
 typedef struct _cairo_font_face_backend     cairo_font_face_backend_t;
 typedef struct _cairo_gstate cairo_gstate_t;
commit d58c0083762302e866598040134b86f3c3e0aeb5
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Tue Aug 9 10:37:15 2011 +0100

    meh

diff --git a/perf/micro/many-strokes.c b/perf/micro/many-strokes.c
index f5b3a8c..7a31f0e 100644
--- a/perf/micro/many-strokes.c
+++ b/perf/micro/many-strokes.c
@@ -175,6 +175,8 @@ many_strokes (cairo_perf_t *perf, cairo_t *cr, int width, int height)
     if (! cairo_perf_can_run (perf, "many-strokes", NULL))
 	return;
 
+    cairo_set_source_rgb (cr, 1., 1., 1.);
+
     cairo_perf_run (perf, "many-strokes-halign", do_many_strokes_ha, NULL);
     cairo_perf_run (perf, "many-strokes-valign", do_many_strokes_va, NULL);
     cairo_perf_run (perf, "many-strokes-horizontal", do_many_strokes_h, NULL);
diff --git a/src/cairo-path-fixed.c b/src/cairo-path-fixed.c
index 5f754df..8d661f4 100644
--- a/src/cairo-path-fixed.c
+++ b/src/cairo-path-fixed.c
@@ -230,7 +230,7 @@ int
 _cairo_path_fixed_num_ops (const cairo_path_fixed_t *path)
 {
     const cairo_path_buf_t *buf;
-    int num_ops;
+    int num_ops = 0;
 
     cairo_path_foreach_buf_start (buf, path) {
 	num_ops += buf->num_ops;
diff --git a/src/cairo-path-stroke-polygon.c b/src/cairo-path-stroke-polygon.c
index 6f7ba07..f73102d 100644
--- a/src/cairo-path-stroke-polygon.c
+++ b/src/cairo-path-stroke-polygon.c
@@ -358,6 +358,8 @@ inner_close (struct stroker *stroker,
     if (1 || inner->contour.chain.num_points == 0) {
 	contour_add_point (stroker, inner, &in->point);
 	contour_add_point (stroker, inner, inpt);
+	*_cairo_contour_first_point (&inner->contour) =
+	    *_cairo_contour_last_point (&inner->contour);
 	return;
     }
 
@@ -428,8 +430,11 @@ outer_close (struct stroker *stroker,
 	outer = &stroker->ccw;
     }
 
-    if (close_enough (inpt, outpt, stroker->contour_tolerance))
+    if (close_enough (inpt, outpt, stroker->contour_tolerance)) {
+	*_cairo_contour_first_point (&outer->contour) =
+	    *_cairo_contour_last_point (&outer->contour);
 	return;
+    }
 
     switch (stroker->style.line_join) {
     case CAIRO_LINE_JOIN_ROUND:
@@ -1122,14 +1127,23 @@ spline_to (void *closure,
     if (dump)
 	_cairo_contour_add_point (&stroker->path, point);
 #endif
-
-    compute_face (point, tangent, stroker, &face);
-
-    if (1) {
+    if (tangent->dx == 0 && tangent->dy == 0) {
 	const cairo_point_t *inpt, *outpt;
 	struct stroke_contour *outer;
+	cairo_point_t t;
 	int clockwise;
 
+	face = stroker->current_face;
+
+	face.usr_vector.x = -face.usr_vector.x;
+	face.usr_vector.y = -face.usr_vector.y;
+	face.dev_vector.dx = -face.dev_vector.dx;
+	face.dev_vector.dy = -face.dev_vector.dy;
+
+	t = face.cw;
+	face.cw = face.ccw;
+	face.ccw = t;
+
 	clockwise = join_is_clockwise (&stroker->current_face, &face);
 	if (clockwise) {
 	    inpt = &stroker->current_face.cw;
@@ -1146,11 +1160,13 @@ spline_to (void *closure,
 		 &face.dev_vector,
 		 &stroker->current_face.point, inpt, outpt,
 		 clockwise, outer);
+    } else {
+	compute_face (point, tangent, stroker, &face);
+	contour_add_point (stroker, &stroker->cw, &face.cw);
+	contour_add_point (stroker, &stroker->ccw, &face.ccw);
     }
 
     stroker->current_face = face;
-    contour_add_point (stroker, &stroker->cw, &face.cw);
-    contour_add_point (stroker, &stroker->ccw, &face.ccw);
 
     return CAIRO_STATUS_SUCCESS;
 }
commit ea0d7015eda875139887f540642978b8b499fff3
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Mon Aug 8 23:09:11 2011 +0100

    disable

diff --git a/src/cairo-path-stroke-polygon.c b/src/cairo-path-stroke-polygon.c
index 5603bc7..6f7ba07 100644
--- a/src/cairo-path-stroke-polygon.c
+++ b/src/cairo-path-stroke-polygon.c
@@ -47,7 +47,7 @@
 #include "cairo-path-fixed-private.h"
 #include "cairo-slope-private.h"
 
-#define DEBUG 1
+#define DEBUG 0
 
 static int dump;
 static int dump_once;
@@ -299,6 +299,7 @@ inner_join (struct stroker *stroker,
 	negate = 0;
     }
 
+#if 0
     half_line_width = CAIRO_FIXED_ONE*CAIRO_FIXED_ONE/2 * stroker->style.line_width * out->length + .5;
 
     /* On the inside, the previous end-point is always
@@ -326,6 +327,10 @@ prev:
 
     compute_inner_joint (&last, d_last, p, d_p, half_line_width);
     contour_add_point (stroker, inner, &last);
+#else
+    contour_add_point (stroker, inner, &in->point);
+    contour_add_point (stroker, inner, outpt);
+#endif
 }
 
 static void
@@ -351,7 +356,8 @@ inner_close (struct stroker *stroker,
     }
 
     if (1 || inner->contour.chain.num_points == 0) {
-	contour_add_point (stroker, inner, outpt);
+	contour_add_point (stroker, inner, &in->point);
+	contour_add_point (stroker, inner, inpt);
 	return;
     }
 
@@ -1221,9 +1227,9 @@ close_path (void *closure)
 	    _cairo_debug_print_contour (file, &stroker->cw.contour);
 	    _cairo_debug_print_contour (file, &stroker->ccw.contour);
 	    fclose (file);
-	}
 
-	_cairo_contour_reset (&stroker->path);
+	    _cairo_contour_reset (&stroker->path);
+	}
 #endif
 	_cairo_contour_reset (&stroker->cw.contour);
 	_cairo_contour_reset (&stroker->ccw.contour);
@@ -1259,10 +1265,14 @@ _cairo_path_fixed_stroke_to_polygon (const cairo_path_fixed_t	*path,
 							   polygon);
     }
 
+#if 0
     if (dump_once == 0 && _cairo_path_fixed_num_ops (path) > 1000) {
 	dump = 1;
 	dump_once = 1;
     }
+#else
+    dump = 1;
+#endif
 
     stroker.style = *style;
     stroker.ctm = ctm;
commit bf6bb2ac512a2396ce4c7b6abf17513ce0bf7575
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Mon Aug 8 22:36:57 2011 +0100

    spans-fix

diff --git a/src/cairo-tor-scan-converter.c b/src/cairo-tor-scan-converter.c
index 9cf9f2f..3f709ee 100644
--- a/src/cairo-tor-scan-converter.c
+++ b/src/cairo-tor-scan-converter.c
@@ -1699,7 +1699,7 @@ glitter_scan_converter_render(
 
 	/* Determine if we can ignore this row or use the full pixel
 	 * stepper. */
-	if (GRID_Y == EDGE_Y_BUCKET_HEIGHT && ! polygon->y_buckets[i]) {
+	if (! polygon->y_buckets[i]) {
 	    if (active->head.next == &active->tail) {
 		active->min_height = INT_MAX;
 		for (; j < h && ! polygon->y_buckets[j]; j++)
commit 16f3a40412b16ea11498d364c5133443e4507767
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Sun Aug 7 17:24:37 2011 +0100

    Add a HTML5 Canvas backend
    
    Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>

diff --git a/boilerplate/Makefile.sources b/boilerplate/Makefile.sources
index 63ef34e..22caf8e 100644
--- a/boilerplate/Makefile.sources
+++ b/boilerplate/Makefile.sources
@@ -21,6 +21,7 @@ cairo_boilerplate_private = \
 
 cairo_boilerplate_beos_cxx_sources = cairo-boilerplate-beos.cpp
 cairo_boilerplate_directfb_sources = cairo-boilerplate-directfb.c
+cairo_boilerplate_canvas_sources = cairo-boilerplate-canvas.c
 cairo_boilerplate_drm_sources = cairo-boilerplate-drm.c
 cairo_boilerplate_glx_sources = cairo-boilerplate-glx.c
 cairo_boilerplate_wgl_sources = cairo-boilerplate-wgl.c
diff --git a/boilerplate/Makefile.win32.features b/boilerplate/Makefile.win32.features
index 3542699..fc0b7c2 100644
--- a/boilerplate/Makefile.win32.features
+++ b/boilerplate/Makefile.win32.features
@@ -418,6 +418,18 @@ enabled_cairo_boilerplate_private += $(cairo_boilerplate_recording_private)
 enabled_cairo_boilerplate_cxx_sources += $(cairo_boilerplate_recording_cxx_sources)
 enabled_cairo_boilerplate_sources += $(cairo_boilerplate_recording_sources)
 
+unsupported_cairo_boilerplate_headers += $(cairo_boilerplate_canvas_headers)
+all_cairo_boilerplate_headers += $(cairo_boilerplate_canvas_headers)
+all_cairo_boilerplate_private += $(cairo_boilerplate_canvas_private)
+all_cairo_boilerplate_cxx_sources += $(cairo_boilerplate_canvas_cxx_sources)
+all_cairo_boilerplate_sources += $(cairo_boilerplate_canvas_sources)
+ifeq ($(CAIRO_HAS_CANVAS_SURFACE),1)
+enabled_cairo_boilerplate_headers += $(cairo_boilerplate_canvas_headers)
+enabled_cairo_boilerplate_private += $(cairo_boilerplate_canvas_private)
+enabled_cairo_boilerplate_cxx_sources += $(cairo_boilerplate_canvas_cxx_sources)
+enabled_cairo_boilerplate_sources += $(cairo_boilerplate_canvas_sources)
+endif
+
 unsupported_cairo_boilerplate_headers += $(cairo_boilerplate_tee_headers)
 all_cairo_boilerplate_headers += $(cairo_boilerplate_tee_headers)
 all_cairo_boilerplate_private += $(cairo_boilerplate_tee_private)
diff --git a/boilerplate/cairo-boilerplate-canvas.c b/boilerplate/cairo-boilerplate-canvas.c
new file mode 100644
index 0000000..3d12087
--- /dev/null
+++ b/boilerplate/cairo-boilerplate-canvas.c
@@ -0,0 +1,172 @@
+/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
+/*
+ * Copyright © 2011 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Chris Wilson <chris at chris-wilson.co.uk>
+ *
+ */
+
+#include "cairo-boilerplate-private.h"
+
+#include <cairo-canvas.h>
+
+#if HAVE_SIGNAL_H
+#include <signal.h>
+#endif
+
+static const cairo_user_data_key_t canvas_closure_key;
+
+typedef struct _canvas_target_closure
+{
+    cairo_device_t *device;
+    char	*filename;
+    int		 width;
+    int		 height;
+} canvas_target_closure_t;
+
+#define ARRAY_LENGTH(__array) ((int) (sizeof (__array) / sizeof (__array[0])))
+
+static cairo_surface_t *
+_cairo_boilerplate_canvas_create_surface (const char		 *name,
+				       cairo_content_t		  content,
+				       double			  width,
+				       double			  height,
+				       double			  max_width,
+				       double			  max_height,
+				       cairo_boilerplate_mode_t   mode,
+				       int			  id,
+				       void			**closure)
+{
+    canvas_target_closure_t *ptc;
+    cairo_surface_t *surface;
+    cairo_status_t status;
+
+    *closure = ptc = xmalloc (sizeof (canvas_target_closure_t));
+
+    ptc->width = ceil (width);
+    ptc->height = ceil (height);
+
+    xasprintf (&ptc->filename, "%s.out.html", name);
+    xunlink (ptc->filename);
+
+   ptc->device = cairo_canvas_create (ptc->filename);
+   cairo_canvas_emit_basic_page (ptc->device);
+
+    surface = cairo_canvas_surface_create (ptc->device, NULL, width, height);
+    if (cairo_surface_status (surface))
+	goto CLEANUP_FILENAME;
+
+
+    status = cairo_surface_set_user_data (surface, &canvas_closure_key, ptc, NULL);
+    if (status == CAIRO_STATUS_SUCCESS)
+	return surface;
+
+    cairo_surface_destroy (surface);
+    surface = cairo_boilerplate_surface_create_in_error (status);
+
+  CLEANUP_FILENAME:
+    cairo_device_destroy (ptc->device);
+    free (ptc->filename);
+    free (ptc);
+    return surface;
+}
+
+static cairo_status_t
+_cairo_boilerplate_canvas_finish_surface (cairo_surface_t *surface)
+{
+    canvas_target_closure_t *ptc = cairo_surface_get_user_data (surface,
+							     &canvas_closure_key);
+    cairo_status_t status;
+
+    cairo_surface_finish (surface);
+    status = cairo_surface_status (surface);
+    if (status)
+	return status;
+
+    cairo_device_destroy (ptc->device);
+    ptc->device = NULL;
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_boilerplate_canvas_surface_write_to_png (cairo_surface_t *surface,
+					     const char      *filename)
+{
+    return CAIRO_STATUS_WRITE_ERROR;
+}
+
+static cairo_surface_t *
+_cairo_boilerplate_canvas_convert_to_image (cairo_surface_t *surface,
+					 int		  page)
+{
+    canvas_target_closure_t *ptc =
+	cairo_surface_get_user_data (surface, &canvas_closure_key);
+
+    return cairo_boilerplate_convert_to_image (ptc->filename, page);
+}
+
+static cairo_surface_t *
+_cairo_boilerplate_canvas_get_image_surface (cairo_surface_t *surface,
+					  int		   page,
+					  int		   width,
+					  int		   height)
+{
+    cairo_surface_t *image;
+
+    image = _cairo_boilerplate_canvas_convert_to_image (surface, page);
+    cairo_surface_set_device_offset (image,
+				     cairo_image_surface_get_width (image) - width,
+				     cairo_image_surface_get_height (image) - height);
+    surface = _cairo_boilerplate_get_image_surface (image, 0, width, height);
+    cairo_surface_destroy (image);
+
+    return surface;
+}
+
+static void
+_cairo_boilerplate_canvas_cleanup (void *closure)
+{
+    canvas_target_closure_t *ptc = closure;
+    cairo_device_destroy (ptc->device);
+    free (ptc->filename);
+    free (ptc);
+}
+
+
+static const cairo_boilerplate_target_t targets[] = {
+    {
+	"canvas", "canvas", ".canvas", NULL,
+	CAIRO_SURFACE_TYPE_CANVAS,
+	CAIRO_CONTENT_COLOR_ALPHA, 0,
+	"cairo_canvas_surface_create",
+	_cairo_boilerplate_canvas_create_surface,
+	cairo_surface_create_similar,
+	NULL,
+	_cairo_boilerplate_canvas_finish_surface,
+	_cairo_boilerplate_canvas_get_image_surface,
+	_cairo_boilerplate_canvas_surface_write_to_png,
+	_cairo_boilerplate_canvas_cleanup,
+	NULL, NULL, FALSE, TRUE, TRUE
+    },
+};
+CAIRO_BOILERPLATE (canvas, targets)
diff --git a/build/Makefile.win32.features b/build/Makefile.win32.features
index 5d75ba3..100e41c 100644
--- a/build/Makefile.win32.features
+++ b/build/Makefile.win32.features
@@ -31,6 +31,7 @@ CAIRO_HAS_PS_SURFACE=1
 CAIRO_HAS_PDF_SURFACE=1
 CAIRO_HAS_SVG_SURFACE=1
 CAIRO_HAS_TEST_SURFACES=0
+CAIRO_HAS_CANVAS_SURFACE=0
 CAIRO_HAS_TEE_SURFACE=0
 CAIRO_HAS_XML_SURFACE=0
 CAIRO_HAS_PTHREAD=0
diff --git a/build/Makefile.win32.features-h b/build/Makefile.win32.features-h
index 79de87d..edd5ca0 100644
--- a/build/Makefile.win32.features-h
+++ b/build/Makefile.win32.features-h
@@ -101,6 +101,9 @@ endif
 	@echo "#define CAIRO_HAS_IMAGE_SURFACE 1" >> $(top_srcdir)/src/cairo-features.h
 	@echo "#define CAIRO_HAS_MIME_SURFACE 1" >> $(top_srcdir)/src/cairo-features.h
 	@echo "#define CAIRO_HAS_RECORDING_SURFACE 1" >> $(top_srcdir)/src/cairo-features.h
+ifeq ($(CAIRO_HAS_CANVAS_SURFACE),1)
+	@echo "#define CAIRO_HAS_CANVAS_SURFACE 1" >> $(top_srcdir)/src/cairo-features.h
+endif
 ifeq ($(CAIRO_HAS_TEE_SURFACE),1)
 	@echo "#define CAIRO_HAS_TEE_SURFACE 1" >> $(top_srcdir)/src/cairo-features.h
 endif
diff --git a/build/configure.ac.features b/build/configure.ac.features
index 9c5680a..a6ca856 100644
--- a/build/configure.ac.features
+++ b/build/configure.ac.features
@@ -366,6 +366,7 @@ AC_DEFUN([CAIRO_REPORT],
 	echo "  Image:         yes (always builtin)"
 	echo "  Recording:     yes (always builtin)"
 	echo "  Mime:          yes (always builtin)"
+	echo "  HTML5 Canvas:  $use_canvas"
 	echo "  Tee:           $use_tee"
 	echo "  XML:           $use_xml"
 	echo "  Skia:          $use_skia"
diff --git a/configure.ac b/configure.ac
index 9fe6690..5df61a2 100644
--- a/configure.ac
+++ b/configure.ac
@@ -614,6 +614,7 @@ dnl ===========================================================================
 
 CAIRO_ENABLE_SURFACE_BACKEND(mime, mime, always)
 CAIRO_ENABLE_SURFACE_BACKEND(recording, recording, always)
+CAIRO_ENABLE_SURFACE_BACKEND(canvas, HTML5 Canvas, no)
 CAIRO_ENABLE_SURFACE_BACKEND(tee, tee, no)
 CAIRO_ENABLE_SURFACE_BACKEND(xml, xml, no, [
     use_xml=$have_libz
diff --git a/src/Makefile.sources b/src/Makefile.sources
index 8d6480e..2c505fc 100644
--- a/src/Makefile.sources
+++ b/src/Makefile.sources
@@ -399,5 +399,13 @@ cairo_tee_sources = cairo-tee-surface.c
 cairo_xml_headers = cairo-xml.h
 cairo_xml_sources = cairo-xml-surface.c
 
+cairo_canvas_headers = cairo-canvas.h
+cairo_canvas_private = cairo-canvas-private.h
+cairo_canvas_sources = \
+	cairo-canvas-context.c \
+	cairo-canvas-device.c \
+	cairo-canvas-surface.c \
+	$(NULL)
+
 cairo_vg_headers = cairo-vg.h
 cairo_vg_sources = cairo-vg-surface.c
diff --git a/src/Makefile.win32.features b/src/Makefile.win32.features
index 6e72fdb..a103dcf 100644
--- a/src/Makefile.win32.features
+++ b/src/Makefile.win32.features
@@ -540,6 +540,22 @@ enabled_cairo_private += $(cairo_recording_private)
 enabled_cairo_cxx_sources += $(cairo_recording_cxx_sources)
 enabled_cairo_sources += $(cairo_recording_sources)
 
+unsupported_cairo_headers += $(cairo_canvas_headers)
+all_cairo_headers += $(cairo_canvas_headers)
+all_cairo_private += $(cairo_canvas_private)
+all_cairo_cxx_sources += $(cairo_canvas_cxx_sources)
+all_cairo_sources += $(cairo_canvas_sources)
+ifeq ($(CAIRO_HAS_CANVAS_SURFACE),1)
+enabled_cairo_headers += $(cairo_canvas_headers)
+enabled_cairo_private += $(cairo_canvas_private)
+enabled_cairo_cxx_sources += $(cairo_canvas_cxx_sources)
+enabled_cairo_sources += $(cairo_canvas_sources)
+endif
+all_cairo_pkgconf += cairo-canvas.pc
+ifeq ($(CAIRO_HAS_CANVAS_SURFACE),1)
+enabled_cairo_pkgconf += cairo-canvas.pc
+endif
+
 unsupported_cairo_headers += $(cairo_tee_headers)
 all_cairo_headers += $(cairo_tee_headers)
 all_cairo_private += $(cairo_tee_private)
diff --git a/src/cairo-backend-private.h b/src/cairo-backend-private.h
index 1dd5ea0..050f0d3 100644
--- a/src/cairo-backend-private.h
+++ b/src/cairo-backend-private.h
@@ -40,6 +40,7 @@
 
 typedef enum _cairo_backend_type {
     CAIRO_TYPE_DEFAULT,
+    CAIRO_TYPE_CANVAS,
     CAIRO_TYPE_SKIA,
 } cairo_backend_type_t;
 
diff --git a/src/cairo-canvas-context.c b/src/cairo-canvas-context.c
new file mode 100644
index 0000000..246a4f4
--- /dev/null
+++ b/src/cairo-canvas-context.c
@@ -0,0 +1,1296 @@
+/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2002 University of Southern California
+ * Copyright © 2005 Red Hat, Inc.
+ * Copyright © 2011 Intel Corporation
+ *
+ * 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>
+ */
+
+/* Missing from Cairo:
+ * - separate fill/stroke styles
+ * - shadow
+ */
+
+#include "cairoint.h"
+
+#include "cairo-canvas-private.h"
+
+#include "cairo-private.h"
+#include "cairo-arc-private.h"
+#include "cairo-backend-private.h"
+#include "cairo-default-context-private.h"
+#include "cairo-error-private.h"
+#include "cairo-freed-pool-private.h"
+#include "cairo-path-private.h"
+#include "cairo-pattern-private.h"
+
+/* Ideally all we map all of cairo's API directly onto the canvas.
+ * However, some operations (such as mask) cannot be supported and so
+ * we need to replay onto a fallback image and upload that instead.
+ * There we do everything twice...
+ */
+
+#define CAIRO_TOLERANCE_MINIMUM	_cairo_fixed_to_double(1)
+
+#if !defined(INFINITY)
+#define INFINITY HUGE_VAL
+#endif
+
+static freed_pool_t context_pool;
+
+void
+_cairo_canvas_context_reset_static_data (void)
+{
+    _freed_pool_reset (&context_pool);
+}
+
+static void
+_cairo_canvas_context_destroy (void *abstract_cr)
+{
+    cairo_canvas_context_t *cr = abstract_cr;
+
+    _cairo_default_context_fini (&cr->base);
+
+    /* mark the context as invalid to protect against misuse */
+    cr->base.base.status = CAIRO_STATUS_NULL_POINTER;
+    _freed_pool_put (&context_pool, cr);
+}
+
+static cairo_surface_t *
+_cairo_canvas_context_get_original_target (void *abstract_cr)
+{
+    cairo_canvas_context_t *cr = abstract_cr;
+
+    return cr->original_target;
+}
+
+static cairo_surface_t *
+_cairo_canvas_context_get_current_target (void *abstract_cr)
+{
+    cairo_canvas_context_t *cr = abstract_cr;
+
+    return cr->target;
+}
+
+static cairo_status_t
+_cairo_canvas_context_save (void *abstract_cr)
+{
+    cairo_canvas_context_t *cr = abstract_cr;
+    cairo_status_t status;
+
+    status = cr->default_backend->save (&cr->base);
+    if (unlikely (status))
+	return status;
+
+    return _cairo_canvas_emit (cr->device, "cr%d.save()\n", cr->var);
+}
+
+static cairo_status_t
+_cairo_canvas_context_restore (void *abstract_cr)
+{
+    cairo_canvas_context_t *cr = abstract_cr;
+    cairo_status_t status;
+
+    status = cr->default_backend->restore (&cr->base);
+    if (unlikely (status))
+	return status;
+
+    cr->clear = cr->default_backend->get_operator == CAIRO_OPERATOR_CLEAR;
+
+    return _cairo_canvas_emit (cr->device, "cr%d.restore()\n", cr->var);
+}
+
+static cairo_status_t
+_cairo_canvas_context_push_group (void *abstract_cr, cairo_content_t content)
+{
+}
+
+static cairo_pattern_t *
+_cairo_canvas_context_pop_group (void *abstract_cr)
+{
+}
+
+static cairo_status_t
+_cairo_canvas_context_set_source (void *abstract_cr,
+				  cairo_pattern_t *source)
+{
+}
+
+static cairo_status_t
+_cairo_canvas_context_set_source_rgba (void *abstract_cr, double red, double green, double blue, double alpha)
+{
+    cairo_canvas_context_t *cr = abstract_cr;
+    cairo_status_t status;
+
+    status = cr->default_backend->set_source_rgba (&cr->base,
+						   red, green, blue, alpha);
+    if (unlikely (status))
+	return status;
+
+    red   = _cairo_restrict_value (red,   0.0, 1.0);
+    green = _cairo_restrict_value (green, 0.0, 1.0);
+    blue  = _cairo_restrict_value (blue,  0.0, 1.0);
+    alpha = _cairo_restrict_value (alpha, 0.0, 1.0);
+
+    if (CAIRO_ALPHA_IS_OPAQUE (alpha)) {
+	return _cairo_canvas_emit (cr->device,
+				   "cr%d.fillStyle = cr%d.strokeStyle = '#%02x%02x%02x'\n",
+				   cr->var, cr->var,
+				   (int)(255*red),
+				   (int)(255*green),
+				   (int)(255*blue));
+    } else {
+	return _cairo_canvas_emit (cr->device,
+				   "cr%d.fillStyle = cr%d.strokeStyle = 'rgba(%d, %d, %d, %d)'\n",
+				   cr->var, cr->var,
+				   (int)(255*red),
+				   (int)(255*green),
+				   (int)(255*blue),
+				   (int)(255*alpha));
+    }
+}
+
+static cairo_status_t
+_cairo_canvas_context_set_source_surface (void *abstract_cr,
+					  cairo_surface_t *surface,
+					  double	   x,
+					  double	   y)
+{
+}
+
+static cairo_pattern_t *
+_cairo_canvas_context_get_source (void *abstract_cr)
+{
+    cairo_canvas_context_t *cr = abstract_cr;
+    return cr->default_backend->get_source (&cr->base);
+}
+
+static cairo_status_t
+_cairo_canvas_context_set_tolerance (void *abstract_cr,
+				     double tolerance)
+{
+    /* XXX ignored */
+    cairo_canvas_context_t *cr = abstract_cr;
+    return cr->default_backend->set_tolerance (&cr->base, tolerance);
+}
+
+static cairo_status_t
+_cairo_canvas_context_set_operator (void *abstract_cr, cairo_operator_t op)
+{
+    cairo_canvas_context_t *cr = abstract_cr;
+    cairo_status_t status;
+    static const char *map[] = {
+	[CAIRO_OPERATOR_ATOP] = "source-atop",
+	[CAIRO_OPERATOR_IN] = "source-in",
+	[CAIRO_OPERATOR_OUT] = "source-out",
+	[CAIRO_OPERATOR_OVER] = "source-over",
+	[CAIRO_OPERATOR_DEST_ATOP] = "destination-atop",
+	[CAIRO_OPERATOR_DEST_IN] = "destination-in",
+	[CAIRO_OPERATOR_DEST_OUT] = "destination-out",
+	[CAIRO_OPERATOR_DEST_OVER] = "destination-over",
+	[CAIRO_OPERATOR_LIGHTEN] = "lighter",
+	[CAIRO_OPERATOR_SOURCE] = "copy",
+	[CAIRO_OPERATOR_XOR] = "xor",
+    };
+
+    status = cr->default_backend->set_operator (&cr->base, op);
+    if (unlikely (status))
+	return status;
+
+    if (op == CAIRO_OPERATOR_CLEAR) {
+	cr->clear = TRUE;
+	return CAIRO_STATUS_SUCCESS;
+    }
+    cr->clear = FALSE;
+
+    /* XXX */
+
+    return _cairo_canvas_emit (cr->device,
+			       "cr%d.globalCompositeOperation = '%s'\n",
+			       cr->var, map[op]);
+}
+
+static cairo_status_t
+_cairo_canvas_context_set_opacity (void *abstract_cr, double opacity)
+{
+    cairo_canvas_context_t *cr = abstract_cr;
+    cairo_status_t status;
+
+    status = cr->default_backend->set_opacity (&cr->base, opacity);
+    if (unlikely (status))
+	return status;
+
+    return _cairo_canvas_emit (cr->device,
+			       "cr%d.globalAlpha = %f\n",
+			       cr->var,
+			       opacity);
+}
+
+static cairo_status_t
+_cairo_canvas_context_set_antialias (void *abstract_cr,
+				     cairo_antialias_t antialias)
+{
+    cairo_canvas_context_t *cr = abstract_cr;
+    cairo_status_t status;
+
+    status = cr->default_backend->set_antialias (&cr->base, antialias);
+    if (unlikely (status))
+	return status;
+
+    /* XXX ignored */
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_canvas_context_set_fill_rule (void *abstract_cr,
+				     cairo_fill_rule_t fill_rule)
+{
+    cairo_canvas_context_t *cr = abstract_cr;
+    cairo_status_t status;
+
+    status = cr->default_backend->set_fill_rule (&cr->base, fill_rule);
+    if (unlikely (status))
+	return status;
+
+    /* XXX even-odd fallback! */
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_canvas_context_set_line_width (void *abstract_cr,
+				      double line_width)
+{
+    cairo_canvas_context_t *cr = abstract_cr;
+    cairo_status_t status;
+
+    status = cr->default_backend->set_line_width (&cr->base, line_width);
+    if (unlikely (status))
+	return status;
+
+    return _cairo_canvas_emit (cr->device,
+			       "cr%d.lineWidth = %f\n",
+			       cr->var,
+			       line_width);
+}
+
+static cairo_status_t
+_cairo_canvas_context_set_line_cap (void *abstract_cr,
+				    cairo_line_cap_t line_cap)
+{
+    cairo_canvas_context_t *cr = abstract_cr;
+    cairo_status_t status;
+    static const char *map[] = {
+	[CAIRO_LINE_CAP_BUTT] = "butt",
+	[CAIRO_LINE_CAP_ROUND] = "round",
+	[CAIRO_LINE_CAP_SQUARE] = "square"
+    };
+
+    status = cr->default_backend->set_line_cap (&cr->base, line_cap);
+    if (unlikely (status))
+	return status;
+
+    return _cairo_canvas_emit (cr->device,
+			       "cr%d.lineCap = '%s'\n",
+			       cr->var,
+			       map[line_cap]);
+}
+
+static cairo_status_t
+_cairo_canvas_context_set_line_join (void *abstract_cr,
+				      cairo_line_join_t line_join)
+{
+    cairo_canvas_context_t *cr = abstract_cr;
+    cairo_status_t status;
+    static const char *map[] = {
+	[CAIRO_LINE_JOIN_MITER] = "miter",
+	[CAIRO_LINE_JOIN_BEVEL] = "bevel",
+	[CAIRO_LINE_JOIN_ROUND] = "round"
+    };
+
+    status = cr->default_backend->set_line_join (&cr->base, line_join);
+    if (unlikely (status))
+	return status;
+
+    return _cairo_canvas_emit (cr->device,
+			       "cr%d.lineJoin = '%s'\n",
+			       cr->var,
+			       map[line_join]);
+}
+
+static cairo_status_t
+_cairo_canvas_context_set_dash (void *abstract_cr,
+				const double *dashes,
+				int	      num_dashes,
+				double	      offset)
+{
+    cairo_canvas_context_t *cr = abstract_cr;
+    cairo_status_t status;
+
+    status = cr->default_backend->set_dash (&cr->base, dashes, num_dashes, offset);
+    if (unlikely (status))
+	return status;
+
+    /* XXX stroke-to-path fallback */
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_canvas_context_set_miter_limit (void *abstract_cr,
+				       double limit)
+{
+    cairo_canvas_context_t *cr = abstract_cr;
+    cairo_status_t status;
+
+    status = cr->default_backend->set_miter_limit (&cr->base, limit);
+    if (unlikely (status))
+	return status;
+
+    return _cairo_canvas_emit (cr->device,
+			       "cr%d.miterLimit = %f\n",
+			       cr->var,
+			       limit);
+}
+
+static cairo_antialias_t
+_cairo_canvas_context_get_antialias (void *abstract_cr)
+{
+    cairo_canvas_context_t *cr = abstract_cr;
+    return cr->default_backend->get_antialias (&cr->base);
+}
+
+static void
+_cairo_canvas_context_get_dash (void *abstract_cr,
+				double *dashes,
+				int *num_dashes,
+				double *offset)
+{
+    cairo_canvas_context_t *cr = abstract_cr;
+    return cr->default_backend->get_dash (&cr->base,
+					  dashes, num_dashes, offset);
+}
+
+static cairo_fill_rule_t
+_cairo_canvas_context_get_fill_rule (void *abstract_cr)
+{
+    cairo_canvas_context_t *cr = abstract_cr;
+    return cr->default_backend->get_fill_rule (&cr->base);
+}
+
+static double
+_cairo_canvas_context_get_line_width (void *abstract_cr)
+{
+    cairo_canvas_context_t *cr = abstract_cr;
+    return cr->default_backend->get_line_width (&cr->base);
+}
+
+static cairo_line_cap_t
+_cairo_canvas_context_get_line_cap (void *abstract_cr)
+{
+    cairo_canvas_context_t *cr = abstract_cr;
+    return cr->default_backend->get_line_cap (&cr->base);
+}
+
+static cairo_line_join_t
+_cairo_canvas_context_get_line_join (void *abstract_cr)
+{
+    cairo_canvas_context_t *cr = abstract_cr;
+    return cr->default_backend->get_line_join (&cr->base);
+}
+
+static double
+_cairo_canvas_context_get_miter_limit (void *abstract_cr)
+{
+    cairo_canvas_context_t *cr = abstract_cr;
+    return cr->default_backend->get_miter_limit (&cr->base);
+}
+
+static cairo_operator_t
+_cairo_canvas_context_get_operator (void *abstract_cr)
+{
+    cairo_canvas_context_t *cr = abstract_cr;
+    return cr->default_backend->get_operator (&cr->base);
+}
+
+static double
+_cairo_canvas_context_get_opacity (void *abstract_cr)
+{
+    cairo_canvas_context_t *cr = abstract_cr;
+    return cr->default_backend->get_opacity (&cr->base);
+}
+
+static double
+_cairo_canvas_context_get_tolerance (void *abstract_cr)
+{
+    cairo_canvas_context_t *cr = abstract_cr;
+    return cr->default_backend->get_tolerance (&cr->base);
+}
+
+/* Current transformation matrix */
+
+static cairo_status_t
+_cairo_canvas_context_translate (void *abstract_cr,
+				  double tx,
+				  double ty)
+{
+    cairo_canvas_context_t *cr = abstract_cr;
+    cairo_status_t status;
+
+    status = cr->default_backend->translate (&cr->base, tx, ty);
+    if (unlikely (status))
+	return status;
+
+    return _cairo_canvas_emit (cr->device,
+			       "cr%d.translate(%f, %f)\n",
+			       cr->var,
+			       tx, ty);
+}
+
+static cairo_status_t
+_cairo_canvas_context_scale (void *abstract_cr,
+			      double sx,
+			      double sy)
+{
+    cairo_canvas_context_t *cr = abstract_cr;
+    cairo_status_t status;
+
+    status = cr->default_backend->scale (&cr->base, sx, sy);
+    if (unlikely (status))
+	return status;
+
+    return _cairo_canvas_emit (cr->device,
+			       "cr%d.scale(%f, %f)\n",
+			       cr->var,
+			       sx, sy);
+}
+
+static cairo_status_t
+_cairo_canvas_context_rotate (void *abstract_cr,
+			       double theta)
+{
+    cairo_canvas_context_t *cr = abstract_cr;
+    cairo_status_t status;
+
+    status = cr->default_backend->rotate (&cr->base, theta);
+    if (unlikely (status))
+	return status;
+
+    return _cairo_canvas_emit (cr->device,
+			       "cr%d.rotate(%f)\n",
+			       cr->var,
+			       theta);
+}
+
+static cairo_status_t
+_cairo_canvas_context_transform (void *abstract_cr,
+				  const cairo_matrix_t *matrix)
+{
+    cairo_canvas_context_t *cr = abstract_cr;
+    cairo_status_t status;
+
+    status = cr->default_backend->transform (&cr->base, matrix);
+    if (unlikely (status))
+	return status;
+
+    return _cairo_canvas_emit (cr->device,
+			       "cr%d.transform(%f, %f, %f, %f, %f, %f)\n",
+			       cr->var,
+			       matrix->xx, matrix->xy,
+			       matrix->yx, matrix->yy,
+			       matrix->x0, matrix->y0);
+}
+
+static cairo_status_t
+_cairo_canvas_context_set_matrix (void *abstract_cr,
+				   const cairo_matrix_t *matrix)
+{
+    cairo_canvas_context_t *cr = abstract_cr;
+    cairo_status_t status;
+
+    status = cr->default_backend->set_matrix (&cr->base, matrix);
+    if (unlikely (status))
+	return status;
+
+    return _cairo_canvas_emit (cr->device,
+			       "cr%d.setTransform(%f, %f, %f, %f, %f, %f)\n",
+			       cr->var,
+			       matrix->xx, matrix->xy,
+			       matrix->yx, matrix->yy,
+			       matrix->x0, matrix->y0);
+}
+
+static cairo_status_t
+_cairo_canvas_context_set_identity_matrix (void *abstract_cr)
+{
+    cairo_canvas_context_t *cr = abstract_cr;
+    cairo_status_t status;
+
+    status = cr->default_backend->set_identity_matrix (&cr->base);
+    if (unlikely (status))
+	return status;
+
+    return _cairo_canvas_emit (cr->device,
+			       "cr%d.setTransform(1, 0, 0, 1, 0, 0)\n",
+			       cr->var);
+}
+
+static void
+_cairo_canvas_context_get_matrix (void *abstract_cr,
+				  cairo_matrix_t *matrix)
+{
+    cairo_canvas_context_t *cr = abstract_cr;
+    cr->default_backend->get_matrix (&cr->base, matrix);
+}
+
+static void
+_cairo_canvas_context_user_to_device (void *abstract_cr,
+				       double *x,
+				       double *y)
+{
+    cairo_canvas_context_t *cr = abstract_cr;
+    cr->default_backend->user_to_device (&cr->base, x, y);
+}
+
+static void
+_cairo_canvas_context_user_to_device_distance (void *abstract_cr, double *dx, double *dy)
+{
+    cairo_canvas_context_t *cr = abstract_cr;
+    cr->default_backend->user_to_device_distance (&cr->base, dx, dy);
+}
+
+static void
+_cairo_canvas_context_device_to_user (void *abstract_cr,
+				       double *x,
+				       double *y)
+{
+    cairo_canvas_context_t *cr = abstract_cr;
+    cr->default_backend->device_to_user (&cr->base, x, y);
+}
+
+static void
+_cairo_canvas_context_device_to_user_distance (void *abstract_cr,
+						double *dx,
+						double *dy)
+{
+    cairo_canvas_context_t *cr = abstract_cr;
+    cr->default_backend->device_to_user_distance (&cr->base, dx, dy);
+}
+
+/* Path constructor */
+
+static cairo_status_t
+_cairo_canvas_context_new_path (void *abstract_cr)
+{
+    cairo_canvas_context_t *cr = abstract_cr;
+    cairo_status_t status;
+
+    status = cr->default_backend->new_path (&cr->base);
+    if (unlikely (status))
+	return status;
+
+    return _cairo_canvas_emit (cr->device, "cr%d.beginPath()\n", cr->var);
+}
+
+static cairo_status_t
+_cairo_canvas_context_new_sub_path (void *abstract_cr)
+{
+    cairo_canvas_context_t *cr = abstract_cr;
+    return cr->default_backend->new_sub_path (&cr->base);
+}
+
+static cairo_status_t
+_cairo_canvas_context_move_to (void *abstract_cr, double x, double y)
+{
+    cairo_canvas_context_t *cr = abstract_cr;
+    cairo_status_t status;
+
+    status = cr->default_backend->move_to (&cr->base, x, y);
+    if (unlikely (status))
+	return status;
+
+    cr->x = x;
+    cr->y = y;
+
+    return _cairo_canvas_emit (cr->device,
+			       "cr%d.moveTo(%f, %f)\n",
+			       cr->var,
+			       x, y);
+}
+
+static cairo_status_t
+_cairo_canvas_context_line_to (void *abstract_cr, double x, double y)
+{
+    cairo_canvas_context_t *cr = abstract_cr;
+    cairo_status_t status;
+
+    status = cr->default_backend->line_to (&cr->base, x, y);
+    if (unlikely (status))
+	return status;
+
+    cr->x = x;
+    cr->y = y;
+
+    return _cairo_canvas_emit (cr->device,
+			       "cr%d.lineTo(%f, %f)\n",
+			       cr->var,
+			       x, y);
+}
+
+static cairo_status_t
+_cairo_canvas_context_curve_to (void *abstract_cr,
+				double x1, double y1,
+				double x2, double y2,
+				double x3, double y3)
+{
+    cairo_canvas_context_t *cr = abstract_cr;
+    cairo_status_t status;
+
+    status = cr->default_backend->curve_to (&cr->base, x1, y1, x2, y2, x3, y3);
+    if (unlikely (status))
+	return status;
+
+    cr->x = x3;
+    cr->y = y3;
+
+    return _cairo_canvas_emit (cr->device,
+			       "cr%d.bezierCurveTo(%f, %f, %f, %f, %f, %f)\n",
+			       cr->var,
+			       x1, y1, x2, y2, x3, y3);
+}
+
+static cairo_status_t
+_cairo_canvas_context_arc (void *abstract_cr,
+			   double xc, double yc, double radius,
+			   double angle1, double angle2,
+			   cairo_bool_t forward)
+{
+    cairo_canvas_context_t *cr = abstract_cr;
+    cairo_status_t status;
+
+    status = cr->default_backend->arc (&cr->base, xc, yc, radius, angle1, angle2, forward);
+    if (unlikely (status))
+	return status;
+
+    return _cairo_canvas_emit (cr->device,
+			       "cr%d.arc(%f, %f, %f, %f, %f%s)\n",
+			       cr->var,
+			       xc, yc, radius, angle1, angle2,
+			       forward ? "" : ", false");
+}
+
+static cairo_status_t
+_cairo_canvas_context_rel_move_to (void *abstract_cr, double dx, double dy)
+{
+    cairo_canvas_context_t *cr = abstract_cr;
+    return _cairo_canvas_context_move_to (cr, cr->x + dx, cr->y + dy);
+}
+
+static cairo_status_t
+_cairo_canvas_context_rel_line_to (void *abstract_cr, double dx, double dy)
+{
+    cairo_canvas_context_t *cr = abstract_cr;
+    return _cairo_canvas_context_line_to (cr, cr->x + dx, cr->y + dy);
+}
+
+static cairo_status_t
+_cairo_canvas_context_rel_curve_to (void *abstract_cr,
+				    double dx1, double dy1,
+				    double dx2, double dy2,
+				    double dx3, double dy3)
+{
+    cairo_canvas_context_t *cr = abstract_cr;
+    return _cairo_canvas_context_curve_to (cr,
+					   cr->x + dx1, cr->y + dy1,
+					   cr->x + dx2, cr->y + dy2,
+					   cr->x + dx3, cr->y + dy3);
+}
+
+static cairo_status_t
+_cairo_canvas_context_close_path (void *abstract_cr)
+{
+    cairo_canvas_context_t *cr = abstract_cr;
+    cairo_status_t status;
+
+    status = cr->default_backend->close_path (&cr->base);
+    if (unlikely (status))
+	return status;
+
+    return _cairo_canvas_emit (cr->device, "cr%d.closePath()\n", cr->var);
+}
+
+static cairo_status_t
+_cairo_canvas_context_rectangle (void *abstract_cr,
+				  double x, double y,
+				  double width, double height)
+{
+    cairo_canvas_context_t *cr = abstract_cr;
+    cairo_status_t status;
+
+    status = cr->default_backend->rectangle (&cr->base, x, y, width,height);
+    if (unlikely (status))
+	return status;
+
+    return _cairo_canvas_emit (cr->device,
+			       "cr%d.rect(%f, %f, %f, %f)\n",
+			       cr->var,
+			       x, y,width, height);
+}
+
+static void
+_cairo_canvas_context_path_extents (void *abstract_cr,
+				    double *x1, double *y1,
+				    double *x2, double *y2)
+{
+    cairo_canvas_context_t *cr = abstract_cr;
+    return cr->default_backend->path_extents (&cr->base, x1, y1, x2, y2);
+}
+
+static cairo_bool_t
+_cairo_canvas_context_has_current_point (void *abstract_cr)
+{
+    cairo_canvas_context_t *cr = abstract_cr;
+    return cr->default_backend->has_current_point (&cr->base);
+}
+
+static cairo_bool_t
+_cairo_canvas_context_get_current_point (void *abstract_cr,
+					 double *x,
+					 double *y)
+{
+    cairo_canvas_context_t *cr = abstract_cr;
+    return cr->default_backend->get_current_point (&cr->base, x, y);
+}
+
+static cairo_path_t *
+_cairo_canvas_context_copy_path (void *abstract_cr)
+{
+    cairo_canvas_context_t *cr = abstract_cr;
+    return cr->default_backend->copy_path (&cr->base);
+}
+
+static cairo_path_t *
+_cairo_canvas_context_copy_path_flat (void *abstract_cr)
+{
+    cairo_canvas_context_t *cr = abstract_cr;
+    return cr->default_backend->copy_path_flat (&cr->base);
+}
+
+static cairo_status_t
+_cairo_canvas_context_append_path (void *abstract_cr,
+				   const cairo_path_t *path)
+{
+}
+
+static cairo_status_t
+_cairo_canvas_context_paint (void *abstract_cr)
+{
+    cairo_canvas_context_t *cr = abstract_cr;
+    cairo_canvas_surface_t *target = (cairo_canvas_surface_t *)cr->target;
+    cairo_status_t status;
+
+    status = cr->default_backend->paint (&cr->base);
+    if (unlikely (status))
+	return status;
+
+    target->base.is_clear = FALSE;
+    if (cr->clear) {
+	return _cairo_canvas_emit (cr->device,
+				   "cr%d.clearRect(0, 0, %d, %d)\n",
+				   cr->var,
+				   target->width, target->height);
+    } else {
+	return _cairo_canvas_emit (cr->device,
+				   "cr%d.fillRect(0, 0, %d, %d)\n",
+				   cr->var,
+				   target->width, target->height);
+    }
+}
+
+static cairo_status_t
+_cairo_canvas_context_paint_with_alpha (void *abstract_cr,
+					double alpha)
+{
+    cairo_canvas_context_t *cr = abstract_cr;
+    cairo_canvas_surface_t *target = (cairo_canvas_surface_t *)cr->target;
+    cairo_status_t status;
+    double opacity;
+
+    status = cr->default_backend->paint (&cr->base);
+    if (unlikely (status))
+	return status;
+
+    if (cr->clear) {
+	return _cairo_canvas_emit (cr->device,
+				   "cr%d.clearRect(0, 0, %d, %d)\n",
+				   cr->var,
+				   target->width, target->height);
+    }
+
+    opacity = cr->default_backend->get_opacity (&cr->base);
+
+    _cairo_canvas_emit (cr->device, "cr%d.globalAlpha = %f\n",
+			cr->var,
+			alpha * opacity);
+
+    if (cr->default_backend->get_operator (&cr->base) == CAIRO_OPERATOR_CLEAR) {
+	_cairo_canvas_emit (cr->device,
+				   "cr%d.clearRect(0, 0, %d, %d)\n",
+				   cr->var,
+				   target->width, target->height);
+    } else {
+	_cairo_canvas_emit (cr->device,
+				   "cr%d.fillRect(0, 0, %d, %d)\n",
+				   cr->var,
+				   target->width, target->height);
+    }
+
+    target->base.is_clear = FALSE;
+    return _cairo_canvas_emit (cr->device, "cr.globalAlpha = %f", opacity);
+}
+
+static cairo_status_t
+_cairo_canvas_context_mask (void *abstract_cr,
+			    cairo_pattern_t *mask)
+{
+}
+
+static cairo_status_t
+_cairo_canvas_context_stroke_preserve (void *abstract_cr)
+{
+    cairo_canvas_context_t *cr = abstract_cr;
+    cairo_canvas_surface_t *target = (cairo_canvas_surface_t *)cr->target;
+    cairo_status_t status;
+
+    status = cr->default_backend->stroke_preserve (&cr->base);
+    if (unlikely (status))
+	return status;
+
+    if (cr->clear) {
+	_cairo_canvas_emit (cr->device,
+			    "cr%d.globalCompositeOperation = 'dest-out'\n"
+			    "cr%d.strokeStyle = 'white'\n",
+			    cr->var, cr->var);
+    }
+
+    target->base.is_clear = FALSE;
+    return _cairo_canvas_emit (cr->device, "cr%d.stroke()\n", cr->var);
+}
+
+static cairo_status_t
+_cairo_canvas_context_stroke (void *abstract_cr)
+{
+    cairo_canvas_context_t *cr = abstract_cr;
+
+    _cairo_canvas_context_stroke_preserve (cr);
+    return _cairo_canvas_context_new_path (cr);
+}
+
+static cairo_status_t
+_cairo_canvas_context_in_stroke (void *abstract_cr,
+				 double x, double y,
+				 cairo_bool_t *inside)
+{
+    cairo_canvas_context_t *cr = abstract_cr;
+    return cr->default_backend->in_stroke (&cr->base, x, y, inside);
+}
+
+static cairo_status_t
+_cairo_canvas_context_stroke_extents (void *abstract_cr,
+				      double *x1, double *y1,
+				      double *x2, double *y2)
+{
+    cairo_canvas_context_t *cr = abstract_cr;
+    return cr->default_backend->stroke_extents (&cr->base, x1, y1, x2, y2);
+}
+
+static cairo_status_t
+_cairo_canvas_context_fill_preserve (void *abstract_cr)
+{
+    cairo_canvas_context_t *cr = abstract_cr;
+    cairo_canvas_surface_t *target = (cairo_canvas_surface_t *)cr->target;
+    cairo_status_t status;
+
+    status = cr->default_backend->fill_preserve (&cr->base);
+    if (unlikely (status))
+	return status;
+
+    if (cr->clear) {
+	_cairo_canvas_emit (cr->device,
+			    "cr%d.globalCompositeOperation = 'dest-out'\n"
+			    "cr%d.strokeStyle = 'white'\n",
+			    cr->var, cr->var);
+    }
+
+    target->base.is_clear = FALSE;
+    return _cairo_canvas_emit (cr->device, "cr%d.fill()\n", cr->var);
+}
+
+static cairo_status_t
+_cairo_canvas_context_fill (void *abstract_cr)
+{
+    cairo_canvas_context_t *cr = abstract_cr;
+
+    _cairo_canvas_context_fill_preserve (cr);
+    return _cairo_canvas_context_new_path (cr);
+}
+
+static cairo_status_t
+_cairo_canvas_context_in_fill (void *abstract_cr,
+				double x, double y,
+				cairo_bool_t *inside)
+{
+    cairo_canvas_context_t *cr = abstract_cr;
+    return cr->default_backend->in_fill (&cr->base, x, y, inside);
+}
+
+static cairo_status_t
+_cairo_canvas_context_fill_extents (void *abstract_cr,
+				    double *x1, double *y1,
+				    double *x2, double *y2)
+{
+    cairo_canvas_context_t *cr = abstract_cr;
+    return cr->default_backend->fill_extents (&cr->base, x1, y1, x2, y2);
+}
+
+static cairo_status_t
+_cairo_canvas_context_clip_preserve (void *abstract_cr)
+{
+    cairo_canvas_context_t *cr = abstract_cr;
+    cairo_status_t status;
+
+    status = cr->default_backend->clip_preserve (&cr->base);
+    if (unlikely (status))
+	return status;
+
+    return _cairo_canvas_emit (cr->device, "cr%d.clip()\n", cr->var);
+}
+
+static cairo_status_t
+_cairo_canvas_context_clip (void *abstract_cr)
+{
+    cairo_canvas_context_t *cr = abstract_cr;
+
+    _cairo_canvas_context_clip_preserve (cr);
+    return _cairo_canvas_context_new_path (cr);
+}
+
+static cairo_status_t
+_cairo_canvas_context_in_clip (void *abstract_cr,
+			       double x, double y,
+			       cairo_bool_t *inside)
+{
+    cairo_canvas_context_t *cr = abstract_cr;
+    return cr->default_backend->in_clip (&cr->base, x, y, inside);
+}
+
+static cairo_status_t
+_cairo_canvas_context_reset_clip (void *abstract_cr)
+{
+    /* XXX */
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_canvas_context_clip_extents (void *abstract_cr,
+				    double *x1, double *y1,
+				    double *x2, double *y2)
+{
+    cairo_canvas_context_t *cr = abstract_cr;
+    return cr->default_backend->clip_extents (&cr->base, x1, y1, x2, y2);
+}
+
+static cairo_rectangle_list_t *
+_cairo_canvas_context_copy_clip_rectangle_list (void *abstract_cr)
+{
+    cairo_canvas_context_t *cr = abstract_cr;
+    return cr->default_backend->clip_copy_rectangle_list (&cr->base);
+}
+
+static cairo_status_t
+_cairo_canvas_context_copy_page (void *abstract_cr)
+{
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_canvas_context_show_page (void *abstract_cr)
+{
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_canvas_context_set_font_face (void *abstract_cr,
+				      cairo_font_face_t *font_face)
+{
+    cairo_canvas_context_t *cr = abstract_cr;
+    return cr->default_backend->set_font_face (&cr->base, font_face);
+}
+
+static cairo_font_face_t *
+_cairo_canvas_context_get_font_face (void *abstract_cr)
+{
+    cairo_canvas_context_t *cr = abstract_cr;
+    return cr->default_backend->get_font_face (&cr->base);
+}
+
+static cairo_status_t
+_cairo_canvas_context_font_extents (void *abstract_cr,
+				    cairo_font_extents_t *extents)
+{
+    cairo_canvas_context_t *cr = abstract_cr;
+    return cr->default_backend->font_extents (&cr->base, extents);
+}
+
+static cairo_status_t
+_cairo_canvas_context_set_font_size (void *abstract_cr,
+				     double size)
+{
+    cairo_canvas_context_t *cr = abstract_cr;
+    return cr->default_backend->set_font_size (&cr->base, size);
+}
+
+static cairo_status_t
+_cairo_canvas_context_set_font_matrix (void *abstract_cr,
+				       const cairo_matrix_t *matrix)
+{
+    cairo_canvas_context_t *cr = abstract_cr;
+    return cr->default_backend->set_font_matrix (&cr->base, matrix);
+}
+
+static void
+_cairo_canvas_context_get_font_matrix (void *abstract_cr,
+				       cairo_matrix_t *matrix)
+{
+    cairo_canvas_context_t *cr = abstract_cr;
+    cr->default_backend->get_font_matrix (&cr->base, matrix);
+}
+
+static cairo_status_t
+_cairo_canvas_context_set_font_options (void *abstract_cr,
+					const cairo_font_options_t *options)
+{
+    cairo_canvas_context_t *cr = abstract_cr;
+    return cr->default_backend->set_font_options (&cr->base, options);
+}
+
+static void
+_cairo_canvas_context_get_font_options (void *abstract_cr,
+					 cairo_font_options_t *options)
+{
+    cairo_canvas_context_t *cr = abstract_cr;
+    cr->default_backend->get_font_options (&cr->base, options);
+}
+
+static cairo_status_t
+_cairo_canvas_context_set_scaled_font (void *abstract_cr,
+				       cairo_scaled_font_t *scaled_font)
+{
+    cairo_canvas_context_t *cr = abstract_cr;
+    return cr->default_backend->set_scaled_font (&cr->base, scaled_font);
+}
+
+static cairo_scaled_font_t *
+_cairo_canvas_context_get_scaled_font (void *abstract_cr)
+{
+    cairo_canvas_context_t *cr = abstract_cr;
+    return cr->default_backend->get_scaled_font (&cr->base);
+}
+
+static cairo_status_t
+_cairo_canvas_context_glyphs (void *abstract_cr,
+			       const cairo_glyph_t *glyphs,
+			       int num_glyphs,
+			       cairo_glyph_text_info_t *info)
+{
+}
+
+static cairo_status_t
+_cairo_canvas_context_glyph_path (void *abstract_cr,
+				   const cairo_glyph_t *glyphs,
+				   int num_glyphs)
+{
+}
+
+static cairo_status_t
+_cairo_canvas_context_glyph_extents (void                *abstract_cr,
+				      const cairo_glyph_t    *glyphs,
+				      int                    num_glyphs,
+				      cairo_text_extents_t   *extents)
+{
+}
+
+static const cairo_backend_t _cairo_canvas_context_backend = {
+    CAIRO_TYPE_CANVAS,
+    _cairo_canvas_context_destroy,
+
+    _cairo_canvas_context_get_original_target,
+    _cairo_canvas_context_get_current_target,
+
+    _cairo_canvas_context_save,
+    _cairo_canvas_context_restore,
+
+    _cairo_canvas_context_push_group,
+    _cairo_canvas_context_pop_group,
+
+    _cairo_canvas_context_set_source_rgba,
+    _cairo_canvas_context_set_source_surface,
+    _cairo_canvas_context_set_source,
+    _cairo_canvas_context_get_source,
+
+    _cairo_canvas_context_set_antialias,
+    _cairo_canvas_context_set_dash,
+    _cairo_canvas_context_set_fill_rule,
+    _cairo_canvas_context_set_line_cap,
+    _cairo_canvas_context_set_line_join,
+    _cairo_canvas_context_set_line_width,
+    _cairo_canvas_context_set_miter_limit,
+    _cairo_canvas_context_set_opacity,
+    _cairo_canvas_context_set_operator,
+    _cairo_canvas_context_set_tolerance,
+    _cairo_canvas_context_get_antialias,
+    _cairo_canvas_context_get_dash,
+    _cairo_canvas_context_get_fill_rule,
+    _cairo_canvas_context_get_line_cap,
+    _cairo_canvas_context_get_line_join,
+    _cairo_canvas_context_get_line_width,
+    _cairo_canvas_context_get_miter_limit,
+    _cairo_canvas_context_get_opacity,
+    _cairo_canvas_context_get_operator,
+    _cairo_canvas_context_get_tolerance,
+
+    _cairo_canvas_context_translate,
+    _cairo_canvas_context_scale,
+    _cairo_canvas_context_rotate,
+    _cairo_canvas_context_transform,
+    _cairo_canvas_context_set_matrix,
+    _cairo_canvas_context_set_identity_matrix,
+    _cairo_canvas_context_get_matrix,
+    _cairo_canvas_context_user_to_device,
+    _cairo_canvas_context_user_to_device_distance,
+    _cairo_canvas_context_device_to_user,
+    _cairo_canvas_context_device_to_user_distance,
+
+    _cairo_canvas_context_new_path,
+    _cairo_canvas_context_new_sub_path,
+    _cairo_canvas_context_move_to,
+    _cairo_canvas_context_rel_move_to,
+    _cairo_canvas_context_line_to,
+    _cairo_canvas_context_rel_line_to,
+    _cairo_canvas_context_curve_to,
+    _cairo_canvas_context_rel_curve_to,
+    NULL, /* arc-to */
+    NULL, /* rel-arc-to */
+    _cairo_canvas_context_close_path,
+    _cairo_canvas_context_arc,
+    _cairo_canvas_context_rectangle,
+    _cairo_canvas_context_path_extents,
+    _cairo_canvas_context_has_current_point,
+    _cairo_canvas_context_get_current_point,
+    _cairo_canvas_context_copy_path,
+    _cairo_canvas_context_copy_path_flat,
+    _cairo_canvas_context_append_path,
+
+    NULL, /* stroke-to-path */
+
+    _cairo_canvas_context_clip,
+    _cairo_canvas_context_clip_preserve,
+    _cairo_canvas_context_in_clip,
+    _cairo_canvas_context_clip_extents,
+    _cairo_canvas_context_reset_clip,
+    _cairo_canvas_context_copy_clip_rectangle_list,
+
+    _cairo_canvas_context_paint,
+    _cairo_canvas_context_paint_with_alpha,
+    _cairo_canvas_context_mask,
+
+    _cairo_canvas_context_stroke,
+    _cairo_canvas_context_stroke_preserve,
+    _cairo_canvas_context_in_stroke,
+    _cairo_canvas_context_stroke_extents,
+
+    _cairo_canvas_context_fill,
+    _cairo_canvas_context_fill_preserve,
+    _cairo_canvas_context_in_fill,
+    _cairo_canvas_context_fill_extents,
+
+    _cairo_canvas_context_set_font_face,
+    _cairo_canvas_context_get_font_face,
+    _cairo_canvas_context_set_font_size,
+    _cairo_canvas_context_set_font_matrix,
+    _cairo_canvas_context_get_font_matrix,
+    _cairo_canvas_context_set_font_options,
+    _cairo_canvas_context_get_font_options,
+    _cairo_canvas_context_set_scaled_font,
+    _cairo_canvas_context_get_scaled_font,
+    _cairo_canvas_context_font_extents,
+
+    _cairo_canvas_context_glyphs,
+    _cairo_canvas_context_glyph_path,
+    _cairo_canvas_context_glyph_extents,
+
+    _cairo_canvas_context_copy_page,
+    _cairo_canvas_context_show_page,
+};
+
+
+cairo_t *
+_cairo_canvas_context_create (void *_target)
+{
+    cairo_canvas_surface_t *target = _target;
+    cairo_canvas_context_t *cr;
+    cairo_status_t status;
+
+    cr = _freed_pool_get (&context_pool);
+    if (unlikely (cr == NULL)) {
+	cr = malloc (sizeof (cairo_canvas_context_t));
+	if (unlikely (cr == NULL))
+	    return _cairo_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+    }
+
+    status = _cairo_default_context_init (&cr->base, target->recording);
+    if (unlikely (status)) {
+	_freed_pool_put (&context_pool, cr);
+	return _cairo_create_in_error (status);
+    }
+
+    cr->default_backend = cr->base.base.backend;
+    cr->base.base.backend = &_cairo_canvas_context_backend;
+
+    cr->target = &target->base;
+    cr->device = target->base.device;
+
+    cr->var = _cairo_canvas_create_context (cr->device, target);
+
+    return &cr->base.base;
+}
diff --git a/src/cairo-canvas-device.c b/src/cairo-canvas-device.c
new file mode 100644
index 0000000..6c6b281
--- /dev/null
+++ b/src/cairo-canvas-device.c
@@ -0,0 +1,245 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2011 Intel Corporation
+ *
+ * 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 Red Hat, Inc.
+ *
+ * Contributor(s):
+ *	Chris Wilson <chris at chris-wilson.co.uk>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-canvas.h"
+#include "cairo-canvas-private.h"
+
+#include "cairo-output-stream-private.h"
+
+#include <stdarg.h>
+
+#define _cairo_output_stream_puts(S, STR) \
+    _cairo_output_stream_write ((S), (STR), strlen (STR))
+
+static const char *basic_html_page_header =
+"<!DOCTYPE html>\n"
+"<html>\n"
+"  <head>\n"
+"    <title>Basic Canvas</title>\n"
+"  </head>\n"
+"  <body>\n";
+static const char *basic_html_page_footer =
+"  </body>\n"
+"</html>\n";
+
+int
+_cairo_canvas_create_context (cairo_device_t *device,
+			      cairo_canvas_surface_t *surface)
+{
+    cairo_canvas_device_t *canvas = (cairo_canvas_device_t *)device;
+    int id = canvas->context_id++;
+
+    if (canvas->emit_page && canvas->current == NULL ) {
+	_cairo_canvas_emit (device, "<script>\n");
+	canvas->indent += 2;
+	canvas->current = &surface->base;
+    }
+
+    _cairo_canvas_emit (device,
+			"var cr%d = document.getElementById('%s').getContext('2d')\n",
+			id, surface->id);
+
+    return id;
+}
+
+static void CAIRO_PRINTF_FORMAT ( 2, 0)
+__cairo_canvas_emit (cairo_canvas_device_t *canvas,
+		     const char *format,
+		     va_list va)
+{
+    char buf[80];
+
+    if (canvas->indent) {
+	memset(buf, ' ', canvas->indent);
+	_cairo_output_stream_write (canvas->stream, buf, canvas->indent);
+    }
+
+    _cairo_output_stream_vprintf (canvas->stream, format, va);
+}
+
+cairo_status_t
+_cairo_canvas_emit (cairo_device_t *device,
+		    const char *format,
+		    ...)
+{
+    cairo_canvas_device_t *canvas = (cairo_canvas_device_t *)device;
+    va_list ap;
+
+    va_start (ap, format);
+    __cairo_canvas_emit (canvas, format, ap);
+    va_end (ap);
+
+    return _cairo_output_stream_get_status (canvas->stream);
+}
+
+void
+_cairo_canvas_basic_page_emit (cairo_device_t *device,
+			      const char *format,
+			      ...)
+{
+    cairo_canvas_device_t *canvas = (cairo_canvas_device_t *)device;
+    va_list ap;
+
+    if (! canvas->emit_page)
+	return;
+
+    va_start (ap, format);
+    __cairo_canvas_emit (canvas, format, ap);
+    va_end (ap);
+}
+
+static cairo_status_t
+_cairo_canvas_device_flush (void *abstract_device)
+{
+    cairo_canvas_device_t *canvas = abstract_device;
+    cairo_status_t status;
+
+    status = _cairo_output_stream_flush (canvas->stream);
+
+    return status;
+}
+
+static void
+_cairo_canvas_device_destroy (void *abstract_device)
+{
+    cairo_canvas_device_t *canvas = abstract_device;
+
+    if (canvas->emit_page) {
+	if (canvas->current ) {
+	    _cairo_canvas_emit (&canvas->base, "</script>\n");
+	    canvas->indent -= 2;
+	}
+	_cairo_output_stream_puts (canvas->stream,
+				   basic_html_page_footer);
+    }
+
+    _cairo_output_stream_destroy (canvas->stream);
+
+    free (canvas->images);
+    free (canvas);
+}
+
+static const cairo_device_backend_t _cairo_canvas_device_backend = {
+    CAIRO_DEVICE_TYPE_CANVAS,
+
+    NULL, NULL, /* lock, unlock */
+
+    _cairo_canvas_device_flush,
+    NULL,  /* finish */
+    _cairo_canvas_device_destroy
+};
+
+static cairo_device_t *
+_cairo_canvas_create_internal (cairo_output_stream_t *stream)
+{
+    cairo_canvas_device_t *canvas;
+
+    canvas = malloc (sizeof (cairo_canvas_device_t));
+    if (unlikely (canvas == NULL))
+	return _cairo_device_create_in_error (CAIRO_STATUS_NO_MEMORY);
+
+    memset (canvas, 0, sizeof (cairo_canvas_device_t));
+
+    _cairo_device_init (&canvas->base, &_cairo_canvas_device_backend);
+    canvas->stream = stream;
+
+    return &canvas->base;
+}
+
+cairo_device_t *
+cairo_canvas_create (const char *filename)
+{
+    cairo_output_stream_t *stream;
+    cairo_status_t status;
+
+    stream = _cairo_output_stream_create_for_filename (filename);
+    if ((status = _cairo_output_stream_get_status (stream)))
+	return _cairo_device_create_in_error (status);
+
+    return _cairo_canvas_create_internal (stream);
+}
+
+cairo_device_t *
+cairo_canvas_create_for_stream (cairo_write_func_t write_func,
+				void *closure)
+{
+    cairo_output_stream_t *stream;
+    cairo_status_t status;
+
+    stream = _cairo_output_stream_create (write_func, NULL, closure);
+    if ((status = _cairo_output_stream_get_status (stream)))
+	return _cairo_device_create_in_error (status);
+
+    return _cairo_canvas_create_internal (stream);
+}
+
+void
+cairo_canvas_emit_basic_page (cairo_device_t *device)
+{
+    cairo_canvas_device_t *canvas;
+
+    if (unlikely (device->backend->type != CAIRO_DEVICE_TYPE_CANVAS))
+	return;
+
+    if (unlikely (device->status))
+	return;
+
+    canvas = (cairo_canvas_device_t *)device;
+    canvas->emit_page = TRUE;
+    _cairo_output_stream_puts (canvas->stream, basic_html_page_header);
+    canvas->indent = 4;
+}
+
+void
+cairo_canvas_set_image_directory (cairo_device_t *device,
+				  const char *directory)
+{
+    cairo_canvas_device_t *canvas;
+
+    if (unlikely (device->backend->type != CAIRO_DEVICE_TYPE_CANVAS))
+	return;
+
+    if (unlikely (device->status))
+	return;
+
+    canvas = (cairo_canvas_device_t *)device;
+    free (canvas->images);
+    if (directory)
+	canvas->images = strdup (directory);
+    else
+	canvas->images = NULL;
+}
diff --git a/src/cairo-canvas-private.h b/src/cairo-canvas-private.h
new file mode 100644
index 0000000..5f79c44
--- /dev/null
+++ b/src/cairo-canvas-private.h
@@ -0,0 +1,102 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2011 Intel Corporation
+ *
+ * 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 Red Hat, Inc.
+ *
+ * Contributor(s):
+ *	Chris Wilson <chris at chris-wilson.co.uk>
+ */
+
+#ifndef CAIRO_CANVAS_PRIVATE_H
+#define CAIRO_CANVAS_PRIVATE_H
+
+#include "cairo-default-context-private.h"
+#include "cairo-device-private.h"
+#include "cairo-surface-private.h"
+
+typedef struct _cairo_canvas_context cairo_canvas_context_t;
+typedef struct _cairo_canvas_device cairo_canvas_device_t;
+typedef struct _cairo_canvas_surface cairo_canvas_surface_t;
+
+struct _cairo_canvas_context {
+    cairo_default_context_t base;
+    const cairo_backend_t *default_backend;
+
+    cairo_device_t *device;
+    int var;
+
+    cairo_surface_t *target;
+    cairo_surface_t *original_target;
+    cairo_surface_t *parent_target;
+    cairo_bool_t clear;
+
+    double x, y;
+};
+
+struct _cairo_canvas_device {
+    cairo_device_t base;
+
+    cairo_output_stream_t *stream;
+    int indent;
+    int canvas_id;
+    int context_id;
+
+    cairo_surface_t *current;
+
+    cairo_bool_t emit_page;
+    char *images;
+};
+
+struct _cairo_canvas_surface {
+    cairo_surface_t base;
+
+    char *id;
+    int width, height;
+
+    cairo_surface_t *recording;
+};
+
+cairo_private cairo_t *
+_cairo_canvas_context_create (void *target);
+
+cairo_private void
+_cairo_canvas_basic_page_emit (cairo_device_t *device,
+			       const char *format,
+			       ...) CAIRO_PRINTF_FORMAT ( 2, 3);
+
+cairo_private int
+_cairo_canvas_create_context (cairo_device_t *device,
+			      cairo_canvas_surface_t *surface);
+
+cairo_private_no_warn cairo_status_t
+_cairo_canvas_emit (cairo_device_t *device,
+		    const char *format,
+		    ...) CAIRO_PRINTF_FORMAT ( 2, 3);
+
+#endif /* CAIRO_CANVAS_PRIVATE_H */
diff --git a/src/cairo-canvas-surface.c b/src/cairo-canvas-surface.c
new file mode 100644
index 0000000..c36d405
--- /dev/null
+++ b/src/cairo-canvas-surface.c
@@ -0,0 +1,187 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2011 Intel Corporation
+ *
+ * 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 Red Hat, Inc.
+ *
+ * Contributor(s):
+ *	Chris Wilson <chris at chris-wilson.co.uk>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-canvas.h"
+#include "cairo-canvas-private.h"
+
+#include "cairo-recording-surface-private.h"
+
+static cairo_status_t
+_cairo_canvas_surface_finish (void *abstract_surface)
+{
+    cairo_canvas_surface_t *surface = abstract_surface;
+
+    free (surface->id);
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_surface_t *
+_cairo_canvas_surface_create_similar (void	       *abstract_other,
+				      cairo_content_t	content,
+				      int		width,
+				      int		height)
+{
+    cairo_canvas_surface_t *other = abstract_other;
+
+    return cairo_canvas_surface_create (other->base.device, NULL,
+					width, height);
+}
+
+static cairo_bool_t
+_cairo_canvas_surface_get_extents (void			  *abstract_surface,
+				   cairo_rectangle_int_t   *rectangle)
+{
+    cairo_canvas_surface_t *surface = abstract_surface;
+
+    rectangle->x = 0;
+    rectangle->y = 0;
+    rectangle->width  = surface->width;
+    rectangle->height = surface->height;
+
+    return TRUE;
+}
+
+static cairo_surface_t *
+_cairo_canvas_surface_snapshot (void *abstract_surface)
+{
+    cairo_canvas_surface_t *surface = abstract_surface;
+
+    return _cairo_surface_snapshot (surface->recording);
+}
+
+static const cairo_surface_backend_t
+_cairo_canvas_surface_backend = {
+    CAIRO_SURFACE_TYPE_CANVAS,
+    _cairo_canvas_surface_finish,
+    _cairo_canvas_context_create,
+
+    _cairo_canvas_surface_create_similar,
+    NULL, /* create_similar_image */
+    NULL, /* map_to_image */
+    NULL, /* unmap_image */
+
+    NULL, NULL, /* source image */
+    NULL, NULL, /* dst image */
+    NULL, /* clone_similar */
+    NULL, /* composite */
+    NULL, /* fill_rectangles */
+    NULL, /* composite_trapezoids */
+    NULL, /* create_span_renderer */
+    NULL, /* check_span_renderer */
+    NULL, NULL, /* copy/show page */
+    _cairo_canvas_surface_get_extents,
+    NULL, /* old_show_glyphs */
+    NULL, /* get_font_options */
+    NULL, /* flush */
+    NULL, /* mark_dirty_rectangle */
+    NULL, /* font fini */
+    NULL, /* scaled_glyph_fini */
+
+    /* The 5 high level operations */
+    /* XXX allow for replay? */
+    NULL, /* paint */
+    NULL, /* mask */
+    NULL, /* stroke */
+    NULL, /* fill */
+    NULL, /* glyphs */
+
+    _cairo_canvas_surface_snapshot,
+
+    NULL, /* is_similar */
+    NULL, /* fill_stroke */
+    NULL, /* create_solid_pattern_surface */
+    NULL, /* can_repaint_solid_pattern_surface */
+
+    /* The alternate high-level text operation */
+    NULL, NULL, /* has, show_text_glyphs */
+};
+
+cairo_surface_t *
+cairo_canvas_surface_create (cairo_device_t *device,
+			     const char *id,
+			     int width, int height)
+{
+    cairo_canvas_surface_t *surface;
+    cairo_canvas_device_t *canvas;
+
+    if (unlikely (device->backend->type != CAIRO_DEVICE_TYPE_CANVAS))
+	return _cairo_surface_create_in_error (CAIRO_STATUS_DEVICE_TYPE_MISMATCH);
+
+    if (unlikely (device->status))
+	return _cairo_surface_create_in_error (device->status);
+
+    canvas = (cairo_canvas_device_t *)device;
+
+
+    surface = malloc (sizeof (cairo_canvas_surface_t));
+    if (unlikely (surface == NULL))
+	return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+    _cairo_surface_init (&surface->base,
+			 &_cairo_canvas_surface_backend, device,
+			 CAIRO_CONTENT_COLOR_ALPHA);
+
+    surface->width = width;
+    surface->height = height;
+    surface->base.is_clear = TRUE;
+
+    surface->recording =
+	cairo_recording_surface_create (surface->base.content, NULL);
+
+    if (id) {
+	surface->id = strdup (id);
+    } else {
+	char buf[80], len;
+	len = sprintf (buf, "cairo-canvas-%d", ++canvas->canvas_id);
+	surface->id = malloc (len+1);
+	if (surface->id)
+	    memcpy (surface->id, buf, len+1);
+    }
+    if (unlikely (surface->id == NULL)) {
+	free (surface);
+	return _cairo_surface_create_in_error (CAIRO_STATUS_NO_MEMORY);
+    }
+
+    _cairo_canvas_basic_page_emit (device,
+				   "<canvas id=\"%s\" width=\"%dpx\" height=\"%dpx\">\n",
+				   surface->id,
+				   surface->width,
+				   surface->height);
+    _cairo_canvas_basic_page_emit (device, "</canvas>\n");
+
+    return &surface->base;
+}
diff --git a/src/cairo-canvas.h b/src/cairo-canvas.h
new file mode 100644
index 0000000..b9ef824
--- /dev/null
+++ b/src/cairo-canvas.h
@@ -0,0 +1,69 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2011 Intel Corporation
+ *
+ * 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 Chris Wilson
+ *
+ * Contributor(s):
+ *	Chris Wilson <chris at chris-wilson.co.uk>
+ */
+
+#ifndef CAIRO_CANVAS_H
+#define CAIRO_CANVAS_H
+
+#include "cairo.h"
+
+#if CAIRO_HAS_CANVAS_SURFACE
+
+CAIRO_BEGIN_DECLS
+
+cairo_public cairo_device_t *
+cairo_canvas_create (const char *filename);
+
+cairo_public cairo_device_t *
+cairo_canvas_create_for_stream (cairo_write_func_t	 write_func,
+				void		*closure);
+
+cairo_public void
+cairo_canvas_emit_basic_page (cairo_device_t *canvas);
+
+cairo_public void
+cairo_canvas_set_image_directory (cairo_device_t *canvas,
+				  const char *directory);
+
+cairo_public cairo_surface_t *
+cairo_canvas_surface_create (cairo_device_t *canvas,
+			     const char *id, int width, int height);
+
+CAIRO_END_DECLS
+
+#else  /*CAIRO_HAS_CANVAS_SURFACE*/
+# error Cairo was not compiled with support for the HTML5 Canvas backend
+#endif /*CAIRO_HAS_CANVAS_SURFACE*/
+
+#endif /*CAIRO_CANVAS_H*/
diff --git a/src/cairo-debug.c b/src/cairo-debug.c
index 6afdb3f..162335c 100644
--- a/src/cairo-debug.c
+++ b/src/cairo-debug.c
@@ -89,6 +89,10 @@ cairo_debug_reset_static_data (void)
     _cairo_drm_device_reset_static_data ();
 #endif
 
+#if CAIRO_HAS_CANVAS_SURFACE
+    _cairo_canvas_context_reset_static_data ();
+#endif
+
     _cairo_default_context_reset_static_data ();
 
     CAIRO_MUTEX_FINALIZE ();
diff --git a/src/cairo-default-context-private.h b/src/cairo-default-context-private.h
index 32fd12d..4933cdd 100644
--- a/src/cairo-default-context-private.h
+++ b/src/cairo-default-context-private.h
@@ -55,4 +55,11 @@ struct _cairo_default_context {
 cairo_private cairo_t *
 _cairo_default_context_create (void *target);
 
+cairo_private cairo_status_t
+_cairo_default_context_init (cairo_default_context_t *cr,
+			     void *target);
+
+cairo_private void
+_cairo_default_context_fini (cairo_default_context_t *cr);
+
 #endif /* CAIRO_DEFAULT_CONTEXT_PRIVATE_H */
diff --git a/src/cairo-default-context.c b/src/cairo-default-context.c
index 4124632..79cddc6 100644
--- a/src/cairo-default-context.c
+++ b/src/cairo-default-context.c
@@ -63,11 +63,9 @@ _cairo_default_context_reset_static_data (void)
     _freed_pool_reset (&context_pool);
 }
 
-static void
-_cairo_default_context_destroy (void *abstract_cr)
+void
+_cairo_default_context_fini (cairo_default_context_t *cr)
 {
-    cairo_default_context_t *cr = abstract_cr;
-
     while (cr->gstate != &cr->gstate_tail[0]) {
 	if (_cairo_gstate_restore (&cr->gstate, &cr->gstate_freelist))
 	    break;
@@ -84,6 +82,14 @@ _cairo_default_context_destroy (void *abstract_cr)
     _cairo_path_fixed_fini (cr->path);
 
     _cairo_fini (&cr->base);
+}
+
+static void
+_cairo_default_context_destroy (void *abstract_cr)
+{
+    cairo_default_context_t *cr = abstract_cr;
+
+    _cairo_default_context_fini (cr);
 
     /* mark the context as invalid to protect against misuse */
     cr->base.status = CAIRO_STATUS_NULL_POINTER;
@@ -1381,6 +1387,20 @@ static const cairo_backend_t _cairo_default_context_backend = {
     _cairo_default_context_show_page,
 };
 
+cairo_status_t
+_cairo_default_context_init (cairo_default_context_t *cr,
+			     void *target)
+{
+    _cairo_init (&cr->base, &_cairo_default_context_backend);
+    _cairo_path_fixed_init (cr->path);
+
+    cr->gstate = &cr->gstate_tail[0];
+    cr->gstate_freelist = &cr->gstate_tail[1];
+    cr->gstate_tail[1].next = NULL;
+
+    return _cairo_gstate_init (cr->gstate, target);
+}
+
 cairo_t *
 _cairo_default_context_create (void *target)
 {
@@ -1394,14 +1414,7 @@ _cairo_default_context_create (void *target)
 	    return _cairo_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
     }
 
-    _cairo_init (&cr->base, &_cairo_default_context_backend);
-    _cairo_path_fixed_init (cr->path);
-
-    cr->gstate = &cr->gstate_tail[0];
-    cr->gstate_freelist = &cr->gstate_tail[1];
-    cr->gstate_tail[1].next = NULL;
-
-    status = _cairo_gstate_init (cr->gstate, target);
+    status = _cairo_default_context_init (cr, target);
     if (unlikely (status)) {
 	_freed_pool_put (&context_pool, cr);
 	return _cairo_create_in_error (status);
diff --git a/src/cairo.h b/src/cairo.h
index 24b9f0d..e2114b4 100644
--- a/src/cairo.h
+++ b/src/cairo.h
@@ -2017,7 +2017,7 @@ cairo_device_reference (cairo_device_t *device);
  * @CAIRO_DEVICE_TYPE_XCB: The device is of type xcb
  * @CAIRO_DEVICE_TYPE_XLIB: The device is of type xlib
  * @CAIRO_DEVICE_TYPE_XML: The device is of type XML
- * @CAIRO_DEVICE_TYPE_INVALID: The device is invalid
+ * @CAIRO_DEVICE_TYPE_CANVAS: The device is of type HTML5 Canvas (since 1.12)
  *
  * #cairo_device_type_t is used to describe the type of a given
  * device. The devices types are also known as "backends" within cairo.
@@ -2045,6 +2045,7 @@ typedef enum _cairo_device_type {
     CAIRO_DEVICE_TYPE_XCB,
     CAIRO_DEVICE_TYPE_XLIB,
     CAIRO_DEVICE_TYPE_XML,
+    CAIRO_DEVICE_TYPE_CANVAS,
 
     CAIRO_DEVICE_TYPE_INVALID = -1
 } cairo_device_type_t;
@@ -2158,6 +2159,7 @@ cairo_surface_status (cairo_surface_t *surface);
  * @CAIRO_SURFACE_TYPE_SKIA: The surface is of type Skia, since 1.10
  * @CAIRO_SURFACE_TYPE_SUBSURFACE: The surface is a subsurface created with
  *   cairo_surface_create_for_rectangle(), since 1.10
+ * @CAIRO_SURFACE_TYPE_CANVAS: The surface is of type HTML5 Canvas, since 1.12
  * @CAIRO_SURFACE_TYPE_FASTIMAGE: The surface is of type fast-image, since 1.12
  *
  * #cairo_surface_type_t is used to describe the type of a given
@@ -2209,6 +2211,7 @@ typedef enum _cairo_surface_type {
     CAIRO_SURFACE_TYPE_XML,
     CAIRO_SURFACE_TYPE_SKIA,
     CAIRO_SURFACE_TYPE_SUBSURFACE,
+    CAIRO_SURFACE_TYPE_CANVAS,
     CAIRO_SURFACE_TYPE_FASTIMAGE
 } cairo_surface_type_t;
 
diff --git a/src/cairoint.h b/src/cairoint.h
index 0d3790b..1976a7a 100644
--- a/src/cairoint.h
+++ b/src/cairoint.h
@@ -430,6 +430,9 @@ cairo_private void
 _cairo_default_context_reset_static_data (void);
 
 cairo_private void
+_cairo_canvas_context_reset_static_data (void);
+
+cairo_private void
 _cairo_toy_font_face_reset_static_data (void);
 
 cairo_private void
commit 06bba6a2a90cee3694645743053117df04091108
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Sun Aug 7 11:54:00 2011 +0100

    moah

diff --git a/src/cairo-path-fixed-private.h b/src/cairo-path-fixed-private.h
index e24b1c7..aa3a6b9 100644
--- a/src/cairo-path-fixed-private.h
+++ b/src/cairo-path-fixed-private.h
@@ -182,4 +182,7 @@ _cairo_path_fixed_fill_maybe_region (const cairo_path_fixed_t *path)
 	   path->current_point.y == path->last_move_point.y;
 }
 
+cairo_private int
+_cairo_path_fixed_num_ops (const cairo_path_fixed_t *path);
+
 #endif /* CAIRO_PATH_FIXED_PRIVATE_H */
diff --git a/src/cairo-path-fixed.c b/src/cairo-path-fixed.c
index 8c1e3ab..5f754df 100644
--- a/src/cairo-path-fixed.c
+++ b/src/cairo-path-fixed.c
@@ -226,6 +226,19 @@ _cairo_path_fixed_size (const cairo_path_fixed_t *path)
 	   num_points * sizeof (buf->points[0]);
 }
 
+int
+_cairo_path_fixed_num_ops (const cairo_path_fixed_t *path)
+{
+    const cairo_path_buf_t *buf;
+    int num_ops;
+
+    cairo_path_foreach_buf_start (buf, path) {
+	num_ops += buf->num_ops;
+    } cairo_path_foreach_buf_end (buf, path);
+
+    return num_ops;
+}
+
 cairo_bool_t
 _cairo_path_fixed_equal (const cairo_path_fixed_t *a,
 			 const cairo_path_fixed_t *b)
diff --git a/src/cairo-path-stroke-polygon.c b/src/cairo-path-stroke-polygon.c
index 94c2c05..5603bc7 100644
--- a/src/cairo-path-stroke-polygon.c
+++ b/src/cairo-path-stroke-polygon.c
@@ -49,6 +49,9 @@
 
 #define DEBUG 1
 
+static int dump;
+static int dump_once;
+
 struct stroker {
     cairo_stroke_style_t style;
 
@@ -81,7 +84,7 @@ struct stroker {
     cairo_stroke_face_t first_face;
 };
 
-static inline void
+static inline double
 normalize_slope (double *dx, double *dy);
 
 static void
@@ -227,36 +230,48 @@ join_is_clockwise (const cairo_stroke_face_t *in,
     return _cairo_slope_compare (&in->dev_vector, &out->dev_vector) < 0;
 }
 
-static double
+static cairo_int64_t
 distance_from_face (const cairo_stroke_face_t *face,
-		    const cairo_point_t *p)
+		    const cairo_point_t *p,
+		    cairo_bool_t negate)
 {
-    return (p->x - face->point.x) * face->dev_slope.y - (p->y - face->point.y) * face->dev_slope.x;
+    int32_t dx = (p->x - face->point.x);
+    int32_t dy = (p->y - face->point.y);
+    cairo_int64_t d;
+
+    d = _cairo_int64_sub (_cairo_int32x32_64_mul (dx, face->dev_vector.dy),
+			  _cairo_int32x32_64_mul (dy, face->dev_vector.dx));
+    if (negate)
+	d = _cairo_int64_negate (d);
+    return d;
 }
 
-static double
+static cairo_int64_t
 distance_along_face (const cairo_stroke_face_t *face,
 		    const cairo_point_t *p)
 {
-    return (p->x - face->point.x) * face->dev_slope.x + (p->y - face->point.y) * face->dev_slope.y;
+    int32_t dx = (p->x - face->point.x);
+    int32_t dy = (p->y - face->point.y);
+    return _cairo_int64_add (_cairo_int32x32_64_mul (dx, face->dev_vector.dx),
+			     _cairo_int32x32_64_mul (dy, face->dev_vector.dy));
 }
 
-
-static double
-distance_from_edge (struct stroker *stroker,
-		    const cairo_point_t *p,
-		    const cairo_point_t *e1,
-		    const cairo_point_t *e2)
+static void
+compute_inner_joint (cairo_point_t *p1, cairo_int64_t d_p1,
+		     const cairo_point_t *p2, cairo_int64_t d_p2,
+		     cairo_int64_t half_line_width)
 {
-    cairo_stroke_face_t edge;
-    cairo_slope_t slope;
+    int32_t dx = p2->x - p1->x;
+    int32_t dy = p2->y - p1->y;
 
-    _cairo_slope_init (&slope, e1, e2);
-    if (slope.dx == 0 && slope.dy == 0)
-	return 0;
+    half_line_width = _cairo_int64_sub (half_line_width, d_p1);
+    d_p2 = _cairo_int64_sub (d_p2, d_p1);
 
-    compute_face (e1, &slope, stroker, &edge);
-    return fabs (distance_from_face (&edge, p));
+    p1->x += _cairo_int_96by64_32x64_divrem (_cairo_int64x32_128_mul (half_line_width, dx),
+					      d_p2).quo;
+
+    p1->y += _cairo_int_96by64_32x64_divrem (_cairo_int64x32_128_mul (half_line_width, dy),
+					      d_p2).quo;
 }
 
 static void
@@ -268,32 +283,29 @@ inner_join (struct stroker *stroker,
     cairo_point_t last;
     const cairo_point_t *p, *outpt;
     struct stroke_contour *inner;
-    double d_p, d_last = -1;
-    double dot;
-    double line_width;
-    double sign;
+    cairo_int64_t d_p, d_last;
+    cairo_int64_t half_line_width;
+    cairo_bool_t negate;
 
     /* XXX line segments shorter than line-width */
 
     if (clockwise) {
 	inner = &stroker->ccw;
 	outpt = &out->ccw;
-	sign = -1;
+	negate = 1;
     } else {
 	inner = &stroker->cw;
 	outpt = &out->cw;
-	sign = 1;
+	negate = 0;
     }
 
-    line_width = stroker->style.line_width/2;
-    line_width *= CAIRO_FIXED_ONE;
+    half_line_width = CAIRO_FIXED_ONE*CAIRO_FIXED_ONE/2 * stroker->style.line_width * out->length + .5;
 
     /* On the inside, the previous end-point is always
      * closer to the new face by definition.
      */
     last = *_cairo_contour_last_point (&inner->contour);
-    d_last = sign * distance_from_face (out, &last);
-    assert (d_last <= line_width);
+    d_last = distance_from_face (out, &last, negate);
     _cairo_contour_remove_last_point (&inner->contour);
 
 prev:
@@ -302,8 +314,9 @@ prev:
 	return;
     }
     p = _cairo_contour_last_point (&inner->contour);
-    if ((d_p = sign * distance_from_face (out, p)) <= line_width &&
-	distance_along_face (out, p) >= 0)
+    d_p = distance_from_face (out, p, negate);
+    if (_cairo_int64_lt (d_p, half_line_width) &&
+	!_cairo_int64_negative (distance_along_face (out, p)))
     {
 	last = *p;
 	d_last = d_p;
@@ -311,11 +324,7 @@ prev:
 	goto prev;
     }
 
-    if (d_p != d_last) { /* parallel edge */
-	dot = (line_width - d_last) / (d_p - d_last);
-	last.x += dot * (p->x - last.x);
-	last.y += dot * (p->y - last.y);
-    }
+    compute_inner_joint (&last, d_last, p, d_p, half_line_width);
     contour_add_point (stroker, inner, &last);
 }
 
@@ -328,10 +337,6 @@ inner_close (struct stroker *stroker,
     const cairo_point_t *p, *outpt, *inpt;
     struct stroke_contour *inner;
     struct _cairo_contour_chain *chain;
-    double d_p, d_last = -1;
-    double line_width;
-    double sign;
-    int i;
 
     /* XXX line segments shorter than line-width */
 
@@ -339,19 +344,18 @@ inner_close (struct stroker *stroker,
 	inner = &stroker->ccw;
 	outpt = &in->ccw;
 	inpt = &out->ccw;
-	sign = -1;
     } else {
 	inner = &stroker->cw;
 	outpt = &in->cw;
 	inpt = &out->cw;
-	sign = 1;
     }
 
-    if (inner->contour.chain.num_points == 0) {
+    if (1 || inner->contour.chain.num_points == 0) {
 	contour_add_point (stroker, inner, outpt);
 	return;
     }
 
+#if 0
     line_width = stroker->style.line_width/2;
     line_width *= CAIRO_FIXED_ONE;
 
@@ -390,6 +394,7 @@ out:
 	    *pp = last;
 	}
     }
+#endif
 }
 
 static void
@@ -571,6 +576,8 @@ outer_close (struct stroker *stroker,
 		p.y = _cairo_fixed_from_double (my);
 
 		*_cairo_contour_last_point (&outer->contour) = p;
+		*_cairo_contour_first_point (&outer->contour) = p;
+		return;
 	    }
 	}
 	break;
@@ -606,9 +613,6 @@ outer_join (struct stroker *stroker,
 	outer = &stroker->ccw;
     }
 
-    if (close_enough (inpt, outpt, stroker->contour_tolerance))
-	return;
-
     switch (stroker->style.line_join) {
     case CAIRO_LINE_JOIN_ROUND:
 	/* construct a fan around the common midpoint */
@@ -751,7 +755,7 @@ outer_join (struct stroker *stroker,
 	     * Make sure the miter point line lies between the two
 	     * faces by comparing the slopes
 	     */
-	    if (slope_compare_sgn (fdx1, fdy1, mdx, mdy) !=
+	    if (1 || slope_compare_sgn (fdx1, fdy1, mdx, mdy) !=
 		slope_compare_sgn (fdx2, fdy2, mdx, mdy))
 	    {
 		cairo_point_t p;
@@ -852,7 +856,7 @@ add_trailing_cap (struct stroker *stroker,
     add_cap (stroker, face, c);
 }
 
-static inline void
+static inline double
 normalize_slope (double *dx, double *dy)
 {
     double dx0 = *dx, dy0 = *dy;
@@ -883,6 +887,8 @@ normalize_slope (double *dx, double *dy)
 	*dx = dx0 / mag;
 	*dy = dy0 / mag;
     }
+
+    return mag;
 }
 
 static void
@@ -897,7 +903,7 @@ compute_face (const cairo_point_t *point,
 
     slope_dx = _cairo_fixed_to_double (dev_slope->dx);
     slope_dy = _cairo_fixed_to_double (dev_slope->dy);
-    normalize_slope (&slope_dx, &slope_dy);
+    face->length = normalize_slope (&slope_dx, &slope_dy);
     face->dev_slope.x = slope_dx;
     face->dev_slope.y = slope_dy;
 
@@ -973,28 +979,39 @@ add_caps (struct stroker *stroker)
 	_cairo_polygon_add_contour (stroker->polygon, &stroker->ccw.contour);
 	_cairo_contour_reset (&stroker->ccw.contour);
     } else {
+	if (stroker->has_current_face)
+	    add_trailing_cap (stroker, &stroker->current_face, &stroker->ccw);
+
 #if DEBUG
-	{
+	if (dump) {
 	    FILE *file = fopen ("contours.txt", "a");
 	    _cairo_debug_print_contour (file, &stroker->path);
 	    _cairo_debug_print_contour (file, &stroker->cw.contour);
 	    _cairo_debug_print_contour (file, &stroker->ccw.contour);
 	    fclose (file);
+	    _cairo_contour_reset (&stroker->path);
 	}
-	_cairo_contour_reset (&stroker->path);
 #endif
 
-	if (stroker->has_current_face)
-	    add_trailing_cap (stroker, &stroker->current_face, &stroker->ccw);
-
 	_cairo_polygon_add_contour (stroker->polygon, &stroker->ccw.contour);
 	_cairo_contour_reset (&stroker->ccw.contour);
 
 	if (stroker->has_first_face) {
+	    _cairo_contour_add_point (&stroker->ccw.contour,
+				      &stroker->first_face.cw);
 	    add_leading_cap (stroker, &stroker->first_face, &stroker->ccw);
+#if DEBUG
+	if (dump) {
+	    FILE *file = fopen ("contours.txt", "a");
+	    _cairo_debug_print_contour (file, &stroker->ccw.contour);
+	    fclose (file);
+	}
+#endif
+
+	    _cairo_polygon_add_contour (stroker->polygon,
+					&stroker->ccw.contour);
+	    _cairo_contour_reset (&stroker->ccw.contour);
 	}
-	_cairo_polygon_add_contour (stroker->polygon, &stroker->ccw.contour);
-	_cairo_contour_reset (&stroker->ccw.contour);
 
 	_cairo_polygon_add_contour (stroker->polygon, &stroker->cw.contour);
 	_cairo_contour_reset (&stroker->cw.contour);
@@ -1020,7 +1037,8 @@ move_to (void *closure,
     stroker->first_point = *point;
 
 #if DEBUG
-    _cairo_contour_add_point (&stroker->path, point);
+    if (dump)
+	_cairo_contour_add_point (&stroker->path, point);
 #endif
 
     stroker->current_face.point = *point;
@@ -1043,7 +1061,8 @@ line_to (void *closure,
 	return CAIRO_STATUS_SUCCESS;
 
 #if DEBUG
-    _cairo_contour_add_point (&stroker->path, point);
+    if (dump)
+	_cairo_contour_add_point (&stroker->path, point);
 #endif
 
     _cairo_slope_init (&dev_slope, p1, point);
@@ -1094,7 +1113,8 @@ spline_to (void *closure,
     cairo_stroke_face_t face;
 
 #if DEBUG
-    _cairo_contour_add_point (&stroker->path, point);
+    if (dump)
+	_cairo_contour_add_point (&stroker->path, point);
 #endif
 
     compute_face (point, tangent, stroker, &face);
@@ -1195,7 +1215,7 @@ close_path (void *closure)
 	_cairo_polygon_add_contour (stroker->polygon, &stroker->ccw.contour);
 
 #if DEBUG
-	{
+	if (dump) {
 	    FILE *file = fopen ("contours.txt", "a");
 	    _cairo_debug_print_contour (file, &stroker->path);
 	    _cairo_debug_print_contour (file, &stroker->cw.contour);
@@ -1239,6 +1259,11 @@ _cairo_path_fixed_stroke_to_polygon (const cairo_path_fixed_t	*path,
 							   polygon);
     }
 
+    if (dump_once == 0 && _cairo_path_fixed_num_ops (path) > 1000) {
+	dump = 1;
+	dump_once = 1;
+    }
+
     stroker.style = *style;
     stroker.ctm = ctm;
     stroker.ctm_inverse = ctm_inverse;
@@ -1263,7 +1288,8 @@ _cairo_path_fixed_stroke_to_polygon (const cairo_path_fixed_t	*path,
     stroker.has_initial_sub_path = FALSE;
 
 #if DEBUG
-    _cairo_contour_init (&stroker.path, 0);
+    if (dump)
+	_cairo_contour_init (&stroker.path, 0);
 #endif
     _cairo_contour_init (&stroker.cw.contour, 1);
     _cairo_contour_init (&stroker.ccw.contour, -1);
@@ -1286,5 +1312,14 @@ _cairo_path_fixed_stroke_to_polygon (const cairo_path_fixed_t	*path,
     _cairo_contour_fini (&stroker.ccw.contour);
     _cairo_pen_fini (&stroker.pen);
 
+#if DEBUG
+    if (dump) {
+	FILE *file = fopen ("polygons.txt", "a");
+	_cairo_debug_print_polygon (file, polygon);
+	fclose (file);
+	dump = 0;
+    }
+#endif
+
     return status;
 }
diff --git a/src/cairoint.h b/src/cairoint.h
index c6243be..0d3790b 100644
--- a/src/cairoint.h
+++ b/src/cairoint.h
@@ -1032,6 +1032,7 @@ typedef struct _cairo_stroke_face {
     cairo_slope_t dev_vector;
     cairo_point_double_t dev_slope;
     cairo_point_double_t usr_vector;
+    double length;
 } cairo_stroke_face_t;
 
 /* cairo.c */
commit 877c958932451f4f8d1b3a6ffb3f2a2dfbd6f8d9
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Sat Aug 6 10:26:03 2011 +0100

    progress?

diff --git a/src/cairo-path-fixed.c b/src/cairo-path-fixed.c
index 7af45f8..8c1e3ab 100644
--- a/src/cairo-path-fixed.c
+++ b/src/cairo-path-fixed.c
@@ -1128,7 +1128,7 @@ _cpf_curve_to (void		*closure,
     cairo_point_t *p0 = &cpf->current_point;
 
     if (! _cairo_spline_init (&spline,
-			      cpf->line_to,
+			      (cairo_spline_add_point_func_t)cpf->line_to,
 			      cpf->closure,
 			      p0, p1, p2, p3))
     {
diff --git a/src/cairo-path-in-fill.c b/src/cairo-path-in-fill.c
index 3738874..1787fb1 100644
--- a/src/cairo-path-in-fill.c
+++ b/src/cairo-path-in-fill.c
@@ -217,7 +217,7 @@ _cairo_in_fill_curve_to (void *closure,
 
     /* XXX Investigate direct inspection of the inflections? */
     if (! _cairo_spline_init (&spline,
-			      _cairo_in_fill_line_to,
+			      (cairo_spline_add_point_func_t)_cairo_in_fill_line_to,
 			      in_fill,
 			      &in_fill->current_point, b, c, d))
     {
diff --git a/src/cairo-path-stroke-polygon.c b/src/cairo-path-stroke-polygon.c
index fed3dba..94c2c05 100644
--- a/src/cairo-path-stroke-polygon.c
+++ b/src/cairo-path-stroke-polygon.c
@@ -59,18 +59,17 @@ struct stroker {
     struct stroke_contour {
 	/* Note that these are not strictly contours as they may intersect */
 	cairo_contour_t contour;
-	cairo_uint64_t tolerance;
     } cw, ccw;
     cairo_polygon_t *polygon;
 
     const cairo_matrix_t *ctm;
     const cairo_matrix_t *ctm_inverse;
     double tolerance;
+    cairo_uint64_t contour_tolerance;
     cairo_bool_t ctm_det_positive;
 
     cairo_pen_t pen;
 
-    cairo_point_t current_point;
     cairo_point_t first_point;
 
     cairo_bool_t has_initial_sub_path;
@@ -85,6 +84,12 @@ struct stroker {
 static inline void
 normalize_slope (double *dx, double *dy);
 
+static void
+compute_face (const cairo_point_t *point,
+	      const cairo_slope_t *dev_slope,
+	      struct stroker *stroker,
+	      cairo_stroke_face_t *face);
+
 static cairo_uint64_t
 _cairo_point_distance2 (const cairo_point_t *p1,
 			const cairo_point_t *p2)
@@ -95,18 +100,20 @@ _cairo_point_distance2 (const cairo_point_t *p1,
 }
 
 static cairo_bool_t
-close_enough (const struct stroke_contour *c,
-	      const cairo_point_t *p1,
-	      const cairo_point_t *p2)
+close_enough (const cairo_point_t *p1,
+	      const cairo_point_t *p2,
+	      cairo_uint64_t tolerance)
 {
-    return _cairo_int64_lt (_cairo_point_distance2 (p1, p2), c->tolerance);
+    return _cairo_int64_lt (_cairo_point_distance2 (p1, p2), tolerance);
 }
 
 static void
-contour_add_point (struct stroke_contour *c,
+contour_add_point (struct stroker *stroker,
+		   struct stroke_contour *c,
 		   const cairo_point_t *point)
 {
-    if (! close_enough (c,point, _cairo_contour_last_point (&c->contour)))
+    if (! close_enough (point, _cairo_contour_last_point (&c->contour),
+			stroker->contour_tolerance))
 	_cairo_contour_add_point (&c->contour, point);
 }
 
@@ -209,7 +216,7 @@ add_fan (struct stroker *stroker,
     {
 	cairo_point_t p = *midpt;
 	translate_point (&p, &stroker->pen.vertices[i].point);
-	contour_add_point (c, &p);
+	contour_add_point (stroker, c, &p);
     }
 }
 
@@ -227,10 +234,36 @@ distance_from_face (const cairo_stroke_face_t *face,
     return (p->x - face->point.x) * face->dev_slope.y - (p->y - face->point.y) * face->dev_slope.x;
 }
 
+static double
+distance_along_face (const cairo_stroke_face_t *face,
+		    const cairo_point_t *p)
+{
+    return (p->x - face->point.x) * face->dev_slope.x + (p->y - face->point.y) * face->dev_slope.y;
+}
+
+
+static double
+distance_from_edge (struct stroker *stroker,
+		    const cairo_point_t *p,
+		    const cairo_point_t *e1,
+		    const cairo_point_t *e2)
+{
+    cairo_stroke_face_t edge;
+    cairo_slope_t slope;
+
+    _cairo_slope_init (&slope, e1, e2);
+    if (slope.dx == 0 && slope.dy == 0)
+	return 0;
+
+    compute_face (e1, &slope, stroker, &edge);
+    return fabs (distance_from_face (&edge, p));
+}
+
 static void
 inner_join (struct stroker *stroker,
 	    const cairo_stroke_face_t *in,
-	    const cairo_stroke_face_t *out)
+	    const cairo_stroke_face_t *out,
+	    int clockwise)
 {
     cairo_point_t last;
     const cairo_point_t *p, *outpt;
@@ -242,40 +275,48 @@ inner_join (struct stroker *stroker,
 
     /* XXX line segments shorter than line-width */
 
-    if (join_is_clockwise (in, out)) {
+    if (clockwise) {
 	inner = &stroker->ccw;
 	outpt = &out->ccw;
 	sign = -1;
     } else {
-	outpt = &out->cw;
 	inner = &stroker->cw;
+	outpt = &out->cw;
 	sign = 1;
     }
 
     line_width = stroker->style.line_width/2;
     line_width *= CAIRO_FIXED_ONE;
+
+    /* On the inside, the previous end-point is always
+     * closer to the new face by definition.
+     */
+    last = *_cairo_contour_last_point (&inner->contour);
+    d_last = sign * distance_from_face (out, &last);
+    assert (d_last <= line_width);
+    _cairo_contour_remove_last_point (&inner->contour);
+
 prev:
     if (inner->contour.chain.num_points == 0) {
-	contour_add_point (inner, outpt);
+	contour_add_point (stroker, inner, outpt);
 	return;
     }
     p = _cairo_contour_last_point (&inner->contour);
-    if ((d_p = sign * distance_from_face (out, p)) <= line_width) {
+    if ((d_p = sign * distance_from_face (out, p)) <= line_width &&
+	distance_along_face (out, p) >= 0)
+    {
 	last = *p;
 	d_last = d_p;
 	_cairo_contour_remove_last_point (&inner->contour);
 	goto prev;
     }
 
-    if (d_last < 0) {
-	//contour_add_point (inner, outpt);
-	return;
+    if (d_p != d_last) { /* parallel edge */
+	dot = (line_width - d_last) / (d_p - d_last);
+	last.x += dot * (p->x - last.x);
+	last.y += dot * (p->y - last.y);
     }
-
-    dot = (line_width - d_last) / (d_p - d_last);
-    last.x += dot * (p->x - last.x);
-    last.y += dot * (p->y - last.y);
-    contour_add_point (inner, &last);
+    contour_add_point (stroker, inner, &last);
 }
 
 static void
@@ -284,11 +325,10 @@ inner_close (struct stroker *stroker,
 	     cairo_stroke_face_t *out)
 {
     cairo_point_t last;
-    const cairo_point_t *p, *outpt;
+    const cairo_point_t *p, *outpt, *inpt;
     struct stroke_contour *inner;
     struct _cairo_contour_chain *chain;
     double d_p, d_last = -1;
-    double dot;
     double line_width;
     double sign;
     int i;
@@ -298,39 +338,48 @@ inner_close (struct stroker *stroker,
     if (join_is_clockwise (in, out)) {
 	inner = &stroker->ccw;
 	outpt = &in->ccw;
+	inpt = &out->ccw;
 	sign = -1;
     } else {
-	outpt = &in->cw;
 	inner = &stroker->cw;
+	outpt = &in->cw;
+	inpt = &out->cw;
 	sign = 1;
     }
 
     if (inner->contour.chain.num_points == 0) {
-	contour_add_point (inner, outpt);
+	contour_add_point (stroker, inner, outpt);
 	return;
     }
 
     line_width = stroker->style.line_width/2;
     line_width *= CAIRO_FIXED_ONE;
 
+    d_last = sign * distance_from_face (out, outpt);
+    last = *outpt;
+
     for (chain = &inner->contour.chain; chain; chain = chain->next) {
 	for (i = 0; i < chain->num_points; i++) {
 	    p = &chain->points[i];
-	    if ((d_p = sign * distance_from_face (in, p)) >= line_width)
+	    if ((d_p = sign * distance_from_face (in, p)) >= line_width &&
+		distance_from_edge (stroker, inpt, &last, p) < line_width)
 	    {
-		printf ("point [%d] =%f, last_dp=%f\n", i,d_p, d_last);
 		goto out;
 	    }
 
-	    last = *p;
-	    d_last = d_p;
+	    if (p->x != last.x || p->y != last.y) {
+		last = *p;
+		d_last = d_p;
+	    }
 	}
     }
 out:
 
-    dot = (line_width - d_last) / (d_p - d_last);
-    last.x += dot * (p->x - last.x);
-    last.y += dot * (p->y - last.y);
+    if (d_p != d_last) {
+	double dot = (line_width - d_last) / (d_p - d_last);
+	last.x += dot * (p->x - last.x);
+	last.y += dot * (p->y - last.y);
+    }
     *_cairo_contour_last_point (&inner->contour) = last;
 
     for (chain = &inner->contour.chain; chain; chain = chain->next) {
@@ -368,7 +417,7 @@ outer_close (struct stroker *stroker,
 	outer = &stroker->ccw;
     }
 
-    if (close_enough (outer, inpt, outpt))
+    if (close_enough (inpt, outpt, stroker->contour_tolerance))
 	return;
 
     switch (stroker->style.line_join) {
@@ -530,24 +579,23 @@ outer_close (struct stroker *stroker,
     case CAIRO_LINE_JOIN_BEVEL:
 	break;
     }
-    contour_add_point (outer, outpt);
+    contour_add_point (stroker, outer, outpt);
 }
 
 static void
-join (struct stroker *stroker,
-      const cairo_stroke_face_t *in,
-      const cairo_stroke_face_t *out)
+outer_join (struct stroker *stroker,
+	    const cairo_stroke_face_t *in,
+	    const cairo_stroke_face_t *out,
+	    int clockwise)
 {
     const cairo_point_t	*inpt, *outpt;
     struct stroke_contour *outer;
-    int	clockwise;
 
     if (in->cw.x == out->cw.x && in->cw.y == out->cw.y &&
 	in->ccw.x == out->ccw.x && out->ccw.y == out->ccw.y)
     {
 	return;
     }
-    clockwise = join_is_clockwise (in, out);
     if (clockwise) {
 	inpt = &in->cw;
 	outpt = &out->cw;
@@ -558,7 +606,7 @@ join (struct stroker *stroker,
 	outer = &stroker->ccw;
     }
 
-    if (close_enough (outer, inpt, outpt))
+    if (close_enough (inpt, outpt, stroker->contour_tolerance))
 	return;
 
     switch (stroker->style.line_join) {
@@ -721,7 +769,7 @@ join (struct stroker *stroker,
     case CAIRO_LINE_JOIN_BEVEL:
 	break;
     }
-    contour_add_point (outer, outpt);
+    contour_add_point (stroker,outer, outpt);
 }
 
 static void
@@ -762,15 +810,15 @@ add_cap (struct stroker *stroker,
 	quad[2].y = f->cw.y + fvector.dy;
 	quad[3] = f->cw;
 
-	contour_add_point (c, &quad[1]);
-	contour_add_point (c, &quad[2]);
+	contour_add_point (stroker, c, &quad[1]);
+	contour_add_point (stroker, c, &quad[2]);
     }
 
     case CAIRO_LINE_CAP_BUTT:
     default:
 	break;
     }
-    contour_add_point (c, &f->cw);
+    contour_add_point (stroker, c, &f->cw);
 }
 
 static void
@@ -788,6 +836,7 @@ add_leading_cap (struct stroker *stroker,
     reversed.usr_vector.y = -reversed.usr_vector.y;
     reversed.dev_vector.dx = -reversed.dev_vector.dx;
     reversed.dev_vector.dy = -reversed.dev_vector.dy;
+
     t = reversed.cw;
     reversed.cw = reversed.ccw;
     reversed.ccw = t;
@@ -837,8 +886,10 @@ normalize_slope (double *dx, double *dy)
 }
 
 static void
-compute_face (const cairo_point_t *point, cairo_slope_t *dev_slope,
-	      struct stroker *stroker, cairo_stroke_face_t *face)
+compute_face (const cairo_point_t *point,
+	      const cairo_slope_t *dev_slope,
+	      struct stroker *stroker,
+	      cairo_stroke_face_t *face)
 {
     double face_dx, face_dy;
     cairo_point_t offset_ccw, offset_cw;
@@ -913,8 +964,7 @@ add_caps (struct stroker *stroker)
 	cairo_slope_t slope = { CAIRO_FIXED_ONE, 0 };
 	cairo_stroke_face_t face;
 
-	/* arbitrarily choose first_point
-	 * first_point and current_point should be the same */
+	/* arbitrarily choose first_point */
 	compute_face (&stroker->first_point, &slope, stroker, &face);
 
 	add_leading_cap (stroker, &face, &stroker->ccw);
@@ -937,8 +987,6 @@ add_caps (struct stroker *stroker)
 	if (stroker->has_current_face)
 	    add_trailing_cap (stroker, &stroker->current_face, &stroker->ccw);
 
-	//_cairo_contour_simplify (&stroker->ccw.contour, stroker->tolerance);
-
 	_cairo_polygon_add_contour (stroker->polygon, &stroker->ccw.contour);
 	_cairo_contour_reset (&stroker->ccw.contour);
 
@@ -948,13 +996,15 @@ add_caps (struct stroker *stroker)
 	_cairo_polygon_add_contour (stroker->polygon, &stroker->ccw.contour);
 	_cairo_contour_reset (&stroker->ccw.contour);
 
-	//_cairo_contour_simplify (&stroker->cw.contour, stroker->tolerance);
 	_cairo_polygon_add_contour (stroker->polygon, &stroker->cw.contour);
 	_cairo_contour_reset (&stroker->cw.contour);
     }
 }
 
 static cairo_status_t
+close_path (void *closure);
+
+static cairo_status_t
 move_to (void *closure,
 	 const cairo_point_t *point)
 {
@@ -963,16 +1013,17 @@ move_to (void *closure,
     /* Cap the start and end of the previous sub path as needed */
     add_caps (stroker);
 
+    stroker->has_first_face = FALSE;
+    stroker->has_current_face = FALSE;
+    stroker->has_initial_sub_path = FALSE;
+
     stroker->first_point = *point;
-    stroker->current_point = *point;
 
 #if DEBUG
     _cairo_contour_add_point (&stroker->path, point);
 #endif
 
-    stroker->has_first_face = FALSE;
-    stroker->has_current_face = FALSE;
-    stroker->has_initial_sub_path = FALSE;
+    stroker->current_face.point = *point;
 
     return CAIRO_STATUS_SUCCESS;
 }
@@ -983,7 +1034,7 @@ line_to (void *closure,
 {
     struct stroker *stroker = closure;
     cairo_stroke_face_t start;
-    cairo_point_t *p1 = &stroker->current_point;
+    cairo_point_t *p1 = &stroker->current_face.point;
     cairo_slope_t dev_slope;
 
     stroker->has_initial_sub_path = TRUE;
@@ -999,9 +1050,16 @@ line_to (void *closure,
     compute_face (p1, &dev_slope, stroker, &start);
 
     if (stroker->has_current_face) {
+	int clockwise = join_is_clockwise (&stroker->current_face, &start);
 	/* Join with final face from previous segment */
-	join (stroker, &stroker->current_face, &start);
-	inner_join (stroker, &stroker->current_face, &start);
+	if (! close_enough (&stroker->current_face.ccw, &start.ccw,
+			  stroker->contour_tolerance) ||
+	    ! close_enough (&stroker->current_face.cw, &start.cw,
+			    stroker->contour_tolerance))
+	{
+	    outer_join (stroker, &stroker->current_face, &start, clockwise);
+	    inner_join (stroker, &stroker->current_face, &start, clockwise);
+	}
     } else {
 	if (! stroker->has_first_face) {
 	    /* Save sub path's first face in case needed for closing join */
@@ -1010,8 +1068,8 @@ line_to (void *closure,
 	}
 	stroker->has_current_face = TRUE;
 
-	contour_add_point (&stroker->cw, &start.cw);
-	contour_add_point (&stroker->ccw, &start.ccw);
+	contour_add_point (stroker, &stroker->cw, &start.cw);
+	contour_add_point (stroker, &stroker->ccw, &start.ccw);
     }
 
     stroker->current_face = start;
@@ -1021,36 +1079,52 @@ line_to (void *closure,
     stroker->current_face.cw.x += dev_slope.dx;
     stroker->current_face.cw.y += dev_slope.dy;
 
-    contour_add_point (&stroker->cw, &stroker->current_face.cw);
-    contour_add_point (&stroker->ccw, &stroker->current_face.ccw);
-
-    stroker->current_point = *point;
+    contour_add_point (stroker, &stroker->cw, &stroker->current_face.cw);
+    contour_add_point (stroker, &stroker->ccw, &stroker->current_face.ccw);
 
     return CAIRO_STATUS_SUCCESS;
 }
 
 static cairo_status_t
 spline_to (void *closure,
-	 const cairo_point_t *point)
+	   const cairo_point_t *point,
+	   const cairo_slope_t *tangent)
 {
     struct stroker *stroker = closure;
-    cairo_slope_t dev_slope;
-
-    if (stroker->current_point.x == point->x &&
-	stroker->current_point.y == point->y)
-	return CAIRO_STATUS_SUCCESS;
+    cairo_stroke_face_t face;
 
 #if DEBUG
     _cairo_contour_add_point (&stroker->path, point);
 #endif
 
-    _cairo_slope_init (&dev_slope, &stroker->current_point, point);
-    compute_face (point, &dev_slope, stroker, &stroker->current_face);
+    compute_face (point, tangent, stroker, &face);
 
-    contour_add_point (&stroker->cw, &stroker->current_face.cw);
-    contour_add_point (&stroker->ccw, &stroker->current_face.ccw);
+    if (1) {
+	const cairo_point_t *inpt, *outpt;
+	struct stroke_contour *outer;
+	int clockwise;
 
-    stroker->current_point = *point;
+	clockwise = join_is_clockwise (&stroker->current_face, &face);
+	if (clockwise) {
+	    inpt = &stroker->current_face.cw;
+	    outpt = &face.cw;
+	    outer = &stroker->cw;
+	} else {
+	    inpt = &stroker->current_face.ccw;
+	    outpt = &face.ccw;
+	    outer = &stroker->ccw;
+	}
+
+	add_fan (stroker,
+		 &stroker->current_face.dev_vector,
+		 &face.dev_vector,
+		 &stroker->current_face.point, inpt, outpt,
+		 clockwise, outer);
+    }
+
+    stroker->current_face = face;
+    contour_add_point (stroker, &stroker->cw, &face.cw);
+    contour_add_point (stroker, &stroker->ccw, &face.ccw);
 
     return CAIRO_STATUS_SUCCESS;
 }
@@ -1064,49 +1138,35 @@ curve_to (void *closure,
     struct stroker *stroker = closure;
     cairo_spline_t spline;
     cairo_stroke_face_t face;
-    cairo_status_t status = CAIRO_STATUS_SUCCESS;
 
     if (! _cairo_spline_init (&spline, spline_to, stroker,
-			      &stroker->current_point, b, c, d))
+			      &stroker->current_face.point, b, c, d))
     {
 	return line_to (closure, d);
     }
 
-    /* Compute the initial face */
-    compute_face (&stroker->current_point,
-		  &spline.initial_slope,
+    compute_face (&stroker->current_face.point, &spline.initial_slope,
 		  stroker, &face);
 
     if (stroker->has_current_face) {
-	join (stroker, &stroker->current_face, &face);
-	inner_join (stroker, &stroker->current_face, &face);
+	int clockwise = join_is_clockwise (&stroker->current_face, &face);
+	/* Join with final face from previous segment */
+	outer_join (stroker, &stroker->current_face, &face, clockwise);
+	inner_join (stroker, &stroker->current_face, &face, clockwise);
     } else {
-	contour_add_point (&stroker->cw, &face.cw);
-	contour_add_point (&stroker->ccw, &face.ccw);
-
 	if (! stroker->has_first_face) {
 	    /* Save sub path's first face in case needed for closing join */
 	    stroker->first_face = face;
 	    stroker->has_first_face = TRUE;
 	}
 	stroker->has_current_face = TRUE;
-    }
-    stroker->current_face = face;
-
-    /* Temporarily modify the stroker to use round joins to guarantee
-     * smooth stroked curves. */
-    status = _cairo_spline_decompose (&spline, stroker->tolerance);
-    assert (status == CAIRO_STATUS_SUCCESS);
-
-    /* Tweak the final point to lie on the out-face */
-    compute_face (d, &spline.final_slope, stroker, &face);
-
-    *_cairo_contour_last_point (&stroker->cw.contour) = face.cw;
-    *_cairo_contour_last_point (&stroker->ccw.contour) = face.ccw;
 
+	contour_add_point (stroker, &stroker->cw, &face.cw);
+	contour_add_point (stroker, &stroker->ccw, &face.ccw);
+    }
     stroker->current_face = face;
 
-    return CAIRO_STATUS_SUCCESS;
+    return _cairo_spline_decompose (&spline, stroker->tolerance);
 }
 
 static cairo_status_t
@@ -1209,8 +1269,7 @@ _cairo_path_fixed_stroke_to_polygon (const cairo_path_fixed_t	*path,
     _cairo_contour_init (&stroker.ccw.contour, -1);
     tolerance *= 1 << CAIRO_FIXED_FRAC_BITS;
     tolerance *= tolerance;
-    stroker.cw.tolerance = tolerance;
-    stroker.ccw.tolerance = tolerance;
+    stroker.contour_tolerance = tolerance;
     stroker.polygon = polygon;
 
     status = _cairo_path_fixed_interpret (path,
diff --git a/src/cairo-path-stroke.c b/src/cairo-path-stroke.c
index fcbc8e1..56eb97d 100644
--- a/src/cairo-path-stroke.c
+++ b/src/cairo-path-stroke.c
@@ -1118,7 +1118,7 @@ _cairo_stroker_curve_to (void *closure,
 	_cairo_stroker_line_to;
 
     if (! _cairo_spline_init (&spline,
-			      line_to, stroker,
+			      (cairo_spline_add_point_func_t)line_to, stroker,
 			      &stroker->current_point, b, c, d))
     {
 	return line_to (closure, d);
diff --git a/src/cairo-slope.c b/src/cairo-slope.c
index 827037f..73b79eb 100644
--- a/src/cairo-slope.c
+++ b/src/cairo-slope.c
@@ -59,10 +59,14 @@
 int
 _cairo_slope_compare (const cairo_slope_t *a, const cairo_slope_t *b)
 {
-    cairo_int64_t ady_bdx = _cairo_int32x32_64_mul (a->dy, b->dx);
-    cairo_int64_t bdy_adx = _cairo_int32x32_64_mul (b->dy, a->dx);
+    cairo_int64_t ady_bdx, bdy_adx;
     int cmp;
 
+    if (a->dx == b->dx && a->dy == b->dy)
+	return 0;
+
+    ady_bdx = _cairo_int32x32_64_mul (a->dy, b->dx);
+    bdy_adx = _cairo_int32x32_64_mul (b->dy, a->dx);
     cmp = _cairo_int64_cmp (ady_bdx, bdy_adx);
     if (cmp)
 	return cmp;
@@ -71,11 +75,9 @@ _cairo_slope_compare (const cairo_slope_t *a, const cairo_slope_t *b)
      * zero vectors all compare equal, and more positive than any
      * non-zero vector.
      */
-    if (a->dx == 0 && a->dy == 0 && b->dx == 0 && b->dy ==0)
-	return 0;
     if (a->dx == 0 && a->dy == 0)
-	return 1;
-    if (b->dx == 0 && b->dy ==0)
+	return +1;
+    if (b->dx == 0 && b->dy == 0)
 	return -1;
 
     /* Finally, we're looking at two vectors that are either equal or
@@ -87,13 +89,8 @@ _cairo_slope_compare (const cairo_slope_t *a, const cairo_slope_t *b)
      * of b by an infinitesimally small amount, (that is, 'a' will
      * always be considered less than 'b').
      */
-    if ((a->dx ^ b->dx) < 0 || (a->dy ^ b->dy) < 0) {
-	if (a->dx > 0 || (a->dx == 0 && a->dy > 0))
-	    return +1;
-	else
-	    return -1;
-    }
-
-    /* Finally, for identical slopes, we obviously return 0. */
-    return 0;
+    if (a->dx > 0 || (a->dx == 0 && a->dy > 0))
+	return +1;
+    else
+	return -1;
 }
diff --git a/src/cairo-spline.c b/src/cairo-spline.c
index 016ecee..bf0619a 100644
--- a/src/cairo-spline.c
+++ b/src/cairo-spline.c
@@ -73,16 +73,21 @@ _cairo_spline_init (cairo_spline_t *spline,
 }
 
 static cairo_status_t
-_cairo_spline_add_point (cairo_spline_t *spline, cairo_point_t *point)
+_cairo_spline_add_point (cairo_spline_t *spline,
+			 const cairo_point_t *point,
+			 const cairo_point_t *knot)
 {
     cairo_point_t *prev;
+    cairo_slope_t slope;
 
     prev = &spline->last_point;
     if (prev->x == point->x && prev->y == point->y)
 	return CAIRO_STATUS_SUCCESS;
 
+    _cairo_slope_init (&slope, point, knot);
+
     spline->last_point = *point;
-    return spline->add_point_func (spline->closure, point);
+    return spline->add_point_func (spline->closure, point, &slope);
 }
 
 static void
@@ -184,13 +189,15 @@ _cairo_spline_error_squared (const cairo_spline_knots_t *knots)
 }
 
 static cairo_status_t
-_cairo_spline_decompose_into (cairo_spline_knots_t *s1, double tolerance_squared, cairo_spline_t *result)
+_cairo_spline_decompose_into (cairo_spline_knots_t *s1,
+			      double tolerance_squared,
+			      cairo_spline_t *result)
 {
     cairo_spline_knots_t s2;
     cairo_status_t status;
 
     if (_cairo_spline_error_squared (s1) < tolerance_squared)
-	return _cairo_spline_add_point (result, &s1->a);
+	return _cairo_spline_add_point (result, &s1->a, &s1->b);
 
     _de_casteljau (s1, &s2);
 
@@ -206,6 +213,7 @@ _cairo_spline_decompose (cairo_spline_t *spline, double tolerance)
 {
     cairo_spline_knots_t s1;
     cairo_status_t status;
+    cairo_slope_t slope;
 
     s1 = spline->knots;
     spline->last_point = s1.a;
@@ -213,7 +221,8 @@ _cairo_spline_decompose (cairo_spline_t *spline, double tolerance)
     if (unlikely (status))
 	return status;
 
-    return _cairo_spline_add_point (spline, &spline->knots.d);
+    _cairo_slope_init (&slope, &spline->knots.c, &spline->knots.d);
+    return spline->add_point_func (spline->closure, &spline->knots.d, &slope);
 }
 
 /* Note: this function is only good for computing bounds in device space. */
@@ -325,7 +334,7 @@ _cairo_spline_bound (cairo_spline_add_point_func_t add_point_func,
     c = -y0 + y1;
     FIND_EXTREMES (a, b, c);
 
-    status = add_point_func (closure, p0);
+    status = add_point_func (closure, p0, NULL);
     if (unlikely (status))
 	return status;
 
@@ -359,10 +368,10 @@ _cairo_spline_bound (cairo_spline_add_point_func_t add_point_func,
 
 	p.x = _cairo_fixed_from_double (x);
 	p.y = _cairo_fixed_from_double (y);
-	status = add_point_func (closure, &p);
+	status = add_point_func (closure, &p, NULL);
 	if (unlikely (status))
 	    return status;
     }
 
-    return add_point_func (closure, p3);
+    return add_point_func (closure, p3, NULL);
 }
diff --git a/src/cairo-types-private.h b/src/cairo-types-private.h
index f7a1f26..a1d4863 100644
--- a/src/cairo-types-private.h
+++ b/src/cairo-types-private.h
@@ -307,7 +307,8 @@ typedef struct _cairo_polygon {
 
 typedef cairo_warn cairo_status_t
 (*cairo_spline_add_point_func_t) (void *closure,
-				  const cairo_point_t *point);
+				  const cairo_point_t *point,
+				  const cairo_slope_t *tangent);
 
 typedef struct _cairo_spline_knots {
     cairo_point_t a, b, c, d;
diff --git a/util/show-contour.c b/util/show-contour.c
index 1e549cb..b9adfbc 100644
--- a/util/show-contour.c
+++ b/util/show-contour.c
@@ -228,6 +228,14 @@ trap_view_draw (TrapView *self, cairo_t *cr)
 			    cairo_set_source_rgb (cr, 1.0, 0.0, 0.0);
 			    break;
 			}
+			{
+			    const point_t *p = &contour->points[0];
+			    cairo_arc (cr, p->x, p->y, 4/zoom, 0, 2 * M_PI);
+			    cairo_save (cr);
+			    cairo_identity_matrix (cr);
+			    cairo_stroke (cr);
+			    cairo_restore (cr);
+			}
 			for (n = 0; n < contour->num_points; n++) {
 			    const point_t *p = &contour->points[n];
 			    cairo_arc (cr, p->x, p->y, 2/zoom, 0, 2 * M_PI);
commit 75175dea5f1c1e13559d03bc99a8572e5674d8a2
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Fri Aug 5 09:36:00 2011 +0100

    stroke: Experiment with vertex reduction

diff --git a/src/Makefile.sources b/src/Makefile.sources
index a1ff678..8d6480e 100644
--- a/src/Makefile.sources
+++ b/src/Makefile.sources
@@ -63,6 +63,7 @@ cairo_private = \
 	cairo-combsort-private.h \
 	cairo-compiler-private.h \
 	cairo-composite-rectangles-private.h \
+	cairo-contour-private.h \
 	cairo-default-context-private.h \
 	cairo-device-private.h \
 	cairo-error-private.h \
@@ -130,6 +131,7 @@ cairo_sources = \
 	cairo-clip-region.c \
 	cairo-clip-surface.c \
 	cairo-color.c \
+	cairo-contour.c \
 	cairo-composite-rectangles.c \
 	cairo-debug.c \
 	cairo-default-context.c \
diff --git a/src/cairo-contour-private.h b/src/cairo-contour-private.h
new file mode 100644
index 0000000..ef9ad9d
--- /dev/null
+++ b/src/cairo-contour-private.h
@@ -0,0 +1,150 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2011 Intel Corporation
+ *
+ * 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 Intel Corporation
+ *
+ * Contributor(s):
+ *	Chris Wilson <chris at chris-wilson.co.uk>
+ */
+
+#ifndef CAIRO_CONTOUR_PRIVATE_H
+#define CAIRO_CONTOUR_PRIVATE_H
+
+#include "cairo-types-private.h"
+#include "cairo-compiler-private.h"
+#include "cairo-list-private.h"
+
+#include <stdio.h>
+
+CAIRO_BEGIN_DECLS
+
+/* A contour is simply a closed chain of points that divide the infinite plane
+ * into inside and outside. Each contour is a simple polygon, that is it
+ * contains no holes, but maybe either concave or convex.
+ */
+
+struct _cairo_contour {
+    int direction;
+    struct _cairo_contour_chain {
+	cairo_point_t *points;
+	int num_points, size_points;
+	struct _cairo_contour_chain *next;
+    } chain, *tail;
+    cairo_point_t embedded_points[64];
+    cairo_list_t next;
+};
+
+/* Initial definition of a shape is a set of contours (some representing holes) */
+struct _cairo_shape {
+    cairo_list_t contours;
+};
+
+typedef struct _cairo_shape cairo_shape_t;
+
+#if 0
+cairo_private cairo_status_t
+_cairo_shape_init_from_polygon (cairo_shape_t *shape,
+				const cairo_polygon_t *polygon);
+
+cairo_private cairo_status_t
+_cairo_shape_reduce (cairo_shape_t *shape, double tolerance);
+#endif
+
+cairo_private void
+_cairo_contour_init (cairo_contour_t *contour,
+		     int direction);
+
+cairo_private cairo_int_status_t
+__cairo_contour_add_point (cairo_contour_t *contour,
+			   const cairo_point_t *point);
+
+static inline cairo_int_status_t
+_cairo_contour_add_point (cairo_contour_t *contour,
+			  const cairo_point_t *point)
+{
+    struct _cairo_contour_chain *tail = contour->tail;
+
+    if (unlikely (tail->num_points == tail->size_points))
+	return __cairo_contour_add_point (contour, point);
+
+    tail->points[tail->num_points++] = *point;
+    return CAIRO_INT_STATUS_SUCCESS;
+}
+
+static inline cairo_point_t *
+_cairo_contour_first_point (cairo_contour_t *c)
+{
+    return &c->chain.points[0];
+}
+
+static inline cairo_point_t *
+_cairo_contour_last_point (cairo_contour_t *c)
+{
+    return &c->tail->points[c->tail->num_points-1];
+}
+
+cairo_private void
+_cairo_contour_simplify (cairo_contour_t *contour, double tolerance);
+
+cairo_private void
+_cairo_contour_reverse (cairo_contour_t *contour);
+
+cairo_private cairo_int_status_t
+_cairo_contour_add (cairo_contour_t *dst,
+		    const cairo_contour_t *src);
+
+cairo_private cairo_int_status_t
+_cairo_contour_add_reversed (cairo_contour_t *dst,
+			     const cairo_contour_t *src);
+
+cairo_private void
+__cairo_contour_remove_last_chain (cairo_contour_t *contour);
+
+static inline void
+_cairo_contour_remove_last_point (cairo_contour_t *contour)
+{
+    if (contour->chain.num_points == 0)
+	return;
+
+    if (--contour->tail->num_points == 0)
+	__cairo_contour_remove_last_chain (contour);
+}
+
+cairo_private void
+_cairo_contour_reset (cairo_contour_t *contour);
+
+cairo_private void
+_cairo_contour_fini (cairo_contour_t *contour);
+
+cairo_private void
+_cairo_debug_print_contour (FILE *file, cairo_contour_t *contour);
+
+CAIRO_END_DECLS
+
+#endif /* CAIRO_CONTOUR_PRIVATE_H */
diff --git a/src/cairo-contour.c b/src/cairo-contour.c
new file mode 100644
index 0000000..aa3ad6d
--- /dev/null
+++ b/src/cairo-contour.c
@@ -0,0 +1,460 @@
+/*
+ * Copyright © 2004 Carl Worth
+ * Copyright © 2006 Red Hat, Inc.
+ * Copyright © 2008 Chris Wilson
+ * Copyright © 2011 Intel Corporation
+ *
+ * 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 Carl Worth
+ *
+ * Contributor(s):
+ *	Carl D. Worth <cworth at cworth.org>
+ *	Chris Wilson <chris at chris-wilson.co.uk>
+ */
+
+/* Provide definitions for standalone compilation */
+#include "cairoint.h"
+
+#include "cairo-error-private.h"
+#include "cairo-freelist-private.h"
+#include "cairo-combsort-private.h"
+#include "cairo-contour-private.h"
+
+void
+_cairo_contour_init (cairo_contour_t *contour,
+		     int direction)
+{
+    contour->direction = direction;
+    contour->chain.points = contour->embedded_points;
+    contour->chain.next = NULL;
+    contour->chain.num_points = 0;
+    contour->chain.size_points = ARRAY_LENGTH (contour->embedded_points);
+    contour->tail = &contour->chain;
+}
+
+cairo_int_status_t
+__cairo_contour_add_point (cairo_contour_t *contour,
+			  const cairo_point_t *point)
+{
+    struct _cairo_contour_chain *tail = contour->tail;
+    struct _cairo_contour_chain *next;
+
+    assert (tail->next == NULL);
+
+    next = _cairo_malloc_ab_plus_c (tail->size_points*2,
+				    sizeof (cairo_point_t),
+				    sizeof (struct _cairo_contour_chain));
+    if (unlikely (next == NULL))
+	return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    next->size_points = tail->size_points*2;
+    next->num_points = 1;
+    next->points = (cairo_point_t *)(next+1);
+    next->next = NULL;
+    tail->next = next;
+    contour->tail = next;
+
+    next->points[0] = *point;
+    return CAIRO_INT_STATUS_SUCCESS;
+}
+
+static void
+first_inc (cairo_contour_t *contour,
+	   cairo_point_t **p,
+	   struct _cairo_contour_chain **chain)
+{
+    if (*p == (*chain)->points + (*chain)->num_points) {
+	assert ((*chain)->next);
+	*chain = (*chain)->next;
+	*p = &(*chain)->points[0];
+    } else
+	++*p;
+}
+
+static void
+last_dec (cairo_contour_t *contour,
+	  cairo_point_t **p,
+	  struct _cairo_contour_chain **chain)
+{
+    if (*p == (*chain)->points) {
+	struct _cairo_contour_chain *prev;
+	assert (*chain != &contour->chain);
+	for (prev = &contour->chain; prev->next != *chain; prev = prev->next)
+	    ;
+	*chain = prev;
+	*p = &(*chain)->points[(*chain)->num_points-1];
+    } else
+	--*p;
+}
+
+void
+_cairo_contour_reverse (cairo_contour_t *contour)
+{
+    struct _cairo_contour_chain *first_chain, *last_chain;
+    cairo_point_t *first, *last;
+
+    contour->direction = -contour->direction;
+
+    if (contour->chain.num_points <= 1)
+	return;
+
+    first_chain = &contour->chain;
+    last_chain = contour->tail;
+
+    first = &first_chain->points[0];
+    last = &last_chain->points[last_chain->num_points-1];
+
+    while (first != last) {
+	cairo_point_t p;
+
+	p = *first;
+	*first = *last;
+	*last = p;
+
+	first_inc (contour, &first, &first_chain);
+	last_dec (contour, &last, &last_chain);
+    }
+}
+
+cairo_int_status_t
+_cairo_contour_add (cairo_contour_t *dst,
+		    const cairo_contour_t *src)
+{
+    const struct _cairo_contour_chain *chain;
+    cairo_int_status_t status;
+    int i;
+
+    for (chain = &src->chain; chain; chain = chain->next) {
+	for (i = 0; i < chain->num_points; i++) {
+	    status = _cairo_contour_add_point (dst, &chain->points[i]);
+	    if (unlikely (status))
+		return status;
+	}
+    }
+
+    return CAIRO_INT_STATUS_SUCCESS;
+}
+
+struct _cairo_contour_iter {
+    struct _cairo_contour_chain *chain;
+    cairo_point_t *point;
+};
+
+static inline cairo_bool_t
+iter_next (struct _cairo_contour_iter *iter)
+{
+    if (iter->point == &iter->chain->points[iter->chain->size_points-1]) {
+	iter->chain = iter->chain->next;
+	if (iter->chain == NULL)
+	    return FALSE;
+
+	iter->point = &iter->chain->points[0];
+	return TRUE;
+    } else {
+	iter->point++;
+	return TRUE;
+    }
+}
+
+static cairo_bool_t
+iter_equal (const struct _cairo_contour_iter *i1,
+	    const struct _cairo_contour_iter *i2)
+{
+    return i1->chain == i2->chain && i1->point == i2->point;
+}
+
+static void
+iter_init (struct _cairo_contour_iter *iter, cairo_contour_t *contour)
+{
+    iter->chain = &contour->chain;
+    iter->point = &contour->chain.points[0];
+}
+
+static void
+iter_init_last (struct _cairo_contour_iter *iter, cairo_contour_t *contour)
+{
+    iter->chain = contour->tail;
+    iter->point = &contour->tail->points[contour->tail->num_points-1];
+}
+
+static const struct _cairo_contour_chain *prev_const_chain(const cairo_contour_t *contour,
+							   const struct _cairo_contour_chain *chain)
+{
+    const struct _cairo_contour_chain *prev;
+
+    if (chain == &contour->chain)
+	return NULL;
+
+    for (prev = &contour->chain; prev->next != chain; prev = prev->next)
+	;
+
+    return prev;
+}
+
+cairo_int_status_t
+_cairo_contour_add_reversed (cairo_contour_t *dst,
+			     const cairo_contour_t *src)
+{
+    const struct _cairo_contour_chain *last;
+    cairo_int_status_t status;
+    int i;
+
+    if (src->chain.num_points == 0)
+	return CAIRO_INT_STATUS_SUCCESS;
+
+    for (last = src->tail; last; last = prev_const_chain (src, last)) {
+	for (i = last->num_points-1; i >= 0; i--) {
+	    status = _cairo_contour_add_point (dst, &last->points[i]);
+	    if (unlikely (status))
+		return status;
+	}
+    }
+
+    return CAIRO_INT_STATUS_SUCCESS;
+}
+
+
+static uint64_t
+_cairo_point_distance2 (const cairo_point_t *p1, const cairo_point_t *p2)
+{
+    int dx = p1->x - p2->x;
+    int dy = p1->y - p2->y;
+    return dx * dx + dy * dy;
+}
+
+#define DELETED(p) ((p)->x == INT_MIN && (p)->y == INT_MAX)
+#define MARK_DELETED(p) ((p)->x = INT_MIN, (p)->y = INT_MAX)
+
+static cairo_bool_t
+_cairo_contour_simplify_chain (cairo_contour_t *contour, const double tolerance,
+			       const struct _cairo_contour_iter *first,
+			       const struct _cairo_contour_iter *last)
+{
+    struct _cairo_contour_iter iter, furthest;
+    uint64_t max_error;
+    int x0, y0;
+    int nx, ny;
+    int count;
+
+    iter = *first;
+    iter_next (&iter);
+    if (iter_equal (&iter, last))
+	return FALSE;
+
+    x0 = first->point->x;
+    y0 = first->point->y;
+    nx = last->point->y - y0;
+    ny = x0 - last->point->x;
+
+    count = 0;
+    max_error = 0;
+    do {
+	cairo_point_t *p = iter.point;
+	if (! DELETED(p)) {
+	    uint64_t d = (uint64_t)nx * (x0 - p->x) + (uint64_t)ny * (y0 - p->y);
+	    if (d * d > max_error) {
+		max_error = d * d;
+		furthest = iter;
+	    }
+	    count++;
+	}
+	iter_next (&iter);
+    } while (! iter_equal (&iter, last));
+    if (count == 0)
+	return FALSE;
+
+    if (max_error > tolerance * ((uint64_t)nx * nx + (uint64_t)ny * ny)) {
+	cairo_bool_t simplified;
+
+	simplified = FALSE;
+	simplified |= _cairo_contour_simplify_chain (contour, tolerance,
+						     first, &furthest);
+	simplified |= _cairo_contour_simplify_chain (contour, tolerance,
+						     &furthest, last);
+	return simplified;
+    } else {
+	iter = *first;
+	iter_next (&iter);
+	do {
+	    MARK_DELETED (iter.point);
+	    iter_next (&iter);
+	} while (! iter_equal (&iter, last));
+
+	return TRUE;
+    }
+}
+
+void
+_cairo_contour_simplify (cairo_contour_t *contour, double tolerance)
+{
+    struct _cairo_contour_chain *chain;
+    cairo_point_t *last = NULL;
+    struct _cairo_contour_iter iter, furthest;
+    cairo_bool_t simplified;
+    uint64_t max = 0;
+    int i;
+
+    if (contour->chain.num_points <= 2)
+	return;
+
+    tolerance = tolerance * (1 << CAIRO_FIXED_FRAC_BITS);
+    tolerance *= tolerance;
+
+    /* stage 1: vertex reduction */
+    for (chain = &contour->chain; chain; chain = chain->next) {
+	for (i = 0; i < chain->num_points; i++) {
+	    if (last == NULL ||
+		_cairo_point_distance2 (last,
+					&chain->points[i]) > tolerance) {
+		last = &chain->points[i];
+	    } else {
+		MARK_DELETED (&chain->points[i]);
+	    }
+	}
+    }
+
+    /* stage2: polygon simplification using Douglas-Peucker */
+    simplified = FALSE;
+    do {
+	last = &contour->chain.points[0];
+	iter_init (&furthest, contour);
+	max = 0;
+	for (chain = &contour->chain; chain; chain = chain->next) {
+	    for (i = 0; i < chain->num_points; i++) {
+		uint64_t d;
+
+		if (DELETED (&chain->points[i]))
+		    continue;
+
+		d = _cairo_point_distance2 (last, &chain->points[i]);
+		if (d > max) {
+		    furthest.chain = chain;
+		    furthest.point = &chain->points[i];
+		    max = d;
+		}
+	    }
+	}
+	assert (max);
+
+	simplified = FALSE;
+	iter_init (&iter, contour);
+	simplified |= _cairo_contour_simplify_chain (contour, tolerance,
+						     &iter, &furthest);
+
+	iter_init_last (&iter, contour);
+	if (! iter_equal (&furthest, &iter))
+	    simplified |= _cairo_contour_simplify_chain (contour, tolerance,
+							 &furthest, &iter);
+    } while (simplified);
+
+    iter_init (&iter, contour);
+    for (chain = &contour->chain; chain; chain = chain->next) {
+	int num_points = chain->num_points;
+	chain->num_points = 0;
+	for (i = 0; i < num_points; i++) {
+	    if (! DELETED(&chain->points[i])) {
+		if (iter.point != &chain->points[i])
+		    *iter.point = chain->points[i];
+		iter.chain->num_points++;
+		iter_next (&iter);
+	    }
+	}
+    }
+
+    if (iter.chain) {
+	struct _cairo_contour_chain *next;
+
+	for (chain = iter.chain->next; chain; chain = next) {
+	    next = chain->next;
+	    free (chain);
+	}
+
+	iter.chain->next = NULL;
+	contour->tail = iter.chain;
+    }
+}
+
+void
+_cairo_contour_reset (cairo_contour_t *contour)
+{
+    _cairo_contour_fini (contour);
+    _cairo_contour_init (contour, contour->direction);
+}
+
+void
+_cairo_contour_fini (cairo_contour_t *contour)
+{
+    struct _cairo_contour_chain *chain, *next;
+
+    for (chain = contour->chain.next; chain; chain = next) {
+	next = chain->next;
+	free (chain);
+    }
+}
+
+void
+_cairo_debug_print_contour (FILE *file, cairo_contour_t *contour)
+{
+    struct _cairo_contour_chain *chain;
+    int num_points, size_points;
+    int i;
+
+    num_points = 0;
+    size_points = 0;
+    for (chain = &contour->chain; chain; chain = chain->next) {
+	num_points += chain->num_points;
+	size_points += chain->size_points;
+    }
+
+    fprintf (file, "contour: direction=%d, num_points=%d / %d\n",
+	     contour->direction, num_points, size_points);
+
+    num_points = 0;
+    for (chain = &contour->chain; chain; chain = chain->next) {
+	for (i = 0; i < chain->num_points; i++) {
+	    fprintf (file, "  [%d] = (%f, %f)\n",
+		     num_points++,
+		     _cairo_fixed_to_double (chain->points[i].x),
+		     _cairo_fixed_to_double (chain->points[i].y));
+	}
+    }
+}
+
+void
+__cairo_contour_remove_last_chain (cairo_contour_t *contour)
+{
+    struct _cairo_contour_chain *chain;
+
+    if (contour->tail == &contour->chain)
+	return;
+
+    for (chain = &contour->chain; chain->next != contour->tail; chain = chain->next)
+	;
+    free (contour->tail);
+    contour->tail = chain;
+    chain->next = NULL;
+}
diff --git a/src/cairo-path-stroke-polygon.c b/src/cairo-path-stroke-polygon.c
index d6cd82f..fed3dba 100644
--- a/src/cairo-path-stroke-polygon.c
+++ b/src/cairo-path-stroke-polygon.c
@@ -42,26 +42,33 @@
 
 #include "cairo-box-private.h"
 #include "cairo-boxes-private.h"
+#include "cairo-contour-private.h"
 #include "cairo-error-private.h"
 #include "cairo-path-fixed-private.h"
 #include "cairo-slope-private.h"
 
+#define DEBUG 1
+
 struct stroker {
     cairo_stroke_style_t style;
 
-    cairo_polygon_t *polygon;
+#if DEBUG
+    cairo_contour_t path;
+#endif
 
-    struct contour {
-	cairo_point_t first, current;
-    } inside, outside;
+    struct stroke_contour {
+	/* Note that these are not strictly contours as they may intersect */
+	cairo_contour_t contour;
+	cairo_uint64_t tolerance;
+    } cw, ccw;
+    cairo_polygon_t *polygon;
 
     const cairo_matrix_t *ctm;
     const cairo_matrix_t *ctm_inverse;
     double tolerance;
-    double ctm_determinant;
     cairo_bool_t ctm_det_positive;
 
-    cairo_pen_t	  pen;
+    cairo_pen_t pen;
 
     cairo_point_t current_point;
     cairo_point_t first_point;
@@ -75,33 +82,43 @@ struct stroker {
     cairo_stroke_face_t first_face;
 };
 
-static void
-translate_point (cairo_point_t *point, const cairo_point_t *offset)
+static inline void
+normalize_slope (double *dx, double *dy);
+
+static cairo_uint64_t
+_cairo_point_distance2 (const cairo_point_t *p1,
+			const cairo_point_t *p2)
 {
-    point->x += offset->x;
-    point->y += offset->y;
+    int32_t dx = p1->x - p2->x;
+    int32_t dy = p1->y - p2->y;
+    return _cairo_int32x32_64_mul (dx, dx) + _cairo_int32x32_64_mul (dy, dy);
 }
 
-static int
-join_is_clockwise (const cairo_stroke_face_t *in,
-		   const cairo_stroke_face_t *out)
+static cairo_bool_t
+close_enough (const struct stroke_contour *c,
+	      const cairo_point_t *p1,
+	      const cairo_point_t *p2)
 {
-    cairo_slope_t in_slope, out_slope;
+    return _cairo_int64_lt (_cairo_point_distance2 (p1, p2), c->tolerance);
+}
 
-    _cairo_slope_init (&in_slope, &in->point, &in->cw);
-    _cairo_slope_init (&out_slope, &out->point, &out->cw);
+static void
+contour_add_point (struct stroke_contour *c,
+		   const cairo_point_t *point)
+{
+    if (! close_enough (c,point, _cairo_contour_last_point (&c->contour)))
+	_cairo_contour_add_point (&c->contour, point);
+}
 
-    return _cairo_slope_compare (&in_slope, &out_slope) < 0;
+static void
+translate_point (cairo_point_t *point, const cairo_point_t *offset)
+{
+    point->x += offset->x;
+    point->y += offset->y;
 }
 
-/**
- * _cairo_slope_compare_sgn
- *
- * Return -1, 0 or 1 depending on the relative slopes of
- * two lines.
- */
 static int
-_cairo_slope_compare_sgn (double dx1, double dy1, double dx2, double dy2)
+slope_compare_sgn (double dx1, double dy1, double dx2, double dy2)
 {
     double  c = (dx1 * dy2 - dx2 * dy1);
 
@@ -125,43 +142,19 @@ _range_step (int i, int step, int max)
  * Construct a fan around the midpoint using the vertices from pen between
  * inpt and outpt.
  */
-static cairo_status_t
-_tessellate_fan (struct stroker *stroker,
-		 const cairo_slope_t *in_vector,
-		 const cairo_slope_t *out_vector,
-		 const cairo_point_t *midpt,
-		 const cairo_point_t *inpt,
-		 const cairo_point_t *outpt,
-		 cairo_bool_t clockwise)
+static void
+add_fan (struct stroker *stroker,
+	 const cairo_slope_t *in_vector,
+	 const cairo_slope_t *out_vector,
+	 const cairo_point_t *midpt,
+	 const cairo_point_t *inpt,
+	 const cairo_point_t *outpt,
+	 cairo_bool_t clockwise,
+	 struct stroke_contour *c)
 {
-    cairo_point_t stack_points[64], *points = stack_points;
     int start, stop, step, i, npoints;
-    cairo_status_t status;
 
     if (clockwise) {
-	step  = -1;
-
-	start = _cairo_pen_find_active_ccw_vertex_index (&stroker->pen,
-							 in_vector);
-	if (_cairo_slope_compare (&stroker->pen.vertices[start].slope_ccw,
-				  in_vector) < 0)
-	    start = _range_step (start, -1, stroker->pen.num_vertices);
-
-	stop  = _cairo_pen_find_active_ccw_vertex_index (&stroker->pen,
-							 out_vector);
-	if (_cairo_slope_compare (&stroker->pen.vertices[stop].slope_cw,
-				  out_vector) > 0)
-	{
-	    stop = _range_step (stop, 1, stroker->pen.num_vertices);
-	    if (_cairo_slope_compare (&stroker->pen.vertices[stop].slope_ccw,
-				      in_vector) < 0)
-	    {
-		goto BEVEL;
-	    }
-	}
-
-	npoints = start - stop;
-    } else {
 	step  = 1;
 
 	start = _cairo_pen_find_active_cw_vertex_index (&stroker->pen,
@@ -178,119 +171,405 @@ _tessellate_fan (struct stroker *stroker,
 	    stop = _range_step (stop, -1, stroker->pen.num_vertices);
 	    if (_cairo_slope_compare (&stroker->pen.vertices[stop].slope_cw,
 				      in_vector) < 0)
-	    {
-		goto BEVEL;
-	    }
+		return;
 	}
 
 	npoints = stop - start;
+    } else {
+	step  = -1;
+
+	start = _cairo_pen_find_active_ccw_vertex_index (&stroker->pen,
+							 in_vector);
+	if (_cairo_slope_compare (&stroker->pen.vertices[start].slope_ccw,
+				  in_vector) < 0)
+	    start = _range_step (start, -1, stroker->pen.num_vertices);
+
+	stop  = _cairo_pen_find_active_ccw_vertex_index (&stroker->pen,
+							 out_vector);
+	if (_cairo_slope_compare (&stroker->pen.vertices[stop].slope_cw,
+				  out_vector) > 0)
+	{
+	    stop = _range_step (stop, 1, stroker->pen.num_vertices);
+	    if (_cairo_slope_compare (&stroker->pen.vertices[stop].slope_ccw,
+				      in_vector) < 0)
+		return;
+	}
+
+	npoints = start - stop;
     }
     stop = _range_step (stop, step, stroker->pen.num_vertices);
-
     if (npoints < 0)
 	npoints += stroker->pen.num_vertices;
-    npoints += 3;
-
     if (npoints <= 1)
-	goto BEVEL;
-
-    if (npoints > ARRAY_LENGTH (stack_points)) {
-	points = _cairo_malloc_ab (npoints, sizeof (cairo_point_t));
-	if (unlikely (points == NULL))
-	    return _cairo_error (CAIRO_STATUS_NO_MEMORY);
-    }
-
+	return;
 
-    /* Construct the fan. */
-    npoints = 0;
-    points[npoints++] = *inpt;
     for (i = start;
 	 i != stop;
 	i = _range_step (i, step, stroker->pen.num_vertices))
     {
-	points[npoints] = *midpt;
-	translate_point (&points[npoints], &stroker->pen.vertices[i].point);
-	npoints++;
+	cairo_point_t p = *midpt;
+	translate_point (&p, &stroker->pen.vertices[i].point);
+	contour_add_point (c, &p);
     }
-    points[npoints++] = *outpt;
+}
 
-    for (i = 0; i < npoints - 1; i++) {
-	if (clockwise) {
-	    status = _cairo_polygon_add_external_edge (stroker->polygon,
-						 &points[i], &points[i+1]);
-	} else {
-	    status = _cairo_polygon_add_external_edge (stroker->polygon,
-						 &points[i+1], &points[i]);
-	}
-	if (unlikely (status))
-	    break;
+static int
+join_is_clockwise (const cairo_stroke_face_t *in,
+		   const cairo_stroke_face_t *out)
+{
+    return _cairo_slope_compare (&in->dev_vector, &out->dev_vector) < 0;
+}
+
+static double
+distance_from_face (const cairo_stroke_face_t *face,
+		    const cairo_point_t *p)
+{
+    return (p->x - face->point.x) * face->dev_slope.y - (p->y - face->point.y) * face->dev_slope.x;
+}
+
+static void
+inner_join (struct stroker *stroker,
+	    const cairo_stroke_face_t *in,
+	    const cairo_stroke_face_t *out)
+{
+    cairo_point_t last;
+    const cairo_point_t *p, *outpt;
+    struct stroke_contour *inner;
+    double d_p, d_last = -1;
+    double dot;
+    double line_width;
+    double sign;
+
+    /* XXX line segments shorter than line-width */
+
+    if (join_is_clockwise (in, out)) {
+	inner = &stroker->ccw;
+	outpt = &out->ccw;
+	sign = -1;
+    } else {
+	outpt = &out->cw;
+	inner = &stroker->cw;
+	sign = 1;
     }
 
-    if (points != stack_points)
-	free (points);
+    line_width = stroker->style.line_width/2;
+    line_width *= CAIRO_FIXED_ONE;
+prev:
+    if (inner->contour.chain.num_points == 0) {
+	contour_add_point (inner, outpt);
+	return;
+    }
+    p = _cairo_contour_last_point (&inner->contour);
+    if ((d_p = sign * distance_from_face (out, p)) <= line_width) {
+	last = *p;
+	d_last = d_p;
+	_cairo_contour_remove_last_point (&inner->contour);
+	goto prev;
+    }
 
-    return status;
+    if (d_last < 0) {
+	//contour_add_point (inner, outpt);
+	return;
+    }
 
-BEVEL:
-    /* Ensure a leak free connection... */
-    if (clockwise)
-	return _cairo_polygon_add_external_edge (stroker->polygon, inpt, outpt);
-    else
-	return _cairo_polygon_add_external_edge (stroker->polygon, outpt, inpt);
+    dot = (line_width - d_last) / (d_p - d_last);
+    last.x += dot * (p->x - last.x);
+    last.y += dot * (p->y - last.y);
+    contour_add_point (inner, &last);
 }
 
-static cairo_status_t
-join (struct stroker *stroker,
-      const cairo_stroke_face_t *in,
-      const cairo_stroke_face_t *out)
+static void
+inner_close (struct stroker *stroker,
+	     const cairo_stroke_face_t *in,
+	     cairo_stroke_face_t *out)
 {
-    int	clockwise = join_is_clockwise (out, in);
-    const cairo_point_t	*inpt, *outpt;
-    cairo_point_t points[4];
-    cairo_status_t status;
+    cairo_point_t last;
+    const cairo_point_t *p, *outpt;
+    struct stroke_contour *inner;
+    struct _cairo_contour_chain *chain;
+    double d_p, d_last = -1;
+    double dot;
+    double line_width;
+    double sign;
+    int i;
+
+    /* XXX line segments shorter than line-width */
+
+    if (join_is_clockwise (in, out)) {
+	inner = &stroker->ccw;
+	outpt = &in->ccw;
+	sign = -1;
+    } else {
+	outpt = &in->cw;
+	inner = &stroker->cw;
+	sign = 1;
+    }
 
-    if (in->cw.x  == out->cw.x  && in->cw.y  == out->cw.y &&
-	in->ccw.x == out->ccw.x && in->ccw.y == out->ccw.y)
-    {
-	return CAIRO_STATUS_SUCCESS;
+    if (inner->contour.chain.num_points == 0) {
+	contour_add_point (inner, outpt);
+	return;
     }
 
-    if (clockwise) {
-	status = _cairo_polygon_add_external_edge (stroker->polygon,
-					     &out->cw, &in->point);
-	if (unlikely (status))
-	    return status;
+    line_width = stroker->style.line_width/2;
+    line_width *= CAIRO_FIXED_ONE;
+
+    for (chain = &inner->contour.chain; chain; chain = chain->next) {
+	for (i = 0; i < chain->num_points; i++) {
+	    p = &chain->points[i];
+	    if ((d_p = sign * distance_from_face (in, p)) >= line_width)
+	    {
+		printf ("point [%d] =%f, last_dp=%f\n", i,d_p, d_last);
+		goto out;
+	    }
+
+	    last = *p;
+	    d_last = d_p;
+	}
+    }
+out:
+
+    dot = (line_width - d_last) / (d_p - d_last);
+    last.x += dot * (p->x - last.x);
+    last.y += dot * (p->y - last.y);
+    *_cairo_contour_last_point (&inner->contour) = last;
+
+    for (chain = &inner->contour.chain; chain; chain = chain->next) {
+	for (i = 0; i < chain->num_points; i++) {
+	    cairo_point_t *pp = &chain->points[i];
+	    if (pp == p)
+		return;
+	    *pp = last;
+	}
+    }
+}
 
-	status = _cairo_polygon_add_external_edge (stroker->polygon,
-					     &in->point, &in->cw);
-	if (unlikely (status))
-	    return status;
+static void
+outer_close (struct stroker *stroker,
+	     const cairo_stroke_face_t *in,
+	     const cairo_stroke_face_t *out)
+{
+    const cairo_point_t	*inpt, *outpt;
+    struct stroke_contour *outer;
+    int	clockwise;
 
+    if (in->cw.x == out->cw.x && in->cw.y == out->cw.y &&
+	in->ccw.x == out->ccw.x && out->ccw.y == out->ccw.y)
+    {
+	return;
+    }
+    clockwise = join_is_clockwise (in, out);
+    if (clockwise) {
+	inpt = &in->cw;
+	outpt = &out->cw;
+	outer = &stroker->cw;
+    } else {
 	inpt = &in->ccw;
 	outpt = &out->ccw;
-    } else {
-	status = _cairo_polygon_add_external_edge (stroker->polygon,
-					     &in->ccw, &in->point);
-	if (unlikely (status))
-	    return status;
+	outer = &stroker->ccw;
+    }
+
+    if (close_enough (outer, inpt, outpt))
+	return;
+
+    switch (stroker->style.line_join) {
+    case CAIRO_LINE_JOIN_ROUND:
+	/* construct a fan around the common midpoint */
+	add_fan (stroker,
+		 &in->dev_vector,
+		 &out->dev_vector,
+		 &in->point, inpt, outpt,
+		 clockwise, outer);
+	break;
 
-	status = _cairo_polygon_add_external_edge (stroker->polygon,
-					     &in->point, &out->ccw);
-	if (unlikely (status))
-	    return status;
+    case CAIRO_LINE_JOIN_MITER:
+    default: {
+	/* dot product of incoming slope vector with outgoing slope vector */
+	double	in_dot_out = -in->usr_vector.x * out->usr_vector.x +
+			     -in->usr_vector.y * out->usr_vector.y;
+	double	ml = stroker->style.miter_limit;
 
+	/* Check the miter limit -- lines meeting at an acute angle
+	 * can generate long miters, the limit converts them to bevel
+	 *
+	 * Consider the miter join formed when two line segments
+	 * meet at an angle psi:
+	 *
+	 *	   /.\
+	 *	  /. .\
+	 *	 /./ \.\
+	 *	/./psi\.\
+	 *
+	 * We can zoom in on the right half of that to see:
+	 *
+	 *	    |\
+	 *	    | \ psi/2
+	 *	    |  \
+	 *	    |   \
+	 *	    |    \
+	 *	    |     \
+	 *	  miter    \
+	 *	 length     \
+	 *	    |        \
+	 *	    |        .\
+	 *	    |    .     \
+	 *	    |.   line   \
+	 *	     \    width  \
+	 *	      \           \
+	 *
+	 *
+	 * The right triangle in that figure, (the line-width side is
+	 * shown faintly with three '.' characters), gives us the
+	 * following expression relating miter length, angle and line
+	 * width:
+	 *
+	 *	1 /sin (psi/2) = miter_length / line_width
+	 *
+	 * The right-hand side of this relationship is the same ratio
+	 * in which the miter limit (ml) is expressed. We want to know
+	 * when the miter length is within the miter limit. That is
+	 * when the following condition holds:
+	 *
+	 *	1/sin(psi/2) <= ml
+	 *	1 <= ml sin(psi/2)
+	 *	1 <= ml² sin²(psi/2)
+	 *	2 <= ml² 2 sin²(psi/2)
+	 *				2·sin²(psi/2) = 1-cos(psi)
+	 *	2 <= ml² (1-cos(psi))
+	 *
+	 *				in · out = |in| |out| cos (psi)
+	 *
+	 * in and out are both unit vectors, so:
+	 *
+	 *				in · out = cos (psi)
+	 *
+	 *	2 <= ml² (1 - in · out)
+	 *
+	 */
+	if (2 <= ml * ml * (1 - in_dot_out)) {
+	    double		x1, y1, x2, y2;
+	    double		mx, my;
+	    double		dx1, dx2, dy1, dy2;
+	    double		ix, iy;
+	    double		fdx1, fdy1, fdx2, fdy2;
+	    double		mdx, mdy;
+
+	    /*
+	     * we've got the points already transformed to device
+	     * space, but need to do some computation with them and
+	     * also need to transform the slope from user space to
+	     * device space
+	     */
+	    /* outer point of incoming line face */
+	    x1 = _cairo_fixed_to_double (inpt->x);
+	    y1 = _cairo_fixed_to_double (inpt->y);
+	    dx1 = in->usr_vector.x;
+	    dy1 = in->usr_vector.y;
+	    cairo_matrix_transform_distance (stroker->ctm, &dx1, &dy1);
+
+	    /* outer point of outgoing line face */
+	    x2 = _cairo_fixed_to_double (outpt->x);
+	    y2 = _cairo_fixed_to_double (outpt->y);
+	    dx2 = out->usr_vector.x;
+	    dy2 = out->usr_vector.y;
+	    cairo_matrix_transform_distance (stroker->ctm, &dx2, &dy2);
+
+	    /*
+	     * Compute the location of the outer corner of the miter.
+	     * That's pretty easy -- just the intersection of the two
+	     * outer edges.  We've got slopes and points on each
+	     * of those edges.  Compute my directly, then compute
+	     * mx by using the edge with the larger dy; that avoids
+	     * dividing by values close to zero.
+	     */
+	    my = (((x2 - x1) * dy1 * dy2 - y2 * dx2 * dy1 + y1 * dx1 * dy2) /
+		  (dx1 * dy2 - dx2 * dy1));
+	    if (fabs (dy1) >= fabs (dy2))
+		mx = (my - y1) * dx1 / dy1 + x1;
+	    else
+		mx = (my - y2) * dx2 / dy2 + x2;
+
+	    /*
+	     * When the two outer edges are nearly parallel, slight
+	     * perturbations in the position of the outer points of the lines
+	     * caused by representing them in fixed point form can cause the
+	     * intersection point of the miter to move a large amount. If
+	     * that moves the miter intersection from between the two faces,
+	     * then draw a bevel instead.
+	     */
+
+	    ix = _cairo_fixed_to_double (in->point.x);
+	    iy = _cairo_fixed_to_double (in->point.y);
+
+	    /* slope of one face */
+	    fdx1 = x1 - ix; fdy1 = y1 - iy;
+
+	    /* slope of the other face */
+	    fdx2 = x2 - ix; fdy2 = y2 - iy;
+
+	    /* slope from the intersection to the miter point */
+	    mdx = mx - ix; mdy = my - iy;
+
+	    /*
+	     * Make sure the miter point line lies between the two
+	     * faces by comparing the slopes
+	     */
+	    if (slope_compare_sgn (fdx1, fdy1, mdx, mdy) !=
+		slope_compare_sgn (fdx2, fdy2, mdx, mdy))
+	    {
+		cairo_point_t p;
+
+		p.x = _cairo_fixed_from_double (mx);
+		p.y = _cairo_fixed_from_double (my);
+
+		*_cairo_contour_last_point (&outer->contour) = p;
+	    }
+	}
+	break;
+    }
+
+    case CAIRO_LINE_JOIN_BEVEL:
+	break;
+    }
+    contour_add_point (outer, outpt);
+}
+
+static void
+join (struct stroker *stroker,
+      const cairo_stroke_face_t *in,
+      const cairo_stroke_face_t *out)
+{
+    const cairo_point_t	*inpt, *outpt;
+    struct stroke_contour *outer;
+    int	clockwise;
+
+    if (in->cw.x == out->cw.x && in->cw.y == out->cw.y &&
+	in->ccw.x == out->ccw.x && out->ccw.y == out->ccw.y)
+    {
+	return;
+    }
+    clockwise = join_is_clockwise (in, out);
+    if (clockwise) {
 	inpt = &in->cw;
 	outpt = &out->cw;
+	outer = &stroker->cw;
+    } else {
+	inpt = &in->ccw;
+	outpt = &out->ccw;
+	outer = &stroker->ccw;
     }
 
+    if (close_enough (outer, inpt, outpt))
+	return;
+
     switch (stroker->style.line_join) {
     case CAIRO_LINE_JOIN_ROUND:
 	/* construct a fan around the common midpoint */
-	return _tessellate_fan (stroker,
-				&in->dev_vector,
-				&out->dev_vector,
-				&in->point, inpt, outpt,
-				clockwise);
+	add_fan (stroker,
+		 &in->dev_vector,
+		 &out->dev_vector,
+		 &in->point, inpt, outpt,
+		 clockwise, outer);
+	break;
 
     case CAIRO_LINE_JOIN_MITER:
     default: {
@@ -424,55 +703,31 @@ join (struct stroker *stroker,
 	     * Make sure the miter point line lies between the two
 	     * faces by comparing the slopes
 	     */
-	    if (_cairo_slope_compare_sgn (fdx1, fdy1, mdx, mdy) !=
-		_cairo_slope_compare_sgn (fdx2, fdy2, mdx, mdy))
+	    if (slope_compare_sgn (fdx1, fdy1, mdx, mdy) !=
+		slope_compare_sgn (fdx2, fdy2, mdx, mdy))
 	    {
-		points[0].x = _cairo_fixed_from_double (mx);
-		points[0].y = _cairo_fixed_from_double (my);
-
-		if (clockwise) {
-		    status = _cairo_polygon_add_external_edge (stroker->polygon,
-							 inpt, &points[0]);
-		    if (unlikely (status))
-			return status;
-
-		    status = _cairo_polygon_add_external_edge (stroker->polygon,
-							 &points[0], outpt);
-		    if (unlikely (status))
-			return status;
-		} else {
-		    status = _cairo_polygon_add_external_edge (stroker->polygon,
-							 outpt, &points[0]);
-		    if (unlikely (status))
-			return status;
-
-		    status = _cairo_polygon_add_external_edge (stroker->polygon,
-							 &points[0], inpt);
-		    if (unlikely (status))
-			return status;
-		}
-
-		return CAIRO_STATUS_SUCCESS;
+		cairo_point_t p;
+
+		p.x = _cairo_fixed_from_double (mx);
+		p.y = _cairo_fixed_from_double (my);
+
+		*_cairo_contour_last_point (&outer->contour) = p;
+		return;
 	    }
 	}
+	break;
     }
 
-    /* fall through ... */
-
     case CAIRO_LINE_JOIN_BEVEL:
-	if (clockwise) {
-	    return _cairo_polygon_add_external_edge (stroker->polygon,
-					       inpt, outpt);
-	} else {
-	    return _cairo_polygon_add_external_edge (stroker->polygon,
-					       outpt, inpt);
-	}
+	break;
     }
+    contour_add_point (outer, outpt);
 }
 
-static cairo_status_t
-_cairo_stroker_add_cap (struct stroker *stroker,
-			const cairo_stroke_face_t *f)
+static void
+add_cap (struct stroker *stroker,
+	 const cairo_stroke_face_t *f,
+	 struct stroke_contour *c)
 {
     switch (stroker->style.line_cap) {
     case CAIRO_LINE_CAP_ROUND: {
@@ -481,12 +736,10 @@ _cairo_stroker_add_cap (struct stroker *stroker,
 	slope.dx = -f->dev_vector.dx;
 	slope.dy = -f->dev_vector.dy;
 
-	return _tessellate_fan (stroker,
-				&f->dev_vector,
-				&slope,
-				&f->point, &f->cw, &f->ccw,
-				FALSE);
-
+	add_fan (stroker, &f->dev_vector, &slope,
+		 &f->point, &f->ccw, &f->cw,
+		 FALSE, c);
+	break;
     }
 
     case CAIRO_LINE_CAP_SQUARE: {
@@ -509,38 +762,21 @@ _cairo_stroker_add_cap (struct stroker *stroker,
 	quad[2].y = f->cw.y + fvector.dy;
 	quad[3] = f->cw;
 
-	{
-	    cairo_status_t status;
-
-	    status = _cairo_polygon_add_external_edge (stroker->polygon,
-						 &quad[0], &quad[1]);
-	    if (unlikely (status))
-		return status;
-
-	    status = _cairo_polygon_add_external_edge (stroker->polygon,
-						 &quad[1], &quad[2]);
-	    if (unlikely (status))
-		return status;
-
-	    status = _cairo_polygon_add_external_edge (stroker->polygon,
-						 &quad[2], &quad[3]);
-	    if (unlikely (status))
-		return status;
-
-	    return CAIRO_STATUS_SUCCESS;
-	}
+	contour_add_point (c, &quad[1]);
+	contour_add_point (c, &quad[2]);
     }
 
     case CAIRO_LINE_CAP_BUTT:
     default:
-	return _cairo_polygon_add_external_edge (stroker->polygon,
-					   &f->ccw, &f->cw);
+	break;
     }
+    contour_add_point (c, &f->cw);
 }
 
-static cairo_status_t
-_cairo_stroker_add_leading_cap (struct stroker *stroker,
-				const cairo_stroke_face_t *face)
+static void
+add_leading_cap (struct stroker *stroker,
+		 const cairo_stroke_face_t *face,
+		 struct stroke_contour *c)
 {
     cairo_stroke_face_t reversed;
     cairo_point_t t;
@@ -556,25 +792,23 @@ _cairo_stroker_add_leading_cap (struct stroker *stroker,
     reversed.cw = reversed.ccw;
     reversed.ccw = t;
 
-    return _cairo_stroker_add_cap (stroker, &reversed);
+    add_cap (stroker, &reversed, c);
 }
 
-static cairo_status_t
-_cairo_stroker_add_trailing_cap (struct stroker *stroker,
-				 const cairo_stroke_face_t *face)
+static void
+add_trailing_cap (struct stroker *stroker,
+		  const cairo_stroke_face_t *face,
+		  struct stroke_contour *c)
 {
-    return _cairo_stroker_add_cap (stroker, face);
+    add_cap (stroker, face, c);
 }
 
 static inline void
-normalized_device_slope (double *dx, double *dy,
-			 const cairo_matrix_t *ctm_inverse)
+normalize_slope (double *dx, double *dy)
 {
     double dx0 = *dx, dy0 = *dy;
     double mag;
 
-    cairo_matrix_transform_distance (ctm_inverse, &dx0, &dy0);
-
     assert (dx0 != 0.0 || dy0 != 0.0);
 
     if (dx0 == 0.0) {
@@ -604,11 +838,17 @@ normalized_device_slope (double *dx, double *dy,
 
 static void
 compute_face (const cairo_point_t *point, cairo_slope_t *dev_slope,
-	      double slope_dx, double slope_dy,
 	      struct stroker *stroker, cairo_stroke_face_t *face)
 {
     double face_dx, face_dy;
     cairo_point_t offset_ccw, offset_cw;
+    double slope_dx, slope_dy;
+
+    slope_dx = _cairo_fixed_to_double (dev_slope->dx);
+    slope_dy = _cairo_fixed_to_double (dev_slope->dy);
+    normalize_slope (&slope_dx, &slope_dy);
+    face->dev_slope.x = slope_dx;
+    face->dev_slope.y = slope_dy;
 
     /*
      * rotate to get a line_width/2 vector along the face, note that
@@ -617,19 +857,29 @@ compute_face (const cairo_point_t *point, cairo_slope_t *dev_slope,
      * whether the ctm reflects or not, and that can be determined
      * by looking at the determinant of the matrix.
      */
-    if (stroker->ctm_det_positive)
-    {
+    if (! _cairo_matrix_is_identity (stroker->ctm_inverse)) {
+	/* Normalize the matrix! */
+	cairo_matrix_transform_distance (stroker->ctm_inverse,
+					 &slope_dx, &slope_dy);
+	normalize_slope (&slope_dx, &slope_dy);
+
+	if (stroker->ctm_det_positive)
+	{
+	    face_dx = - slope_dy * (stroker->style.line_width / 2.0);
+	    face_dy = slope_dx * (stroker->style.line_width / 2.0);
+	}
+	else
+	{
+	    face_dx = slope_dy * (stroker->style.line_width / 2.0);
+	    face_dy = - slope_dx * (stroker->style.line_width / 2.0);
+	}
+
+	/* back to device space */
+	cairo_matrix_transform_distance (stroker->ctm, &face_dx, &face_dy);
+    } else {
 	face_dx = - slope_dy * (stroker->style.line_width / 2.0);
 	face_dy = slope_dx * (stroker->style.line_width / 2.0);
     }
-    else
-    {
-	face_dx = slope_dy * (stroker->style.line_width / 2.0);
-	face_dy = - slope_dx * (stroker->style.line_width / 2.0);
-    }
-
-    /* back to device space */
-    cairo_matrix_transform_distance (stroker->ctm, &face_dx, &face_dy);
 
     offset_ccw.x = _cairo_fixed_from_double (face_dx);
     offset_ccw.y = _cairo_fixed_from_double (face_dy);
@@ -650,88 +900,58 @@ compute_face (const cairo_point_t *point, cairo_slope_t *dev_slope,
     face->dev_vector = *dev_slope;
 }
 
-static cairo_status_t
-_cairo_stroker_add_caps (struct stroker *stroker)
+static void
+add_caps (struct stroker *stroker)
 {
-    cairo_status_t status;
-
     /* check for a degenerative sub_path */
-    if (stroker->has_initial_sub_path
-	&& ! stroker->has_first_face
-	&& ! stroker->has_current_face
-	&& stroker->style.line_cap == CAIRO_LINE_CAP_ROUND)
+    if (stroker->has_initial_sub_path &&
+	! stroker->has_first_face &&
+	! stroker->has_current_face &&
+	stroker->style.line_cap == CAIRO_LINE_CAP_ROUND)
     {
 	/* pick an arbitrary slope to use */
-	double dx = 1.0, dy = 0.0;
 	cairo_slope_t slope = { CAIRO_FIXED_ONE, 0 };
 	cairo_stroke_face_t face;
 
-	normalized_device_slope (&dx, &dy, stroker->ctm_inverse);
-
 	/* arbitrarily choose first_point
 	 * first_point and current_point should be the same */
-	compute_face (&stroker->first_point, &slope, dx, dy, stroker, &face);
-
-	status = _cairo_stroker_add_leading_cap (stroker, &face);
-	if (unlikely (status))
-	    return status;
+	compute_face (&stroker->first_point, &slope, stroker, &face);
 
-	status = _cairo_stroker_add_trailing_cap (stroker, &face);
-	if (unlikely (status))
-	    return status;
-    }
-
-    if (stroker->has_first_face) {
-	status = _cairo_stroker_add_leading_cap (stroker,
-						 &stroker->first_face);
-	if (unlikely (status))
-	    return status;
-    }
+	add_leading_cap (stroker, &face, &stroker->ccw);
+	add_trailing_cap (stroker, &face, &stroker->ccw);
 
-    if (stroker->has_current_face) {
-	status = _cairo_stroker_add_trailing_cap (stroker,
-						  &stroker->current_face);
-	if (unlikely (status))
-	    return status;
-    }
-
-    return CAIRO_STATUS_SUCCESS;
-}
-
-static cairo_status_t
-add_segment (struct stroker *stroker,
-	      const cairo_point_t *p1,
-	      const cairo_point_t *p2,
-	      cairo_slope_t *dev_slope,
-	      double slope_dx, double slope_dy,
-	      cairo_stroke_face_t *start,
-	      cairo_stroke_face_t *end)
-{
-    cairo_status_t status;
-
-    if (p1->y == p2->y)
-	return CAIRO_STATUS_SUCCESS;
+	_cairo_polygon_add_contour (stroker->polygon, &stroker->ccw.contour);
+	_cairo_contour_reset (&stroker->ccw.contour);
+    } else {
+#if DEBUG
+	{
+	    FILE *file = fopen ("contours.txt", "a");
+	    _cairo_debug_print_contour (file, &stroker->path);
+	    _cairo_debug_print_contour (file, &stroker->cw.contour);
+	    _cairo_debug_print_contour (file, &stroker->ccw.contour);
+	    fclose (file);
+	}
+	_cairo_contour_reset (&stroker->path);
+#endif
 
-    compute_face (p1, dev_slope, slope_dx, slope_dy, stroker, start);
-    *end = *start;
+	if (stroker->has_current_face)
+	    add_trailing_cap (stroker, &stroker->current_face, &stroker->ccw);
 
-    end->point = *p2;
-    end->ccw.x += p2->x - p1->x;
-    end->ccw.y += p2->y - p1->y;
-    end->cw.x += p2->x - p1->x;
-    end->cw.y += p2->y - p1->y;
+	//_cairo_contour_simplify (&stroker->ccw.contour, stroker->tolerance);
 
-    status = _cairo_polygon_add_external_edge (stroker->polygon,
-					       &end->cw, &start->cw);
-    if (unlikely (status))
-	return status;
+	_cairo_polygon_add_contour (stroker->polygon, &stroker->ccw.contour);
+	_cairo_contour_reset (&stroker->ccw.contour);
 
-    status = _cairo_polygon_add_external_edge (stroker->polygon,
-					       &start->ccw, &end->ccw);
-    if (unlikely (status))
-	return status;
+	if (stroker->has_first_face) {
+	    add_leading_cap (stroker, &stroker->first_face, &stroker->ccw);
+	}
+	_cairo_polygon_add_contour (stroker->polygon, &stroker->ccw.contour);
+	_cairo_contour_reset (&stroker->ccw.contour);
 
-    return CAIRO_STATUS_SUCCESS;
+	//_cairo_contour_simplify (&stroker->cw.contour, stroker->tolerance);
+	_cairo_polygon_add_contour (stroker->polygon, &stroker->cw.contour);
+	_cairo_contour_reset (&stroker->cw.contour);
+    }
 }
 
 static cairo_status_t
@@ -739,16 +959,17 @@ move_to (void *closure,
 	 const cairo_point_t *point)
 {
     struct stroker *stroker = closure;
-    cairo_status_t status;
 
     /* Cap the start and end of the previous sub path as needed */
-    status = _cairo_stroker_add_caps (stroker);
-    if (unlikely (status))
-	return status;
+    add_caps (stroker);
 
     stroker->first_point = *point;
     stroker->current_point = *point;
 
+#if DEBUG
+    _cairo_contour_add_point (&stroker->path, point);
+#endif
+
     stroker->has_first_face = FALSE;
     stroker->has_current_face = FALSE;
     stroker->has_initial_sub_path = FALSE;
@@ -761,40 +982,73 @@ line_to (void *closure,
 	 const cairo_point_t *point)
 {
     struct stroker *stroker = closure;
-    cairo_stroke_face_t start, end;
+    cairo_stroke_face_t start;
     cairo_point_t *p1 = &stroker->current_point;
     cairo_slope_t dev_slope;
-    double slope_dx, slope_dy;
-    cairo_status_t status;
 
     stroker->has_initial_sub_path = TRUE;
 
     if (p1->x == point->x && p1->y == point->y)
 	return CAIRO_STATUS_SUCCESS;
 
-    _cairo_slope_init (&dev_slope, p1, point);
-    slope_dx = _cairo_fixed_to_double (point->x - p1->x);
-    slope_dy = _cairo_fixed_to_double (point->y - p1->y);
-    normalized_device_slope (&slope_dx, &slope_dy, stroker->ctm_inverse);
+#if DEBUG
+    _cairo_contour_add_point (&stroker->path, point);
+#endif
 
-    status = add_segment (stroker, p1, point, &dev_slope,
-			  slope_dx, slope_dy,
-			  &start, &end);
-    if (unlikely (status))
-	return status;
+    _cairo_slope_init (&dev_slope, p1, point);
+    compute_face (p1, &dev_slope, stroker, &start);
 
     if (stroker->has_current_face) {
 	/* Join with final face from previous segment */
-	status = join (stroker, &stroker->current_face, &start);
-	if (unlikely (status))
-	    return status;
-    } else if (! stroker->has_first_face) {
-	/* Save sub path's first face in case needed for closing join */
-	stroker->first_face = start;
-	stroker->has_first_face = TRUE;
+	join (stroker, &stroker->current_face, &start);
+	inner_join (stroker, &stroker->current_face, &start);
+    } else {
+	if (! stroker->has_first_face) {
+	    /* Save sub path's first face in case needed for closing join */
+	    stroker->first_face = start;
+	    stroker->has_first_face = TRUE;
+	}
+	stroker->has_current_face = TRUE;
+
+	contour_add_point (&stroker->cw, &start.cw);
+	contour_add_point (&stroker->ccw, &start.ccw);
     }
-    stroker->current_face = end;
-    stroker->has_current_face = TRUE;
+
+    stroker->current_face = start;
+    stroker->current_face.point = *point;
+    stroker->current_face.ccw.x += dev_slope.dx;
+    stroker->current_face.ccw.y += dev_slope.dy;
+    stroker->current_face.cw.x += dev_slope.dx;
+    stroker->current_face.cw.y += dev_slope.dy;
+
+    contour_add_point (&stroker->cw, &stroker->current_face.cw);
+    contour_add_point (&stroker->ccw, &stroker->current_face.ccw);
+
+    stroker->current_point = *point;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+spline_to (void *closure,
+	 const cairo_point_t *point)
+{
+    struct stroker *stroker = closure;
+    cairo_slope_t dev_slope;
+
+    if (stroker->current_point.x == point->x &&
+	stroker->current_point.y == point->y)
+	return CAIRO_STATUS_SUCCESS;
+
+#if DEBUG
+    _cairo_contour_add_point (&stroker->path, point);
+#endif
+
+    _cairo_slope_init (&dev_slope, &stroker->current_point, point);
+    compute_face (point, &dev_slope, stroker, &stroker->current_face);
+
+    contour_add_point (&stroker->cw, &stroker->current_face.cw);
+    contour_add_point (&stroker->ccw, &stroker->current_face.ccw);
 
     stroker->current_point = *point;
 
@@ -809,64 +1063,49 @@ curve_to (void *closure,
 {
     struct stroker *stroker = closure;
     cairo_spline_t spline;
-    cairo_line_join_t line_join_save;
     cairo_stroke_face_t face;
-    double slope_dx, slope_dy;
     cairo_status_t status = CAIRO_STATUS_SUCCESS;
 
-    if (! _cairo_spline_init (&spline, line_to, stroker,
+    if (! _cairo_spline_init (&spline, spline_to, stroker,
 			      &stroker->current_point, b, c, d))
     {
 	return line_to (closure, d);
     }
 
     /* Compute the initial face */
-    slope_dx = _cairo_fixed_to_double (spline.initial_slope.dx);
-    slope_dy = _cairo_fixed_to_double (spline.initial_slope.dy);
-    normalized_device_slope (&slope_dx, &slope_dy, stroker->ctm_inverse);
     compute_face (&stroker->current_point,
 		  &spline.initial_slope,
-		  slope_dx, slope_dy,
 		  stroker, &face);
 
     if (stroker->has_current_face) {
-	status = join (stroker, &stroker->current_face, &face);
-	if (unlikely (status))
-	    return status;
-    } else if (! stroker->has_first_face) {
-	stroker->first_face = face;
-	stroker->has_first_face = TRUE;
-    }
+	join (stroker, &stroker->current_face, &face);
+	inner_join (stroker, &stroker->current_face, &face);
+    } else {
+	contour_add_point (&stroker->cw, &face.cw);
+	contour_add_point (&stroker->ccw, &face.ccw);
 
+	if (! stroker->has_first_face) {
+	    /* Save sub path's first face in case needed for closing join */
+	    stroker->first_face = face;
+	    stroker->has_first_face = TRUE;
+	}
+	stroker->has_current_face = TRUE;
+    }
     stroker->current_face = face;
-    stroker->has_current_face = TRUE;
 
     /* Temporarily modify the stroker to use round joins to guarantee
      * smooth stroked curves. */
-    line_join_save = stroker->style.line_join;
-    stroker->style.line_join = CAIRO_LINE_JOIN_ROUND;
-
     status = _cairo_spline_decompose (&spline, stroker->tolerance);
-    if (unlikely (status))
-	return status;
+    assert (status == CAIRO_STATUS_SUCCESS);
 
-    /* And join the final face */
-    slope_dx = _cairo_fixed_to_double (spline.final_slope.dx);
-    slope_dy = _cairo_fixed_to_double (spline.final_slope.dy);
-    normalized_device_slope (&slope_dx, &slope_dy, stroker->ctm_inverse);
-    compute_face (&stroker->current_point,
-		  &spline.final_slope,
-		  slope_dx, slope_dy,
-		  stroker, &face);
+    /* Tweak the final point to lie on the out-face */
+    compute_face (d, &spline.final_slope, stroker, &face);
 
-    status = join (stroker, &stroker->current_face, &face);
-    if (unlikely (status))
-	return status;
+    *_cairo_contour_last_point (&stroker->cw.contour) = face.cw;
+    *_cairo_contour_last_point (&stroker->ccw.contour) = face.ccw;
 
     stroker->current_face = face;
 
-    stroker->style.line_join = line_join_save;
-
     return CAIRO_STATUS_SUCCESS;
 }
 
@@ -882,14 +1121,35 @@ close_path (void *closure)
 
     if (stroker->has_first_face && stroker->has_current_face) {
 	/* Join first and final faces of sub path */
-	status = join (stroker, &stroker->current_face, &stroker->first_face);
-	if (unlikely (status))
-	    return status;
+	outer_close (stroker, &stroker->current_face, &stroker->first_face);
+	inner_close (stroker, &stroker->current_face, &stroker->first_face);
+#if 0
+	*_cairo_contour_first_point (&stroker->ccw.contour) =
+	    *_cairo_contour_last_point (&stroker->ccw.contour);
+
+	*_cairo_contour_first_point (&stroker->cw.contour) =
+	    *_cairo_contour_last_point (&stroker->cw.contour);
+#endif
+
+	_cairo_polygon_add_contour (stroker->polygon, &stroker->cw.contour);
+	_cairo_polygon_add_contour (stroker->polygon, &stroker->ccw.contour);
+
+#if DEBUG
+	{
+	    FILE *file = fopen ("contours.txt", "a");
+	    _cairo_debug_print_contour (file, &stroker->path);
+	    _cairo_debug_print_contour (file, &stroker->cw.contour);
+	    _cairo_debug_print_contour (file, &stroker->ccw.contour);
+	    fclose (file);
+	}
+
+	_cairo_contour_reset (&stroker->path);
+#endif
+	_cairo_contour_reset (&stroker->cw.contour);
+	_cairo_contour_reset (&stroker->ccw.contour);
     } else {
 	/* Cap the start and end of the sub path as needed */
-	status = _cairo_stroker_add_caps (stroker);
-	if (unlikely (status))
-	    return status;
+	add_caps (stroker);
     }
 
     stroker->has_initial_sub_path = FALSE;
@@ -924,8 +1184,8 @@ _cairo_path_fixed_stroke_to_polygon (const cairo_path_fixed_t	*path,
     stroker.ctm_inverse = ctm_inverse;
     stroker.tolerance = tolerance;
 
-    stroker.ctm_determinant = _cairo_matrix_compute_determinant (ctm);
-    stroker.ctm_det_positive = stroker.ctm_determinant >= 0.0;
+    stroker.ctm_det_positive =
+	_cairo_matrix_compute_determinant (ctm) >= 0.0;
 
     status = _cairo_pen_init (&stroker.pen,
 		              style->line_width / 2.0,
@@ -942,6 +1202,15 @@ _cairo_path_fixed_stroke_to_polygon (const cairo_path_fixed_t	*path,
     stroker.has_first_face = FALSE;
     stroker.has_initial_sub_path = FALSE;
 
+#if DEBUG
+    _cairo_contour_init (&stroker.path, 0);
+#endif
+    _cairo_contour_init (&stroker.cw.contour, 1);
+    _cairo_contour_init (&stroker.ccw.contour, -1);
+    tolerance *= 1 << CAIRO_FIXED_FRAC_BITS;
+    tolerance *= tolerance;
+    stroker.cw.tolerance = tolerance;
+    stroker.ccw.tolerance = tolerance;
     stroker.polygon = polygon;
 
     status = _cairo_path_fixed_interpret (path,
@@ -952,8 +1221,10 @@ _cairo_path_fixed_stroke_to_polygon (const cairo_path_fixed_t	*path,
 					  &stroker);
     /* Cap the start and end of the final sub path as needed */
     if (likely (status == CAIRO_STATUS_SUCCESS))
-	status = _cairo_stroker_add_caps (&stroker);
+	add_caps (&stroker);
 
+    _cairo_contour_fini (&stroker.cw.contour);
+    _cairo_contour_fini (&stroker.ccw.contour);
     _cairo_pen_fini (&stroker.pen);
 
     return status;
diff --git a/src/cairo-polygon.c b/src/cairo-polygon.c
index 74ec9fa..6436712 100644
--- a/src/cairo-polygon.c
+++ b/src/cairo-polygon.c
@@ -38,12 +38,14 @@
 #include "cairoint.h"
 
 #include "cairo-boxes-private.h"
+#include "cairo-contour-private.h"
 #include "cairo-error-private.h"
 
 static void
 _cairo_polygon_add_edge (cairo_polygon_t *polygon,
 			 const cairo_point_t *p1,
-			 const cairo_point_t *p2);
+			 const cairo_point_t *p2,
+			 int dir);
 
 void
 _cairo_polygon_init (cairo_polygon_t *polygon,
@@ -131,12 +133,12 @@ _cairo_polygon_init_boxes (cairo_polygon_t *polygon,
 		    p1 = chunk->base[i].p1;
 		    p2.x = p1.x;
 		    p2.y = chunk->base[i].p2.y;
-		    _cairo_polygon_add_edge (polygon, &p1, &p2);
+		    _cairo_polygon_add_edge (polygon, &p1, &p2, 1);
 
 		    p1 = chunk->base[i].p2;
 		    p2.x = p1.x;
 		    p2.y = chunk->base[i].p1.y;
-		    _cairo_polygon_add_edge (polygon, &p1, &p2);
+		    _cairo_polygon_add_edge (polygon, &p1, &p2, 1);
 	    }
     }
 
@@ -178,12 +180,12 @@ _cairo_polygon_init_box_array (cairo_polygon_t *polygon,
 	p1 = boxes[i].p1;
 	p2.x = p1.x;
 	p2.y = boxes[i].p2.y;
-	_cairo_polygon_add_edge (polygon, &p1, &p2);
+	_cairo_polygon_add_edge (polygon, &p1, &p2, 1);
 
 	p1 = boxes[i].p2;
 	p2.x = p1.x;
 	p2.y = boxes[i].p1.y;
-	_cairo_polygon_add_edge (polygon, &p1, &p2);
+	_cairo_polygon_add_edge (polygon, &p1, &p2, 1);
     }
 
     return polygon->status;
@@ -406,20 +408,17 @@ _add_clipped_edge (cairo_polygon_t *polygon,
 static void
 _cairo_polygon_add_edge (cairo_polygon_t *polygon,
 			 const cairo_point_t *p1,
-			 const cairo_point_t *p2)
+			 const cairo_point_t *p2,
+			 int dir)
 {
-    int dir;
-
     /* drop horizontal edges */
     if (p1->y == p2->y)
 	return;
 
-    if (p1->y < p2->y) {
-	dir = 1;
-    } else {
+    if (p1->y > p2->y) {
 	const cairo_point_t *t;
 	t = p1, p1 = p2, p2 = t;
-	dir = -1;
+	dir = -dir;
     }
 
     if (polygon->num_limits) {
@@ -439,7 +438,7 @@ _cairo_polygon_add_external_edge (void *polygon,
 				  const cairo_point_t *p1,
 				  const cairo_point_t *p2)
 {
-    _cairo_polygon_add_edge (polygon, p1, p2);
+    _cairo_polygon_add_edge (polygon, p1, p2, 1);
     return _cairo_polygon_status (polygon);
 }
 
@@ -469,3 +468,26 @@ _cairo_polygon_add_line (cairo_polygon_t *polygon,
 
     return polygon->status;
 }
+
+cairo_status_t
+_cairo_polygon_add_contour (cairo_polygon_t *polygon,
+			    const cairo_contour_t *contour)
+{
+    const struct _cairo_contour_chain *chain;
+    const cairo_point_t *prev = NULL;
+    int i;
+
+    if (contour->chain.num_points <= 1)
+	return CAIRO_INT_STATUS_SUCCESS;
+
+    prev = &contour->chain.points[0];
+    for (chain = &contour->chain; chain; chain = chain->next) {
+	for (i = 0; i < chain->num_points; i++) {
+	    _cairo_polygon_add_edge (polygon, prev, &chain->points[i],
+				     contour->direction);
+	    prev = &chain->points[i];
+	}
+    }
+
+    return polygon->status;
+}
diff --git a/src/cairo-slope-private.h b/src/cairo-slope-private.h
index 6a58c9f..72dc0dd 100644
--- a/src/cairo-slope-private.h
+++ b/src/cairo-slope-private.h
@@ -57,11 +57,17 @@ _cairo_slope_equal (const cairo_slope_t *a, const cairo_slope_t *b)
 			    _cairo_int32x32_64_mul (b->dy, a->dx));
 }
 
+static inline cairo_int64_t
+_cairo_slope_dot_product (const cairo_slope_t *a, const cairo_slope_t *b)
+{
+    return _cairo_int64_add (_cairo_int32x32_64_mul (a->dx, b->dx),
+			     _cairo_int32x32_64_mul (a->dy, b->dy));
+}
+
 static inline cairo_bool_t
 _cairo_slope_backwards (const cairo_slope_t *a, const cairo_slope_t *b)
 {
-    return _cairo_int64_negative (_cairo_int64_add (_cairo_int32x32_64_mul (a->dx, b->dx),
-						    _cairo_int32x32_64_mul (a->dy, b->dy)));
+    return _cairo_int64_negative (_cairo_slope_dot_product (a, b));
 }
 
 cairo_private int
diff --git a/src/cairo-types-private.h b/src/cairo-types-private.h
index 4933cf2..f7a1f26 100644
--- a/src/cairo-types-private.h
+++ b/src/cairo-types-private.h
@@ -63,6 +63,7 @@ typedef struct _cairo_clip cairo_clip_t;
 typedef struct _cairo_clip_path cairo_clip_path_t;
 typedef struct _cairo_color cairo_color_t;
 typedef struct _cairo_color_stop cairo_color_stop_t;
+typedef struct _cairo_contour cairo_contour_t;
 typedef struct _cairo_device_backend cairo_device_backend_t;
 typedef struct _cairo_font_face_backend     cairo_font_face_backend_t;
 typedef struct _cairo_gstate cairo_gstate_t;
diff --git a/src/cairoint.h b/src/cairoint.h
index f745f41..c6243be 100644
--- a/src/cairoint.h
+++ b/src/cairoint.h
@@ -1030,6 +1030,7 @@ typedef struct _cairo_stroke_face {
     cairo_point_t point;
     cairo_point_t cw;
     cairo_slope_t dev_vector;
+    cairo_point_double_t dev_slope;
     cairo_point_double_t usr_vector;
 } cairo_stroke_face_t;
 
@@ -2097,6 +2098,10 @@ _cairo_polygon_add_external_edge (void *polygon,
 				  const cairo_point_t *p2);
 
 cairo_private cairo_status_t
+_cairo_polygon_add_contour (cairo_polygon_t *polygon,
+			    const cairo_contour_t *contour);
+
+cairo_private cairo_status_t
 _cairo_polygon_reduce (cairo_polygon_t *polygon,
 		       cairo_fill_rule_t fill_rule);
 
diff --git a/util/.gitignore b/util/.gitignore
index ff7ffbd..c3ac04f 100644
--- a/util/.gitignore
+++ b/util/.gitignore
@@ -2,6 +2,7 @@
 .libs
 Makefile
 Makefile.in
+show-contour
 show-edges
 show-events
 show-traps
diff --git a/util/Makefile.am b/util/Makefile.am
index f3aa079..6c6c849 100644
--- a/util/Makefile.am
+++ b/util/Makefile.am
@@ -32,7 +32,7 @@ AM_CPPFLAGS = -I$(top_srcdir)/src \
 	      -I$(top_srcdir)/util/cairo-script	\
 	      $(CAIRO_CFLAGS)
 
-EXTRA_PROGRAMS += show-traps show-edges show-polygon show-events
+EXTRA_PROGRAMS += show-contour show-traps show-edges show-polygon show-events
 if CAIRO_HAS_INTERPRETER
 EXTRA_PROGRAMS += trace-to-xml xml-to-trace
 endif
@@ -56,6 +56,11 @@ show_edges_CFLAGS = $(gtk_CFLAGS)
 #show_edges_LDADD = $(top_builddir)/src/libcairo.la $(gtk_LIBS)
 show_edges_LDADD = $(gtk_LIBS)
 
+show_contour_SOURCES = show-contour.c
+show_contour_CFLAGS = $(gtk_CFLAGS)
+#show_contour_LDADD = $(top_builddir)/src/libcairo.la $(gtk_LIBS)
+show_contour_LDADD = $(gtk_LIBS)
+
 show_events_SOURCES = show-events.c
 show_events_CFLAGS = $(gtk_CFLAGS)
 #show_events_LDADD = $(top_builddir)/src/libcairo.la $(gtk_LIBS)
diff --git a/util/show-contour.c b/util/show-contour.c
new file mode 100644
index 0000000..1e549cb
--- /dev/null
+++ b/util/show-contour.c
@@ -0,0 +1,652 @@
+#define _GNU_SOURCE
+#include <gtk/gtk.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <gdk/gdkkeysyms.h>
+#include <math.h>
+
+typedef struct _point {
+    gdouble x, y;
+} point_t;
+typedef struct _box {
+    point_t p1, p2;
+} box_t;
+
+typedef struct _contour {
+    struct _contour *next, *prev;
+    int direction;
+    int num_points;
+    int size;
+    point_t points[0];
+} contour_t;
+
+typedef struct _TrapView {
+    GtkWidget widget;
+
+    cairo_surface_t *pixmap;
+    int pixmap_width, pixmap_height;
+
+    box_t extents;
+    contour_t *contours;
+
+    double px, py;
+
+    gint mag_x, mag_y;
+    gint mag_size;
+    gdouble mag_zoom;
+    gboolean in_mag_drag;
+    gint mag_drag_x, mag_drag_y;
+} TrapView;
+
+typedef struct _TrapViewClass {
+    GtkWidgetClass parent_class;
+} TrapViewClass;
+
+G_DEFINE_TYPE (TrapView, trap_view, GTK_TYPE_WIDGET)
+
+static cairo_surface_t *
+pixmap_create (TrapView *self, cairo_surface_t *target)
+{
+    cairo_surface_t *surface =
+	cairo_surface_create_similar (target, CAIRO_CONTENT_COLOR,
+				      self->widget.allocation.width,
+				      self->widget.allocation.height);
+    cairo_t *cr = cairo_create (surface);
+    contour_t *contour;
+    gdouble sf_x, sf_y, sf;
+    gdouble mid, dim;
+    gdouble x0,  y0;
+    int n;
+    box_t extents;
+
+    cairo_set_source_rgb (cr, 1, 1, 1);
+    cairo_paint (cr);
+
+    if (self->contours == NULL) {
+	cairo_destroy(cr);
+	return surface;
+    }
+
+    extents = self->extents;
+
+    mid = (extents.p2.x + extents.p1.x) / 2.;
+    dim = (extents.p2.x - extents.p1.x) / 2. * 1.25;
+    sf_x = self->widget.allocation.width / dim / 2;
+
+    mid = (extents.p2.y + extents.p1.y) / 2.;
+    dim = (extents.p2.y - extents.p1.y) / 2. * 1.25;
+    sf_y = self->widget.allocation.height / dim / 2;
+
+    sf = MIN (sf_x, sf_y);
+
+    mid = (extents.p2.x + extents.p1.x) / 2.;
+    dim = sf_x / sf * (extents.p2.x - extents.p1.x) / 2. * 1.25;
+    x0 = mid - dim;
+    mid = (extents.p2.y + extents.p1.y) / 2.;
+    dim = sf_y / sf * (extents.p2.y - extents.p1.y) / 2. * 1.25;
+    y0 = mid - dim;
+
+    for (contour = self->contours; contour; contour = contour->next) {
+	if (contour->num_points == 0)
+	    continue;
+
+	cairo_save (cr); {
+	    cairo_scale (cr, sf, sf);
+	    cairo_translate (cr, -x0, -y0);
+	    switch (contour->direction) {
+	    case -1:
+		cairo_set_source_rgb (cr, 0.0, 0.0, 1.0);
+		break;
+	    case 0:
+		cairo_set_source_rgb (cr, 0.0, 1.0, 0.0);
+		break;
+	    case 1:
+		cairo_set_source_rgb (cr, 1.0, 0.0, 0.0);
+		break;
+	    }
+	    {
+		const point_t *p = &contour->points[0];
+		cairo_arc (cr, p->x, p->y, 4/sf, 0, 2 * M_PI);
+		cairo_save (cr);
+		cairo_identity_matrix (cr);
+		cairo_stroke (cr);
+		cairo_restore (cr);
+	    }
+	    for (n = 0; n < contour->num_points; n++) {
+		const point_t *p = &contour->points[n];
+		cairo_arc (cr, p->x, p->y, 2/sf, 0, 2 * M_PI);
+		cairo_fill (cr);
+	    }
+	    for (n = 0; n < contour->num_points; n++) {
+		const point_t *p = &contour->points[n];
+		cairo_line_to (cr, p->x, p->y);
+	    }
+	} cairo_restore (cr);
+
+	switch (contour->direction) {
+	case -1:
+	    cairo_set_source_rgb (cr, 0.3, 0.3, 0.9);
+	    break;
+	case 0:
+	    cairo_set_source_rgb (cr, 0.3, 0.9, 0.3);
+	    break;
+	case 1:
+	    cairo_set_source_rgb (cr, 0.9, 0.3, 0.3);
+	    break;
+	}
+	cairo_set_line_width (cr, 1.);
+	cairo_stroke (cr);
+    }
+
+    cairo_destroy (cr);
+    return surface;
+}
+
+static void
+trap_view_draw (TrapView *self, cairo_t *cr)
+{
+    contour_t *contour;
+    gdouble sf_x, sf_y, sf;
+    gdouble mid, dim;
+    gdouble x0,  y0;
+    int n;
+    box_t extents;
+
+    extents = self->extents;
+
+    mid = (extents.p2.x + extents.p1.x) / 2.;
+    dim = (extents.p2.x - extents.p1.x) / 2. * 1.25;
+    sf_x = self->widget.allocation.width / dim / 2;
+
+    mid = (extents.p2.y + extents.p1.y) / 2.;
+    dim = (extents.p2.y - extents.p1.y) / 2. * 1.25;
+    sf_y = self->widget.allocation.height / dim / 2;
+
+    sf = MIN (sf_x, sf_y);
+
+    mid = (extents.p2.x + extents.p1.x) / 2.;
+    dim = sf_x / sf * (extents.p2.x - extents.p1.x) / 2. * 1.25;
+    x0 = mid - dim;
+    mid = (extents.p2.y + extents.p1.y) / 2.;
+    dim = sf_y / sf * (extents.p2.y - extents.p1.y) / 2. * 1.25;
+    y0 = mid - dim;
+
+    if (self->pixmap_width != self->widget.allocation.width ||
+	self->pixmap_height != self->widget.allocation.height)
+    {
+	cairo_surface_destroy (self->pixmap);
+	self->pixmap = pixmap_create (self, cairo_get_target (cr));
+	self->pixmap_width = self->widget.allocation.width;
+	self->pixmap_height = self->widget.allocation.height;
+    }
+
+    cairo_set_source_surface (cr, self->pixmap, 0, 0);
+    cairo_paint (cr);
+
+    if (self->contours == NULL)
+	return;
+
+    /* draw a zoom view of the area around the mouse */
+    if (1) {
+	double zoom = self->mag_zoom;
+	int size = self->mag_size;
+	int mag_x = self->mag_x;
+	int mag_y = self->mag_y;
+
+	if (1) {
+	    mag_x = self->px + size/4;
+	    mag_y = self->py - size/2;
+	}
+
+	cairo_save (cr); {
+	    /* bottom right */
+	    cairo_rectangle (cr, mag_x, mag_y, size, size);
+	    cairo_stroke_preserve (cr);
+	    cairo_set_source_rgb (cr, 1, 1, 1);
+	    cairo_fill_preserve (cr);
+	    cairo_clip (cr);
+
+	    /* compute roi in extents */
+	    cairo_translate (cr, mag_x + size/2, mag_y + size/2);
+
+	    cairo_save (cr); {
+		for (contour = self->contours; contour; contour = contour->next) {
+		    if (contour->num_points == 0)
+			continue;
+
+		    cairo_save (cr); {
+			cairo_scale (cr, zoom, zoom);
+			cairo_translate (cr, -(self->px / sf + x0), -(self->py /sf + y0));
+			switch (contour->direction) {
+			case -1:
+			    cairo_set_source_rgb (cr, 0.0, 0.0, 1.0);
+			    break;
+			case 0:
+			    cairo_set_source_rgb (cr, 0.0, 1.0, 0.0);
+			    break;
+			case 1:
+			    cairo_set_source_rgb (cr, 1.0, 0.0, 0.0);
+			    break;
+			}
+			for (n = 0; n < contour->num_points; n++) {
+			    const point_t *p = &contour->points[n];
+			    cairo_arc (cr, p->x, p->y, 2/zoom, 0, 2 * M_PI);
+			    cairo_fill (cr);
+			}
+			for (n = 0; n < contour->num_points; n++) {
+			    const point_t *p = &contour->points[n];
+			    cairo_line_to (cr, p->x, p->y);
+			}
+		    } cairo_restore (cr);
+
+		    switch (contour->direction) {
+		    case -1:
+			cairo_set_source_rgb (cr, 0.3, 0.3, 0.9);
+			break;
+		    case 0:
+			cairo_set_source_rgb (cr, 0.3, 0.9, 0.3);
+			break;
+		    case 1:
+			cairo_set_source_rgb (cr, 0.9, 0.3, 0.3);
+			break;
+		    }
+		    cairo_stroke (cr);
+		}
+	    } cairo_restore (cr);
+
+	    /* grid */
+	    cairo_save (cr); {
+		int i;
+
+		cairo_translate (cr,
+				 -zoom*fmod (self->px/sf + x0, 1.),
+				 -zoom*fmod (self->py/sf + y0, 1.));
+		zoom /= 2;
+		for (i = -size/2/zoom; i <= size/2/zoom + 1; i+=2) {
+		    cairo_move_to (cr, zoom*i, -size/2);
+		    cairo_line_to (cr, zoom*i, size/2 + zoom);
+		    cairo_move_to (cr, -size/2, zoom*i);
+		    cairo_line_to (cr, size/2 + zoom, zoom*i);
+		}
+		zoom *= 2;
+		cairo_set_source_rgba (cr, .7, .7, .7, .5);
+		cairo_set_line_width (cr, 1.);
+		cairo_stroke (cr);
+
+		for (i = -size/2/zoom - 1; i <= size/2/zoom + 1; i++) {
+		    cairo_move_to (cr, zoom*i, -size/2);
+		    cairo_line_to (cr, zoom*i, size/2 + zoom);
+		    cairo_move_to (cr, -size/2, zoom*i);
+		    cairo_line_to (cr, size/2 + zoom, zoom*i);
+		}
+		cairo_set_source_rgba (cr, .1, .1, .1, .5);
+		cairo_set_line_width (cr, 2.);
+		cairo_stroke (cr);
+	    } cairo_restore (cr);
+
+	} cairo_restore (cr);
+    }
+}
+
+
+static gdouble
+edge_length (const point_t *p1, const point_t *p2)
+{
+    return hypot (p2->x - p1->x, p2->y - p1->y);
+}
+
+static gdouble
+contour_compute_total_length (const contour_t *contour)
+{
+    int n;
+    gdouble len = 0.;
+    for (n = 1; n < contour->num_points; n++)
+	len += edge_length (&contour->points[n-1], &contour->points[n]);
+    return len;
+}
+
+static void
+trap_view_draw_labels (TrapView *self, cairo_t *cr)
+{
+    contour_t *contour;
+    int y = 12;
+
+    for (contour = self->contours; contour; contour = contour->next) {
+	double total_length = contour_compute_total_length (contour) / 256.;
+	PangoLayout *layout;
+	gint width, height;
+	GString *string;
+	gchar *str;
+
+	if (contour->num_points == 0)
+	    continue;
+
+	string = g_string_new (NULL);
+	g_string_append_printf (string,
+				"Number of points:\t%d\n"
+				"Total length of contour: \t%.2f",
+				contour->num_points,
+				total_length);
+
+	str = g_string_free (string, FALSE);
+	layout = gtk_widget_create_pango_layout (&self->widget, str);
+	g_free (str);
+
+	pango_layout_get_pixel_size (layout, &width, &height);
+
+	switch (contour->direction) {
+	case -1:
+	    cairo_set_source_rgb (cr, 0.9, 0.3, 0.3);
+	    break;
+	case 0:
+	    cairo_set_source_rgb (cr, 0.3, 0.9, 0.3);
+	    break;
+	case 1:
+	    cairo_set_source_rgb (cr, 0.3, 0.3, 0.9);
+	    break;
+	}
+
+	cairo_move_to (cr, 10, y);
+	pango_cairo_show_layout (cr, layout);
+	g_object_unref (layout);
+
+	y += height + 4;
+    }
+}
+
+static gboolean
+trap_view_expose (GtkWidget *w, GdkEventExpose *ev)
+{
+    TrapView *self = (TrapView *) w;
+    cairo_t *cr;
+
+    cr = gdk_cairo_create (w->window);
+    gdk_cairo_region (cr, ev->region);
+    cairo_clip (cr);
+
+    trap_view_draw (self, cr);
+    trap_view_draw_labels (self, cr);
+
+    cairo_destroy (cr);
+    return FALSE;
+}
+
+static gboolean
+trap_view_key_press (GtkWidget *w, GdkEventKey *ev)
+{
+    switch (ev->keyval) {
+    case GDK_Escape:
+    case GDK_Q:
+	gtk_main_quit ();
+	break;
+    }
+
+    return FALSE;
+}
+
+static gboolean
+trap_view_button_press (GtkWidget *w, GdkEventButton *ev)
+{
+    TrapView *self = (TrapView *) w;
+
+    if (ev->x < self->mag_x ||
+	ev->y < self->mag_y ||
+	ev->x > self->mag_x + self->mag_size ||
+	ev->y > self->mag_y + self->mag_size)
+    {
+    }
+    else
+    {
+	self->in_mag_drag = TRUE;
+	self->mag_drag_x = ev->x;
+	self->mag_drag_y = ev->y;
+    }
+
+    return FALSE;
+}
+
+static gboolean
+trap_view_button_release (GtkWidget *w, GdkEventButton *ev)
+{
+    TrapView *self = (TrapView *) w;
+
+    self->in_mag_drag = FALSE;
+
+    return FALSE;
+}
+
+static void
+trap_view_update_mouse (TrapView *self, GdkEventMotion *ev)
+{
+    self->px = ev->x;
+    self->py = ev->y;
+
+    gtk_widget_queue_draw (&self->widget);
+}
+
+static void
+trap_view_update_magnifier (TrapView *self, gint *xy)
+{
+    self->mag_x = xy[0];
+    self->mag_y = xy[1];
+
+    gtk_widget_queue_draw (&self->widget);
+}
+
+static gboolean
+trap_view_motion (GtkWidget *w, GdkEventMotion *ev)
+{
+    TrapView *self = (TrapView *) w;
+
+    if (self->in_mag_drag) {
+	int xy[2];
+
+	xy[0] = self->mag_x + ev->x - self->mag_drag_x;
+	xy[1] = self->mag_y + ev->y - self->mag_drag_y;
+
+	trap_view_update_magnifier (self, xy);
+
+	self->mag_drag_x = ev->x;
+	self->mag_drag_y = ev->y;
+    } else if (ev->x < self->mag_x ||
+	       ev->y < self->mag_y ||
+	       ev->x > self->mag_x + self->mag_size ||
+	       ev->y > self->mag_y + self->mag_size)
+    {
+	trap_view_update_mouse (self, ev);
+    }
+
+    return FALSE;
+}
+
+static void
+trap_view_realize (GtkWidget *widget)
+{
+    GdkWindowAttr attributes;
+
+    GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
+
+    attributes.window_type = GDK_WINDOW_CHILD;
+    attributes.x = widget->allocation.x;
+    attributes.y = widget->allocation.y;
+    attributes.width  = widget->allocation.width;
+    attributes.height = widget->allocation.height;
+    attributes.wclass = GDK_INPUT_OUTPUT;
+    attributes.visual = gtk_widget_get_visual (widget);
+    attributes.colormap = gtk_widget_get_colormap (widget);
+    attributes.event_mask = gtk_widget_get_events (widget) |
+	                    GDK_BUTTON_PRESS_MASK |
+	                    GDK_BUTTON_RELEASE_MASK |
+	                    GDK_KEY_PRESS_MASK |
+	                    GDK_KEY_RELEASE_MASK |
+			    GDK_POINTER_MOTION_MASK |
+			    GDK_BUTTON_MOTION_MASK |
+	                    GDK_EXPOSURE_MASK;
+
+    widget->window = gdk_window_new (gtk_widget_get_parent_window (widget),
+				     &attributes,
+				     GDK_WA_X | GDK_WA_Y |
+				     GDK_WA_VISUAL | GDK_WA_COLORMAP);
+    gdk_window_set_user_data (widget->window, widget);
+
+    widget->style = gtk_style_attach (widget->style, widget->window);
+    gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
+}
+
+static void
+trap_view_size_allocate (GtkWidget *w, GdkRectangle *r)
+{
+    TrapView *self = (TrapView *) w;
+
+    GTK_WIDGET_CLASS (trap_view_parent_class)->size_allocate (w, r);
+
+    self->mag_x = w->allocation.width - self->mag_size - 10;
+    self->mag_y = w->allocation.height - self->mag_size - 10;
+}
+
+static void
+trap_view_finalize (GObject *obj)
+{
+    G_OBJECT_CLASS (trap_view_parent_class)->finalize (obj);
+}
+
+static void
+trap_view_class_init (TrapViewClass *klass)
+{
+    GObjectClass *object_class = (GObjectClass *) klass;
+    GtkWidgetClass *widget_class = (GtkWidgetClass *) klass;
+
+    object_class->finalize = trap_view_finalize;
+
+    widget_class->realize = trap_view_realize;
+    widget_class->size_allocate = trap_view_size_allocate;
+    widget_class->expose_event = trap_view_expose;
+    widget_class->key_press_event = trap_view_key_press;
+    widget_class->button_press_event = trap_view_button_press;
+    widget_class->button_release_event = trap_view_button_release;
+    widget_class->motion_notify_event = trap_view_motion;
+}
+
+static void
+trap_view_init (TrapView *self)
+{
+    self->mag_zoom = 64;
+    self->mag_size = 200;
+
+    self->extents.p1.x = G_MAXDOUBLE;
+    self->extents.p1.y = G_MAXDOUBLE;
+    self->extents.p2.x = -G_MAXDOUBLE;
+    self->extents.p2.y = -G_MAXDOUBLE;
+
+    GTK_WIDGET_SET_FLAGS (self, GTK_CAN_FOCUS);
+}
+
+static contour_t *
+_contour_add_point (TrapView *tv, contour_t *contour, point_t *p)
+{
+    if (contour == NULL)
+	return NULL;
+
+    if (p->y < tv->extents.p1.y)
+	tv->extents.p1.y = p->y;
+    if (p->y > tv->extents.p2.y)
+	tv->extents.p2.y = p->y;
+
+    if (p->x < tv->extents.p1.x)
+	tv->extents.p1.x = p->x;
+    if (p->x > tv->extents.p2.x)
+	tv->extents.p2.x = p->x;
+
+    if (contour->num_points == contour->size) {
+	int newsize = 2 * contour->size;
+	void *newcontour;
+
+	newcontour = g_realloc (contour,
+			      sizeof (contour_t) + newsize * sizeof (point_t));
+	if (newcontour == NULL)
+	    return contour;
+
+	contour = newcontour;
+	contour->size = newsize;
+
+	if (contour->next != NULL)
+	    contour->next->prev = newcontour;
+	if (contour->prev != NULL)
+	    contour->prev->next = newcontour;
+	else
+	    tv->contours = newcontour;
+    }
+
+    contour->points[contour->num_points++] = *p;
+
+    return contour;
+}
+
+static contour_t *
+contour_new (TrapView *tv, int direction)
+{
+    contour_t *t;
+
+    t = g_malloc (sizeof (contour_t) + 128 * sizeof (point_t));
+    t->direction = direction;
+    t->prev = NULL;
+    t->next = tv->contours;
+    if (tv->contours)
+	tv->contours->prev = t;
+    tv->contours = t;
+
+    t->size = 128;
+    t->num_points = 0;
+
+    return t;
+}
+
+int
+main (int argc, char **argv)
+{
+    TrapView *tv;
+    contour_t *contour = NULL;
+    GtkWidget *window;
+    FILE *file;
+    char *line = NULL;
+    size_t len = 0;
+
+    gtk_init (&argc, &argv);
+
+    tv = g_object_new (trap_view_get_type (), NULL);
+
+    file = fopen (argv[1], "r");
+    if (file != NULL) {
+	while (getline (&line, &len, file) != -1) {
+	    point_t p;
+	    int direction;
+
+	    if (sscanf (line, "contour: direction=%d", &direction)) {
+		if (contour)
+		    g_print ("read %d contour\n", contour->num_points);
+
+		contour = contour_new (tv, direction);
+	    } else if (sscanf (line, "  [%*d] = (%lf, %lf)", &p.x, &p.y) == 2) {
+		contour = _contour_add_point (tv, contour, &p);
+	    }
+	}
+
+	if (contour)
+	    g_print ("read %d contour\n", contour->num_points);
+
+	g_print ("extents=(%lg, %lg), (%lg, %lg)\n",
+		 tv->extents.p1.x, tv->extents.p1.y,
+		 tv->extents.p2.x, tv->extents.p2.y);
+	fclose (file);
+    }
+
+    window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+    g_signal_connect (window, "delete-event",
+		      G_CALLBACK (gtk_main_quit), NULL);
+    gtk_widget_set_size_request (window, 800, 800);
+    gtk_container_add (GTK_CONTAINER (window), &tv->widget);
+    gtk_widget_show_all (window);
+
+    gtk_main ();
+    return 0;
+}
commit 7b34be56c09271e9df4b12adf370e14aef7f57c9
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Thu Aug 4 17:31:05 2011 +0100

    step1 of a fast stroke-to-polygon

diff --git a/src/Makefile.sources b/src/Makefile.sources
index 28c9d13..a1ff678 100644
--- a/src/Makefile.sources
+++ b/src/Makefile.sources
@@ -164,6 +164,7 @@ cairo_sources = \
 	cairo-path-in-fill.c \
 	cairo-path-stroke.c \
 	cairo-path-stroke-boxes.c \
+	cairo-path-stroke-polygon.c \
 	cairo-pattern.c \
 	cairo-pen.c \
 	cairo-polygon.c \
diff --git a/src/cairo-path-stroke-polygon.c b/src/cairo-path-stroke-polygon.c
new file mode 100644
index 0000000..d6cd82f
--- /dev/null
+++ b/src/cairo-path-stroke-polygon.c
@@ -0,0 +1,960 @@
+/* -*- 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
+ * Copyright © 2011 Intel Corporation
+ *
+ * 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"
+
+struct stroker {
+    cairo_stroke_style_t style;
+
+    cairo_polygon_t *polygon;
+
+    struct contour {
+	cairo_point_t first, current;
+    } inside, outside;
+
+    const cairo_matrix_t *ctm;
+    const cairo_matrix_t *ctm_inverse;
+    double tolerance;
+    double ctm_determinant;
+    cairo_bool_t ctm_det_positive;
+
+    cairo_pen_t	  pen;
+
+    cairo_point_t current_point;
+    cairo_point_t first_point;
+
+    cairo_bool_t has_initial_sub_path;
+
+    cairo_bool_t has_current_face;
+    cairo_stroke_face_t current_face;
+
+    cairo_bool_t has_first_face;
+    cairo_stroke_face_t first_face;
+};
+
+static void
+translate_point (cairo_point_t *point, const cairo_point_t *offset)
+{
+    point->x += offset->x;
+    point->y += offset->y;
+}
+
+static int
+join_is_clockwise (const cairo_stroke_face_t *in,
+		   const cairo_stroke_face_t *out)
+{
+    cairo_slope_t in_slope, out_slope;
+
+    _cairo_slope_init (&in_slope, &in->point, &in->cw);
+    _cairo_slope_init (&out_slope, &out->point, &out->cw);
+
+    return _cairo_slope_compare (&in_slope, &out_slope) < 0;
+}
+
+/**
+ * _cairo_slope_compare_sgn
+ *
+ * Return -1, 0 or 1 depending on the relative slopes of
+ * two lines.
+ */
+static int
+_cairo_slope_compare_sgn (double dx1, double dy1, double dx2, double dy2)
+{
+    double  c = (dx1 * dy2 - dx2 * dy1);
+
+    if (c > 0) return 1;
+    if (c < 0) return -1;
+    return 0;
+}
+
+static inline int
+_range_step (int i, int step, int max)
+{
+    i += step;
+    if (i < 0)
+	i = max - 1;
+    if (i >= max)
+	i = 0;
+    return i;
+}
+
+/*
+ * Construct a fan around the midpoint using the vertices from pen between
+ * inpt and outpt.
+ */
+static cairo_status_t
+_tessellate_fan (struct stroker *stroker,
+		 const cairo_slope_t *in_vector,
+		 const cairo_slope_t *out_vector,
+		 const cairo_point_t *midpt,
+		 const cairo_point_t *inpt,
+		 const cairo_point_t *outpt,
+		 cairo_bool_t clockwise)
+{
+    cairo_point_t stack_points[64], *points = stack_points;
+    int start, stop, step, i, npoints;
+    cairo_status_t status;
+
+    if (clockwise) {
+	step  = -1;
+
+	start = _cairo_pen_find_active_ccw_vertex_index (&stroker->pen,
+							 in_vector);
+	if (_cairo_slope_compare (&stroker->pen.vertices[start].slope_ccw,
+				  in_vector) < 0)
+	    start = _range_step (start, -1, stroker->pen.num_vertices);
+
+	stop  = _cairo_pen_find_active_ccw_vertex_index (&stroker->pen,
+							 out_vector);
+	if (_cairo_slope_compare (&stroker->pen.vertices[stop].slope_cw,
+				  out_vector) > 0)
+	{
+	    stop = _range_step (stop, 1, stroker->pen.num_vertices);
+	    if (_cairo_slope_compare (&stroker->pen.vertices[stop].slope_ccw,
+				      in_vector) < 0)
+	    {
+		goto BEVEL;
+	    }
+	}
+
+	npoints = start - stop;
+    } else {
+	step  = 1;
+
+	start = _cairo_pen_find_active_cw_vertex_index (&stroker->pen,
+							in_vector);
+	if (_cairo_slope_compare (&stroker->pen.vertices[start].slope_cw,
+				  in_vector) < 0)
+	    start = _range_step (start, 1, stroker->pen.num_vertices);
+
+	stop  = _cairo_pen_find_active_cw_vertex_index (&stroker->pen,
+							out_vector);
+	if (_cairo_slope_compare (&stroker->pen.vertices[stop].slope_ccw,
+				  out_vector) > 0)
+	{
+	    stop = _range_step (stop, -1, stroker->pen.num_vertices);
+	    if (_cairo_slope_compare (&stroker->pen.vertices[stop].slope_cw,
+				      in_vector) < 0)
+	    {
+		goto BEVEL;
+	    }
+	}
+
+	npoints = stop - start;
+    }
+    stop = _range_step (stop, step, stroker->pen.num_vertices);
+
+    if (npoints < 0)
+	npoints += stroker->pen.num_vertices;
+    npoints += 3;
+
+    if (npoints <= 1)
+	goto BEVEL;
+
+    if (npoints > ARRAY_LENGTH (stack_points)) {
+	points = _cairo_malloc_ab (npoints, sizeof (cairo_point_t));
+	if (unlikely (points == NULL))
+	    return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+    }
+
+
+    /* Construct the fan. */
+    npoints = 0;
+    points[npoints++] = *inpt;
+    for (i = start;
+	 i != stop;
+	i = _range_step (i, step, stroker->pen.num_vertices))
+    {
+	points[npoints] = *midpt;
+	translate_point (&points[npoints], &stroker->pen.vertices[i].point);
+	npoints++;
+    }
+    points[npoints++] = *outpt;
+
+    for (i = 0; i < npoints - 1; i++) {
+	if (clockwise) {
+	    status = _cairo_polygon_add_external_edge (stroker->polygon,
+						 &points[i], &points[i+1]);
+	} else {
+	    status = _cairo_polygon_add_external_edge (stroker->polygon,
+						 &points[i+1], &points[i]);
+	}
+	if (unlikely (status))
+	    break;
+    }
+
+    if (points != stack_points)
+	free (points);
+
+    return status;
+
+BEVEL:
+    /* Ensure a leak free connection... */
+    if (clockwise)
+	return _cairo_polygon_add_external_edge (stroker->polygon, inpt, outpt);
+    else
+	return _cairo_polygon_add_external_edge (stroker->polygon, outpt, inpt);
+}
+
+static cairo_status_t
+join (struct stroker *stroker,
+      const cairo_stroke_face_t *in,
+      const cairo_stroke_face_t *out)
+{
+    int	clockwise = join_is_clockwise (out, in);
+    const cairo_point_t	*inpt, *outpt;
+    cairo_point_t points[4];
+    cairo_status_t status;
+
+    if (in->cw.x  == out->cw.x  && in->cw.y  == out->cw.y &&
+	in->ccw.x == out->ccw.x && in->ccw.y == out->ccw.y)
+    {
+	return CAIRO_STATUS_SUCCESS;
+    }
+
+    if (clockwise) {
+	status = _cairo_polygon_add_external_edge (stroker->polygon,
+					     &out->cw, &in->point);
+	if (unlikely (status))
+	    return status;
+
+	status = _cairo_polygon_add_external_edge (stroker->polygon,
+					     &in->point, &in->cw);
+	if (unlikely (status))
+	    return status;
+
+	inpt = &in->ccw;
+	outpt = &out->ccw;
+    } else {
+	status = _cairo_polygon_add_external_edge (stroker->polygon,
+					     &in->ccw, &in->point);
+	if (unlikely (status))
+	    return status;
+
+	status = _cairo_polygon_add_external_edge (stroker->polygon,
+					     &in->point, &out->ccw);
+	if (unlikely (status))
+	    return status;
+
+	inpt = &in->cw;
+	outpt = &out->cw;
+    }
+
+    switch (stroker->style.line_join) {
+    case CAIRO_LINE_JOIN_ROUND:
+	/* construct a fan around the common midpoint */
+	return _tessellate_fan (stroker,
+				&in->dev_vector,
+				&out->dev_vector,
+				&in->point, inpt, outpt,
+				clockwise);
+
+    case CAIRO_LINE_JOIN_MITER:
+    default: {
+	/* dot product of incoming slope vector with outgoing slope vector */
+	double	in_dot_out = -in->usr_vector.x * out->usr_vector.x +
+			     -in->usr_vector.y * out->usr_vector.y;
+	double	ml = stroker->style.miter_limit;
+
+	/* Check the miter limit -- lines meeting at an acute angle
+	 * can generate long miters, the limit converts them to bevel
+	 *
+	 * Consider the miter join formed when two line segments
+	 * meet at an angle psi:
+	 *
+	 *	   /.\
+	 *	  /. .\
+	 *	 /./ \.\
+	 *	/./psi\.\
+	 *
+	 * We can zoom in on the right half of that to see:
+	 *
+	 *	    |\
+	 *	    | \ psi/2
+	 *	    |  \
+	 *	    |   \
+	 *	    |    \
+	 *	    |     \
+	 *	  miter    \
+	 *	 length     \
+	 *	    |        \
+	 *	    |        .\
+	 *	    |    .     \
+	 *	    |.   line   \
+	 *	     \    width  \
+	 *	      \           \
+	 *
+	 *
+	 * The right triangle in that figure, (the line-width side is
+	 * shown faintly with three '.' characters), gives us the
+	 * following expression relating miter length, angle and line
+	 * width:
+	 *
+	 *	1 /sin (psi/2) = miter_length / line_width
+	 *
+	 * The right-hand side of this relationship is the same ratio
+	 * in which the miter limit (ml) is expressed. We want to know
+	 * when the miter length is within the miter limit. That is
+	 * when the following condition holds:
+	 *
+	 *	1/sin(psi/2) <= ml
+	 *	1 <= ml sin(psi/2)
+	 *	1 <= ml² sin²(psi/2)
+	 *	2 <= ml² 2 sin²(psi/2)
+	 *				2·sin²(psi/2) = 1-cos(psi)
+	 *	2 <= ml² (1-cos(psi))
+	 *
+	 *				in · out = |in| |out| cos (psi)
+	 *
+	 * in and out are both unit vectors, so:
+	 *
+	 *				in · out = cos (psi)
+	 *
+	 *	2 <= ml² (1 - in · out)
+	 *
+	 */
+	if (2 <= ml * ml * (1 - in_dot_out)) {
+	    double		x1, y1, x2, y2;
+	    double		mx, my;
+	    double		dx1, dx2, dy1, dy2;
+	    double		ix, iy;
+	    double		fdx1, fdy1, fdx2, fdy2;
+	    double		mdx, mdy;
+
+	    /*
+	     * we've got the points already transformed to device
+	     * space, but need to do some computation with them and
+	     * also need to transform the slope from user space to
+	     * device space
+	     */
+	    /* outer point of incoming line face */
+	    x1 = _cairo_fixed_to_double (inpt->x);
+	    y1 = _cairo_fixed_to_double (inpt->y);
+	    dx1 = in->usr_vector.x;
+	    dy1 = in->usr_vector.y;
+	    cairo_matrix_transform_distance (stroker->ctm, &dx1, &dy1);
+
+	    /* outer point of outgoing line face */
+	    x2 = _cairo_fixed_to_double (outpt->x);
+	    y2 = _cairo_fixed_to_double (outpt->y);
+	    dx2 = out->usr_vector.x;
+	    dy2 = out->usr_vector.y;
+	    cairo_matrix_transform_distance (stroker->ctm, &dx2, &dy2);
+
+	    /*
+	     * Compute the location of the outer corner of the miter.
+	     * That's pretty easy -- just the intersection of the two
+	     * outer edges.  We've got slopes and points on each
+	     * of those edges.  Compute my directly, then compute
+	     * mx by using the edge with the larger dy; that avoids
+	     * dividing by values close to zero.
+	     */
+	    my = (((x2 - x1) * dy1 * dy2 - y2 * dx2 * dy1 + y1 * dx1 * dy2) /
+		  (dx1 * dy2 - dx2 * dy1));
+	    if (fabs (dy1) >= fabs (dy2))
+		mx = (my - y1) * dx1 / dy1 + x1;
+	    else
+		mx = (my - y2) * dx2 / dy2 + x2;
+
+	    /*
+	     * When the two outer edges are nearly parallel, slight
+	     * perturbations in the position of the outer points of the lines
+	     * caused by representing them in fixed point form can cause the
+	     * intersection point of the miter to move a large amount. If
+	     * that moves the miter intersection from between the two faces,
+	     * then draw a bevel instead.
+	     */
+
+	    ix = _cairo_fixed_to_double (in->point.x);
+	    iy = _cairo_fixed_to_double (in->point.y);
+
+	    /* slope of one face */
+	    fdx1 = x1 - ix; fdy1 = y1 - iy;
+
+	    /* slope of the other face */
+	    fdx2 = x2 - ix; fdy2 = y2 - iy;
+
+	    /* slope from the intersection to the miter point */
+	    mdx = mx - ix; mdy = my - iy;
+
+	    /*
+	     * Make sure the miter point line lies between the two
+	     * faces by comparing the slopes
+	     */
+	    if (_cairo_slope_compare_sgn (fdx1, fdy1, mdx, mdy) !=
+		_cairo_slope_compare_sgn (fdx2, fdy2, mdx, mdy))
+	    {
+		points[0].x = _cairo_fixed_from_double (mx);
+		points[0].y = _cairo_fixed_from_double (my);
+
+		if (clockwise) {
+		    status = _cairo_polygon_add_external_edge (stroker->polygon,
+							 inpt, &points[0]);
+		    if (unlikely (status))
+			return status;
+
+		    status = _cairo_polygon_add_external_edge (stroker->polygon,
+							 &points[0], outpt);
+		    if (unlikely (status))
+			return status;
+		} else {
+		    status = _cairo_polygon_add_external_edge (stroker->polygon,
+							 outpt, &points[0]);
+		    if (unlikely (status))
+			return status;
+
+		    status = _cairo_polygon_add_external_edge (stroker->polygon,
+							 &points[0], inpt);
+		    if (unlikely (status))
+			return status;
+		}
+
+		return CAIRO_STATUS_SUCCESS;
+	    }
+	}
+    }
+
+    /* fall through ... */
+
+    case CAIRO_LINE_JOIN_BEVEL:
+	if (clockwise) {
+	    return _cairo_polygon_add_external_edge (stroker->polygon,
+					       inpt, outpt);
+	} else {
+	    return _cairo_polygon_add_external_edge (stroker->polygon,
+					       outpt, inpt);
+	}
+    }
+}
+
+static cairo_status_t
+_cairo_stroker_add_cap (struct stroker *stroker,
+			const cairo_stroke_face_t *f)
+{
+    switch (stroker->style.line_cap) {
+    case CAIRO_LINE_CAP_ROUND: {
+	cairo_slope_t slope;
+
+	slope.dx = -f->dev_vector.dx;
+	slope.dy = -f->dev_vector.dy;
+
+	return _tessellate_fan (stroker,
+				&f->dev_vector,
+				&slope,
+				&f->point, &f->cw, &f->ccw,
+				FALSE);
+
+    }
+
+    case CAIRO_LINE_CAP_SQUARE: {
+	double dx, dy;
+	cairo_slope_t	fvector;
+	cairo_point_t	quad[4];
+
+	dx = f->usr_vector.x;
+	dy = f->usr_vector.y;
+	dx *= stroker->style.line_width / 2.0;
+	dy *= stroker->style.line_width / 2.0;
+	cairo_matrix_transform_distance (stroker->ctm, &dx, &dy);
+	fvector.dx = _cairo_fixed_from_double (dx);
+	fvector.dy = _cairo_fixed_from_double (dy);
+
+	quad[0] = f->ccw;
+	quad[1].x = f->ccw.x + fvector.dx;
+	quad[1].y = f->ccw.y + fvector.dy;
+	quad[2].x = f->cw.x + fvector.dx;
+	quad[2].y = f->cw.y + fvector.dy;
+	quad[3] = f->cw;
+
+	{
+	    cairo_status_t status;
+
+	    status = _cairo_polygon_add_external_edge (stroker->polygon,
+						 &quad[0], &quad[1]);
+	    if (unlikely (status))
+		return status;
+
+	    status = _cairo_polygon_add_external_edge (stroker->polygon,
+						 &quad[1], &quad[2]);
+	    if (unlikely (status))
+		return status;
+
+	    status = _cairo_polygon_add_external_edge (stroker->polygon,
+						 &quad[2], &quad[3]);
+	    if (unlikely (status))
+		return status;
+
+	    return CAIRO_STATUS_SUCCESS;
+	}
+    }
+
+    case CAIRO_LINE_CAP_BUTT:
+    default:
+	return _cairo_polygon_add_external_edge (stroker->polygon,
+					   &f->ccw, &f->cw);
+    }
+}
+
+static cairo_status_t
+_cairo_stroker_add_leading_cap (struct stroker *stroker,
+				const cairo_stroke_face_t *face)
+{
+    cairo_stroke_face_t reversed;
+    cairo_point_t t;
+
+    reversed = *face;
+
+    /* The initial cap needs an outward facing vector. Reverse everything */
+    reversed.usr_vector.x = -reversed.usr_vector.x;
+    reversed.usr_vector.y = -reversed.usr_vector.y;
+    reversed.dev_vector.dx = -reversed.dev_vector.dx;
+    reversed.dev_vector.dy = -reversed.dev_vector.dy;
+    t = reversed.cw;
+    reversed.cw = reversed.ccw;
+    reversed.ccw = t;
+
+    return _cairo_stroker_add_cap (stroker, &reversed);
+}
+
+static cairo_status_t
+_cairo_stroker_add_trailing_cap (struct stroker *stroker,
+				 const cairo_stroke_face_t *face)
+{
+    return _cairo_stroker_add_cap (stroker, face);
+}
+
+static inline void
+normalized_device_slope (double *dx, double *dy,
+			 const cairo_matrix_t *ctm_inverse)
+{
+    double dx0 = *dx, dy0 = *dy;
+    double mag;
+
+    cairo_matrix_transform_distance (ctm_inverse, &dx0, &dy0);
+
+    assert (dx0 != 0.0 || dy0 != 0.0);
+
+    if (dx0 == 0.0) {
+	*dx = 0.0;
+	if (dy0 > 0.0) {
+	    mag = dy0;
+	    *dy = 1.0;
+	} else {
+	    mag = -dy0;
+	    *dy = -1.0;
+	}
+    } else if (dy0 == 0.0) {
+	*dy = 0.0;
+	if (dx0 > 0.0) {
+	    mag = dx0;
+	    *dx = 1.0;
+	} else {
+	    mag = -dx0;
+	    *dx = -1.0;
+	}
+    } else {
+	mag = hypot (dx0, dy0);
+	*dx = dx0 / mag;
+	*dy = dy0 / mag;
+    }
+}
+
+static void
+compute_face (const cairo_point_t *point, cairo_slope_t *dev_slope,
+	      double slope_dx, double slope_dy,
+	      struct stroker *stroker, cairo_stroke_face_t *face)
+{
+    double face_dx, face_dy;
+    cairo_point_t offset_ccw, offset_cw;
+
+    /*
+     * rotate to get a line_width/2 vector along the face, note that
+     * the vector must be rotated the right direction in device space,
+     * but by 90° in user space. So, the rotation depends on
+     * whether the ctm reflects or not, and that can be determined
+     * by looking at the determinant of the matrix.
+     */
+    if (stroker->ctm_det_positive)
+    {
+	face_dx = - slope_dy * (stroker->style.line_width / 2.0);
+	face_dy = slope_dx * (stroker->style.line_width / 2.0);
+    }
+    else
+    {
+	face_dx = slope_dy * (stroker->style.line_width / 2.0);
+	face_dy = - slope_dx * (stroker->style.line_width / 2.0);
+    }
+
+    /* back to device space */
+    cairo_matrix_transform_distance (stroker->ctm, &face_dx, &face_dy);
+
+    offset_ccw.x = _cairo_fixed_from_double (face_dx);
+    offset_ccw.y = _cairo_fixed_from_double (face_dy);
+    offset_cw.x = -offset_ccw.x;
+    offset_cw.y = -offset_ccw.y;
+
+    face->ccw = *point;
+    translate_point (&face->ccw, &offset_ccw);
+
+    face->point = *point;
+
+    face->cw = *point;
+    translate_point (&face->cw, &offset_cw);
+
+    face->usr_vector.x = slope_dx;
+    face->usr_vector.y = slope_dy;
+
+    face->dev_vector = *dev_slope;
+}
+
+static cairo_status_t
+_cairo_stroker_add_caps (struct stroker *stroker)
+{
+    cairo_status_t status;
+
+    /* check for a degenerative sub_path */
+    if (stroker->has_initial_sub_path
+	&& ! stroker->has_first_face
+	&& ! stroker->has_current_face
+	&& stroker->style.line_cap == CAIRO_LINE_CAP_ROUND)
+    {
+	/* pick an arbitrary slope to use */
+	double dx = 1.0, dy = 0.0;
+	cairo_slope_t slope = { CAIRO_FIXED_ONE, 0 };
+	cairo_stroke_face_t face;
+
+	normalized_device_slope (&dx, &dy, stroker->ctm_inverse);
+
+	/* arbitrarily choose first_point
+	 * first_point and current_point should be the same */
+	compute_face (&stroker->first_point, &slope, dx, dy, stroker, &face);
+
+	status = _cairo_stroker_add_leading_cap (stroker, &face);
+	if (unlikely (status))
+	    return status;
+
+	status = _cairo_stroker_add_trailing_cap (stroker, &face);
+	if (unlikely (status))
+	    return status;
+    }
+
+    if (stroker->has_first_face) {
+	status = _cairo_stroker_add_leading_cap (stroker,
+						 &stroker->first_face);
+	if (unlikely (status))
+	    return status;
+    }
+
+    if (stroker->has_current_face) {
+	status = _cairo_stroker_add_trailing_cap (stroker,
+						  &stroker->current_face);
+	if (unlikely (status))
+	    return status;
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+add_segment (struct stroker *stroker,
+	      const cairo_point_t *p1,
+	      const cairo_point_t *p2,
+	      cairo_slope_t *dev_slope,
+	      double slope_dx, double slope_dy,
+	      cairo_stroke_face_t *start,
+	      cairo_stroke_face_t *end)
+{
+    cairo_status_t status;
+
+    if (p1->y == p2->y)
+	return CAIRO_STATUS_SUCCESS;
+
+    compute_face (p1, dev_slope, slope_dx, slope_dy, stroker, start);
+    *end = *start;
+
+    end->point = *p2;
+    end->ccw.x += p2->x - p1->x;
+    end->ccw.y += p2->y - p1->y;
+    end->cw.x += p2->x - p1->x;
+    end->cw.y += p2->y - p1->y;
+
+    status = _cairo_polygon_add_external_edge (stroker->polygon,
+					       &end->cw, &start->cw);
+    if (unlikely (status))
+	return status;
+
+    status = _cairo_polygon_add_external_edge (stroker->polygon,
+					       &start->ccw, &end->ccw);
+    if (unlikely (status))
+	return status;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+move_to (void *closure,
+	 const cairo_point_t *point)
+{
+    struct stroker *stroker = closure;
+    cairo_status_t status;
+
+    /* Cap the start and end of the previous sub path as needed */
+    status = _cairo_stroker_add_caps (stroker);
+    if (unlikely (status))
+	return status;
+
+    stroker->first_point = *point;
+    stroker->current_point = *point;
+
+    stroker->has_first_face = FALSE;
+    stroker->has_current_face = FALSE;
+    stroker->has_initial_sub_path = FALSE;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+line_to (void *closure,
+	 const cairo_point_t *point)
+{
+    struct stroker *stroker = closure;
+    cairo_stroke_face_t start, end;
+    cairo_point_t *p1 = &stroker->current_point;
+    cairo_slope_t dev_slope;
+    double slope_dx, slope_dy;
+    cairo_status_t status;
+
+    stroker->has_initial_sub_path = TRUE;
+
+    if (p1->x == point->x && p1->y == point->y)
+	return CAIRO_STATUS_SUCCESS;
+
+    _cairo_slope_init (&dev_slope, p1, point);
+    slope_dx = _cairo_fixed_to_double (point->x - p1->x);
+    slope_dy = _cairo_fixed_to_double (point->y - p1->y);
+    normalized_device_slope (&slope_dx, &slope_dy, stroker->ctm_inverse);
+
+    status = add_segment (stroker, p1, point, &dev_slope,
+			  slope_dx, slope_dy,
+			  &start, &end);
+    if (unlikely (status))
+	return status;
+
+    if (stroker->has_current_face) {
+	/* Join with final face from previous segment */
+	status = join (stroker, &stroker->current_face, &start);
+	if (unlikely (status))
+	    return status;
+    } else if (! stroker->has_first_face) {
+	/* Save sub path's first face in case needed for closing join */
+	stroker->first_face = start;
+	stroker->has_first_face = TRUE;
+    }
+    stroker->current_face = end;
+    stroker->has_current_face = TRUE;
+
+    stroker->current_point = *point;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+curve_to (void *closure,
+	  const cairo_point_t *b,
+	  const cairo_point_t *c,
+	  const cairo_point_t *d)
+{
+    struct stroker *stroker = closure;
+    cairo_spline_t spline;
+    cairo_line_join_t line_join_save;
+    cairo_stroke_face_t face;
+    double slope_dx, slope_dy;
+    cairo_status_t status = CAIRO_STATUS_SUCCESS;
+
+    if (! _cairo_spline_init (&spline, line_to, stroker,
+			      &stroker->current_point, b, c, d))
+    {
+	return line_to (closure, d);
+    }
+
+    /* Compute the initial face */
+    slope_dx = _cairo_fixed_to_double (spline.initial_slope.dx);
+    slope_dy = _cairo_fixed_to_double (spline.initial_slope.dy);
+    normalized_device_slope (&slope_dx, &slope_dy, stroker->ctm_inverse);
+    compute_face (&stroker->current_point,
+		  &spline.initial_slope,
+		  slope_dx, slope_dy,
+		  stroker, &face);
+
+    if (stroker->has_current_face) {
+	status = join (stroker, &stroker->current_face, &face);
+	if (unlikely (status))
+	    return status;
+    } else if (! stroker->has_first_face) {
+	stroker->first_face = face;
+	stroker->has_first_face = TRUE;
+    }
+
+    stroker->current_face = face;
+    stroker->has_current_face = TRUE;
+
+    /* Temporarily modify the stroker to use round joins to guarantee
+     * smooth stroked curves. */
+    line_join_save = stroker->style.line_join;
+    stroker->style.line_join = CAIRO_LINE_JOIN_ROUND;
+
+    status = _cairo_spline_decompose (&spline, stroker->tolerance);
+    if (unlikely (status))
+	return status;
+
+    /* And join the final face */
+    slope_dx = _cairo_fixed_to_double (spline.final_slope.dx);
+    slope_dy = _cairo_fixed_to_double (spline.final_slope.dy);
+    normalized_device_slope (&slope_dx, &slope_dy, stroker->ctm_inverse);
+    compute_face (&stroker->current_point,
+		  &spline.final_slope,
+		  slope_dx, slope_dy,
+		  stroker, &face);
+
+    status = join (stroker, &stroker->current_face, &face);
+    if (unlikely (status))
+	return status;
+
+    stroker->current_face = face;
+
+    stroker->style.line_join = line_join_save;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+close_path (void *closure)
+{
+    struct stroker *stroker = closure;
+    cairo_status_t status;
+
+    status = line_to (stroker, &stroker->first_point);
+    if (unlikely (status))
+	return status;
+
+    if (stroker->has_first_face && stroker->has_current_face) {
+	/* Join first and final faces of sub path */
+	status = join (stroker, &stroker->current_face, &stroker->first_face);
+	if (unlikely (status))
+	    return status;
+    } else {
+	/* Cap the start and end of the sub path as needed */
+	status = _cairo_stroker_add_caps (stroker);
+	if (unlikely (status))
+	    return status;
+    }
+
+    stroker->has_initial_sub_path = FALSE;
+    stroker->has_first_face = FALSE;
+    stroker->has_current_face = FALSE;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_status_t
+_cairo_path_fixed_stroke_to_polygon (const cairo_path_fixed_t	*path,
+				     const cairo_stroke_style_t	*style,
+				     const cairo_matrix_t	*ctm,
+				     const cairo_matrix_t	*ctm_inverse,
+				     double		 tolerance,
+				     cairo_polygon_t *polygon)
+{
+    struct stroker stroker;
+    cairo_status_t status;
+
+    if (style->num_dashes) {
+	return _cairo_path_fixed_stroke_dashed_to_polygon (path,
+							   style,
+							   ctm,
+							   ctm_inverse,
+							   tolerance,
+							   polygon);
+    }
+
+    stroker.style = *style;
+    stroker.ctm = ctm;
+    stroker.ctm_inverse = ctm_inverse;
+    stroker.tolerance = tolerance;
+
+    stroker.ctm_determinant = _cairo_matrix_compute_determinant (ctm);
+    stroker.ctm_det_positive = stroker.ctm_determinant >= 0.0;
+
+    status = _cairo_pen_init (&stroker.pen,
+		              style->line_width / 2.0,
+			      tolerance, ctm);
+    if (unlikely (status))
+	return status;
+
+    /* If the line width is so small that the pen is reduced to a
+       single point, then we have nothing to do. */
+    if (stroker.pen.num_vertices <= 1)
+	return CAIRO_STATUS_SUCCESS;
+
+    stroker.has_current_face = FALSE;
+    stroker.has_first_face = FALSE;
+    stroker.has_initial_sub_path = FALSE;
+
+    stroker.polygon = polygon;
+
+    status = _cairo_path_fixed_interpret (path,
+					  move_to,
+					  line_to,
+					  curve_to,
+					  close_path,
+					  &stroker);
+    /* Cap the start and end of the final sub path as needed */
+    if (likely (status == CAIRO_STATUS_SUCCESS))
+	status = _cairo_stroker_add_caps (&stroker);
+
+    _cairo_pen_fini (&stroker.pen);
+
+    return status;
+}
diff --git a/src/cairo-path-stroke.c b/src/cairo-path-stroke.c
index a182e65..fcbc8e1 100644
--- a/src/cairo-path-stroke.c
+++ b/src/cairo-path-stroke.c
@@ -1274,12 +1274,12 @@ BAIL:
 }
 
 cairo_status_t
-_cairo_path_fixed_stroke_to_polygon (const cairo_path_fixed_t	*path,
-				     const cairo_stroke_style_t	*stroke_style,
-				     const cairo_matrix_t	*ctm,
-				     const cairo_matrix_t	*ctm_inverse,
-				     double		 tolerance,
-				     cairo_polygon_t *polygon)
+_cairo_path_fixed_stroke_dashed_to_polygon (const cairo_path_fixed_t	*path,
+					    const cairo_stroke_style_t	*stroke_style,
+					    const cairo_matrix_t	*ctm,
+					    const cairo_matrix_t	*ctm_inverse,
+					    double		 tolerance,
+					    cairo_polygon_t *polygon)
 {
     cairo_stroker_t stroker;
     cairo_status_t status;
diff --git a/src/cairoint.h b/src/cairoint.h
index 50f99c9..f745f41 100644
--- a/src/cairoint.h
+++ b/src/cairoint.h
@@ -1403,6 +1403,14 @@ _cairo_path_fixed_stroke_to_polygon (const cairo_path_fixed_t	*path,
 				     double		 tolerance,
 				     cairo_polygon_t	*polygon);
 
+cairo_private cairo_status_t
+_cairo_path_fixed_stroke_dashed_to_polygon (const cairo_path_fixed_t	*path,
+					    const cairo_stroke_style_t	*stroke_style,
+					    const cairo_matrix_t	*ctm,
+					    const cairo_matrix_t	*ctm_inverse,
+					    double		 tolerance,
+					    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,
commit b8c353f6d39a49cec3ce043fe057f3112ccc9a53
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Thu Aug 4 11:59:08 2011 +0100

    rectangular-bug-fix

diff --git a/src/cairo-clip-private.h b/src/cairo-clip-private.h
index 5302471..9aa8f49 100644
--- a/src/cairo-clip-private.h
+++ b/src/cairo-clip-private.h
@@ -40,6 +40,7 @@
 #include "cairo-types-private.h"
 
 #include "cairo-boxes-private.h"
+#include "cairo-error-private.h"
 #include "cairo-compiler-private.h"
 #include "cairo-path-fixed-private.h"
 #include "cairo-reference-count-private.h"
diff --git a/src/cairo-image-surface.c b/src/cairo-image-surface.c
index 4302868..9ba0d7b 100644
--- a/src/cairo-image-surface.c
+++ b/src/cairo-image-surface.c
@@ -817,7 +817,7 @@ _cairo_image_surface_release_source_image (void                   *abstract_surf
 static pixman_op_t
 _pixman_operator (cairo_operator_t op)
 {
-    switch (op) {
+    switch ((int) op) {
     case CAIRO_OPERATOR_CLEAR:
 	return PIXMAN_OP_CLEAR;
 
@@ -2752,11 +2752,12 @@ _cairo_image_clipped_spans (void *abstract_renderer,
     assert (num_spans);
 
     do {
-	if (! spans[0].is_clipped)
+	if (! spans[0].is_clipped) {
 	    pixman_image_compositor_blt (r->compositor,
 					 spans[0].x, y,
 					 spans[1].x - spans[0].x, height,
 					 r->opacity * spans[0].coverage);
+	}
 	spans++;
     } while (--num_spans > 1);
 
@@ -2809,6 +2810,7 @@ _cairo_image_span_renderer_init (cairo_image_span_renderer_t *r,
 	else
 	    r->base.render_rows = _cairo_image_bounded_spans;
 	r->base.finish = NULL;
+	r->extents = composite->bounded;
     } else {
 	if (needs_clip)
 	    r->base.render_rows = _cairo_image_clipped_spans;
@@ -2816,9 +2818,9 @@ _cairo_image_span_renderer_init (cairo_image_span_renderer_t *r,
 	    r->base.render_rows = _cairo_image_unbounded_spans;
         r->base.finish =      _cairo_image_finish_unbounded_spans;
 	r->extents = composite->unbounded;
-	r->extents.height += r->extents.y;
-
     }
+    r->extents.height += r->extents.y;
+
     r->compositor =
 	pixman_image_create_compositor (_pixman_operator (op),
 					r->src, NULL, dst->pixman_image,
diff --git a/src/cairo-rectangular-scan-converter.c b/src/cairo-rectangular-scan-converter.c
index 1922364..68040a2 100644
--- a/src/cairo-rectangular-scan-converter.c
+++ b/src/cairo-rectangular-scan-converter.c
@@ -493,13 +493,13 @@ generate (cairo_rectangular_scan_converter_t *self,
     cairo_status_t status;
 
     sweep_line_init (&sweep_line);
-    sweep_line.xmin = self->xmin;
-    sweep_line.xmax = self->xmax;
+    sweep_line.xmin = _cairo_fixed_integer_part (self->extents.p1.x);
+    sweep_line.xmax = _cairo_fixed_integer_part (self->extents.p2.x);
     sweep_line.start = rectangles;
     if ((status = setjmp (sweep_line.jmpbuf)))
 	goto BAIL;
 
-    sweep_line.current_y = self->ymin;
+    sweep_line.current_y = _cairo_fixed_integer_part (self->extents.p1.y);
     start = *sweep_line.start++;
     do {
 	if (start->top_y != sweep_line.current_y) {
@@ -582,7 +582,7 @@ generate (cairo_rectangular_scan_converter_t *self,
   out:
     status =  renderer->render_rows (renderer,
 				     sweep_line.current_y,
-				     self->ymax - sweep_line.current_y,
+				     _cairo_fixed_integer_part (self->extents.p2.y) - sweep_line.current_y,
 				     NULL, 0);
 
   BAIL:
@@ -615,18 +615,18 @@ static void generate_row(cairo_span_renderer_t *renderer,
 	}
 
 	if (! _cairo_fixed_is_integer (r->right)) {
-	    spans[num_spans].x = x2;
+	    spans[num_spans].x = x2++;
 	    spans[num_spans].coverage =
 		coverage * _cairo_fixed_fractional_part (r->right) >> 8;
 	    num_spans++;
 	}
     } else {
-	spans[num_spans].x = x1;
+	spans[num_spans].x = x2++;
 	spans[num_spans].coverage = coverage * (r->right - r->left) >> 8;
 	num_spans++;
     }
 
-    spans[num_spans].x = x2 + 1;
+    spans[num_spans].x = x2;
     spans[num_spans].coverage = 0;
     num_spans++;
 
@@ -672,7 +672,8 @@ _cairo_rectangular_scan_converter_generate (void			*converter,
 
     if (unlikely (self->num_rectangles == 0)) {
 	return renderer->render_rows (renderer,
-				      self->ymin, self->ymax - self->ymin,
+				      _cairo_fixed_integer_part (self->extents.p1.y),
+				      _cairo_fixed_integer_part (self->extents.p2.y - self->extents.p1.y),
 				      NULL, 0);
     }
 
@@ -747,17 +748,22 @@ _cairo_rectangular_scan_converter_add_box (cairo_rectangular_scan_converter_t *s
     if (unlikely (rectangle == NULL))
 	return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
-    rectangle->left  = box->p1.x;
-    rectangle->right = box->p2.x;
     rectangle->dir = dir;
+    rectangle->left  = MAX (box->p1.x, self->extents.p1.x);
+    rectangle->right = MIN (box->p2.x, self->extents.p2.x);
+    if (unlikely (rectangle->right <= rectangle->left)) {
+	self->tail->count--;
+	return CAIRO_STATUS_SUCCESS;
+    }
 
-    rectangle->top = box->p1.y;
-    rectangle->top_y  = _cairo_fixed_integer_floor (box->p1.y);
-    rectangle->bottom = box->p2.y;
-    rectangle->bottom_y = _cairo_fixed_integer_floor (box->p2.y);
-    assert (rectangle->bottom_y >= rectangle->top_y);
-
-    self->num_rectangles++;
+    rectangle->top = MAX (box->p1.y, self->extents.p1.y);
+    rectangle->top_y  = _cairo_fixed_integer_floor (rectangle->top);
+    rectangle->bottom = MIN (box->p2.y, self->extents.p2.y);
+    rectangle->bottom_y = _cairo_fixed_integer_floor (rectangle->bottom);
+    if (likely (rectangle->bottom_y > rectangle->top_y))
+	self->num_rectangles++;
+    else
+	self->tail->count--;
 
     return CAIRO_STATUS_SUCCESS;
 }
@@ -783,10 +789,7 @@ _cairo_rectangular_scan_converter_init (cairo_rectangular_scan_converter_t *self
     self->base.add_polygon = NULL;
     self->base.generate = _cairo_rectangular_scan_converter_generate;
 
-    self->xmin = extents->x;
-    self->xmax = extents->x + extents->width;
-    self->ymin = extents->y;
-    self->ymax = extents->y + extents->height;
+    _cairo_box_from_rectangle (&self->extents, extents);
 
     self->chunks.base = self->buf;
     self->chunks.next = NULL;
diff --git a/src/cairo-spans-private.h b/src/cairo-spans-private.h
index 3acfd48..52ed1d5 100644
--- a/src/cairo-spans-private.h
+++ b/src/cairo-spans-private.h
@@ -140,8 +140,7 @@ _cairo_tor33_scan_converter_reset (void *converter);
 typedef struct _cairo_rectangular_scan_converter {
     cairo_scan_converter_t base;
 
-    int xmin, xmax;
-    int ymin, ymax;
+    cairo_box_t extents;
 
     struct _cairo_rectangular_scan_converter_chunk {
 	struct _cairo_rectangular_scan_converter_chunk *next;
commit fdb879b8f5f167fcf8b161ca934774cc166f2665
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Thu Aug 4 00:19:42 2011 +0100

    image: move surface definition to new header for subclassing
    
    Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>

diff --git a/src/Makefile.sources b/src/Makefile.sources
index fb67e5b..28c9d13 100644
--- a/src/Makefile.sources
+++ b/src/Makefile.sources
@@ -75,6 +75,7 @@ cairo_private = \
 	cairo-gstate-private.h \
 	cairo-hash-private.h \
 	cairo-image-info-private.h \
+	cairo-image-surface-private.h \
 	cairo-list-private.h \
 	cairo-malloc-private.h \
 	cairo-mutex-impl-private.h \
diff --git a/src/cairo-debug.c b/src/cairo-debug.c
index ca94e2b..6afdb3f 100644
--- a/src/cairo-debug.c
+++ b/src/cairo-debug.c
@@ -34,6 +34,7 @@
  */
 
 #include "cairoint.h"
+#include "cairo-image-surface-private.h"
 
 /**
  * cairo_debug_reset_static_data:
diff --git a/src/cairo-fast-image-surface.c b/src/cairo-fast-image-surface.c
index ab27c74..de1af94 100644
--- a/src/cairo-fast-image-surface.c
+++ b/src/cairo-fast-image-surface.c
@@ -45,6 +45,7 @@
 #include "cairo-default-context-private.h"
 #include "cairo-error-private.h"
 #include "cairo-gstate-private.h"
+#include "cairo-image-surface-private.h"
 #include "cairo-paginated-private.h"
 #include "cairo-pattern-private.h"
 #include "cairo-recording-surface-private.h"
diff --git a/src/cairo-ft-font.c b/src/cairo-ft-font.c
index 7f5f6d0..8ef37aa 100644
--- a/src/cairo-ft-font.c
+++ b/src/cairo-ft-font.c
@@ -42,6 +42,7 @@
 #include "cairoint.h"
 
 #include "cairo-error-private.h"
+#include "cairo-image-surface-private.h"
 #include "cairo-ft-private.h"
 #include "cairo-pattern-private.h"
 
diff --git a/src/cairo-image-surface-private.h b/src/cairo-image-surface-private.h
new file mode 100644
index 0000000..97745af
--- /dev/null
+++ b/src/cairo-image-surface-private.h
@@ -0,0 +1,84 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2002 University of Southern California
+ * Copyright © 2005 Red Hat, Inc.
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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>
+ */
+
+#ifndef CAIRO_IMAGE_SURFACE_PRIVATE_H
+#define CAIRO_IMAGE_SURFACE_PRIVATE_H
+
+#include "cairo-surface-private.h"
+
+CAIRO_BEGIN_DECLS
+
+struct _cairo_image_surface {
+    cairo_surface_t base;
+
+    pixman_format_code_t pixman_format;
+    cairo_format_t format;
+    unsigned char *data;
+
+    int width;
+    int height;
+    int stride;
+    int depth;
+
+    pixman_image_t *pixman_image;
+
+    unsigned owns_data : 1;
+    unsigned transparency : 2;
+    unsigned color : 2;
+};
+
+extern const cairo_private cairo_surface_backend_t _cairo_image_surface_backend;
+
+cairo_private void
+_cairo_image_surface_init (cairo_image_surface_t *surface,
+			   pixman_image_t	*pixman_image,
+			   pixman_format_code_t	 pixman_format);
+
+cairo_private_no_warn cairo_bool_t
+_cairo_image_surface_get_extents (void			  *abstract_surface,
+				  cairo_rectangle_int_t   *rectangle);
+
+cairo_private void
+_cairo_image_surface_get_font_options (void                  *abstract_surface,
+				       cairo_font_options_t  *options);
+
+cairo_private cairo_status_t
+_cairo_image_surface_finish (void *abstract_surface);
+
+CAIRO_END_DECLS
+
+#endif /* CAIRO_IMAGE_SURFACE_PRIVATE_H */
diff --git a/src/cairo-image-surface.c b/src/cairo-image-surface.c
index 2989953..4302868 100644
--- a/src/cairo-image-surface.c
+++ b/src/cairo-image-surface.c
@@ -44,6 +44,7 @@
 #include "cairo-composite-rectangles-private.h"
 #include "cairo-default-context-private.h"
 #include "cairo-error-private.h"
+#include "cairo-image-surface-private.h"
 #include "cairo-paginated-private.h"
 #include "cairo-pattern-private.h"
 #include "cairo-recording-surface-private.h"
@@ -144,13 +145,33 @@ _cairo_content_from_pixman_format (pixman_format_code_t pixman_format)
     return content;
 }
 
+void
+_cairo_image_surface_init (cairo_image_surface_t *surface,
+			   pixman_image_t	*pixman_image,
+			   pixman_format_code_t	 pixman_format)
+{
+    surface->pixman_image = pixman_image;
+
+    surface->pixman_format = pixman_format;
+    surface->format = _cairo_format_from_pixman_format (pixman_format);
+    surface->data = (uint8_t *) pixman_image_get_data (pixman_image);
+    surface->owns_data = FALSE;
+    surface->transparency = CAIRO_IMAGE_UNKNOWN;
+    surface->color = CAIRO_IMAGE_UNKNOWN_COLOR;
+
+    surface->width = pixman_image_get_width (pixman_image);
+    surface->height = pixman_image_get_height (pixman_image);
+    surface->stride = pixman_image_get_stride (pixman_image);
+    surface->depth = pixman_image_get_depth (pixman_image);
+
+    surface->base.is_clear = surface->width == 0 || surface->height == 0;
+}
+
 cairo_surface_t *
 _cairo_image_surface_create_for_pixman_image (pixman_image_t		*pixman_image,
 					      pixman_format_code_t	 pixman_format)
 {
     cairo_image_surface_t *surface;
-    int width = pixman_image_get_width (pixman_image);
-    int height = pixman_image_get_height (pixman_image);
 
     surface = malloc (sizeof (cairo_image_surface_t));
     if (unlikely (surface == NULL))
@@ -161,21 +182,7 @@ _cairo_image_surface_create_for_pixman_image (pixman_image_t		*pixman_image,
 			 NULL, /* device */
 			 _cairo_content_from_pixman_format (pixman_format));
 
-    surface->pixman_image = pixman_image;
-
-    surface->pixman_format = pixman_format;
-    surface->format = _cairo_format_from_pixman_format (pixman_format);
-    surface->data = (uint8_t *) pixman_image_get_data (pixman_image);
-    surface->owns_data = FALSE;
-    surface->transparency = CAIRO_IMAGE_UNKNOWN;
-    surface->color = CAIRO_IMAGE_UNKNOWN_COLOR;
-
-    surface->width = width;
-    surface->height = height;
-    surface->stride = pixman_image_get_stride (pixman_image);
-    surface->depth = pixman_image_get_depth (pixman_image);
-
-    surface->base.is_clear = width == 0 || height == 0;
+    _cairo_image_surface_init (surface, pixman_image, pixman_format);
 
     return &surface->base;
 }
@@ -760,7 +767,7 @@ _cairo_image_surface_unmap_image (void *abstract_surface,
     return CAIRO_INT_STATUS_SUCCESS;
 }
 
-static cairo_status_t
+cairo_status_t
 _cairo_image_surface_finish (void *abstract_surface)
 {
     cairo_image_surface_t *surface = abstract_surface;
@@ -3396,7 +3403,7 @@ _clip_and_composite_trapezoids (cairo_image_surface_t *dst,
 }
 
 /* high level image interface */
-static cairo_bool_t
+cairo_bool_t
 _cairo_image_surface_get_extents (void			  *abstract_surface,
 				  cairo_rectangle_int_t   *rectangle)
 {
@@ -4155,7 +4162,7 @@ _cairo_image_surface_glyphs (void			*abstract_surface,
     return status;
 }
 
-static void
+void
 _cairo_image_surface_get_font_options (void                  *abstract_surface,
 				       cairo_font_options_t  *options)
 {
diff --git a/src/cairo-mime-surface.c b/src/cairo-mime-surface.c
index d1ec974..8fc3714 100644
--- a/src/cairo-mime-surface.c
+++ b/src/cairo-mime-surface.c
@@ -45,6 +45,7 @@
 
 #include "cairoint.h"
 #include "cairo-error-private.h"
+#include "cairo-image-surface-private.h"
 
 typedef struct _cairo_mime_surface {
     cairo_surface_t base;
diff --git a/src/cairo-paginated-surface.c b/src/cairo-paginated-surface.c
index 4d93642..61c2815 100644
--- a/src/cairo-paginated-surface.c
+++ b/src/cairo-paginated-surface.c
@@ -49,6 +49,7 @@
 #include "cairo-recording-surface-private.h"
 #include "cairo-analysis-surface-private.h"
 #include "cairo-error-private.h"
+#include "cairo-image-surface-private.h"
 
 static const cairo_surface_backend_t cairo_paginated_surface_backend;
 
diff --git a/src/cairo-pattern.c b/src/cairo-pattern.c
index 8c33e4c..581cc2b 100644
--- a/src/cairo-pattern.c
+++ b/src/cairo-pattern.c
@@ -33,6 +33,7 @@
 #include "cairo-freed-pool-private.h"
 #include "cairo-path-private.h"
 #include "cairo-pattern-private.h"
+#include "cairo-image-surface-private.h"
 
 #include <float.h>
 
diff --git a/src/cairo-pdf-surface.c b/src/cairo-pdf-surface.c
index 01ebcf1..0af638f 100644
--- a/src/cairo-pdf-surface.c
+++ b/src/cairo-pdf-surface.c
@@ -49,6 +49,7 @@
 #include "cairo-composite-rectangles-private.h"
 #include "cairo-default-context-private.h"
 #include "cairo-error-private.h"
+#include "cairo-image-surface-private.h"
 #include "cairo-image-info-private.h"
 #include "cairo-recording-surface-private.h"
 #include "cairo-output-stream-private.h"
diff --git a/src/cairo-png.c b/src/cairo-png.c
index 818b7b0..59bbe68 100644
--- a/src/cairo-png.c
+++ b/src/cairo-png.c
@@ -39,6 +39,7 @@
 #include "cairoint.h"
 
 #include "cairo-error-private.h"
+#include "cairo-image-surface-private.h"
 #include "cairo-output-stream-private.h"
 
 #include <stdio.h>
diff --git a/src/cairo-ps-surface.c b/src/cairo-ps-surface.c
index 96f5bcf..267fd59 100644
--- a/src/cairo-ps-surface.c
+++ b/src/cairo-ps-surface.c
@@ -62,6 +62,7 @@
 #include "cairo-composite-rectangles-private.h"
 #include "cairo-default-context-private.h"
 #include "cairo-error-private.h"
+#include "cairo-image-surface-private.h"
 #include "cairo-scaled-font-subsets-private.h"
 #include "cairo-paginated-private.h"
 #include "cairo-recording-surface-private.h"
diff --git a/src/cairo-recording-surface.c b/src/cairo-recording-surface.c
index 478695f..be803ec 100644
--- a/src/cairo-recording-surface.c
+++ b/src/cairo-recording-surface.c
@@ -83,6 +83,7 @@
 #include "cairo-composite-rectangles-private.h"
 #include "cairo-default-context-private.h"
 #include "cairo-error-private.h"
+#include "cairo-image-surface-private.h"
 #include "cairo-recording-surface-private.h"
 #include "cairo-surface-wrapper-private.h"
 
diff --git a/src/cairo-scaled-font.c b/src/cairo-scaled-font.c
index cb59bce..5b77546 100644
--- a/src/cairo-scaled-font.c
+++ b/src/cairo-scaled-font.c
@@ -40,6 +40,7 @@
 
 #include "cairoint.h"
 #include "cairo-error-private.h"
+#include "cairo-image-surface-private.h"
 #include "cairo-pattern-private.h"
 #include "cairo-scaled-font-private.h"
 
diff --git a/src/cairo-surface-fallback.c b/src/cairo-surface-fallback.c
index acbf799..b65b2bf 100644
--- a/src/cairo-surface-fallback.c
+++ b/src/cairo-surface-fallback.c
@@ -44,6 +44,7 @@
 #include "cairo-clip-private.h"
 #include "cairo-composite-rectangles-private.h"
 #include "cairo-error-private.h"
+#include "cairo-image-surface-private.h"
 #include "cairo-pattern-private.h"
 #include "cairo-region-private.h"
 #include "cairo-spans-private.h"
diff --git a/src/cairo-surface-snapshot.c b/src/cairo-surface-snapshot.c
index c88d015..56d108b 100644
--- a/src/cairo-surface-snapshot.c
+++ b/src/cairo-surface-snapshot.c
@@ -40,6 +40,7 @@
 #include "cairoint.h"
 
 #include "cairo-error-private.h"
+#include "cairo-image-surface-private.h"
 #include "cairo-surface-snapshot-private.h"
 
 static cairo_status_t
diff --git a/src/cairo-surface-subsurface.c b/src/cairo-surface-subsurface.c
index 29d9ef0..248c20c 100644
--- a/src/cairo-surface-subsurface.c
+++ b/src/cairo-surface-subsurface.c
@@ -36,6 +36,7 @@
 #include "cairoint.h"
 
 #include "cairo-error-private.h"
+#include "cairo-image-surface-private.h"
 #include "cairo-recording-surface-private.h"
 #include "cairo-surface-offset-private.h"
 #include "cairo-surface-subsurface-private.h"
diff --git a/src/cairo-surface.c b/src/cairo-surface.c
index 71f8aea..db127fb 100644
--- a/src/cairo-surface.c
+++ b/src/cairo-surface.c
@@ -42,6 +42,7 @@
 #include "cairo-clip-private.h"
 #include "cairo-device-private.h"
 #include "cairo-error-private.h"
+#include "cairo-image-surface-private.h"
 #include "cairo-recording-surface-private.h"
 #include "cairo-region-private.h"
 #include "cairo-tee-surface-private.h"
diff --git a/src/cairo-svg-surface.c b/src/cairo-svg-surface.c
index 66d0fc2..88acad2 100644
--- a/src/cairo-svg-surface.c
+++ b/src/cairo-svg-surface.c
@@ -46,6 +46,7 @@
 #include "cairo-default-context-private.h"
 #include "cairo-error-private.h"
 #include "cairo-image-info-private.h"
+#include "cairo-image-surface-private.h"
 #include "cairo-recording-surface-private.h"
 #include "cairo-output-stream-private.h"
 #include "cairo-path-fixed-private.h"
diff --git a/src/cairo-type3-glyph-surface.c b/src/cairo-type3-glyph-surface.c
index 93c3a6a..0755415 100644
--- a/src/cairo-type3-glyph-surface.c
+++ b/src/cairo-type3-glyph-surface.c
@@ -44,6 +44,7 @@
 #include "cairo-analysis-surface-private.h"
 #include "cairo-default-context-private.h"
 #include "cairo-error-private.h"
+#include "cairo-image-surface-private.h"
 #include "cairo-surface-clipper-private.h"
 
 static const cairo_surface_backend_t cairo_type3_glyph_surface_backend;
diff --git a/src/cairo-xcb-private.h b/src/cairo-xcb-private.h
index 150aab9..d9557b4 100644
--- a/src/cairo-xcb-private.h
+++ b/src/cairo-xcb-private.h
@@ -83,7 +83,7 @@ struct _cairo_xcb_shm_info {
 
 struct _cairo_xcb_surface {
     cairo_surface_t base;
-    cairo_image_surface_t *fallback;
+    cairo_surface_t *fallback;
 
     cairo_xcb_connection_t *connection;
     cairo_xcb_screen_t *screen;
diff --git a/src/cairo-xcb-surface-core.c b/src/cairo-xcb-surface-core.c
index c3c0953..0770dcd 100644
--- a/src/cairo-xcb-surface-core.c
+++ b/src/cairo-xcb-surface-core.c
@@ -33,6 +33,7 @@
 
 #include "cairo-boxes-private.h"
 #include "cairo-xcb-private.h"
+#include "cairo-image-surface-private.h"
 
 /* XXX dithering */
 
diff --git a/src/cairo-xcb-surface-render.c b/src/cairo-xcb-surface-render.c
index e08ca68..b42795e 100644
--- a/src/cairo-xcb-surface-render.c
+++ b/src/cairo-xcb-surface-render.c
@@ -34,6 +34,7 @@
 #include "cairo-boxes-private.h"
 #include "cairo-clip-private.h"
 #include "cairo-composite-rectangles-private.h"
+#include "cairo-image-surface-private.h"
 #include "cairo-region-private.h"
 #include "cairo-surface-offset-private.h"
 #include "cairo-surface-snapshot-private.h"
diff --git a/src/cairo-xcb-surface.c b/src/cairo-xcb-surface.c
index da35e75..abf8fe3 100644
--- a/src/cairo-xcb-surface.c
+++ b/src/cairo-xcb-surface.c
@@ -44,6 +44,7 @@
 #include "cairo-xcb-private.h"
 
 #include "cairo-default-context-private.h"
+#include "cairo-image-surface-private.h"
 
 #define XLIB_COORD_MAX 32767
 
@@ -222,8 +223,8 @@ _cairo_xcb_surface_finish (void *abstract_surface)
     cairo_status_t status;
 
     if (surface->fallback != NULL) {
-	cairo_surface_finish (&surface->fallback->base);
-	cairo_surface_destroy (&surface->fallback->base);
+	cairo_surface_finish (surface->fallback);
+	cairo_surface_destroy (surface->fallback);
     }
 
     cairo_list_del (&surface->link);
@@ -297,7 +298,7 @@ _cairo_xcb_surface_create_shm_image (cairo_xcb_connection_t *connection,
 }
 #endif
 
-static cairo_image_surface_t *
+static cairo_surface_t *
 _get_shm_image (cairo_xcb_surface_t *surface,
 		int x, int y,
 		int width, int height)
@@ -329,19 +330,19 @@ _get_shm_image (cairo_xcb_surface_t *surface,
     }
 
 done:
-    return (cairo_image_surface_t *) image;
+    return image;
 #else
-    return NULL;;
+    return NULL;
 #endif
 }
 
-static cairo_image_surface_t *
+static cairo_surface_t *
 _get_image (cairo_xcb_surface_t		 *surface,
 	    cairo_bool_t		  use_shm,
 	    int x, int y,
 	    int width, int height)
 {
-    cairo_image_surface_t *image;
+    cairo_surface_t *image;
     cairo_xcb_connection_t *connection;
     xcb_get_image_reply_t *reply;
     cairo_int_status_t status;
@@ -353,7 +354,7 @@ _get_image (cairo_xcb_surface_t		 *surface,
     assert (y + height <= surface->height);
 
     if (surface->deferred_clear) {
-	image = (cairo_image_surface_t *)
+	image =
 	    _cairo_image_surface_create_with_pixman_format (NULL,
 							    surface->pixman_format,
 							    width, height,
@@ -362,14 +363,13 @@ _get_image (cairo_xcb_surface_t		 *surface,
 	    cairo_solid_pattern_t solid;
 
 	    _cairo_pattern_init_solid (&solid, &surface->deferred_clear_color);
-	    status = _cairo_surface_paint (&image->base,
+	    status = _cairo_surface_paint (image,
 					   CAIRO_OPERATOR_SOURCE,
 					   &solid.base,
 					   NULL);
 	    if (unlikely (status)) {
-		cairo_surface_destroy (&image->base);
-		image = (cairo_image_surface_t *)
-		    _cairo_surface_create_in_error (status);
+		cairo_surface_destroy (image);
+		image = _cairo_surface_create_in_error (status);
 	    }
 	}
 	return image;
@@ -379,7 +379,7 @@ _get_image (cairo_xcb_surface_t		 *surface,
 
     status = _cairo_xcb_connection_acquire (connection);
     if (unlikely (status))
-	return (cairo_image_surface_t *) _cairo_surface_create_in_error (status);
+	return _cairo_surface_create_in_error (status);
 
     if (use_shm) {
 	image = _get_shm_image (surface, x, y, width, height);
@@ -450,22 +450,20 @@ _get_image (cairo_xcb_surface_t		 *surface,
     /* XXX format conversion */
     assert (reply->depth == surface->depth);
 
-    image = (cairo_image_surface_t *)
-	_cairo_image_surface_create_with_pixman_format
+    image = _cairo_image_surface_create_with_pixman_format
 	(xcb_get_image_data (reply),
 	 surface->pixman_format,
 	 width, height,
 	 CAIRO_STRIDE_FOR_WIDTH_BPP (width,
 				     PIXMAN_FORMAT_BPP (surface->pixman_format)));
-    status = image->base.status;
+    status = image->status;
     if (unlikely (status)) {
 	free (reply);
 	goto FAIL;
     }
 
-    assert (xcb_get_image_data_length (reply) == image->height * image->stride);
-
-    pixman_image_set_destroy_function (image->pixman_image, _destroy_image, reply);
+    /* XXX */
+    pixman_image_set_destroy_function (((cairo_image_surface_t *)image)->pixman_image, _destroy_image, reply);
 
     _cairo_xcb_connection_release (connection);
 
@@ -473,7 +471,7 @@ _get_image (cairo_xcb_surface_t		 *surface,
 
 FAIL:
     _cairo_xcb_connection_release (connection);
-    return (cairo_image_surface_t *) _cairo_surface_create_in_error (status);
+    return _cairo_surface_create_in_error (status);
 }
 
 static cairo_status_t
@@ -482,29 +480,28 @@ _cairo_xcb_surface_acquire_source_image (void *abstract_surface,
 					 void **image_extra)
 {
     cairo_xcb_surface_t *surface = abstract_surface;
-    cairo_image_surface_t *image;
+    cairo_surface_t *image;
 
     if (surface->fallback != NULL) {
-	image = (cairo_image_surface_t *) cairo_surface_reference (&surface->fallback->base);
+	image = cairo_surface_reference (surface->fallback);
 	goto DONE;
     }
 
-    image = (cairo_image_surface_t *)
-	_cairo_surface_has_snapshot (&surface->base,
-				     &_cairo_image_surface_backend);
+    image = _cairo_surface_has_snapshot (&surface->base,
+					 &_cairo_image_surface_backend);
     if (image != NULL) {
-	image = (cairo_image_surface_t *) cairo_surface_reference (&image->base);
+	image = cairo_surface_reference (image);
 	goto DONE;
     }
 
     image = _get_image (surface, FALSE, 0, 0, surface->width, surface->height);
-    if (unlikely (image->base.status))
-	return image->base.status;
+    if (unlikely (image->status))
+	return image->status;
 
-    _cairo_surface_attach_snapshot (&surface->base, &image->base, NULL);
+    _cairo_surface_attach_snapshot (&surface->base, image, NULL);
 
 DONE:
-    *image_out = image;
+    *image_out = (cairo_image_surface_t *) image;
     *image_extra = NULL;
     return CAIRO_STATUS_SUCCESS;
 }
@@ -629,20 +626,20 @@ _cairo_xcb_surface_flush (void *abstract_surface)
 
     status = surface->base.status;
     if (status == CAIRO_STATUS_SUCCESS && ! surface->base.finished) {
-	status = cairo_surface_status (&surface->fallback->base);
+	status = cairo_surface_status (surface->fallback);
 
 	if (status == CAIRO_STATUS_SUCCESS) {
-	    status = _put_image (surface, surface->fallback);
+	    status = _put_image (surface, (cairo_image_surface_t *)surface->fallback);
 	}
 
 	if (status == CAIRO_STATUS_SUCCESS) {
 	    _cairo_surface_attach_snapshot (&surface->base,
-					    &surface->fallback->base,
+					    surface->fallback,
 					    cairo_surface_finish);
 	}
     }
 
-    cairo_surface_destroy (&surface->fallback->base);
+    cairo_surface_destroy (surface->fallback);
     surface->fallback = NULL;
 
     return status;
@@ -653,16 +650,16 @@ _cairo_xcb_surface_map_to_image (void *abstract_surface,
 				 const cairo_rectangle_int_t *extents)
 {
     cairo_xcb_surface_t *surface = abstract_surface;
-    cairo_image_surface_t *image;
+    cairo_surface_t *image;
 
     if (surface->fallback)
-	return surface->fallback->base.backend->map_to_image (surface->fallback, extents);
+	return surface->fallback->backend->map_to_image (surface->fallback, extents);
 
     image = _get_image (surface, TRUE,
 			extents->x, extents->y,
 			extents->width, extents->height);
-    if (unlikely (image->base.status))
-	return &image->base;
+    if (unlikely (image->status))
+	return image;
 
     /* Do we have a deferred clear and this image surface does NOT cover the
      * whole xcb surface? Have to apply the clear in that case, else
@@ -675,14 +672,14 @@ _cairo_xcb_surface_map_to_image (void *abstract_surface,
 	       extents->height == surface->height)) {
 	cairo_status_t status = _cairo_xcb_surface_clear (surface);
 	if (unlikely (status)) {
-	    cairo_surface_destroy(&image->base);
+	    cairo_surface_destroy(image);
 	    return _cairo_surface_create_in_error (status);
 	}
     }
     surface->deferred_clear = FALSE;
 
-    cairo_surface_set_device_offset (&image->base, -extents->x, -extents->y);
-    return &image->base;
+    cairo_surface_set_device_offset (image, -extents->x, -extents->y);
+    return image;
 }
 
 static cairo_int_status_t
@@ -692,18 +689,19 @@ _cairo_xcb_surface_unmap (void *abstract_surface,
     cairo_xcb_surface_t *surface = abstract_surface;
 
     if (surface->fallback)
-	return surface->fallback->base.backend->unmap_image (surface->fallback, image);
+	return surface->fallback->backend->unmap_image (surface->fallback, image);
     return _put_image (abstract_surface, image);
 }
 
-static cairo_image_surface_t *
+static cairo_surface_t *
 _cairo_xcb_surface_fallback (cairo_xcb_surface_t *surface)
 {
-    cairo_image_surface_t *image;
+    cairo_surface_t *image;
+
     image = _get_image (surface, TRUE, 0, 0, surface->width, surface->height);
 
     /* If there was a deferred clear, _get_image applied it */
-    if (image->base.status == CAIRO_STATUS_SUCCESS)
+    if (image->status == CAIRO_STATUS_SUCCESS)
 	surface->deferred_clear = FALSE;
 
     return image;
@@ -730,7 +728,7 @@ _cairo_xcb_surface_paint (void			*abstract_surface,
 	surface->fallback = _cairo_xcb_surface_fallback (surface);
     }
 
-    return _cairo_surface_paint (&surface->fallback->base, op, source, clip);
+    return _cairo_surface_paint (surface->fallback, op, source, clip);
 }
 
 static cairo_int_status_t
@@ -757,7 +755,7 @@ _cairo_xcb_surface_mask (void			*abstract_surface,
 	surface->fallback = _cairo_xcb_surface_fallback (surface);
     }
 
-    return _cairo_surface_mask (&surface->fallback->base,
+    return _cairo_surface_mask (surface->fallback,
 				op, source, mask,
 				clip);
 }
@@ -799,7 +797,7 @@ _cairo_xcb_surface_stroke (void				*abstract_surface,
 	surface->fallback = _cairo_xcb_surface_fallback (surface);
     }
 
-    return _cairo_surface_stroke (&surface->fallback->base,
+    return _cairo_surface_stroke (surface->fallback,
 				  op, source,
 				  path, style,
 				  ctm, ctm_inverse,
@@ -838,7 +836,7 @@ _cairo_xcb_surface_fill (void			*abstract_surface,
 	surface->fallback = _cairo_xcb_surface_fallback (surface);
     }
 
-    return _cairo_surface_fill (&surface->fallback->base,
+    return _cairo_surface_fill (surface->fallback,
 				op, source,
 				path, fill_rule,
 				tolerance, antialias,
@@ -878,7 +876,7 @@ _cairo_xcb_surface_glyphs (void				*abstract_surface,
 	surface->fallback = _cairo_xcb_surface_fallback (surface);
     }
 
-    return _cairo_surface_show_text_glyphs (&surface->fallback->base,
+    return _cairo_surface_show_text_glyphs (surface->fallback,
 					    op, source,
 					    NULL, 0,
 					    glyphs, num_glyphs,
diff --git a/src/cairo-xlib-surface.c b/src/cairo-xlib-surface.c
index 57b2604..35e3362 100644
--- a/src/cairo-xlib-surface.c
+++ b/src/cairo-xlib-surface.c
@@ -54,6 +54,7 @@
 #include "cairo-clip-private.h"
 #include "cairo-default-context-private.h"
 #include "cairo-error-private.h"
+#include "cairo-image-surface-private.h"
 #include "cairo-pattern-private.h"
 #include "cairo-region-private.h"
 #include "cairo-scaled-font-private.h"
diff --git a/src/cairoint.h b/src/cairoint.h
index 43ec8c7..50f99c9 100644
--- a/src/cairoint.h
+++ b/src/cairoint.h
@@ -255,6 +255,7 @@ _cairo_isdigit (int c)
 #include "cairo-cache-private.h"
 #include "cairo-reference-count-private.h"
 #include "cairo-spans-private.h"
+#include "cairo-surface-private.h"
 
 cairo_private void
 _cairo_box_from_doubles (cairo_box_t *box,
@@ -796,7 +797,7 @@ struct _cairo_surface_backend {
      * FALSE the surface is considered to be
      * boundless and infinite bounds are used for it.
      */
-    cairo_warn cairo_bool_t
+    cairo_bool_t
     (*get_extents)		(void			 *surface,
 				 cairo_rectangle_int_t   *extents);
 
@@ -947,29 +948,6 @@ struct _cairo_surface_backend {
 					 void                   **image_extra);
 };
 
-#include "cairo-surface-private.h"
-
-struct _cairo_image_surface {
-    cairo_surface_t base;
-
-    pixman_format_code_t pixman_format;
-    cairo_format_t format;
-    unsigned char *data;
-
-    int width;
-    int height;
-    int stride;
-    int depth;
-
-    pixman_image_t *pixman_image;
-
-    unsigned owns_data : 1;
-    unsigned transparency : 2;
-    unsigned color : 2;
-};
-
-extern const cairo_private cairo_surface_backend_t _cairo_image_surface_backend;
-
 #define CAIRO_EXTEND_SURFACE_DEFAULT CAIRO_EXTEND_NONE
 #define CAIRO_EXTEND_GRADIENT_DEFAULT CAIRO_EXTEND_PAD
 #define CAIRO_FILTER_DEFAULT CAIRO_FILTER_GOOD
@@ -1859,7 +1837,7 @@ cairo_private cairo_bool_t
 _cairo_surface_is_similar (cairo_surface_t *surface_a,
 	                   cairo_surface_t *surface_b);
 
-cairo_private cairo_bool_t
+cairo_private_no_warn cairo_bool_t
 _cairo_surface_get_extents (cairo_surface_t         *surface,
 			    cairo_rectangle_int_t   *extents);
 
diff --git a/src/skia/cairo-skia-private.h b/src/skia/cairo-skia-private.h
index 3af3eee..cbd8c88 100644
--- a/src/skia/cairo-skia-private.h
+++ b/src/skia/cairo-skia-private.h
@@ -37,6 +37,7 @@
 #define CAIRO_SKIA_CONTEXT_PRIVATE_H
 
 #include "cairo-private.h"
+#include "cairo-image-surface-private.h"
 
 #include <SkBitmap.h>
 #include <SkCanvas.h>
diff --git a/src/skia/cairo-skia-surface.cpp b/src/skia/cairo-skia-surface.cpp
index 4c10625..ba41b86 100644
--- a/src/skia/cairo-skia-surface.cpp
+++ b/src/skia/cairo-skia-surface.cpp
@@ -34,6 +34,29 @@
  *	Vladimir Vukicevic <vladimir at mozilla.com>
  */
 
+
+/***
+
+Todo:
+
+*** Skia:
+
+- mask()
+
+*** Sk:
+
+High:
+- antialiased clipping?
+
+Medium:
+- implement clip path reset (to avoid restore/save)
+- implement complex radial patterns (2 centers and 2 radii)
+
+Low:
+- implement EXTEND_NONE
+
+***/
+
 #include "cairoint.h"
 
 #include "cairo-skia.h"
@@ -82,10 +105,9 @@ _cairo_skia_surface_finish (void *asurface)
 {
     cairo_skia_surface_t *surface = (cairo_skia_surface_t *) asurface;
 
-    cairo_surface_finish (&surface->image.base);
     delete surface->bitmap;
 
-    return CAIRO_STATUS_SUCCESS;
+    return _cairo_image_surface_finish (&surface->image);
 }
 
 static cairo_surface_t *
@@ -140,27 +162,6 @@ _cairo_skia_surface_release_source_image (void *asurface,
     surface->bitmap->unlockPixels ();
 }
 
-static cairo_bool_t
-_cairo_skia_surface_get_extents (void *asurface,
-				  cairo_rectangle_int_t *extents)
-{
-    cairo_skia_surface_t *surface = (cairo_skia_surface_t *) asurface;
-    extents->x = extents->y = 0;
-    extents->width  = surface->image.width;
-    extents->height = surface->image.height;
-    return TRUE;
-}
-
-static void
-_cairo_skia_surface_get_font_options (void                  *abstract_surface,
-				       cairo_font_options_t  *options)
-{
-    _cairo_font_options_init_default (options);
-
-    cairo_font_options_set_hint_metrics (options, CAIRO_HINT_METRICS_ON);
-    _cairo_font_options_set_round_glyph_positions (options, CAIRO_ROUND_GLYPH_POS_ON);
-}
-
 static cairo_rectangle_t *
 to_rectangle (cairo_rectangle_t *rf,
 	      cairo_rectangle_int_t *ri)
@@ -379,9 +380,9 @@ cairo_skia_surface_backend = {
     NULL, /* copy_page */
     NULL, /* show_page */
 
-    _cairo_skia_surface_get_extents,
+    _cairo_image_surface_get_extents,
     NULL, /* old_show_glyphs */
-    _cairo_skia_surface_get_font_options,
+    _cairo_image_surface_get_font_options,
     NULL, /* flush */
     NULL, /* mark_dirty_rectangle */
     NULL, /* scaled_font_fini */
@@ -426,6 +427,7 @@ sk_config_to_pixman_format_code (SkBitmap::Config config,
 	return (pixman_format_code_t) -1;
     }
 }
+
 static cairo_skia_surface_t *
 _cairo_skia_surface_create_internal (SkBitmap::Config config,
 				     bool opaque,
@@ -501,25 +503,3 @@ cairo_skia_surface_create_for_data (unsigned char *data,
 
     return &_cairo_skia_surface_create_internal (config, opaque, data, width, height, stride)->image.base;
 }
-
-/***
-
-Todo:
-
-*** Skia:
-
-- mask()
-
-*** Sk:
-
-High:
-- antialiased clipping?
-
-Medium:
-- implement clip path reset (to avoid restore/save)
-- implement complex radial patterns (2 centers and 2 radii)
-
-Low:
-- implement EXTEND_NONE
-
-***/
commit fa259aa1a1b8b16dd9d23110e52576210af8404c
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Fri Apr 23 23:40:06 2010 +0100

    skia

diff --git a/src/cairo-backend-private.h b/src/cairo-backend-private.h
index e09b436..1dd5ea0 100644
--- a/src/cairo-backend-private.h
+++ b/src/cairo-backend-private.h
@@ -40,6 +40,7 @@
 
 typedef enum _cairo_backend_type {
     CAIRO_TYPE_DEFAULT,
+    CAIRO_TYPE_SKIA,
 } cairo_backend_type_t;
 
 struct _cairo_backend {
diff --git a/src/cairo-error-private.h b/src/cairo-error-private.h
index 1c82ae6..a548a35 100644
--- a/src/cairo-error-private.h
+++ b/src/cairo-error-private.h
@@ -46,6 +46,10 @@
 
 CAIRO_BEGIN_DECLS
 
+/* Sure wish C had a real enum type so that this would be distinct
+ * from #cairo_status_t. Oh well, without that, I'll use this bogus 100
+ * offset.  We want to keep it fit in int8_t as the compiler may choose
+ * that for #cairo_status_t */
 enum _cairo_int_status {
     CAIRO_INT_STATUS_SUCCESS = 0,
 
@@ -97,6 +101,8 @@ enum _cairo_int_status {
     CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN,
 };
 
+typedef enum _cairo_int_status cairo_int_status_t;
+
 #define _cairo_status_is_error(status) \
     (status != CAIRO_STATUS_SUCCESS && status < CAIRO_STATUS_LAST_STATUS)
 
@@ -107,7 +113,7 @@ static inline cairo_status_t
 _cairo_public_status (cairo_int_status_t status)
 {
     assert (status <= CAIRO_INT_STATUS_LAST_STATUS);
-    return status;
+    return (cairo_status_t) status;
 }
 
 cairo_private cairo_status_t
diff --git a/src/cairo-types-private.h b/src/cairo-types-private.h
index b1b0db3..4933cf2 100644
--- a/src/cairo-types-private.h
+++ b/src/cairo-types-private.h
@@ -44,6 +44,8 @@
 #include "cairo-list-private.h"
 #include "cairo-reference-count-private.h"
 
+CAIRO_BEGIN_DECLS
+
 /**
  * SECTION:cairo-types
  * @Title: Types
@@ -225,12 +227,6 @@ typedef enum _cairo_paginated_mode {
     CAIRO_PAGINATED_MODE_FALLBACK	/* paint fallback images */
 } cairo_paginated_mode_t;
 
-/* Sure wish C had a real enum type so that this would be distinct
- * from #cairo_status_t. Oh well, without that, I'll use this bogus 100
- * offset.  We want to keep it fit in int8_t as the compiler may choose
- * that for #cairo_status_t */
-typedef enum _cairo_int_status cairo_int_status_t;
-
 typedef enum _cairo_internal_surface_type {
     CAIRO_INTERNAL_SURFACE_TYPE_SNAPSHOT = 0x1000,
     CAIRO_INTERNAL_SURFACE_TYPE_PAGINATED,
@@ -419,4 +415,7 @@ typedef struct _cairo_scaled_glyph {
 
     void		    *surface_private;	/* for the surface backend */
 } cairo_scaled_glyph_t;
+
+CAIRO_END_DECLS
+
 #endif /* CAIRO_TYPES_PRIVATE_H */
diff --git a/src/cairoint.h b/src/cairoint.h
index 4354e3f..43ec8c7 100644
--- a/src/cairoint.h
+++ b/src/cairoint.h
@@ -71,6 +71,7 @@
 #include <pixman.h>
 
 #include "cairo-compiler-private.h"
+#include "cairo-error-private.h"
 
 #if CAIRO_HAS_PS_SURFACE || CAIRO_HAS_SCRIPT_SURFACE || CAIRO_HAS_XML_SURFACE
 #define CAIRO_HAS_DEFLATE_STREAM 1
diff --git a/src/skia/cairo-skia-context.cpp b/src/skia/cairo-skia-context.cpp
index 58fc7dd..de17845 100644
--- a/src/skia/cairo-skia-context.cpp
+++ b/src/skia/cairo-skia-context.cpp
@@ -45,8 +45,10 @@
 #include "cairo-arc-private.h"
 #include "cairo-backend-private.h"
 #include "cairo-default-context-private.h"
+#include "cairo-freed-pool-private.h"
 #include "cairo-gstate-private.h"
 #include "cairo-path-private.h"
+#include "cairo-pattern-private.h"
 #include "cairo-skia-private.h"
 
 #include <SkShader.h>
@@ -68,53 +70,7 @@
 #endif
 
 
-#if HAS_ATOMIC_OPS
-/* We keep a small stash of contexts to reduce malloc pressure */
-#define CAIRO_STASH_SIZE 4
-static struct {
-    cairo_skia_context_t pool[CAIRO_STASH_SIZE];
-    cairo_atomic_int_t occupied;
-} _context_stash;
-
-static cairo_skia_context_t *
-_context_get (void)
-{
-    cairo_atomic_int_t avail, old, n;
-
-    do {
-	old = _cairo_atomic_int_get (&_context_stash.occupied);
-	avail = ffs (~old) - 1;
-	if (avail >= CAIRO_STASH_SIZE)
-	    return NULL;
-
-	n = old | (1 << avail);
-    } while (_cairo_atomic_int_cmpxchg (&_context_stash.occupied, old, n) != old);
-
-    return &_context_stash.pool[avail];
-}
-
-static void
-_context_put (cairo_skia_context_t *cr)
-{
-    cairo_atomic_int_t old, n, avail;
-
-    if (cr < &_context_stash.pool[0] ||
-	cr >= &_context_stash.pool[CAIRO_STASH_SIZE])
-    {
-	delete (cr);
-	return;
-    }
-
-    avail = ~(1 << (cr - &_context_stash.pool[0]));
-    do {
-	old = _cairo_atomic_int_get (&_context_stash.occupied);
-	n = old & avail;
-    } while (_cairo_atomic_int_cmpxchg (&_context_stash.occupied, old, n) != old);
-}
-#else
-#define _context_get() NULL
-#define _context_put(cr) delete (cr)
-#endif
+static freed_pool_t context_pool;
 
 static void
 _cairo_skia_context_destroy (void *abstract_cr)
@@ -140,7 +96,7 @@ _cairo_skia_context_destroy (void *abstract_cr)
 
     _cairo_fini (&cr->base);
 
-    _context_put (cr);
+    _freed_pool_put (&context_pool, cr);
 }
 
 static cairo_surface_t *
@@ -343,6 +299,16 @@ color_to_sk (const cairo_color_t& c)
 			   (U8CPU) (c.blue * 255));
 }
 
+static inline SkColor
+color_stop_to_sk (const cairo_color_stop_t& c)
+{
+    /* Need unpremultiplied 1-byte values */
+    return SkColorSetARGB ((U8CPU) (c.alpha * 255),
+			   (U8CPU) (c.red * 255),
+			   (U8CPU) (c.green * 255),
+			   (U8CPU) (c.blue * 255));
+}
+
 static SkShader*
 source_to_sk_shader (cairo_skia_context_t *cr,
 		     const cairo_pattern_t *pattern)
@@ -401,17 +367,17 @@ source_to_sk_shader (cairo_skia_context_t *cr,
 
 	for (unsigned int i = 0; i < gradient->n_stops; i++) {
 	    pos[i] = CAIRO_FIXED_TO_SK_SCALAR (gradient->stops[i].offset);
-	    colors[i] = color_to_sk (gradient->stops[i].color);
+	    colors[i] = color_stop_to_sk (gradient->stops[i].color);
 	}
 
 	if (pattern->type == CAIRO_PATTERN_TYPE_LINEAR) {
 	    cairo_linear_pattern_t *linear = (cairo_linear_pattern_t *) gradient;
 	    SkPoint points[2];
 
-	    points[0].set (CAIRO_FIXED_TO_SK_SCALAR (linear->p1.x),
-			   CAIRO_FIXED_TO_SK_SCALAR (linear->p1.y));
-	    points[1].set (CAIRO_FIXED_TO_SK_SCALAR (linear->p2.x),
-			   CAIRO_FIXED_TO_SK_SCALAR (linear->p2.y));
+	    points[0].set (SkFloatToScalar (linear->pd1.x),
+			   SkFloatToScalar (linear->pd1.y));
+	    points[1].set (SkFloatToScalar (linear->pd2.x),
+			   SkFloatToScalar (linear->pd2.y));
 	    shader = SkGradientShader::CreateLinear (points, colors, pos, gradient->n_stops,
 						     extend_to_sk (pattern->extend));
 	} else {
@@ -1264,12 +1230,26 @@ _cairo_skia_context_paint (void *abstract_cr)
 }
 
 static cairo_status_t
+_cairo_skia_context_paint_with_alpha (void *abstract_cr,
+				      double alpha)
+{
+    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    if (CAIRO_ALPHA_IS_OPAQUE (alpha))
+	return _cairo_skia_context_paint (cr);
+
+    /*XXX */
+    return _cairo_skia_context_paint (cr);
+}
+
+static cairo_status_t
 _cairo_skia_context_mask (void *abstract_cr,
-			     cairo_pattern_t *mask)
+			  cairo_pattern_t *mask)
 {
     //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
 
-    ASSERT_NOT_REACHED;
+    /* XXX */
+    //ASSERT_NOT_REACHED;
     return CAIRO_STATUS_SUCCESS;
 }
 
@@ -1544,7 +1524,8 @@ _cairo_skia_context_glyphs (void *abstract_cr,
 {
     //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
 
-    ASSERT_NOT_REACHED;
+    /* XXX */
+    //ASSERT_NOT_REACHED;
     return CAIRO_STATUS_SUCCESS;
 }
 
@@ -1572,6 +1553,7 @@ _cairo_skia_context_glyph_extents (void                *abstract_cr,
 }
 
 static const cairo_backend_t _cairo_skia_context_backend = {
+    CAIRO_TYPE_SKIA,
     _cairo_skia_context_destroy,
 
     _cairo_skia_context_get_original_target,
@@ -1651,6 +1633,7 @@ static const cairo_backend_t _cairo_skia_context_backend = {
     _cairo_skia_context_copy_clip_rectangle_list,
 
     _cairo_skia_context_paint,
+    _cairo_skia_context_paint_with_alpha,
     _cairo_skia_context_mask,
 
     _cairo_skia_context_stroke,
@@ -1688,7 +1671,7 @@ _cairo_skia_context_create (void *target)
     cairo_skia_surface_t *surface = (cairo_skia_surface_t *) target;
     cairo_skia_context_t *cr;
 
-    cr = _context_get ();
+    cr = (cairo_skia_context_t *) _freed_pool_get (&context_pool);
     if (unlikely (cr == NULL)) {
 	    cr = new cairo_skia_context_t;
 	    if (unlikely (cr == NULL))
diff --git a/src/skia/cairo-skia-private.h b/src/skia/cairo-skia-private.h
index 7e85613..3af3eee 100644
--- a/src/skia/cairo-skia-private.h
+++ b/src/skia/cairo-skia-private.h
@@ -37,7 +37,6 @@
 #define CAIRO_SKIA_CONTEXT_PRIVATE_H
 
 #include "cairo-private.h"
-#include "cairo-image-surface-private.h"
 
 #include <SkBitmap.h>
 #include <SkCanvas.h>
@@ -95,6 +94,7 @@ format_to_sk_config (cairo_format_t format,
     case CAIRO_FORMAT_A1:
 	config = SkBitmap::kA1_Config;
 	break;
+    case CAIRO_FORMAT_RGB30:
     case CAIRO_FORMAT_INVALID:
     default:
 	return false;
diff --git a/src/skia/cairo-skia-surface.cpp b/src/skia/cairo-skia-surface.cpp
index 95ec84d..4c10625 100644
--- a/src/skia/cairo-skia-surface.cpp
+++ b/src/skia/cairo-skia-surface.cpp
@@ -39,8 +39,8 @@
 #include "cairo-skia.h"
 #include "cairo-skia-private.h"
 
+#include "cairo-composite-rectangles-private.h"
 #include "cairo-error-private.h"
-#include "cairo-foreign-context-private.h"
 
 static cairo_skia_surface_t *
 _cairo_skia_surface_create_internal (SkBitmap::Config config,
@@ -82,9 +82,38 @@ _cairo_skia_surface_finish (void *asurface)
 {
     cairo_skia_surface_t *surface = (cairo_skia_surface_t *) asurface;
 
+    cairo_surface_finish (&surface->image.base);
     delete surface->bitmap;
 
-    return _cairo_image_surface_finish (&surface->image);
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_surface_t *
+_cairo_skia_surface_map_to_image (void *asurface,
+				  const cairo_rectangle_int_t *extents)
+{
+    cairo_skia_surface_t *surface = (cairo_skia_surface_t *) asurface;
+
+    surface->bitmap->lockPixels ();
+
+    if (extents->width < surface->image.width ||
+	extents->height < surface->image.height)
+    {
+	return _cairo_surface_create_for_rectangle_int (&surface->image.base,
+							extents);
+    }
+
+    return cairo_surface_reference (&surface->image.base);
+}
+
+static cairo_int_status_t
+_cairo_skia_surface_unmap_image (void *asurface,
+				 cairo_image_surface_t *image)
+{
+    cairo_skia_surface_t *surface = (cairo_skia_surface_t *) asurface;
+
+    surface->bitmap->unlockPixels ();
+    return CAIRO_INT_STATUS_SUCCESS;
 }
 
 static cairo_status_t
@@ -111,13 +140,231 @@ _cairo_skia_surface_release_source_image (void *asurface,
     surface->bitmap->unlockPixels ();
 }
 
+static cairo_bool_t
+_cairo_skia_surface_get_extents (void *asurface,
+				  cairo_rectangle_int_t *extents)
+{
+    cairo_skia_surface_t *surface = (cairo_skia_surface_t *) asurface;
+    extents->x = extents->y = 0;
+    extents->width  = surface->image.width;
+    extents->height = surface->image.height;
+    return TRUE;
+}
+
+static void
+_cairo_skia_surface_get_font_options (void                  *abstract_surface,
+				       cairo_font_options_t  *options)
+{
+    _cairo_font_options_init_default (options);
+
+    cairo_font_options_set_hint_metrics (options, CAIRO_HINT_METRICS_ON);
+    _cairo_font_options_set_round_glyph_positions (options, CAIRO_ROUND_GLYPH_POS_ON);
+}
+
+static cairo_rectangle_t *
+to_rectangle (cairo_rectangle_t *rf,
+	      cairo_rectangle_int_t *ri)
+{
+    rf->x = ri->x;
+    rf->y = ri->y;
+    rf->width = ri->width;
+    rf->height = ri->height;
+    return rf;
+}
+
+static cairo_int_status_t
+_cairo_foreign_surface_paint (void			*abstract_surface,
+			      cairo_operator_t		 op,
+			      const cairo_pattern_t	*source,
+			      const cairo_clip_t	*clip)
+{
+    cairo_surface_t *surface = (cairo_surface_t *) abstract_surface;
+    cairo_surface_t *image;
+    cairo_rectangle_int_t extents;
+    cairo_rectangle_t rect;
+    cairo_composite_rectangles_t composite;
+    cairo_int_status_t status;
+
+    _cairo_surface_get_extents (surface, &extents);
+    status = _cairo_composite_rectangles_init_for_paint (&composite, &extents,
+							 op, source,
+							 clip);
+    if (unlikely (status))
+	return status;
+
+    image = cairo_surface_map_to_image (surface,
+					to_rectangle(&rect, &composite.unbounded));
+    status = (cairo_int_status_t)
+	_cairo_surface_paint (image, op, source, clip);
+    cairo_surface_unmap_image (surface, image);
+
+    _cairo_composite_rectangles_fini (&composite);
+
+    return status;
+}
+
+static cairo_int_status_t
+_cairo_foreign_surface_mask (void			*abstract_surface,
+			      cairo_operator_t		 op,
+			      const cairo_pattern_t	*source,
+			      const cairo_pattern_t	*mask,
+			      const cairo_clip_t	*clip)
+{
+    cairo_surface_t *surface =(cairo_surface_t *) abstract_surface;
+    cairo_surface_t *image;
+    cairo_rectangle_int_t extents;
+    cairo_rectangle_t rect;
+    cairo_composite_rectangles_t composite;
+    cairo_int_status_t status;
+
+    _cairo_surface_get_extents (surface, &extents);
+    status = _cairo_composite_rectangles_init_for_mask (&composite, &extents,
+							op, source, mask,
+							clip);
+    if (unlikely (status))
+	return status;
+
+    image = cairo_surface_map_to_image (surface,
+					to_rectangle(&rect, &composite.unbounded));
+    status = (cairo_int_status_t)
+	_cairo_surface_mask (image, op, source, mask, clip);
+    cairo_surface_unmap_image (surface, image);
+
+    _cairo_composite_rectangles_fini (&composite);
+
+    return status;
+}
+
+static cairo_int_status_t
+_cairo_foreign_surface_stroke (void			*abstract_surface,
+			       cairo_operator_t		 op,
+			       const cairo_pattern_t	*source,
+			       const cairo_path_fixed_t	*path,
+			       const cairo_stroke_style_t*style,
+			       const cairo_matrix_t	*ctm,
+			       const cairo_matrix_t	*ctm_inverse,
+			       double			 tolerance,
+			       cairo_antialias_t	 antialias,
+			       const cairo_clip_t	*clip)
+{
+    cairo_surface_t *surface =(cairo_surface_t *) abstract_surface;
+    cairo_surface_t *image;
+    cairo_composite_rectangles_t composite;
+    cairo_rectangle_int_t extents;
+    cairo_rectangle_t rect;
+    cairo_int_status_t status;
+
+    _cairo_surface_get_extents (surface, &extents);
+    status = _cairo_composite_rectangles_init_for_stroke (&composite, &extents,
+							  op, source,
+							  path, style, ctm,
+							  clip);
+    if (unlikely (status))
+	return status;
+
+    image = cairo_surface_map_to_image (surface,
+					to_rectangle(&rect, &composite.unbounded));
+    status = (cairo_int_status_t)
+	_cairo_surface_stroke (image, op, source, path, style, ctm, ctm_inverse, tolerance, antialias, clip);
+    cairo_surface_unmap_image (surface, image);
+
+    _cairo_composite_rectangles_fini (&composite);
+
+    return status;
+}
+
+static cairo_int_status_t
+_cairo_foreign_surface_fill (void				*abstract_surface,
+			     cairo_operator_t		 op,
+			     const cairo_pattern_t	*source,
+			     const cairo_path_fixed_t	*path,
+			     cairo_fill_rule_t		 fill_rule,
+			     double			 tolerance,
+			     cairo_antialias_t		 antialias,
+			     const cairo_clip_t		*clip)
+{
+    cairo_surface_t *surface =(cairo_surface_t *) abstract_surface;
+    cairo_surface_t *image;
+    cairo_composite_rectangles_t composite;
+    cairo_rectangle_int_t extents;
+    cairo_rectangle_t rect;
+    cairo_int_status_t status;
+
+    _cairo_surface_get_extents (surface, &extents);
+    status = _cairo_composite_rectangles_init_for_fill (&composite, &extents,
+							op, source, path,
+							clip);
+    if (unlikely (status))
+	return status;
+
+    image = cairo_surface_map_to_image (surface,
+					to_rectangle(&rect, &composite.unbounded));
+    status = (cairo_int_status_t)
+	_cairo_surface_fill (image, op, source, path, fill_rule, tolerance, antialias, clip);
+    cairo_surface_unmap_image (surface, image);
+
+    _cairo_composite_rectangles_fini (&composite);
+
+    return status;
+}
+
+static cairo_int_status_t
+_cairo_foreign_surface_glyphs (void			*abstract_surface,
+			       cairo_operator_t		 op,
+			       const cairo_pattern_t	*source,
+			       cairo_glyph_t		*glyphs,
+			       int			 num_glyphs,
+			       cairo_scaled_font_t	*scaled_font,
+			       const cairo_clip_t	*clip,
+			       int *num_remaining)
+{
+    cairo_surface_t *surface =(cairo_surface_t *) abstract_surface;
+    cairo_surface_t *image;
+    cairo_composite_rectangles_t composite;
+    cairo_rectangle_int_t extents;
+    cairo_rectangle_t rect;
+    cairo_int_status_t status;
+    cairo_bool_t overlap;
+
+    _cairo_surface_get_extents (surface, &extents);
+    status = _cairo_composite_rectangles_init_for_glyphs (&composite, &extents,
+							  op, source,
+							  scaled_font,
+							  glyphs, num_glyphs,
+							  clip,
+							  &overlap);
+    if (unlikely (status))
+	return status;
+
+    image = cairo_surface_map_to_image (surface,
+					to_rectangle(&rect, &composite.unbounded));
+    status = (cairo_int_status_t)
+	_cairo_surface_show_text_glyphs (image,
+					 op, source,
+					 NULL, 0,
+					 glyphs, num_glyphs,
+					 NULL, 0, (cairo_text_cluster_flags_t)0,
+					 scaled_font,
+					 clip);
+    cairo_surface_unmap_image (surface, image);
+    _cairo_composite_rectangles_fini (&composite);
+
+    *num_remaining = 0;
+    return status;
+}
+
 static const struct _cairo_surface_backend
 cairo_skia_surface_backend = {
     CAIRO_SURFACE_TYPE_SKIA,
+    _cairo_skia_surface_finish,
+
     _cairo_skia_context_create,
 
     _cairo_skia_surface_create_similar,
-    _cairo_skia_surface_finish,
+    NULL, //_cairo_skia_surface_create_similar_image,
+    _cairo_skia_surface_map_to_image,
+    _cairo_skia_surface_unmap_image,
+
     _cairo_skia_surface_acquire_source_image,
     _cairo_skia_surface_release_source_image,
 
@@ -132,20 +379,20 @@ cairo_skia_surface_backend = {
     NULL, /* copy_page */
     NULL, /* show_page */
 
-    _cairo_image_surface_get_extents,
+    _cairo_skia_surface_get_extents,
     NULL, /* old_show_glyphs */
-    _cairo_image_surface_get_font_options,
+    _cairo_skia_surface_get_font_options,
     NULL, /* flush */
     NULL, /* mark_dirty_rectangle */
     NULL, /* scaled_font_fini */
     NULL, /* scaled_glyph_fini */
 
-    /* XXX native surface functions */
-    _cairo_foreign_context_paint,
-    _cairo_foreign_context_mask,
-    _cairo_foreign_context_stroke,
-    _cairo_foreign_context_fill,
-    _cairo_foreign_context_glyphs
+    /* XXX native surface functions? */
+    _cairo_foreign_surface_paint,
+    _cairo_foreign_surface_mask,
+    _cairo_foreign_surface_stroke,
+    _cairo_foreign_surface_fill,
+    _cairo_foreign_surface_glyphs
 };
 
 /*
commit e39363367bdaa14acb73e268effe777ac793404f
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Sat Apr 24 00:50:33 2010 +0100

    skia: Update to use cairo_backend_t interface

diff --git a/configure.ac b/configure.ac
index 793263f..9fe6690 100644
--- a/configure.ac
+++ b/configure.ac
@@ -199,7 +199,7 @@ CAIRO_ENABLE_SURFACE_BACKEND(skia, Skia, no, [
 	      [AS_HELP_STRING([--with-skia=/path/to/skia],
 			      [directory to find compiled skia sources])],
 	      [skia_DIR="$withval"],
-	      [skia_DIR="`pwd`/../mesa"])
+	      [skia_DIR="`pwd`/../skia"])
   skia_NONPKGCONFIG_CFLAGS="-I$skia_DIR/include/config -I$skia_DIR/include/core -I$skia_DIR/include/effects"
   skia_NONPKGCONFIG_LIBS="$skia_DIR/out/libskia.a"
   AC_SUBST(skia_DIR)
diff --git a/src/Makefile.sources b/src/Makefile.sources
index 059f6ae..fb67e5b 100644
--- a/src/Makefile.sources
+++ b/src/Makefile.sources
@@ -312,7 +312,10 @@ cairo_win32_sources = cairo-win32-surface.c cairo-win32-printing-surface.c
 cairo_win32_font_sources = cairo-win32-font.c
 
 cairo_skia_headers = cairo-skia.h
-cairo_skia_cxx_sources = cairo-skia-surface.cpp
+cairo_skia_cxx_sources = \
+	skia/cairo-skia-context.cpp \
+	skia/cairo-skia-surface.cpp \
+	$(NULL)
 
 cairo_os2_headers = cairo-os2.h
 cairo_os2_private = cairo-os2-private.h
diff --git a/src/cairo-skia.h b/src/cairo-skia.h
index f628235..99b9286 100644
--- a/src/cairo-skia.h
+++ b/src/cairo-skia.h
@@ -55,24 +55,6 @@ cairo_skia_surface_create_for_data (unsigned char *data,
 				    int height,
 				    int stride);
 
-cairo_public unsigned char *
-cairo_skia_surface_get_data (cairo_surface_t *surface);
-
-cairo_public cairo_format_t
-cairo_skia_surface_get_format (cairo_surface_t *surface);
-
-cairo_public int
-cairo_skia_surface_get_width (cairo_surface_t *surface);
-
-cairo_public int
-cairo_skia_surface_get_height (cairo_surface_t *surface);
-
-cairo_public int
-cairo_skia_surface_get_stride (cairo_surface_t *surface);
-
-cairo_public cairo_surface_t *
-cairo_skia_surface_get_image (cairo_surface_t *surface);
-
 CAIRO_END_DECLS
 
 #else
diff --git a/src/skia/cairo-skia-context.cpp b/src/skia/cairo-skia-context.cpp
new file mode 100644
index 0000000..58fc7dd
--- /dev/null
+++ b/src/skia/cairo-skia-context.cpp
@@ -0,0 +1,1728 @@
+/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2002 University of Southern California
+ * Copyright © 2005 Red Hat, Inc.
+ * Copyright © 2010 Intel Corporation
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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-private.h"
+#include "cairo-error-private.h"
+#include "cairo-arc-private.h"
+#include "cairo-backend-private.h"
+#include "cairo-default-context-private.h"
+#include "cairo-gstate-private.h"
+#include "cairo-path-private.h"
+#include "cairo-skia-private.h"
+
+#include <SkShader.h>
+#include <SkColorShader.h>
+#include <SkGradientShader.h>
+#include <SkDashPathEffect.h>
+
+#if !defined(INFINITY)
+#define INFINITY HUGE_VAL
+#endif
+
+#if (CAIRO_FIXED_BITS == 32) && (CAIRO_FIXED_FRAC_BITS == 16) && defined(SK_SCALAR_IS_FIXED)
+# define CAIRO_FIXED_TO_SK_SCALAR(x)  (x)
+#elif defined(SK_SCALAR_IS_FIXED)
+/* This can be done better, but this will do for now */
+# define CAIRO_FIXED_TO_SK_SCALAR(x)  SkFloatToScalar(_cairo_fixed_to_double(x))
+#else
+# define CAIRO_FIXED_TO_SK_SCALAR(x)  SkFloatToScalar(_cairo_fixed_to_double(x))
+#endif
+
+
+#if HAS_ATOMIC_OPS
+/* We keep a small stash of contexts to reduce malloc pressure */
+#define CAIRO_STASH_SIZE 4
+static struct {
+    cairo_skia_context_t pool[CAIRO_STASH_SIZE];
+    cairo_atomic_int_t occupied;
+} _context_stash;
+
+static cairo_skia_context_t *
+_context_get (void)
+{
+    cairo_atomic_int_t avail, old, n;
+
+    do {
+	old = _cairo_atomic_int_get (&_context_stash.occupied);
+	avail = ffs (~old) - 1;
+	if (avail >= CAIRO_STASH_SIZE)
+	    return NULL;
+
+	n = old | (1 << avail);
+    } while (_cairo_atomic_int_cmpxchg (&_context_stash.occupied, old, n) != old);
+
+    return &_context_stash.pool[avail];
+}
+
+static void
+_context_put (cairo_skia_context_t *cr)
+{
+    cairo_atomic_int_t old, n, avail;
+
+    if (cr < &_context_stash.pool[0] ||
+	cr >= &_context_stash.pool[CAIRO_STASH_SIZE])
+    {
+	delete (cr);
+	return;
+    }
+
+    avail = ~(1 << (cr - &_context_stash.pool[0]));
+    do {
+	old = _cairo_atomic_int_get (&_context_stash.occupied);
+	n = old & avail;
+    } while (_cairo_atomic_int_cmpxchg (&_context_stash.occupied, old, n) != old);
+}
+#else
+#define _context_get() NULL
+#define _context_put(cr) delete (cr)
+#endif
+
+static void
+_cairo_skia_context_destroy (void *abstract_cr)
+{
+    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    cr->path->reset ();
+    cr->paint->reset ();
+
+    delete cr->canvas;
+
+    cairo_surface_destroy (&cr->target->image.base);
+    cairo_surface_destroy (&cr->original->image.base);
+
+    if (cr->source != NULL) {
+	if (cr->source_image != NULL) {
+	    _cairo_surface_release_source_image (cr->source, cr->source_image, cr->source_extra);
+	    cr->source_image = NULL;
+	}
+	cairo_surface_destroy (cr->source);
+	cr->source = NULL;
+    }
+
+    _cairo_fini (&cr->base);
+
+    _context_put (cr);
+}
+
+static cairo_surface_t *
+_cairo_skia_context_get_original_target (void *abstract_cr)
+{
+    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    return &cr->original->image.base;
+}
+
+static cairo_surface_t *
+_cairo_skia_context_get_current_target (void *abstract_cr)
+{
+    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    return &cr->target->image.base;
+}
+
+static cairo_status_t
+_cairo_skia_context_save (void *abstract_cr)
+{
+    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    cr->canvas->save ();
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_restore (void *abstract_cr)
+{
+    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    cr->canvas->restore ();
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_push_group (void *abstract_cr, cairo_content_t content)
+{
+    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+    cairo_surface_t *group_surface;
+    cairo_status_t status;
+    int width, height;
+
+    //clip = _cairo_gstate_get_clip (cr->gstate);
+    width = cr->target->image.width;
+    height = cr->target->image.height;
+    group_surface = cr->target->image.base.backend->create_similar (&cr->target->image.base,
+								    content, width, height);
+
+#if 0
+    /* Set device offsets on the new surface so that logically it appears at
+     * the same location on the parent surface -- when we pop_group this,
+     * the source pattern will get fixed up for the appropriate target surface
+     * device offsets, so we want to set our own surface offsets from /that/,
+     * and not from the device origin. */
+    cairo_surface_set_device_offset (group_surface,
+				     parent_surface->device_transform.x0 - extents.x,
+				     parent_surface->device_transform.y0 - extents.y);
+
+    /* If we have a current path, we need to adjust it to compensate for
+     * the device offset just applied. */
+    _cairo_path_fixed_transform (cr->path,
+				 &group_surface->device_transform);
+#endif
+
+    status = _cairo_skia_context_save (cr);
+    if (unlikely (status)) {
+	cairo_surface_destroy (group_surface);
+	return status;
+    }
+
+    cairo_surface_destroy (&cr->target->image.base);
+    cr->target = (cairo_skia_surface_t *) group_surface;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_pattern_t *
+_cairo_skia_context_pop_group (void *abstract_cr)
+{
+    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+    cairo_surface_t *group_surface;
+    cairo_pattern_t *group_pattern;
+    cairo_status_t status;
+
+    group_surface = cairo_surface_reference (&cr->target->image.base);
+
+    status = _cairo_skia_context_restore (cr);
+    if (unlikely (status)) {
+	group_pattern = _cairo_pattern_create_in_error (status);
+	goto done;
+    }
+
+    group_pattern = cairo_pattern_create_for_surface (group_surface);
+    status = group_pattern->status;
+    if (unlikely (status))
+        goto done;
+
+#if 0
+    _cairo_gstate_get_matrix (cr->gstate, &group_matrix);
+    /* Transform by group_matrix centered around device_transform so that when
+     * we call _cairo_gstate_copy_transformed_pattern the result is a pattern
+     * with a matrix equivalent to the device_transform of group_surface. */
+    if (_cairo_surface_has_device_transform (group_surface)) {
+	cairo_pattern_set_matrix (group_pattern, &group_surface->device_transform);
+	_cairo_pattern_transform (group_pattern, &group_matrix);
+	_cairo_pattern_transform (group_pattern, &group_surface->device_transform_inverse);
+    } else {
+	cairo_pattern_set_matrix (group_pattern, &group_matrix);
+    }
+
+    /* If we have a current path, we need to adjust it to compensate for
+     * the device offset just removed. */
+    _cairo_path_fixed_transform (cr->path,
+				 &group_surface->device_transform_inverse);
+#endif
+
+done:
+    cairo_surface_destroy (group_surface);
+
+    return group_pattern;
+}
+
+static inline cairo_surface_t *
+surface_from_pattern (const cairo_pattern_t *pattern)
+{
+    return (reinterpret_cast <const cairo_surface_pattern_t *> (pattern))->surface;
+}
+
+static inline bool
+surface_to_sk_bitmap (cairo_surface_t *surface, SkBitmap& bitmap)
+{
+    cairo_image_surface_t *img = (cairo_image_surface_t *) surface;
+    SkBitmap::Config config;
+    bool opaque;
+
+    if (unlikely (! format_to_sk_config (img->format, config, opaque)))
+	return false;
+
+    bitmap.reset ();
+    bitmap.setConfig (config, img->width, img->height, img->stride);
+    bitmap.setIsOpaque (opaque);
+    bitmap.setPixels (img->data);
+
+    return true;
+}
+
+static inline SkMatrix
+matrix_to_sk (const cairo_matrix_t& mat)
+{
+    SkMatrix skm;
+
+    skm.reset ();
+    skm.set (SkMatrix::kMScaleX, SkFloatToScalar (mat.xx));
+    skm.set (SkMatrix::kMSkewX,  SkFloatToScalar (mat.xy));
+    skm.set (SkMatrix::kMTransX, SkFloatToScalar (mat.x0));
+    skm.set (SkMatrix::kMSkewY,  SkFloatToScalar (mat.yx));
+    skm.set (SkMatrix::kMScaleY, SkFloatToScalar (mat.yy));
+    skm.set (SkMatrix::kMTransY, SkFloatToScalar (mat.y0));
+
+    /*
+    skm[6] = SkFloatToScalar (0.0);
+    skm[7] = SkFloatToScalar (0.0);
+    skm[8] = SkFloatToScalar (1.0); -- this isn't right, it wants a magic value in there that it'll set itself.  It wants Sk_Fract1 (2.30), not Sk_Scalar1
+    */
+
+    return skm;
+}
+
+static inline SkMatrix
+matrix_inverse_to_sk (const cairo_matrix_t& mat)
+{
+    cairo_matrix_t inv = mat;
+    cairo_status_t status = cairo_matrix_invert (&inv);
+    assert (status == CAIRO_STATUS_SUCCESS);
+    return matrix_to_sk (inv);
+}
+
+static SkShader::TileMode
+extend_to_sk (cairo_extend_t extend)
+{
+    static const SkShader::TileMode modeMap[] = {
+	SkShader::kClamp_TileMode,  // NONE behaves like PAD, because noone wants NONE
+	SkShader::kRepeat_TileMode,
+	SkShader::kMirror_TileMode,
+	SkShader::kClamp_TileMode
+    };
+
+    return modeMap[extend];
+}
+
+static inline SkColor
+color_to_sk (const cairo_color_t& c)
+{
+    /* Need unpremultiplied 1-byte values */
+    return SkColorSetARGB ((U8CPU) (c.alpha * 255),
+			   (U8CPU) (c.red * 255),
+			   (U8CPU) (c.green * 255),
+			   (U8CPU) (c.blue * 255));
+}
+
+static SkShader*
+source_to_sk_shader (cairo_skia_context_t *cr,
+		     const cairo_pattern_t *pattern)
+{
+    SkShader *shader = NULL;
+
+    if (pattern->type == CAIRO_PATTERN_TYPE_SOLID) {
+	cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) pattern;
+	return new SkColorShader (color_to_sk (solid->color));
+    } else if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) {
+	cairo_surface_t *surface = surface_from_pattern (pattern);
+
+	cr->source = cairo_surface_reference (surface);
+
+	if (surface->type == CAIRO_SURFACE_TYPE_SKIA) {
+	    cairo_skia_surface_t *esurf = (cairo_skia_surface_t *) surface;
+
+	    shader = SkShader::CreateBitmapShader (*esurf->bitmap,
+						   extend_to_sk (pattern->extend),
+						   extend_to_sk (pattern->extend));
+	} else {
+	    SkBitmap bitmap;
+
+	    if (! _cairo_surface_is_image (surface)) {
+		cairo_status_t status;
+
+		status = _cairo_surface_acquire_source_image (surface,
+							      &cr->source_image,
+							      &cr->source_extra);
+		if (status)
+		    return NULL;
+
+		surface = &cr->source_image->base;
+	    }
+
+	    if (unlikely (! surface_to_sk_bitmap (surface, bitmap)))
+		return NULL;
+
+	    shader = SkShader::CreateBitmapShader (bitmap,
+						   extend_to_sk (pattern->extend),
+						   extend_to_sk (pattern->extend));
+	}
+    } else if (pattern->type == CAIRO_PATTERN_TYPE_LINEAR
+	       /* || pattern->type == CAIRO_PATTERN_TYPE_RADIAL */)
+    {
+	cairo_gradient_pattern_t *gradient = (cairo_gradient_pattern_t *) pattern;
+	SkColor colors_stack[10];
+	SkScalar pos_stack[10];
+	SkColor *colors = colors_stack;
+	SkScalar *pos = pos_stack;
+
+	if (gradient->n_stops > 10) {
+	    colors = new SkColor[gradient->n_stops];
+	    pos = new SkScalar[gradient->n_stops];
+	}
+
+	for (unsigned int i = 0; i < gradient->n_stops; i++) {
+	    pos[i] = CAIRO_FIXED_TO_SK_SCALAR (gradient->stops[i].offset);
+	    colors[i] = color_to_sk (gradient->stops[i].color);
+	}
+
+	if (pattern->type == CAIRO_PATTERN_TYPE_LINEAR) {
+	    cairo_linear_pattern_t *linear = (cairo_linear_pattern_t *) gradient;
+	    SkPoint points[2];
+
+	    points[0].set (CAIRO_FIXED_TO_SK_SCALAR (linear->p1.x),
+			   CAIRO_FIXED_TO_SK_SCALAR (linear->p1.y));
+	    points[1].set (CAIRO_FIXED_TO_SK_SCALAR (linear->p2.x),
+			   CAIRO_FIXED_TO_SK_SCALAR (linear->p2.y));
+	    shader = SkGradientShader::CreateLinear (points, colors, pos, gradient->n_stops,
+						     extend_to_sk (pattern->extend));
+	} else {
+	    // XXX todo -- implement real radial shaders in Skia
+	}
+
+	if (gradient->n_stops > 10) {
+	    delete [] colors;
+	    delete [] pos;
+	}
+    }
+
+    if (shader && ! _cairo_matrix_is_identity (&pattern->matrix))
+	shader->setLocalMatrix (matrix_inverse_to_sk (pattern->matrix));
+
+    return shader;
+}
+
+static inline bool
+pattern_filter_to_sk (const cairo_pattern_t *pattern)
+{
+    switch (pattern->filter) {
+    case CAIRO_FILTER_GOOD:
+    case CAIRO_FILTER_BEST:
+    case CAIRO_FILTER_BILINEAR:
+    case CAIRO_FILTER_GAUSSIAN:
+	return true;
+    default:
+    case CAIRO_FILTER_FAST:
+    case CAIRO_FILTER_NEAREST:
+	return false;
+    }
+}
+
+static inline bool
+pattern_to_sk_color (const cairo_pattern_t *pattern, SkColor& color)
+{
+    if (pattern->type != CAIRO_PATTERN_TYPE_SOLID)
+	return false;
+
+    color = color_to_sk (((cairo_solid_pattern_t *) pattern)->color);
+    return true;
+}
+
+static cairo_status_t
+_cairo_skia_context_set_source (void *abstract_cr,
+				cairo_pattern_t *source)
+{
+    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+    SkColor color;
+
+    if (cr->source != NULL) {
+	if (cr->source_image != NULL) {
+	    _cairo_surface_release_source_image (cr->source, cr->source_image, cr->source_extra);
+	    cr->source_image = NULL;
+	}
+	cairo_surface_destroy (cr->source);
+	cr->source = NULL;
+    }
+
+    if (pattern_to_sk_color (source, color)) {
+	cr->paint->setColor (color);
+    } else {
+	SkShader *shader = source_to_sk_shader (cr, source);
+	if (shader == NULL) {
+	    ASSERT_NOT_REACHED;
+	    return CAIRO_STATUS_SUCCESS;
+	}
+
+	cr->paint->setShader (shader);
+	shader->unref ();
+
+	cr->paint->setFilterBitmap (pattern_filter_to_sk (source));
+    }
+
+    /* XXX change notification */
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_set_source_rgba (void *abstract_cr, double red, double green, double blue, double alpha)
+{
+    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    /* Need unpremultiplied 1-byte values */
+    cr->paint->setARGB ((U8CPU) (alpha * 255),
+			(U8CPU) (red * 255),
+			(U8CPU) (green * 255),
+			(U8CPU) (blue * 255));
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_set_source_surface (void *abstract_cr,
+					cairo_surface_t *surface,
+					double	   x,
+					double	   y)
+{
+    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+    cairo_pattern_t *pattern;
+    cairo_matrix_t matrix;
+    cairo_status_t status;
+
+    if (surface->type == CAIRO_SURFACE_TYPE_SKIA) {
+	cairo_skia_surface_t *esurf = (cairo_skia_surface_t *) surface;
+	SkShader *shader;
+
+	shader = SkShader::CreateBitmapShader (*esurf->bitmap,
+					       SkShader::kClamp_TileMode, /* XXX */
+					       SkShader::kClamp_TileMode);
+
+	cr->paint->setShader (shader);
+	shader->unref ();
+
+	cr->paint->setFilterBitmap (true);
+
+	return CAIRO_STATUS_SUCCESS;
+    }
+
+    pattern = cairo_pattern_create_for_surface (surface);
+    if (unlikely (pattern->status))
+	return pattern->status;
+
+    cairo_matrix_init_translate (&matrix, -x, -y);
+    cairo_pattern_set_matrix (pattern, &matrix);
+
+    status = _cairo_skia_context_set_source (cr, pattern);
+    cairo_pattern_destroy (pattern);
+
+    return status;
+}
+
+static cairo_pattern_t *
+_cairo_skia_context_get_source (void *abstract_cr)
+{
+    //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    ASSERT_NOT_REACHED;
+    return NULL;
+}
+
+static cairo_status_t
+_cairo_skia_context_set_tolerance (void *abstract_cr,
+				   double tolerance)
+{
+    //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    /* XXX ignored */
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static inline SkXfermode::Mode
+operator_to_sk (cairo_operator_t op)
+{
+    static const SkXfermode::Mode modeMap[] = {
+	SkXfermode::kClear_Mode,
+
+	SkXfermode::kSrc_Mode,
+	SkXfermode::kSrcOver_Mode,
+	SkXfermode::kSrcIn_Mode,
+	SkXfermode::kSrcOut_Mode,
+	SkXfermode::kSrcATop_Mode,
+
+	SkXfermode::kDst_Mode,
+	SkXfermode::kDstOver_Mode,
+	SkXfermode::kDstIn_Mode,
+	SkXfermode::kDstOut_Mode,
+	SkXfermode::kDstATop_Mode,
+
+	SkXfermode::kXor_Mode,
+	SkXfermode::kPlus_Mode, // XXX Add?
+	SkXfermode::kPlus_Mode, // XXX SATURATE
+
+	SkXfermode::kPlus_Mode,
+	SkXfermode::kMultiply_Mode,
+	SkXfermode::kScreen_Mode,
+	SkXfermode::kOverlay_Mode,
+	SkXfermode::kDarken_Mode,
+	SkXfermode::kLighten_Mode,
+	SkXfermode::kColorDodge_Mode,
+	SkXfermode::kColorBurn_Mode,
+	SkXfermode::kHardLight_Mode,
+	SkXfermode::kSoftLight_Mode,
+	SkXfermode::kDifference_Mode,
+	SkXfermode::kExclusion_Mode,
+
+	SkXfermode::kSrcOver_Mode, // XXX: CAIRO_OPERATOR_HSL_HUE
+	SkXfermode::kSrcOver_Mode, // XXX: CAIRO_OPERATOR_HSL_SATURATION,
+	SkXfermode::kSrcOver_Mode, // XXX: CAIRO_OPERATOR_HSL_COLOR,
+	SkXfermode::kSrcOver_Mode, // XXX: CAIRO_OPERATOR_HSL_LUMINOSITY
+    };
+
+    return modeMap[op];
+}
+
+static cairo_status_t
+_cairo_skia_context_set_operator (void *abstract_cr, cairo_operator_t op)
+{
+    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    cr->paint->setXfermodeMode (operator_to_sk (op));
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_set_opacity (void *abstract_cr, double opacity)
+{
+    //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    /* XXX */
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_set_antialias (void *abstract_cr, cairo_antialias_t antialias)
+{
+    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    cr->paint->setAntiAlias (antialias != CAIRO_ANTIALIAS_NONE);
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_set_fill_rule (void *abstract_cr,
+				   cairo_fill_rule_t fill_rule)
+{
+    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    cr->path->setFillType (fill_rule == CAIRO_FILL_RULE_WINDING ? SkPath::kWinding_FillType : SkPath::kEvenOdd_FillType);
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_set_line_width (void *abstract_cr,
+				    double line_width)
+{
+    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    cr->paint->setStrokeWidth (SkFloatToScalar (line_width));
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_set_line_cap (void *abstract_cr,
+				  cairo_line_cap_t line_cap)
+{
+    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+    static const SkPaint::Cap map[] = {
+	SkPaint::kButt_Cap,
+	SkPaint::kRound_Cap,
+	SkPaint::kSquare_Cap
+    };
+    cr->paint->setStrokeCap (map[line_cap]);
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_set_line_join (void *abstract_cr,
+				   cairo_line_join_t line_join)
+{
+    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+    static const SkPaint::Join map[] = {
+	SkPaint::kMiter_Join,
+	SkPaint::kRound_Join,
+	SkPaint::kBevel_Join
+    };
+    cr->paint->setStrokeJoin (map[line_join]);
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_set_dash (void *abstract_cr,
+			      const double *dashes,
+			      int	      num_dashes,
+			      double	      offset)
+{
+    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+    SkScalar intervals_static[20];
+    SkScalar *intervals = intervals_static;
+
+    int loop = 0;
+    if ((num_dashes & 1) != 0) {
+	loop = 1;
+	num_dashes <<= 1;
+    }
+
+    if (num_dashes > 20)
+	intervals = new SkScalar[num_dashes];
+
+    int i = 0;
+    do {
+	for (int j = 0; i < num_dashes; j++)
+	    intervals[i++] = SkFloatToScalar (dashes[j]);
+    } while (loop--);
+
+    SkDashPathEffect *dash = new SkDashPathEffect (intervals, num_dashes, SkFloatToScalar (offset));
+
+    cr->paint->setPathEffect (dash);
+    dash->unref ();
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_set_miter_limit (void *abstract_cr, double limit)
+{
+    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    cr->paint->setStrokeMiter (SkFloatToScalar (limit));
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_antialias_t
+_cairo_skia_context_get_antialias (void *abstract_cr)
+{
+    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    return cr->paint->isAntiAlias () ? CAIRO_ANTIALIAS_DEFAULT : CAIRO_ANTIALIAS_NONE;
+}
+
+static void
+_cairo_skia_context_get_dash (void *abstract_cr,
+			      double *dashes,
+			      int *num_dashes,
+			      double *offset)
+{
+    //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    *num_dashes = 0;
+    /* XXX */
+}
+
+static cairo_fill_rule_t
+_cairo_skia_context_get_fill_rule (void *abstract_cr)
+{
+    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+    SkPath::FillType ft;
+
+    ft = cr->path->getFillType ();
+    if (ft == SkPath::kWinding_FillType)
+	return CAIRO_FILL_RULE_WINDING;
+    if (ft == SkPath::kEvenOdd_FillType)
+	return CAIRO_FILL_RULE_EVEN_ODD;;
+
+    ASSERT_NOT_REACHED;
+    return CAIRO_FILL_RULE_WINDING;
+}
+
+static double
+_cairo_skia_context_get_line_width (void *abstract_cr)
+{
+    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    return /* ScalarToFloat */ cr->paint->getStrokeWidth ();
+}
+
+static cairo_line_cap_t
+_cairo_skia_context_get_line_cap (void *abstract_cr)
+{
+    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+    static const cairo_line_cap_t map[] = {
+	CAIRO_LINE_CAP_BUTT,
+	CAIRO_LINE_CAP_ROUND,
+	CAIRO_LINE_CAP_SQUARE
+    };
+    return map[cr->paint->getStrokeCap ()];
+}
+
+static cairo_line_join_t
+_cairo_skia_context_get_line_join (void *abstract_cr)
+{
+    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+    static const cairo_line_join_t map[] = {
+	CAIRO_LINE_JOIN_MITER,
+	CAIRO_LINE_JOIN_ROUND,
+	CAIRO_LINE_JOIN_BEVEL
+    };
+    return map[cr->paint->getStrokeJoin ()];
+}
+
+static double
+_cairo_skia_context_get_miter_limit (void *abstract_cr)
+{
+    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    return /* SkScalarToFloat */ cr->paint->getStrokeMiter ();
+}
+
+static cairo_operator_t
+_cairo_skia_context_get_operator (void *abstract_cr)
+{
+    //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    ASSERT_NOT_REACHED;
+    //cr->paint->getXfermode ();
+    return CAIRO_OPERATOR_OVER;
+}
+
+static double
+_cairo_skia_context_get_opacity (void *abstract_cr)
+{
+    //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    ASSERT_NOT_REACHED;
+    return 1.;
+}
+
+static double
+_cairo_skia_context_get_tolerance (void *abstract_cr)
+{
+    //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    /* XXX */
+    return CAIRO_GSTATE_TOLERANCE_DEFAULT;
+}
+
+
+/* Current tranformation matrix */
+
+static cairo_status_t
+_cairo_skia_context_translate (void *abstract_cr,
+			       double tx,
+			       double ty)
+{
+    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    cairo_matrix_translate (&cr->matrix, tx, ty);
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_scale (void *abstract_cr,
+			   double sx,
+			      double sy)
+{
+    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    cairo_matrix_scale (&cr->matrix, sx, sy);
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_rotate (void *abstract_cr,
+			    double theta)
+{
+    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    cairo_matrix_rotate (&cr->matrix, theta);
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_transform (void *abstract_cr,
+			       const cairo_matrix_t *matrix)
+{
+    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    cairo_matrix_multiply (&cr->matrix, &cr->matrix, matrix);
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_set_matrix (void *abstract_cr,
+				const cairo_matrix_t *matrix)
+{
+    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    cr->matrix = *matrix;
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_set_identity_matrix (void *abstract_cr)
+{
+    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    cairo_matrix_init_identity (&cr->matrix);
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_skia_context_get_matrix (void *abstract_cr,
+				cairo_matrix_t *matrix)
+{
+    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    *matrix = cr->matrix;
+}
+
+static void
+_cairo_skia_context_user_to_device (void *abstract_cr,
+				    double *x,
+				    double *y)
+{
+    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    cairo_matrix_transform_point (&cr->matrix, x, y);
+}
+
+static void
+_cairo_skia_context_user_to_device_distance (void *abstract_cr,
+					     double *dx,
+					     double *dy)
+{
+    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    cairo_matrix_transform_distance (&cr->matrix, dx, dy);
+}
+
+static void
+_cairo_skia_context_device_to_user (void *abstract_cr,
+				    double *x,
+				    double *y)
+{
+    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+    cairo_matrix_t inverse;
+    cairo_status_t status;
+
+    inverse = cr->matrix;
+    status = cairo_matrix_invert (&inverse);
+    assert (CAIRO_STATUS_SUCCESS == status);
+
+    cairo_matrix_transform_point (&inverse, x, y);
+}
+
+static void
+_cairo_skia_context_device_to_user_distance (void *abstract_cr,
+					     double *dx,
+					     double *dy)
+{
+    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+    cairo_matrix_t inverse;
+    cairo_status_t status;
+
+    inverse = cr->matrix;
+    status = cairo_matrix_invert (&inverse);
+    assert (CAIRO_STATUS_SUCCESS == status);
+
+    cairo_matrix_transform_distance (&inverse, dx, dy);
+}
+
+/* Path constructor */
+
+static cairo_status_t
+_cairo_skia_context_new_path (void *abstract_cr)
+{
+    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    cr->path->reset ();
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_new_sub_path (void *abstract_cr)
+{
+    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    cr->path->rMoveTo (0, 0); /* XXX */
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+user_to_device_point (cairo_skia_context_t *cr, double *x, double *y)
+{
+    cairo_matrix_transform_point (&cr->matrix, x, y);
+    cairo_matrix_transform_point (&cr->target->image.base.device_transform, x, y);
+}
+
+static void
+user_to_device_distance (cairo_skia_context_t *cr, double *dx, double *dy)
+{
+    cairo_matrix_transform_distance (&cr->matrix, dx, dy);
+    cairo_matrix_transform_distance (&cr->target->image.base.device_transform, dx, dy);
+}
+
+static cairo_status_t
+_cairo_skia_context_move_to (void *abstract_cr, double x, double y)
+{
+    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    user_to_device_point (cr, &x, &y);
+    cr->path->moveTo (SkFloatToScalar (x), SkFloatToScalar (y));
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_line_to (void *abstract_cr, double x, double y)
+{
+    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    user_to_device_point (cr, &x, &y);
+    cr->path->lineTo (SkFloatToScalar (x), SkFloatToScalar (y));
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_curve_to (void *abstract_cr,
+			      double x1, double y1,
+			      double x2, double y2,
+			      double x3, double y3)
+{
+    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    user_to_device_point (cr, &x1, &y1);
+    user_to_device_point (cr, &x2, &y2);
+    user_to_device_point (cr, &x3, &y3);
+    cr->path->cubicTo (SkFloatToScalar (x1), SkFloatToScalar (y1),
+		       SkFloatToScalar (x2), SkFloatToScalar (y2),
+		       SkFloatToScalar (x3), SkFloatToScalar (y3));
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_arc_to (void *abstract_cr,
+			    double x1, double y1,
+			    double x2, double y2,
+			    double radius)
+{
+    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+#if 0
+    user_to_device_point (cr, &x1, &y1);
+    user_to_device_point (cr, &x2, &y2);
+    user_to_device_distance (cr, &radius, &radius);
+#endif
+
+    cr->path->arcTo (SkFloatToScalar (x1), SkFloatToScalar (y1),
+		     SkFloatToScalar (x2), SkFloatToScalar (y2),
+		     SkFloatToScalar (radius));
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_rel_move_to (void *abstract_cr, double dx, double dy)
+{
+    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    user_to_device_distance (cr, &dx, &dy);
+    cr->path->rMoveTo (SkFloatToScalar (dx), SkFloatToScalar (dy));
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_rel_line_to (void *abstract_cr, double dx, double dy)
+{
+    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    user_to_device_distance (cr, &dx, &dy);
+    cr->path->rLineTo (SkFloatToScalar (dx), SkFloatToScalar (dy));
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_rel_curve_to (void *abstract_cr,
+				  double dx1, double dy1,
+				  double dx2, double dy2,
+				  double dx3, double dy3)
+{
+    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    user_to_device_distance (cr, &dx1, &dy1);
+    user_to_device_distance (cr, &dx2, &dy2);
+    user_to_device_distance (cr, &dx3, &dy3);
+    cr->path->rCubicTo (SkFloatToScalar (dx1), SkFloatToScalar (dy1),
+			SkFloatToScalar (dx2), SkFloatToScalar (dy2),
+			SkFloatToScalar (dx3), SkFloatToScalar (dy3));
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_rel_arc_to (void *abstract_cr,
+				double dx1, double dy1,
+				double dx2, double dy2,
+				double radius)
+{
+    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+#if 0
+    user_to_device_point (cr, &x1, &y1);
+    user_to_device_point (cr, &x2, &y2);
+    user_to_device_distance (cr, &radius, &radius);
+#endif
+
+    cr->path->arcTo (SkFloatToScalar (dx1), SkFloatToScalar (dy1),
+		     SkFloatToScalar (dx2), SkFloatToScalar (dy2),
+		     SkFloatToScalar (radius));
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_close_path (void *abstract_cr)
+{
+    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    cr->path->close ();
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_rectangle (void *abstract_cr,
+			       double x, double y,
+			       double width, double height)
+{
+    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+    double x1, y1, x2, y2;
+
+    /* XXX assume no rotation! */
+    x1 = x, y1 = y;
+    user_to_device_point (cr, &x1, &y1);
+
+    x2 = x + width, y2 = y + height;
+    user_to_device_point (cr, &x2, &y2);
+
+    cr->path->addRect (SkFloatToScalar (x1), SkFloatToScalar (y1),
+		       SkFloatToScalar (x2), SkFloatToScalar (y2));
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_arc (void *abstract_cr,
+			 double xc, double yc, double radius,
+			 double angle1, double angle2,
+			 cairo_bool_t forward)
+{
+    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+    cairo_status_t status;
+
+    /* XXX cr->path->arc() */
+
+    while (angle2 < angle1)
+	angle2 += 2 * M_PI;
+
+    status = _cairo_skia_context_line_to (cr,
+					  xc + radius * cos (angle1),
+					  yc + radius * sin (angle1));
+    if (unlikely (status))
+	return status;
+
+    if (forward)
+	_cairo_arc_path (&cr->base, xc, yc, radius, angle1, angle2);
+    else
+	_cairo_arc_path_negative (&cr->base, xc, yc, radius, angle1, angle2);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_skia_context_path_extents (void *abstract_cr,
+				  double *x1,
+				  double *y1,
+				  double *x2,
+				  double *y2)
+{
+    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+    SkRect rect;
+
+    rect = cr->path->getBounds ();
+
+    ASSERT_NOT_REACHED;
+    /* XXX transform SkScalar rect to user */
+}
+
+static cairo_bool_t
+_cairo_skia_context_has_current_point (void *abstract_cr)
+{
+    //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    return TRUE;
+}
+
+static cairo_bool_t
+_cairo_skia_context_get_current_point (void *abstract_cr,
+				       double *x,
+				       double *y)
+{
+    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+    SkPoint pt;
+
+    cr->path->getLastPt (&pt);
+    //*x = SkScalarToFloat (pt.x);
+    //*y = SkScalarToFloat (pt.y);
+    //_cairo_gstate_backend_to_user (cr->gstate, x, y);
+
+    return TRUE;
+}
+
+static cairo_path_t *
+_cairo_skia_context_copy_path (void *abstract_cr)
+{
+    //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    /* XXX iterate */
+    ASSERT_NOT_REACHED;
+    return NULL;
+}
+
+static cairo_path_t *
+_cairo_skia_context_copy_path_flat (void *abstract_cr)
+{
+    //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    /* XXX iterate and decompose */
+    ASSERT_NOT_REACHED;
+    return NULL;
+}
+
+static cairo_status_t
+_cairo_skia_context_append_path (void *abstract_cr,
+				 const cairo_path_t *path)
+{
+    //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+   // return _cairo_path_append_to_context (path, cr);
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_stroke_to_path (void *abstract_cr)
+{
+    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    cr->paint->getFillPath (*cr->path, cr->path);
+    return CAIRO_STATUS_SUCCESS;
+}
+
+
+static cairo_status_t
+_cairo_skia_context_paint (void *abstract_cr)
+{
+    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    if (cr->source != NULL) {
+	SkBitmap bitmap;
+	SkMatrix bitmapMatrix;
+
+	if (cr->source->type == CAIRO_SURFACE_TYPE_SKIA) {
+	    cairo_skia_surface_t *esurf = (cairo_skia_surface_t *) cr->source->type;
+
+	    bitmap = *esurf->bitmap;
+	} else {
+	    surface_to_sk_bitmap (&cr->source_image->base, bitmap);
+	}
+
+	// XXX pattern->matrix, pattern->filter, pattern->extend
+	cr->canvas->drawBitmapMatrix (bitmap, bitmapMatrix, cr->paint);
+    } else {
+	cr->canvas->drawPaint (*cr->paint);
+    }
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_mask (void *abstract_cr,
+			     cairo_pattern_t *mask)
+{
+    //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    ASSERT_NOT_REACHED;
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_stroke_preserve (void *abstract_cr)
+{
+    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    /* XXX pen transformation? */
+    assert (_cairo_matrix_is_identity (&cr->matrix));
+    cr->canvas->drawPath (*cr->path, *cr->paint);
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_stroke (void *abstract_cr)
+{
+    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+    cairo_status_t status;
+
+    status = _cairo_skia_context_stroke_preserve (cr);
+    if (unlikely (status))
+	return status;
+
+    return _cairo_skia_context_new_path (cr);
+}
+
+static cairo_status_t
+_cairo_skia_context_in_stroke (void *abstract_cr,
+			       double x, double y,
+			       cairo_bool_t *inside)
+{
+    //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    ASSERT_NOT_REACHED;
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_stroke_extents (void *abstract_cr,
+				    double *x1, double *y1, double *x2, double *y2)
+{
+    //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    ASSERT_NOT_REACHED;
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_fill_preserve (void *abstract_cr)
+{
+    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    cr->canvas->drawPath (*cr->path, *cr->paint);
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_fill (void *abstract_cr)
+{
+    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+    cairo_status_t status;
+
+    status = _cairo_skia_context_fill_preserve (cr);
+    if (unlikely (status))
+	return status;
+
+    return _cairo_skia_context_new_path (cr);
+}
+
+static cairo_status_t
+_cairo_skia_context_in_fill (void *abstract_cr,
+				double x, double y,
+				cairo_bool_t *inside)
+{
+    //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    ASSERT_NOT_REACHED;
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_fill_extents (void *abstract_cr,
+				     double *x1, double *y1, double *x2, double *y2)
+{
+    //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    ASSERT_NOT_REACHED;
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_clip_preserve (void *abstract_cr)
+{
+    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    cr->canvas->clipPath (*cr->path);
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_clip (void *abstract_cr)
+{
+    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+    cairo_status_t status;
+
+    status = _cairo_skia_context_clip_preserve (cr);
+    if (unlikely (status))
+	return status;
+
+    return _cairo_skia_context_new_path (cr);
+}
+
+static cairo_status_t
+_cairo_skia_context_in_clip (void *abstract_cr,
+				double x, double y,
+				cairo_bool_t *inside)
+{
+    //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_reset_clip (void *abstract_cr)
+{
+    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    /* XXX TODO: teach Skia how to reset the clip path ??? */
+    cr->canvas->restore ();
+    cr->canvas->save ();
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_clip_extents (void *abstract_cr,
+				     double *x1, double *y1, double *x2, double *y2)
+{
+    //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    ASSERT_NOT_REACHED;
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_rectangle_list_t *
+_cairo_skia_context_copy_clip_rectangle_list (void *abstract_cr)
+{
+    //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    ASSERT_NOT_REACHED;
+    return NULL;
+}
+
+static cairo_status_t
+_cairo_skia_context_copy_page (void *abstract_cr)
+{
+    //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    ASSERT_NOT_REACHED;
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_show_page (void *abstract_cr)
+{
+    //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+    ASSERT_NOT_REACHED;
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_set_font_face (void *abstract_cr,
+				   cairo_font_face_t *font_face)
+{
+   // cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    //return _cairo_gstate_set_font_face (cr->gstate, font_face);
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_font_face_t *
+_cairo_skia_context_get_font_face (void *abstract_cr)
+{
+    //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    ASSERT_NOT_REACHED;
+    return NULL;
+}
+
+static cairo_status_t
+_cairo_skia_context_font_extents (void *abstract_cr,
+				  cairo_font_extents_t *extents)
+{
+    //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    ASSERT_NOT_REACHED;
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_set_font_size (void *abstract_cr,
+				   double size)
+{
+    //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    ASSERT_NOT_REACHED;
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_set_font_matrix (void *abstract_cr,
+				     const cairo_matrix_t *matrix)
+{
+    //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    ASSERT_NOT_REACHED;
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_skia_context_get_font_matrix (void *abstract_cr,
+				     cairo_matrix_t *matrix)
+{
+    //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    ASSERT_NOT_REACHED;
+}
+
+static cairo_status_t
+_cairo_skia_context_set_font_options (void *abstract_cr,
+				      const cairo_font_options_t *options)
+{
+    //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    ASSERT_NOT_REACHED;
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_skia_context_get_font_options (void *abstract_cr,
+				      cairo_font_options_t *options)
+{
+    //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    ASSERT_NOT_REACHED;
+}
+
+static cairo_status_t
+_cairo_skia_context_set_scaled_font (void *abstract_cr,
+					cairo_scaled_font_t *scaled_font)
+{
+    //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    ASSERT_NOT_REACHED;
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_scaled_font_t *
+_cairo_skia_context_get_scaled_font (void *abstract_cr)
+{
+    //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    ASSERT_NOT_REACHED;
+    return NULL;
+}
+
+static cairo_status_t
+_cairo_skia_context_glyphs (void *abstract_cr,
+			    const cairo_glyph_t *glyphs,
+			    int num_glyphs,
+			    cairo_glyph_text_info_t *info)
+{
+    //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    ASSERT_NOT_REACHED;
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_glyph_path (void *abstract_cr,
+				const cairo_glyph_t *glyphs,
+				int num_glyphs)
+{
+    //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    ASSERT_NOT_REACHED;
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_glyph_extents (void                *abstract_cr,
+				   const cairo_glyph_t    *glyphs,
+				   int                    num_glyphs,
+				   cairo_text_extents_t   *extents)
+{
+    //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    ASSERT_NOT_REACHED;
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static const cairo_backend_t _cairo_skia_context_backend = {
+    _cairo_skia_context_destroy,
+
+    _cairo_skia_context_get_original_target,
+    _cairo_skia_context_get_current_target,
+
+    _cairo_skia_context_save,
+    _cairo_skia_context_restore,
+
+    _cairo_skia_context_push_group,
+    _cairo_skia_context_pop_group,
+
+    _cairo_skia_context_set_source_rgba,
+    _cairo_skia_context_set_source_surface,
+    _cairo_skia_context_set_source,
+    _cairo_skia_context_get_source,
+
+    _cairo_skia_context_set_antialias,
+    _cairo_skia_context_set_dash,
+    _cairo_skia_context_set_fill_rule,
+    _cairo_skia_context_set_line_cap,
+    _cairo_skia_context_set_line_join,
+    _cairo_skia_context_set_line_width,
+    _cairo_skia_context_set_miter_limit,
+    _cairo_skia_context_set_opacity,
+    _cairo_skia_context_set_operator,
+    _cairo_skia_context_set_tolerance,
+    _cairo_skia_context_get_antialias,
+    _cairo_skia_context_get_dash,
+    _cairo_skia_context_get_fill_rule,
+    _cairo_skia_context_get_line_cap,
+    _cairo_skia_context_get_line_join,
+    _cairo_skia_context_get_line_width,
+    _cairo_skia_context_get_miter_limit,
+    _cairo_skia_context_get_opacity,
+    _cairo_skia_context_get_operator,
+    _cairo_skia_context_get_tolerance,
+
+    _cairo_skia_context_translate,
+    _cairo_skia_context_scale,
+    _cairo_skia_context_rotate,
+    _cairo_skia_context_transform,
+    _cairo_skia_context_set_matrix,
+    _cairo_skia_context_set_identity_matrix,
+    _cairo_skia_context_get_matrix,
+    _cairo_skia_context_user_to_device,
+    _cairo_skia_context_user_to_device_distance,
+    _cairo_skia_context_device_to_user,
+    _cairo_skia_context_device_to_user_distance,
+
+    _cairo_skia_context_new_path,
+    _cairo_skia_context_new_sub_path,
+    _cairo_skia_context_move_to,
+    _cairo_skia_context_rel_move_to,
+    _cairo_skia_context_line_to,
+    _cairo_skia_context_rel_line_to,
+    _cairo_skia_context_curve_to,
+    _cairo_skia_context_rel_curve_to,
+    _cairo_skia_context_arc_to,
+    _cairo_skia_context_rel_arc_to,
+    _cairo_skia_context_close_path,
+    _cairo_skia_context_arc,
+    _cairo_skia_context_rectangle,
+    _cairo_skia_context_path_extents,
+    _cairo_skia_context_has_current_point,
+    _cairo_skia_context_get_current_point,
+    _cairo_skia_context_copy_path,
+    _cairo_skia_context_copy_path_flat,
+    _cairo_skia_context_append_path,
+
+    _cairo_skia_stroke_to_path,
+
+    _cairo_skia_context_clip,
+    _cairo_skia_context_clip_preserve,
+    _cairo_skia_context_in_clip,
+    _cairo_skia_context_clip_extents,
+    _cairo_skia_context_reset_clip,
+    _cairo_skia_context_copy_clip_rectangle_list,
+
+    _cairo_skia_context_paint,
+    _cairo_skia_context_mask,
+
+    _cairo_skia_context_stroke,
+    _cairo_skia_context_stroke_preserve,
+    _cairo_skia_context_in_stroke,
+    _cairo_skia_context_stroke_extents,
+
+    _cairo_skia_context_fill,
+    _cairo_skia_context_fill_preserve,
+    _cairo_skia_context_in_fill,
+    _cairo_skia_context_fill_extents,
+
+    _cairo_skia_context_set_font_face,
+    _cairo_skia_context_get_font_face,
+    _cairo_skia_context_set_font_size,
+    _cairo_skia_context_set_font_matrix,
+    _cairo_skia_context_get_font_matrix,
+    _cairo_skia_context_set_font_options,
+    _cairo_skia_context_get_font_options,
+    _cairo_skia_context_set_scaled_font,
+    _cairo_skia_context_get_scaled_font,
+    _cairo_skia_context_font_extents,
+
+    _cairo_skia_context_glyphs,
+    _cairo_skia_context_glyph_path,
+    _cairo_skia_context_glyph_extents,
+
+    _cairo_skia_context_copy_page,
+    _cairo_skia_context_show_page,
+};
+
+cairo_t *
+_cairo_skia_context_create (void *target)
+{
+    cairo_skia_surface_t *surface = (cairo_skia_surface_t *) target;
+    cairo_skia_context_t *cr;
+
+    cr = _context_get ();
+    if (unlikely (cr == NULL)) {
+	    cr = new cairo_skia_context_t;
+	    if (unlikely (cr == NULL))
+		return _cairo_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+	    cr->path = new SkPath;
+	    cr->paint = new SkPaint;
+    }
+
+    _cairo_init (&cr->base, &_cairo_skia_context_backend);
+
+    cr->source = NULL;
+    cr->source_image = NULL;
+
+    cr->target = (cairo_skia_surface_t *) cairo_surface_reference ((cairo_surface_t *) target);
+    cr->original = (cairo_skia_surface_t *) cairo_surface_reference ((cairo_surface_t *) target);
+    cr->canvas = new SkCanvas (*surface->bitmap);
+    cr->canvas->save ();
+
+    cairo_matrix_init_identity (&cr->matrix);
+
+    return &cr->base;
+}
+
+#if 0
+void
+_cairo_skia_context_set_SkPaint (cairo_t *cr, SkPaint paint)
+{
+    *cr->paint = paint;
+}
+
+void
+_cairo_skia_context_set_SkPath (cairo_t *cr, SkPath path)
+{
+    *cr->path = path;
+}
+#endif
diff --git a/src/skia/cairo-skia-private.h b/src/skia/cairo-skia-private.h
new file mode 100644
index 0000000..7e85613
--- /dev/null
+++ b/src/skia/cairo-skia-private.h
@@ -0,0 +1,109 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2005 Red Hat, Inc.
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 Red Hat, Inc.
+ *
+ * Contributor(s):
+ *	Carl D. Worth <cworth at redhat.com>
+ */
+
+#ifndef CAIRO_SKIA_CONTEXT_PRIVATE_H
+#define CAIRO_SKIA_CONTEXT_PRIVATE_H
+
+#include "cairo-private.h"
+#include "cairo-image-surface-private.h"
+
+#include <SkBitmap.h>
+#include <SkCanvas.h>
+#include <SkPaint.h>
+#include <SkPath.h>
+
+typedef struct _cairo_skia_context cairo_skia_context_t;
+typedef struct _cairo_skia_surface cairo_skia_surface_t;
+
+struct _cairo_skia_context {
+    cairo_t base;
+
+    cairo_skia_surface_t *original;
+    cairo_skia_surface_t *target;
+
+    cairo_matrix_t matrix;
+
+    SkCanvas *canvas;
+    SkPaint *paint;
+    SkPath *path;
+
+    cairo_surface_t *source;
+    cairo_image_surface_t *source_image;
+    void *source_extra;
+};
+
+struct _cairo_skia_surface {
+    cairo_image_surface_t image;
+
+    SkBitmap *bitmap;
+};
+
+static inline bool
+format_to_sk_config (cairo_format_t format,
+		     SkBitmap::Config& config,
+		     bool& opaque)
+{
+    opaque = false;
+
+    switch (format) {
+    case CAIRO_FORMAT_ARGB32:
+	config = SkBitmap::kARGB_8888_Config;
+	break;
+    case CAIRO_FORMAT_RGB24:
+	config = SkBitmap::kARGB_8888_Config;
+	opaque = true;
+	break;
+    case CAIRO_FORMAT_RGB16_565:
+	config = SkBitmap::kRGB_565_Config;
+	opaque = true;
+	break;
+    case CAIRO_FORMAT_A8:
+	config = SkBitmap::kA8_Config;
+	break;
+    case CAIRO_FORMAT_A1:
+	config = SkBitmap::kA1_Config;
+	break;
+    case CAIRO_FORMAT_INVALID:
+    default:
+	return false;
+    }
+
+    return true;
+}
+
+cairo_private cairo_t *
+_cairo_skia_context_create (void *target);
+
+#endif /* CAIRO_SKIA_CONTEXT_PRIVATE_H */
diff --git a/src/skia/cairo-skia-surface.cpp b/src/skia/cairo-skia-surface.cpp
new file mode 100644
index 0000000..95ec84d
--- /dev/null
+++ b/src/skia/cairo-skia-surface.cpp
@@ -0,0 +1,278 @@
+/* -*- Mode: c++; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2007 Mozilla Corporation
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 Mozilla Corporation.
+ *
+ * Contributor(s):
+ *	Vladimir Vukicevic <vladimir at mozilla.com>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-skia.h"
+#include "cairo-skia-private.h"
+
+#include "cairo-error-private.h"
+#include "cairo-foreign-context-private.h"
+
+static cairo_skia_surface_t *
+_cairo_skia_surface_create_internal (SkBitmap::Config config,
+				     bool opaque,
+				     unsigned char *data,
+				     int width,
+				     int height,
+				     int stride);
+
+static cairo_surface_t *
+_cairo_skia_surface_create_similar (void *asurface,
+				    cairo_content_t content,
+				    int width,
+				    int height)
+{
+    cairo_skia_surface_t *surface = (cairo_skia_surface_t *) asurface;
+    SkBitmap::Config config;
+    bool opaque;
+
+    if (content == surface->image.base.content)
+    {
+	config = surface->bitmap->getConfig ();
+	opaque = surface->bitmap->isOpaque ();
+    }
+    else if (! format_to_sk_config (_cairo_format_from_content (content),
+				    config, opaque))
+    {
+	return NULL;
+    }
+
+    return &_cairo_skia_surface_create_internal (config, opaque,
+						 NULL,
+						 width, height,
+						 0)->image.base;
+}
+
+static cairo_status_t
+_cairo_skia_surface_finish (void *asurface)
+{
+    cairo_skia_surface_t *surface = (cairo_skia_surface_t *) asurface;
+
+    delete surface->bitmap;
+
+    return _cairo_image_surface_finish (&surface->image);
+}
+
+static cairo_status_t
+_cairo_skia_surface_acquire_source_image (void *asurface,
+					  cairo_image_surface_t **image_out,
+					  void **image_extra)
+{
+    cairo_skia_surface_t *surface = (cairo_skia_surface_t *) asurface;
+
+    surface->bitmap->lockPixels ();
+
+    *image_out = &surface->image;
+    *image_extra = NULL;
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_skia_surface_release_source_image (void *asurface,
+					  cairo_image_surface_t *image,
+					  void *image_extra)
+{
+    cairo_skia_surface_t *surface = (cairo_skia_surface_t *) asurface;
+
+    surface->bitmap->unlockPixels ();
+}
+
+static const struct _cairo_surface_backend
+cairo_skia_surface_backend = {
+    CAIRO_SURFACE_TYPE_SKIA,
+    _cairo_skia_context_create,
+
+    _cairo_skia_surface_create_similar,
+    _cairo_skia_surface_finish,
+    _cairo_skia_surface_acquire_source_image,
+    _cairo_skia_surface_release_source_image,
+
+    NULL, NULL,
+    NULL, /* clone similar */
+    NULL, /* composite */
+    NULL, /* fill_rectangles */
+    NULL, /* composite_trapezoids */
+    NULL, /* create_span_renderer */
+    NULL, /* check_span_renderer */
+
+    NULL, /* copy_page */
+    NULL, /* show_page */
+
+    _cairo_image_surface_get_extents,
+    NULL, /* old_show_glyphs */
+    _cairo_image_surface_get_font_options,
+    NULL, /* flush */
+    NULL, /* mark_dirty_rectangle */
+    NULL, /* scaled_font_fini */
+    NULL, /* scaled_glyph_fini */
+
+    /* XXX native surface functions */
+    _cairo_foreign_context_paint,
+    _cairo_foreign_context_mask,
+    _cairo_foreign_context_stroke,
+    _cairo_foreign_context_fill,
+    _cairo_foreign_context_glyphs
+};
+
+/*
+ * Surface constructors
+ */
+
+static inline pixman_format_code_t
+sk_config_to_pixman_format_code (SkBitmap::Config config,
+				 bool opaque)
+{
+    switch (config) {
+    case SkBitmap::kARGB_8888_Config:
+	return opaque ? PIXMAN_x8r8g8b8 : PIXMAN_a8r8g8b8;
+
+    case SkBitmap::kA8_Config:
+	return PIXMAN_a8;
+
+    case SkBitmap::kA1_Config:
+	return PIXMAN_a1;
+    case SkBitmap::kRGB_565_Config:
+	return PIXMAN_r5g6b5;
+    case SkBitmap::kARGB_4444_Config:
+	return PIXMAN_a4r4g4b4;
+
+    case SkBitmap::kNo_Config:
+    case SkBitmap::kIndex8_Config:
+    case SkBitmap::kRLE_Index8_Config:
+    case SkBitmap::kConfigCount:
+    default:
+	ASSERT_NOT_REACHED;
+	return (pixman_format_code_t) -1;
+    }
+}
+static cairo_skia_surface_t *
+_cairo_skia_surface_create_internal (SkBitmap::Config config,
+				     bool opaque,
+				     unsigned char *data,
+				     int width,
+				     int height,
+				     int stride)
+{
+    cairo_skia_surface_t *surface;
+    pixman_image_t *pixman_image;
+    pixman_format_code_t pixman_format;
+
+    surface = (cairo_skia_surface_t *) malloc (sizeof (cairo_skia_surface_t));
+    if (unlikely (surface == NULL))
+	return (cairo_skia_surface_t *) _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+    pixman_format = sk_config_to_pixman_format_code (config, opaque);
+    pixman_image = pixman_image_create_bits (pixman_format,
+					     width, height,
+					     (uint32_t *) data, stride);
+    if (unlikely (pixman_image == NULL))
+	return (cairo_skia_surface_t *) _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+    _cairo_surface_init (&surface->image.base,
+			 &cairo_skia_surface_backend,
+			 NULL, /* device */
+			 _cairo_content_from_pixman_format (pixman_format));
+
+    _cairo_image_surface_init (&surface->image, pixman_image, pixman_format);
+
+    surface->bitmap = new SkBitmap;
+    surface->bitmap->setConfig (config, width, height, surface->image.stride);
+    surface->bitmap->setIsOpaque (opaque);
+    surface->bitmap->setPixels (surface->image.data);
+
+    surface->image.base.is_clear = data == NULL;
+
+    return surface;
+}
+
+cairo_surface_t *
+cairo_skia_surface_create (cairo_format_t format,
+			   int width,
+			   int height)
+{
+    SkBitmap::Config config;
+    bool opaque;
+
+    if (! CAIRO_FORMAT_VALID (format) ||
+	! format_to_sk_config (format, config, opaque))
+    {
+	return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT));
+    }
+
+    return &_cairo_skia_surface_create_internal (config, opaque, NULL, width, height, 0)->image.base;
+}
+
+cairo_surface_t *
+cairo_skia_surface_create_for_data (unsigned char *data,
+				    cairo_format_t format,
+				    int width,
+				    int height,
+				    int stride)
+{
+    SkBitmap::Config config;
+    bool opaque;
+
+    if (! CAIRO_FORMAT_VALID (format) ||
+	! format_to_sk_config (format, config, opaque))
+    {
+	return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT));
+    }
+
+    return &_cairo_skia_surface_create_internal (config, opaque, data, width, height, stride)->image.base;
+}
+
+/***
+
+Todo:
+
+*** Skia:
+
+- mask()
+
+*** Sk:
+
+High:
+- antialiased clipping?
+
+Medium:
+- implement clip path reset (to avoid restore/save)
+- implement complex radial patterns (2 centers and 2 radii)
+
+Low:
+- implement EXTEND_NONE
+
+***/
commit ecd345d68b74cc4dfbe377b219957907186d6fe8
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Wed Aug 3 14:11:49 2011 +0100

    fast-image: perform an incremental stroke
    
    Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>

diff --git a/src/cairo-fast-image-surface.c b/src/cairo-fast-image-surface.c
index 630607b..ab27c74 100644
--- a/src/cairo-fast-image-surface.c
+++ b/src/cairo-fast-image-surface.c
@@ -2820,7 +2820,7 @@ _composite_polygon (cairo_fast_image_surface_t *dst,
 							r->x + r->width,
 							r->y + r->height,
 							fill_rule);
-	status = converter->add_polygon (converter, polygon);
+	status = _cairo_tor33_scan_converter_add_polygon (converter, polygon);
 	if (unlikely (status))
 	    goto CLEANUP_CONVERTER;
     }
@@ -2913,6 +2913,130 @@ _clip_and_composite_polygon (cairo_fast_image_surface_t *dst,
 				extents, need_bounded_clip (extents));
 }
 
+struct fast_image_surface_incremental_stroke {
+    fast_image_span_renderer_t renderer;
+    cairo_scan_converter_t *converter;
+};
+
+static cairo_status_t
+fast_image_surface_add_triangle (void *closure,
+				 const cairo_point_t triangle[3])
+{
+    struct fast_image_surface_incremental_stroke *stroker = closure;
+    cairo_int_status_t status;
+
+    status = _cairo_tor33_scan_converter_reset (stroker->converter);
+    if (unlikely (status))
+	return status;
+
+    status = _cairo_tor33_scan_converter_add_contour (stroker->converter,
+						      triangle, 3, TRUE);
+    if (unlikely (status))
+	return status;
+
+    return stroker->converter->generate (stroker->converter,
+					 &stroker->renderer.base);
+}
+
+static cairo_status_t
+fast_image_surface_add_triangle_fan (void *closure,
+				     const cairo_point_t *midpt,
+				     const cairo_point_t *points,
+				     int npoints)
+{
+    struct fast_image_surface_incremental_stroke *stroker = closure;
+    cairo_int_status_t status;
+
+    status = _cairo_tor33_scan_converter_reset (stroker->converter);
+    if (unlikely (status))
+	return status;
+
+    status = _cairo_tor33_scan_converter_add_edge (stroker->converter,
+						   midpt, points);
+    if (unlikely (status))
+	return status;
+
+    status = _cairo_tor33_scan_converter_add_contour (stroker->converter,
+						      points, npoints, FALSE);
+    if (unlikely (status))
+	return status;
+
+    status = _cairo_tor33_scan_converter_add_edge (stroker->converter,
+						   &points[npoints-1], midpt);
+    if (unlikely (status))
+	return status;
+
+    return stroker->converter->generate (stroker->converter,
+					 &stroker->renderer.base);
+}
+
+static cairo_status_t
+fast_image_surface_add_convex_quad (void *closure,
+				    const cairo_point_t quad[4])
+{
+    struct fast_image_surface_incremental_stroke *stroker = closure;
+    cairo_int_status_t status;
+
+    status = _cairo_tor33_scan_converter_reset (stroker->converter);
+    if (unlikely (status))
+	return status;
+
+    status = _cairo_tor33_scan_converter_add_contour (stroker->converter,
+						      quad, 4, TRUE);
+    if (unlikely (status))
+	return status;
+
+    return stroker->converter->generate (stroker->converter,
+					 &stroker->renderer.base);
+}
+
+static cairo_int_status_t
+fast_image_surface_incremental_stroke (cairo_fast_image_surface_t *surface,
+				       cairo_operator_t		 op,
+				       const cairo_pattern_t	*source,
+				       const cairo_path_fixed_t	*path,
+				       const cairo_stroke_style_t	*style,
+				       const cairo_matrix_t	*ctm,
+				       const cairo_matrix_t	*ctm_inverse,
+				       double			 tolerance,
+				       cairo_antialias_t	 antialias,
+				       cairo_composite_rectangles_t *composite)
+{
+    struct fast_image_surface_incremental_stroke stroker;
+    const cairo_rectangle_int_t *r = &composite->bounded;
+    cairo_int_status_t status;
+
+    if (! composite->is_bounded)
+	return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    if (composite->clip->path)
+	return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    status = fast_image_span_renderer_init (&stroker.renderer,
+					    surface, op, source, 1.,
+					    composite, FALSE);
+    if (unlikely (status))
+	return status;
+
+    stroker.converter = _cairo_tor33_scan_converter_create (r->x, r->y,
+							    r->x + r->width,
+							    r->y + r->height,
+							    CAIRO_FILL_RULE_WINDING);
+
+    status = _cairo_path_fixed_stroke_to_shaper (path, style,
+						 ctm, ctm_inverse,
+						 tolerance,
+						 fast_image_surface_add_triangle,
+						 fast_image_surface_add_triangle_fan,
+						 fast_image_surface_add_convex_quad,
+						 &stroker);
+
+    _cairo_image_span_renderer_fini (&stroker.renderer);
+    stroker.converter->destroy (stroker.converter);
+
+    return status;
+}
+
 static cairo_int_status_t
 _cairo_fast_image_surface_stroke (void			*abstract_surface,
 				  cairo_operator_t		 op,
@@ -2959,6 +3083,14 @@ _cairo_fast_image_surface_stroke (void			*abstract_surface,
     }
 
     if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
+	status = fast_image_surface_incremental_stroke (surface, op, source,
+							path, style,
+							ctm, ctm_inverse,
+							tolerance, antialias,
+							&composite);
+    }
+
+    if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
 	cairo_polygon_t polygon;
 	cairo_box_t extents;
 
diff --git a/src/cairo-path-stroke.c b/src/cairo-path-stroke.c
index a164b28..a182e65 100644
--- a/src/cairo-path-stroke.c
+++ b/src/cairo-path-stroke.c
@@ -1223,8 +1223,8 @@ _cairo_stroker_close_path (void *closure)
     return CAIRO_STATUS_SUCCESS;
 }
 
-cairo_status_t
-_cairo_path_fixed_stroke_to_shaper (cairo_path_fixed_t	*path,
+cairo_int_status_t
+_cairo_path_fixed_stroke_to_shaper (const cairo_path_fixed_t	*path,
 				    const cairo_stroke_style_t	*stroke_style,
 				    const cairo_matrix_t	*ctm,
 				    const cairo_matrix_t	*ctm_inverse,
diff --git a/src/cairo-spans-private.h b/src/cairo-spans-private.h
index d35da4f..3acfd48 100644
--- a/src/cairo-spans-private.h
+++ b/src/cairo-spans-private.h
@@ -123,6 +123,20 @@ _cairo_clip_tor_scan_converter_create (cairo_clip_t *clip,
 				       cairo_polygon_t *polygon,
 				       cairo_fill_rule_t fill_rule);
 
+cairo_private cairo_status_t
+_cairo_tor33_scan_converter_add_edge (void		*converter,
+				      const cairo_point_t *p1,
+				      const cairo_point_t *p2);
+
+cairo_private cairo_status_t
+_cairo_tor33_scan_converter_add_contour (void		*converter,
+					 const cairo_point_t *points,
+					 int npoints,
+					 cairo_bool_t close);
+
+cairo_private cairo_status_t
+_cairo_tor33_scan_converter_reset (void *converter);
+
 typedef struct _cairo_rectangular_scan_converter {
     cairo_scan_converter_t base;
 
diff --git a/src/cairo-tor33-scan-converter.c b/src/cairo-tor33-scan-converter.c
index 1141f11..3bee35a 100644
--- a/src/cairo-tor33-scan-converter.c
+++ b/src/cairo-tor33-scan-converter.c
@@ -999,6 +999,11 @@ polygon_reset (struct polygon *polygon,
 
     pool_reset(polygon->edge_pool.base);
 
+    if (polygon->ymin == ymin && polygon->ymax == ymax) {
+	memset (polygon->y_buckets, 0, num_buckets * sizeof (struct edge *));
+	return CAIRO_STATUS_SUCCESS;
+    }
+
     if (unlikely (h > 0x7FFFFFFFU - GRID_Y))
 	goto bail_no_mem; /* even if you could, you wouldn't want to. */
 
@@ -1893,40 +1898,6 @@ _cairo_tor33_scan_converter_destroy (void *converter)
 }
 
 static cairo_status_t
-_cairo_tor33_scan_converter_add_edge (void		*converter,
-				    const cairo_point_t *p1,
-				    const cairo_point_t *p2,
-				    int top, int bottom,
-				    int dir)
-{
-    cairo_tor33_scan_converter_t *self = converter;
-    cairo_edge_t edge;
-
-    edge.line.p1 = *p1;
-    edge.line.p2 = *p2;
-    edge.top = top;
-    edge.bottom = bottom;
-    edge.dir = dir;
-
-    glitter_scan_converter_add_edge (self->converter, &edge);
-    return CAIRO_STATUS_SUCCESS;
-}
-
-cairo_status_t
-_cairo_tor33_scan_converter_add_polygon (void		*converter,
-				       const cairo_polygon_t *polygon)
-{
-    cairo_tor33_scan_converter_t *self = converter;
-    int i;
-
-    for (i = 0; i < polygon->num_edges; i++)
-	 glitter_scan_converter_add_edge (self->converter,
-						  &polygon->edges[i]);
-
-    return CAIRO_STATUS_SUCCESS;
-}
-
-static cairo_status_t
 _cairo_tor33_scan_converter_generate (void			*converter,
 				    cairo_span_renderer_t	*renderer)
 {
@@ -1960,8 +1931,6 @@ _cairo_tor33_scan_converter_create (int			xmin,
     }
 
     self->base.destroy = _cairo_tor33_scan_converter_destroy;
-    self->base.add_edge = _cairo_tor33_scan_converter_add_edge;
-    self->base.add_polygon = _cairo_tor33_scan_converter_add_polygon;
     self->base.generate = _cairo_tor33_scan_converter_generate;
 
     pool_init (self->span_pool.base, &self->jmp,
@@ -1983,3 +1952,84 @@ _cairo_tor33_scan_converter_create (int			xmin,
  bail_nomem:
     return _cairo_scan_converter_create_in_error (status);
 }
+
+cairo_status_t
+_cairo_tor33_scan_converter_add_edge (void		*converter,
+				      const cairo_point_t *p1,
+				      const cairo_point_t *p2)
+{
+    cairo_tor33_scan_converter_t *self = converter;
+    cairo_edge_t edge;
+    int dir;
+
+    if (p1->y == p2->y)
+	return CAIRO_STATUS_SUCCESS;
+
+    if (p1->y < p2->y) {
+	dir = 1;
+    } else {
+	const cairo_point_t *tmp = p1;
+	p1 = p2;
+	p2 = tmp;
+	dir = -1;
+    }
+
+    edge.line.p1 = *p1;
+    edge.line.p2 = *p2;
+    edge.top = p1->y;
+    edge.bottom = p2->y;
+    edge.dir = dir;
+
+    glitter_scan_converter_add_edge (self->converter, &edge);
+    return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_status_t
+_cairo_tor33_scan_converter_add_contour (void		*converter,
+					 const cairo_point_t *points,
+					 int npoints,
+					 cairo_bool_t close)
+{
+    cairo_status_t status;
+    int n;
+
+    for (n = 0; n < npoints-1; n++) {
+	status = _cairo_tor33_scan_converter_add_edge (converter,
+						       &points[n], &points[n+1]);
+	if (unlikely (status))
+	    return status;
+    }
+
+    status = CAIRO_STATUS_SUCCESS;
+    if (close)
+	status = _cairo_tor33_scan_converter_add_edge (converter,
+						       &points[npoints-1],
+						       &points[0]);
+    return status;
+}
+
+cairo_status_t
+_cairo_tor33_scan_converter_add_polygon (void		*converter,
+				       const cairo_polygon_t *polygon)
+{
+    cairo_tor33_scan_converter_t *self = converter;
+    int i;
+
+    for (i = 0; i < polygon->num_edges; i++)
+	 glitter_scan_converter_add_edge (self->converter,
+					  &polygon->edges[i]);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_status_t
+_cairo_tor33_scan_converter_reset (void *converter)
+{
+    cairo_tor33_scan_converter_t *self = converter;
+
+    active_list_reset(self->converter->active);
+    cell_list_reset(self->converter->coverages);
+    return polygon_reset(self->converter->polygon,
+			 self->converter->ymin,
+			 self->converter->ymax);
+}
diff --git a/src/cairoint.h b/src/cairoint.h
index 049fc92..4354e3f 100644
--- a/src/cairoint.h
+++ b/src/cairoint.h
@@ -1454,8 +1454,8 @@ _cairo_path_fixed_stroke_to_traps (const cairo_path_fixed_t	*path,
 				   double		 tolerance,
 				   cairo_traps_t	*traps);
 
-cairo_private cairo_status_t
-_cairo_path_fixed_stroke_to_shaper (cairo_path_fixed_t	*path,
+cairo_private cairo_int_status_t
+_cairo_path_fixed_stroke_to_shaper (const cairo_path_fixed_t	*path,
 				   const cairo_stroke_style_t	*stroke_style,
 				   const cairo_matrix_t	*ctm,
 				   const cairo_matrix_t	*ctm_inverse,
commit 995cfdf1802074181678a708061c9322f5daabfe
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Wed Aug 3 13:02:46 2011 +0100

    fast-image: use a low-quality tor scan converter
    
    Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>

diff --git a/src/Makefile.sources b/src/Makefile.sources
index 2a1a1a6..059f6ae 100644
--- a/src/Makefile.sources
+++ b/src/Makefile.sources
@@ -188,6 +188,7 @@ cairo_sources = \
 	cairo-surface-wrapper.c \
 	cairo-system.c \
 	cairo-tor-scan-converter.c \
+	cairo-tor33-scan-converter.c \
 	cairo-clip-tor-scan-converter.c \
 	cairo-toy-font-face.c \
 	cairo-traps.c \
diff --git a/src/cairo-fast-image-surface.c b/src/cairo-fast-image-surface.c
index b50ad80..630607b 100644
--- a/src/cairo-fast-image-surface.c
+++ b/src/cairo-fast-image-surface.c
@@ -2767,13 +2767,13 @@ _composite_spans (void                          *closure,
     if (unlikely (status))
 	return status;
 
-    converter = _cairo_tor_scan_converter_create (r->x, r->y,
-						  r->x + r->width,
-						  r->y + r->height,
-						  info->fill_rule);
+    converter = _cairo_tor33_scan_converter_create (r->x, r->y,
+						    r->x + r->width,
+						    r->y + r->height,
+						    info->fill_rule);
 
-    status= _cairo_tor_scan_converter_add_polygon (converter,
-						   info->polygon);
+    status= _cairo_tor33_scan_converter_add_polygon (converter,
+						     info->polygon);
     if (unlikely (status))
 	goto CLEANUP_CONVERTER;
 
@@ -2816,10 +2816,10 @@ _composite_polygon (cairo_fast_image_surface_t *dst,
 							   polygon,
 							   fill_rule);
     } else {
-	converter = _cairo_tor_scan_converter_create (r->x, r->y,
-						      r->x + r->width,
-						      r->y + r->height,
-						      fill_rule);
+	converter = _cairo_tor33_scan_converter_create (r->x, r->y,
+							r->x + r->width,
+							r->y + r->height,
+							fill_rule);
 	status = converter->add_polygon (converter, polygon);
 	if (unlikely (status))
 	    goto CLEANUP_CONVERTER;
diff --git a/src/cairo-spans-private.h b/src/cairo-spans-private.h
index 45853f0..d35da4f 100644
--- a/src/cairo-spans-private.h
+++ b/src/cairo-spans-private.h
@@ -109,6 +109,16 @@ _cairo_tor_scan_converter_add_polygon (void		*converter,
 				       const cairo_polygon_t *polygon);
 
 cairo_private cairo_scan_converter_t *
+_cairo_tor33_scan_converter_create (int			xmin,
+				    int			ymin,
+				    int			xmax,
+				    int			ymax,
+				    cairo_fill_rule_t	fill_rule);
+cairo_private cairo_status_t
+_cairo_tor33_scan_converter_add_polygon (void		*converter,
+					 const cairo_polygon_t *polygon);
+
+cairo_private cairo_scan_converter_t *
 _cairo_clip_tor_scan_converter_create (cairo_clip_t *clip,
 				       cairo_polygon_t *polygon,
 				       cairo_fill_rule_t fill_rule);
diff --git a/src/cairo-tor33-scan-converter.c b/src/cairo-tor33-scan-converter.c
new file mode 100644
index 0000000..1141f11
--- /dev/null
+++ b/src/cairo-tor33-scan-converter.c
@@ -0,0 +1,1985 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* glitter-paths - polygon scan converter
+ *
+ * Copyright (c) 2008  M Joonas Pihlaja
+ * Copyright (c) 2007  David Turner
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+/* This is the Glitter paths scan converter incorporated into cairo.
+ * The source is from commit 734c53237a867a773640bd5b64816249fa1730f8
+ * of
+ *
+ *   http://gitweb.freedesktop.org/?p=users/joonas/glitter-paths
+ */
+/* Glitter-paths is a stand alone polygon rasteriser derived from
+ * David Turner's reimplementation of Tor Anderssons's 15x17
+ * supersampling rasteriser from the Apparition graphics library.  The
+ * main new feature here is cheaply choosing per-scan line between
+ * doing fully analytical coverage computation for an entire row at a
+ * time vs. using a supersampling approach.
+ *
+ * David Turner's code can be found at
+ *
+ *   http://david.freetype.org/rasterizer-shootout/raster-comparison-20070813.tar.bz2
+ *
+ * In particular this file incorporates large parts of ftgrays_tor10.h
+ * from raster-comparison-20070813.tar.bz2
+ */
+/* Overview
+ *
+ * A scan converter's basic purpose to take polygon edges and convert
+ * them into an RLE compressed A8 mask.  This one works in two phases:
+ * gathering edges and generating spans.
+ *
+ * 1) As the user feeds the scan converter edges they are vertically
+ * clipped and bucketted into a _polygon_ data structure.  The edges
+ * are also snapped from the user's coordinates to the subpixel grid
+ * coordinates used during scan conversion.
+ *
+ *     user
+ *      |
+ *      | edges
+ *      V
+ *    polygon buckets
+ *
+ * 2) Generating spans works by performing a vertical sweep of pixel
+ * rows from top to bottom and maintaining an _active_list_ of edges
+ * that intersect the row.  From the active list the fill rule
+ * determines which edges are the left and right edges of the start of
+ * each span, and their contribution is then accumulated into a pixel
+ * coverage list (_cell_list_) as coverage deltas.  Once the coverage
+ * deltas of all edges are known we can form spans of constant pixel
+ * coverage by summing the deltas during a traversal of the cell list.
+ * At the end of a pixel row the cell list is sent to a coverage
+ * blitter for rendering to some target surface.
+ *
+ * The pixel coverages are computed by either supersampling the row
+ * and box filtering a mono rasterisation, or by computing the exact
+ * coverages of edges in the active list.  The supersampling method is
+ * used whenever some edge starts or stops within the row or there are
+ * edge intersections in the row.
+ *
+ *   polygon bucket for       \
+ *   current pixel row        |
+ *      |                     |
+ *      | activate new edges  |  Repeat GRID_Y times if we
+ *      V                     \  are supersampling this row,
+ *   active list              /  or just once if we're computing
+ *      |                     |  analytical coverage.
+ *      | coverage deltas     |
+ *      V                     |
+ *   pixel coverage list     /
+ *      |
+ *      V
+ *   coverage blitter
+ */
+#include "cairoint.h"
+#include "cairo-spans-private.h"
+#include "cairo-error-private.h"
+
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+#include <setjmp.h>
+
+/*-------------------------------------------------------------------------
+ * cairo specific config
+ */
+#define I static
+
+/* Prefer cairo's status type. */
+#define GLITTER_HAVE_STATUS_T 1
+#define GLITTER_STATUS_SUCCESS CAIRO_STATUS_SUCCESS
+#define GLITTER_STATUS_NO_MEMORY CAIRO_STATUS_NO_MEMORY
+typedef cairo_status_t glitter_status_t;
+
+/* The input coordinate scale and the rasterisation grid scales. */
+#define GLITTER_INPUT_BITS CAIRO_FIXED_FRAC_BITS
+#define GRID_X_BITS 3
+#define GRID_Y_BITS 3
+
+/* Set glitter up to use a cairo span renderer to do the coverage
+ * blitting. */
+struct pool;
+struct cell_list;
+
+static glitter_status_t
+blit_with_span_renderer(
+    struct cell_list		*coverages,
+    cairo_span_renderer_t	*span_renderer,
+    struct pool			*span_pool,
+    int				 y,
+    int				 height,
+    int				 xmin,
+    int				 xmax);
+
+#define GLITTER_BLIT_COVERAGES_ARGS \
+	cairo_span_renderer_t *span_renderer, \
+	struct pool *span_pool
+
+#define GLITTER_BLIT_COVERAGES(cells, y, height,xmin, xmax) do {	\
+    blit_with_span_renderer (cells,		\
+			     span_renderer,	\
+			     span_pool,		\
+			     y, height,		\
+			     xmin, xmax);	\
+} while (0)
+
+/*-------------------------------------------------------------------------
+ * glitter-paths.h
+ */
+
+/* "Input scaled" numbers are fixed precision reals with multiplier
+ * 2**GLITTER_INPUT_BITS.  Input coordinates are given to glitter as
+ * pixel scaled numbers.  These get converted to the internal grid
+ * scaled numbers as soon as possible. Internal overflow is possible
+ * if GRID_X/Y inside glitter-paths.c is larger than
+ * 1<<GLITTER_INPUT_BITS. */
+#ifndef GLITTER_INPUT_BITS
+#  define GLITTER_INPUT_BITS 8
+#endif
+#define GLITTER_INPUT_SCALE (1<<GLITTER_INPUT_BITS)
+typedef int glitter_input_scaled_t;
+
+#if !GLITTER_HAVE_STATUS_T
+typedef enum {
+    GLITTER_STATUS_SUCCESS = 0,
+    GLITTER_STATUS_NO_MEMORY
+} glitter_status_t;
+#endif
+
+#ifndef I
+# define I /*static*/
+#endif
+
+/* Opaque type for scan converting. */
+typedef struct glitter_scan_converter glitter_scan_converter_t;
+
+/* Reset a scan converter to accept polygon edges and set the clip box
+ * in pixels.  Allocates O(ymax-ymin) bytes of memory.	The clip box
+ * is set to integer pixel coordinates xmin <= x < xmax, ymin <= y <
+ * ymax. */
+I glitter_status_t
+glitter_scan_converter_reset(
+    glitter_scan_converter_t *converter,
+    int xmin, int ymin,
+    int xmax, int ymax);
+
+/* Add a new polygon edge from pixel (x1,y1) to (x2,y2) to the scan
+ * converter.  The coordinates represent pixel positions scaled by
+ * 2**GLITTER_PIXEL_BITS.  If this function fails then the scan
+ * converter should be reset or destroyed.  Dir must be +1 or -1,
+ * with the latter reversing the orientation of the edge. */
+I void
+glitter_scan_converter_add_edge (glitter_scan_converter_t *converter,
+				 const cairo_edge_t *edge);
+
+/* Render the polygon in the scan converter to the given A8 format
+ * image raster.  Only the pixels accessible as pixels[y*stride+x] for
+ * x,y inside the clip box are written to, where xmin <= x < xmax,
+ * ymin <= y < ymax.  The image is assumed to be clear on input.
+ *
+ * If nonzero_fill is true then the interior of the polygon is
+ * computed with the non-zero fill rule.  Otherwise the even-odd fill
+ * rule is used.
+ *
+ * The scan converter must be reset or destroyed after this call. */
+#ifndef GLITTER_BLIT_COVERAGES_ARGS
+# define GLITTER_BLIT_COVERAGES_ARGS unsigned char *raster_pixels, long raster_stride
+#endif
+I void
+glitter_scan_converter_render(
+    glitter_scan_converter_t *converter,
+    int nonzero_fill,
+    GLITTER_BLIT_COVERAGES_ARGS);
+
+/*-------------------------------------------------------------------------
+ * glitter-paths.c: Implementation internal types
+ */
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+
+/* All polygon coordinates are snapped onto a subsample grid. "Grid
+ * scaled" numbers are fixed precision reals with multiplier GRID_X or
+ * GRID_Y. */
+typedef int grid_scaled_t;
+typedef int grid_scaled_x_t;
+typedef int grid_scaled_y_t;
+
+/* Default x/y scale factors.
+ *  You can either define GRID_X/Y_BITS to get a power-of-two scale
+ *  or define GRID_X/Y separately. */
+#if !defined(GRID_X) && !defined(GRID_X_BITS)
+#  define GRID_X_BITS 8
+#endif
+#if !defined(GRID_Y) && !defined(GRID_Y_BITS)
+#  define GRID_Y 15
+#endif
+
+/* Use GRID_X/Y_BITS to define GRID_X/Y if they're available. */
+#ifdef GRID_X_BITS
+#  define GRID_X (1 << GRID_X_BITS)
+#endif
+#ifdef GRID_Y_BITS
+#  define GRID_Y (1 << GRID_Y_BITS)
+#endif
+
+/* The GRID_X_TO_INT_FRAC macro splits a grid scaled coordinate into
+ * integer and fractional parts. The integer part is floored. */
+#if defined(GRID_X_TO_INT_FRAC)
+  /* do nothing */
+#elif defined(GRID_X_BITS)
+#  define GRID_X_TO_INT_FRAC(x, i, f) \
+	_GRID_TO_INT_FRAC_shift(x, i, f, GRID_X_BITS)
+#else
+#  define GRID_X_TO_INT_FRAC(x, i, f) \
+	_GRID_TO_INT_FRAC_general(x, i, f, GRID_X)
+#endif
+
+#define _GRID_TO_INT_FRAC_general(t, i, f, m) do {	\
+    (i) = (t) / (m);					\
+    (f) = (t) % (m);					\
+    if ((f) < 0) {					\
+	--(i);						\
+	(f) += (m);					\
+    }							\
+} while (0)
+
+#define _GRID_TO_INT_FRAC_shift(t, i, f, b) do {	\
+    (f) = (t) & ((1 << (b)) - 1);			\
+    (i) = (t) >> (b);					\
+} while (0)
+
+/* A grid area is a real in [0,1] scaled by 2*GRID_X*GRID_Y.  We want
+ * to be able to represent exactly areas of subpixel trapezoids whose
+ * vertices are given in grid scaled coordinates.  The scale factor
+ * comes from needing to accurately represent the area 0.5*dx*dy of a
+ * triangle with base dx and height dy in grid scaled numbers. */
+typedef int grid_area_t;
+#define GRID_XY (2*GRID_X*GRID_Y) /* Unit area on the grid. */
+
+/* GRID_AREA_TO_ALPHA(area): map [0,GRID_XY] to [0,255]. */
+#if GRID_XY == 510
+#  define GRID_AREA_TO_ALPHA(c)	  (((c)+1) >> 1)
+#elif GRID_XY == 255
+#  define  GRID_AREA_TO_ALPHA(c)  (c)
+#elif GRID_XY == 64
+#  define  GRID_AREA_TO_ALPHA(c)  (((c) << 2) | -(((c) & 0x40) >> 6))
+#elif GRID_XY == 128
+#  define  GRID_AREA_TO_ALPHA(c)  ((((c) << 1) | -((c) >> 7)) & 255)
+#elif GRID_XY == 256
+#  define  GRID_AREA_TO_ALPHA(c)  (((c) | -((c) >> 8)) & 255)
+#elif GRID_XY == 15
+#  define  GRID_AREA_TO_ALPHA(c)  (((c) << 4) + (c))
+#elif GRID_XY == 2*256*15
+#  define  GRID_AREA_TO_ALPHA(c)  (((c) + ((c)<<4) + 256) >> 9)
+#else
+#  define  GRID_AREA_TO_ALPHA(c)  (((c)*255 + GRID_XY/2) / GRID_XY)
+#endif
+
+#define UNROLL3(x) x x x
+
+struct quorem {
+    int32_t quo;
+    int32_t rem;
+};
+
+/* Header for a chunk of memory in a memory pool. */
+struct _pool_chunk {
+    /* # bytes used in this chunk. */
+    size_t size;
+
+    /* # bytes total in this chunk */
+    size_t capacity;
+
+    /* Pointer to the previous chunk or %NULL if this is the sentinel
+     * chunk in the pool header. */
+    struct _pool_chunk *prev_chunk;
+
+    /* Actual data starts here.	 Well aligned for pointers. */
+};
+
+/* A memory pool.  This is supposed to be embedded on the stack or
+ * within some other structure.	 It may optionally be followed by an
+ * embedded array from which requests are fulfilled until
+ * malloc needs to be called to allocate a first real chunk. */
+struct pool {
+    /* Chunk we're allocating from. */
+    struct _pool_chunk *current;
+
+    jmp_buf *jmp;
+
+    /* Free list of previously allocated chunks.  All have >= default
+     * capacity. */
+    struct _pool_chunk *first_free;
+
+    /* The default capacity of a chunk. */
+    size_t default_capacity;
+
+    /* Header for the sentinel chunk.  Directly following the pool
+     * struct should be some space for embedded elements from which
+     * the sentinel chunk allocates from. */
+    struct _pool_chunk sentinel[1];
+};
+
+/* A polygon edge. */
+struct edge {
+    /* Next in y-bucket or active list. */
+    struct edge *next;
+
+    /* Current x coordinate while the edge is on the active
+     * list. Initialised to the x coordinate of the top of the
+     * edge. The quotient is in grid_scaled_x_t units and the
+     * remainder is mod dy in grid_scaled_y_t units.*/
+    struct quorem x;
+
+    /* Advance of the current x when moving down a subsample line. */
+    struct quorem dxdy;
+
+    /* Advance of the current x when moving down a full pixel
+     * row. Only initialised when the height of the edge is large
+     * enough that there's a chance the edge could be stepped by a
+     * full row's worth of subsample rows at a time. */
+    struct quorem dxdy_full;
+
+    /* The clipped y of the top of the edge. */
+    grid_scaled_y_t ytop;
+
+    /* y2-y1 after orienting the edge downwards.  */
+    grid_scaled_y_t dy;
+
+    /* Number of subsample rows remaining to scan convert of this
+     * edge. */
+    grid_scaled_y_t height_left;
+
+    /* Original sign of the edge: +1 for downwards, -1 for upwards
+     * edges.  */
+    int dir;
+    int vertical;
+};
+
+#define EDGE_Y_BUCKET_INDEX(y, ymin) (((y) - (ymin))/GRID_Y)
+
+/* A collection of sorted and vertically clipped edges of the polygon.
+ * Edges are moved from the polygon to an active list while scan
+ * converting. */
+struct polygon {
+    /* The vertical clip extents. */
+    grid_scaled_y_t ymin, ymax;
+
+    /* Array of edges all starting in the same bucket.	An edge is put
+     * into bucket EDGE_BUCKET_INDEX(edge->ytop, polygon->ymin) when
+     * it is added to the polygon. */
+    struct edge **y_buckets;
+    struct edge *y_buckets_embedded[64];
+
+    struct {
+	struct pool base[1];
+	struct edge embedded[32];
+    } edge_pool;
+};
+
+/* A cell records the effect on pixel coverage of polygon edges
+ * passing through a pixel.  It contains two accumulators of pixel
+ * coverage.
+ *
+ * Consider the effects of a polygon edge on the coverage of a pixel
+ * it intersects and that of the following one.  The coverage of the
+ * following pixel is the height of the edge multiplied by the width
+ * of the pixel, and the coverage of the pixel itself is the area of
+ * the trapezoid formed by the edge and the right side of the pixel.
+ *
+ * +-----------------------+-----------------------+
+ * |                       |                       |
+ * |                       |                       |
+ * |_______________________|_______________________|
+ * |   \...................|.......................|\
+ * |    \..................|.......................| |
+ * |     \.................|.......................| |
+ * |      \....covered.....|.......................| |
+ * |       \....area.......|.......................| } covered height
+ * |        \..............|.......................| |
+ * |uncovered\.............|.......................| |
+ * |  area    \............|.......................| |
+ * |___________\...........|.......................|/
+ * |                       |                       |
+ * |                       |                       |
+ * |                       |                       |
+ * +-----------------------+-----------------------+
+ *
+ * Since the coverage of the following pixel will always be a multiple
+ * of the width of the pixel, we can store the height of the covered
+ * area instead.  The coverage of the pixel itself is the total
+ * coverage minus the area of the uncovered area to the left of the
+ * edge.  As it's faster to compute the uncovered area we only store
+ * that and subtract it from the total coverage later when forming
+ * spans to blit.
+ *
+ * The heights and areas are signed, with left edges of the polygon
+ * having positive sign and right edges having negative sign.  When
+ * two edges intersect they swap their left/rightness so their
+ * contribution above and below the intersection point must be
+ * computed separately. */
+struct cell {
+    struct cell		*next;
+    int			 x;
+    grid_area_t		 uncovered_area;
+    grid_scaled_y_t	 covered_height;
+};
+
+/* A cell list represents the scan line sparsely as cells ordered by
+ * ascending x.  It is geared towards scanning the cells in order
+ * using an internal cursor. */
+struct cell_list {
+    /* Sentinel nodes */
+    struct cell head, tail;
+
+    /* Cursor state for iterating through the cell list. */
+    struct cell *cursor;
+
+    /* Cells in the cell list are owned by the cell list and are
+     * allocated from this pool.  */
+    struct {
+	struct pool base[1];
+	struct cell embedded[32];
+    } cell_pool;
+};
+
+struct cell_pair {
+    struct cell *cell1;
+    struct cell *cell2;
+};
+
+/* The active list contains edges in the current scan line ordered by
+ * the x-coordinate of the intercept of the edge and the scan line. */
+struct active_list {
+    /* Leftmost edge on the current scan line. */
+    struct edge *head;
+
+    /* A lower bound on the height of the active edges is used to
+     * estimate how soon some active edge ends.	 We can't advance the
+     * scan conversion by a full pixel row if an edge ends somewhere
+     * within it. */
+    grid_scaled_y_t min_height;
+};
+
+struct glitter_scan_converter {
+    struct polygon	polygon[1];
+    struct active_list	active[1];
+    struct cell_list	coverages[1];
+
+    /* Clip box. */
+    grid_scaled_x_t xmin, xmax;
+    grid_scaled_y_t ymin, ymax;
+};
+
+/* Compute the floored division a/b. Assumes / and % perform symmetric
+ * division. */
+inline static struct quorem
+floored_divrem(int a, int b)
+{
+    struct quorem qr;
+    qr.quo = a/b;
+    qr.rem = a%b;
+    if ((a^b)<0 && qr.rem) {
+	qr.quo -= 1;
+	qr.rem += b;
+    }
+    return qr;
+}
+
+/* Compute the floored division (x*a)/b. Assumes / and % perform symmetric
+ * division. */
+static struct quorem
+floored_muldivrem(int x, int a, int b)
+{
+    struct quorem qr;
+    long long xa = (long long)x*a;
+    qr.quo = xa/b;
+    qr.rem = xa%b;
+    if ((xa>=0) != (b>=0) && qr.rem) {
+	qr.quo -= 1;
+	qr.rem += b;
+    }
+    return qr;
+}
+
+static struct _pool_chunk *
+_pool_chunk_init(
+    struct _pool_chunk *p,
+    struct _pool_chunk *prev_chunk,
+    size_t capacity)
+{
+    p->prev_chunk = prev_chunk;
+    p->size = 0;
+    p->capacity = capacity;
+    return p;
+}
+
+static struct _pool_chunk *
+_pool_chunk_create(struct pool *pool, size_t size)
+{
+    struct _pool_chunk *p;
+
+    p = malloc(size + sizeof(struct _pool_chunk));
+    if (unlikely (NULL == p))
+	longjmp (*pool->jmp, _cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+    return _pool_chunk_init(p, pool->current, size);
+}
+
+static void
+pool_init(struct pool *pool,
+	  jmp_buf *jmp,
+	  size_t default_capacity,
+	  size_t embedded_capacity)
+{
+    pool->jmp = jmp;
+    pool->current = pool->sentinel;
+    pool->first_free = NULL;
+    pool->default_capacity = default_capacity;
+    _pool_chunk_init(pool->sentinel, NULL, embedded_capacity);
+}
+
+static void
+pool_fini(struct pool *pool)
+{
+    struct _pool_chunk *p = pool->current;
+    do {
+	while (NULL != p) {
+	    struct _pool_chunk *prev = p->prev_chunk;
+	    if (p != pool->sentinel)
+		free(p);
+	    p = prev;
+	}
+	p = pool->first_free;
+	pool->first_free = NULL;
+    } while (NULL != p);
+}
+
+/* Satisfy an allocation by first allocating a new large enough chunk
+ * and adding it to the head of the pool's chunk list. This function
+ * is called as a fallback if pool_alloc() couldn't do a quick
+ * allocation from the current chunk in the pool. */
+static void *
+_pool_alloc_from_new_chunk(
+    struct pool *pool,
+    size_t size)
+{
+    struct _pool_chunk *chunk;
+    void *obj;
+    size_t capacity;
+
+    /* If the allocation is smaller than the default chunk size then
+     * try getting a chunk off the free list.  Force alloc of a new
+     * chunk for large requests. */
+    capacity = size;
+    chunk = NULL;
+    if (size < pool->default_capacity) {
+	capacity = pool->default_capacity;
+	chunk = pool->first_free;
+	if (chunk) {
+	    pool->first_free = chunk->prev_chunk;
+	    _pool_chunk_init(chunk, pool->current, chunk->capacity);
+	}
+    }
+
+    if (NULL == chunk)
+	chunk = _pool_chunk_create (pool, capacity);
+    pool->current = chunk;
+
+    obj = ((unsigned char*)chunk + sizeof(*chunk) + chunk->size);
+    chunk->size += size;
+    return obj;
+}
+
+/* Allocate size bytes from the pool.  The first allocated address
+ * returned from a pool is aligned to sizeof(void*).  Subsequent
+ * addresses will maintain alignment as long as multiples of void* are
+ * allocated.  Returns the address of a new memory area or %NULL on
+ * allocation failures.	 The pool retains ownership of the returned
+ * memory. */
+inline static void *
+pool_alloc (struct pool *pool, size_t size)
+{
+    struct _pool_chunk *chunk = pool->current;
+
+    if (size <= chunk->capacity - chunk->size) {
+	void *obj = ((unsigned char*)chunk + sizeof(*chunk) + chunk->size);
+	chunk->size += size;
+	return obj;
+    } else {
+	return _pool_alloc_from_new_chunk(pool, size);
+    }
+}
+
+/* Relinquish all pool_alloced memory back to the pool. */
+static void
+pool_reset (struct pool *pool)
+{
+    /* Transfer all used chunks to the chunk free list. */
+    struct _pool_chunk *chunk = pool->current;
+    if (chunk != pool->sentinel) {
+	while (chunk->prev_chunk != pool->sentinel) {
+	    chunk = chunk->prev_chunk;
+	}
+	chunk->prev_chunk = pool->first_free;
+	pool->first_free = pool->current;
+    }
+    /* Reset the sentinel as the current chunk. */
+    pool->current = pool->sentinel;
+    pool->sentinel->size = 0;
+}
+
+/* Rewinds the cell list's cursor to the beginning.  After rewinding
+ * we're good to cell_list_find() the cell any x coordinate. */
+inline static void
+cell_list_rewind (struct cell_list *cells)
+{
+    cells->cursor = &cells->head;
+}
+
+/* Rewind the cell list if its cursor has been advanced past x. */
+inline static void
+cell_list_maybe_rewind (struct cell_list *cells, int x)
+{
+    struct cell *tail = cells->cursor;
+    if (tail->x > x)
+	cell_list_rewind (cells);
+}
+
+static void
+cell_list_init(struct cell_list *cells, jmp_buf *jmp)
+{
+    pool_init(cells->cell_pool.base, jmp,
+	      256*sizeof(struct cell),
+	      sizeof(cells->cell_pool.embedded));
+    cells->tail.next = NULL;
+    cells->tail.x = INT_MAX;
+    cells->head.x = INT_MIN;
+    cells->head.next = &cells->tail;
+    cell_list_rewind (cells);
+}
+
+static void
+cell_list_fini(struct cell_list *cells)
+{
+    pool_fini (cells->cell_pool.base);
+}
+
+/* Empty the cell list.  This is called at the start of every pixel
+ * row. */
+inline static void
+cell_list_reset (struct cell_list *cells)
+{
+    cell_list_rewind (cells);
+    cells->head.next = &cells->tail;
+    pool_reset (cells->cell_pool.base);
+}
+
+static struct cell *
+cell_list_alloc (struct cell_list *cells,
+		 struct cell *tail,
+		 int x)
+{
+    struct cell *cell;
+
+    cell = pool_alloc (cells->cell_pool.base, sizeof (struct cell));
+    cell->next = tail->next;
+    tail->next = cell;
+    cell->x = x;
+    cell->uncovered_area = 0;
+    cell->covered_height = 0;
+    return cell;
+}
+
+/* Find a cell at the given x-coordinate.  Returns %NULL if a new cell
+ * needed to be allocated but couldn't be.  Cells must be found with
+ * non-decreasing x-coordinate until the cell list is rewound using
+ * cell_list_rewind(). Ownership of the returned cell is retained by
+ * the cell list. */
+inline static struct cell *
+cell_list_find (struct cell_list *cells, int x)
+{
+    struct cell *tail = cells->cursor;
+
+    while (1) {
+	UNROLL3({
+	    if (tail->next->x > x)
+		break;
+	    tail = tail->next;
+	});
+    }
+
+    if (tail->x != x)
+	tail = cell_list_alloc (cells, tail, x);
+    return cells->cursor = tail;
+
+}
+
+/* Find two cells at x1 and x2.	 This is exactly equivalent
+ * to
+ *
+ *   pair.cell1 = cell_list_find(cells, x1);
+ *   pair.cell2 = cell_list_find(cells, x2);
+ *
+ * except with less function call overhead. */
+inline static struct cell_pair
+cell_list_find_pair(struct cell_list *cells, int x1, int x2)
+{
+    struct cell_pair pair;
+
+    pair.cell1 = cells->cursor;
+    while (1) {
+	UNROLL3({
+	    if (pair.cell1->next->x > x1)
+		break;
+	    pair.cell1 = pair.cell1->next;
+	});
+    }
+    if (pair.cell1->x != x1) {
+	struct cell *cell = pool_alloc (cells->cell_pool.base,
+					sizeof (struct cell));
+	cell->x = x1;
+	cell->uncovered_area = 0;
+	cell->covered_height = 0;
+	cell->next = pair.cell1->next;
+	pair.cell1->next = cell;
+	pair.cell1 = cell;
+    }
+
+    pair.cell2 = pair.cell1;
+    while (1) {
+	UNROLL3({
+	    if (pair.cell2->next->x > x2)
+		break;
+	    pair.cell2 = pair.cell2->next;
+	});
+    }
+    if (pair.cell2->x != x2) {
+	struct cell *cell = pool_alloc (cells->cell_pool.base,
+					sizeof (struct cell));
+	cell->uncovered_area = 0;
+	cell->covered_height = 0;
+	cell->x = x2;
+	cell->next = pair.cell2->next;
+	pair.cell2->next = cell;
+	pair.cell2 = cell;
+    }
+
+    cells->cursor = pair.cell2;
+    return pair;
+}
+
+/* Add an unbounded subpixel span covering subpixels >= x to the
+ * coverage cells. */
+static void
+cell_list_add_unbounded_subspan (struct cell_list *cells,
+				 grid_scaled_x_t x)
+{
+    struct cell *cell;
+    int ix, fx;
+
+    GRID_X_TO_INT_FRAC(x, ix, fx);
+
+    cell = cell_list_find (cells, ix);
+    cell->uncovered_area += 2*fx;
+    cell->covered_height++;
+}
+
+/* Add a subpixel span covering [x1, x2) to the coverage cells. */
+inline static void
+cell_list_add_subspan(
+    struct cell_list *cells,
+    grid_scaled_x_t x1,
+    grid_scaled_x_t x2)
+{
+    int ix1, fx1;
+    int ix2, fx2;
+
+    GRID_X_TO_INT_FRAC(x1, ix1, fx1);
+    GRID_X_TO_INT_FRAC(x2, ix2, fx2);
+
+    if (ix1 != ix2) {
+	struct cell_pair p;
+	p = cell_list_find_pair(cells, ix1, ix2);
+	p.cell1->uncovered_area += 2*fx1;
+	++p.cell1->covered_height;
+	p.cell2->uncovered_area -= 2*fx2;
+	--p.cell2->covered_height;
+    } else {
+	struct cell *cell = cell_list_find(cells, ix1);
+	cell->uncovered_area += 2*(fx1-fx2);
+    }
+}
+
+/* Adds the analytical coverage of an edge crossing the current pixel
+ * row to the coverage cells and advances the edge's x position to the
+ * following row.
+ *
+ * This function is only called when we know that during this pixel row:
+ *
+ * 1) The relative order of all edges on the active list doesn't
+ * change.  In particular, no edges intersect within this row to pixel
+ * precision.
+ *
+ * 2) No new edges start in this row.
+ *
+ * 3) No existing edges end mid-row.
+ *
+ * This function depends on being called with all edges from the
+ * active list in the order they appear on the list (i.e. with
+ * non-decreasing x-coordinate.)  */
+static void
+cell_list_render_edge(
+    struct cell_list *cells,
+    struct edge *edge,
+    int sign)
+{
+    grid_scaled_y_t y1, y2, dy;
+    grid_scaled_x_t dx;
+    int ix1, ix2;
+    grid_scaled_x_t fx1, fx2;
+
+    struct quorem x1 = edge->x;
+    struct quorem x2 = x1;
+
+    if (! edge->vertical) {
+	x2.quo += edge->dxdy_full.quo;
+	x2.rem += edge->dxdy_full.rem;
+	if (x2.rem >= 0) {
+	    ++x2.quo;
+	    x2.rem -= edge->dy;
+	}
+
+	edge->x = x2;
+    }
+
+    GRID_X_TO_INT_FRAC(x1.quo, ix1, fx1);
+    GRID_X_TO_INT_FRAC(x2.quo, ix2, fx2);
+
+    /* Edge is entirely within a column? */
+    if (ix1 == ix2) {
+	/* We always know that ix1 is >= the cell list cursor in this
+	 * case due to the no-intersections precondition.  */
+	struct cell *cell = cell_list_find(cells, ix1);
+	cell->covered_height += sign*GRID_Y;
+	cell->uncovered_area += sign*(fx1 + fx2)*GRID_Y;
+	return;
+    }
+
+    /* Orient the edge left-to-right. */
+    dx = x2.quo - x1.quo;
+    if (dx >= 0) {
+	y1 = 0;
+	y2 = GRID_Y;
+    } else {
+	int tmp;
+	tmp = ix1; ix1 = ix2; ix2 = tmp;
+	tmp = fx1; fx1 = fx2; fx2 = tmp;
+	dx = -dx;
+	sign = -sign;
+	y1 = GRID_Y;
+	y2 = 0;
+    }
+    dy = y2 - y1;
+
+    /* Add coverage for all pixels [ix1,ix2] on this row crossed
+     * by the edge. */
+    {
+	struct cell_pair pair;
+	struct quorem y = floored_divrem((GRID_X - fx1)*dy, dx);
+
+	/* When rendering a previous edge on the active list we may
+	 * advance the cell list cursor past the leftmost pixel of the
+	 * current edge even though the two edges don't intersect.
+	 * e.g. consider two edges going down and rightwards:
+	 *
+	 *  --\_+---\_+-----+-----+----
+	 *      \_    \_    |     |
+	 *      | \_  | \_  |     |
+	 *      |   \_|   \_|     |
+	 *      |     \_    \_    |
+	 *  ----+-----+-\---+-\---+----
+	 *
+	 * The left edge touches cells past the starting cell of the
+	 * right edge.  Fortunately such cases are rare.
+	 *
+	 * The rewinding is never necessary if the current edge stays
+	 * within a single column because we've checked before calling
+	 * this function that the active list order won't change. */
+	cell_list_maybe_rewind(cells, ix1);
+
+	pair = cell_list_find_pair(cells, ix1, ix1+1);
+	pair.cell1->uncovered_area += sign*y.quo*(GRID_X + fx1);
+	pair.cell1->covered_height += sign*y.quo;
+	y.quo += y1;
+
+	if (ix1+1 < ix2) {
+	    struct quorem dydx_full = floored_divrem(GRID_X*dy, dx);
+	    struct cell *cell = pair.cell2;
+
+	    ++ix1;
+	    do {
+		grid_scaled_y_t y_skip = dydx_full.quo;
+		y.rem += dydx_full.rem;
+		if (y.rem >= dx) {
+		    ++y_skip;
+		    y.rem -= dx;
+		}
+
+		y.quo += y_skip;
+
+		y_skip *= sign;
+		cell->uncovered_area += y_skip*GRID_X;
+		cell->covered_height += y_skip;
+
+		++ix1;
+		cell = cell_list_find(cells, ix1);
+	    } while (ix1 != ix2);
+
+	    pair.cell2 = cell;
+	}
+	pair.cell2->uncovered_area += sign*(y2 - y.quo)*fx2;
+	pair.cell2->covered_height += sign*(y2 - y.quo);
+    }
+}
+
+static void
+polygon_init (struct polygon *polygon, jmp_buf *jmp)
+{
+    polygon->ymin = polygon->ymax = 0;
+    polygon->y_buckets = polygon->y_buckets_embedded;
+    pool_init (polygon->edge_pool.base, jmp,
+	       8192 - sizeof (struct _pool_chunk),
+	       sizeof (polygon->edge_pool.embedded));
+}
+
+static void
+polygon_fini (struct polygon *polygon)
+{
+    if (polygon->y_buckets != polygon->y_buckets_embedded)
+	free (polygon->y_buckets);
+
+    pool_fini (polygon->edge_pool.base);
+}
+
+/* Empties the polygon of all edges. The polygon is then prepared to
+ * receive new edges and clip them to the vertical range
+ * [ymin,ymax). */
+static glitter_status_t
+polygon_reset (struct polygon *polygon,
+	       grid_scaled_y_t ymin,
+	       grid_scaled_y_t ymax)
+{
+    unsigned h = ymax - ymin;
+    unsigned num_buckets = EDGE_Y_BUCKET_INDEX(ymax + GRID_Y-1, ymin);
+
+    pool_reset(polygon->edge_pool.base);
+
+    if (unlikely (h > 0x7FFFFFFFU - GRID_Y))
+	goto bail_no_mem; /* even if you could, you wouldn't want to. */
+
+    if (polygon->y_buckets != polygon->y_buckets_embedded)
+	free (polygon->y_buckets);
+
+    polygon->y_buckets =  polygon->y_buckets_embedded;
+    if (num_buckets > ARRAY_LENGTH (polygon->y_buckets_embedded)) {
+	polygon->y_buckets = _cairo_malloc_ab (num_buckets,
+					       sizeof (struct edge *));
+	if (unlikely (NULL == polygon->y_buckets))
+	    goto bail_no_mem;
+    }
+    memset (polygon->y_buckets, 0, num_buckets * sizeof (struct edge *));
+
+    polygon->ymin = ymin;
+    polygon->ymax = ymax;
+    return GLITTER_STATUS_SUCCESS;
+
+ bail_no_mem:
+    polygon->ymin = 0;
+    polygon->ymax = 0;
+    return GLITTER_STATUS_NO_MEMORY;
+}
+
+static void
+_polygon_insert_edge_into_its_y_bucket(
+    struct polygon *polygon,
+    struct edge *e)
+{
+    unsigned ix = EDGE_Y_BUCKET_INDEX(e->ytop, polygon->ymin);
+    struct edge **ptail = &polygon->y_buckets[ix];
+    e->next = *ptail;
+    *ptail = e;
+}
+
+inline static void
+polygon_add_edge (struct polygon *polygon,
+		  const cairo_edge_t *edge)
+{
+    struct edge *e;
+    grid_scaled_x_t dx;
+    grid_scaled_y_t dy;
+    grid_scaled_y_t ytop, ybot;
+    grid_scaled_y_t ymin = polygon->ymin;
+    grid_scaled_y_t ymax = polygon->ymax;
+
+    assert (edge->bottom > edge->top);
+
+    if (unlikely (edge->top >= ymax || edge->bottom <= ymin))
+	return;
+
+    e = pool_alloc (polygon->edge_pool.base, sizeof (struct edge));
+
+    dx = edge->line.p2.x - edge->line.p1.x;
+    dy = edge->line.p2.y - edge->line.p1.y;
+    e->dy = dy;
+    e->dir = edge->dir;
+
+    ytop = edge->top >= ymin ? edge->top : ymin;
+    ybot = edge->bottom <= ymax ? edge->bottom : ymax;
+    e->ytop = ytop;
+    e->height_left = ybot - ytop;
+
+    if (dx == 0) {
+	e->vertical = TRUE;
+	e->x.quo = edge->line.p1.x;
+	e->x.rem = 0;
+	e->dxdy.quo = 0;
+	e->dxdy.rem = 0;
+	e->dxdy_full.quo = 0;
+	e->dxdy_full.rem = 0;
+    } else {
+	e->vertical = FALSE;
+	e->dxdy = floored_divrem (dx, dy);
+	if (ytop == edge->line.p1.y) {
+	    e->x.quo = edge->line.p1.x;
+	    e->x.rem = 0;
+	} else {
+	    e->x = floored_muldivrem (ytop - edge->line.p1.y, dx, dy);
+	    e->x.quo += edge->line.p1.x;
+	}
+
+	if (e->height_left >= GRID_Y) {
+	    e->dxdy_full = floored_muldivrem (GRID_Y, dx, dy);
+	} else {
+	    e->dxdy_full.quo = 0;
+	    e->dxdy_full.rem = 0;
+	}
+    }
+
+    _polygon_insert_edge_into_its_y_bucket (polygon, e);
+
+    e->x.rem -= dy;		/* Bias the remainder for faster
+				 * edge advancement. */
+}
+
+static void
+active_list_reset (struct active_list *active)
+{
+    active->head = NULL;
+    active->min_height = 0;
+}
+
+static void
+active_list_init(struct active_list *active)
+{
+    active_list_reset(active);
+}
+
+/*
+ * Merge two sorted edge lists.
+ * Input:
+ *  - head_a: The head of the first list.
+ *  - head_b: The head of the second list; head_b cannot be NULL.
+ * Output:
+ * Returns the head of the merged list.
+ *
+ * Implementation notes:
+ * To make it fast (in particular, to reduce to an insertion sort whenever
+ * one of the two input lists only has a single element) we iterate through
+ * a list until its head becomes greater than the head of the other list,
+ * then we switch their roles. As soon as one of the two lists is empty, we
+ * just attach the other one to the current list and exit.
+ * Writes to memory are only needed to "switch" lists (as it also requires
+ * attaching to the output list the list which we will be iterating next) and
+ * to attach the last non-empty list.
+ */
+static struct edge *
+merge_sorted_edges (struct edge *head_a, struct edge *head_b)
+{
+    struct edge *head, **next;
+    int32_t x;
+
+    if (head_a == NULL)
+	return head_b;
+
+    next = &head;
+    if (head_a->x.quo <= head_b->x.quo) {
+	head = head_a;
+    } else {
+	head = head_b;
+	goto start_with_b;
+    }
+
+    do {
+	x = head_b->x.quo;
+	while (head_a != NULL && head_a->x.quo <= x) {
+	    next = &head_a->next;
+	    head_a = head_a->next;
+	}
+
+	*next = head_b;
+	if (head_a == NULL)
+	    return head;
+
+start_with_b:
+	x = head_a->x.quo;
+	while (head_b != NULL && head_b->x.quo <= x) {
+	    next = &head_b->next;
+	    head_b = head_b->next;
+	}
+
+	*next = head_a;
+	if (head_b == NULL)
+	    return head;
+    } while (1);
+}
+
+/*
+ * Sort (part of) a list.
+ * Input:
+ *  - list: The list to be sorted; list cannot be NULL.
+ *  - limit: Recursion limit.
+ * Output:
+ *  - head_out: The head of the sorted list containing the first 2^(level+1) elements of the
+ *              input list; if the input list has fewer elements, head_out be a sorted list
+ *              containing all the elements of the input list.
+ * Returns the head of the list of unprocessed elements (NULL if the sorted list contains
+ * all the elements of the input list).
+ *
+ * Implementation notes:
+ * Special case single element list, unroll/inline the sorting of the first two elements.
+ * Some tail recursion is used since we iterate on the bottom-up solution of the problem
+ * (we start with a small sorted list and keep merging other lists of the same size to it).
+ */
+static struct edge *
+sort_edges (struct edge  *list,
+	    unsigned int  level,
+	    struct edge **head_out)
+{
+    struct edge *head_other, *remaining;
+    unsigned int i;
+
+    head_other = list->next;
+
+    /* Single element list -> return */
+    if (head_other == NULL) {
+	*head_out = list;
+	return NULL;
+    }
+
+    /* Unroll the first iteration of the following loop (halves the number of calls to merge_sorted_edges):
+     *  - Initialize remaining to be the list containing the elements after the second in the input list.
+     *  - Initialize *head_out to be the sorted list containing the first two element.
+     */
+    remaining = head_other->next;
+    if (list->x.quo <= head_other->x.quo) {
+	*head_out = list;
+	/* list->next = head_other; */ /* The input list is already like this. */
+	head_other->next = NULL;
+    } else {
+	*head_out = head_other;
+	head_other->next = list;
+	list->next = NULL;
+    }
+
+    for (i = 0; i < level && remaining; i++) {
+	/* Extract a sorted list of the same size as *head_out
+	 * (2^(i+1) elements) from the list of remaining elements. */
+	remaining = sort_edges (remaining, i, &head_other);
+	*head_out = merge_sorted_edges (*head_out, head_other);
+    }
+
+    /* *head_out now contains (at most) 2^(level+1) elements. */
+
+    return remaining;
+}
+
+/* Test if the edges on the active list can be safely advanced by a
+ * full row without intersections or any edges ending. */
+inline static int
+active_list_can_step_full_row (struct active_list *active)
+{
+    const struct edge *e;
+    int prev_x = INT_MIN;
+
+    /* Recomputes the minimum height of all edges on the active
+     * list if we have been dropping edges. */
+    if (active->min_height <= 0) {
+	int min_height = INT_MAX;
+
+	e = active->head;
+	while (NULL != e) {
+	    if (e->height_left < min_height)
+		min_height = e->height_left;
+	    e = e->next;
+	}
+
+	active->min_height = min_height;
+    }
+
+    if (active->min_height < GRID_Y)
+	return 0;
+
+    /* Check for intersections as no edges end during the next row. */
+    e = active->head;
+    while (NULL != e) {
+	struct quorem x = e->x;
+
+	if (! e->vertical) {
+	    x.quo += e->dxdy_full.quo;
+	    x.rem += e->dxdy_full.rem;
+	    if (x.rem >= 0)
+		++x.quo;
+	}
+
+	if (x.quo <= prev_x)
+	    return 0;
+
+	prev_x = x.quo;
+	e = e->next;
+    }
+
+    return 1;
+}
+
+/* Merges edges on the given subpixel row from the polygon to the
+ * active_list. */
+inline static void
+active_list_merge_edges_from_polygon(struct active_list *active,
+				     struct edge **ptail,
+				     grid_scaled_y_t y,
+				     struct polygon *polygon)
+{
+    /* Split off the edges on the current subrow and merge them into
+     * the active list. */
+    int min_height = active->min_height;
+    struct edge *subrow_edges = NULL;
+    struct edge *tail = *ptail;
+
+    do {
+	struct edge *next = tail->next;
+
+	if (y == tail->ytop) {
+	    tail->next = subrow_edges;
+	    subrow_edges = tail;
+
+	    if (tail->height_left < min_height)
+		min_height = tail->height_left;
+
+	    *ptail = next;
+	} else
+	    ptail = &tail->next;
+
+	tail = next;
+    } while (tail);
+
+    if (subrow_edges) {
+	sort_edges (subrow_edges, UINT_MAX, &subrow_edges);
+	active->head = merge_sorted_edges (active->head, subrow_edges);
+	active->min_height = min_height;
+    }
+}
+
+/* Advance the edges on the active list by one subsample row by
+ * updating their x positions.  Drop edges from the list that end. */
+inline static void
+active_list_substep_edges(struct active_list *active)
+{
+    struct edge **cursor = &active->head;
+    grid_scaled_x_t prev_x = INT_MIN;
+    struct edge *unsorted = NULL;
+    struct edge *edge = *cursor;
+
+    do {
+	UNROLL3({
+	    struct edge *next;
+
+	    if (NULL == edge)
+		break;
+
+	    next = edge->next;
+	    if (--edge->height_left) {
+		edge->x.quo += edge->dxdy.quo;
+		edge->x.rem += edge->dxdy.rem;
+		if (edge->x.rem >= 0) {
+		    ++edge->x.quo;
+		    edge->x.rem -= edge->dy;
+		}
+
+		if (edge->x.quo < prev_x) {
+		    *cursor = next;
+		    edge->next = unsorted;
+		    unsorted = edge;
+		} else {
+		    prev_x = edge->x.quo;
+		    cursor = &edge->next;
+		}
+	    } else {
+		 *cursor = next;
+	    }
+	    edge = next;
+	})
+    } while (1);
+
+    if (unsorted) {
+	sort_edges (unsorted, UINT_MAX, &unsorted);
+	active->head = merge_sorted_edges (active->head, unsorted);
+    }
+}
+
+inline static void
+apply_nonzero_fill_rule_for_subrow (struct active_list *active,
+				    struct cell_list *coverages)
+{
+    struct edge *edge = active->head;
+    int winding = 0;
+    int xstart;
+    int xend;
+
+    cell_list_rewind (coverages);
+
+    while (NULL != edge) {
+	xstart = edge->x.quo;
+	winding = edge->dir;
+	while (1) {
+	    edge = edge->next;
+	    if (NULL == edge)
+		return cell_list_add_unbounded_subspan (coverages, xstart);
+
+	    winding += edge->dir;
+	    if (0 == winding) {
+		if (edge->next == NULL || edge->next->x.quo != edge->x.quo)
+		    break;
+	    }
+	}
+
+	xend = edge->x.quo;
+	cell_list_add_subspan (coverages, xstart, xend);
+
+	edge = edge->next;
+    }
+}
+
+static void
+apply_evenodd_fill_rule_for_subrow (struct active_list *active,
+				    struct cell_list *coverages)
+{
+    struct edge *edge = active->head;
+    int xstart;
+    int xend;
+
+    cell_list_rewind (coverages);
+
+    while (NULL != edge) {
+	xstart = edge->x.quo;
+
+	while (1) {
+	    edge = edge->next;
+	    if (NULL == edge) {
+		cell_list_add_unbounded_subspan (coverages, xstart);
+		return;
+	    }
+
+	    if (edge->next == NULL || edge->next->x.quo != edge->x.quo)
+		break;
+
+	    edge = edge->next;
+	}
+
+	xend = edge->x.quo;
+	cell_list_add_subspan (coverages, xstart, xend);
+
+	edge = edge->next;
+    }
+}
+
+static void
+apply_nonzero_fill_rule_and_step_edges (struct active_list *active,
+					struct cell_list *coverages)
+{
+    struct edge **cursor = &active->head;
+    struct edge *left_edge;
+
+    left_edge = *cursor;
+    while (NULL != left_edge) {
+	struct edge *right_edge;
+	int winding = left_edge->dir;
+
+	left_edge->height_left -= GRID_Y;
+	if (left_edge->height_left)
+	    cursor = &left_edge->next;
+	else
+	    *cursor = left_edge->next;
+
+	while (1) {
+	    right_edge = *cursor;
+	    if (NULL == right_edge) {
+		cell_list_render_edge (coverages, left_edge, +1);
+		return;
+	    }
+
+	    right_edge->height_left -= GRID_Y;
+	    if (right_edge->height_left)
+		cursor = &right_edge->next;
+	    else
+		*cursor = right_edge->next;
+
+	    winding += right_edge->dir;
+	    if (0 == winding) {
+		if (right_edge->next == NULL ||
+		    right_edge->next->x.quo != right_edge->x.quo)
+		{
+		    break;
+		}
+	    }
+
+	    if (! right_edge->vertical) {
+		right_edge->x.quo += right_edge->dxdy_full.quo;
+		right_edge->x.rem += right_edge->dxdy_full.rem;
+		if (right_edge->x.rem >= 0) {
+		    ++right_edge->x.quo;
+		    right_edge->x.rem -= right_edge->dy;
+		}
+	    }
+	}
+
+	cell_list_render_edge (coverages, left_edge, +1);
+	cell_list_render_edge (coverages, right_edge, -1);
+
+	left_edge = *cursor;
+    }
+}
+
+static void
+apply_evenodd_fill_rule_and_step_edges (struct active_list *active,
+					struct cell_list *coverages)
+{
+    struct edge **cursor = &active->head;
+    struct edge *left_edge;
+
+    left_edge = *cursor;
+    while (NULL != left_edge) {
+	struct edge *right_edge;
+
+	left_edge->height_left -= GRID_Y;
+	if (left_edge->height_left)
+	    cursor = &left_edge->next;
+	else
+	    *cursor = left_edge->next;
+
+	while (1) {
+	    right_edge = *cursor;
+	    if (NULL == right_edge) {
+		cell_list_render_edge (coverages, left_edge, +1);
+		return;
+	    }
+
+	    right_edge->height_left -= GRID_Y;
+	    if (right_edge->height_left)
+		cursor = &right_edge->next;
+	    else
+		*cursor = right_edge->next;
+
+	    if (right_edge->next == NULL ||
+		right_edge->next->x.quo != right_edge->x.quo)
+	    {
+		break;
+	    }
+
+	    if (! right_edge->vertical) {
+		right_edge->x.quo += right_edge->dxdy_full.quo;
+		right_edge->x.rem += right_edge->dxdy_full.rem;
+		if (right_edge->x.rem >= 0) {
+		    ++right_edge->x.quo;
+		    right_edge->x.rem -= right_edge->dy;
+		}
+	    }
+	}
+
+	cell_list_render_edge (coverages, left_edge, +1);
+	cell_list_render_edge (coverages, right_edge, -1);
+
+	left_edge = *cursor;
+    }
+}
+
+static void
+_glitter_scan_converter_init(glitter_scan_converter_t *converter, jmp_buf *jmp)
+{
+    polygon_init(converter->polygon, jmp);
+    active_list_init(converter->active);
+    cell_list_init(converter->coverages, jmp);
+    converter->xmin=0;
+    converter->ymin=0;
+    converter->xmax=0;
+    converter->ymax=0;
+}
+
+static void
+_glitter_scan_converter_fini(glitter_scan_converter_t *converter)
+{
+    polygon_fini(converter->polygon);
+    cell_list_fini(converter->coverages);
+    converter->xmin=0;
+    converter->ymin=0;
+    converter->xmax=0;
+    converter->ymax=0;
+}
+
+static grid_scaled_t
+int_to_grid_scaled(int i, int scale)
+{
+    /* Clamp to max/min representable scaled number. */
+    if (i >= 0) {
+	if (i >= INT_MAX/scale)
+	    i = INT_MAX/scale;
+    }
+    else {
+	if (i <= INT_MIN/scale)
+	    i = INT_MIN/scale;
+    }
+    return i*scale;
+}
+
+#define int_to_grid_scaled_x(x) int_to_grid_scaled((x), GRID_X)
+#define int_to_grid_scaled_y(x) int_to_grid_scaled((x), GRID_Y)
+
+I glitter_status_t
+glitter_scan_converter_reset(
+    glitter_scan_converter_t *converter,
+    int xmin, int ymin,
+    int xmax, int ymax)
+{
+    glitter_status_t status;
+
+    converter->xmin = 0; converter->xmax = 0;
+    converter->ymin = 0; converter->ymax = 0;
+
+    xmin = int_to_grid_scaled_x(xmin);
+    ymin = int_to_grid_scaled_y(ymin);
+    xmax = int_to_grid_scaled_x(xmax);
+    ymax = int_to_grid_scaled_y(ymax);
+
+    active_list_reset(converter->active);
+    cell_list_reset(converter->coverages);
+    status = polygon_reset(converter->polygon, ymin, ymax);
+    if (status)
+	return status;
+
+    converter->xmin = xmin;
+    converter->xmax = xmax;
+    converter->ymin = ymin;
+    converter->ymax = ymax;
+    return GLITTER_STATUS_SUCCESS;
+}
+
+/* INPUT_TO_GRID_X/Y (in_coord, out_grid_scaled, grid_scale)
+ *   These macros convert an input coordinate in the client's
+ *   device space to the rasterisation grid.
+ */
+/* Gah.. this bit of ugly defines INPUT_TO_GRID_X/Y so as to use
+ * shifts if possible, and something saneish if not.
+ */
+#if !defined(INPUT_TO_GRID_Y) && defined(GRID_Y_BITS) && GRID_Y_BITS <= GLITTER_INPUT_BITS
+#  define INPUT_TO_GRID_Y(in, out) (out) = (in) >> (GLITTER_INPUT_BITS - GRID_Y_BITS)
+#else
+#  define INPUT_TO_GRID_Y(in, out) INPUT_TO_GRID_general(in, out, GRID_Y)
+#endif
+
+#if !defined(INPUT_TO_GRID_X) && defined(GRID_X_BITS) && GRID_X_BITS <= GLITTER_INPUT_BITS
+#  define INPUT_TO_GRID_X(in, out) (out) = (in) >> (GLITTER_INPUT_BITS - GRID_X_BITS)
+#else
+#  define INPUT_TO_GRID_X(in, out) INPUT_TO_GRID_general(in, out, GRID_X)
+#endif
+
+#define INPUT_TO_GRID_general(in, out, grid_scale) do {		\
+	long long tmp__ = (long long)(grid_scale) * (in);	\
+	tmp__ >>= GLITTER_INPUT_BITS;				\
+	(out) = tmp__;						\
+} while (0)
+
+I void
+glitter_scan_converter_add_edge (glitter_scan_converter_t *converter,
+				 const cairo_edge_t *edge)
+{
+    cairo_edge_t e;
+
+    INPUT_TO_GRID_Y (edge->top, e.top);
+    INPUT_TO_GRID_Y (edge->bottom, e.bottom);
+    if (e.top >= e.bottom)
+	return;
+
+    /* XXX: possible overflows if GRID_X/Y > 2**GLITTER_INPUT_BITS */
+    INPUT_TO_GRID_Y (edge->line.p1.y, e.line.p1.y);
+    INPUT_TO_GRID_Y (edge->line.p2.y, e.line.p2.y);
+    if (e.line.p1.y == e.line.p2.y)
+	return;
+
+    INPUT_TO_GRID_X (edge->line.p1.x, e.line.p1.x);
+    INPUT_TO_GRID_X (edge->line.p2.x, e.line.p2.x);
+
+    e.dir = edge->dir;
+
+    polygon_add_edge (converter->polygon, &e);
+}
+
+static cairo_bool_t
+active_list_is_vertical (struct active_list *active)
+{
+    struct edge *e;
+
+    for (e = active->head; e != NULL; e = e->next) {
+	if (! e->vertical)
+	    return FALSE;
+    }
+
+    return TRUE;
+}
+
+static void
+step_edges (struct active_list *active, int count)
+{
+    struct edge **cursor = &active->head;
+    struct edge *edge;
+
+    for (edge = *cursor; edge != NULL; edge = *cursor) {
+	edge->height_left -= GRID_Y * count;
+	if (edge->height_left)
+	    cursor = &edge->next;
+	else
+	    *cursor = edge->next;
+    }
+}
+
+I void
+glitter_scan_converter_render(
+    glitter_scan_converter_t *converter,
+    int nonzero_fill,
+    GLITTER_BLIT_COVERAGES_ARGS)
+{
+    int i, j;
+    int ymax_i = converter->ymax / GRID_Y;
+    int ymin_i = converter->ymin / GRID_Y;
+    int xmin_i, xmax_i;
+    int h = ymax_i - ymin_i;
+    struct polygon *polygon = converter->polygon;
+    struct cell_list *coverages = converter->coverages;
+    struct active_list *active = converter->active;
+
+    xmin_i = converter->xmin / GRID_X;
+    xmax_i = converter->xmax / GRID_X;
+    if (xmin_i >= xmax_i)
+	return;
+
+    /* Render each pixel row. */
+    for (i = 0; i < h; i = j) {
+	int do_full_step = 0;
+
+	j = i + 1;
+
+	/* Determine if we can ignore this row or use the full pixel
+	 * stepper. */
+	if (! polygon->y_buckets[i]) {
+	    if (! active->head) {
+		for (; j < h && ! polygon->y_buckets[j]; j++)
+		    ;
+		continue;
+	    }
+
+	    do_full_step = active_list_can_step_full_row (active);
+	}
+
+	if (do_full_step) {
+	    /* Step by a full pixel row's worth. */
+	    if (nonzero_fill)
+		apply_nonzero_fill_rule_and_step_edges (active, coverages);
+	    else
+		apply_evenodd_fill_rule_and_step_edges (active, coverages);
+
+	    if (active_list_is_vertical (active)) {
+		while (j < h &&
+		       polygon->y_buckets[j] == NULL &&
+		       active->min_height >= 2*GRID_Y)
+		{
+		    active->min_height -= GRID_Y;
+		    j++;
+		}
+		if (j != i + 1)
+		    step_edges (active, j - (i + 1));
+	    }
+	} else {
+	    grid_scaled_y_t suby;
+
+	    /* Subsample this row. */
+	    for (suby = 0; suby < GRID_Y; suby++) {
+		grid_scaled_y_t y = (i+ymin_i)*GRID_Y + suby;
+
+		if (polygon->y_buckets[i]) {
+		    active_list_merge_edges_from_polygon (active,
+							  &polygon->y_buckets[i], y,
+							  polygon);
+		}
+
+		if (nonzero_fill)
+		    apply_nonzero_fill_rule_for_subrow (active, coverages);
+		else
+		    apply_evenodd_fill_rule_for_subrow (active, coverages);
+
+		active_list_substep_edges(active);
+	    }
+	}
+
+	GLITTER_BLIT_COVERAGES(coverages, i+ymin_i, j-i, xmin_i, xmax_i);
+	cell_list_reset (coverages);
+
+	if (! active->head)
+	    active->min_height = INT_MAX;
+	else
+	    active->min_height -= GRID_Y;
+    }
+}
+
+/*-------------------------------------------------------------------------
+ * cairo specific implementation: the coverage blitter and
+ * scan converter subclass. */
+
+static glitter_status_t
+blit_with_span_renderer (struct cell_list *cells,
+			 cairo_span_renderer_t *renderer,
+			 struct pool *span_pool,
+			 int y, int height,
+			 int xmin, int xmax)
+{
+    struct cell *cell = cells->head.next;
+    int prev_x = xmin, last_x = -1;
+    int cover = 0, last_cover = 0;
+    cairo_half_open_span_t *spans;
+    unsigned num_spans;
+
+    if (cell == &cells->tail)
+	return CAIRO_STATUS_SUCCESS;
+
+    /* Skip cells to the left of the clip region. */
+    while (cell->x < xmin) {
+	cover += cell->covered_height;
+	cell = cell->next;
+    }
+    cover *= GRID_X*2;
+
+    /* Count number of cells remaining. */
+    {
+	struct cell *next = cell;
+	num_spans = 2;
+	while (next->x < xmax) {
+	    next = next->next;
+	    ++num_spans;
+	}
+	num_spans = 2*num_spans;
+    }
+
+    /* Allocate enough spans for the row. */
+    pool_reset (span_pool);
+    spans = pool_alloc (span_pool, sizeof(spans[0])*num_spans);
+    num_spans = 0;
+
+    /* Form the spans from the coverages and areas. */
+    for (; cell->x < xmax; cell = cell->next) {
+	int x = cell->x;
+	int area;
+
+	if (x > prev_x && cover != last_cover) {
+	    spans[num_spans].x = prev_x;
+	    spans[num_spans].is_clipped = 0;
+	    spans[num_spans].coverage = GRID_AREA_TO_ALPHA (cover);
+	    last_cover = cover;
+	    last_x = prev_x;
+	    ++num_spans;
+	}
+
+	cover += cell->covered_height*GRID_X*2;
+	area = cover - cell->uncovered_area;
+
+	if (area != last_cover) {
+	    spans[num_spans].x = x;
+	    spans[num_spans].is_clipped = 0;
+	    spans[num_spans].coverage = GRID_AREA_TO_ALPHA (area);
+	    last_cover = area;
+	    last_x = x;
+	    ++num_spans;
+	}
+
+	prev_x = x+1;
+    }
+
+    if (prev_x <= xmax && cover != last_cover) {
+	spans[num_spans].x = prev_x;
+	spans[num_spans].is_clipped = 0;
+	spans[num_spans].coverage = GRID_AREA_TO_ALPHA (cover);
+	last_cover = cover;
+	last_x = prev_x;
+	++num_spans;
+    }
+
+    if (last_x < xmax && last_cover) {
+	spans[num_spans].x = xmax;
+	spans[num_spans].coverage = 0;
+	++num_spans;
+    }
+
+    /* Dump them into the renderer. */
+    return renderer->render_rows (renderer, y, height, spans, num_spans);
+}
+
+struct _cairo_tor33_scan_converter {
+    cairo_scan_converter_t base;
+
+    glitter_scan_converter_t converter[1];
+    cairo_fill_rule_t fill_rule;
+
+    jmp_buf jmp;
+
+    struct {
+	struct pool base[1];
+	cairo_half_open_span_t embedded[32];
+    } span_pool;
+};
+
+typedef struct _cairo_tor33_scan_converter cairo_tor33_scan_converter_t;
+
+static void
+_cairo_tor33_scan_converter_destroy (void *converter)
+{
+    cairo_tor33_scan_converter_t *self = converter;
+    if (self == NULL) {
+	return;
+    }
+    _glitter_scan_converter_fini (self->converter);
+    pool_fini (self->span_pool.base);
+    free(self);
+}
+
+static cairo_status_t
+_cairo_tor33_scan_converter_add_edge (void		*converter,
+				    const cairo_point_t *p1,
+				    const cairo_point_t *p2,
+				    int top, int bottom,
+				    int dir)
+{
+    cairo_tor33_scan_converter_t *self = converter;
+    cairo_edge_t edge;
+
+    edge.line.p1 = *p1;
+    edge.line.p2 = *p2;
+    edge.top = top;
+    edge.bottom = bottom;
+    edge.dir = dir;
+
+    glitter_scan_converter_add_edge (self->converter, &edge);
+    return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_status_t
+_cairo_tor33_scan_converter_add_polygon (void		*converter,
+				       const cairo_polygon_t *polygon)
+{
+    cairo_tor33_scan_converter_t *self = converter;
+    int i;
+
+    for (i = 0; i < polygon->num_edges; i++)
+	 glitter_scan_converter_add_edge (self->converter,
+						  &polygon->edges[i]);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_tor33_scan_converter_generate (void			*converter,
+				    cairo_span_renderer_t	*renderer)
+{
+    cairo_tor33_scan_converter_t *self = converter;
+    cairo_status_t status;
+
+    if ((status = setjmp (self->jmp)))
+	return _cairo_scan_converter_set_error (self, _cairo_error (status));
+
+    glitter_scan_converter_render (self->converter,
+				   self->fill_rule == CAIRO_FILL_RULE_WINDING,
+				   renderer,
+				   self->span_pool.base);
+    return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_scan_converter_t *
+_cairo_tor33_scan_converter_create (int			xmin,
+				  int			ymin,
+				  int			xmax,
+				  int			ymax,
+				  cairo_fill_rule_t	fill_rule)
+{
+    cairo_tor33_scan_converter_t *self;
+    cairo_status_t status;
+
+    self = calloc (1, sizeof(struct _cairo_tor33_scan_converter));
+    if (unlikely (self == NULL)) {
+	status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+	goto bail_nomem;
+    }
+
+    self->base.destroy = _cairo_tor33_scan_converter_destroy;
+    self->base.add_edge = _cairo_tor33_scan_converter_add_edge;
+    self->base.add_polygon = _cairo_tor33_scan_converter_add_polygon;
+    self->base.generate = _cairo_tor33_scan_converter_generate;
+
+    pool_init (self->span_pool.base, &self->jmp,
+	       250 * sizeof(self->span_pool.embedded[0]),
+	       sizeof(self->span_pool.embedded));
+
+    _glitter_scan_converter_init (self->converter, &self->jmp);
+    status = glitter_scan_converter_reset (self->converter,
+					   xmin, ymin, xmax, ymax);
+    if (unlikely (status))
+	goto bail;
+
+    self->fill_rule = fill_rule;
+
+    return &self->base;
+
+ bail:
+    self->base.destroy(&self->base);
+ bail_nomem:
+    return _cairo_scan_converter_create_in_error (status);
+}
commit ca68f4a520ec6390014bbe2d76d4089054950cac
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Wed Aug 3 12:53:18 2011 +0100

    fast-image: Use tolerance to adjust rectilinear strokes to the nearest pixel
    
    Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>

diff --git a/src/Makefile.sources b/src/Makefile.sources
index 31f16a6..2a1a1a6 100644
--- a/src/Makefile.sources
+++ b/src/Makefile.sources
@@ -95,6 +95,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 \
@@ -161,6 +162,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 \
@@ -175,6 +177,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-fast-image-surface.c b/src/cairo-fast-image-surface.c
index 30d3702..b50ad80 100644
--- a/src/cairo-fast-image-surface.c
+++ b/src/cairo-fast-image-surface.c
@@ -2930,6 +2930,8 @@ _cairo_fast_image_surface_stroke (void			*abstract_surface,
     cairo_rectangle_int_t unbounded;
     cairo_int_status_t status;
 
+    tolerance = FAST_IMAGE_TOLERANCE;
+
     _cairo_fast_image_surface_get_extents (surface, &unbounded);
     status = _cairo_composite_rectangles_init_for_stroke (&composite, &unbounded,
 							  op, source,
@@ -2943,11 +2945,12 @@ _cairo_fast_image_surface_stroke (void			*abstract_surface,
 	cairo_boxes_t boxes;
 
 	_cairo_boxes_init_with_clip (&boxes, composite.clip);
-	status = _cairo_path_fixed_stroke_rectilinear_to_boxes (path,
-								style,
-								ctm,
-								antialias,
-								&boxes);
+	status = _cairo_path_fixed_stroke_rectilinear_round_to_boxes (path,
+								      style,
+								      ctm,
+								      antialias,
+								      tolerance,
+								      &boxes);
 	if (likely (status == CAIRO_INT_STATUS_SUCCESS)) {
 	    status = _clip_and_composite_boxes (surface, op, source, 1.,
 						&boxes, &composite);
diff --git a/src/cairo-path-stroke-boxes.c b/src/cairo-path-stroke-boxes.c
new file mode 100644
index 0000000..d1959bc
--- /dev/null
+++ b/src/cairo-path-stroke-boxes.c
@@ -0,0 +1,776 @@
+/* -*- 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
+ * Copyright © 2011 Intel Corporation
+ *
+ * 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;
+    double tolerance;
+
+    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,
+				 double				 tolerance,
+				 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->tolerance = _cairo_fixed_from_double (tolerance);
+
+    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_fixed_t
+adjust_within_tolerance (cairo_fixed_t v, const cairo_fixed_t tolerance)
+{
+    if (! _cairo_fixed_is_integer (v)) {
+	cairo_fixed_t f = _cairo_fixed_round (v);
+	if (abs (f - v) < tolerance)
+	    v = f;
+    }
+    return v;
+}
+
+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;
+    }
+
+    if (stroker->tolerance > 0) {
+	stroker->segments[stroker->num_segments].p1.x =
+	    adjust_within_tolerance (p1->x, stroker->tolerance);
+	stroker->segments[stroker->num_segments].p1.y =
+	    adjust_within_tolerance (p1->y, stroker->tolerance);
+
+	stroker->segments[stroker->num_segments].p2.x =
+	    adjust_within_tolerance (p2->x, stroker->tolerance);
+	stroker->segments[stroker->num_segments].p2.y =
+	    adjust_within_tolerance (p2->y, stroker->tolerance);
+    } else {
+	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;
+	cairo_box_t box;
+
+	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;
+	}
+
+	box.p1 = *a;
+	box.p2 = *b;
+	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_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_box_t box;
+
+	    _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;
+	    }
+
+
+	    box.p1 = p1;
+	    box.p2 = p2;
+	    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;
+		}
+	    }
+
+	    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;
+
+
+	box.p1 = *a;
+	box.p2 = *b;
+
+	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, 0,
+					   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;
+}
+
+cairo_int_status_t
+_cairo_path_fixed_stroke_rectilinear_round_to_boxes (const cairo_path_fixed_t	*path,
+						     const cairo_stroke_style_t	*stroke_style,
+						     const cairo_matrix_t	*ctm,
+						     cairo_antialias_t	 antialias,
+						     double tolerance,
+						     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,
+					   tolerance,
+					   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..a164b28 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,
@@ -2100,63 +2034,3 @@ BAIL:
 
     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..74ee862
--- /dev/null
+++ b/src/cairo-stroke-dash-private.h
@@ -0,0 +1,65 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2005 Red Hat, Inc.
+ *
+ * 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 Red Hat, Inc.
+ *
+ * Contributor(s):
+ *	Carl D. Worth <cworth at redhat.com>
+ */
+
+#ifndef CAIRO_STROKE_DASH_PRIVATE_H
+#define CAIRO_STROKE_DASH_PRIVATE_H
+
+#include "cairo-types-private.h"
+#include "cairo-compiler-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;
+
+
+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);
+
+#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..97dbae5
--- /dev/null
+++ b/src/cairo-stroke-dash.c
@@ -0,0 +1,93 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2005 Red Hat, Inc
+ *
+ * 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 Red Hat, Inc.
+ *
+ * Contributor(s):
+ *	Carl Worth <cworth at cworth.org>
+ */
+
+#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/cairoint.h b/src/cairoint.h
index 5715ad1..049fc92 100644
--- a/src/cairoint.h
+++ b/src/cairoint.h
@@ -1439,6 +1439,14 @@ _cairo_path_fixed_stroke_rectilinear_to_boxes (const cairo_path_fixed_t	*path,
 					       cairo_boxes_t		*boxes);
 
 cairo_private cairo_int_status_t
+_cairo_path_fixed_stroke_rectilinear_round_to_boxes (const cairo_path_fixed_t	*path,
+						     const cairo_stroke_style_t	*stroke_style,
+						     const cairo_matrix_t	*ctm,
+						     cairo_antialias_t	 antialias,
+						     double tolerance,
+						     cairo_boxes_t		*boxes);
+
+cairo_private cairo_int_status_t
 _cairo_path_fixed_stroke_to_traps (const cairo_path_fixed_t	*path,
 				   const cairo_stroke_style_t	*stroke_style,
 				   const cairo_matrix_t	*ctm,
commit 206a43c7824a038a226439ac9e7ee1eea5eb9d8a
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Wed Aug 3 12:08:29 2011 +0100

    api: Introduce cairo_fast_image_surface_t
    
    The intention of this experimental surface/context backend is to compromise on
    image quality in the quest for speed.
    
    It is strongly recommended that you think twice before using this!
    
    Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>

diff --git a/boilerplate/cairo-boilerplate.c b/boilerplate/cairo-boilerplate.c
index b89e6d8..9f406eb 100644
--- a/boilerplate/cairo-boilerplate.c
+++ b/boilerplate/cairo-boilerplate.c
@@ -127,10 +127,10 @@ cairo_boilerplate_format_from_content (cairo_content_t content)
 static cairo_surface_t *
 _cairo_boilerplate_image_create_surface (const char		   *name,
 					 cairo_content_t	    content,
-					 double 		    width,
-					 double 		    height,
-					 double 		    max_width,
-					 double 		    max_height,
+					 double			    width,
+					 double			    height,
+					 double			    max_width,
+					 double			    max_height,
 					 cairo_boilerplate_mode_t   mode,
 					 int			    id,
 					 void			  **closure)
@@ -181,6 +181,60 @@ _cairo_boilerplate_image_create_similar (cairo_surface_t *other,
 }
 
 static cairo_surface_t *
+_cairo_boilerplate_fast_image_create_surface (const char		   *name,
+					      cairo_content_t	    content,
+					      double			    width,
+					      double			    height,
+					      double			    max_width,
+					      double			    max_height,
+					      cairo_boilerplate_mode_t   mode,
+					      int			    id,
+					      void			  **closure)
+{
+    cairo_format_t format;
+
+    *closure = NULL;
+
+    if (content == CAIRO_CONTENT_COLOR_ALPHA) {
+	format = CAIRO_FORMAT_ARGB32;
+    } else if (content == CAIRO_CONTENT_COLOR) {
+	format = CAIRO_FORMAT_RGB24;
+    } else {
+	assert (0); /* not reached */
+	return NULL;
+    }
+
+    return cairo_fast_image_surface_create (format, ceil (width), ceil (height));
+}
+
+static cairo_surface_t *
+_cairo_boilerplate_fast_image_create_similar (cairo_surface_t *other,
+					      cairo_content_t content,
+					      int width, int height)
+{
+    cairo_format_t format;
+    cairo_surface_t *surface;
+    int stride;
+    void *ptr;
+
+    switch (content) {
+    case CAIRO_CONTENT_ALPHA: format = CAIRO_FORMAT_A8; break;
+    case CAIRO_CONTENT_COLOR: format = CAIRO_FORMAT_RGB24; break;
+    default:
+    case CAIRO_CONTENT_COLOR_ALPHA: format = CAIRO_FORMAT_ARGB32; break;
+    }
+
+    stride = cairo_format_stride_for_width(format, width);
+    ptr = malloc (stride* height);
+
+    surface = cairo_fast_image_surface_create_for_data (ptr, format,
+							width, height, stride);
+    cairo_surface_set_user_data (surface, &key, ptr, free);
+
+    return surface;
+}
+
+static cairo_surface_t *
 _cairo_boilerplate_image16_create_surface (const char		     *name,
 					   cairo_content_t	      content,
 					   double		      width,
@@ -228,7 +282,7 @@ static char *
 _cairo_boilerplate_image_describe (void *closure)
 {
     char *s;
-  
+
     xasprintf (&s, "pixman %s", pixman_version_string ());
 
     return s;
@@ -236,7 +290,7 @@ _cairo_boilerplate_image_describe (void *closure)
 
 #if CAIRO_HAS_RECORDING_SURFACE
 static cairo_surface_t *
-_cairo_boilerplate_recording_create_surface (const char 	       *name,
+_cairo_boilerplate_recording_create_surface (const char		       *name,
 					     cairo_content_t		content,
 					     double			width,
 					     double			height,
@@ -426,6 +480,32 @@ static const cairo_boilerplate_target_t builtin_targets[] = {
         _cairo_boilerplate_image_describe,
 	TRUE, FALSE, FALSE
     },
+    {
+	"fast-image", "fast-image", NULL, NULL,
+	CAIRO_SURFACE_TYPE_FASTIMAGE, CAIRO_CONTENT_COLOR_ALPHA, 4,
+	NULL,
+	_cairo_boilerplate_fast_image_create_surface,
+	_cairo_boilerplate_fast_image_create_similar,
+	NULL, NULL,
+	_cairo_boilerplate_get_image_surface,
+	cairo_surface_write_to_png,
+	NULL, NULL,
+        _cairo_boilerplate_image_describe,
+	TRUE, FALSE, FALSE
+    },
+    {
+	"fast-image", "fast-image", NULL, NULL,
+	CAIRO_SURFACE_TYPE_FASTIMAGE, CAIRO_CONTENT_COLOR, 4,
+	NULL,
+	_cairo_boilerplate_fast_image_create_surface,
+	_cairo_boilerplate_fast_image_create_similar,
+	NULL, NULL,
+	_cairo_boilerplate_get_image_surface,
+	cairo_surface_write_to_png,
+	NULL, NULL,
+        _cairo_boilerplate_image_describe,
+	FALSE, FALSE, FALSE
+    },
 #if CAIRO_HAS_RECORDING_SURFACE
     {
 	"recording", "image", NULL, NULL,
diff --git a/src/Makefile.sources b/src/Makefile.sources
index 1dbe4c0..31f16a6 100644
--- a/src/Makefile.sources
+++ b/src/Makefile.sources
@@ -145,6 +145,7 @@ cairo_sources = \
 	cairo-hull.c \
 	cairo-image-info.c \
 	cairo-image-surface.c \
+	cairo-fast-image-surface.c \
 	cairo-lzw.c \
 	cairo-matrix.c \
 	cairo-mesh-pattern-rasterizer.c \
diff --git a/src/cairo-debug.c b/src/cairo-debug.c
index ada0cdb..ca94e2b 100644
--- a/src/cairo-debug.c
+++ b/src/cairo-debug.c
@@ -82,6 +82,7 @@ cairo_debug_reset_static_data (void)
     _cairo_clip_reset_static_data ();
 
     _cairo_image_reset_static_data ();
+    _cairo_fast_image_reset_static_data ();
 
 #if CAIRO_HAS_DRM_SURFACE
     _cairo_drm_device_reset_static_data ();
diff --git a/src/cairo-fast-image-surface.c b/src/cairo-fast-image-surface.c
new file mode 100644
index 0000000..30d3702
--- /dev/null
+++ b/src/cairo-fast-image-surface.c
@@ -0,0 +1,3490 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2003 University of Southern California
+ * Copyright © 2009,2010 Intel Corporation
+ *
+ * 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-boxes-private.h"
+#include "cairo-clip-private.h"
+#include "cairo-composite-rectangles-private.h"
+#include "cairo-default-context-private.h"
+#include "cairo-error-private.h"
+#include "cairo-gstate-private.h"
+#include "cairo-paginated-private.h"
+#include "cairo-pattern-private.h"
+#include "cairo-recording-surface-private.h"
+#include "cairo-region-private.h"
+#include "cairo-scaled-font-private.h"
+#include "cairo-surface-snapshot-private.h"
+#include "cairo-surface-subsurface-private.h"
+
+#define FAST_IMAGE_TOLERANCE 0.3
+
+
+/* Limit on the width / height of an image surface in pixels.  This is
+ * mainly determined by coordinates of things sent to pixman at the
+ * moment being in 16.16 format. */
+#define MAX_IMAGE_SIZE 16383
+#define PIXMAN_MAX_INT ((pixman_fixed_1 >> 1) - pixman_fixed_e) /* need to ensure deltas also fit */
+
+#if CAIRO_NO_MUTEX
+#undef PIXMAN_HAS_ATOMIC_OPS
+#define PIXMAN_HAS_ATOMIC_OPS 1
+#endif
+
+/**
+ * SECTION:cairo-fast-image
+ * @Title: Fast [Low Quality] Image Surfaces
+ * @Short_Description: Rendering to memory buffers
+ * @See_Also: #cairo_surface_t
+ *
+ * Image surfaces provide the ability to render to memory buffers
+ * either allocated by cairo or by the calling code.  The supported
+ * image formats are those defined in #cairo_format_t.
+ */
+
+static const cairo_surface_backend_t fast_image_surface_backend;
+
+typedef struct _cairo_fast_image_surface {
+    cairo_surface_t base;
+
+    pixman_image_t *pixman_image;
+    pixman_format_code_t pixman_format;
+    cairo_format_t format;
+
+    int width;
+    int height;
+} cairo_fast_image_surface_t;
+
+static pixman_image_t *
+_pixman_image_for_solid (const cairo_solid_pattern_t *pattern);
+
+static cairo_surface_t *
+fast_image_surface_create (pixman_format_code_t	 pixman_format,
+			   int			 width,
+			   int			 height)
+{
+    cairo_fast_image_surface_t *surface;
+    pixman_image_t *pixman_image;
+
+    pixman_image = pixman_image_create_bits (pixman_format, width, height,
+					     NULL, 0);
+    if (unlikely (pixman_image == NULL))
+	return _cairo_surface_create_in_error (CAIRO_STATUS_NO_MEMORY);
+
+    surface = malloc (sizeof (cairo_fast_image_surface_t));
+    if (unlikely (surface == NULL)) {
+	pixman_image_unref (pixman_image);
+	return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+    }
+
+    _cairo_surface_init (&surface->base,
+			 &fast_image_surface_backend,
+			 NULL, /* device */
+			 _cairo_content_from_pixman_format (pixman_format));
+
+    surface->pixman_image = pixman_image;
+    surface->pixman_format = pixman_format;
+    surface->format = _cairo_format_from_pixman_format (pixman_format);
+    surface->base.is_clear = TRUE;
+
+    surface->width = width;
+    surface->height = height;
+
+    return &surface->base;
+}
+
+static cairo_bool_t
+is_size_valid (int width, int height)
+{
+    return 0 <= width  &&  width <= MAX_IMAGE_SIZE &&
+	   0 <= height && height <= MAX_IMAGE_SIZE;
+}
+
+static cairo_surface_t *
+_cairo_fast_image_surface_create_similar (void	       *abstract_other,
+					  cairo_content_t	content,
+					  int		width,
+					  int		height)
+{
+    cairo_fast_image_surface_t *other = abstract_other;
+
+    if (! is_size_valid (width, height))
+	return _cairo_image_surface_create_with_content (content, width, height);
+
+    if (content == other->base.content)
+	return fast_image_surface_create (other->pixman_format, width, height);
+
+    return fast_image_surface_create (_cairo_format_to_pixman_format_code (_cairo_format_from_content (content)),
+				      width, height);
+}
+
+static cairo_surface_t *
+_cairo_fast_image_surface_map_to_image (void *abstract_other,
+					const cairo_rectangle_int_t *extents)
+{
+    cairo_fast_image_surface_t *other = abstract_other;
+    cairo_surface_t *surface;
+    int stride = pixman_image_get_stride (other->pixman_image);
+    uint8_t *data;
+
+    data = (uint8_t *) pixman_image_get_data (other->pixman_image);
+    data += extents->y * stride;
+    data += extents->x * PIXMAN_FORMAT_BPP (other->pixman_format)/ 8;
+
+    surface =
+	_cairo_image_surface_create_with_pixman_format (data,
+							other->pixman_format,
+							extents->width,
+							extents->height,
+							stride);
+
+    cairo_surface_set_device_offset (surface, -extents->x, -extents->y);
+    return surface;
+}
+
+static cairo_int_status_t
+_cairo_fast_image_surface_unmap_image (void *abstract_surface,
+				       cairo_image_surface_t *image)
+{
+    return CAIRO_INT_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_fast_image_surface_finish (void *abstract_surface)
+{
+    cairo_fast_image_surface_t *surface = abstract_surface;
+
+    if (surface->pixman_image) {
+	pixman_image_unref (surface->pixman_image);
+	surface->pixman_image = NULL;
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_fast_image_surface_acquire_source_image (void                    *abstract_surface,
+						cairo_image_surface_t  **image_out,
+						void                   **image_extra)
+{
+    cairo_fast_image_surface_t *surface = abstract_surface;
+
+    *image_out =
+	(cairo_image_surface_t *)
+	_cairo_image_surface_create_with_pixman_format ((uint8_t *) pixman_image_get_data (surface->pixman_image),
+							surface->pixman_format,
+							surface->width,
+							surface->height,
+							pixman_image_get_stride (surface->pixman_image));
+    *image_extra = NULL;
+
+    return (*image_out)->base.status;
+}
+
+static void
+_cairo_fast_image_surface_release_source_image (void                   *abstract_surface,
+						cairo_image_surface_t  *image,
+						void                   *image_extra)
+{
+    cairo_surface_destroy (&image->base);
+}
+
+static pixman_op_t
+_pixman_operator (cairo_operator_t op)
+{
+    switch (op) {
+    case CAIRO_OPERATOR_CLEAR:
+	return PIXMAN_OP_CLEAR;
+
+    case CAIRO_OPERATOR_SOURCE:
+	return PIXMAN_OP_SRC;
+    case CAIRO_OPERATOR_OVER:
+	return PIXMAN_OP_OVER;
+    case CAIRO_OPERATOR_IN:
+	return PIXMAN_OP_IN;
+    case CAIRO_OPERATOR_OUT:
+	return PIXMAN_OP_OUT;
+    case CAIRO_OPERATOR_ATOP:
+	return PIXMAN_OP_ATOP;
+
+    case CAIRO_OPERATOR_DEST:
+	return PIXMAN_OP_DST;
+    case CAIRO_OPERATOR_DEST_OVER:
+	return PIXMAN_OP_OVER_REVERSE;
+    case CAIRO_OPERATOR_DEST_IN:
+	return PIXMAN_OP_IN_REVERSE;
+    case CAIRO_OPERATOR_DEST_OUT:
+	return PIXMAN_OP_OUT_REVERSE;
+    case CAIRO_OPERATOR_DEST_ATOP:
+	return PIXMAN_OP_ATOP_REVERSE;
+
+    case CAIRO_OPERATOR_XOR:
+	return PIXMAN_OP_XOR;
+    case CAIRO_OPERATOR_ADD:
+	return PIXMAN_OP_ADD;
+    case CAIRO_OPERATOR_SATURATE:
+	return PIXMAN_OP_SATURATE;
+
+    case CAIRO_OPERATOR_MULTIPLY:
+	return PIXMAN_OP_MULTIPLY;
+    case CAIRO_OPERATOR_SCREEN:
+	return PIXMAN_OP_SCREEN;
+    case CAIRO_OPERATOR_OVERLAY:
+	return PIXMAN_OP_OVERLAY;
+    case CAIRO_OPERATOR_DARKEN:
+	return PIXMAN_OP_DARKEN;
+    case CAIRO_OPERATOR_LIGHTEN:
+	return PIXMAN_OP_LIGHTEN;
+    case CAIRO_OPERATOR_COLOR_DODGE:
+	return PIXMAN_OP_COLOR_DODGE;
+    case CAIRO_OPERATOR_COLOR_BURN:
+	return PIXMAN_OP_COLOR_BURN;
+    case CAIRO_OPERATOR_HARD_LIGHT:
+	return PIXMAN_OP_HARD_LIGHT;
+    case CAIRO_OPERATOR_SOFT_LIGHT:
+	return PIXMAN_OP_SOFT_LIGHT;
+    case CAIRO_OPERATOR_DIFFERENCE:
+	return PIXMAN_OP_DIFFERENCE;
+    case CAIRO_OPERATOR_EXCLUSION:
+	return PIXMAN_OP_EXCLUSION;
+    case CAIRO_OPERATOR_HSL_HUE:
+	return PIXMAN_OP_HSL_HUE;
+    case CAIRO_OPERATOR_HSL_SATURATION:
+	return PIXMAN_OP_HSL_SATURATION;
+    case CAIRO_OPERATOR_HSL_COLOR:
+	return PIXMAN_OP_HSL_COLOR;
+    case CAIRO_OPERATOR_HSL_LUMINOSITY:
+	return PIXMAN_OP_HSL_LUMINOSITY;
+
+    default:
+	ASSERT_NOT_REACHED;
+	return PIXMAN_OP_OVER;
+    }
+}
+
+#if PIXMAN_HAS_ATOMIC_OPS
+static pixman_image_t *__pixman_transparent_image;
+static pixman_image_t *__pixman_black_image;
+static pixman_image_t *__pixman_white_image;
+
+static pixman_image_t *
+_pixman_transparent_image (void)
+{
+    pixman_image_t *image;
+
+    image = __pixman_transparent_image;
+    if (unlikely (image == NULL)) {
+	pixman_color_t color;
+
+	color.red   = 0x00;
+	color.green = 0x00;
+	color.blue  = 0x00;
+	color.alpha = 0x00;
+
+	image = pixman_image_create_solid_fill (&color);
+	if (unlikely (image == NULL))
+	    return NULL;
+
+	if (_cairo_atomic_ptr_cmpxchg (&__pixman_transparent_image,
+				       NULL, image))
+	{
+	    pixman_image_ref (image);
+	}
+    } else {
+	pixman_image_ref (image);
+    }
+
+    return image;
+}
+
+static pixman_image_t *
+_pixman_black_image (void)
+{
+    pixman_image_t *image;
+
+    image = __pixman_black_image;
+    if (unlikely (image == NULL)) {
+	pixman_color_t color;
+
+	color.red   = 0x00;
+	color.green = 0x00;
+	color.blue  = 0x00;
+	color.alpha = 0xffff;
+
+	image = pixman_image_create_solid_fill (&color);
+	if (unlikely (image == NULL))
+	    return NULL;
+
+	if (_cairo_atomic_ptr_cmpxchg (&__pixman_black_image,
+				       NULL, image))
+	{
+	    pixman_image_ref (image);
+	}
+    } else {
+	pixman_image_ref (image);
+    }
+
+    return image;
+}
+
+static pixman_image_t *
+_pixman_white_image (void)
+{
+    pixman_image_t *image;
+
+    image = __pixman_white_image;
+    if (unlikely (image == NULL)) {
+	pixman_color_t color;
+
+	color.red   = 0xffff;
+	color.green = 0xffff;
+	color.blue  = 0xffff;
+	color.alpha = 0xffff;
+
+	image = pixman_image_create_solid_fill (&color);
+	if (unlikely (image == NULL))
+	    return NULL;
+
+	if (_cairo_atomic_ptr_cmpxchg (&__pixman_white_image,
+				       NULL, image))
+	{
+	    pixman_image_ref (image);
+	}
+    } else {
+	pixman_image_ref (image);
+    }
+
+    return image;
+}
+
+static uint32_t
+hars_petruska_f54_1_random (void)
+{
+#define rol(x,k) ((x << k) | (x >> (32-k)))
+    static uint32_t x;
+    return x = (x ^ rol (x, 5) ^ rol (x, 24)) + 0x37798849;
+#undef rol
+}
+
+static struct {
+    cairo_color_t color;
+    pixman_image_t *image;
+} cache[16];
+static int n_cached;
+
+#else  /* !PIXMAN_HAS_ATOMIC_OPS */
+static pixman_image_t *
+_pixman_transparent_image (void)
+{
+    return _pixman_image_for_solid (&_cairo_pattern_clear);
+}
+
+static pixman_image_t *
+_pixman_black_image (void)
+{
+    return _pixman_image_for_solid (&_cairo_pattern_black);
+}
+
+static pixman_image_t *
+_pixman_white_image (void)
+{
+    return _pixman_image_for_solid (&_cairo_pattern_white);
+}
+#endif /* !PIXMAN_HAS_ATOMIC_OPS */
+
+void
+_cairo_fast_image_reset_static_data (void)
+{
+#if PIXMAN_HAS_ATOMIC_OPS
+    while (n_cached)
+	pixman_image_unref (cache[--n_cached].image);
+
+    if (__pixman_transparent_image) {
+	pixman_image_unref (__pixman_transparent_image);
+	__pixman_transparent_image = NULL;
+    }
+
+    if (__pixman_black_image) {
+	pixman_image_unref (__pixman_black_image);
+	__pixman_black_image = NULL;
+    }
+
+    if (__pixman_white_image) {
+	pixman_image_unref (__pixman_white_image);
+	__pixman_white_image = NULL;
+    }
+#endif
+}
+
+static pixman_image_t *
+_pixman_image_for_solid (const cairo_solid_pattern_t *pattern)
+{
+    pixman_color_t color;
+    pixman_image_t *image;
+
+#if PIXMAN_HAS_ATOMIC_OPS
+    int i;
+
+    if (pattern->color.alpha_short <= 0x00ff)
+	return _pixman_transparent_image ();
+
+    if (pattern->color.alpha_short >= 0xff00) {
+	if (pattern->color.red_short <= 0x00ff &&
+	    pattern->color.green_short <= 0x00ff &&
+	    pattern->color.blue_short <= 0x00ff)
+	{
+	    return _pixman_black_image ();
+	}
+
+	if (pattern->color.red_short >= 0xff00 &&
+	    pattern->color.green_short >= 0xff00 &&
+	    pattern->color.blue_short >= 0xff00)
+	{
+	    return _pixman_white_image ();
+	}
+    }
+
+    CAIRO_MUTEX_LOCK (_cairo_image_solid_cache_mutex);
+    for (i = 0; i < n_cached; i++) {
+	if (_cairo_color_equal (&cache[i].color, &pattern->color)) {
+	    image = pixman_image_ref (cache[i].image);
+	    goto UNLOCK;
+	}
+    }
+#endif
+
+    color.red   = pattern->color.red_short;
+    color.green = pattern->color.green_short;
+    color.blue  = pattern->color.blue_short;
+    color.alpha = pattern->color.alpha_short;
+
+    image = pixman_image_create_solid_fill (&color);
+#if PIXMAN_HAS_ATOMIC_OPS
+    if (image == NULL)
+	goto UNLOCK;
+
+    if (n_cached < ARRAY_LENGTH (cache)) {
+	i = n_cached++;
+    } else {
+	i = hars_petruska_f54_1_random () % ARRAY_LENGTH (cache);
+	pixman_image_unref (cache[i].image);
+    }
+    cache[i].image = pixman_image_ref (image);
+    cache[i].color = pattern->color;
+
+UNLOCK:
+    CAIRO_MUTEX_UNLOCK (_cairo_image_solid_cache_mutex);
+#endif
+    return image;
+}
+
+static pixman_image_t *
+_pixman_image_for_gradient (const cairo_gradient_pattern_t *pattern,
+			    const cairo_rectangle_int_t *extents,
+			    int *ix, int *iy)
+{
+    pixman_image_t	  *pixman_image;
+    pixman_gradient_stop_t pixman_stops_static[2];
+    pixman_gradient_stop_t *pixman_stops = pixman_stops_static;
+    pixman_transform_t      pixman_transform;
+    cairo_matrix_t matrix;
+    cairo_circle_double_t extremes[2];
+    pixman_point_fixed_t p1, p2;
+    unsigned int i;
+    cairo_int_status_t status;
+
+    if (pattern->n_stops > ARRAY_LENGTH(pixman_stops_static)) {
+	pixman_stops = _cairo_malloc_ab (pattern->n_stops,
+					 sizeof(pixman_gradient_stop_t));
+	if (unlikely (pixman_stops == NULL))
+	    return NULL;
+    }
+
+    for (i = 0; i < pattern->n_stops; i++) {
+	pixman_stops[i].x = _cairo_fixed_16_16_from_double (pattern->stops[i].offset);
+	pixman_stops[i].color.red   = pattern->stops[i].color.red_short;
+	pixman_stops[i].color.green = pattern->stops[i].color.green_short;
+	pixman_stops[i].color.blue  = pattern->stops[i].color.blue_short;
+	pixman_stops[i].color.alpha = pattern->stops[i].color.alpha_short;
+    }
+
+    _cairo_gradient_pattern_fit_to_range (pattern, PIXMAN_MAX_INT >> 1, &matrix, extremes);
+
+    p1.x = _cairo_fixed_16_16_from_double (extremes[0].center.x);
+    p1.y = _cairo_fixed_16_16_from_double (extremes[0].center.y);
+    p2.x = _cairo_fixed_16_16_from_double (extremes[1].center.x);
+    p2.y = _cairo_fixed_16_16_from_double (extremes[1].center.y);
+
+    if (pattern->base.type == CAIRO_PATTERN_TYPE_LINEAR) {
+	pixman_image = pixman_image_create_linear_gradient (&p1, &p2,
+							    pixman_stops,
+							    pattern->n_stops);
+    } else {
+	pixman_fixed_t r1, r2;
+
+	r1   = _cairo_fixed_16_16_from_double (extremes[0].radius);
+	r2   = _cairo_fixed_16_16_from_double (extremes[1].radius);
+
+	pixman_image = pixman_image_create_radial_gradient (&p1, &p2, r1, r2,
+							    pixman_stops,
+							    pattern->n_stops);
+    }
+
+    if (pixman_stops != pixman_stops_static)
+	free (pixman_stops);
+
+    if (unlikely (pixman_image == NULL))
+	return NULL;
+
+    *ix = *iy = 0;
+    status = _cairo_matrix_to_pixman_matrix_offset (&matrix, pattern->base.filter,
+						    extents->x + extents->width/2.,
+						    extents->y + extents->height/2.,
+						    &pixman_transform, ix, iy);
+    if (status != CAIRO_INT_STATUS_NOTHING_TO_DO) {
+	if (unlikely (status != CAIRO_INT_STATUS_SUCCESS) ||
+	    ! pixman_image_set_transform (pixman_image, &pixman_transform))
+	{
+	    pixman_image_unref (pixman_image);
+	    return NULL;
+	}
+    }
+
+    {
+	pixman_repeat_t pixman_repeat;
+
+	switch (pattern->base.extend) {
+	default:
+	case CAIRO_EXTEND_NONE:
+	    pixman_repeat = PIXMAN_REPEAT_NONE;
+	    break;
+	case CAIRO_EXTEND_REPEAT:
+	    pixman_repeat = PIXMAN_REPEAT_NORMAL;
+	    break;
+	case CAIRO_EXTEND_REFLECT:
+	    pixman_repeat = PIXMAN_REPEAT_REFLECT;
+	    break;
+	case CAIRO_EXTEND_PAD:
+	    pixman_repeat = PIXMAN_REPEAT_PAD;
+	    break;
+	}
+
+	pixman_image_set_repeat (pixman_image, pixman_repeat);
+    }
+
+    return pixman_image;
+}
+
+struct acquire_source_cleanup {
+    cairo_surface_t *surface;
+    cairo_image_surface_t *image;
+    void *image_extra;
+};
+
+static void
+_acquire_source_cleanup (pixman_image_t *pixman_image,
+			 void *closure)
+{
+    struct acquire_source_cleanup *data = closure;
+
+    _cairo_surface_release_source_image (data->surface,
+					 data->image,
+					 data->image_extra);
+    free (data);
+}
+
+static uint16_t
+expand_channel (uint16_t v, uint32_t bits)
+{
+    int offset = 16 - bits;
+    while (offset > 0) {
+	v |= v >> bits;
+	offset -= bits;
+	bits += bits;
+    }
+    return v;
+}
+
+static pixman_image_t *
+_pixel_to_solid (cairo_format_t format, uint8_t *data, int stride, int x, int y)
+{
+    uint32_t pixel;
+    pixman_color_t color;
+
+    switch (format) {
+    default:
+	ASSERT_NOT_REACHED;
+	return NULL;
+
+    case CAIRO_FORMAT_INVALID:
+	return NULL;
+
+    case CAIRO_FORMAT_A1:
+	pixel = *(uint8_t *) (data + y * stride + x/8);
+	return pixel & (1 << (x&7)) ? _pixman_black_image () : _pixman_transparent_image ();
+
+    case CAIRO_FORMAT_A8:
+	color.alpha = *(uint8_t *) (data + y * stride + x);
+	color.alpha |= color.alpha << 8;
+	if (color.alpha == 0)
+	    return _pixman_transparent_image ();
+	if (color.alpha == 0xffff)
+	    return _pixman_black_image ();
+
+	color.red = color.green = color.blue = 0;
+	return pixman_image_create_solid_fill (&color);
+
+    case CAIRO_FORMAT_RGB16_565:
+	pixel = *(uint16_t *) (data + y * stride + 2 * x);
+	if (pixel == 0)
+	    return _pixman_black_image ();
+	if (pixel == 0xffff)
+	    return _pixman_white_image ();
+
+	color.alpha = 0xffff;
+	color.red = expand_channel ((pixel >> 11 & 0x1f) << 11, 5);
+	color.green = expand_channel ((pixel >> 5 & 0x3f) << 10, 6);
+	color.blue = expand_channel ((pixel & 0x1f) << 11, 5);
+	return pixman_image_create_solid_fill (&color);
+
+    case CAIRO_FORMAT_RGB30:
+	pixel = *(uint32_t *) (data + y * stride + 4 * x);
+	pixel &= 0x3fffffff; /* ignore alpha bits */
+	if (pixel == 0)
+	    return _pixman_black_image ();
+	if (pixel == 0x3fffffff)
+	    return _pixman_white_image ();
+
+	/* convert 10bpc to 16bpc */
+	color.alpha = 0xffff;
+	color.red = expand_channel((pixel >> 20) & 0x3fff, 10);
+	color.green = expand_channel((pixel >> 10) & 0x3fff, 10);
+	color.blue = expand_channel(pixel & 0x3fff, 10);
+	return pixman_image_create_solid_fill (&color);
+
+    case CAIRO_FORMAT_ARGB32:
+    case CAIRO_FORMAT_RGB24:
+	pixel = *(uint32_t *) (data + y * stride + 4 * x);
+	color.alpha = format == CAIRO_FORMAT_ARGB32 ? (pixel >> 24) | (pixel >> 16 & 0xff00) : 0xffff;
+	if (color.alpha == 0)
+	    return _pixman_transparent_image ();
+	if (pixel == 0xffffffff)
+	    return _pixman_white_image ();
+	if (color.alpha == 0xffff && (pixel & 0xffffff) == 0)
+	    return _pixman_black_image ();
+
+	color.red = (pixel >> 16 & 0xff) | (pixel >> 8 & 0xff00);
+	color.green = (pixel >> 8 & 0xff) | (pixel & 0xff00);
+	color.blue = (pixel & 0xff) | (pixel << 8 & 0xff00);
+	return pixman_image_create_solid_fill (&color);
+    }
+}
+
+static pixman_image_t *
+_fast_pixel_to_solid (cairo_fast_image_surface_t *image, int x, int y)
+{
+    uint8_t *data = (uint8_t *) pixman_image_get_data (image->pixman_image);
+    int stride = pixman_image_get_stride (image->pixman_image);
+    return _pixel_to_solid (image->format, data, stride, x, y);
+}
+
+static pixman_image_t *
+_image_pixel_to_solid (cairo_image_surface_t *image, int x, int y)
+{
+    return _pixel_to_solid (image->format, image->data, image->stride, x, y);
+}
+
+static pixman_image_t *
+_pixman_image_for_image (const cairo_surface_pattern_t *pattern,
+			 cairo_bool_t is_mask,
+			 const cairo_rectangle_int_t *extents,
+			 const cairo_rectangle_int_t *sample,
+			 cairo_extend_t *extend,
+			 int *ix, int *iy)
+{
+    pixman_image_t *pixman_image = NULL;
+
+    if (pattern->surface->type == CAIRO_SURFACE_TYPE_IMAGE &&
+	(! is_mask || ! pattern->base.has_component_alpha ||
+	 (pattern->surface->content & CAIRO_CONTENT_COLOR) == 0))
+    {
+	cairo_image_surface_t *source = (cairo_image_surface_t *) pattern->surface;
+	cairo_surface_type_t type;
+
+	if (source->base.backend->type == CAIRO_INTERNAL_SURFACE_TYPE_SNAPSHOT)
+	    source = (cairo_image_surface_t *) ((cairo_surface_snapshot_t *) pattern->surface)->target;
+
+	type = source->base.backend->type;
+	if (type == CAIRO_SURFACE_TYPE_IMAGE) {
+	    if (*extend != CAIRO_EXTEND_NONE &&
+		sample->x >= 0 &&
+		sample->y >= 0 &&
+		sample->x + sample->width  <= source->width &&
+		sample->y + sample->height <= source->height)
+	    {
+		*extend = CAIRO_EXTEND_NONE;
+	    }
+
+	    if (sample->width == 1 && sample->height == 1) {
+		if (sample->x < 0 ||
+		    sample->y < 0 ||
+		    sample->x >= source->width ||
+		    sample->y >= source->height)
+		{
+		    if (extend == CAIRO_EXTEND_NONE)
+			return _pixman_transparent_image ();
+		}
+		else
+		{
+		    pixman_image = _image_pixel_to_solid (source,
+							  sample->x, sample->y);
+                    if (pixman_image)
+                        return pixman_image;
+		}
+	    }
+
+#if PIXMAN_HAS_ATOMIC_OPS
+	    /* avoid allocating a 'pattern' image if we can reuse the original */
+	    if (*extend == CAIRO_EXTEND_NONE &&
+		_cairo_matrix_is_pixman_translation (&pattern->base.matrix,
+						     filter, ix, iy))
+	    {
+		return pixman_image_ref (source->pixman_image);
+	    }
+#endif
+
+	    pixman_image = pixman_image_create_bits (source->pixman_format,
+						     source->width,
+						     source->height,
+						     (uint32_t *) source->data,
+						     source->stride);
+	    if (unlikely (pixman_image == NULL))
+		return NULL;
+	} else if (type == CAIRO_SURFACE_TYPE_SUBSURFACE) {
+	    cairo_surface_subsurface_t *sub;
+	    cairo_bool_t is_contained = FALSE;
+
+	    sub = (cairo_surface_subsurface_t *) source;
+	    source = (cairo_image_surface_t *) sub->target;
+
+	    if (sample->x >= 0 &&
+		sample->y >= 0 &&
+		sample->x + sample->width  <= sub->extents.width &&
+		sample->y + sample->height <= sub->extents.height)
+	    {
+		is_contained = TRUE;
+	    }
+
+	    if (sample->width == 1 && sample->height == 1) {
+		if (is_contained) {
+		    pixman_image = _image_pixel_to_solid (source,
+							  sub->extents.x + sample->x,
+							  sub->extents.y + sample->y);
+                    if (pixman_image)
+                        return pixman_image;
+		} else {
+		    if (extend == CAIRO_EXTEND_NONE)
+			return _pixman_transparent_image ();
+		}
+	    }
+
+#if PIXMAN_HAS_ATOMIC_OPS
+	    *ix = sub->extents.x;
+	    *iy = sub->extents.y;
+	    if (is_contained &&
+		_cairo_matrix_is_pixman_translation (&pattern->base.matrix,
+						     filter, ix, iy))
+	    {
+		return pixman_image_ref (source->pixman_image);
+	    }
+#endif
+
+	    /* Avoid sub-byte offsets, force a copy in that case. */
+	    if (PIXMAN_FORMAT_BPP (source->pixman_format) >= 8) {
+		void *data = source->data
+		    + sub->extents.x * PIXMAN_FORMAT_BPP(source->pixman_format)/8
+		    + sub->extents.y * source->stride;
+		pixman_image = pixman_image_create_bits (source->pixman_format,
+							 sub->extents.width,
+							 sub->extents.height,
+							 data,
+							 source->stride);
+		if (unlikely (pixman_image == NULL))
+		    return NULL;
+	    }
+	}
+    }
+
+    return pixman_image;
+}
+
+static pixman_image_t *
+_pixman_image_for_fast_image (const cairo_surface_pattern_t *pattern,
+			      cairo_bool_t is_mask,
+			      const cairo_rectangle_int_t *extents,
+			      const cairo_rectangle_int_t *sample,
+			      cairo_extend_t *extend,
+			      int *ix, int *iy)
+{
+    pixman_image_t *pixman_image = NULL;
+
+    if (pattern->surface->type == CAIRO_SURFACE_TYPE_FASTIMAGE &&
+	(! is_mask || ! pattern->base.has_component_alpha ||
+	 (pattern->surface->content & CAIRO_CONTENT_COLOR) == 0))
+    {
+	cairo_fast_image_surface_t *source = (cairo_fast_image_surface_t *) pattern->surface;
+	cairo_surface_type_t type;
+
+	if (source->base.backend->type == CAIRO_INTERNAL_SURFACE_TYPE_SNAPSHOT)
+	    source = (cairo_fast_image_surface_t *) ((cairo_surface_snapshot_t *) pattern->surface)->target;
+
+	type = source->base.backend->type;
+	if (type == CAIRO_SURFACE_TYPE_FASTIMAGE) {
+	    if (*extend != CAIRO_EXTEND_NONE &&
+		sample->x >= 0 &&
+		sample->y >= 0 &&
+		sample->x + sample->width  <= source->width &&
+		sample->y + sample->height <= source->height)
+	    {
+		*extend = CAIRO_EXTEND_NONE;
+	    }
+
+	    if (sample->width == 1 && sample->height == 1) {
+		if (sample->x < 0 ||
+		    sample->y < 0 ||
+		    sample->x >= source->width ||
+		    sample->y >= source->height)
+		{
+		    if (*extend == CAIRO_EXTEND_NONE)
+			return _pixman_transparent_image ();
+		}
+		else
+		{
+		    pixman_image = _fast_pixel_to_solid (source,
+							 sample->x, sample->y);
+                    if (pixman_image)
+                        return pixman_image;
+		}
+	    }
+
+#if PIXMAN_HAS_ATOMIC_OPS
+	    /* avoid allocating a 'pattern' image if we can reuse the original */
+	    if (*extend == CAIRO_EXTEND_NONE &&
+		_cairo_matrix_is_pixman_translation (&pattern->base.matrix,
+						     filter, ix, iy))
+	    {
+		return pixman_image_ref (source->pixman_image);
+	    }
+#endif
+
+	    pixman_image = pixman_image_create_bits (source->pixman_format,
+						     source->width,
+						     source->height,
+						     pixman_image_get_data (source->pixman_image),
+						     pixman_image_get_stride (source->pixman_image));
+	    if (unlikely (pixman_image == NULL))
+		return NULL;
+	} else if (type == CAIRO_SURFACE_TYPE_SUBSURFACE) {
+	    cairo_surface_subsurface_t *sub;
+	    cairo_bool_t is_contained = FALSE;
+
+	    sub = (cairo_surface_subsurface_t *) source;
+	    source = (cairo_fast_image_surface_t *) sub->target;
+
+	    if (sample->x >= 0 &&
+		sample->y >= 0 &&
+		sample->x + sample->width  <= sub->extents.width &&
+		sample->y + sample->height <= sub->extents.height)
+	    {
+		is_contained = TRUE;
+	    }
+
+	    if (sample->width == 1 && sample->height == 1) {
+		if (is_contained) {
+		    pixman_image = _fast_pixel_to_solid (source,
+							 sub->extents.x + sample->x,
+							 sub->extents.y + sample->y);
+                    if (pixman_image)
+                        return pixman_image;
+		} else {
+		    if (*extend == CAIRO_EXTEND_NONE)
+			return _pixman_transparent_image ();
+		}
+	    }
+
+#if PIXMAN_HAS_ATOMIC_OPS
+	    *ix = sub->extents.x;
+	    *iy = sub->extents.y;
+	    if (is_contained &&
+		_cairo_matrix_is_pixman_translation (&pattern->base.matrix,
+						     filter, ix, iy))
+	    {
+		return pixman_image_ref (source->pixman_image);
+	    }
+#endif
+
+	    /* Avoid sub-byte offsets, force a copy in that case. */
+	    if (PIXMAN_FORMAT_BPP (source->pixman_format) >= 8) {
+		int stride = pixman_image_get_stride (source->pixman_image);
+		void *data = (uint8_t *) pixman_image_get_data (source->pixman_image)
+		    + sub->extents.x * PIXMAN_FORMAT_BPP(source->pixman_format)/8
+		    + sub->extents.y * stride;
+		pixman_image = pixman_image_create_bits (source->pixman_format,
+							 sub->extents.width,
+							 sub->extents.height,
+							 data, stride);
+	    }
+	}
+    }
+
+    return pixman_image;
+}
+
+static pixman_image_t *
+_pixman_image_for_surface (const cairo_surface_pattern_t *pattern,
+			   cairo_bool_t is_mask,
+			   const cairo_rectangle_int_t *extents,
+			   cairo_matrix_t *dst_device_transform,
+			   int *ix, int *iy)
+{
+    pixman_transform_t pixman_transform;
+    pixman_image_t *pixman_image;
+    cairo_matrix_t m;
+    cairo_rectangle_int_t sample;
+    cairo_extend_t extend;
+    cairo_filter_t filter;
+    cairo_int_status_t status;
+    cairo_bool_t undo_src_transform = FALSE;
+
+    extend = pattern->base.extend;
+    filter = _cairo_surface_pattern_sampled_area (pattern, extents, &sample);
+
+    *ix = *iy = 0;
+    pixman_image = _pixman_image_for_fast_image (pattern, is_mask,
+						 extents, &sample, &extend,
+						 ix, iy);
+    if (pixman_image == NULL)
+	pixman_image = _pixman_image_for_image (pattern, is_mask,
+						extents, &sample, &extend,
+						ix, iy);
+#if PIXMAN_HAS_ATOMIC_OPS
+    *ix = *iy = 0;
+#endif
+
+    if (pixman_image == NULL) {
+	struct acquire_source_cleanup *cleanup;
+	cairo_image_surface_t *image;
+	void *extra;
+	cairo_status_t status;
+
+	status = _cairo_surface_acquire_source_image_transformed (pattern->surface, dst_device_transform, &image, &extra);
+	if (unlikely (status))
+	    return NULL;
+
+	if (sample.x >= 0 && sample.y >= 0 &&
+	    sample.x + sample.width  <= image->width &&
+	    sample.y + sample.height <= image->height)
+	{
+	    extend = CAIRO_EXTEND_NONE;
+	}
+
+	if (sample.width == 1 && sample.height == 1) {
+	    if (sample.x < 0 ||
+		sample.y < 0 ||
+		sample.x >= image->width ||
+		sample.y >= image->height)
+	    {
+		if (extend == CAIRO_EXTEND_NONE) {
+		    pixman_image = _pixman_transparent_image ();
+		    _cairo_surface_release_source_image (pattern->surface, image, extra);
+		    return pixman_image;
+		}
+	    }
+	    else
+	    {
+		pixman_image = _image_pixel_to_solid (image, sample.x, sample.y);
+                if (pixman_image)
+                {
+                    _cairo_surface_release_source_image (pattern->surface, image, extra);
+                    return pixman_image;
+                }
+	    }
+	}
+
+	pixman_image = pixman_image_create_bits (image->pixman_format,
+						 image->width,
+						 image->height,
+						 (uint32_t *) image->data,
+						 image->stride);
+	if (unlikely (pixman_image == NULL)) {
+	    _cairo_surface_release_source_image (pattern->surface, image, extra);
+	    return NULL;
+	}
+
+	cleanup = malloc (sizeof (*cleanup));
+	if (unlikely (cleanup == NULL)) {
+	    _cairo_surface_release_source_image (pattern->surface, image, extra);
+	    pixman_image_unref (pixman_image);
+	    return NULL;
+	}
+
+	cleanup->surface = pattern->surface;
+	cleanup->image = image;
+	cleanup->image_extra = extra;
+	pixman_image_set_destroy_function (pixman_image,
+					   _acquire_source_cleanup, cleanup);
+	undo_src_transform = TRUE;
+    }
+
+    m = pattern->base.matrix;
+    if (undo_src_transform) {
+	cairo_matrix_t sm;
+
+	cairo_matrix_init_scale (&sm,
+				 dst_device_transform->xx,
+				 dst_device_transform->yy);
+	cairo_matrix_multiply (&m, &m, &sm);
+    }
+
+    status = _cairo_matrix_to_pixman_matrix_offset (&m, filter,
+						    extents->x + extents->width/2.,
+						    extents->y + extents->height/2.,
+						    &pixman_transform, ix, iy);
+    if (status == CAIRO_INT_STATUS_NOTHING_TO_DO)
+    {
+	/* If the transform is an identity, we don't need to set it
+	 * and we can use any filtering, so choose the fastest one. */
+	pixman_image_set_filter (pixman_image, PIXMAN_FILTER_NEAREST, NULL, 0);
+    }
+    else if (unlikely (status != CAIRO_INT_STATUS_SUCCESS ||
+		       ! pixman_image_set_transform (pixman_image,
+						     &pixman_transform)))
+    {
+	pixman_image_unref (pixman_image);
+	return NULL;
+    }
+    else
+    {
+	pixman_filter_t pixman_filter;
+
+	switch (filter) {
+	case CAIRO_FILTER_FAST:
+	    pixman_filter = PIXMAN_FILTER_FAST;
+	    break;
+	case CAIRO_FILTER_GOOD:
+	    pixman_filter = PIXMAN_FILTER_GOOD;
+	    break;
+	case CAIRO_FILTER_BEST:
+	    pixman_filter = PIXMAN_FILTER_BEST;
+	    break;
+	case CAIRO_FILTER_NEAREST:
+	    pixman_filter = PIXMAN_FILTER_NEAREST;
+	    break;
+	case CAIRO_FILTER_BILINEAR:
+	    pixman_filter = PIXMAN_FILTER_BILINEAR;
+	    break;
+	case CAIRO_FILTER_GAUSSIAN:
+	    /* XXX: The GAUSSIAN value has no implementation in cairo
+	     * whatsoever, so it was really a mistake to have it in the
+	     * API. We could fix this by officially deprecating it, or
+	     * else inventing semantics and providing an actual
+	     * implementation for it. */
+	default:
+	    pixman_filter = PIXMAN_FILTER_BEST;
+	}
+
+	pixman_image_set_filter (pixman_image, pixman_filter, NULL, 0);
+    }
+
+    {
+	pixman_repeat_t pixman_repeat;
+
+	switch (extend) {
+	default:
+	case CAIRO_EXTEND_NONE:
+	    pixman_repeat = PIXMAN_REPEAT_NONE;
+	    break;
+	case CAIRO_EXTEND_REPEAT:
+	    pixman_repeat = PIXMAN_REPEAT_NORMAL;
+	    break;
+	case CAIRO_EXTEND_REFLECT:
+	    pixman_repeat = PIXMAN_REPEAT_REFLECT;
+	    break;
+	case CAIRO_EXTEND_PAD:
+	    pixman_repeat = PIXMAN_REPEAT_PAD;
+	    break;
+	}
+
+	pixman_image_set_repeat (pixman_image, pixman_repeat);
+    }
+
+    if (pattern->base.has_component_alpha)
+	pixman_image_set_component_alpha (pixman_image, TRUE);
+
+    return pixman_image;
+}
+
+static pixman_image_t *
+_pixman_image_for_pattern (const cairo_pattern_t *pattern,
+			   cairo_bool_t is_mask,
+			   const cairo_rectangle_int_t *extents,
+			   cairo_matrix_t *dst_device_transform,
+			   int *tx, int *ty)
+{
+    *tx = *ty = 0;
+
+    if (pattern == NULL)
+	return _pixman_white_image ();
+
+    switch (pattern->type) {
+    default:
+	ASSERT_NOT_REACHED;
+    case CAIRO_PATTERN_TYPE_SOLID:
+	return _pixman_image_for_solid ((const cairo_solid_pattern_t *) pattern);
+
+    case CAIRO_PATTERN_TYPE_RADIAL:
+    case CAIRO_PATTERN_TYPE_LINEAR:
+	return _pixman_image_for_gradient ((const cairo_gradient_pattern_t *) pattern,
+					   extents, tx, ty);
+
+    case CAIRO_PATTERN_TYPE_SURFACE:
+	return _pixman_image_for_surface ((const cairo_surface_pattern_t *) pattern,
+					  is_mask, extents, dst_device_transform, tx, ty);
+
+    case CAIRO_PATTERN_TYPE_MESH: {
+	cairo_surface_t *image;
+	pixman_image_t *r;
+	void * data;
+	int width, height, stride;
+
+	*tx = -extents->x;
+	*ty = -extents->y;
+	width = extents->width;
+	height = extents->height;
+
+	image = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
+	if (unlikely (image->status))
+	    return NULL;
+
+	stride = cairo_image_surface_get_stride (image);
+	data = cairo_image_surface_get_data (image);
+
+	_cairo_mesh_pattern_rasterize ((cairo_mesh_pattern_t *) pattern,
+				       data, width, height, stride, *tx, *ty);
+	r = pixman_image_ref (((cairo_image_surface_t *)image)->pixman_image);
+	cairo_surface_destroy (image);
+	return r;
+    }
+    }
+}
+
+static cairo_status_t
+fast_image_surface_fixup_unbounded (cairo_fast_image_surface_t *dst,
+				    const cairo_composite_rectangles_t *rects,
+				    cairo_clip_t *clip)
+{
+    cairo_surface_t *clip_surface = NULL;
+    pixman_image_t *mask = NULL;
+    pixman_box32_t boxes[4];
+    int i, mask_x = 0, mask_y = 0, n_boxes = 0;
+    cairo_status_t status = CAIRO_STATUS_SUCCESS;
+
+    if (clip != NULL) {
+	int clip_x, clip_y;
+
+	clip_surface = _cairo_clip_get_surface (clip, &dst->base, &clip_x, &clip_y);
+	if (unlikely (clip_surface->status))
+	    return clip_surface->status;
+
+	mask = ((cairo_image_surface_t *) clip_surface)->pixman_image;
+	mask_x = -clip_x;
+	mask_y = -clip_y;
+    } else {
+	if (rects->bounded.width  == rects->unbounded.width &&
+	    rects->bounded.height == rects->unbounded.height)
+	{
+	    return CAIRO_STATUS_SUCCESS;
+	}
+    }
+
+    /* wholly unbounded? */
+    if (rects->bounded.width == 0 || rects->bounded.height == 0) {
+	int x = rects->unbounded.x;
+	int y = rects->unbounded.y;
+	int width = rects->unbounded.width;
+	int height = rects->unbounded.height;
+
+	if (mask != NULL) {
+	    pixman_image_composite32 (PIXMAN_OP_OUT_REVERSE,
+                                      mask, NULL, dst->pixman_image,
+                                      x + mask_x, y + mask_y,
+                                      0, 0,
+                                      x, y,
+                                      width, height);
+	} else {
+            pixman_color_t color = { 0, };
+            pixman_box32_t box = { x, y, x + width, y + height };
+
+            if (! pixman_image_fill_boxes (PIXMAN_OP_CLEAR,
+					   dst->pixman_image,
+					   &color,
+					   1, &box))
+		status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+	}
+
+	cairo_surface_destroy (clip_surface);
+	return status;
+    }
+
+    /* top */
+    if (rects->bounded.y != rects->unbounded.y) {
+        boxes[n_boxes].x1 = rects->unbounded.x;
+        boxes[n_boxes].y1 = rects->unbounded.y;
+        boxes[n_boxes].x2 = rects->unbounded.x + rects->unbounded.width;
+        boxes[n_boxes].y2 = rects->bounded.y;
+        n_boxes++;
+    }
+
+    /* left */
+    if (rects->bounded.x != rects->unbounded.x) {
+        boxes[n_boxes].x1 = rects->unbounded.x;
+        boxes[n_boxes].y1 = rects->bounded.y;
+        boxes[n_boxes].x2 = rects->bounded.x;
+        boxes[n_boxes].y2 = rects->bounded.y + rects->bounded.height;
+        n_boxes++;
+    }
+
+    /* right */
+    if (rects->bounded.x + rects->bounded.width != rects->unbounded.x + rects->unbounded.width) {
+        boxes[n_boxes].x1 = rects->bounded.x + rects->bounded.width;
+        boxes[n_boxes].y1 = rects->bounded.y;
+        boxes[n_boxes].x2 = rects->unbounded.x + rects->unbounded.width;
+        boxes[n_boxes].y2 = rects->bounded.y + rects->bounded.height;
+        n_boxes++;
+    }
+
+    /* bottom */
+    if (rects->bounded.y + rects->bounded.height != rects->unbounded.y + rects->unbounded.height) {
+        boxes[n_boxes].x1 = rects->unbounded.x;
+        boxes[n_boxes].y1 = rects->bounded.y + rects->bounded.height;
+        boxes[n_boxes].x2 = rects->unbounded.x + rects->unbounded.width;
+        boxes[n_boxes].y2 = rects->unbounded.y + rects->unbounded.height;
+        n_boxes++;
+    }
+
+    if (mask != NULL) {
+        for (i = 0; i < n_boxes; i++) {
+            pixman_image_composite32 (PIXMAN_OP_OUT_REVERSE,
+                                      mask, NULL, dst->pixman_image,
+                                      boxes[i].x1 + mask_x, boxes[i].y1 + mask_y,
+                                      0, 0,
+                                      boxes[i].x1, boxes[i].y1,
+                                      boxes[i].x2 - boxes[i].x1, boxes[i].y2 - boxes[i].y1);
+        }
+    } else {
+        pixman_color_t color = { 0, };
+
+        if (! pixman_image_fill_boxes (PIXMAN_OP_CLEAR,
+				       dst->pixman_image,
+				       &color,
+				       n_boxes,
+				       boxes))
+	{
+	    status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+	}
+    }
+
+    cairo_surface_destroy (clip_surface);
+    return status;
+}
+
+static cairo_status_t
+fast_image_surface_fixup_unbounded_boxes (cairo_fast_image_surface_t *dst,
+					  const cairo_composite_rectangles_t *extents,
+					  cairo_region_t *clip_region,
+					  cairo_boxes_t *boxes)
+{
+    cairo_boxes_t clear;
+    cairo_box_t box;
+    cairo_status_t status;
+    struct _cairo_boxes_chunk *chunk;
+    int i;
+
+    if (boxes->num_boxes <= 1 && clip_region == NULL)
+	return fast_image_surface_fixup_unbounded (dst, extents, NULL);
+
+    _cairo_boxes_init (&clear);
+
+    box.p1.x = _cairo_fixed_from_int (extents->unbounded.x + extents->unbounded.width);
+    box.p1.y = _cairo_fixed_from_int (extents->unbounded.y);
+    box.p2.x = _cairo_fixed_from_int (extents->unbounded.x);
+    box.p2.y = _cairo_fixed_from_int (extents->unbounded.y + extents->unbounded.height);
+
+    if (clip_region == NULL) {
+	cairo_boxes_t tmp;
+
+	_cairo_boxes_init (&tmp);
+
+	status = _cairo_boxes_add (&tmp, CAIRO_ANTIALIAS_DEFAULT, &box);
+	assert (status == CAIRO_STATUS_SUCCESS);
+
+	tmp.chunks.next = &boxes->chunks;
+	tmp.num_boxes += boxes->num_boxes;
+
+	status = _cairo_bentley_ottmann_tessellate_boxes (&tmp,
+							  CAIRO_FILL_RULE_WINDING,
+							  &clear);
+
+	tmp.chunks.next = NULL;
+    } else {
+	pixman_box32_t *pbox;
+
+	pbox = pixman_region32_rectangles (&clip_region->rgn, &i);
+	_cairo_boxes_limit (&clear, (cairo_box_t *) pbox, i);
+
+	status = _cairo_boxes_add (&clear, CAIRO_ANTIALIAS_DEFAULT, &box);
+	assert (status == CAIRO_STATUS_SUCCESS);
+
+	for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) {
+	    for (i = 0; i < chunk->count; i++) {
+		status = _cairo_boxes_add (&clear,
+					   CAIRO_ANTIALIAS_DEFAULT,
+					   &chunk->base[i]);
+		if (unlikely (status)) {
+		    _cairo_boxes_fini (&clear);
+		    return status;
+		}
+	    }
+	}
+
+	status = _cairo_bentley_ottmann_tessellate_boxes (&clear,
+							  CAIRO_FILL_RULE_WINDING,
+							  &clear);
+    }
+
+    if (likely (status == CAIRO_STATUS_SUCCESS)) {
+	for (chunk = &clear.chunks; chunk != NULL; chunk = chunk->next) {
+	    for (i = 0; i < chunk->count; i++) {
+		int x1 = _cairo_fixed_integer_part (chunk->base[i].p1.x);
+		int y1 = _cairo_fixed_integer_part (chunk->base[i].p1.y);
+		int x2 = _cairo_fixed_integer_part (chunk->base[i].p2.x);
+		int y2 = _cairo_fixed_integer_part (chunk->base[i].p2.y);
+
+		pixman_fill (pixman_image_get_data (dst->pixman_image),
+			     pixman_image_get_stride (dst->pixman_image) / sizeof (uint32_t),
+			     PIXMAN_FORMAT_BPP (dst->pixman_format),
+			     x1, y1, x2 - x1, y2 - y1,
+			     0);
+	    }
+	}
+    }
+
+    _cairo_boxes_fini (&clear);
+
+    return status;
+}
+
+static cairo_bool_t
+can_reduce_alpha_op (cairo_operator_t op)
+{
+    int iop = op;
+    switch (iop) {
+    case CAIRO_OPERATOR_OVER:
+    case CAIRO_OPERATOR_SOURCE:
+    case CAIRO_OPERATOR_ADD:
+	return TRUE;
+    default:
+	return FALSE;
+    }
+}
+
+static cairo_bool_t
+reduce_alpha_op (cairo_surface_t *dst,
+		 cairo_operator_t op,
+		 const cairo_pattern_t *pattern)
+{
+    return dst->is_clear &&
+	   dst->content == CAIRO_CONTENT_ALPHA &&
+	   _cairo_pattern_is_opaque_solid (pattern) &&
+	   can_reduce_alpha_op (op);
+}
+
+/* low level compositor */
+typedef cairo_status_t
+(*image_draw_func_t) (void				*closure,
+		      cairo_fast_image_surface_t        *dst,
+		      cairo_operator_t			 op,
+		      const cairo_pattern_t		*src,
+		      double                             opacity,
+		      int				 dst_x,
+		      int				 dst_y,
+		      const cairo_composite_rectangles_t	*extents);
+
+static pixman_image_t *
+_create_composite_mask_pattern (cairo_composite_rectangles_t *extents,
+				image_draw_func_t              draw_func,
+				void                          *draw_closure,
+				double                         opacity,
+				cairo_fast_image_surface_t    *dst)
+{
+    cairo_region_t *clip_region = _cairo_clip_get_region (extents->clip);
+    cairo_bool_t need_clip_surface = ! _cairo_clip_is_region (extents->clip);
+    cairo_fast_image_surface_t *mask;
+    pixman_image_t *image;
+    cairo_status_t status;
+
+    if (clip_region != NULL && cairo_region_num_rectangles (clip_region) == 1)
+	clip_region = NULL;
+
+    mask = (cairo_fast_image_surface_t *)
+	fast_image_surface_create (PIXMAN_a8,
+				   extents->bounded.width,
+				   extents->bounded.height);
+    if (unlikely (mask->base.status))
+	return NULL;
+
+    /* Is it worth setting the clip region here? */
+    if (clip_region != NULL) {
+	pixman_bool_t ret;
+
+	pixman_region32_translate (&clip_region->rgn, -extents->bounded.x, -extents->bounded.y);
+	ret = pixman_image_set_clip_region32 (mask->pixman_image,
+					      &clip_region->rgn);
+	pixman_region32_translate (&clip_region->rgn, extents->bounded.x, extents->bounded.y);
+
+	if (! ret) {
+	    cairo_surface_destroy (&mask->base);
+	    return NULL;
+	}
+    }
+
+    mask->base.device_transform = dst->base.device_transform;
+    status = draw_func (draw_closure, mask,
+			CAIRO_OPERATOR_ADD, NULL, opacity,
+			extents->bounded.x, extents->bounded.y,
+			extents);
+    if (unlikely (status)) {
+	cairo_surface_destroy (&mask->base);
+	return NULL;
+    }
+
+    if (need_clip_surface) {
+	status = _cairo_clip_combine_with_surface (extents->clip, &mask->base,
+						   extents->bounded.x,
+						   extents->bounded.y);
+	if (unlikely (status)) {
+	    cairo_surface_destroy (&mask->base);
+	    return NULL;
+	}
+    }
+
+    if (clip_region != NULL)
+	pixman_image_set_clip_region (mask->pixman_image, NULL);
+
+    image = mask->pixman_image;
+    pixman_image_ref (image);
+    cairo_surface_destroy (&mask->base);
+
+    return image;
+}
+
+/* Handles compositing with a clip surface when the operator allows
+ * us to combine the clip with the mask
+ */
+static cairo_status_t
+_clip_and_composite_with_mask (cairo_composite_rectangles_t *extents,
+			       cairo_operator_t               op,
+			       const cairo_pattern_t         *pattern,
+			       double                         opacity,
+			       image_draw_func_t              draw_func,
+			       void                          *draw_closure,
+			       cairo_fast_image_surface_t   *dst)
+{
+    pixman_image_t *mask;
+
+    mask = _create_composite_mask_pattern (extents,
+					   draw_func, draw_closure,
+					   opacity,
+					   dst);
+    if (unlikely (mask == NULL))
+	return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    if (pattern == NULL) {
+	if (dst->pixman_format == PIXMAN_a8) {
+	    pixman_image_composite32 (_pixman_operator (op),
+                                      mask, NULL, dst->pixman_image,
+                                      0, 0, 0, 0,
+                                      extents->bounded.x,      extents->bounded.y,
+                                      extents->bounded.width,  extents->bounded.height);
+	} else {
+	    pixman_image_t *src;
+
+	    src = _pixman_white_image ();
+	    if (unlikely (src == NULL)) {
+		pixman_image_unref (mask);
+		return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+	    }
+
+	    pixman_image_composite32 (_pixman_operator (op),
+                                      src, mask, dst->pixman_image,
+                                      0, 0, 0, 0,
+                                      extents->bounded.x,      extents->bounded.y,
+                                      extents->bounded.width,  extents->bounded.height);
+	    pixman_image_unref (src);
+	}
+    } else {
+	pixman_image_t *src;
+	int src_x, src_y;
+
+	src = _pixman_image_for_pattern (pattern, FALSE, &extents->bounded,
+					 &dst->base.device_transform,
+					 &src_x, &src_y);
+	if (unlikely (src == NULL)) {
+	    pixman_image_unref (mask);
+	    return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+	}
+
+	pixman_image_composite32 (_pixman_operator (op),
+                                  src, mask, dst->pixman_image,
+                                  extents->bounded.x + src_x,  extents->bounded.y + src_y,
+                                  0, 0,
+                                  extents->bounded.x,          extents->bounded.y,
+                                  extents->bounded.width,      extents->bounded.height);
+	pixman_image_unref (src);
+    }
+
+    pixman_image_unref (mask);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+/* Handles compositing with a clip surface when we have to do the operation
+ * in two pieces and combine them together.
+ */
+static cairo_status_t
+_clip_and_composite_combine (cairo_composite_rectangles_t  *extents,
+			     cairo_operator_t               op,
+			     const cairo_pattern_t         *src,
+			     double                         opacity,
+			     image_draw_func_t              draw_func,
+			     void                          *draw_closure,
+			     cairo_fast_image_surface_t    *dst)
+{
+    cairo_fast_image_surface_t *tmp;
+    cairo_surface_t *clip_surface;
+    int clip_x, clip_y;
+    cairo_status_t status;
+
+    tmp = (cairo_fast_image_surface_t *)
+	fast_image_surface_create (dst->pixman_format,
+				   extents->bounded.width,
+				   extents->bounded.height);
+    if (unlikely (tmp->base.status))
+	return tmp->base.status;
+
+    tmp->base.device_transform = dst->base.device_transform;
+    if (src == NULL) {
+	status = (*draw_func) (draw_closure, tmp,
+			       CAIRO_OPERATOR_ADD, NULL, opacity,
+			       extents->bounded.x, extents->bounded.y,
+			       extents);
+    } else {
+	/* Initialize the temporary surface from the destination surface */
+	if (! dst->base.is_clear) {
+	    pixman_image_composite32 (PIXMAN_OP_SRC,
+                                      dst->pixman_image, NULL, tmp->pixman_image,
+                                      extents->bounded.x,
+				      extents->bounded.y,
+                                      0, 0,
+                                      0, 0,
+                                      extents->bounded.width,
+				      extents->bounded.height);
+	}
+
+	status = (*draw_func) (draw_closure, tmp, op, src, opacity,
+			       extents->bounded.x, extents->bounded.y,
+			       extents);
+    }
+    if (unlikely (status))
+	goto CLEANUP_SURFACE;
+
+    clip_surface = _cairo_clip_get_surface (extents->clip, &dst->base,
+					    &clip_x, &clip_y);
+    if (unlikely (clip_surface->status))
+	goto CLEANUP_SURFACE;
+
+    if (! dst->base.is_clear) {
+#if PIXMAN_HAS_OP_LERP
+	pixman_image_composite32 (PIXMAN_OP_LERP,
+                                  tmp->pixman_image,
+                                  ((cairo_image_surface_t *) clip_surface)->pixman_image,
+                                  dst->pixman_image,
+                                  0, 0,
+                                  extents->bounded.x - clip_x,
+                                  extents->bounded.y - clip_y,
+                                  extents->bounded.x, extents->bounded.y,
+                                  extents->bounded.width, extents->bounded.height);
+#else
+	/* Punch the clip out of the destination */
+	pixman_image_composite32 (PIXMAN_OP_OUT_REVERSE,
+                                  ((cairo_image_surface_t *) clip_surface)->pixman_image,
+                                  NULL,
+				  dst->pixman_image,
+                                  extents->bounded.x - clip_x,
+                                  extents->bounded.y - clip_y,
+                                  0, 0,
+                                  extents->bounded.x, extents->bounded.y,
+                                  extents->bounded.width, extents->bounded.height);
+
+	/* Now add the two results together */
+	pixman_image_composite32 (PIXMAN_OP_ADD,
+                                  tmp->pixman_image,
+                                  ((cairo_image_surface_t *) clip_surface)->pixman_image,
+                                  dst->pixman_image,
+                                  0, 0,
+                                  extents->bounded.x - clip_x,
+                                  extents->bounded.y - clip_y,
+                                  extents->bounded.x, extents->bounded.y,
+                                  extents->bounded.width, extents->bounded.height);
+#endif
+    } else {
+	pixman_image_composite32 (PIXMAN_OP_SRC,
+                                  tmp->pixman_image,
+                                  ((cairo_image_surface_t *) clip_surface)->pixman_image,
+                                  dst->pixman_image,
+                                  0, 0,
+                                  extents->bounded.x - clip_x,
+                                  extents->bounded.y - clip_y,
+                                  extents->bounded.x, extents->bounded.y,
+                                  extents->bounded.width, extents->bounded.height);
+    }
+
+    cairo_surface_destroy (clip_surface);
+ CLEANUP_SURFACE:
+    cairo_surface_destroy (&tmp->base);
+
+    return status;
+}
+
+/* Handles compositing for %CAIRO_OPERATOR_SOURCE, which is special; it's
+ * defined as (src IN mask IN clip) ADD (dst OUT (mask IN clip))
+ */
+static cairo_status_t
+_clip_and_composite_source (cairo_composite_rectangles_t  *extents,
+			    const cairo_pattern_t         *pattern,
+			    double                         opacity,
+			    image_draw_func_t              draw_func,
+			    void                          *draw_closure,
+			    cairo_fast_image_surface_t    *dst)
+{
+    pixman_image_t *mask, *src;
+    int src_x, src_y;
+
+    if (pattern == NULL) {
+	cairo_status_t status;
+
+	status = draw_func (draw_closure, dst,
+			    CAIRO_OPERATOR_SOURCE, NULL, opacity,
+			    extents->bounded.x, extents->bounded.y,
+			    extents);
+	if (unlikely (status))
+	    return status;
+
+	if (! _cairo_clip_is_region (extents->clip))
+	    status = _cairo_clip_combine_with_surface (extents->clip,
+						       &dst->base, 0, 0);
+
+	return status;
+    }
+
+    /* Create a surface that is mask IN clip */
+    mask = _create_composite_mask_pattern (extents,
+					   draw_func, draw_closure, opacity,
+					   dst);
+    if (unlikely (mask == NULL))
+	return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    src = _pixman_image_for_pattern (pattern, FALSE, &extents->bounded,
+				     &dst->base.device_transform,
+				     &src_x, &src_y);
+    if (unlikely (src == NULL)) {
+	pixman_image_unref (mask);
+	return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+    }
+
+    if (! dst->base.is_clear) {
+#if PIXMAN_HAS_OP_LERP
+	pixman_image_composite32 (PIXMAN_OP_LERP,
+                                  src, mask, dst->pixman_image,
+                                  extents->bounded.x + src_x, extents->bounded.y + src_y,
+                                  0, 0,
+                                  extents->bounded.x,     extents->bounded.y,
+                                  extents->bounded.width, extents->bounded.height);
+#else
+	/* Compute dest' = dest OUT (mask IN clip) */
+	pixman_image_composite32 (PIXMAN_OP_OUT_REVERSE,
+                                  mask, NULL, dst->pixman_image,
+                                  0, 0, 0, 0,
+                                  extents->bounded.x,     extents->bounded.y,
+                                  extents->bounded.width, extents->bounded.height);
+
+	/* Now compute (src IN (mask IN clip)) ADD dest' */
+	pixman_image_composite32 (PIXMAN_OP_ADD,
+                                  src, mask, dst->pixman_image,
+                                  extents->bounded.x + src_x, extents->bounded.y + src_y,
+                                  0, 0,
+                                  extents->bounded.x,     extents->bounded.y,
+                                  extents->bounded.width, extents->bounded.height);
+#endif
+    } else {
+	pixman_image_composite32 (PIXMAN_OP_SRC,
+                                  src, mask, dst->pixman_image,
+                                  extents->bounded.x + src_x, extents->bounded.y + src_y,
+                                  0, 0,
+                                  extents->bounded.x,     extents->bounded.y,
+                                  extents->bounded.width, extents->bounded.height);
+    }
+
+    pixman_image_unref (src);
+    pixman_image_unref (mask);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+enum {
+    NEED_CLIP_REGION = 0x1,
+    NEED_CLIP_SURFACE = 0x2,
+    FORCE_CLIP_REGION = 0x4,
+};
+
+static cairo_bool_t
+need_bounded_clip (cairo_composite_rectangles_t *extents)
+{
+    unsigned int flags = NEED_CLIP_REGION;
+    if (! _cairo_clip_is_region (extents->clip))
+	flags |= NEED_CLIP_SURFACE;
+    return flags;
+}
+
+static cairo_bool_t
+need_unbounded_clip (cairo_composite_rectangles_t *extents)
+{
+    unsigned int flags = 0;
+    if (! extents->is_bounded) {
+	flags |= NEED_CLIP_REGION;
+	if (! _cairo_clip_is_region (extents->clip))
+	    flags |= NEED_CLIP_SURFACE;
+    }
+    if (extents->clip->path != NULL)
+	flags |= NEED_CLIP_SURFACE;
+    return flags;
+}
+
+
+static cairo_status_t
+_clip_and_composite (cairo_fast_image_surface_t	*dst,
+		     cairo_operator_t		 op,
+		     const cairo_pattern_t	*src,
+		     double			 opacity,
+		     image_draw_func_t		 draw_func,
+		     void			*draw_closure,
+		     cairo_composite_rectangles_t*extents,
+		     unsigned int need_clip)
+{
+    cairo_region_t *clip_region = NULL;
+    cairo_status_t status;
+
+    if (need_clip & NEED_CLIP_REGION) {
+	clip_region = _cairo_clip_get_region (extents->clip);
+	if ((need_clip & FORCE_CLIP_REGION) == 0 &&
+	    cairo_region_contains_rectangle (clip_region,
+					     &extents->unbounded) == CAIRO_REGION_OVERLAP_IN)
+	    clip_region = NULL;
+	if (clip_region != NULL)
+	    pixman_image_set_clip_region32 (dst->pixman_image,
+					    &clip_region->rgn);
+    }
+
+    if (reduce_alpha_op (&dst->base, op, src)) {
+	op = CAIRO_OPERATOR_ADD;
+	src = NULL;
+    }
+
+    if (op == CAIRO_OPERATOR_SOURCE) {
+	status = _clip_and_composite_source (extents, src, opacity,
+					     draw_func, draw_closure, dst);
+    } else {
+	if (op == CAIRO_OPERATOR_CLEAR) {
+	    src = NULL;
+	    op = CAIRO_OPERATOR_DEST_OUT;
+	}
+
+	if (need_clip & NEED_CLIP_SURFACE) {
+	    if (extents->is_bounded) {
+		status = _clip_and_composite_with_mask (extents, op, src, opacity,
+							draw_func, draw_closure,
+							dst);
+	    } else {
+		status = _clip_and_composite_combine (extents, op, src, opacity,
+						      draw_func, draw_closure,
+						      dst);
+	    }
+	} else {
+	    status = draw_func (draw_closure, dst, op, src, opacity,
+				0, 0, extents);
+	}
+    }
+
+    if (status == CAIRO_STATUS_SUCCESS && ! extents->is_bounded) {
+	status = fast_image_surface_fixup_unbounded (dst, extents,
+						     need_clip & NEED_CLIP_SURFACE ? extents->clip : NULL);
+    }
+
+    if (clip_region != NULL)
+	pixman_image_set_clip_region32 (dst->pixman_image, NULL);
+
+    return status;
+}
+
+static inline uint32_t
+color_to_uint32 (const cairo_color_t *color)
+{
+    return
+        (color->alpha_short >> 8 << 24) |
+        (color->red_short >> 8 << 16)   |
+        (color->green_short & 0xff00)   |
+        (color->blue_short >> 8);
+}
+
+static inline cairo_bool_t
+color_to_pixel (const cairo_color_t	*color,
+		double opacity,
+                pixman_format_code_t	 format,
+                uint32_t		*pixel)
+{
+    cairo_color_t opacity_color;
+    uint32_t c;
+
+    if (!(format == PIXMAN_a8r8g8b8     ||
+          format == PIXMAN_x8r8g8b8     ||
+          format == PIXMAN_a8b8g8r8     ||
+          format == PIXMAN_x8b8g8r8     ||
+          format == PIXMAN_b8g8r8a8     ||
+          format == PIXMAN_b8g8r8x8     ||
+          format == PIXMAN_r5g6b5       ||
+          format == PIXMAN_b5g6r5       ||
+          format == PIXMAN_a8))
+    {
+	return FALSE;
+    }
+
+    if (opacity != 1.0) {
+	_cairo_color_init_rgba (&opacity_color,
+				color->red,
+				color->green,
+				color->blue,
+				color->alpha * opacity);
+	color = &opacity_color;
+    }
+    c = color_to_uint32 (color);
+
+    if (PIXMAN_FORMAT_TYPE (format) == PIXMAN_TYPE_ABGR) {
+	c = ((c & 0xff000000) >>  0) |
+	    ((c & 0x00ff0000) >> 16) |
+	    ((c & 0x0000ff00) >>  0) |
+	    ((c & 0x000000ff) << 16);
+    }
+
+    if (PIXMAN_FORMAT_TYPE (format) == PIXMAN_TYPE_BGRA) {
+	c = ((c & 0xff000000) >> 24) |
+	    ((c & 0x00ff0000) >>  8) |
+	    ((c & 0x0000ff00) <<  8) |
+	    ((c & 0x000000ff) << 24);
+    }
+
+    if (format == PIXMAN_a8) {
+	c = c >> 24;
+    } else if (format == PIXMAN_r5g6b5 || format == PIXMAN_b5g6r5) {
+	c = ((((c) >> 3) & 0x001f) |
+	     (((c) >> 5) & 0x07e0) |
+	     (((c) >> 8) & 0xf800));
+    }
+
+    *pixel = c;
+    return TRUE;
+}
+
+static inline cairo_bool_t
+pattern_to_pixel (const cairo_solid_pattern_t *solid,
+		  double opacity,
+		  cairo_operator_t op,
+		  pixman_format_code_t format,
+		  uint32_t *pixel)
+{
+    if (op == CAIRO_OPERATOR_CLEAR) {
+	*pixel = 0;
+	return TRUE;
+    }
+
+    if (solid->base.type != CAIRO_PATTERN_TYPE_SOLID)
+	return FALSE;
+
+    if (op == CAIRO_OPERATOR_OVER) {
+	if (opacity * solid->color.alpha_short >= 0xff00)
+	    op = CAIRO_OPERATOR_SOURCE;
+    }
+
+    if (op != CAIRO_OPERATOR_SOURCE)
+	return FALSE;
+
+    return color_to_pixel (&solid->color, opacity, format, pixel);
+}
+
+typedef struct _cairo_fast_image_span_renderer {
+    cairo_span_renderer_t base;
+    pixman_image_compositor_t *compositor;
+    pixman_image_t *src;
+    float opacity;
+    cairo_rectangle_int_t extents;
+} fast_image_span_renderer_t;
+
+static cairo_status_t
+fast_image_bounded_opaque_spans (void *abstract_renderer,
+				 int y, int height,
+				 const cairo_half_open_span_t *spans,
+				 unsigned num_spans)
+{
+    fast_image_span_renderer_t *r = abstract_renderer;
+
+    if (num_spans == 0)
+	return CAIRO_STATUS_SUCCESS;
+
+    do {
+	if (spans[0].coverage)
+	    pixman_image_compositor_blt (r->compositor,
+					 spans[0].x, y,
+					 spans[1].x - spans[0].x, height,
+					 spans[0].coverage);
+	spans++;
+    } while (--num_spans > 1);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+fast_image_bounded_spans (void *abstract_renderer,
+			  int y, int height,
+			  const cairo_half_open_span_t *spans,
+			  unsigned num_spans)
+{
+    fast_image_span_renderer_t *r = abstract_renderer;
+
+    if (num_spans == 0)
+	return CAIRO_STATUS_SUCCESS;
+
+    do {
+	if (spans[0].coverage) {
+	    pixman_image_compositor_blt (r->compositor,
+					 spans[0].x, y,
+					 spans[1].x - spans[0].x, height,
+					  r->opacity * spans[0].coverage);
+	}
+	spans++;
+    } while (--num_spans > 1);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+fast_image_unbounded_spans (void *abstract_renderer,
+			    int y, int height,
+			    const cairo_half_open_span_t *spans,
+			    unsigned num_spans)
+{
+    fast_image_span_renderer_t *r = abstract_renderer;
+
+    if (y > r->extents.y) {
+	pixman_image_compositor_blt (r->compositor,
+				     r->extents.x, r->extents.y,
+				     r->extents.width, y - r->extents.y,
+				     0);
+    }
+
+    if (num_spans == 0) {
+	pixman_image_compositor_blt (r->compositor,
+				     r->extents.x, y,
+				     r->extents.width,  height,
+				     0);
+    } else {
+	if (spans[0].x != r->extents.x) {
+	    pixman_image_compositor_blt (r->compositor,
+					 r->extents.x, y,
+					 spans[0].x - r->extents.x,
+					 height,
+					 0);
+	}
+
+	do {
+	    pixman_image_compositor_blt (r->compositor,
+					 spans[0].x, y,
+					 spans[1].x - spans[0].x, height,
+					 r->opacity * spans[0].coverage);
+	    spans++;
+	} while (--num_spans > 1);
+
+	if (spans[0].x != r->extents.x + r->extents.width) {
+	    pixman_image_compositor_blt (r->compositor,
+					 spans[0].x,     y,
+					 r->extents.x + r->extents.width - spans[0].x, height,
+					 0);
+	}
+    }
+
+    r->extents.y = y + height;
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+fast_image_clipped_spans (void *abstract_renderer,
+			  int y, int height,
+			  const cairo_half_open_span_t *spans,
+			  unsigned num_spans)
+{
+    fast_image_span_renderer_t *r = abstract_renderer;
+
+    assert (num_spans);
+
+    do {
+	if (! spans[0].is_clipped)
+	    pixman_image_compositor_blt (r->compositor,
+					 spans[0].x, y,
+					 spans[1].x - spans[0].x, height,
+					 r->opacity * spans[0].coverage);
+	spans++;
+    } while (--num_spans > 1);
+
+    r->extents.y = y + height;
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+fast_image_finish_unbounded_spans (void *abstract_renderer)
+{
+    fast_image_span_renderer_t *r = abstract_renderer;
+
+    if (r->extents.y < r->extents.height) {
+	pixman_image_compositor_blt (r->compositor,
+				     r->extents.x, r->extents.y,
+				     r->extents.width,
+				     r->extents.height - r->extents.y,
+				     0);
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+fast_image_span_renderer_init (fast_image_span_renderer_t *r,
+			       cairo_fast_image_surface_t *dst,
+			       cairo_operator_t op,
+			       const cairo_pattern_t *pattern,
+			       float opacity,
+			       const cairo_composite_rectangles_t *composite,
+			       cairo_bool_t needs_clip)
+{
+    int src_x, src_y;
+
+    if (op == CAIRO_OPERATOR_CLEAR) {
+	op = CAIRO_OPERATOR_DEST_OUT;
+	pattern = NULL;
+    }
+
+    r->src = _pixman_image_for_pattern (pattern, FALSE,
+					&composite->bounded,
+					&dst->base.device_transform,
+					&src_x, &src_y);
+    if (unlikely (r->src == NULL))
+	return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    if (composite->is_bounded) {
+	if (opacity == 1.)
+	    r->base.render_rows = fast_image_bounded_opaque_spans;
+	else
+	    r->base.render_rows = fast_image_bounded_spans;
+	r->base.finish = NULL;
+    } else {
+	if (needs_clip)
+	    r->base.render_rows = fast_image_clipped_spans;
+	else
+	    r->base.render_rows = fast_image_unbounded_spans;
+        r->base.finish =      fast_image_finish_unbounded_spans;
+	r->extents = composite->unbounded;
+	r->extents.height += r->extents.y;
+
+    }
+    r->compositor =
+	pixman_image_create_compositor (_pixman_operator (op),
+					r->src, NULL, dst->pixman_image,
+					composite->bounded.x + src_x,
+					composite->bounded.y + src_y,
+					0, 0,
+					composite->bounded.x,
+					composite->bounded.y,
+					composite->bounded.width,
+					composite->bounded.height);
+    r->opacity = opacity;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_image_span_renderer_fini (fast_image_span_renderer_t *r)
+{
+    if (r->base.finish)
+	r->base.finish (r);
+
+    pixman_image_compositor_destroy (r->compositor);
+    pixman_image_unref (r->src);
+}
+
+static cairo_status_t
+_composite_unaligned_boxes (cairo_fast_image_surface_t *dst,
+			    cairo_operator_t op,
+			    const cairo_pattern_t *pattern,
+			    double opacity,
+			    const cairo_boxes_t *boxes,
+			    const cairo_composite_rectangles_t *extents)
+{
+    fast_image_span_renderer_t renderer;
+    cairo_rectangular_scan_converter_t converter;
+    const struct _cairo_boxes_chunk *chunk;
+    cairo_status_t status;
+    int i;
+
+    status = fast_image_span_renderer_init (&renderer,
+					    dst, op, pattern, opacity,
+					    extents, FALSE);
+    if (unlikely (status))
+	return status;
+
+    assert (! extents->clip->path);
+    _cairo_rectangular_scan_converter_init (&converter, &extents->bounded);
+
+    for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) {
+	const cairo_box_t *box = chunk->base;
+
+	for (i = 0; i < chunk->count; i++) {
+	    status = _cairo_rectangular_scan_converter_add_box (&converter, &box[i], 1);
+	    if (unlikely (status))
+		goto CLEANUP;
+	}
+    }
+
+    status = converter.base.generate (&converter.base, &renderer.base);
+
+CLEANUP:
+    _cairo_image_span_renderer_fini (&renderer);
+    converter.base.destroy (&converter.base);
+    return status;
+}
+
+static cairo_bool_t
+is_recording_pattern (const cairo_pattern_t *pattern)
+{
+    cairo_surface_t *surface;
+
+    if (pattern->type != CAIRO_PATTERN_TYPE_SURFACE)
+	return FALSE;
+
+    surface = ((const cairo_surface_pattern_t *) pattern)->surface;
+    if (_cairo_surface_is_paginated (surface))
+	surface = _cairo_paginated_surface_get_recording (surface);
+    return _cairo_surface_is_recording (surface);
+}
+
+static cairo_surface_t *
+recording_pattern_get_surface (const cairo_pattern_t *pattern)
+{
+    cairo_surface_t *surface;
+
+    surface = ((const cairo_surface_pattern_t *) pattern)->surface;
+    if (_cairo_surface_is_paginated (surface))
+	surface = _cairo_paginated_surface_get_recording (surface);
+    return surface;
+}
+
+static cairo_bool_t
+op_reduces_to_source (cairo_operator_t op,
+		      cairo_surface_t *dst)
+{
+    if (op == CAIRO_OPERATOR_SOURCE)
+	return TRUE;
+
+    if (dst->is_clear)
+	return op == CAIRO_OPERATOR_OVER || op == CAIRO_OPERATOR_ADD;
+
+    return FALSE;
+}
+
+static cairo_bool_t
+recording_pattern_contains_sample (const cairo_pattern_t *pattern,
+				   const cairo_rectangle_int_t *extents)
+{
+    cairo_rectangle_int_t area;
+    cairo_recording_surface_t *surface;
+
+    if (! is_recording_pattern (pattern))
+	return FALSE;
+
+    if (pattern->extend == CAIRO_EXTEND_NONE)
+	return TRUE;
+
+    surface = (cairo_recording_surface_t *) recording_pattern_get_surface (pattern);
+    if (surface->unbounded)
+	return TRUE;
+
+    _cairo_surface_pattern_sampled_area ((cairo_surface_pattern_t *) pattern,
+					 extents, &area);
+    if (area.x >= surface->extents.x &&
+	area.y >= surface->extents.y &&
+	area.x + area.width <= surface->extents.x + surface->extents.width &&
+	area.y + area.height <= surface->extents.y + surface->extents.height)
+    {
+	return TRUE;
+    }
+
+    return FALSE;
+}
+
+static cairo_status_t
+_composite_boxes (cairo_fast_image_surface_t *dst,
+		  cairo_operator_t op,
+		  const cairo_pattern_t *pattern,
+		  double opacity,
+		  cairo_boxes_t *boxes,
+		  const cairo_composite_rectangles_t *extents)
+{
+    cairo_region_t *clip_region = _cairo_clip_get_region (extents->clip);
+    cairo_bool_t need_clip_mask = extents->clip->path != NULL;
+    cairo_status_t status;
+    struct _cairo_boxes_chunk *chunk;
+    uint32_t pixel;
+    int i;
+
+    if (need_clip_mask && opacity != 1.)
+	return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    if (need_clip_mask &&
+	(op == CAIRO_OPERATOR_SOURCE || ! extents->is_bounded))
+    {
+	return CAIRO_INT_STATUS_UNSUPPORTED;
+    }
+
+    if (clip_region != NULL && cairo_region_num_rectangles (clip_region) == 1)
+	clip_region = NULL;
+
+    if (! boxes->is_pixel_aligned) {
+	if (need_clip_mask)
+	    return CAIRO_INT_STATUS_UNSUPPORTED;
+
+	return _composite_unaligned_boxes (dst, op, pattern, opacity, boxes, extents);
+    }
+
+    /* Are we just copying a recording surface? */
+    if (! need_clip_mask && opacity == 1. &&
+	op_reduces_to_source (op, &dst->base) &&
+	recording_pattern_contains_sample (pattern, &extents->bounded))
+    {
+	cairo_clip_t *recording_clip;
+	cairo_bool_t was_clear = dst->base.is_clear;
+
+	/* XXX could also do tiling repeat modes... */
+
+	/* first clear the area about to be overwritten */
+	if (! dst->base.is_clear) {
+	    for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) {
+		cairo_box_t *box = chunk->base;
+
+		for (i = 0; i < chunk->count; i++) {
+		    int x1 = _cairo_fixed_integer_part (box[i].p1.x);
+		    int y1 = _cairo_fixed_integer_part (box[i].p1.y);
+		    int x2 = _cairo_fixed_integer_part (box[i].p2.x);
+		    int y2 = _cairo_fixed_integer_part (box[i].p2.y);
+
+		    if (x2 == x1 || y2 == y1)
+			continue;
+
+		    pixman_fill (pixman_image_get_data (dst->pixman_image),
+				 pixman_image_get_stride (dst->pixman_image) / sizeof (uint32_t),
+				 PIXMAN_FORMAT_BPP (dst->pixman_format),
+				 x1, y1, x2 - x1, y2 - y1,
+				 0);
+		}
+	    }
+
+	    dst->base.is_clear = TRUE;
+	}
+
+	recording_clip = _cairo_clip_from_boxes (boxes);
+	status = _cairo_recording_surface_replay_with_clip (recording_pattern_get_surface (pattern),
+							    &pattern->matrix,
+							    &dst->base,
+							    recording_clip);
+	_cairo_clip_destroy (recording_clip);
+	dst->base.is_clear &= was_clear;
+
+	return status;
+    }
+
+    status = CAIRO_STATUS_SUCCESS;
+    if (! need_clip_mask &&
+	pattern_to_pixel ((cairo_solid_pattern_t *) pattern, opacity, op,
+			  dst->pixman_format,
+			  &pixel))
+    {
+	for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) {
+	    cairo_box_t *box = chunk->base;
+
+	    for (i = 0; i < chunk->count; i++) {
+		int x1 = _cairo_fixed_integer_part (box[i].p1.x);
+		int y1 = _cairo_fixed_integer_part (box[i].p1.y);
+		int x2 = _cairo_fixed_integer_part (box[i].p2.x);
+		int y2 = _cairo_fixed_integer_part (box[i].p2.y);
+
+		if (x2 == x1 || y2 == y1)
+		    continue;
+
+		pixman_fill (pixman_image_get_data (dst->pixman_image),
+			     pixman_image_get_stride (dst->pixman_image) / sizeof (uint32_t),
+			     PIXMAN_FORMAT_BPP (dst->pixman_format),
+			     x1, y1, x2 - x1, y2 - y1,
+			     pixel);
+	    }
+	}
+    }
+    else
+    {
+	cairo_surface_t *clip_surface = NULL;
+	pixman_image_t *src = NULL, *mask = NULL;
+	int src_x, src_y, mask_x = 0, mask_y = 0;
+	pixman_op_t pixman_op = _pixman_operator (op);
+
+	if (need_clip_mask) {
+	    int clip_x, clip_y;
+
+	    clip_surface = _cairo_clip_get_surface (extents->clip,
+						    &dst->base,
+						    &clip_x, &clip_y);
+	    if (unlikely (clip_surface->status))
+		return clip_surface->status;
+
+	    mask_x = -clip_x;
+	    mask_y = -clip_y;
+
+	    if (op == CAIRO_OPERATOR_CLEAR) {
+		pattern = NULL;
+		pixman_op = PIXMAN_OP_OUT_REVERSE;
+	    }
+
+	    mask = ((cairo_image_surface_t *) clip_surface)->pixman_image;
+	}
+
+	if (pattern != NULL) {
+	    src = _pixman_image_for_pattern (pattern, FALSE, &extents->bounded,
+					     &dst->base.device_transform,
+					     &src_x, &src_y);
+	    if (unlikely (src == NULL)) {
+		cairo_surface_destroy (clip_surface);
+		return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+	    }
+	} else {
+	    src = mask;
+	    src_x = mask_x;
+	    src_y = mask_y;
+	    mask = NULL;
+	}
+
+	for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) {
+	    const cairo_box_t *box = chunk->base;
+
+	    for (i = 0; i < chunk->count; i++) {
+		int x1 = _cairo_fixed_integer_part (box[i].p1.x);
+		int y1 = _cairo_fixed_integer_part (box[i].p1.y);
+		int x2 = _cairo_fixed_integer_part (box[i].p2.x);
+		int y2 = _cairo_fixed_integer_part (box[i].p2.y);
+
+		if (x2 == x1 || y2 == y1)
+		    continue;
+
+		pixman_image_composite32 (pixman_op,
+                                          src, mask, dst->pixman_image,
+                                          x1 + src_x,  y1 + src_y,
+                                          x1 + mask_x, y1 + mask_y,
+                                          x1, y1,
+                                          x2 - x1, y2 - y1);
+	    }
+	}
+
+	cairo_surface_destroy (clip_surface);
+	if (pattern != NULL)
+	    pixman_image_unref (src);
+
+	if (! extents->is_bounded) {
+	    status =
+		fast_image_surface_fixup_unbounded_boxes (dst, extents,
+							  clip_region, boxes);
+	}
+    }
+
+    return status;
+}
+
+static cairo_status_t
+_clip_and_composite_polygon (cairo_fast_image_surface_t *dst,
+			     cairo_operator_t op,
+			     const cairo_pattern_t *src,
+			     double opacity,
+			     cairo_polygon_t *polygon,
+			     cairo_fill_rule_t fill_rule,
+			     cairo_antialias_t antialias,
+			     cairo_composite_rectangles_t *extents);
+static cairo_status_t
+_composite_boxes_fallback (void			*closure,
+			   cairo_fast_image_surface_t   *dst,
+			   cairo_operator_t		 op,
+			   const cairo_pattern_t	*pattern,
+			   double                       opacity,
+			   int				 dst_x,
+			   int				 dst_y,
+			   const cairo_composite_rectangles_t	*extents)
+{
+    cairo_boxes_t *boxes = closure;
+    fast_image_span_renderer_t renderer;
+    cairo_rectangular_scan_converter_t converter;
+    const struct _cairo_boxes_chunk *chunk;
+    cairo_status_t status;
+    cairo_fixed_t fx = _cairo_fixed_from_int (-dst_x);
+    cairo_fixed_t fy = _cairo_fixed_from_int (-dst_y);
+    int i;
+
+    assert (! boxes->is_pixel_aligned);
+
+    status = fast_image_span_renderer_init (&renderer,
+					    dst, op, pattern, opacity,
+					    extents, FALSE);
+    if (unlikely (status))
+	return status;
+
+    _cairo_rectangular_scan_converter_init (&converter, &extents->bounded);
+    for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) {
+	const cairo_box_t *box = chunk->base;
+
+	for (i = 0; i < chunk->count; i++) {
+	    cairo_box_t b;
+
+	    b.p1.x = box->p1.x + fx;
+	    b.p1.y = box->p1.y + fy;
+	    b.p2.x = box->p2.x + fx;
+	    b.p2.y = box->p2.y + fy;
+
+	    status = _cairo_rectangular_scan_converter_add_box (&converter,
+								&b, 1);
+	    if (unlikely (status))
+		goto CLEANUP;
+	}
+    }
+
+    status = converter.base.generate (&converter.base, &renderer.base);
+
+CLEANUP:
+    _cairo_image_span_renderer_fini (&renderer);
+    converter.base.destroy (&converter.base);
+
+    return status;
+}
+
+static cairo_status_t
+_clip_and_composite_boxes (cairo_fast_image_surface_t *dst,
+			   cairo_operator_t op,
+			   const cairo_pattern_t *src,
+			   double opacity,
+			   cairo_boxes_t *boxes,
+			   cairo_composite_rectangles_t *extents)
+{
+    cairo_int_status_t status;
+
+    if (boxes->num_boxes == 0 && extents->is_bounded)
+	return CAIRO_STATUS_SUCCESS;
+
+    /* Can we reduce drawing through a clip-mask to simply drawing the clip? */
+    if (extents->clip->path != NULL && extents->is_bounded) {
+	cairo_polygon_t polygon;
+	cairo_fill_rule_t fill_rule;
+	cairo_antialias_t antialias;
+	cairo_clip_t *clip;
+
+	clip = _cairo_clip_copy (extents->clip);
+	clip = _cairo_clip_intersect_boxes (clip, boxes);
+	status = _cairo_clip_get_polygon (clip, &polygon,
+					  &fill_rule, &antialias);
+	_cairo_clip_path_destroy (clip->path);
+	clip->path = NULL;
+	if (likely (status == CAIRO_INT_STATUS_SUCCESS)) {
+	    cairo_clip_t *saved_clip = extents->clip;
+	    extents->clip = clip;
+	    status = _clip_and_composite_polygon (dst, op, src, opacity,
+						  &polygon,
+						  fill_rule,
+						  antialias,
+						  extents);
+	    if (extents->clip != clip)
+		clip = NULL;
+	    extents->clip = saved_clip;
+	    _cairo_polygon_fini (&polygon);
+	}
+	if (clip)
+	    _cairo_clip_destroy (clip);
+
+	if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+	    return status;
+    }
+
+    /* Use a fast path if the boxes are pixel aligned */
+    status = _composite_boxes (dst, op, src, opacity, boxes, extents);
+    if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+	return status;
+
+    /* Otherwise render via a mask and composite in the usual fashion.  */
+    return _clip_and_composite (dst, op, src, opacity,
+				_composite_boxes_fallback, boxes,
+				extents, need_unbounded_clip (extents));
+}
+
+/* high level image interface */
+static cairo_bool_t
+_cairo_fast_image_surface_get_extents (void			  *abstract_surface,
+				       cairo_rectangle_int_t   *rectangle)
+{
+    cairo_fast_image_surface_t *surface = abstract_surface;
+
+    rectangle->x = 0;
+    rectangle->y = 0;
+    rectangle->width  = surface->width;
+    rectangle->height = surface->height;
+
+    return TRUE;
+}
+
+static cairo_int_status_t
+_cairo_fast_image_surface_paint (void			*abstract_surface,
+				 cairo_operator_t		 op,
+				 const cairo_pattern_t	*source,
+				 const cairo_clip_t		*clip)
+{
+    cairo_fast_image_surface_t *surface = abstract_surface;
+    cairo_rectangle_int_t unbounded;
+    cairo_composite_rectangles_t composite;
+    cairo_status_t status;
+    cairo_boxes_t boxes;
+
+    _cairo_fast_image_surface_get_extents (surface, &unbounded);
+    status = _cairo_composite_rectangles_init_for_paint (&composite, &unbounded,
+							 op, source,
+							 clip);
+    if (unlikely (status))
+	return status;
+
+    status = _cairo_clip_to_boxes (composite.clip, &boxes);
+    if (likely (status == CAIRO_STATUS_SUCCESS)) {
+	status = _clip_and_composite_boxes (surface, op, source, 1.,
+					    &boxes, &composite);
+	_cairo_boxes_fini (&boxes);
+    }
+
+    _cairo_composite_rectangles_fini (&composite);
+
+    return status;
+}
+
+static cairo_status_t
+_composite_mask (void				*closure,
+		 cairo_fast_image_surface_t     *dst,
+		 cairo_operator_t		 op,
+		 const cairo_pattern_t		*src_pattern,
+		 double                          opacity,
+		 int				 dst_x,
+		 int				 dst_y,
+		 const cairo_composite_rectangles_t	*extents)
+{
+    const cairo_pattern_t *mask_pattern = closure;
+    pixman_image_t *src, *mask = NULL;
+    int src_x = 0, src_y = 0;
+    int mask_x = 0, mask_y = 0;
+
+    assert (opacity == 1.);
+
+    if (src_pattern != NULL) {
+	src = _pixman_image_for_pattern (src_pattern, FALSE, &extents->bounded,
+					 &dst->base.device_transform,
+					 &src_x, &src_y);
+	if (unlikely (src == NULL))
+	    return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+	mask = _pixman_image_for_pattern (mask_pattern, TRUE, &extents->bounded,
+					  &dst->base.device_transform,
+					  &mask_x, &mask_y);
+	if (unlikely (mask == NULL)) {
+	    pixman_image_unref (src);
+	    return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+	}
+
+	if (mask_pattern->has_component_alpha)
+	    pixman_image_set_component_alpha (mask, TRUE);
+    } else {
+	src = _pixman_image_for_pattern (mask_pattern, FALSE, &extents->bounded,
+					 &dst->base.device_transform,
+					 &src_x, &src_y);
+	if (unlikely (src == NULL))
+	    return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+    }
+
+    pixman_image_composite32 (_pixman_operator (op), src, mask, dst->pixman_image,
+                              extents->bounded.x + src_x,  extents->bounded.y + src_y,
+                              extents->bounded.x + mask_x, extents->bounded.y + mask_y,
+                              extents->bounded.x - dst_x,  extents->bounded.y - dst_y,
+                              extents->bounded.width,      extents->bounded.height);
+
+    if (mask != NULL)
+	pixman_image_unref (mask);
+    pixman_image_unref (src);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_fast_image_surface_mask (void				*abstract_surface,
+				cairo_operator_t		 op,
+				const cairo_pattern_t	*source,
+				const cairo_pattern_t	*mask,
+				const cairo_clip_t		*clip)
+{
+    cairo_fast_image_surface_t *surface = abstract_surface;
+    cairo_composite_rectangles_t extents;
+    cairo_rectangle_int_t unbounded;
+    cairo_int_status_t status;
+
+    _cairo_fast_image_surface_get_extents (surface, &unbounded);
+    status = _cairo_composite_rectangles_init_for_mask (&extents, &unbounded,
+							op, source, mask, clip);
+    if (unlikely (status))
+	return status;
+
+    status = CAIRO_INT_STATUS_UNSUPPORTED;
+    if (mask->type == CAIRO_PATTERN_TYPE_SOLID) {
+	cairo_boxes_t boxes;
+	double opacity = ((cairo_solid_pattern_t *)mask)->color.alpha;
+	status = _cairo_clip_to_boxes (extents.clip, &boxes);
+	if (likely (status == CAIRO_INT_STATUS_SUCCESS)) {
+	    status = _clip_and_composite_boxes (surface,
+						op, source,
+						opacity,
+						&boxes, &extents);
+	    _cairo_boxes_fini (&boxes);
+	}
+    }
+    if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
+	status = _clip_and_composite (surface, op, source, 1.,
+				      _composite_mask, (void *) mask,
+				      &extents, need_bounded_clip (&extents));
+    }
+
+    _cairo_composite_rectangles_fini (&extents);
+
+    return status;
+}
+
+typedef struct {
+    cairo_polygon_t		*polygon;
+    cairo_fill_rule_t		 fill_rule;
+    cairo_antialias_t		 antialias;
+} composite_spans_info_t;
+
+static cairo_status_t
+_composite_spans (void                          *closure,
+		  cairo_fast_image_surface_t    *dst,
+		  cairo_operator_t               op,
+		  const cairo_pattern_t         *pattern,
+		  double                         opacity,
+		  int                            dst_x,
+		  int                            dst_y,
+		  const cairo_composite_rectangles_t   *extents)
+{
+    composite_spans_info_t *info = closure;
+    fast_image_span_renderer_t renderer;
+    cairo_scan_converter_t *converter;
+    const cairo_rectangle_int_t *r = &extents->bounded;
+    cairo_status_t status;
+
+    status = fast_image_span_renderer_init (&renderer,
+					    dst, op, pattern, opacity,
+					    extents, FALSE);
+    if (unlikely (status))
+	return status;
+
+    converter = _cairo_tor_scan_converter_create (r->x, r->y,
+						  r->x + r->width,
+						  r->y + r->height,
+						  info->fill_rule);
+
+    status= _cairo_tor_scan_converter_add_polygon (converter,
+						   info->polygon);
+    if (unlikely (status))
+	goto CLEANUP_CONVERTER;
+
+    status = converter->generate (converter, &renderer.base);
+
+ CLEANUP_CONVERTER:
+    _cairo_image_span_renderer_fini (&renderer);
+    converter->destroy (converter);
+    return status;
+}
+
+static cairo_status_t
+_composite_polygon (cairo_fast_image_surface_t *dst,
+		    cairo_operator_t op,
+		    const cairo_pattern_t *pattern,
+		    double opacity,
+		    cairo_polygon_t *polygon,
+		    cairo_fill_rule_t fill_rule,
+		    cairo_antialias_t antialias,
+		    cairo_composite_rectangles_t *extents)
+{
+    fast_image_span_renderer_t renderer;
+    cairo_scan_converter_t *converter;
+    const cairo_rectangle_int_t *r = &extents->bounded;
+    cairo_bool_t needs_clip = !extents->is_bounded && extents->clip->path;
+    cairo_int_status_t status;
+
+    if (antialias == CAIRO_ANTIALIAS_NONE)
+	return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    status = fast_image_span_renderer_init (&renderer,
+					    dst, op, pattern, opacity,
+					    extents, needs_clip);
+    if (unlikely (status))
+	return status;
+
+    if (needs_clip) {
+	printf ("awooga!\n");
+	converter = _cairo_clip_tor_scan_converter_create (extents->clip,
+							   polygon,
+							   fill_rule);
+    } else {
+	converter = _cairo_tor_scan_converter_create (r->x, r->y,
+						      r->x + r->width,
+						      r->y + r->height,
+						      fill_rule);
+	status = converter->add_polygon (converter, polygon);
+	if (unlikely (status))
+	    goto CLEANUP_CONVERTER;
+    }
+
+    status = converter->generate (converter, &renderer.base);
+
+ CLEANUP_CONVERTER:
+    _cairo_image_span_renderer_fini (&renderer);
+    converter->destroy (converter);
+    return status;
+}
+
+static cairo_status_t
+_clip_and_composite_polygon (cairo_fast_image_surface_t *dst,
+			     cairo_operator_t op,
+			     const cairo_pattern_t *src,
+			     double opacity,
+			     cairo_polygon_t *polygon,
+			     cairo_fill_rule_t fill_rule,
+			     cairo_antialias_t antialias,
+			     cairo_composite_rectangles_t *extents)
+{
+    cairo_int_status_t status;
+    composite_spans_info_t info;
+
+    if (_cairo_polygon_is_empty (polygon)) {
+	cairo_boxes_t boxes;
+
+	if (extents->is_bounded)
+	    return CAIRO_STATUS_SUCCESS;
+
+	status = _cairo_clip_to_boxes (extents->clip, &boxes);
+	if (likely (status == CAIRO_INT_STATUS_SUCCESS)) {
+	    extents->is_bounded = _cairo_operator_bounded_by_either (op);
+	    extents->mask = extents->bounded = extents->unbounded;
+	    status = _clip_and_composite_boxes (dst,
+						CAIRO_OPERATOR_CLEAR,
+						&_cairo_pattern_clear.base,
+						opacity,
+						&boxes, extents);
+	    _cairo_boxes_fini (&boxes);
+	}
+
+	return status;
+    }
+
+    if (extents->is_bounded &&
+	(extents->clip->num_boxes > 1 || extents->clip->path)) {
+	cairo_polygon_t clipper;
+	cairo_antialias_t clip_antialias;
+	cairo_fill_rule_t clip_fill_rule;
+
+	status = _cairo_clip_get_polygon (extents->clip,
+					  &clipper,
+					  &clip_fill_rule,
+					  &clip_antialias);
+	if (likely (status == CAIRO_INT_STATUS_SUCCESS)) {
+	    cairo_clip_t *old_clip;
+
+	    status = _cairo_polygon_intersect (polygon, fill_rule,
+					       &clipper, fill_rule);
+	    _cairo_polygon_fini (&clipper);
+	    if (unlikely (status))
+		return status;
+
+	    old_clip = extents->clip;
+	    extents->clip = _cairo_clip_copy_region (extents->clip);
+	    _cairo_clip_destroy (old_clip);
+	}
+    }
+
+    status = _cairo_composite_rectangles_intersect_mask_extents (extents,
+								 &polygon->extents);
+    if (unlikely (status))
+	return status;
+
+    status = _composite_polygon (dst, op, src, opacity,
+				 polygon, fill_rule, antialias,
+				 extents);
+    if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+	return status;
+
+
+    info.polygon = polygon;
+    info.fill_rule = fill_rule;
+    info.antialias = antialias;
+
+    return _clip_and_composite (dst, op, src, opacity,
+				_composite_spans, &info,
+				extents, need_bounded_clip (extents));
+}
+
+static cairo_int_status_t
+_cairo_fast_image_surface_stroke (void			*abstract_surface,
+				  cairo_operator_t		 op,
+				  const cairo_pattern_t	*source,
+				  const cairo_path_fixed_t	*path,
+				  const cairo_stroke_style_t	*style,
+				  const cairo_matrix_t	*ctm,
+				  const cairo_matrix_t	*ctm_inverse,
+				  double			 tolerance,
+				  cairo_antialias_t		 antialias,
+				  const cairo_clip_t		*clip)
+{
+    cairo_fast_image_surface_t *surface = abstract_surface;
+    cairo_composite_rectangles_t composite;
+    cairo_rectangle_int_t unbounded;
+    cairo_int_status_t status;
+
+    _cairo_fast_image_surface_get_extents (surface, &unbounded);
+    status = _cairo_composite_rectangles_init_for_stroke (&composite, &unbounded,
+							  op, source,
+							  path, style, ctm,
+							  clip);
+    if (unlikely (status))
+	return status;
+
+    status = CAIRO_INT_STATUS_UNSUPPORTED;
+    if (_cairo_path_fixed_stroke_is_rectilinear (path)) {
+	cairo_boxes_t boxes;
+
+	_cairo_boxes_init_with_clip (&boxes, composite.clip);
+	status = _cairo_path_fixed_stroke_rectilinear_to_boxes (path,
+								style,
+								ctm,
+								antialias,
+								&boxes);
+	if (likely (status == CAIRO_INT_STATUS_SUCCESS)) {
+	    status = _clip_and_composite_boxes (surface, op, source, 1.,
+						&boxes, &composite);
+	}
+	_cairo_boxes_fini (&boxes);
+    }
+
+    if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
+	cairo_polygon_t polygon;
+	cairo_box_t extents;
+
+	_cairo_box_from_rectangle (&extents, &composite.clip->extents);
+	_cairo_polygon_init (&polygon, &extents, 1);
+	status = _cairo_path_fixed_stroke_to_polygon (path,
+						      style,
+						      ctm, ctm_inverse,
+						      tolerance,
+						      &polygon);
+	if (likely (status == CAIRO_INT_STATUS_SUCCESS)) {
+	    status = _clip_and_composite_polygon (surface, op, source, 1.,
+						  &polygon,
+						  CAIRO_FILL_RULE_WINDING,
+						  antialias,
+						  &composite);
+	}
+	_cairo_polygon_fini (&polygon);
+    }
+
+    _cairo_composite_rectangles_fini (&composite);
+
+    return status;
+}
+
+static cairo_int_status_t
+_cairo_fast_image_surface_fill (void				*abstract_surface,
+				cairo_operator_t		 op,
+				const cairo_pattern_t	*source,
+				const cairo_path_fixed_t	*path,
+				cairo_fill_rule_t		 fill_rule,
+				double			 tolerance,
+				cairo_antialias_t		 antialias,
+				const cairo_clip_t		*clip)
+{
+    cairo_fast_image_surface_t *surface = abstract_surface;
+    cairo_composite_rectangles_t composite;
+    cairo_rectangle_int_t unbounded;
+    cairo_status_t status;
+
+    _cairo_fast_image_surface_get_extents (surface, &unbounded);
+    status = _cairo_composite_rectangles_init_for_fill (&composite, &unbounded,
+							op, source, path,
+							clip);
+    if (unlikely (status))
+	return status;
+
+    if (_cairo_path_fixed_fill_is_rectilinear (path)) {
+	cairo_boxes_t boxes;
+
+	_cairo_boxes_init_with_clip (&boxes, composite.clip);
+	status = _cairo_path_fixed_fill_rectilinear_to_boxes (path,
+							      fill_rule,
+							      antialias,
+							      &boxes);
+	if (likely (status == CAIRO_STATUS_SUCCESS)) {
+	    status = _clip_and_composite_boxes (surface, op, source, 1.,
+						&boxes, &composite);
+	}
+	_cairo_boxes_fini (&boxes);
+    } else {
+	cairo_polygon_t polygon;
+	cairo_box_t extents;
+
+	assert (! _cairo_path_fixed_fill_is_empty (path));
+
+	_cairo_box_from_rectangle (&extents, &composite.clip->extents);
+	_cairo_polygon_init (&polygon, &extents, 1);
+	status = _cairo_path_fixed_fill_to_polygon (path, tolerance, &polygon);
+	if (likely (status == CAIRO_STATUS_SUCCESS)) {
+	    status = _clip_and_composite_polygon (surface, op, source, 1.,
+						  &polygon,
+						  fill_rule, antialias,
+						  &composite);
+	}
+	_cairo_polygon_fini (&polygon);
+    }
+
+    _cairo_composite_rectangles_fini (&composite);
+
+    return status;
+}
+
+typedef struct {
+    cairo_scaled_font_t *font;
+    cairo_glyph_t *glyphs;
+    int num_glyphs;
+} composite_glyphs_info_t;
+
+static cairo_status_t
+_composite_glyphs_via_mask (void			*closure,
+			    cairo_fast_image_surface_t  *dst,
+			    cairo_operator_t		 op,
+			    const cairo_pattern_t	*pattern,
+			    double                       opacity,
+			    int				 dst_x,
+			    int				 dst_y,
+			    const cairo_composite_rectangles_t	*extents)
+{
+    composite_glyphs_info_t *info = closure;
+    cairo_scaled_font_t *font = info->font;
+    cairo_glyph_t *glyphs = info->glyphs;
+    int num_glyphs = info->num_glyphs;
+    pixman_image_t *mask = NULL;
+    pixman_image_t *src;
+    pixman_image_t *white;
+    pixman_format_code_t mask_format = 0; /* silence gcc */
+    cairo_status_t status = CAIRO_STATUS_SUCCESS; /* silence gcc */
+    int src_x, src_y;
+    int i;
+
+    assert (opacity == 1.);
+
+    src = _pixman_image_for_pattern (pattern, FALSE, &extents->bounded,
+				     &dst->base.device_transform,
+				     &src_x, &src_y);
+    if (unlikely (src == NULL))
+	return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    white = _pixman_white_image ();
+    if (unlikely (white == NULL)) {
+	pixman_image_unref (src);
+	return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+    }
+
+    _cairo_scaled_font_freeze_cache (font);
+
+    for (i = 0; i < num_glyphs; i++) {
+	int x, y;
+	cairo_image_surface_t *glyph_surface;
+	cairo_scaled_glyph_t *scaled_glyph;
+
+	status = _cairo_scaled_glyph_lookup (font, glyphs[i].index,
+					     CAIRO_SCALED_GLYPH_INFO_SURFACE,
+					     &scaled_glyph);
+
+	if (unlikely (status))
+	    goto CLEANUP;
+
+	glyph_surface = scaled_glyph->surface;
+
+	if (glyph_surface->width == 0 || glyph_surface->height == 0)
+	    continue;
+
+	/* To start, create the mask using the format from the first
+	 * glyph. Later we'll deal with different formats. */
+	if (mask == NULL) {
+	    mask_format = glyph_surface->pixman_format;
+	    mask = pixman_image_create_bits (mask_format,
+					     extents->bounded.width,
+					     extents->bounded.height,
+					     NULL, 0);
+	    if (unlikely (mask == NULL)) {
+		status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+		goto CLEANUP;
+	    }
+
+	    if (PIXMAN_FORMAT_RGB (mask_format))
+		pixman_image_set_component_alpha (mask, TRUE);
+	}
+
+	/* If we have glyphs of different formats, we "upgrade" the mask
+	 * to the wider of the formats. */
+	if (glyph_surface->pixman_format != mask_format &&
+	    PIXMAN_FORMAT_BPP (mask_format) <
+	    PIXMAN_FORMAT_BPP (glyph_surface->pixman_format))
+	{
+	    pixman_image_t *new_mask;
+
+	    mask_format = glyph_surface->pixman_format;
+	    new_mask = pixman_image_create_bits (mask_format,
+						 extents->bounded.width,
+						 extents->bounded.height,
+						 NULL, 0);
+	    if (unlikely (new_mask == NULL)) {
+		status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+		goto CLEANUP;
+	    }
+
+	    pixman_image_composite32 (PIXMAN_OP_SRC,
+                                      white, mask, new_mask,
+                                      0, 0, 0, 0, 0, 0,
+                                      extents->bounded.width,
+				      extents->bounded.height);
+
+	    pixman_image_unref (mask);
+	    mask = new_mask;
+	    if (PIXMAN_FORMAT_RGB (mask_format))
+		pixman_image_set_component_alpha (mask, TRUE);
+	}
+
+	/* round glyph locations to the nearest pixel */
+	/* XXX: FRAGILE: We're ignoring device_transform scaling here. A bug? */
+	x = _cairo_lround (glyphs[i].x -
+			   glyph_surface->base.device_transform.x0);
+	y = _cairo_lround (glyphs[i].y -
+			   glyph_surface->base.device_transform.y0);
+	if (glyph_surface->pixman_format == mask_format) {
+	    pixman_image_composite32 (PIXMAN_OP_ADD,
+                                      glyph_surface->pixman_image, NULL, mask,
+                                      0, 0, 0, 0,
+                                      x - extents->bounded.x, y - extents->bounded.y,
+                                      glyph_surface->width,
+                                      glyph_surface->height);
+	} else {
+	    pixman_image_composite32 (PIXMAN_OP_ADD,
+                                      white, glyph_surface->pixman_image, mask,
+                                      0, 0, 0, 0,
+                                      x - extents->bounded.x, y - extents->bounded.y,
+                                      glyph_surface->width,
+                                      glyph_surface->height);
+	}
+    }
+
+    pixman_image_composite32 (_pixman_operator (op),
+                              src, mask, dst->pixman_image,
+                              extents->bounded.x + src_x, extents->bounded.y + src_y,
+                              0, 0,
+                              extents->bounded.x - dst_x, extents->bounded.y - dst_y,
+                              extents->bounded.width,     extents->bounded.height);
+
+CLEANUP:
+    _cairo_scaled_font_thaw_cache (font);
+    if (mask != NULL)
+	pixman_image_unref (mask);
+    pixman_image_unref (src);
+    pixman_image_unref (white);
+
+    return status;
+}
+
+static cairo_status_t
+_composite_glyphs (void				*closure,
+		   cairo_fast_image_surface_t   *dst,
+		   cairo_operator_t		 op,
+		   const cairo_pattern_t	*pattern,
+		   double                        opacity,
+		   int				 dst_x,
+		   int				 dst_y,
+		   const cairo_composite_rectangles_t	*extents)
+{
+    composite_glyphs_info_t *info = closure;
+    cairo_scaled_glyph_t *glyph_cache[64];
+    pixman_op_t pixman_op = _pixman_operator (op);
+    pixman_image_t *src = NULL;
+    int src_x = 0, src_y = 0;
+    cairo_status_t status;
+    int i;
+
+    assert (opacity == 1.);
+
+    if (pattern != NULL) {
+	src = _pixman_image_for_pattern (pattern, FALSE, &extents->bounded,
+					 &dst->base.device_transform,
+					 &src_x, &src_y);
+	src_x -= dst_x;
+	src_y -= dst_y;
+    } else {
+	src = _pixman_white_image ();
+    }
+    if (unlikely (src == NULL))
+	return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    memset (glyph_cache, 0, sizeof (glyph_cache));
+    status = CAIRO_STATUS_SUCCESS;
+
+    _cairo_scaled_font_freeze_cache (info->font);
+    for (i = 0; i < info->num_glyphs; i++) {
+	int x, y;
+	cairo_image_surface_t *glyph_surface;
+	cairo_scaled_glyph_t *scaled_glyph;
+	unsigned long glyph_index = info->glyphs[i].index;
+	int cache_index = glyph_index % ARRAY_LENGTH (glyph_cache);
+
+	scaled_glyph = glyph_cache[cache_index];
+	if (scaled_glyph == NULL ||
+	    _cairo_scaled_glyph_index (scaled_glyph) != glyph_index)
+	{
+	    status = _cairo_scaled_glyph_lookup (info->font, glyph_index,
+						 CAIRO_SCALED_GLYPH_INFO_SURFACE,
+						 &scaled_glyph);
+
+	    if (unlikely (status))
+		break;
+
+	    glyph_cache[cache_index] = scaled_glyph;
+	}
+
+	glyph_surface = scaled_glyph->surface;
+	if (glyph_surface->width && glyph_surface->height) {
+	    int x1, y1, x2, y2;
+
+	    /* round glyph locations to the nearest pixel */
+	    /* XXX: FRAGILE: We're ignoring device_transform scaling here. A bug? */
+	    x = _cairo_lround (info->glyphs[i].x -
+			       glyph_surface->base.device_transform.x0);
+	    y = _cairo_lround (info->glyphs[i].y -
+			       glyph_surface->base.device_transform.y0);
+
+	    x1 = x;
+	    if (x1 < extents->bounded.x)
+		x1 = extents->bounded.x;
+	    x2 = x + glyph_surface->width;
+	    if (x2 > extents->bounded.x + extents->bounded.width)
+		x2 = extents->bounded.x + extents->bounded.width;
+
+	    y1 = y;
+	    if (y1 < extents->bounded.y)
+		y1 = extents->bounded.y;
+	    y2 = y + glyph_surface->height;
+	    if (y2 > extents->bounded.y + extents->bounded.height)
+		y2 = extents->bounded.y + extents->bounded.height;
+
+	    pixman_image_composite32 (pixman_op,
+                                      src, glyph_surface->pixman_image, dst->pixman_image,
+                                      x1 + src_x,  y1 + src_y,
+                                      x1 - x, y1 - y,
+                                      x1 - dst_x, y1 - dst_y,
+                                      x2 - x1, y2 - y1);
+	}
+    }
+    _cairo_scaled_font_thaw_cache (info->font);
+
+    pixman_image_unref (src);
+
+    return status;
+}
+
+static cairo_int_status_t
+_cairo_fast_image_surface_glyphs (void			*abstract_surface,
+				  cairo_operator_t		 op,
+				  const cairo_pattern_t	*source,
+				  cairo_glyph_t		*glyphs,
+				  int			 num_glyphs,
+				  cairo_scaled_font_t	*scaled_font,
+				  const cairo_clip_t		*clip,
+				  int *num_remaining)
+{
+    cairo_fast_image_surface_t *surface = abstract_surface;
+    cairo_composite_rectangles_t extents;
+    cairo_rectangle_int_t unbounded;
+    composite_glyphs_info_t glyph_info;
+    cairo_status_t status;
+    cairo_bool_t overlap;
+    unsigned int flags;
+
+    _cairo_fast_image_surface_get_extents (surface, &unbounded);
+    status = _cairo_composite_rectangles_init_for_glyphs (&extents, &unbounded,
+							  op, source,
+							  scaled_font,
+							  glyphs, num_glyphs,
+							  clip,
+							  &overlap);
+    if (unlikely (status))
+	return status;
+
+    glyph_info.font = scaled_font;
+    glyph_info.glyphs = glyphs;
+    glyph_info.num_glyphs = num_glyphs;
+
+    flags = 0;
+    if (extents.mask.width > extents.unbounded.width ||
+	extents.mask.height > extents.unbounded.height)
+    {
+	flags |= FORCE_CLIP_REGION;
+    }
+    status = _clip_and_composite (surface, op, source, 1.,
+				  extents.is_bounded == 0 ?
+				  _composite_glyphs_via_mask :
+				  _composite_glyphs,
+				  &glyph_info,
+				  &extents,
+				  need_bounded_clip (&extents) | flags);
+
+    _cairo_composite_rectangles_fini (&extents);
+
+    *num_remaining = 0;
+    return status;
+}
+
+static void
+_cairo_fast_image_surface_get_font_options (void                  *abstract_surface,
+					    cairo_font_options_t  *options)
+{
+    _cairo_font_options_init_default (options);
+
+    cairo_font_options_set_hint_metrics (options, CAIRO_HINT_METRICS_ON);
+    _cairo_font_options_set_round_glyph_positions (options, CAIRO_ROUND_GLYPH_POS_ON);
+}
+
+/* Override the default context */
+
+static cairo_t *
+_cairo_fast_image_context_create (void *target)
+{
+    cairo_t *cr;
+    cairo_default_context_t *def;
+
+    cr = _cairo_default_context_create (target);
+    if (unlikely (cr->status))
+	return cr;
+
+    /* set our preferred defaults */
+    def = (cairo_default_context_t *) cr;
+    def->gstate->tolerance = FAST_IMAGE_TOLERANCE;
+
+    return cr;
+}
+
+static const cairo_surface_backend_t fast_image_surface_backend = {
+    CAIRO_SURFACE_TYPE_FASTIMAGE,
+    _cairo_fast_image_surface_finish,
+
+    _cairo_fast_image_context_create,
+
+    _cairo_fast_image_surface_create_similar,
+    NULL, /* create similar image */
+    _cairo_fast_image_surface_map_to_image,
+    _cairo_fast_image_surface_unmap_image,
+
+    _cairo_fast_image_surface_acquire_source_image,
+    _cairo_fast_image_surface_release_source_image,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+
+    NULL, /* copy_page */
+    NULL, /* show_page */
+    _cairo_fast_image_surface_get_extents,
+    NULL, /* old_show_glyphs */
+    _cairo_fast_image_surface_get_font_options,
+    NULL, /* flush */
+    NULL, /* mark dirty */
+    NULL, /* font_fini */
+    NULL, /* glyph_fini */
+
+    _cairo_fast_image_surface_paint,
+    _cairo_fast_image_surface_mask,
+    _cairo_fast_image_surface_stroke,
+    _cairo_fast_image_surface_fill,
+    _cairo_fast_image_surface_glyphs,
+    NULL, /* show_text_glyphs */
+    NULL, /* snapshot */
+    NULL, /* is_similar */
+};
+
+/**
+ * cairo_fast_image_surface_create:
+ * @format: format of pixels in the surface to create
+ * @width: width of the surface, in pixels
+ * @height: height of the surface, in pixels
+ *
+ * Creates an image surface of the specified format and
+ * dimensions. Initially the surface contents are all
+ * 0. (Specifically, within each pixel, each color or alpha channel
+ * belonging to format will be 0. The contents of bits within a pixel,
+ * but not belonging to the given format are undefined).
+ *
+ * Return value: a pointer to the newly created surface. The caller
+ * owns the surface and should call cairo_surface_destroy() when done
+ * with it.
+ *
+ * This function always returns a valid pointer, but it will return a
+ * pointer to a "nil" surface if an error such as out of memory
+ * occurs. You can use cairo_surface_status() to check for this.
+ **/
+cairo_surface_t *
+cairo_fast_image_surface_create (cairo_format_t	format,
+				 int		width,
+				 int		height)
+{
+    if (! CAIRO_FORMAT_VALID (format))
+	return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT));
+
+    if (! is_size_valid (width, height))
+	return cairo_image_surface_create (format, width, height);
+
+    return fast_image_surface_create(_cairo_format_to_pixman_format_code (format),
+				     width, height);
+}
+
+cairo_surface_t *
+cairo_fast_image_surface_create_for_data (void			*data,
+					  cairo_format_t	format,
+					  int			width,
+					  int			height,
+					  int			stride)
+{
+    cairo_fast_image_surface_t *surface;
+    pixman_image_t *pixman_image;
+    pixman_format_code_t pixman_format;
+
+    if (! CAIRO_FORMAT_VALID (format))
+	return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT));
+
+    if (! is_size_valid (width, height))
+	return cairo_image_surface_create (format, width, height);
+
+    pixman_format =_cairo_format_to_pixman_format_code (format),
+    pixman_image = pixman_image_create_bits (pixman_format,
+					     width, height,
+					     data, stride);
+    if (unlikely (pixman_image == NULL))
+	return _cairo_surface_create_in_error (CAIRO_STATUS_NO_MEMORY);
+
+    surface = malloc (sizeof (cairo_fast_image_surface_t));
+    if (unlikely (surface == NULL)) {
+	pixman_image_unref (pixman_image);
+	return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+    }
+
+    _cairo_surface_init (&surface->base,
+			 &fast_image_surface_backend,
+			 NULL, /* device */
+			 _cairo_content_from_pixman_format (pixman_format));
+
+    surface->pixman_image = pixman_image;
+    surface->pixman_format = pixman_format;
+    surface->format = format;
+
+    surface->width = width;
+    surface->height = height;
+
+    surface->base.is_clear = FALSE;
+
+    return &surface->base;
+}
diff --git a/src/cairo-pattern-private.h b/src/cairo-pattern-private.h
index 647eb0b..1c22ee8 100644
--- a/src/cairo-pattern-private.h
+++ b/src/cairo-pattern-private.h
@@ -352,7 +352,10 @@ _cairo_mesh_pattern_rasterize (const cairo_mesh_pattern_t *mesh,
 			       double                      x_offset,
 			       double                      y_offset);
 
-
+cairo_private_no_warn cairo_filter_t
+_cairo_surface_pattern_sampled_area (const cairo_surface_pattern_t *pattern,
+				     const cairo_rectangle_int_t *extents,
+				     cairo_rectangle_int_t *sample);
 
 CAIRO_END_DECLS
 
diff --git a/src/cairo-pattern.c b/src/cairo-pattern.c
index cb6bba8..8c33e4c 100644
--- a/src/cairo-pattern.c
+++ b/src/cairo-pattern.c
@@ -5148,6 +5148,34 @@ cairo_mesh_pattern_get_control_point (cairo_pattern_t *pattern,
 }
 slim_hidden_def (cairo_mesh_pattern_get_control_point);
 
+cairo_filter_t
+_cairo_surface_pattern_sampled_area (const cairo_surface_pattern_t *pattern,
+				     const cairo_rectangle_int_t *extents,
+				     cairo_rectangle_int_t *sample)
+{
+    cairo_filter_t filter;
+    double x1, x2, y1, y2;
+    double pad;
+
+    x1 = extents->x;
+    y1 = extents->y;
+    x2 = extents->x + (int) extents->width;
+    y2 = extents->y + (int) extents->height;
+
+    _cairo_matrix_transform_bounding_box (&pattern->base.matrix,
+                                          &x1, &y1, &x2, &y2,
+                                          NULL);
+
+    filter = _cairo_pattern_analyze_filter (&pattern->base, &pad);
+    sample->x = floor (x1 - pad);
+    sample->y = floor (y1 - pad);
+    sample->width  = ceil (x2 + pad) - sample->x;
+    sample->height = ceil (y2 + pad) - sample->y;
+
+    return filter;
+}
+
+
 void
 _cairo_pattern_reset_static_data (void)
 {
diff --git a/src/cairo.h b/src/cairo.h
index 7987192..24b9f0d 100644
--- a/src/cairo.h
+++ b/src/cairo.h
@@ -2158,6 +2158,7 @@ cairo_surface_status (cairo_surface_t *surface);
  * @CAIRO_SURFACE_TYPE_SKIA: The surface is of type Skia, since 1.10
  * @CAIRO_SURFACE_TYPE_SUBSURFACE: The surface is a subsurface created with
  *   cairo_surface_create_for_rectangle(), since 1.10
+ * @CAIRO_SURFACE_TYPE_FASTIMAGE: The surface is of type fast-image, since 1.12
  *
  * #cairo_surface_type_t is used to describe the type of a given
  * surface. The surface types are also known as "backends" or "surface
@@ -2207,7 +2208,8 @@ typedef enum _cairo_surface_type {
     CAIRO_SURFACE_TYPE_TEE,
     CAIRO_SURFACE_TYPE_XML,
     CAIRO_SURFACE_TYPE_SKIA,
-    CAIRO_SURFACE_TYPE_SUBSURFACE
+    CAIRO_SURFACE_TYPE_SUBSURFACE,
+    CAIRO_SURFACE_TYPE_FASTIMAGE
 } cairo_surface_type_t;
 
 cairo_public cairo_surface_type_t
@@ -2349,6 +2351,18 @@ cairo_image_surface_create_from_png_stream (cairo_read_func_t	read_func,
 
 #endif
 
+cairo_public cairo_surface_t *
+cairo_fast_image_surface_create (cairo_format_t	format,
+				 int		width,
+				 int		height);
+
+cairo_public cairo_surface_t *
+cairo_fast_image_surface_create_for_data (void			*data,
+					  cairo_format_t	format,
+					  int			width,
+					  int			height,
+					  int			stride);
+
 /* Recording-surface functions */
 
 cairo_public cairo_surface_t *
diff --git a/src/cairoint.h b/src/cairoint.h
index e47e14b..5715ad1 100644
--- a/src/cairoint.h
+++ b/src/cairoint.h
@@ -1997,6 +1997,9 @@ _pixman_format_to_masks (pixman_format_code_t	 pixman_format,
 cairo_private void
 _cairo_image_reset_static_data (void);
 
+cairo_private void
+_cairo_fast_image_reset_static_data (void);
+
 cairo_private cairo_surface_t *
 _cairo_image_surface_create_with_pixman_format (unsigned char		*data,
 						pixman_format_code_t	 pixman_format,
commit f33f4e4785a9fe7b680a6941311083737603337e
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Tue Aug 2 23:30:00 2011 +0100

    spans-image-clip

diff --git a/src/cairo-image-surface.c b/src/cairo-image-surface.c
index 508d258..2989953 100644
--- a/src/cairo-image-surface.c
+++ b/src/cairo-image-surface.c
@@ -3429,12 +3429,6 @@ _cairo_image_surface_paint (void			*abstract_surface,
     if (unlikely (status))
 	return status;
 
-    /* If the clip cannot be reduced to a set of boxes, we will need to
-     * use a clipmask. Paint is special as it is the only operation that
-     * does not implicitly use a mask, so we may be able to reduce this
-     * operation to a fill...
-     */
-
     status = _cairo_clip_to_boxes (extents.clip, &boxes);
     if (likely (status == CAIRO_STATUS_SUCCESS)) {
 	status = _clip_and_composite_boxes (surface, op, source, 1.,
@@ -3671,9 +3665,35 @@ _clip_and_composite_polygon (cairo_image_surface_t *dst,
 	return status;
     }
 
-    _cairo_box_round_to_rectangle (&polygon->extents, &extents->mask);
-    if (! _cairo_rectangle_intersect (&extents->bounded, &extents->mask))
-	return CAIRO_STATUS_SUCCESS;
+    if (extents->is_bounded &&
+	(extents->clip->num_boxes > 1 || extents->clip->path)) {
+	cairo_polygon_t clipper;
+	cairo_antialias_t clip_antialias;
+	cairo_fill_rule_t clip_fill_rule;
+
+	status = _cairo_clip_get_polygon (extents->clip,
+					  &clipper,
+					  &clip_fill_rule,
+					  &clip_antialias);
+	if (likely (status == CAIRO_INT_STATUS_SUCCESS)) {
+	    cairo_clip_t *old_clip;
+
+	    status = _cairo_polygon_intersect (polygon, fill_rule,
+					       &clipper, fill_rule);
+	    _cairo_polygon_fini (&clipper);
+	    if (unlikely (status))
+		return status;
+
+	    old_clip = extents->clip;
+	    extents->clip = _cairo_clip_copy_region (extents->clip);
+	    _cairo_clip_destroy (old_clip);
+	}
+    }
+
+    status = _cairo_composite_rectangles_intersect_mask_extents (extents,
+								 &polygon->extents);
+    if (unlikely (status))
+	return status;
 
     status = _composite_polygon (dst, op, src, opacity,
 				 polygon, fill_rule, antialias,
@@ -3690,7 +3710,7 @@ _clip_and_composite_polygon (cairo_image_surface_t *dst,
 
 	status = _clip_and_composite (dst, op, src, opacity,
 				      _composite_spans, &info,
-				      extents, need_unbounded_clip (extents));
+				      extents, need_bounded_clip (extents));
     } else {
 	cairo_traps_t traps;
 
@@ -3725,12 +3745,12 @@ _cairo_image_surface_stroke (void			*abstract_surface,
 			     const cairo_clip_t		*clip)
 {
     cairo_image_surface_t *surface = abstract_surface;
-    cairo_composite_rectangles_t extents;
+    cairo_composite_rectangles_t composite;
     cairo_rectangle_int_t unbounded;
     cairo_int_status_t status;
 
     _cairo_image_surface_get_extents (surface, &unbounded);
-    status = _cairo_composite_rectangles_init_for_stroke (&extents, &unbounded,
+    status = _cairo_composite_rectangles_init_for_stroke (&composite, &unbounded,
 							  op, source,
 							  path, style, ctm,
 							  clip);
@@ -3741,7 +3761,7 @@ _cairo_image_surface_stroke (void			*abstract_surface,
     if (_cairo_path_fixed_stroke_is_rectilinear (path)) {
 	cairo_boxes_t boxes;
 
-	_cairo_boxes_init_with_clip (&boxes, extents.clip);
+	_cairo_boxes_init_with_clip (&boxes, composite.clip);
 	status = _cairo_path_fixed_stroke_rectilinear_to_boxes (path,
 								style,
 								ctm,
@@ -3749,15 +3769,17 @@ _cairo_image_surface_stroke (void			*abstract_surface,
 								&boxes);
 	if (likely (status == CAIRO_INT_STATUS_SUCCESS)) {
 	    status = _clip_and_composite_boxes (surface, op, source, 1.,
-						&boxes, &extents);
+						&boxes, &composite);
 	}
 	_cairo_boxes_fini (&boxes);
     }
 
     if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
 	cairo_polygon_t polygon;
+	cairo_box_t extents;
 
-	_cairo_polygon_init_with_clip (&polygon, extents.clip);
+	_cairo_box_from_rectangle (&extents, &composite.clip->extents);
+	_cairo_polygon_init (&polygon, &extents, 1);
 	status = _cairo_path_fixed_stroke_to_polygon (path,
 						      style,
 						      ctm, ctm_inverse,
@@ -3768,12 +3790,12 @@ _cairo_image_surface_stroke (void			*abstract_surface,
 						  &polygon,
 						  CAIRO_FILL_RULE_WINDING,
 						  antialias,
-						  &extents);
+						  &composite);
 	}
 	_cairo_polygon_fini (&polygon);
     }
 
-    _cairo_composite_rectangles_fini (&extents);
+    _cairo_composite_rectangles_fini (&composite);
 
     return status;
 }
@@ -3789,12 +3811,12 @@ _cairo_image_surface_fill (void				*abstract_surface,
 			   const cairo_clip_t		*clip)
 {
     cairo_image_surface_t *surface = abstract_surface;
-    cairo_composite_rectangles_t extents;
+    cairo_composite_rectangles_t composite;
     cairo_rectangle_int_t unbounded;
     cairo_status_t status;
 
     _cairo_image_surface_get_extents (surface, &unbounded);
-    status = _cairo_composite_rectangles_init_for_fill (&extents, &unbounded,
+    status = _cairo_composite_rectangles_init_for_fill (&composite, &unbounded,
 							op, source, path,
 							clip);
     if (unlikely (status))
@@ -3803,33 +3825,35 @@ _cairo_image_surface_fill (void				*abstract_surface,
     if (_cairo_path_fixed_fill_is_rectilinear (path)) {
 	cairo_boxes_t boxes;
 
-	_cairo_boxes_init_with_clip (&boxes, extents.clip);
+	_cairo_boxes_init_with_clip (&boxes, composite.clip);
 	status = _cairo_path_fixed_fill_rectilinear_to_boxes (path,
 							      fill_rule,
 							      antialias,
 							      &boxes);
 	if (likely (status == CAIRO_STATUS_SUCCESS)) {
 	    status = _clip_and_composite_boxes (surface, op, source, 1.,
-						&boxes, &extents);
+						&boxes, &composite);
 	}
 	_cairo_boxes_fini (&boxes);
     } else {
 	cairo_polygon_t polygon;
+	cairo_box_t extents;
 
 	assert (! _cairo_path_fixed_fill_is_empty (path));
 
-	_cairo_polygon_init_with_clip (&polygon, extents.clip);
+	_cairo_box_from_rectangle (&extents, &composite.clip->extents);
+	_cairo_polygon_init (&polygon, &extents, 1);
 	status = _cairo_path_fixed_fill_to_polygon (path, tolerance, &polygon);
 	if (likely (status == CAIRO_STATUS_SUCCESS)) {
 	    status = _clip_and_composite_polygon (surface, op, source, 1.,
 						  &polygon,
 						  fill_rule, antialias,
-						  &extents);
+						  &composite);
 	}
 	_cairo_polygon_fini (&polygon);
     }
 
-    _cairo_composite_rectangles_fini (&extents);
+    _cairo_composite_rectangles_fini (&composite);
 
     return status;
 }
commit 988f5146ccd4359cc2ee3a94c15a9823b5f8eae2
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Sat Jul 30 17:28:21 2011 +0100

    image: Interface with pixman spans.
    
    Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>

diff --git a/src/Makefile.sources b/src/Makefile.sources
index f22f7ed..1dbe4c0 100644
--- a/src/Makefile.sources
+++ b/src/Makefile.sources
@@ -184,6 +184,7 @@ cairo_sources = \
 	cairo-surface-wrapper.c \
 	cairo-system.c \
 	cairo-tor-scan-converter.c \
+	cairo-clip-tor-scan-converter.c \
 	cairo-toy-font-face.c \
 	cairo-traps.c \
 	cairo-unicode.c \
diff --git a/src/cairo-botor-scan-converter.c b/src/cairo-botor-scan-converter.c
index 0778a5d..6839c5e 100644
--- a/src/cairo-botor-scan-converter.c
+++ b/src/cairo-botor-scan-converter.c
@@ -1397,6 +1397,7 @@ render_rows (cairo_botor_scan_converter_t *self,
 
 	if (x > prev_x) {
 	    spans[num_spans].x = prev_x;
+	    spans[num_spans].is_clipped = 0;
 	    spans[num_spans].coverage = AREA_TO_ALPHA (cover);
 	    ++num_spans;
 	}
@@ -1413,12 +1414,14 @@ render_rows (cairo_botor_scan_converter_t *self,
 
     if (prev_x <= self->xmax) {
 	spans[num_spans].x = prev_x;
+	spans[num_spans].is_clipped = 0;
 	spans[num_spans].coverage = AREA_TO_ALPHA (cover);
 	++num_spans;
     }
 
     if (cover && prev_x < self->xmax) {
 	spans[num_spans].x = self->xmax;
+	spans[num_spans].is_clipped = 1;
 	spans[num_spans].coverage = 0;
 	++num_spans;
     }
diff --git a/src/cairo-clip-tor-scan-converter.c b/src/cairo-clip-tor-scan-converter.c
new file mode 100644
index 0000000..f777996
--- /dev/null
+++ b/src/cairo-clip-tor-scan-converter.c
@@ -0,0 +1,1842 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* glitter-paths - polygon scan converter
+ *
+ * Copyright (c) 2008  M Joonas Pihlaja
+ * Copyright (c) 2007  David Turner
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+/* This is the Glitter paths scan converter incorporated into cairo.
+ * The source is from commit 734c53237a867a773640bd5b64816249fa1730f8
+ * of
+ *
+ *   http://gitweb.freedesktop.org/?p=users/joonas/glitter-paths
+ */
+/* Glitter-paths is a stand alone polygon rasteriser derived from
+ * David Turner's reimplementation of Tor Anderssons's 15x17
+ * supersampling rasteriser from the Apparition graphics library.  The
+ * main new feature here is cheaply choosing per-scan line between
+ * doing fully analytical coverage computation for an entire row at a
+ * time vs. using a supersampling approach.
+ *
+ * David Turner's code can be found at
+ *
+ *   http://david.freetype.org/rasterizer-shootout/raster-comparison-20070813.tar.bz2
+ *
+ * In particular this file incorporates large parts of ftgrays_tor10.h
+ * from raster-comparison-20070813.tar.bz2
+ */
+/* Overview
+ *
+ * A scan converter's basic purpose to take polygon edges and convert
+ * them into an RLE compressed A8 mask.  This one works in two phases:
+ * gathering edges and generating spans.
+ *
+ * 1) As the user feeds the scan converter edges they are vertically
+ * clipped and bucketted into a _polygon_ data structure.  The edges
+ * are also snapped from the user's coordinates to the subpixel grid
+ * coordinates used during scan conversion.
+ *
+ *     user
+ *      |
+ *      | edges
+ *      V
+ *    polygon buckets
+ *
+ * 2) Generating spans works by performing a vertical sweep of pixel
+ * rows from top to bottom and maintaining an _active_list_ of edges
+ * that intersect the row.  From the active list the fill rule
+ * determines which edges are the left and right edges of the start of
+ * each span, and their contribution is then accumulated into a pixel
+ * coverage list (_cell_list_) as coverage deltas.  Once the coverage
+ * deltas of all edges are known we can form spans of constant pixel
+ * coverage by summing the deltas during a traversal of the cell list.
+ * At the end of a pixel row the cell list is sent to a coverage
+ * blitter for rendering to some target surface.
+ *
+ * The pixel coverages are computed by either supersampling the row
+ * and box filtering a mono rasterisation, or by computing the exact
+ * coverages of edges in the active list.  The supersampling method is
+ * used whenever some edge starts or stops within the row or there are
+ * edge intersections in the row.
+ *
+ *   polygon bucket for       \
+ *   current pixel row        |
+ *      |                     |
+ *      | activate new edges  |  Repeat GRID_Y times if we
+ *      V                     \  are supersampling this row,
+ *   active list              /  or just once if we're computing
+ *      |                     |  analytical coverage.
+ *      | coverage deltas     |
+ *      V                     |
+ *   pixel coverage list     /
+ *      |
+ *      V
+ *   coverage blitter
+ */
+#include "cairoint.h"
+#include "cairo-spans-private.h"
+#include "cairo-error-private.h"
+
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+#include <setjmp.h>
+
+/* The input coordinate scale and the rasterisation grid scales. */
+#define GLITTER_INPUT_BITS CAIRO_FIXED_FRAC_BITS
+#define GRID_X_BITS CAIRO_FIXED_FRAC_BITS
+#define GRID_Y 15
+
+/* Set glitter up to use a cairo span renderer to do the coverage
+ * blitting. */
+struct pool;
+struct cell_list;
+
+/*-------------------------------------------------------------------------
+ * glitter-paths.h
+ */
+
+/* "Input scaled" numbers are fixed precision reals with multiplier
+ * 2**GLITTER_INPUT_BITS.  Input coordinates are given to glitter as
+ * pixel scaled numbers.  These get converted to the internal grid
+ * scaled numbers as soon as possible. Internal overflow is possible
+ * if GRID_X/Y inside glitter-paths.c is larger than
+ * 1<<GLITTER_INPUT_BITS. */
+#ifndef GLITTER_INPUT_BITS
+#  define GLITTER_INPUT_BITS 8
+#endif
+#define GLITTER_INPUT_SCALE (1<<GLITTER_INPUT_BITS)
+typedef int glitter_input_scaled_t;
+
+/* Opaque type for scan converting. */
+typedef struct glitter_scan_converter glitter_scan_converter_t;
+
+/*-------------------------------------------------------------------------
+ * glitter-paths.c: Implementation internal types
+ */
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+
+/* All polygon coordinates are snapped onto a subsample grid. "Grid
+ * scaled" numbers are fixed precision reals with multiplier GRID_X or
+ * GRID_Y. */
+typedef int grid_scaled_t;
+typedef int grid_scaled_x_t;
+typedef int grid_scaled_y_t;
+
+/* Default x/y scale factors.
+ *  You can either define GRID_X/Y_BITS to get a power-of-two scale
+ *  or define GRID_X/Y separately. */
+#if !defined(GRID_X) && !defined(GRID_X_BITS)
+#  define GRID_X_BITS 8
+#endif
+#if !defined(GRID_Y) && !defined(GRID_Y_BITS)
+#  define GRID_Y 15
+#endif
+
+/* Use GRID_X/Y_BITS to define GRID_X/Y if they're available. */
+#ifdef GRID_X_BITS
+#  define GRID_X (1 << GRID_X_BITS)
+#endif
+#ifdef GRID_Y_BITS
+#  define GRID_Y (1 << GRID_Y_BITS)
+#endif
+
+/* The GRID_X_TO_INT_FRAC macro splits a grid scaled coordinate into
+ * integer and fractional parts. The integer part is floored. */
+#if defined(GRID_X_TO_INT_FRAC)
+  /* do nothing */
+#elif defined(GRID_X_BITS)
+#  define GRID_X_TO_INT_FRAC(x, i, f) \
+	_GRID_TO_INT_FRAC_shift(x, i, f, GRID_X_BITS)
+#else
+#  define GRID_X_TO_INT_FRAC(x, i, f) \
+	_GRID_TO_INT_FRAC_general(x, i, f, GRID_X)
+#endif
+
+#define _GRID_TO_INT_FRAC_general(t, i, f, m) do {	\
+    (i) = (t) / (m);					\
+    (f) = (t) % (m);					\
+    if ((f) < 0) {					\
+	--(i);						\
+	(f) += (m);					\
+    }							\
+} while (0)
+
+#define _GRID_TO_INT_FRAC_shift(t, i, f, b) do {	\
+    (f) = (t) & ((1 << (b)) - 1);			\
+    (i) = (t) >> (b);					\
+} while (0)
+
+/* A grid area is a real in [0,1] scaled by 2*GRID_X*GRID_Y.  We want
+ * to be able to represent exactly areas of subpixel trapezoids whose
+ * vertices are given in grid scaled coordinates.  The scale factor
+ * comes from needing to accurately represent the area 0.5*dx*dy of a
+ * triangle with base dx and height dy in grid scaled numbers. */
+typedef int grid_area_t;
+#define GRID_XY (2*GRID_X*GRID_Y) /* Unit area on the grid. */
+
+/* GRID_AREA_TO_ALPHA(area): map [0,GRID_XY] to [0,255]. */
+#if GRID_XY == 510
+#  define GRID_AREA_TO_ALPHA(c)	  (((c)+1) >> 1)
+#elif GRID_XY == 255
+#  define  GRID_AREA_TO_ALPHA(c)  (c)
+#elif GRID_XY == 64
+#  define  GRID_AREA_TO_ALPHA(c)  (((c) << 2) | -(((c) & 0x40) >> 6))
+#elif GRID_XY == 128
+#  define  GRID_AREA_TO_ALPHA(c)  ((((c) << 1) | -((c) >> 7)) & 255)
+#elif GRID_XY == 256
+#  define  GRID_AREA_TO_ALPHA(c)  (((c) | -((c) >> 8)) & 255)
+#elif GRID_XY == 15
+#  define  GRID_AREA_TO_ALPHA(c)  (((c) << 4) + (c))
+#elif GRID_XY == 2*256*15
+#  define  GRID_AREA_TO_ALPHA(c)  (((c) + ((c)<<4) + 256) >> 9)
+#else
+#  define  GRID_AREA_TO_ALPHA(c)  (((c)*255 + GRID_XY/2) / GRID_XY)
+#endif
+
+#define UNROLL3(x) x x x
+
+struct quorem {
+    int32_t quo;
+    int32_t rem;
+};
+
+/* Header for a chunk of memory in a memory pool. */
+struct _pool_chunk {
+    /* # bytes used in this chunk. */
+    size_t size;
+
+    /* # bytes total in this chunk */
+    size_t capacity;
+
+    /* Pointer to the previous chunk or %NULL if this is the sentinel
+     * chunk in the pool header. */
+    struct _pool_chunk *prev_chunk;
+
+    /* Actual data starts here.	 Well aligned for pointers. */
+};
+
+/* A memory pool.  This is supposed to be embedded on the stack or
+ * within some other structure.	 It may optionally be followed by an
+ * embedded array from which requests are fulfilled until
+ * malloc needs to be called to allocate a first real chunk. */
+struct pool {
+    /* Chunk we're allocating from. */
+    struct _pool_chunk *current;
+
+    jmp_buf *jmp;
+
+    /* Free list of previously allocated chunks.  All have >= default
+     * capacity. */
+    struct _pool_chunk *first_free;
+
+    /* The default capacity of a chunk. */
+    size_t default_capacity;
+
+    /* Header for the sentinel chunk.  Directly following the pool
+     * struct should be some space for embedded elements from which
+     * the sentinel chunk allocates from. */
+    struct _pool_chunk sentinel[1];
+};
+
+/* A polygon edge. */
+struct edge {
+    /* Next in y-bucket or active list. */
+    struct edge *next;
+
+    /* Current x coordinate while the edge is on the active
+     * list. Initialised to the x coordinate of the top of the
+     * edge. The quotient is in grid_scaled_x_t units and the
+     * remainder is mod dy in grid_scaled_y_t units.*/
+    struct quorem x;
+
+    /* Advance of the current x when moving down a subsample line. */
+    struct quorem dxdy;
+
+    /* Advance of the current x when moving down a full pixel
+     * row. Only initialised when the height of the edge is large
+     * enough that there's a chance the edge could be stepped by a
+     * full row's worth of subsample rows at a time. */
+    struct quorem dxdy_full;
+
+    /* The clipped y of the top of the edge. */
+    grid_scaled_y_t ytop;
+
+    /* y2-y1 after orienting the edge downwards.  */
+    grid_scaled_y_t dy;
+
+    /* Number of subsample rows remaining to scan convert of this
+     * edge. */
+    grid_scaled_y_t height_left;
+
+    /* Original sign of the edge: +1 for downwards, -1 for upwards
+     * edges.  */
+    int dir;
+    int vertical;
+    int clip;
+};
+
+/* Number of subsample rows per y-bucket. Must be GRID_Y. */
+#define EDGE_Y_BUCKET_HEIGHT GRID_Y
+
+#define EDGE_Y_BUCKET_INDEX(y, ymin) (((y) - (ymin))/EDGE_Y_BUCKET_HEIGHT)
+
+/* A collection of sorted and vertically clipped edges of the polygon.
+ * Edges are moved from the polygon to an active list while scan
+ * converting. */
+struct polygon {
+    /* The vertical clip extents. */
+    grid_scaled_y_t ymin, ymax;
+
+    /* Array of edges all starting in the same bucket.	An edge is put
+     * into bucket EDGE_BUCKET_INDEX(edge->ytop, polygon->ymin) when
+     * it is added to the polygon. */
+    struct edge **y_buckets;
+    struct edge *y_buckets_embedded[64];
+
+    struct {
+	struct pool base[1];
+	struct edge embedded[32];
+    } edge_pool;
+};
+
+/* A cell records the effect on pixel coverage of polygon edges
+ * passing through a pixel.  It contains two accumulators of pixel
+ * coverage.
+ *
+ * Consider the effects of a polygon edge on the coverage of a pixel
+ * it intersects and that of the following one.  The coverage of the
+ * following pixel is the height of the edge multiplied by the width
+ * of the pixel, and the coverage of the pixel itself is the area of
+ * the trapezoid formed by the edge and the right side of the pixel.
+ *
+ * +-----------------------+-----------------------+
+ * |                       |                       |
+ * |                       |                       |
+ * |_______________________|_______________________|
+ * |   \...................|.......................|\
+ * |    \..................|.......................| |
+ * |     \.................|.......................| |
+ * |      \....covered.....|.......................| |
+ * |       \....area.......|.......................| } covered height
+ * |        \..............|.......................| |
+ * |uncovered\.............|.......................| |
+ * |  area    \............|.......................| |
+ * |___________\...........|.......................|/
+ * |                       |                       |
+ * |                       |                       |
+ * |                       |                       |
+ * +-----------------------+-----------------------+
+ *
+ * Since the coverage of the following pixel will always be a multiple
+ * of the width of the pixel, we can store the height of the covered
+ * area instead.  The coverage of the pixel itself is the total
+ * coverage minus the area of the uncovered area to the left of the
+ * edge.  As it's faster to compute the uncovered area we only store
+ * that and subtract it from the total coverage later when forming
+ * spans to blit.
+ *
+ * The heights and areas are signed, with left edges of the polygon
+ * having positive sign and right edges having negative sign.  When
+ * two edges intersect they swap their left/rightness so their
+ * contribution above and below the intersection point must be
+ * computed separately. */
+struct cell {
+    struct cell		*next;
+    int			 x;
+    grid_area_t		 uncovered_area;
+    grid_scaled_y_t	 covered_height;
+    grid_scaled_y_t	 clipped_height;
+};
+
+/* A cell list represents the scan line sparsely as cells ordered by
+ * ascending x.  It is geared towards scanning the cells in order
+ * using an internal cursor. */
+struct cell_list {
+    /* Sentinel nodes */
+    struct cell head, tail;
+
+    /* Cursor state for iterating through the cell list. */
+    struct cell *cursor;
+
+    /* Cells in the cell list are owned by the cell list and are
+     * allocated from this pool.  */
+    struct {
+	struct pool base[1];
+	struct cell embedded[32];
+    } cell_pool;
+};
+
+struct cell_pair {
+    struct cell *cell1;
+    struct cell *cell2;
+};
+
+/* The active list contains edges in the current scan line ordered by
+ * the x-coordinate of the intercept of the edge and the scan line. */
+struct active_list {
+    /* Leftmost edge on the current scan line. */
+    struct edge *head;
+
+    /* A lower bound on the height of the active edges is used to
+     * estimate how soon some active edge ends.	 We can't advance the
+     * scan conversion by a full pixel row if an edge ends somewhere
+     * within it. */
+    grid_scaled_y_t min_height;
+};
+
+struct glitter_scan_converter {
+    struct polygon	polygon[1];
+    struct active_list	active[1];
+    struct cell_list	coverages[1];
+
+    /* Clip box. */
+    grid_scaled_y_t ymin, ymax;
+};
+
+/* Compute the floored division a/b. Assumes / and % perform symmetric
+ * division. */
+inline static struct quorem
+floored_divrem(int a, int b)
+{
+    struct quorem qr;
+    qr.quo = a/b;
+    qr.rem = a%b;
+    if ((a^b)<0 && qr.rem) {
+	qr.quo -= 1;
+	qr.rem += b;
+    }
+    return qr;
+}
+
+/* Compute the floored division (x*a)/b. Assumes / and % perform symmetric
+ * division. */
+static struct quorem
+floored_muldivrem(int x, int a, int b)
+{
+    struct quorem qr;
+    long long xa = (long long)x*a;
+    qr.quo = xa/b;
+    qr.rem = xa%b;
+    if ((xa>=0) != (b>=0) && qr.rem) {
+	qr.quo -= 1;
+	qr.rem += b;
+    }
+    return qr;
+}
+
+static struct _pool_chunk *
+_pool_chunk_init(
+    struct _pool_chunk *p,
+    struct _pool_chunk *prev_chunk,
+    size_t capacity)
+{
+    p->prev_chunk = prev_chunk;
+    p->size = 0;
+    p->capacity = capacity;
+    return p;
+}
+
+static struct _pool_chunk *
+_pool_chunk_create(struct pool *pool, size_t size)
+{
+    struct _pool_chunk *p;
+
+    p = malloc(size + sizeof(struct _pool_chunk));
+    if (unlikely (NULL == p))
+	longjmp (*pool->jmp, _cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+    return _pool_chunk_init(p, pool->current, size);
+}
+
+static void
+pool_init(struct pool *pool,
+	  jmp_buf *jmp,
+	  size_t default_capacity,
+	  size_t embedded_capacity)
+{
+    pool->jmp = jmp;
+    pool->current = pool->sentinel;
+    pool->first_free = NULL;
+    pool->default_capacity = default_capacity;
+    _pool_chunk_init(pool->sentinel, NULL, embedded_capacity);
+}
+
+static void
+pool_fini(struct pool *pool)
+{
+    struct _pool_chunk *p = pool->current;
+    do {
+	while (NULL != p) {
+	    struct _pool_chunk *prev = p->prev_chunk;
+	    if (p != pool->sentinel)
+		free(p);
+	    p = prev;
+	}
+	p = pool->first_free;
+	pool->first_free = NULL;
+    } while (NULL != p);
+}
+
+/* Satisfy an allocation by first allocating a new large enough chunk
+ * and adding it to the head of the pool's chunk list. This function
+ * is called as a fallback if pool_alloc() couldn't do a quick
+ * allocation from the current chunk in the pool. */
+static void *
+_pool_alloc_from_new_chunk(
+    struct pool *pool,
+    size_t size)
+{
+    struct _pool_chunk *chunk;
+    void *obj;
+    size_t capacity;
+
+    /* If the allocation is smaller than the default chunk size then
+     * try getting a chunk off the free list.  Force alloc of a new
+     * chunk for large requests. */
+    capacity = size;
+    chunk = NULL;
+    if (size < pool->default_capacity) {
+	capacity = pool->default_capacity;
+	chunk = pool->first_free;
+	if (chunk) {
+	    pool->first_free = chunk->prev_chunk;
+	    _pool_chunk_init(chunk, pool->current, chunk->capacity);
+	}
+    }
+
+    if (NULL == chunk)
+	chunk = _pool_chunk_create (pool, capacity);
+    pool->current = chunk;
+
+    obj = ((unsigned char*)chunk + sizeof(*chunk) + chunk->size);
+    chunk->size += size;
+    return obj;
+}
+
+/* Allocate size bytes from the pool.  The first allocated address
+ * returned from a pool is aligned to sizeof(void*).  Subsequent
+ * addresses will maintain alignment as long as multiples of void* are
+ * allocated.  Returns the address of a new memory area or %NULL on
+ * allocation failures.	 The pool retains ownership of the returned
+ * memory. */
+inline static void *
+pool_alloc (struct pool *pool, size_t size)
+{
+    struct _pool_chunk *chunk = pool->current;
+
+    if (size <= chunk->capacity - chunk->size) {
+	void *obj = ((unsigned char*)chunk + sizeof(*chunk) + chunk->size);
+	chunk->size += size;
+	return obj;
+    } else {
+	return _pool_alloc_from_new_chunk(pool, size);
+    }
+}
+
+/* Relinquish all pool_alloced memory back to the pool. */
+static void
+pool_reset (struct pool *pool)
+{
+    /* Transfer all used chunks to the chunk free list. */
+    struct _pool_chunk *chunk = pool->current;
+    if (chunk != pool->sentinel) {
+	while (chunk->prev_chunk != pool->sentinel) {
+	    chunk = chunk->prev_chunk;
+	}
+	chunk->prev_chunk = pool->first_free;
+	pool->first_free = pool->current;
+    }
+    /* Reset the sentinel as the current chunk. */
+    pool->current = pool->sentinel;
+    pool->sentinel->size = 0;
+}
+
+/* Rewinds the cell list's cursor to the beginning.  After rewinding
+ * we're good to cell_list_find() the cell any x coordinate. */
+inline static void
+cell_list_rewind (struct cell_list *cells)
+{
+    cells->cursor = &cells->head;
+}
+
+/* Rewind the cell list if its cursor has been advanced past x. */
+inline static void
+cell_list_maybe_rewind (struct cell_list *cells, int x)
+{
+    struct cell *tail = cells->cursor;
+    if (tail->x > x)
+	cell_list_rewind (cells);
+}
+
+static void
+cell_list_init(struct cell_list *cells, jmp_buf *jmp)
+{
+    pool_init(cells->cell_pool.base, jmp,
+	      256*sizeof(struct cell),
+	      sizeof(cells->cell_pool.embedded));
+    cells->tail.next = NULL;
+    cells->tail.x = INT_MAX;
+    cells->head.x = INT_MIN;
+    cells->head.next = &cells->tail;
+    cell_list_rewind (cells);
+}
+
+static void
+cell_list_fini(struct cell_list *cells)
+{
+    pool_fini (cells->cell_pool.base);
+}
+
+/* Empty the cell list.  This is called at the start of every pixel
+ * row. */
+inline static void
+cell_list_reset (struct cell_list *cells)
+{
+    cell_list_rewind (cells);
+    cells->head.next = &cells->tail;
+    pool_reset (cells->cell_pool.base);
+}
+
+static struct cell *
+cell_list_alloc (struct cell_list *cells,
+		 struct cell *tail,
+		 int x)
+{
+    struct cell *cell;
+
+    cell = pool_alloc (cells->cell_pool.base, sizeof (struct cell));
+    cell->next = tail->next;
+    tail->next = cell;
+    cell->x = x;
+    cell->uncovered_area = 0;
+    cell->covered_height = 0;
+    cell->clipped_height = 0;
+    return cell;
+}
+
+/* Find a cell at the given x-coordinate.  Returns %NULL if a new cell
+ * needed to be allocated but couldn't be.  Cells must be found with
+ * non-decreasing x-coordinate until the cell list is rewound using
+ * cell_list_rewind(). Ownership of the returned cell is retained by
+ * the cell list. */
+inline static struct cell *
+cell_list_find (struct cell_list *cells, int x)
+{
+    struct cell *tail = cells->cursor;
+
+    while (1) {
+	UNROLL3({
+	    if (tail->next->x > x)
+		break;
+	    tail = tail->next;
+	});
+    }
+
+    if (tail->x != x)
+	tail = cell_list_alloc (cells, tail, x);
+    return cells->cursor = tail;
+
+}
+
+/* Find two cells at x1 and x2.	 This is exactly equivalent
+ * to
+ *
+ *   pair.cell1 = cell_list_find(cells, x1);
+ *   pair.cell2 = cell_list_find(cells, x2);
+ *
+ * except with less function call overhead. */
+inline static struct cell_pair
+cell_list_find_pair(struct cell_list *cells, int x1, int x2)
+{
+    struct cell_pair pair;
+
+    pair.cell1 = cells->cursor;
+    while (1) {
+	UNROLL3({
+	    if (pair.cell1->next->x > x1)
+		break;
+	    pair.cell1 = pair.cell1->next;
+	});
+    }
+    if (pair.cell1->x != x1) {
+	struct cell *cell = pool_alloc (cells->cell_pool.base,
+					sizeof (struct cell));
+	cell->x = x1;
+	cell->uncovered_area = 0;
+	cell->covered_height = 0;
+	cell->clipped_height = 0;
+	cell->next = pair.cell1->next;
+	pair.cell1->next = cell;
+	pair.cell1 = cell;
+    }
+
+    pair.cell2 = pair.cell1;
+    while (1) {
+	UNROLL3({
+	    if (pair.cell2->next->x > x2)
+		break;
+	    pair.cell2 = pair.cell2->next;
+	});
+    }
+    if (pair.cell2->x != x2) {
+	struct cell *cell = pool_alloc (cells->cell_pool.base,
+					sizeof (struct cell));
+	cell->uncovered_area = 0;
+	cell->covered_height = 0;
+	cell->clipped_height = 0;
+	cell->x = x2;
+	cell->next = pair.cell2->next;
+	pair.cell2->next = cell;
+	pair.cell2 = cell;
+    }
+
+    cells->cursor = pair.cell2;
+    return pair;
+}
+
+/* Add a subpixel span covering [x1, x2) to the coverage cells. */
+inline static void
+cell_list_add_subspan(struct cell_list *cells,
+		      grid_scaled_x_t x1,
+		      grid_scaled_x_t x2)
+{
+    int ix1, fx1;
+    int ix2, fx2;
+
+    GRID_X_TO_INT_FRAC(x1, ix1, fx1);
+    GRID_X_TO_INT_FRAC(x2, ix2, fx2);
+
+    if (ix1 != ix2) {
+	struct cell_pair p;
+	p = cell_list_find_pair(cells, ix1, ix2);
+	p.cell1->uncovered_area += 2*fx1;
+	++p.cell1->covered_height;
+	p.cell2->uncovered_area -= 2*fx2;
+	--p.cell2->covered_height;
+    } else {
+	struct cell *cell = cell_list_find(cells, ix1);
+	cell->uncovered_area += 2*(fx1-fx2);
+    }
+}
+
+/* Adds the analytical coverage of an edge crossing the current pixel
+ * row to the coverage cells and advances the edge's x position to the
+ * following row.
+ *
+ * This function is only called when we know that during this pixel row:
+ *
+ * 1) The relative order of all edges on the active list doesn't
+ * change.  In particular, no edges intersect within this row to pixel
+ * precision.
+ *
+ * 2) No new edges start in this row.
+ *
+ * 3) No existing edges end mid-row.
+ *
+ * This function depends on being called with all edges from the
+ * active list in the order they appear on the list (i.e. with
+ * non-decreasing x-coordinate.)  */
+static void
+cell_list_render_edge(
+    struct cell_list *cells,
+    struct edge *edge,
+    int sign)
+{
+    grid_scaled_y_t y1, y2, dy;
+    grid_scaled_x_t dx;
+    int ix1, ix2;
+    grid_scaled_x_t fx1, fx2;
+
+    struct quorem x1 = edge->x;
+    struct quorem x2 = x1;
+
+    if (! edge->vertical) {
+	x2.quo += edge->dxdy_full.quo;
+	x2.rem += edge->dxdy_full.rem;
+	if (x2.rem >= 0) {
+	    ++x2.quo;
+	    x2.rem -= edge->dy;
+	}
+
+	edge->x = x2;
+    }
+
+    GRID_X_TO_INT_FRAC(x1.quo, ix1, fx1);
+    GRID_X_TO_INT_FRAC(x2.quo, ix2, fx2);
+
+    /* Edge is entirely within a column? */
+    if (ix1 == ix2) {
+	/* We always know that ix1 is >= the cell list cursor in this
+	 * case due to the no-intersections precondition.  */
+	struct cell *cell = cell_list_find(cells, ix1);
+	cell->covered_height += sign*GRID_Y;
+	cell->uncovered_area += sign*(fx1 + fx2)*GRID_Y;
+	return;
+    }
+
+    /* Orient the edge left-to-right. */
+    dx = x2.quo - x1.quo;
+    if (dx >= 0) {
+	y1 = 0;
+	y2 = GRID_Y;
+    } else {
+	int tmp;
+	tmp = ix1; ix1 = ix2; ix2 = tmp;
+	tmp = fx1; fx1 = fx2; fx2 = tmp;
+	dx = -dx;
+	sign = -sign;
+	y1 = GRID_Y;
+	y2 = 0;
+    }
+    dy = y2 - y1;
+
+    /* Add coverage for all pixels [ix1,ix2] on this row crossed
+     * by the edge. */
+    {
+	struct cell_pair pair;
+	struct quorem y = floored_divrem((GRID_X - fx1)*dy, dx);
+
+	/* When rendering a previous edge on the active list we may
+	 * advance the cell list cursor past the leftmost pixel of the
+	 * current edge even though the two edges don't intersect.
+	 * e.g. consider two edges going down and rightwards:
+	 *
+	 *  --\_+---\_+-----+-----+----
+	 *      \_    \_    |     |
+	 *      | \_  | \_  |     |
+	 *      |   \_|   \_|     |
+	 *      |     \_    \_    |
+	 *  ----+-----+-\---+-\---+----
+	 *
+	 * The left edge touches cells past the starting cell of the
+	 * right edge.  Fortunately such cases are rare.
+	 *
+	 * The rewinding is never necessary if the current edge stays
+	 * within a single column because we've checked before calling
+	 * this function that the active list order won't change. */
+	cell_list_maybe_rewind(cells, ix1);
+
+	pair = cell_list_find_pair(cells, ix1, ix1+1);
+	pair.cell1->uncovered_area += sign*y.quo*(GRID_X + fx1);
+	pair.cell1->covered_height += sign*y.quo;
+	y.quo += y1;
+
+	if (ix1+1 < ix2) {
+	    struct quorem dydx_full = floored_divrem(GRID_X*dy, dx);
+	    struct cell *cell = pair.cell2;
+
+	    ++ix1;
+	    do {
+		grid_scaled_y_t y_skip = dydx_full.quo;
+		y.rem += dydx_full.rem;
+		if (y.rem >= dx) {
+		    ++y_skip;
+		    y.rem -= dx;
+		}
+
+		y.quo += y_skip;
+
+		y_skip *= sign;
+		cell->uncovered_area += y_skip*GRID_X;
+		cell->covered_height += y_skip;
+
+		++ix1;
+		cell = cell_list_find(cells, ix1);
+	    } while (ix1 != ix2);
+
+	    pair.cell2 = cell;
+	}
+	pair.cell2->uncovered_area += sign*(y2 - y.quo)*fx2;
+	pair.cell2->covered_height += sign*(y2 - y.quo);
+    }
+}
+
+static void
+polygon_init (struct polygon *polygon, jmp_buf *jmp)
+{
+    polygon->ymin = polygon->ymax = 0;
+    polygon->y_buckets = polygon->y_buckets_embedded;
+    pool_init (polygon->edge_pool.base, jmp,
+	       8192 - sizeof (struct _pool_chunk),
+	       sizeof (polygon->edge_pool.embedded));
+}
+
+static void
+polygon_fini (struct polygon *polygon)
+{
+    if (polygon->y_buckets != polygon->y_buckets_embedded)
+	free (polygon->y_buckets);
+
+    pool_fini (polygon->edge_pool.base);
+}
+
+/* Empties the polygon of all edges. The polygon is then prepared to
+ * receive new edges and clip them to the vertical range
+ * [ymin,ymax). */
+static cairo_status_t
+polygon_reset (struct polygon *polygon,
+	       grid_scaled_y_t ymin,
+	       grid_scaled_y_t ymax)
+{
+    unsigned h = ymax - ymin;
+    unsigned num_buckets = EDGE_Y_BUCKET_INDEX(ymax + EDGE_Y_BUCKET_HEIGHT-1,
+					       ymin);
+
+    pool_reset(polygon->edge_pool.base);
+
+    if (unlikely (h > 0x7FFFFFFFU - EDGE_Y_BUCKET_HEIGHT))
+	goto bail_no_mem; /* even if you could, you wouldn't want to. */
+
+    if (polygon->y_buckets != polygon->y_buckets_embedded)
+	free (polygon->y_buckets);
+
+    polygon->y_buckets =  polygon->y_buckets_embedded;
+    if (num_buckets > ARRAY_LENGTH (polygon->y_buckets_embedded)) {
+	polygon->y_buckets = _cairo_malloc_ab (num_buckets,
+					       sizeof (struct edge *));
+	if (unlikely (NULL == polygon->y_buckets))
+	    goto bail_no_mem;
+    }
+    memset (polygon->y_buckets, 0, num_buckets * sizeof (struct edge *));
+
+    polygon->ymin = ymin;
+    polygon->ymax = ymax;
+    return CAIRO_STATUS_SUCCESS;
+
+ bail_no_mem:
+    polygon->ymin = 0;
+    polygon->ymax = 0;
+    return CAIRO_STATUS_NO_MEMORY;
+}
+
+static void
+_polygon_insert_edge_into_its_y_bucket(
+    struct polygon *polygon,
+    struct edge *e)
+{
+    unsigned ix = EDGE_Y_BUCKET_INDEX(e->ytop, polygon->ymin);
+    struct edge **ptail = &polygon->y_buckets[ix];
+    e->next = *ptail;
+    *ptail = e;
+}
+
+inline static void
+polygon_add_edge (struct polygon *polygon,
+		  const cairo_edge_t *edge,
+		  int clip)
+{
+    struct edge *e;
+    grid_scaled_x_t dx;
+    grid_scaled_y_t dy;
+    grid_scaled_y_t ytop, ybot;
+    grid_scaled_y_t ymin = polygon->ymin;
+    grid_scaled_y_t ymax = polygon->ymax;
+
+    assert (edge->bottom > edge->top);
+
+    if (unlikely (edge->top >= ymax || edge->bottom <= ymin))
+	return;
+
+    e = pool_alloc (polygon->edge_pool.base, sizeof (struct edge));
+
+    dx = edge->line.p2.x - edge->line.p1.x;
+    dy = edge->line.p2.y - edge->line.p1.y;
+    e->dy = dy;
+    e->dir = edge->dir;
+    e->clip = clip;
+
+    ytop = edge->top >= ymin ? edge->top : ymin;
+    ybot = edge->bottom <= ymax ? edge->bottom : ymax;
+    e->ytop = ytop;
+    e->height_left = ybot - ytop;
+
+    if (dx == 0) {
+	e->vertical = TRUE;
+	e->x.quo = edge->line.p1.x;
+	e->x.rem = 0;
+	e->dxdy.quo = 0;
+	e->dxdy.rem = 0;
+	e->dxdy_full.quo = 0;
+	e->dxdy_full.rem = 0;
+    } else {
+	e->vertical = FALSE;
+	e->dxdy = floored_divrem (dx, dy);
+	if (ytop == edge->line.p1.y) {
+	    e->x.quo = edge->line.p1.x;
+	    e->x.rem = 0;
+	} else {
+	    e->x = floored_muldivrem (ytop - edge->line.p1.y, dx, dy);
+	    e->x.quo += edge->line.p1.x;
+	}
+
+	if (e->height_left >= GRID_Y) {
+	    e->dxdy_full = floored_muldivrem (GRID_Y, dx, dy);
+	} else {
+	    e->dxdy_full.quo = 0;
+	    e->dxdy_full.rem = 0;
+	}
+    }
+
+    _polygon_insert_edge_into_its_y_bucket (polygon, e);
+
+    e->x.rem -= dy;		/* Bias the remainder for faster
+				 * edge advancement. */
+}
+
+static void
+active_list_reset (struct active_list *active)
+{
+    active->head = NULL;
+    active->min_height = 0;
+}
+
+static void
+active_list_init(struct active_list *active)
+{
+    active_list_reset(active);
+}
+
+/*
+ * Merge two sorted edge lists.
+ * Input:
+ *  - head_a: The head of the first list.
+ *  - head_b: The head of the second list; head_b cannot be NULL.
+ * Output:
+ * Returns the head of the merged list.
+ *
+ * Implementation notes:
+ * To make it fast (in particular, to reduce to an insertion sort whenever
+ * one of the two input lists only has a single element) we iterate through
+ * a list until its head becomes greater than the head of the other list,
+ * then we switch their roles. As soon as one of the two lists is empty, we
+ * just attach the other one to the current list and exit.
+ * Writes to memory are only needed to "switch" lists (as it also requires
+ * attaching to the output list the list which we will be iterating next) and
+ * to attach the last non-empty list.
+ */
+static struct edge *
+merge_sorted_edges (struct edge *head_a, struct edge *head_b)
+{
+    struct edge *head, **next;
+    int32_t x;
+
+    if (head_a == NULL)
+	return head_b;
+
+    next = &head;
+    if (head_a->x.quo <= head_b->x.quo) {
+	head = head_a;
+    } else {
+	head = head_b;
+	goto start_with_b;
+    }
+
+    do {
+	x = head_b->x.quo;
+	while (head_a != NULL && head_a->x.quo <= x) {
+	    next = &head_a->next;
+	    head_a = head_a->next;
+	}
+
+	*next = head_b;
+	if (head_a == NULL)
+	    return head;
+
+start_with_b:
+	x = head_a->x.quo;
+	while (head_b != NULL && head_b->x.quo <= x) {
+	    next = &head_b->next;
+	    head_b = head_b->next;
+	}
+
+	*next = head_a;
+	if (head_b == NULL)
+	    return head;
+    } while (1);
+}
+
+/*
+ * Sort (part of) a list.
+ * Input:
+ *  - list: The list to be sorted; list cannot be NULL.
+ *  - limit: Recursion limit.
+ * Output:
+ *  - head_out: The head of the sorted list containing the first 2^(level+1) elements of the
+ *              input list; if the input list has fewer elements, head_out be a sorted list
+ *              containing all the elements of the input list.
+ * Returns the head of the list of unprocessed elements (NULL if the sorted list contains
+ * all the elements of the input list).
+ *
+ * Implementation notes:
+ * Special case single element list, unroll/inline the sorting of the first two elements.
+ * Some tail recursion is used since we iterate on the bottom-up solution of the problem
+ * (we start with a small sorted list and keep merging other lists of the same size to it).
+ */
+static struct edge *
+sort_edges (struct edge  *list,
+	    unsigned int  level,
+	    struct edge **head_out)
+{
+    struct edge *head_other, *remaining;
+    unsigned int i;
+
+    head_other = list->next;
+
+    /* Single element list -> return */
+    if (head_other == NULL) {
+	*head_out = list;
+	return NULL;
+    }
+
+    /* Unroll the first iteration of the following loop (halves the number of calls to merge_sorted_edges):
+     *  - Initialize remaining to be the list containing the elements after the second in the input list.
+     *  - Initialize *head_out to be the sorted list containing the first two element.
+     */
+    remaining = head_other->next;
+    if (list->x.quo <= head_other->x.quo) {
+	*head_out = list;
+	/* list->next = head_other; */ /* The input list is already like this. */
+	head_other->next = NULL;
+    } else {
+	*head_out = head_other;
+	head_other->next = list;
+	list->next = NULL;
+    }
+
+    for (i = 0; i < level && remaining; i++) {
+	/* Extract a sorted list of the same size as *head_out
+	 * (2^(i+1) elements) from the list of remaining elements. */
+	remaining = sort_edges (remaining, i, &head_other);
+	*head_out = merge_sorted_edges (*head_out, head_other);
+    }
+
+    /* *head_out now contains (at most) 2^(level+1) elements. */
+
+    return remaining;
+}
+
+/* Test if the edges on the active list can be safely advanced by a
+ * full row without intersections or any edges ending. */
+inline static int
+active_list_can_step_full_row (struct active_list *active)
+{
+    const struct edge *e;
+    int prev_x = INT_MIN;
+
+    /* Recomputes the minimum height of all edges on the active
+     * list if we have been dropping edges. */
+    if (active->min_height <= 0) {
+	int min_height = INT_MAX;
+
+	e = active->head;
+	while (NULL != e) {
+	    if (e->height_left < min_height)
+		min_height = e->height_left;
+	    e = e->next;
+	}
+
+	active->min_height = min_height;
+    }
+
+    if (active->min_height < GRID_Y)
+	return 0;
+
+    /* Check for intersections as no edges end during the next row. */
+    e = active->head;
+    while (NULL != e) {
+	struct quorem x = e->x;
+
+	if (! e->vertical) {
+	    x.quo += e->dxdy_full.quo;
+	    x.rem += e->dxdy_full.rem;
+	    if (x.rem >= 0)
+		++x.quo;
+	}
+
+	if (x.quo <= prev_x)
+	    return 0;
+
+	prev_x = x.quo;
+	e = e->next;
+    }
+
+    return 1;
+}
+
+/* Merges edges on the given subpixel row from the polygon to the
+ * active_list. */
+inline static void
+active_list_merge_edges_from_polygon(struct active_list *active,
+				     struct edge **ptail,
+				     grid_scaled_y_t y,
+				     struct polygon *polygon)
+{
+    /* Split off the edges on the current subrow and merge them into
+     * the active list. */
+    int min_height = active->min_height;
+    struct edge *subrow_edges = NULL;
+    struct edge *tail = *ptail;
+
+    do {
+	struct edge *next = tail->next;
+
+	if (y == tail->ytop) {
+	    tail->next = subrow_edges;
+	    subrow_edges = tail;
+
+	    if (tail->height_left < min_height)
+		min_height = tail->height_left;
+
+	    *ptail = next;
+	} else
+	    ptail = &tail->next;
+
+	tail = next;
+    } while (tail);
+
+    if (subrow_edges) {
+	sort_edges (subrow_edges, UINT_MAX, &subrow_edges);
+	active->head = merge_sorted_edges (active->head, subrow_edges);
+	active->min_height = min_height;
+    }
+}
+
+/* Advance the edges on the active list by one subsample row by
+ * updating their x positions.  Drop edges from the list that end. */
+inline static void
+active_list_substep_edges(struct active_list *active)
+{
+    struct edge **cursor = &active->head;
+    grid_scaled_x_t prev_x = INT_MIN;
+    struct edge *unsorted = NULL;
+    struct edge *edge = *cursor;
+
+    do {
+	UNROLL3({
+	    struct edge *next;
+
+	    if (NULL == edge)
+		break;
+
+	    next = edge->next;
+	    if (--edge->height_left) {
+		edge->x.quo += edge->dxdy.quo;
+		edge->x.rem += edge->dxdy.rem;
+		if (edge->x.rem >= 0) {
+		    ++edge->x.quo;
+		    edge->x.rem -= edge->dy;
+		}
+
+		if (edge->x.quo < prev_x) {
+		    *cursor = next;
+		    edge->next = unsorted;
+		    unsorted = edge;
+		} else {
+		    prev_x = edge->x.quo;
+		    cursor = &edge->next;
+		}
+	    } else {
+		 *cursor = next;
+	    }
+	    edge = next;
+	})
+    } while (1);
+
+    if (unsorted) {
+	sort_edges (unsorted, UINT_MAX, &unsorted);
+	active->head = merge_sorted_edges (active->head, unsorted);
+    }
+}
+
+inline static void
+apply_nonzero_fill_rule_for_subrow (struct active_list *active,
+				    struct cell_list *coverages)
+{
+    struct edge *edge = active->head;
+    int winding = 0;
+    int xstart;
+    int xend;
+
+    cell_list_rewind (coverages);
+
+    while (NULL != edge) {
+	xstart = edge->x.quo;
+	winding = edge->dir;
+	while (1) {
+	    edge = edge->next;
+	    if (NULL == edge) {
+		ASSERT_NOT_REACHED;
+		return;
+	    }
+
+	    winding += edge->dir;
+	    if (0 == winding) {
+		if (edge->next == NULL || edge->next->x.quo != edge->x.quo)
+		    break;
+	    }
+	}
+
+	xend = edge->x.quo;
+	cell_list_add_subspan (coverages, xstart, xend);
+
+	edge = edge->next;
+    }
+}
+
+static void
+apply_evenodd_fill_rule_for_subrow (struct active_list *active,
+				    struct cell_list *coverages)
+{
+    struct edge *edge = active->head;
+    int xstart;
+    int xend;
+
+    cell_list_rewind (coverages);
+
+    while (NULL != edge) {
+	xstart = edge->x.quo;
+
+	while (1) {
+	    edge = edge->next;
+	    if (NULL == edge) {
+		ASSERT_NOT_REACHED;
+		return;
+	    }
+
+	    if (edge->next == NULL || edge->next->x.quo != edge->x.quo)
+		break;
+
+	    edge = edge->next;
+	}
+
+	xend = edge->x.quo;
+	cell_list_add_subspan (coverages, xstart, xend);
+
+	edge = edge->next;
+    }
+}
+
+static void
+apply_nonzero_fill_rule_and_step_edges (struct active_list *active,
+					struct cell_list *coverages)
+{
+    struct edge **cursor = &active->head;
+    struct edge *left_edge;
+
+    left_edge = *cursor;
+    while (NULL != left_edge) {
+	struct edge *right_edge;
+	int winding = left_edge->dir;
+
+	left_edge->height_left -= GRID_Y;
+	if (left_edge->height_left)
+	    cursor = &left_edge->next;
+	else
+	    *cursor = left_edge->next;
+
+	while (1) {
+	    right_edge = *cursor;
+	    if (NULL == right_edge) {
+		cell_list_render_edge (coverages, left_edge, +1);
+		return;
+	    }
+
+	    right_edge->height_left -= GRID_Y;
+	    if (right_edge->height_left)
+		cursor = &right_edge->next;
+	    else
+		*cursor = right_edge->next;
+
+	    winding += right_edge->dir;
+	    if (0 == winding) {
+		if (right_edge->next == NULL ||
+		    right_edge->next->x.quo != right_edge->x.quo)
+		{
+		    break;
+		}
+	    }
+
+	    if (! right_edge->vertical) {
+		right_edge->x.quo += right_edge->dxdy_full.quo;
+		right_edge->x.rem += right_edge->dxdy_full.rem;
+		if (right_edge->x.rem >= 0) {
+		    ++right_edge->x.quo;
+		    right_edge->x.rem -= right_edge->dy;
+		}
+	    }
+	}
+
+	cell_list_render_edge (coverages, left_edge, +1);
+	cell_list_render_edge (coverages, right_edge, -1);
+
+	left_edge = *cursor;
+    }
+}
+
+static void
+apply_evenodd_fill_rule_and_step_edges (struct active_list *active,
+					struct cell_list *coverages)
+{
+    struct edge **cursor = &active->head;
+    struct edge *left_edge;
+
+    left_edge = *cursor;
+    while (NULL != left_edge) {
+	struct edge *right_edge;
+
+	left_edge->height_left -= GRID_Y;
+	if (left_edge->height_left)
+	    cursor = &left_edge->next;
+	else
+	    *cursor = left_edge->next;
+
+	while (1) {
+	    right_edge = *cursor;
+	    if (NULL == right_edge) {
+		cell_list_render_edge (coverages, left_edge, +1);
+		return;
+	    }
+
+	    right_edge->height_left -= GRID_Y;
+	    if (right_edge->height_left)
+		cursor = &right_edge->next;
+	    else
+		*cursor = right_edge->next;
+
+	    if (right_edge->next == NULL ||
+		right_edge->next->x.quo != right_edge->x.quo)
+	    {
+		break;
+	    }
+
+	    if (! right_edge->vertical) {
+		right_edge->x.quo += right_edge->dxdy_full.quo;
+		right_edge->x.rem += right_edge->dxdy_full.rem;
+		if (right_edge->x.rem >= 0) {
+		    ++right_edge->x.quo;
+		    right_edge->x.rem -= right_edge->dy;
+		}
+	    }
+	}
+
+	cell_list_render_edge (coverages, left_edge, +1);
+	cell_list_render_edge (coverages, right_edge, -1);
+
+	left_edge = *cursor;
+    }
+}
+
+static void
+_glitter_scan_converter_init(glitter_scan_converter_t *converter, jmp_buf *jmp)
+{
+    polygon_init(converter->polygon, jmp);
+    active_list_init(converter->active);
+    cell_list_init(converter->coverages, jmp);
+    converter->ymin=0;
+    converter->ymax=0;
+}
+
+static void
+_glitter_scan_converter_fini(glitter_scan_converter_t *converter)
+{
+    polygon_fini(converter->polygon);
+    cell_list_fini(converter->coverages);
+    converter->ymin=0;
+    converter->ymax=0;
+}
+
+static grid_scaled_t
+int_to_grid_scaled(int i, int scale)
+{
+    /* Clamp to max/min representable scaled number. */
+    if (i >= 0) {
+	if (i >= INT_MAX/scale)
+	    i = INT_MAX/scale;
+    }
+    else {
+	if (i <= INT_MIN/scale)
+	    i = INT_MIN/scale;
+    }
+    return i*scale;
+}
+
+#define int_to_grid_scaled_x(x) int_to_grid_scaled((x), GRID_X)
+#define int_to_grid_scaled_y(x) int_to_grid_scaled((x), GRID_Y)
+
+static cairo_status_t
+glitter_scan_converter_reset(glitter_scan_converter_t *converter,
+			     int ymin, int ymax)
+{
+    cairo_status_t status;
+
+    converter->ymin = 0;
+    converter->ymax = 0;
+
+    ymin = int_to_grid_scaled_y(ymin);
+    ymax = int_to_grid_scaled_y(ymax);
+
+    active_list_reset(converter->active);
+    cell_list_reset(converter->coverages);
+    status = polygon_reset(converter->polygon, ymin, ymax);
+    if (status)
+	return status;
+
+    converter->ymin = ymin;
+    converter->ymax = ymax;
+    return CAIRO_STATUS_SUCCESS;
+}
+
+/* INPUT_TO_GRID_X/Y (in_coord, out_grid_scaled, grid_scale)
+ *   These macros convert an input coordinate in the client's
+ *   device space to the rasterisation grid.
+ */
+/* Gah.. this bit of ugly defines INPUT_TO_GRID_X/Y so as to use
+ * shifts if possible, and something saneish if not.
+ */
+#if !defined(INPUT_TO_GRID_Y) && defined(GRID_Y_BITS) && GRID_Y_BITS <= GLITTER_INPUT_BITS
+#  define INPUT_TO_GRID_Y(in, out) (out) = (in) >> (GLITTER_INPUT_BITS - GRID_Y_BITS)
+#else
+#  define INPUT_TO_GRID_Y(in, out) INPUT_TO_GRID_general(in, out, GRID_Y)
+#endif
+
+#if !defined(INPUT_TO_GRID_X) && defined(GRID_X_BITS) && GRID_X_BITS <= GLITTER_INPUT_BITS
+#  define INPUT_TO_GRID_X(in, out) (out) = (in) >> (GLITTER_INPUT_BITS - GRID_X_BITS)
+#else
+#  define INPUT_TO_GRID_X(in, out) INPUT_TO_GRID_general(in, out, GRID_X)
+#endif
+
+#define INPUT_TO_GRID_general(in, out, grid_scale) do {		\
+	long long tmp__ = (long long)(grid_scale) * (in);	\
+	tmp__ >>= GLITTER_INPUT_BITS;				\
+	(out) = tmp__;						\
+} while (0)
+
+static void
+glitter_scan_converter_add_edge (glitter_scan_converter_t *converter,
+				 const cairo_edge_t *edge,
+				 int clip)
+{
+    cairo_edge_t e;
+
+    INPUT_TO_GRID_Y (edge->top, e.top);
+    INPUT_TO_GRID_Y (edge->bottom, e.bottom);
+    if (e.top >= e.bottom)
+	return;
+
+    /* XXX: possible overflows if GRID_X/Y > 2**GLITTER_INPUT_BITS */
+    INPUT_TO_GRID_Y (edge->line.p1.y, e.line.p1.y);
+    INPUT_TO_GRID_Y (edge->line.p2.y, e.line.p2.y);
+    if (e.line.p1.y == e.line.p2.y)
+	return;
+
+    INPUT_TO_GRID_X (edge->line.p1.x, e.line.p1.x);
+    INPUT_TO_GRID_X (edge->line.p2.x, e.line.p2.x);
+
+    e.dir = edge->dir;
+
+    polygon_add_edge (converter->polygon, &e, clip);
+}
+
+static cairo_bool_t
+active_list_is_vertical (struct active_list *active)
+{
+    struct edge *e;
+
+    for (e = active->head; e != NULL; e = e->next) {
+	if (! e->vertical)
+	    return FALSE;
+    }
+
+    return TRUE;
+}
+
+static void
+step_edges (struct active_list *active, int count)
+{
+    struct edge **cursor = &active->head;
+    struct edge *edge;
+
+    for (edge = *cursor; edge != NULL; edge = *cursor) {
+	edge->height_left -= GRID_Y * count;
+	if (edge->height_left)
+	    cursor = &edge->next;
+	else
+	    *cursor = edge->next;
+    }
+}
+
+static cairo_status_t
+blit_coverages (struct cell_list *cells,
+		cairo_span_renderer_t *renderer,
+		struct pool *span_pool,
+		int y, int height)
+{
+    struct cell *cell = cells->head.next;
+    int prev_x = -1;
+    int cover = 0, last_cover = 0;
+    int clip = 0;
+    cairo_half_open_span_t *spans;
+    unsigned num_spans;
+
+    assert (cell != &cells->tail);
+
+    /* Count number of cells remaining. */
+    {
+	struct cell *next = cell;
+	num_spans = 2;
+	while (next->next) {
+	    next = next->next;
+	    ++num_spans;
+	}
+	num_spans = 2*num_spans;
+    }
+
+    /* Allocate enough spans for the row. */
+    pool_reset (span_pool);
+    spans = pool_alloc (span_pool, sizeof(spans[0])*num_spans);
+    num_spans = 0;
+
+    /* Form the spans from the coverages and areas. */
+    for (; cell->next; cell = cell->next) {
+	int x = cell->x;
+	int area;
+
+	if (x > prev_x && cover != last_cover) {
+	    spans[num_spans].x = prev_x;
+	    spans[num_spans].coverage = GRID_AREA_TO_ALPHA (cover);
+	    spans[num_spans].is_clipped = 0;
+	    last_cover = cover;
+	    ++num_spans;
+	}
+
+	cover += cell->covered_height*GRID_X*2;
+	clip += cell->covered_height*GRID_X*2;
+	area = cover - cell->uncovered_area;
+
+	if (area != last_cover) {
+	    spans[num_spans].x = x;
+	    spans[num_spans].is_clipped = 0;
+	    spans[num_spans].coverage = GRID_AREA_TO_ALPHA (area);
+	    last_cover = area;
+	    ++num_spans;
+	}
+
+	prev_x = x+1;
+    }
+
+    /* Dump them into the renderer. */
+    return renderer->render_rows (renderer, y, height, spans, num_spans);
+}
+
+static void
+glitter_scan_converter_render(glitter_scan_converter_t *converter,
+			      int nonzero_fill,
+			      cairo_span_renderer_t *span_renderer,
+			      struct pool *span_pool)
+{
+    int i, j;
+    int ymax_i = converter->ymax / GRID_Y;
+    int ymin_i = converter->ymin / GRID_Y;
+    int h = ymax_i - ymin_i;
+    struct polygon *polygon = converter->polygon;
+    struct cell_list *coverages = converter->coverages;
+    struct active_list *active = converter->active;
+
+    /* Render each pixel row. */
+    for (i = 0; i < h; i = j) {
+	int do_full_step = 0;
+
+	j = i + 1;
+
+	/* Determine if we can ignore this row or use the full pixel
+	 * stepper. */
+	if (GRID_Y == EDGE_Y_BUCKET_HEIGHT && ! polygon->y_buckets[i]) {
+	    if (! active->head) {
+		for (; j < h && ! polygon->y_buckets[j]; j++)
+		    ;
+		continue;
+	    }
+
+	    do_full_step = active_list_can_step_full_row (active);
+	}
+
+	if (do_full_step) {
+	    /* Step by a full pixel row's worth. */
+	    if (nonzero_fill)
+		apply_nonzero_fill_rule_and_step_edges (active, coverages);
+	    else
+		apply_evenodd_fill_rule_and_step_edges (active, coverages);
+
+	    if (active_list_is_vertical (active)) {
+		while (j < h &&
+		       polygon->y_buckets[j] == NULL &&
+		       active->min_height >= 2*GRID_Y)
+		{
+		    active->min_height -= GRID_Y;
+		    j++;
+		}
+		if (j != i + 1)
+		    step_edges (active, j - (i + 1));
+	    }
+	} else {
+	    grid_scaled_y_t suby;
+
+	    /* Subsample this row. */
+	    for (suby = 0; suby < GRID_Y; suby++) {
+		grid_scaled_y_t y = (i+ymin_i)*GRID_Y + suby;
+
+		if (polygon->y_buckets[i]) {
+		    active_list_merge_edges_from_polygon (active,
+							  &polygon->y_buckets[i], y,
+							  polygon);
+		}
+
+		if (nonzero_fill)
+		    apply_nonzero_fill_rule_for_subrow (active, coverages);
+		else
+		    apply_evenodd_fill_rule_for_subrow (active, coverages);
+
+		active_list_substep_edges(active);
+	    }
+	}
+
+	blit_coverages (coverages, span_renderer, span_pool, i+ymin_i, j -i);
+	cell_list_reset (coverages);
+
+	if (! active->head)
+	    active->min_height = INT_MAX;
+	else
+	    active->min_height -= GRID_Y;
+    }
+}
+
+struct _cairo_clip_tor_scan_converter {
+    cairo_scan_converter_t base;
+
+    glitter_scan_converter_t converter[1];
+    cairo_fill_rule_t fill_rule;
+
+    cairo_fill_rule_t clip_fill_rule;
+    cairo_antialias_t clip_antialias;
+
+    jmp_buf jmp;
+
+    struct {
+	struct pool base[1];
+	cairo_half_open_span_t embedded[32];
+    } span_pool;
+};
+
+typedef struct _cairo_clip_tor_scan_converter cairo_clip_tor_scan_converter_t;
+
+static void
+_cairo_clip_tor_scan_converter_destroy (void *converter)
+{
+    cairo_clip_tor_scan_converter_t *self = converter;
+    if (self == NULL) {
+	return;
+    }
+    _glitter_scan_converter_fini (self->converter);
+    pool_fini (self->span_pool.base);
+    free(self);
+}
+
+static cairo_status_t
+_cairo_clip_tor_scan_converter_generate (void			*converter,
+				    cairo_span_renderer_t	*renderer)
+{
+    cairo_clip_tor_scan_converter_t *self = converter;
+    cairo_status_t status;
+
+    if ((status = setjmp (self->jmp)))
+	return _cairo_scan_converter_set_error (self, _cairo_error (status));
+
+    glitter_scan_converter_render (self->converter,
+				   self->fill_rule == CAIRO_FILL_RULE_WINDING,
+				   renderer,
+				   self->span_pool.base);
+    return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_scan_converter_t *
+_cairo_clip_tor_scan_converter_create (cairo_clip_t *clip,
+				       cairo_polygon_t *polygon,
+				       cairo_fill_rule_t fill_rule)
+{
+    cairo_clip_tor_scan_converter_t *self;
+    cairo_polygon_t clipper;
+    cairo_status_t status;
+    int i;
+
+    self = calloc (1, sizeof(struct _cairo_clip_tor_scan_converter));
+    if (unlikely (self == NULL)) {
+	status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+	goto bail_nomem;
+    }
+
+    self->base.destroy = _cairo_clip_tor_scan_converter_destroy;
+    self->base.generate = _cairo_clip_tor_scan_converter_generate;
+
+    pool_init (self->span_pool.base, &self->jmp,
+	       250 * sizeof(self->span_pool.embedded[0]),
+	       sizeof(self->span_pool.embedded));
+
+    _glitter_scan_converter_init (self->converter, &self->jmp);
+    status = glitter_scan_converter_reset (self->converter,
+					   clip->extents.y,
+					   clip->extents.y + clip->extents.height);
+    if (unlikely (status))
+	goto bail;
+
+    self->fill_rule = fill_rule;
+
+    for (i = 0; i < polygon->num_edges; i++)
+	 glitter_scan_converter_add_edge (self->converter,
+					  &polygon->edges[i],
+					  FALSE);
+
+    status = _cairo_clip_get_polygon (clip,
+				      &clipper,
+				      &self->clip_fill_rule,
+				      &self->clip_antialias);
+    if (unlikely (status))
+	goto bail;
+
+    for (i = 0; i < clipper.num_edges; i++)
+	 glitter_scan_converter_add_edge (self->converter,
+					  &clipper.edges[i],
+					  TRUE);
+    _cairo_polygon_fini (&clipper);
+
+    return &self->base;
+
+ bail:
+    self->base.destroy(&self->base);
+ bail_nomem:
+    return _cairo_scan_converter_create_in_error (status);
+}
+
diff --git a/src/cairo-image-surface.c b/src/cairo-image-surface.c
index c2afbab..508d258 100644
--- a/src/cairo-image-surface.c
+++ b/src/cairo-image-surface.c
@@ -1487,6 +1487,13 @@ _pixman_image_for_surface (const cairo_surface_pattern_t *pattern,
 	if (unlikely (status))
 	    return NULL;
 
+	if (sample.x >= 0 && sample.y >= 0 &&
+	    sample.x + sample.width  <= image->width &&
+	    sample.y + sample.height <= image->height)
+	{
+	    extend = CAIRO_EXTEND_NONE;
+	}
+
 	if (sample.width == 1 && sample.height == 1) {
 	    if (sample.x < 0 ||
 		sample.y < 0 ||
@@ -1910,32 +1917,35 @@ reduce_alpha_op (cairo_image_surface_t *dst,
 /* low level compositor */
 typedef cairo_status_t
 (*image_draw_func_t) (void				*closure,
-		      pixman_image_t			*dst,
-		      pixman_format_code_t		 dst_format,
+		      cairo_image_surface_t             *dst,
 		      cairo_operator_t			 op,
 		      const cairo_pattern_t		*src,
+		      double                             opacity,
 		      int				 dst_x,
 		      int				 dst_y,
-		      cairo_matrix_t			*dst_device_transform,
 		      const cairo_composite_rectangles_t	*extents);
 
 static pixman_image_t *
 _create_composite_mask_pattern (cairo_composite_rectangles_t *extents,
 				image_draw_func_t              draw_func,
 				void                          *draw_closure,
+				double                         opacity,
 				cairo_image_surface_t         *dst)
 {
     cairo_region_t *clip_region = _cairo_clip_get_region (extents->clip);
     cairo_bool_t need_clip_surface = ! _cairo_clip_is_region (extents->clip);
-    pixman_image_t *mask;
+    cairo_image_surface_t *mask;
+    pixman_image_t *image;
     cairo_status_t status;
 
     if (clip_region != NULL && cairo_region_num_rectangles (clip_region) == 1)
 	clip_region = NULL;
 
-    mask = pixman_image_create_bits (PIXMAN_a8, extents->bounded.width, extents->bounded.height,
-				     NULL, 0);
-    if (unlikely (mask == NULL))
+    mask = (cairo_image_surface_t *)
+	cairo_image_surface_create (CAIRO_FORMAT_A8,
+				    extents->bounded.width,
+				    extents->bounded.height);
+    if (unlikely (mask->base.status))
 	return NULL;
 
     /* Is it worth setting the clip region here? */
@@ -1943,51 +1953,44 @@ _create_composite_mask_pattern (cairo_composite_rectangles_t *extents,
 	pixman_bool_t ret;
 
 	pixman_region32_translate (&clip_region->rgn, -extents->bounded.x, -extents->bounded.y);
-	ret = pixman_image_set_clip_region32 (mask, &clip_region->rgn);
+	ret = pixman_image_set_clip_region32 (mask->pixman_image,
+					      &clip_region->rgn);
 	pixman_region32_translate (&clip_region->rgn, extents->bounded.x, extents->bounded.y);
 
 	if (! ret) {
-	    pixman_image_unref (mask);
+	    cairo_surface_destroy (&mask->base);
 	    return NULL;
 	}
     }
 
-    status = draw_func (draw_closure,
-			mask, PIXMAN_a8,
-			CAIRO_OPERATOR_ADD, NULL,
+    mask->base.device_transform = dst->base.device_transform;
+    status = draw_func (draw_closure, mask,
+			CAIRO_OPERATOR_ADD, NULL, opacity,
 			extents->bounded.x, extents->bounded.y,
-			&dst->base.device_transform,
 			extents);
     if (unlikely (status)) {
-	pixman_image_unref (mask);
+	cairo_surface_destroy (&mask->base);
 	return NULL;
     }
 
     if (need_clip_surface) {
-	cairo_surface_t *tmp;
-
-	tmp = _cairo_image_surface_create_for_pixman_image (mask, PIXMAN_a8);
-	if (unlikely (tmp->status)) {
-	    pixman_image_unref (mask);
-	    return NULL;
-	}
-
-	pixman_image_ref (mask);
-
-	status = _cairo_clip_combine_with_surface (extents->clip, tmp,
+	status = _cairo_clip_combine_with_surface (extents->clip, &mask->base,
 						   extents->bounded.x,
 						   extents->bounded.y);
-	cairo_surface_destroy (tmp);
 	if (unlikely (status)) {
-	    pixman_image_unref (mask);
+	    cairo_surface_destroy (&mask->base);
 	    return NULL;
 	}
     }
 
     if (clip_region != NULL)
-	pixman_image_set_clip_region (mask, NULL);
+	pixman_image_set_clip_region (mask->pixman_image, NULL);
+
+    image = mask->pixman_image;
+    pixman_image_ref (image);
+    cairo_surface_destroy (&mask->base);
 
-    return mask;
+    return image;
 }
 
 /* Handles compositing with a clip surface when the operator allows
@@ -1997,13 +2000,17 @@ static cairo_status_t
 _clip_and_composite_with_mask (cairo_composite_rectangles_t *extents,
 			       cairo_operator_t               op,
 			       const cairo_pattern_t         *pattern,
+			       double                         opacity,
 			       image_draw_func_t              draw_func,
 			       void                          *draw_closure,
 			       cairo_image_surface_t         *dst)
 {
     pixman_image_t *mask;
 
-    mask = _create_composite_mask_pattern (extents, draw_func, draw_closure, dst);
+    mask = _create_composite_mask_pattern (extents,
+					   draw_func, draw_closure,
+					   opacity,
+					   dst);
     if (unlikely (mask == NULL))
 	return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
@@ -2060,37 +2067,39 @@ _clip_and_composite_with_mask (cairo_composite_rectangles_t *extents,
  * in two pieces and combine them together.
  */
 static cairo_status_t
-_clip_and_composite_combine (cairo_composite_rectangles_t	*extents,
+_clip_and_composite_combine (cairo_composite_rectangles_t  *extents,
 			     cairo_operator_t               op,
 			     const cairo_pattern_t         *src,
+			     double                         opacity,
 			     image_draw_func_t              draw_func,
 			     void                          *draw_closure,
-			     cairo_image_surface_t               *dst)
+			     cairo_image_surface_t         *dst)
 {
-    pixman_image_t *tmp;
+    cairo_image_surface_t *tmp;
     cairo_surface_t *clip_surface;
     int clip_x, clip_y;
     cairo_status_t status;
 
-    tmp  = pixman_image_create_bits (dst->pixman_format,
-				     extents->bounded.width,
-				     extents->bounded.height,
-				     NULL, 0);
-    if (unlikely (tmp == NULL))
-	return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+    tmp = (cairo_image_surface_t *)
+	_cairo_image_surface_create_with_pixman_format (NULL,
+							dst->pixman_format,
+							extents->bounded.width,
+							extents->bounded.height,
+							0);
+    if (unlikely (tmp->base.status))
+	return tmp->base.status;
 
+    tmp->base.device_transform = dst->base.device_transform;
     if (src == NULL) {
-	status = (*draw_func) (draw_closure,
-			       tmp, dst->pixman_format,
-			       CAIRO_OPERATOR_ADD, NULL,
+	status = (*draw_func) (draw_closure, tmp,
+			       CAIRO_OPERATOR_ADD, NULL, opacity,
 			       extents->bounded.x, extents->bounded.y,
-			       &dst->base.device_transform,
 			       extents);
     } else {
 	/* Initialize the temporary surface from the destination surface */
 	if (! dst->base.is_clear) {
 	    pixman_image_composite32 (PIXMAN_OP_SRC,
-                                      dst->pixman_image, NULL, tmp,
+                                      dst->pixman_image, NULL, tmp->pixman_image,
                                       extents->bounded.x,
 				      extents->bounded.y,
                                       0, 0,
@@ -2099,11 +2108,8 @@ _clip_and_composite_combine (cairo_composite_rectangles_t	*extents,
 				      extents->bounded.height);
 	}
 
-	status = (*draw_func) (draw_closure,
-			       tmp, dst->pixman_format,
-			       op, src,
+	status = (*draw_func) (draw_closure, tmp, op, src, opacity,
 			       extents->bounded.x, extents->bounded.y,
-			       &dst->base.device_transform,
 			       extents);
     }
     if (unlikely (status))
@@ -2117,7 +2123,7 @@ _clip_and_composite_combine (cairo_composite_rectangles_t	*extents,
     if (! dst->base.is_clear) {
 #if PIXMAN_HAS_OP_LERP
 	pixman_image_composite32 (PIXMAN_OP_LERP,
-                                  tmp,
+                                  tmp->pixman_image,
                                   ((cairo_image_surface_t *) clip_surface)->pixman_image,
                                   dst->pixman_image,
                                   0, 0,
@@ -2138,7 +2144,7 @@ _clip_and_composite_combine (cairo_composite_rectangles_t	*extents,
 
 	/* Now add the two results together */
 	pixman_image_composite32 (PIXMAN_OP_ADD,
-                                  tmp,
+                                  tmp->pixman_image,
                                   ((cairo_image_surface_t *) clip_surface)->pixman_image,
                                   dst->pixman_image,
                                   0, 0,
@@ -2149,7 +2155,7 @@ _clip_and_composite_combine (cairo_composite_rectangles_t	*extents,
 #endif
     } else {
 	pixman_image_composite32 (PIXMAN_OP_SRC,
-                                  tmp,
+                                  tmp->pixman_image,
                                   ((cairo_image_surface_t *) clip_surface)->pixman_image,
                                   dst->pixman_image,
                                   0, 0,
@@ -2161,7 +2167,7 @@ _clip_and_composite_combine (cairo_composite_rectangles_t	*extents,
 
     cairo_surface_destroy (clip_surface);
  CLEANUP_SURFACE:
-    pixman_image_unref (tmp);
+    cairo_surface_destroy (&tmp->base);
 
     return status;
 }
@@ -2172,6 +2178,7 @@ _clip_and_composite_combine (cairo_composite_rectangles_t	*extents,
 static cairo_status_t
 _clip_and_composite_source (cairo_composite_rectangles_t  *extents,
 			    const cairo_pattern_t         *pattern,
+			    double                         opacity,
 			    image_draw_func_t              draw_func,
 			    void                          *draw_closure,
 			    cairo_image_surface_t         *dst)
@@ -2182,11 +2189,9 @@ _clip_and_composite_source (cairo_composite_rectangles_t  *extents,
     if (pattern == NULL) {
 	cairo_status_t status;
 
-	status = draw_func (draw_closure,
-			    dst->pixman_image, dst->pixman_format,
-			    CAIRO_OPERATOR_SOURCE, NULL,
+	status = draw_func (draw_closure, dst,
+			    CAIRO_OPERATOR_SOURCE, NULL, opacity,
 			    extents->bounded.x, extents->bounded.y,
-			    &dst->base.device_transform,
 			    extents);
 	if (unlikely (status))
 	    return status;
@@ -2199,7 +2204,9 @@ _clip_and_composite_source (cairo_composite_rectangles_t  *extents,
     }
 
     /* Create a surface that is mask IN clip */
-    mask = _create_composite_mask_pattern (extents, draw_func, draw_closure, dst);
+    mask = _create_composite_mask_pattern (extents,
+					   draw_func, draw_closure, opacity,
+					   dst);
     if (unlikely (mask == NULL))
 	return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
@@ -2284,6 +2291,7 @@ static cairo_status_t
 _clip_and_composite (cairo_image_surface_t	*dst,
 		     cairo_operator_t		 op,
 		     const cairo_pattern_t	*src,
+		     double			 opacity,
 		     image_draw_func_t		 draw_func,
 		     void			*draw_closure,
 		     cairo_composite_rectangles_t*extents,
@@ -2311,7 +2319,7 @@ _clip_and_composite (cairo_image_surface_t	*dst,
     }
 
     if (op == CAIRO_OPERATOR_SOURCE) {
-	status = _clip_and_composite_source (extents, src,
+	status = _clip_and_composite_source (extents, src, opacity,
 					     draw_func, draw_closure, dst);
     } else {
 	if (op == CAIRO_OPERATOR_CLEAR) {
@@ -2321,21 +2329,17 @@ _clip_and_composite (cairo_image_surface_t	*dst,
 
 	if (need_clip & NEED_CLIP_SURFACE) {
 	    if (extents->is_bounded) {
-		status = _clip_and_composite_with_mask (extents, op, src,
+		status = _clip_and_composite_with_mask (extents, op, src, opacity,
 							draw_func, draw_closure,
 							dst);
 	    } else {
-		status = _clip_and_composite_combine (extents, op, src,
+		status = _clip_and_composite_combine (extents, op, src, opacity,
 						      draw_func, draw_closure,
 						      dst);
 	    }
 	} else {
-	    status = draw_func (draw_closure,
-				dst->pixman_image, dst->pixman_format,
-				op, src,
-				0, 0,
-				&dst->base.device_transform,
-				extents);
+	    status = draw_func (draw_closure, dst, op, src, opacity,
+				0, 0, extents);
 	}
     }
 
@@ -2445,13 +2449,12 @@ _pixman_image_add_traps (pixman_image_t *image,
 
 static cairo_status_t
 _composite_traps (void                          *closure,
-		  pixman_image_t		*dst,
-		  pixman_format_code_t		 dst_format,
+		  cairo_image_surface_t         *dst,
 		  cairo_operator_t               op,
 		  const cairo_pattern_t         *pattern,
+		  double                         opacity,
 		  int                            dst_x,
 		  int                            dst_y,
-		  cairo_matrix_t		*dst_device_transform,
 		  const cairo_composite_rectangles_t   *extents)
 {
     composite_traps_info_t *info = closure;
@@ -2468,16 +2471,17 @@ _composite_traps (void                          *closure,
      * the Cairo core code passes bounds based on the trapezoid extents.
      */
     format = info->antialias == CAIRO_ANTIALIAS_NONE ? PIXMAN_a1 : PIXMAN_a8;
-    if (dst_format == format &&
+    if (dst->pixman_format == format &&
+	opacity == 1. &&
 	(pattern == NULL ||
 	 (op == CAIRO_OPERATOR_ADD && _cairo_pattern_is_opaque_solid (pattern))))
     {
-	_pixman_image_add_traps (dst, dst_x, dst_y, info);
+	_pixman_image_add_traps (dst->pixman_image, dst_x, dst_y, info);
 	return CAIRO_STATUS_SUCCESS;
     }
 
     src = _pixman_image_for_pattern (pattern, FALSE, &extents->bounded,
-				     dst_device_transform,
+				     &dst->base.device_transform,
 				     &src_x, &src_y);
     if (unlikely (src == NULL))
 	return _cairo_error (CAIRO_STATUS_NO_MEMORY);
@@ -2492,17 +2496,41 @@ _composite_traps (void                          *closure,
     }
 
     _pixman_image_add_traps (mask, extents->bounded.x, extents->bounded.y, info);
+    if (opacity != .1) {
+	pixman_color_t color;
+	pixman_image_t *alpha;
+
+	color.red   = 0;
+	color.green = 0;
+	color.blue  = 0;
+	color.alpha = 0xffff * opacity;
+
+	alpha = pixman_image_create_solid_fill (&color);
+	if (alpha == NULL) {
+	    status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+	    goto CLEANUP_MASK;
+	}
+	pixman_image_composite32 (PIXMAN_OP_IN,
+				  alpha, NULL, mask,
+				  0, 0,
+				  0, 0,
+				  0, 0,
+				  extents->bounded.width, extents->bounded.height);
+	pixman_image_unref (alpha);
+    }
+
     pixman_image_composite32 (_pixman_operator (op),
-                              src, mask, dst,
+                              src, mask, dst->pixman_image,
                               extents->bounded.x + src_x, extents->bounded.y + src_y,
                               0, 0,
                               extents->bounded.x - dst_x, extents->bounded.y - dst_y,
                               extents->bounded.width, extents->bounded.height);
 
-    pixman_image_unref (mask);
 
     status = CAIRO_STATUS_SUCCESS;
- CLEANUP_SOURCE:
+CLEANUP_MASK:
+    pixman_image_unref (mask);
+CLEANUP_SOURCE:
     pixman_image_unref (src);
 
     return status;
@@ -2520,9 +2548,11 @@ color_to_uint32 (const cairo_color_t *color)
 
 static inline cairo_bool_t
 color_to_pixel (const cairo_color_t	*color,
+		double opacity,
                 pixman_format_code_t	 format,
                 uint32_t		*pixel)
 {
+    cairo_color_t opacity_color;
     uint32_t c;
 
     if (!(format == PIXMAN_a8r8g8b8     ||
@@ -2538,6 +2568,14 @@ color_to_pixel (const cairo_color_t	*color,
 	return FALSE;
     }
 
+    if (opacity != 1.0) {
+	_cairo_color_init_rgba (&opacity_color,
+				color->red,
+				color->green,
+				color->blue,
+				color->alpha * opacity);
+	color = &opacity_color;
+    }
     c = color_to_uint32 (color);
 
     if (PIXMAN_FORMAT_TYPE (format) == PIXMAN_TYPE_ABGR) {
@@ -2568,6 +2606,7 @@ color_to_pixel (const cairo_color_t	*color,
 
 static inline cairo_bool_t
 pattern_to_pixel (const cairo_solid_pattern_t *solid,
+		  double opacity,
 		  cairo_operator_t op,
 		  pixman_format_code_t format,
 		  uint32_t *pixel)
@@ -2581,301 +2620,244 @@ pattern_to_pixel (const cairo_solid_pattern_t *solid,
 	return FALSE;
 
     if (op == CAIRO_OPERATOR_OVER) {
-	if (solid->color.alpha_short >= 0xff00)
+	if (opacity * solid->color.alpha_short >= 0xff00)
 	    op = CAIRO_OPERATOR_SOURCE;
     }
 
     if (op != CAIRO_OPERATOR_SOURCE)
 	return FALSE;
 
-    return color_to_pixel (&solid->color, format, pixel);
+    return color_to_pixel (&solid->color, opacity, format, pixel);
 }
 
-typedef struct _fill_span {
+typedef struct _cairo_image_span_renderer {
     cairo_span_renderer_t base;
-
-    uint8_t *mask_data;
-    pixman_image_t *src, *dst, *mask;
-} fill_span_renderer_t;
+    pixman_image_compositor_t *compositor;
+    pixman_image_t *src;
+    float opacity;
+    cairo_rectangle_int_t extents;
+} cairo_image_span_renderer_t;
 
 static cairo_status_t
-_fill_span (void *abstract_renderer,
-	    int y, int height,
-	    const cairo_half_open_span_t *spans,
-	    unsigned num_spans)
+_cairo_image_bounded_opaque_spans (void *abstract_renderer,
+				   int y, int height,
+				   const cairo_half_open_span_t *spans,
+				   unsigned num_spans)
 {
-    fill_span_renderer_t *renderer = abstract_renderer;
-    uint8_t *row;
-    unsigned i;
+    cairo_image_span_renderer_t *r = abstract_renderer;
 
     if (num_spans == 0)
 	return CAIRO_STATUS_SUCCESS;
 
-    row = renderer->mask_data - spans[0].x;
-    for (i = 0; i < num_spans - 1; i++) {
-	/* We implement setting the most common single pixel wide
-	 * span case to avoid the overhead of a memset call.
-	 * Open coding setting longer spans didn't show a
-	 * noticeable improvement over memset.
-	 */
-	if (spans[i+1].x == spans[i].x + 1) {
-	    row[spans[i].x] = spans[i].coverage;
-	} else {
-	    memset (row + spans[i].x,
-		    spans[i].coverage,
-		    spans[i+1].x - spans[i].x);
-	}
-    }
-
     do {
-	pixman_image_composite32 (PIXMAN_OP_OVER,
-                                  renderer->src, renderer->mask, renderer->dst,
-                                  0, 0, 0, 0,
-                                  spans[0].x, y++,
-                                  spans[i].x - spans[0].x, 1);
-    } while (--height);
+	if (spans[0].coverage)
+	    pixman_image_compositor_blt (r->compositor,
+					 spans[0].x, y,
+					 spans[1].x - spans[0].x, height,
+					 spans[0].coverage);
+	spans++;
+    } while (--num_spans > 1);
 
     return CAIRO_STATUS_SUCCESS;
 }
 
-/* avoid using region code to re-validate boxes */
 static cairo_status_t
-_fill_unaligned_boxes (cairo_image_surface_t *dst,
-		       const cairo_pattern_t *pattern,
-		       uint32_t pixel,
-		       const cairo_boxes_t *boxes,
-		       const cairo_composite_rectangles_t *extents)
+_cairo_image_bounded_spans (void *abstract_renderer,
+			    int y, int height,
+			    const cairo_half_open_span_t *spans,
+			    unsigned num_spans)
 {
-    uint8_t buf[CAIRO_STACK_BUFFER_SIZE];
-    fill_span_renderer_t renderer;
-    cairo_rectangular_scan_converter_t converter;
-    const struct _cairo_boxes_chunk *chunk;
-    cairo_status_t status;
-    int i;
+    cairo_image_span_renderer_t *r = abstract_renderer;
 
-    /* XXX
-     * using composite for fill:
-     *   spiral-box-nonalign-evenodd-fill.512    2201957    2.202
-     *   spiral-box-nonalign-nonzero-fill.512     336726    0.337
-     *   spiral-box-pixalign-evenodd-fill.512     352256    0.352
-     *   spiral-box-pixalign-nonzero-fill.512     147056    0.147
-     * using fill:
-     *   spiral-box-nonalign-evenodd-fill.512    3174565    3.175
-     *   spiral-box-nonalign-nonzero-fill.512     182710    0.183
-     *   spiral-box-pixalign-evenodd-fill.512     353863    0.354
-     *   spiral-box-pixalign-nonzero-fill.512     147402    0.147
-     *
-     * cairo-perf-trace seems to favour using fill.
-     */
+    if (num_spans == 0)
+	return CAIRO_STATUS_SUCCESS;
 
-    renderer.base.render_rows = _fill_span;
-    renderer.dst = dst->pixman_image;
+    do {
+	if (spans[0].coverage) {
+	    pixman_image_compositor_blt (r->compositor,
+					 spans[0].x, y,
+					 spans[1].x - spans[0].x, height,
+					  r->opacity * spans[0].coverage);
+	}
+	spans++;
+    } while (--num_spans > 1);
 
-    if ((unsigned) extents->bounded.width <= sizeof (buf)) {
-	renderer.mask = pixman_image_create_bits (PIXMAN_a8,
-						  extents->bounded.width, 1,
-						  (uint32_t *) buf,
-						  sizeof (buf));
-    } else {
-	renderer.mask = pixman_image_create_bits (PIXMAN_a8,
-						  extents->bounded.width, 1,
-						  NULL,  0);
-    }
-    if (unlikely (renderer.mask == NULL))
-	return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+    return CAIRO_STATUS_SUCCESS;
+}
 
-    renderer.mask_data = (uint8_t *) pixman_image_get_data (renderer.mask);
+static cairo_status_t
+_cairo_image_unbounded_spans (void *abstract_renderer,
+			      int y, int height,
+			      const cairo_half_open_span_t *spans,
+			      unsigned num_spans)
+{
+    cairo_image_span_renderer_t *r = abstract_renderer;
 
-    renderer.src = _pixman_image_for_solid ((const cairo_solid_pattern_t *) pattern);
-    if (unlikely (renderer.src == NULL)) {
-	status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
-	goto CLEANUP_MASK;
+    if (y > r->extents.y) {
+	pixman_image_compositor_blt (r->compositor,
+				     r->extents.x, r->extents.y,
+				     r->extents.width, y - r->extents.y,
+				     0);
     }
 
-    _cairo_rectangular_scan_converter_init (&converter, &extents->bounded);
-
-    /* first blit any aligned part of the boxes */
-    for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) {
-	const cairo_box_t *box = chunk->base;
+    if (num_spans == 0) {
+	pixman_image_compositor_blt (r->compositor,
+				     r->extents.x, y,
+				     r->extents.width,  height,
+				     0);
+    } else {
+	if (spans[0].x != r->extents.x) {
+	    pixman_image_compositor_blt (r->compositor,
+					 r->extents.x, y,
+					 spans[0].x - r->extents.x,
+					 height,
+					 0);
+	}
 
-	for (i = 0; i < chunk->count; i++) {
-	    int x1 = _cairo_fixed_integer_ceil (box[i].p1.x);
-	    int y1 = _cairo_fixed_integer_ceil (box[i].p1.y);
-	    int x2 = _cairo_fixed_integer_floor (box[i].p2.x);
-	    int y2 = _cairo_fixed_integer_floor (box[i].p2.y);
+	do {
+	    pixman_image_compositor_blt (r->compositor,
+					 spans[0].x, y,
+					 spans[1].x - spans[0].x, height,
+					 r->opacity * spans[0].coverage);
+	    spans++;
+	} while (--num_spans > 1);
+
+	if (spans[0].x != r->extents.x + r->extents.width) {
+	    pixman_image_compositor_blt (r->compositor,
+					 spans[0].x,     y,
+					 r->extents.x + r->extents.width - spans[0].x, height,
+					 0);
+	}
+    }
 
-	    if (x2 > x1 && y2 > y1) {
-		cairo_box_t b;
+    r->extents.y = y + height;
+    return CAIRO_STATUS_SUCCESS;
+}
 
-		pixman_fill ((uint32_t *) dst->data,
-			     dst->stride / sizeof (uint32_t),
-			     PIXMAN_FORMAT_BPP (dst->pixman_format),
-			     x1, y1, x2 - x1, y2 - y1,
-			     pixel);
+static cairo_status_t
+_cairo_image_clipped_spans (void *abstract_renderer,
+			    int y, int height,
+			    const cairo_half_open_span_t *spans,
+			    unsigned num_spans)
+{
+    cairo_image_span_renderer_t *r = abstract_renderer;
 
-		/*
-		 * Corners have to be included only once if the rects
-		 * are passed to the rectangular scan converter
-		 * because it can only handle disjoint rectangles.
-		*/
-
-		/* top (including top-left and top-right corners) */
-		if (! _cairo_fixed_is_integer (box[i].p1.y)) {
-		    b.p1.x = box[i].p1.x;
-		    b.p1.y = box[i].p1.y;
-		    b.p2.x = box[i].p2.x;
-		    b.p2.y = _cairo_fixed_from_int (y1);
-
-		    status = _cairo_rectangular_scan_converter_add_box (&converter, &b, 1);
-		    if (unlikely (status))
-			goto CLEANUP_CONVERTER;
-		}
+    assert (num_spans);
 
-		/* left (no corners) */
-		if (! _cairo_fixed_is_integer (box[i].p1.x)) {
-		    b.p1.x = box[i].p1.x;
-		    b.p1.y = _cairo_fixed_from_int (y1);
-		    b.p2.x = _cairo_fixed_from_int (x1);
-		    b.p2.y = _cairo_fixed_from_int (y2);
+    do {
+	if (! spans[0].is_clipped)
+	    pixman_image_compositor_blt (r->compositor,
+					 spans[0].x, y,
+					 spans[1].x - spans[0].x, height,
+					 r->opacity * spans[0].coverage);
+	spans++;
+    } while (--num_spans > 1);
+
+    r->extents.y = y + height;
+    return CAIRO_STATUS_SUCCESS;
+}
 
-		    status = _cairo_rectangular_scan_converter_add_box (&converter, &b, 1);
-		    if (unlikely (status))
-			goto CLEANUP_CONVERTER;
-		}
+static cairo_status_t
+_cairo_image_finish_unbounded_spans (void *abstract_renderer)
+{
+    cairo_image_span_renderer_t *r = abstract_renderer;
 
-		/* right (no corners) */
-		if (! _cairo_fixed_is_integer (box[i].p2.x)) {
-		    b.p1.x = _cairo_fixed_from_int (x2);
-		    b.p1.y = _cairo_fixed_from_int (y1);
-		    b.p2.x = box[i].p2.x;
-		    b.p2.y = _cairo_fixed_from_int (y2);
+    if (r->extents.y < r->extents.height) {
+	pixman_image_compositor_blt (r->compositor,
+				     r->extents.x, r->extents.y,
+				     r->extents.width,
+				     r->extents.height - r->extents.y,
+				     0);
+    }
 
-		    status = _cairo_rectangular_scan_converter_add_box (&converter, &b, 1);
-		    if (unlikely (status))
-			goto CLEANUP_CONVERTER;
-		}
+    return CAIRO_STATUS_SUCCESS;
+}
 
-		/* bottom (including bottom-left and bottom-right corners) */
-		if (! _cairo_fixed_is_integer (box[i].p2.y)) {
-		    b.p1.x = box[i].p1.x;
-		    b.p1.y = _cairo_fixed_from_int (y2);
-		    b.p2.x = box[i].p2.x;
-		    b.p2.y = box[i].p2.y;
+static cairo_status_t
+_cairo_image_span_renderer_init (cairo_image_span_renderer_t *r,
+				 cairo_image_surface_t *dst,
+				 cairo_operator_t op,
+				 const cairo_pattern_t *pattern,
+				 float opacity,
+				 const cairo_composite_rectangles_t *composite,
+				 cairo_bool_t needs_clip)
+{
+    int src_x, src_y;
 
-		    status = _cairo_rectangular_scan_converter_add_box (&converter, &b, 1);
-		    if (unlikely (status))
-			goto CLEANUP_CONVERTER;
-		}
-	    } else {
-		status = _cairo_rectangular_scan_converter_add_box (&converter, &box[i], 1);
-		if (unlikely (status))
-		    goto CLEANUP_CONVERTER;
-	    }
-	}
+    if (op == CAIRO_OPERATOR_CLEAR) {
+	op = CAIRO_OPERATOR_DEST_OUT;
+	pattern = NULL;
     }
 
-    status = converter.base.generate (&converter.base, &renderer.base);
+    r->src = _pixman_image_for_pattern (pattern, FALSE,
+					&composite->bounded,
+					&dst->base.device_transform,
+					&src_x, &src_y);
+    if (unlikely (r->src == NULL))
+	return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
-  CLEANUP_CONVERTER:
-    converter.base.destroy (&converter.base);
-    pixman_image_unref (renderer.src);
-  CLEANUP_MASK:
-    pixman_image_unref (renderer.mask);
+    if (composite->is_bounded) {
+	if (opacity == 1.)
+	    r->base.render_rows = _cairo_image_bounded_opaque_spans;
+	else
+	    r->base.render_rows = _cairo_image_bounded_spans;
+	r->base.finish = NULL;
+    } else {
+	if (needs_clip)
+	    r->base.render_rows = _cairo_image_clipped_spans;
+	else
+	    r->base.render_rows = _cairo_image_unbounded_spans;
+        r->base.finish =      _cairo_image_finish_unbounded_spans;
+	r->extents = composite->unbounded;
+	r->extents.height += r->extents.y;
+
+    }
+    r->compositor =
+	pixman_image_create_compositor (_pixman_operator (op),
+					r->src, NULL, dst->pixman_image,
+					composite->bounded.x + src_x,
+					composite->bounded.y + src_y,
+					0, 0,
+					composite->bounded.x,
+					composite->bounded.y,
+					composite->bounded.width,
+					composite->bounded.height);
+    r->opacity = opacity;
 
-    return status;
+    return CAIRO_STATUS_SUCCESS;
 }
 
-typedef struct _cairo_image_surface_span_renderer {
-    cairo_span_renderer_t base;
-
-    uint8_t *mask_data;
-    uint32_t mask_stride;
-} cairo_image_surface_span_renderer_t;
-
-static cairo_status_t
-_cairo_image_surface_span (void *abstract_renderer,
-			   int y, int height,
-			   const cairo_half_open_span_t *spans,
-			   unsigned num_spans)
+static void
+_cairo_image_span_renderer_fini (cairo_image_span_renderer_t *r)
 {
-    cairo_image_surface_span_renderer_t *renderer = abstract_renderer;
-    uint8_t *row;
-    unsigned i;
-
-    if (num_spans == 0)
-	return CAIRO_STATUS_SUCCESS;
-
-    /* XXX will it be quicker to repeat the sparse memset,
-     * or perform a simpler memcpy?
-     * The fairly dense spiral benchmarks suggests that the sparse
-     * memset is a win there as well.
-     */
-    row = renderer->mask_data + y * renderer->mask_stride;
-    do {
-	for (i = 0; i < num_spans - 1; i++) {
-	    if (! spans[i].coverage)
-		continue;
-
-	    /* We implement setting rendering the most common single
-	     * pixel wide span case to avoid the overhead of a memset
-	     * call.  Open coding setting longer spans didn't show a
-	     * noticeable improvement over memset. */
-	    if (spans[i+1].x == spans[i].x + 1) {
-		row[spans[i].x] = spans[i].coverage;
-	    } else {
-		memset (row + spans[i].x,
-			spans[i].coverage,
-			spans[i+1].x - spans[i].x);
-	    }
-	}
-	row += renderer->mask_stride;
-    } while (--height);
+    if (r->base.finish)
+	r->base.finish (r);
 
-    return CAIRO_STATUS_SUCCESS;
+    pixman_image_compositor_destroy (r->compositor);
+    pixman_image_unref (r->src);
 }
 
 static cairo_status_t
 _composite_unaligned_boxes (cairo_image_surface_t *dst,
 			    cairo_operator_t op,
 			    const cairo_pattern_t *pattern,
+			    double opacity,
 			    const cairo_boxes_t *boxes,
 			    const cairo_composite_rectangles_t *extents)
 {
-    uint8_t buf[CAIRO_STACK_BUFFER_SIZE];
-    cairo_image_surface_span_renderer_t renderer;
+    cairo_image_span_renderer_t renderer;
     cairo_rectangular_scan_converter_t converter;
-    pixman_image_t *mask, *src;
-    cairo_status_t status;
     const struct _cairo_boxes_chunk *chunk;
-    int i, src_x, src_y;
-
-    /* The below code breaks for operator source. This can best be seen with
-     * multiple boxes where black is drawn to dst outside of the boxes. */
-    if (op == CAIRO_OPERATOR_SOURCE)
-	return CAIRO_INT_STATUS_UNSUPPORTED;
-
-    i = CAIRO_STRIDE_FOR_WIDTH_BPP (extents->bounded.width, 8) * extents->bounded.height;
-    if ((unsigned) i <= sizeof (buf)) {
-	mask = pixman_image_create_bits (PIXMAN_a8,
-					 extents->bounded.width,
-					 extents->bounded.height,
-					 (uint32_t *) buf,
-					 CAIRO_STRIDE_FOR_WIDTH_BPP (extents->bounded.width, 8));
-	memset (buf, 0, i);
-    } else {
-	mask = pixman_image_create_bits (PIXMAN_a8,
-					 extents->bounded.width,
-					 extents->bounded.height,
-					 NULL,  0);
-    }
-    if (unlikely (mask == NULL))
-	return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+    cairo_status_t status;
+    int i;
 
-    renderer.base.render_rows = _cairo_image_surface_span;
-    renderer.mask_stride = pixman_image_get_stride (mask);
-    renderer.mask_data = (uint8_t *) pixman_image_get_data (mask);
-    renderer.mask_data -= extents->bounded.y * renderer.mask_stride + extents->bounded.x;
+    status = _cairo_image_span_renderer_init (&renderer,
+					      dst, op, pattern, opacity,
+					      extents, FALSE);
+    if (unlikely (status))
+	return status;
 
+    assert (! extents->clip->path);
     _cairo_rectangular_scan_converter_init (&converter, &extents->bounded);
 
     for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) {
@@ -2889,29 +2871,10 @@ _composite_unaligned_boxes (cairo_image_surface_t *dst,
     }
 
     status = converter.base.generate (&converter.base, &renderer.base);
-    if (unlikely (status))
-	goto CLEANUP;
-
-    src = _pixman_image_for_pattern (pattern, FALSE, &extents->bounded,
-				     &dst->base.device_transform,
-				     &src_x, &src_y);
-    if (unlikely (src == NULL)) {
-	status =  _cairo_error (CAIRO_STATUS_NO_MEMORY);
-	goto CLEANUP;
-    }
-
-    pixman_image_composite32 (_pixman_operator (op),
-                              src, mask, dst->pixman_image,
-                              extents->bounded.x + src_x, extents->bounded.y + src_y,
-                              0, 0,
-                              extents->bounded.x, extents->bounded.y,
-                              extents->bounded.width, extents->bounded.height);
-    pixman_image_unref (src);
 
-  CLEANUP:
+CLEANUP:
+    _cairo_image_span_renderer_fini (&renderer);
     converter.base.destroy (&converter.base);
-    pixman_image_unref (mask);
-
     return status;
 }
 
@@ -2986,6 +2949,7 @@ static cairo_status_t
 _composite_boxes (cairo_image_surface_t *dst,
 		  cairo_operator_t op,
 		  const cairo_pattern_t *pattern,
+		  double opacity,
 		  cairo_boxes_t *boxes,
 		  const cairo_composite_rectangles_t *extents)
 {
@@ -2996,6 +2960,9 @@ _composite_boxes (cairo_image_surface_t *dst,
     uint32_t pixel;
     int i;
 
+    if (need_clip_mask && opacity != 1.)
+	return CAIRO_INT_STATUS_UNSUPPORTED;
+
     if (need_clip_mask &&
 	(op == CAIRO_OPERATOR_SOURCE || ! extents->is_bounded))
     {
@@ -3009,23 +2976,16 @@ _composite_boxes (cairo_image_surface_t *dst,
 	if (need_clip_mask)
 	    return CAIRO_INT_STATUS_UNSUPPORTED;
 
-	if (pattern_to_pixel ((cairo_solid_pattern_t *) pattern, op,
-			      dst->pixman_format, &pixel))
-	{
-	    return _fill_unaligned_boxes (dst, pattern, pixel, boxes, extents);
-	}
-	else
-	{
-	    return _composite_unaligned_boxes (dst, op, pattern, boxes, extents);
-	}
+	return _composite_unaligned_boxes (dst, op, pattern, opacity, boxes, extents);
     }
 
     /* Are we just copying a recording surface? */
-    if (! need_clip_mask &&
+    if (! need_clip_mask && opacity == 1. &&
 	op_reduces_to_source (op, dst) &&
 	recording_pattern_contains_sample (pattern, &extents->bounded))
     {
 	cairo_clip_t *recording_clip;
+	cairo_bool_t was_clear = dst->base.is_clear;
 
 	/* XXX could also do tiling repeat modes... */
 
@@ -3050,6 +3010,8 @@ _composite_boxes (cairo_image_surface_t *dst,
 				 0);
 		}
 	    }
+
+	    dst->base.is_clear = TRUE;
 	}
 
 	recording_clip = _cairo_clip_from_boxes (boxes);
@@ -3058,13 +3020,15 @@ _composite_boxes (cairo_image_surface_t *dst,
 							    &dst->base,
 							    recording_clip);
 	_cairo_clip_destroy (recording_clip);
+	dst->base.is_clear &= was_clear;
 
 	return status;
     }
 
     status = CAIRO_STATUS_SUCCESS;
     if (! need_clip_mask &&
-	pattern_to_pixel ((cairo_solid_pattern_t *) pattern, op, dst->pixman_format,
+	pattern_to_pixel ((cairo_solid_pattern_t *) pattern, opacity, op,
+			  dst->pixman_format,
 			  &pixel))
     {
 	for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) {
@@ -3167,21 +3131,75 @@ static cairo_status_t
 _clip_and_composite_polygon (cairo_image_surface_t *dst,
 			     cairo_operator_t op,
 			     const cairo_pattern_t *src,
+			     double opacity,
 			     cairo_polygon_t *polygon,
 			     cairo_fill_rule_t fill_rule,
 			     cairo_antialias_t antialias,
 			     cairo_composite_rectangles_t *extents);
+static cairo_status_t
+_composite_boxes_fallback (void			*closure,
+			   cairo_image_surface_t       *dst,
+			   cairo_operator_t		 op,
+			   const cairo_pattern_t	*pattern,
+			   double                       opacity,
+			   int				 dst_x,
+			   int				 dst_y,
+			   const cairo_composite_rectangles_t	*extents)
+{
+    cairo_boxes_t *boxes = closure;
+    cairo_image_span_renderer_t renderer;
+    cairo_rectangular_scan_converter_t converter;
+    const struct _cairo_boxes_chunk *chunk;
+    cairo_status_t status;
+    cairo_fixed_t fx = _cairo_fixed_from_int (-dst_x);
+    cairo_fixed_t fy = _cairo_fixed_from_int (-dst_y);
+    int i;
+
+    assert (! boxes->is_pixel_aligned);
+
+    status = _cairo_image_span_renderer_init (&renderer,
+					      dst, op, pattern, opacity,
+					      extents, FALSE);
+    if (unlikely (status))
+	return status;
+
+    _cairo_rectangular_scan_converter_init (&converter, &extents->bounded);
+    for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) {
+	const cairo_box_t *box = chunk->base;
+
+	for (i = 0; i < chunk->count; i++) {
+	    cairo_box_t b;
+
+	    b.p1.x = box->p1.x + fx;
+	    b.p1.y = box->p1.y + fy;
+	    b.p2.x = box->p2.x + fx;
+	    b.p2.y = box->p2.y + fy;
+
+	    status = _cairo_rectangular_scan_converter_add_box (&converter,
+								&b, 1);
+	    if (unlikely (status))
+		goto CLEANUP;
+	}
+    }
+
+    status = converter.base.generate (&converter.base, &renderer.base);
+
+CLEANUP:
+    _cairo_image_span_renderer_fini (&renderer);
+    converter.base.destroy (&converter.base);
+
+    return status;
+}
 
 static cairo_status_t
 _clip_and_composite_boxes (cairo_image_surface_t *dst,
 			   cairo_operator_t op,
 			   const cairo_pattern_t *src,
+			   double opacity,
 			   cairo_boxes_t *boxes,
 			   cairo_composite_rectangles_t *extents)
 {
-    cairo_traps_t traps;
     cairo_int_status_t status;
-    composite_traps_info_t info;
 
     if (boxes->num_boxes == 0 && extents->is_bounded)
 	return CAIRO_STATUS_SUCCESS;
@@ -3202,7 +3220,7 @@ _clip_and_composite_boxes (cairo_image_surface_t *dst,
 	if (likely (status == CAIRO_INT_STATUS_SUCCESS)) {
 	    cairo_clip_t *saved_clip = extents->clip;
 	    extents->clip = clip;
-	    status = _clip_and_composite_polygon (dst, op, src,
+	    status = _clip_and_composite_polygon (dst, op, src, opacity,
 						  &polygon,
 						  fill_rule,
 						  antialias,
@@ -3220,24 +3238,14 @@ _clip_and_composite_boxes (cairo_image_surface_t *dst,
     }
 
     /* Use a fast path if the boxes are pixel aligned */
-    status = _composite_boxes (dst, op, src, boxes, extents);
+    status = _composite_boxes (dst, op, src, opacity, boxes, extents);
     if (status != CAIRO_INT_STATUS_UNSUPPORTED)
 	return status;
 
     /* Otherwise render via a mask and composite in the usual fashion.  */
-    status = _cairo_traps_init_boxes (&traps, boxes);
-    if (unlikely (status))
-	return status;
-
-    info.num_traps = traps.num_traps;
-    info.traps = traps.traps;
-    info.antialias = CAIRO_ANTIALIAS_DEFAULT;
-    status = _clip_and_composite (dst, op, src,
-				  _composite_traps, &info,
-				  extents, need_unbounded_clip (extents));
-
-    _cairo_traps_fini (&traps);
-    return status;
+    return _clip_and_composite (dst, op, src, opacity,
+				_composite_boxes_fallback, boxes,
+				extents, need_unbounded_clip (extents));
 }
 
 static cairo_bool_t
@@ -3335,6 +3343,7 @@ static cairo_status_t
 _clip_and_composite_trapezoids (cairo_image_surface_t *dst,
 				cairo_operator_t op,
 				const cairo_pattern_t *src,
+				double opacity,
 				cairo_traps_t *traps,
 				cairo_antialias_t antialias,
 				cairo_composite_rectangles_t *extents)
@@ -3370,7 +3379,7 @@ _clip_and_composite_trapezoids (cairo_image_surface_t *dst,
 	cairo_boxes_t boxes;
 
 	_boxes_for_traps (&boxes, traps, antialias);
-	return _clip_and_composite_boxes (dst, op, src,
+	return _clip_and_composite_boxes (dst, op, src, opacity,
 					  &boxes, extents);
     }
 
@@ -3381,7 +3390,7 @@ _clip_and_composite_trapezoids (cairo_image_surface_t *dst,
     info.traps = traps->traps;
     info.num_traps = traps->num_traps;
     info.antialias = antialias;
-    return _clip_and_composite (dst, op, src,
+    return _clip_and_composite (dst, op, src, opacity,
 				_composite_traps, &info,
 				extents, need_unbounded_clip (extents));
 }
@@ -3428,7 +3437,7 @@ _cairo_image_surface_paint (void			*abstract_surface,
 
     status = _cairo_clip_to_boxes (extents.clip, &boxes);
     if (likely (status == CAIRO_STATUS_SUCCESS)) {
-	status = _clip_and_composite_boxes (surface, op, source,
+	status = _clip_and_composite_boxes (surface, op, source, 1.,
 					    &boxes, &extents);
 	_cairo_boxes_fini (&boxes);
     }
@@ -3440,13 +3449,12 @@ _cairo_image_surface_paint (void			*abstract_surface,
 
 static cairo_status_t
 _composite_mask (void				*closure,
-		 pixman_image_t			*dst,
-		 pixman_format_code_t		 dst_format,
+		 cairo_image_surface_t          *dst,
 		 cairo_operator_t		 op,
 		 const cairo_pattern_t		*src_pattern,
+		 double                          opacity,
 		 int				 dst_x,
 		 int				 dst_y,
-		 cairo_matrix_t			*dst_device_transform,
 		 const cairo_composite_rectangles_t	*extents)
 {
     const cairo_pattern_t *mask_pattern = closure;
@@ -3454,15 +3462,17 @@ _composite_mask (void				*closure,
     int src_x = 0, src_y = 0;
     int mask_x = 0, mask_y = 0;
 
+    assert (opacity == 1.);
+
     if (src_pattern != NULL) {
 	src = _pixman_image_for_pattern (src_pattern, FALSE, &extents->bounded,
-					 dst_device_transform,
+					 &dst->base.device_transform,
 					 &src_x, &src_y);
 	if (unlikely (src == NULL))
 	    return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
 	mask = _pixman_image_for_pattern (mask_pattern, TRUE, &extents->bounded,
-					  dst_device_transform,
+					  &dst->base.device_transform,
 					  &mask_x, &mask_y);
 	if (unlikely (mask == NULL)) {
 	    pixman_image_unref (src);
@@ -3473,13 +3483,13 @@ _composite_mask (void				*closure,
 	    pixman_image_set_component_alpha (mask, TRUE);
     } else {
 	src = _pixman_image_for_pattern (mask_pattern, FALSE, &extents->bounded,
-					 dst_device_transform,
+					 &dst->base.device_transform,
 					 &src_x, &src_y);
 	if (unlikely (src == NULL))
 	    return _cairo_error (CAIRO_STATUS_NO_MEMORY);
     }
 
-    pixman_image_composite32 (_pixman_operator (op), src, mask, dst,
+    pixman_image_composite32 (_pixman_operator (op), src, mask, dst->pixman_image,
                               extents->bounded.x + src_x,  extents->bounded.y + src_y,
                               extents->bounded.x + mask_x, extents->bounded.y + mask_y,
                               extents->bounded.x - dst_x,  extents->bounded.y - dst_y,
@@ -3492,151 +3502,6 @@ _composite_mask (void				*closure,
     return CAIRO_STATUS_SUCCESS;
 }
 
-static void do_unaligned_row(void (*blt)(void *closure,
-					 int16_t x, int16_t y,
-					 int16_t w, int16_t h,
-					 uint16_t coverage),
-			     void *closure,
-			     const cairo_box_t *b,
-			     int tx, int y, int h,
-			     uint16_t coverage)
-{
-    int x1 = _cairo_fixed_integer_part (b->p1.x) - tx;
-    int x2 = _cairo_fixed_integer_part (b->p2.x) - tx;
-    if (x2 > x1) {
-	if (! _cairo_fixed_is_integer (b->p1.x)) {
-	    blt(closure, x1, y, 1, h,
-		coverage * (256 - _cairo_fixed_fractional_part (b->p1.x)));
-	    x1++;
-	}
-
-	if (x2 > x1)
-	    blt(closure, x1, y, x2-x1, h, (coverage << 8) - (coverage >> 8));
-
-	if (! _cairo_fixed_is_integer (b->p2.x))
-	    blt(closure, x2, y, 1, h,
-		coverage * _cairo_fixed_fractional_part (b->p2.x));
-    } else
-	blt(closure, x1, y, 1, h,
-	    coverage * (b->p2.x - b->p1.x));
-}
-
-static void do_unaligned_box(void (*blt)(void *closure,
-					 int16_t x, int16_t y,
-					 int16_t w, int16_t h,
-					 uint16_t coverage),
-			     void *closure,
-			     const cairo_box_t *b, int tx, int ty)
-{
-    int y1 = _cairo_fixed_integer_part (b->p1.y) - ty;
-    int y2 = _cairo_fixed_integer_part (b->p2.y) - ty;
-    if (y2 > y1) {
-	if (! _cairo_fixed_is_integer (b->p1.y)) {
-	    do_unaligned_row(blt, closure, b, tx, y1, 1,
-			     256 - _cairo_fixed_fractional_part (b->p1.y));
-	    y1++;
-	}
-
-	if (y2 > y1)
-	    do_unaligned_row(blt, closure, b, tx, y1, y2-y1, 256);
-
-	if (! _cairo_fixed_is_integer (b->p2.y))
-	    do_unaligned_row(blt, closure, b, tx, y2, 1,
-			     _cairo_fixed_fractional_part (b->p2.y));
-    } else
-	do_unaligned_row(blt, closure, b, tx, y1, 1,
-			 b->p2.y - b->p1.y);
-}
-
-struct composite_opacity_info {
-    uint8_t op;
-    pixman_image_t *dst;
-    pixman_image_t *src;
-    int src_x, src_y;
-    double opacity;
-};
-
-static void composite_opacity(void *closure,
-			      int16_t x, int16_t y,
-			      int16_t w, int16_t h,
-			      uint16_t coverage)
-{
-    struct composite_opacity_info *info = closure;
-    pixman_color_t color;
-    pixman_image_t *mask;
-
-    color.red   = 0;
-    color.green = 0;
-    color.blue  = 0;
-    color.alpha = coverage * info->opacity;
-
-    mask = pixman_image_create_solid_fill (&color);
-    if (mask == NULL)
-	return;
-
-    if (info->src) {
-	pixman_image_composite32 (info->op,
-				  info->src,
-				  mask,
-				  info->dst,
-				  x + info->src_x,  y + info->src_y,
-				  0,                 0,
-				  x,                 y,
-				  w,                 h);
-    } else {
-	pixman_image_composite32 (info->op,
-				  mask,
-				  NULL,
-				  info->dst,
-				  0, 0,
-				  0, 0,
-				  x, y,
-				  w, h);
-    }
-
-    pixman_image_unref (mask);
-}
-
-static cairo_status_t
-_composite_opacity_boxes (void				*closure,
-			  pixman_image_t		*dst,
-			  pixman_format_code_t		 dst_format,
-			  cairo_operator_t		 op,
-			  const cairo_pattern_t		*src_pattern,
-			  int				 dst_x,
-			  int				 dst_y,
-			  cairo_matrix_t		*dst_device_transform,
-			  const cairo_composite_rectangles_t	*extents)
-{
-    const cairo_solid_pattern_t *mask_pattern = closure;
-    struct composite_opacity_info info;
-    int i;
-
-    info.op = _pixman_operator (op);
-    info.dst = dst;
-
-    if (src_pattern != NULL) {
-	info.src = _pixman_image_for_pattern (src_pattern, TRUE, &extents->bounded,
-					      dst_device_transform,
-					      &info.src_x, &info.src_y);
-	if (unlikely (info.src == NULL))
-	    return _cairo_error (CAIRO_STATUS_NO_MEMORY);
-    } else
-	info.src = NULL;
-
-    info.opacity = mask_pattern->color.alpha;
-
-    /* XXX for lots of boxes create a clip region for the fully opaque areas */
-    for (i = 0; i < extents->clip->num_boxes; i++)
-	do_unaligned_box(composite_opacity, &info,
-			 &extents->clip->boxes[i], dst_x, dst_y);
-    if (info.src)
-	pixman_image_unref (info.src);
-
-    return CAIRO_STATUS_SUCCESS;
-}
-
-
 static cairo_int_status_t
 _cairo_image_surface_mask (void				*abstract_surface,
 			   cairo_operator_t		 op,
@@ -3647,7 +3512,7 @@ _cairo_image_surface_mask (void				*abstract_surface,
     cairo_image_surface_t *surface = abstract_surface;
     cairo_composite_rectangles_t extents;
     cairo_rectangle_int_t unbounded;
-    cairo_status_t status;
+    cairo_int_status_t status;
 
     _cairo_image_surface_get_extents (surface, &unbounded);
     status = _cairo_composite_rectangles_init_for_mask (&extents, &unbounded,
@@ -3655,15 +3520,21 @@ _cairo_image_surface_mask (void				*abstract_surface,
     if (unlikely (status))
 	return status;
 
-    if (mask->type == CAIRO_PATTERN_TYPE_SOLID &&
-	extents.clip->path == NULL &&
-	! _cairo_clip_is_region (extents.clip))
-    {
-	status = _clip_and_composite (surface, op, source,
-				      _composite_opacity_boxes, (void *) mask,
-				      &extents, need_bounded_clip (&extents));
-    } else {
-	status = _clip_and_composite (surface, op, source,
+    status = CAIRO_INT_STATUS_UNSUPPORTED;
+    if (mask->type == CAIRO_PATTERN_TYPE_SOLID) {
+	cairo_boxes_t boxes;
+	double opacity = ((cairo_solid_pattern_t *)mask)->color.alpha;
+	status = _cairo_clip_to_boxes (extents.clip, &boxes);
+	if (likely (status == CAIRO_INT_STATUS_SUCCESS)) {
+	    status = _clip_and_composite_boxes (surface,
+						op, source,
+						opacity,
+						&boxes, &extents);
+	    _cairo_boxes_fini (&boxes);
+	}
+    }
+    if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
+	status = _clip_and_composite (surface, op, source, 1.,
 				      _composite_mask, (void *) mask,
 				      &extents, need_bounded_clip (&extents));
     }
@@ -3679,124 +3550,91 @@ typedef struct {
     cairo_antialias_t		 antialias;
 } composite_spans_info_t;
 
-//#define USE_BOTOR_SCAN_CONVERTER
 static cairo_status_t
 _composite_spans (void                          *closure,
-		  pixman_image_t		*dst,
-		  pixman_format_code_t		 dst_format,
+		  cairo_image_surface_t         *dst,
 		  cairo_operator_t               op,
 		  const cairo_pattern_t         *pattern,
+		  double                         opacity,
 		  int                            dst_x,
 		  int                            dst_y,
-		  cairo_matrix_t		*dst_device_transform,
 		  const cairo_composite_rectangles_t   *extents)
 {
-    uint8_t mask_buf[CAIRO_STACK_BUFFER_SIZE];
     composite_spans_info_t *info = closure;
-    cairo_image_surface_span_renderer_t renderer;
-#if USE_BOTOR_SCAN_CONVERTER
-    cairo_box_t box;
-    cairo_botor_scan_converter_t converter;
-#else
+    cairo_image_span_renderer_t renderer;
     cairo_scan_converter_t *converter;
-#endif
-    pixman_image_t *mask;
     const cairo_rectangle_int_t *r = &extents->bounded;
     cairo_status_t status;
 
-#if USE_BOTOR_SCAN_CONVERTER
-    box.p1.x = _cairo_fixed_from_int (r->x);
-    box.p1.y = _cairo_fixed_from_int (r->y);
-    box.p2.x = _cairo_fixed_from_int (r->x + r->width);
-    box.p2.y = _cairo_fixed_from_int (r->y + r->height);
-    _cairo_botor_scan_converter_init (&converter, &box, info->fill_rule);
-    status = converter.base.add_polygon (&converter.base, info->polygon);
-#else
+    status = _cairo_image_span_renderer_init (&renderer,
+					      dst, op, pattern, opacity,
+					      extents, FALSE);
+    if (unlikely (status))
+	return status;
+
     converter = _cairo_tor_scan_converter_create (r->x, r->y,
 						  r->x + r->width,
 						  r->y + r->height,
 						  info->fill_rule);
-    status = converter->add_polygon (converter, info->polygon);
-#endif
+
+    status= _cairo_tor_scan_converter_add_polygon (converter,
+						   info->polygon);
     if (unlikely (status))
 	goto CLEANUP_CONVERTER;
 
-    /* TODO: support rendering to A1 surfaces (or: go add span
-     * compositing to pixman.) */
-
-    if (pattern == NULL &&
-	dst_format == PIXMAN_a8 &&
-	op == CAIRO_OPERATOR_SOURCE)
-    {
-	mask = dst;
-	dst = NULL;
-    }
-    else
-    {
-	int stride = CAIRO_STRIDE_FOR_WIDTH_BPP (r->width, 8);
-	uint8_t *data = mask_buf;
+    status = converter->generate (converter, &renderer.base);
 
-	if (r->height * stride <= (int) sizeof (mask_buf))
-	    memset (data, 0, r->height * stride);
-	else
-	    data = NULL, stride = 0;
+ CLEANUP_CONVERTER:
+    _cairo_image_span_renderer_fini (&renderer);
+    converter->destroy (converter);
+    return status;
+}
 
-	mask = pixman_image_create_bits (PIXMAN_a8,
-					 r->width,
-					 r->height,
-					 (uint32_t *) data,
-					 stride);
-	if (unlikely (mask == NULL)) {
-	    status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
-	    goto CLEANUP_CONVERTER;
-	}
-    }
+static cairo_status_t
+_composite_polygon (cairo_image_surface_t *dst,
+		    cairo_operator_t op,
+		    const cairo_pattern_t *pattern,
+		    double opacity,
+		    cairo_polygon_t *polygon,
+		    cairo_fill_rule_t fill_rule,
+		    cairo_antialias_t antialias,
+		    cairo_composite_rectangles_t *extents)
+{
+    cairo_image_span_renderer_t renderer;
+    cairo_scan_converter_t *converter;
+    const cairo_rectangle_int_t *r = &extents->bounded;
+    cairo_bool_t needs_clip = !extents->is_bounded && extents->clip->path;
+    cairo_int_status_t status;
 
-    renderer.base.render_rows = _cairo_image_surface_span;
-    renderer.mask_stride = pixman_image_get_stride (mask);
-    renderer.mask_data = (uint8_t *) pixman_image_get_data (mask);
-    if (dst != NULL)
-	renderer.mask_data -= r->y * renderer.mask_stride + r->x;
-    else
-	renderer.mask_data -= dst_y * renderer.mask_stride + dst_x;
+    if (antialias == CAIRO_ANTIALIAS_NONE)
+	return CAIRO_INT_STATUS_UNSUPPORTED;
 
-#if USE_BOTOR_SCAN_CONVERTER
-    status = converter.base.generate (&converter.base, &renderer.base);
-#else
-    status = converter->generate (converter, &renderer.base);
-#endif
+    status = _cairo_image_span_renderer_init (&renderer,
+					      dst, op, pattern, opacity,
+					      extents, needs_clip);
     if (unlikely (status))
-	goto CLEANUP_RENDERER;
-
-    if (dst != NULL) {
-	pixman_image_t *src;
-	int src_x, src_y;
-
-	src = _pixman_image_for_pattern (pattern, FALSE, r,
-					 dst_device_transform,
-					 &src_x, &src_y);
-	if (unlikely (src == NULL)) {
-	    status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
-	    goto CLEANUP_RENDERER;
-	}
+	return status;
 
-	pixman_image_composite32 (_pixman_operator (op), src, mask, dst,
-                                  r->x + src_x, r->y + src_y,
-                                  0, 0, /* mask.x, mask.y */
-                                  r->x - dst_x, r->y - dst_y,
-                                  r->width, r->height);
-	pixman_image_unref (src);
+    if (needs_clip) {
+	printf ("awooga!\n");
+	converter = _cairo_clip_tor_scan_converter_create (extents->clip,
+							   polygon,
+							   fill_rule);
+    } else {
+	converter = _cairo_tor_scan_converter_create (r->x, r->y,
+						      r->x + r->width,
+						      r->y + r->height,
+						      fill_rule);
+	status = converter->add_polygon (converter, polygon);
+	if (unlikely (status))
+	    goto CLEANUP_CONVERTER;
     }
 
- CLEANUP_RENDERER:
-    if (dst != NULL)
-	pixman_image_unref (mask);
+    status = converter->generate (converter, &renderer.base);
+
  CLEANUP_CONVERTER:
-#if USE_BOTOR_SCAN_CONVERTER
-    converter.base.destroy (&converter.base);
-#else
+    _cairo_image_span_renderer_fini (&renderer);
     converter->destroy (converter);
-#endif
     return status;
 }
 
@@ -3804,12 +3642,13 @@ static cairo_status_t
 _clip_and_composite_polygon (cairo_image_surface_t *dst,
 			     cairo_operator_t op,
 			     const cairo_pattern_t *src,
+			     double opacity,
 			     cairo_polygon_t *polygon,
 			     cairo_fill_rule_t fill_rule,
 			     cairo_antialias_t antialias,
 			     cairo_composite_rectangles_t *extents)
 {
-    cairo_status_t status;
+    cairo_int_status_t status;
 
     if (_cairo_polygon_is_empty (polygon)) {
 	cairo_boxes_t boxes;
@@ -3818,12 +3657,13 @@ _clip_and_composite_polygon (cairo_image_surface_t *dst,
 	    return CAIRO_STATUS_SUCCESS;
 
 	status = _cairo_clip_to_boxes (extents->clip, &boxes);
-	if (likely (status == CAIRO_STATUS_SUCCESS)) {
+	if (likely (status == CAIRO_INT_STATUS_SUCCESS)) {
 	    extents->is_bounded = _cairo_operator_bounded_by_either (op);
 	    extents->mask = extents->bounded = extents->unbounded;
 	    status = _clip_and_composite_boxes (dst,
 						CAIRO_OPERATOR_CLEAR,
 						&_cairo_pattern_clear.base,
+						opacity,
 						&boxes, extents);
 	    _cairo_boxes_fini (&boxes);
 	}
@@ -3835,6 +3675,12 @@ _clip_and_composite_polygon (cairo_image_surface_t *dst,
     if (! _cairo_rectangle_intersect (&extents->bounded, &extents->mask))
 	return CAIRO_STATUS_SUCCESS;
 
+    status = _composite_polygon (dst, op, src, opacity,
+				 polygon, fill_rule, antialias,
+				 extents);
+    if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+	return status;
+
     if (antialias != CAIRO_ANTIALIAS_NONE) {
 	composite_spans_info_t info;
 
@@ -3842,7 +3688,7 @@ _clip_and_composite_polygon (cairo_image_surface_t *dst,
 	info.fill_rule = fill_rule;
 	info.antialias = antialias;
 
-	status = _clip_and_composite (dst, op, src,
+	status = _clip_and_composite (dst, op, src, opacity,
 				      _composite_spans, &info,
 				      extents, need_unbounded_clip (extents));
     } else {
@@ -3854,8 +3700,8 @@ _clip_and_composite_polygon (cairo_image_surface_t *dst,
 	status = _cairo_bentley_ottmann_tessellate_polygon (&traps,
 							    polygon,
 							    fill_rule);
-	if (likely (status == CAIRO_STATUS_SUCCESS)) {
-	    status = _clip_and_composite_trapezoids (dst, op, src,
+	if (likely (status == CAIRO_INT_STATUS_SUCCESS)) {
+	    status = _clip_and_composite_trapezoids (dst, op, src, opacity,
 						     &traps, antialias,
 						     extents);
 	}
@@ -3902,7 +3748,7 @@ _cairo_image_surface_stroke (void			*abstract_surface,
 								antialias,
 								&boxes);
 	if (likely (status == CAIRO_INT_STATUS_SUCCESS)) {
-	    status = _clip_and_composite_boxes (surface, op, source,
+	    status = _clip_and_composite_boxes (surface, op, source, 1.,
 						&boxes, &extents);
 	}
 	_cairo_boxes_fini (&boxes);
@@ -3918,7 +3764,8 @@ _cairo_image_surface_stroke (void			*abstract_surface,
 						      tolerance,
 						      &polygon);
 	if (likely (status == CAIRO_INT_STATUS_SUCCESS)) {
-	    status = _clip_and_composite_polygon (surface, op, source, &polygon,
+	    status = _clip_and_composite_polygon (surface, op, source, 1.,
+						  &polygon,
 						  CAIRO_FILL_RULE_WINDING,
 						  antialias,
 						  &extents);
@@ -3962,7 +3809,7 @@ _cairo_image_surface_fill (void				*abstract_surface,
 							      antialias,
 							      &boxes);
 	if (likely (status == CAIRO_STATUS_SUCCESS)) {
-	    status = _clip_and_composite_boxes (surface, op, source,
+	    status = _clip_and_composite_boxes (surface, op, source, 1.,
 						&boxes, &extents);
 	}
 	_cairo_boxes_fini (&boxes);
@@ -3974,7 +3821,8 @@ _cairo_image_surface_fill (void				*abstract_surface,
 	_cairo_polygon_init_with_clip (&polygon, extents.clip);
 	status = _cairo_path_fixed_fill_to_polygon (path, tolerance, &polygon);
 	if (likely (status == CAIRO_STATUS_SUCCESS)) {
-	    status = _clip_and_composite_polygon (surface, op, source, &polygon,
+	    status = _clip_and_composite_polygon (surface, op, source, 1.,
+						  &polygon,
 						  fill_rule, antialias,
 						  &extents);
 	}
@@ -3994,13 +3842,12 @@ typedef struct {
 
 static cairo_status_t
 _composite_glyphs_via_mask (void			*closure,
-			    pixman_image_t		*dst,
-			    pixman_format_code_t	 dst_format,
+			    cairo_image_surface_t       *dst,
 			    cairo_operator_t		 op,
 			    const cairo_pattern_t	*pattern,
+			    double                       opacity,
 			    int				 dst_x,
 			    int				 dst_y,
-			    cairo_matrix_t		*dst_device_transform,
 			    const cairo_composite_rectangles_t	*extents)
 {
     composite_glyphs_info_t *info = closure;
@@ -4015,8 +3862,10 @@ _composite_glyphs_via_mask (void			*closure,
     int src_x, src_y;
     int i;
 
+    assert (opacity == 1.);
+
     src = _pixman_image_for_pattern (pattern, FALSE, &extents->bounded,
-				     dst_device_transform,
+				     &dst->base.device_transform,
 				     &src_x, &src_y);
     if (unlikely (src == NULL))
 	return _cairo_error (CAIRO_STATUS_NO_MEMORY);
@@ -4117,7 +3966,7 @@ _composite_glyphs_via_mask (void			*closure,
     }
 
     pixman_image_composite32 (_pixman_operator (op),
-                              src, mask, dst,
+                              src, mask, dst->pixman_image,
                               extents->bounded.x + src_x, extents->bounded.y + src_y,
                               0, 0,
                               extents->bounded.x - dst_x, extents->bounded.y - dst_y,
@@ -4135,13 +3984,12 @@ CLEANUP:
 
 static cairo_status_t
 _composite_glyphs (void				*closure,
-		   pixman_image_t		*dst,
-		   pixman_format_code_t		 dst_format,
+		   cairo_image_surface_t        *dst,
 		   cairo_operator_t		 op,
 		   const cairo_pattern_t	*pattern,
+		   double                        opacity,
 		   int				 dst_x,
 		   int				 dst_y,
-		   cairo_matrix_t		*dst_device_transform,
 		   const cairo_composite_rectangles_t	*extents)
 {
     composite_glyphs_info_t *info = closure;
@@ -4152,9 +4000,11 @@ _composite_glyphs (void				*closure,
     cairo_status_t status;
     int i;
 
+    assert (opacity == 1.);
+
     if (pattern != NULL) {
 	src = _pixman_image_for_pattern (pattern, FALSE, &extents->bounded,
-					 dst_device_transform,
+					 &dst->base.device_transform,
 					 &src_x, &src_y);
 	src_x -= dst_x;
 	src_y -= dst_y;
@@ -4215,7 +4065,7 @@ _composite_glyphs (void				*closure,
 		y2 = extents->bounded.y + extents->bounded.height;
 
 	    pixman_image_composite32 (pixman_op,
-                                      src, glyph_surface->pixman_image, dst,
+                                      src, glyph_surface->pixman_image, dst->pixman_image,
                                       x1 + src_x,  y1 + src_y,
                                       x1 - x, y1 - y,
                                       x1 - dst_x, y1 - dst_y,
@@ -4267,7 +4117,7 @@ _cairo_image_surface_glyphs (void			*abstract_surface,
     {
 	flags |= FORCE_CLIP_REGION;
     }
-    status = _clip_and_composite (surface, op, source,
+    status = _clip_and_composite (surface, op, source, 1.,
 				  overlap || extents.is_bounded == 0 ?
 				  _composite_glyphs_via_mask :
 				  _composite_glyphs,
@@ -4573,14 +4423,8 @@ _cairo_image_surface_composite_trapezoids (cairo_operator_t	op,
     info.traps = traps;
     info.num_traps = num_traps;
     info.antialias = antialias;
-    status = _composite_traps (&info,
-			       dst->pixman_image,
-			       dst->pixman_format,
-			       op,
-			       &source_pattern.base,
-			       0, 0,
-			       &dst->base.device_transform,
-			       &extents);
+    status = _composite_traps (&info, dst, op, &source_pattern.base, 1.,
+			       0, 0, &extents);
 
     if (status == CAIRO_STATUS_SUCCESS && ! extents.is_bounded)
 	status = _cairo_image_surface_fixup_unbounded (dst, &extents, NULL);
diff --git a/src/cairo-rectangular-scan-converter.c b/src/cairo-rectangular-scan-converter.c
index 56e552b..1922364 100644
--- a/src/cairo-rectangular-scan-converter.c
+++ b/src/cairo-rectangular-scan-converter.c
@@ -380,6 +380,7 @@ _active_edges_to_spans (sweep_line_t	*sweep)
 	if (cell->x != prev_x && coverage != prev_coverage) {
 	    int n = sweep->num_spans++;
 	    sweep->spans[n].x = prev_x;
+	    sweep->spans[n].is_clipped = 0;
 	    sweep->spans[n].coverage = coverage >> (CAIRO_FIXED_FRAC_BITS * 2 - 8);
 	    sweep->spans[n].coverage -= sweep->spans[n].coverage >> 8;
 	    prev_coverage = coverage;
@@ -389,6 +390,7 @@ _active_edges_to_spans (sweep_line_t	*sweep)
 	if (coverage != prev_coverage) {
 	    int n = sweep->num_spans++;
 	    sweep->spans[n].x = cell->x;
+	    sweep->spans[n].is_clipped = 0;
 	    sweep->spans[n].coverage = coverage >> (CAIRO_FIXED_FRAC_BITS * 2 - 8);
 	    sweep->spans[n].coverage -= sweep->spans[n].coverage >> 8;
 	    prev_coverage = coverage;
@@ -402,12 +404,14 @@ _active_edges_to_spans (sweep_line_t	*sweep)
 	if (prev_x <= sweep->xmax) {
 	    int n = sweep->num_spans++;
 	    sweep->spans[n].x = prev_x;
+	    sweep->spans[n].is_clipped = 0;
 	    sweep->spans[n].coverage = coverage;
 	}
 
 	if (coverage && prev_x < sweep->xmax) {
 	    int n = sweep->num_spans++;
 	    sweep->spans[n].x = sweep->xmax;
+	    sweep->spans[n].is_clipped = 1;
 	    sweep->spans[n].coverage = 0;
 	}
     }
diff --git a/src/cairo-spans-private.h b/src/cairo-spans-private.h
index 00a4df8..45853f0 100644
--- a/src/cairo-spans-private.h
+++ b/src/cairo-spans-private.h
@@ -39,6 +39,8 @@ typedef struct _cairo_half_open_span {
     /* The inclusive x-coordinate of the start of the span. */
     int x;
 
+    cairo_bool_t is_clipped;
+
     /* The pixel coverage for the pixels to the right. */
     int coverage;
 } cairo_half_open_span_t;
@@ -102,6 +104,14 @@ _cairo_tor_scan_converter_create (int			xmin,
 				  int			xmax,
 				  int			ymax,
 				  cairo_fill_rule_t	fill_rule);
+cairo_private cairo_status_t
+_cairo_tor_scan_converter_add_polygon (void		*converter,
+				       const cairo_polygon_t *polygon);
+
+cairo_private cairo_scan_converter_t *
+_cairo_clip_tor_scan_converter_create (cairo_clip_t *clip,
+				       cairo_polygon_t *polygon,
+				       cairo_fill_rule_t fill_rule);
 
 typedef struct _cairo_rectangular_scan_converter {
     cairo_scan_converter_t base;
diff --git a/src/cairo-tor-scan-converter.c b/src/cairo-tor-scan-converter.c
index d9c9ce8..9cf9f2f 100644
--- a/src/cairo-tor-scan-converter.c
+++ b/src/cairo-tor-scan-converter.c
@@ -134,9 +134,6 @@ blit_with_span_renderer(
     int				 xmin,
     int				 xmax);
 
-static glitter_status_t
-blit_empty_with_span_renderer (cairo_span_renderer_t *renderer, int y, int height);
-
 #define GLITTER_BLIT_COVERAGES_ARGS \
 	cairo_span_renderer_t *span_renderer, \
 	struct pool *span_pool
@@ -149,10 +146,6 @@ blit_empty_with_span_renderer (cairo_span_renderer_t *renderer, int y, int heigh
 			     xmin, xmax);	\
 } while (0)
 
-#define GLITTER_BLIT_COVERAGES_EMPTY(y, height, xmin, xmax) do {		\
-    blit_empty_with_span_renderer (span_renderer, y, height); \
-} while (0)
-
 /*-------------------------------------------------------------------------
  * glitter-paths.h
  */
@@ -387,10 +380,7 @@ struct edge {
     grid_scaled_y_t dy;
 };
 
-/* Number of subsample rows per y-bucket. Must be GRID_Y. */
-#define EDGE_Y_BUCKET_HEIGHT GRID_Y
-
-#define EDGE_Y_BUCKET_INDEX(y, ymin) (((y) - (ymin))/EDGE_Y_BUCKET_HEIGHT)
+#define EDGE_Y_BUCKET_INDEX(y, ymin) (((y) - (ymin))/GRID_Y)
 
 /* A collection of sorted and vertically clipped edges of the polygon.
  * Edges are moved from the polygon to an active list while scan
@@ -1005,12 +995,11 @@ polygon_reset (struct polygon *polygon,
 	       grid_scaled_y_t ymax)
 {
     unsigned h = ymax - ymin;
-    unsigned num_buckets = EDGE_Y_BUCKET_INDEX(ymax + EDGE_Y_BUCKET_HEIGHT-1,
-					       ymin);
+    unsigned num_buckets = EDGE_Y_BUCKET_INDEX(ymax + GRID_Y-1, ymin);
 
     pool_reset(polygon->edge_pool.base);
 
-    if (unlikely (h > 0x7FFFFFFFU - EDGE_Y_BUCKET_HEIGHT))
+    if (unlikely (h > 0x7FFFFFFFU - GRID_Y))
 	goto bail_no_mem; /* even if you could, you wouldn't want to. */
 
     if (polygon->y_buckets != polygon->y_buckets_embedded)
@@ -1653,18 +1642,6 @@ glitter_scan_converter_add_edge (glitter_scan_converter_t *converter,
     polygon_add_edge (converter->polygon, &e);
 }
 
-#ifndef GLITTER_BLIT_COVERAGES_BEGIN
-# define GLITTER_BLIT_COVERAGES_BEGIN
-#endif
-
-#ifndef GLITTER_BLIT_COVERAGES_END
-# define GLITTER_BLIT_COVERAGES_END
-#endif
-
-#ifndef GLITTER_BLIT_COVERAGES_EMPTY
-# define GLITTER_BLIT_COVERAGES_EMPTY(y0, y1, xmin, xmax)
-#endif
-
 static cairo_bool_t
 active_list_is_vertical (struct active_list *active)
 {
@@ -1714,9 +1691,6 @@ glitter_scan_converter_render(
     if (xmin_i >= xmax_i)
 	return;
 
-    /* Let the coverage blitter initialise itself. */
-    GLITTER_BLIT_COVERAGES_BEGIN;
-
     /* Render each pixel row. */
     for (i = 0; i < h; i = j) {
 	int do_full_step = 0;
@@ -1730,7 +1704,6 @@ glitter_scan_converter_render(
 		active->min_height = INT_MAX;
 		for (; j < h && ! polygon->y_buckets[j]; j++)
 		    ;
-		GLITTER_BLIT_COVERAGES_EMPTY (i+ymin_i, j-i, xmin_i, xmax_i);
 		continue;
 	    }
 
@@ -1784,9 +1757,6 @@ glitter_scan_converter_render(
 
 	active->min_height -= GRID_Y;
     }
-
-    /* Clean up the coverage blitter. */
-    GLITTER_BLIT_COVERAGES_END;
 }
 
 /*-------------------------------------------------------------------------
@@ -1807,7 +1777,7 @@ blit_with_span_renderer (struct cell_list *cells,
     unsigned num_spans;
 
     if (cell == &cells->tail)
-	return blit_empty_with_span_renderer (renderer, y, height);
+	return CAIRO_STATUS_SUCCESS;
 
     /* Skip cells to the left of the clip region. */
     while (cell->x < xmin) {
@@ -1839,6 +1809,7 @@ blit_with_span_renderer (struct cell_list *cells,
 
 	if (x > prev_x && cover != last_cover) {
 	    spans[num_spans].x = prev_x;
+	    spans[num_spans].is_clipped = 0;
 	    spans[num_spans].coverage = GRID_AREA_TO_ALPHA (cover);
 	    last_cover = cover;
 	    last_x = prev_x;
@@ -1850,6 +1821,7 @@ blit_with_span_renderer (struct cell_list *cells,
 
 	if (area != last_cover) {
 	    spans[num_spans].x = x;
+	    spans[num_spans].is_clipped = 0;
 	    spans[num_spans].coverage = GRID_AREA_TO_ALPHA (area);
 	    last_cover = area;
 	    last_x = x;
@@ -1861,6 +1833,7 @@ blit_with_span_renderer (struct cell_list *cells,
 
     if (prev_x <= xmax && cover != last_cover) {
 	spans[num_spans].x = prev_x;
+	spans[num_spans].is_clipped = 0;
 	spans[num_spans].coverage = GRID_AREA_TO_ALPHA (cover);
 	last_cover = cover;
 	last_x = prev_x;
@@ -1877,12 +1850,6 @@ blit_with_span_renderer (struct cell_list *cells,
     return renderer->render_rows (renderer, y, height, spans, num_spans);
 }
 
-static glitter_status_t
-blit_empty_with_span_renderer (cairo_span_renderer_t *renderer, int y, int height)
-{
-    return renderer->render_rows (renderer, y, height, NULL, 0);
-}
-
 struct _cairo_tor_scan_converter {
     cairo_scan_converter_t base;
 
@@ -1931,7 +1898,7 @@ _cairo_tor_scan_converter_add_edge (void		*converter,
     return CAIRO_STATUS_SUCCESS;
 }
 
-static cairo_status_t
+cairo_status_t
 _cairo_tor_scan_converter_add_polygon (void		*converter,
 				       const cairo_polygon_t *polygon)
 {
commit c822674f93110fcd4a68421807c8736a788c6f32
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Tue Aug 2 22:31:49 2011 +0100

    Introduce cairo_mime_surface_t
    
    The mime surface is a user-callback surface designed for interfacing
    cairo with an opaque data source. For instance, in a web browser, the
    incoming page may be laid out and rendered to a recording surface before
    all the image data has finished being downloaded. In this circumstance
    we need to pass a place holder to cairo and to supply the image data
    later upon demand.

diff --git a/boilerplate/Makefile.win32.features b/boilerplate/Makefile.win32.features
index 0cf7095..3542699 100644
--- a/boilerplate/Makefile.win32.features
+++ b/boilerplate/Makefile.win32.features
@@ -398,6 +398,16 @@ enabled_cairo_boilerplate_private += $(cairo_boilerplate_image_private)
 enabled_cairo_boilerplate_cxx_sources += $(cairo_boilerplate_image_cxx_sources)
 enabled_cairo_boilerplate_sources += $(cairo_boilerplate_image_sources)
 
+supported_cairo_boilerplate_headers += $(cairo_boilerplate_mime_headers)
+all_cairo_boilerplate_headers += $(cairo_boilerplate_mime_headers)
+all_cairo_boilerplate_private += $(cairo_boilerplate_mime_private)
+all_cairo_boilerplate_cxx_sources += $(cairo_boilerplate_mime_cxx_sources)
+all_cairo_boilerplate_sources += $(cairo_boilerplate_mime_sources)
+enabled_cairo_boilerplate_headers += $(cairo_boilerplate_mime_headers)
+enabled_cairo_boilerplate_private += $(cairo_boilerplate_mime_private)
+enabled_cairo_boilerplate_cxx_sources += $(cairo_boilerplate_mime_cxx_sources)
+enabled_cairo_boilerplate_sources += $(cairo_boilerplate_mime_sources)
+
 supported_cairo_boilerplate_headers += $(cairo_boilerplate_recording_headers)
 all_cairo_boilerplate_headers += $(cairo_boilerplate_recording_headers)
 all_cairo_boilerplate_private += $(cairo_boilerplate_recording_private)
diff --git a/build/Makefile.win32.features-h b/build/Makefile.win32.features-h
index e35cc6c..79de87d 100644
--- a/build/Makefile.win32.features-h
+++ b/build/Makefile.win32.features-h
@@ -99,6 +99,7 @@ ifeq ($(CAIRO_HAS_TEST_SURFACES),1)
 	@echo "#define CAIRO_HAS_TEST_SURFACES 1" >> $(top_srcdir)/src/cairo-features.h
 endif
 	@echo "#define CAIRO_HAS_IMAGE_SURFACE 1" >> $(top_srcdir)/src/cairo-features.h
+	@echo "#define CAIRO_HAS_MIME_SURFACE 1" >> $(top_srcdir)/src/cairo-features.h
 	@echo "#define CAIRO_HAS_RECORDING_SURFACE 1" >> $(top_srcdir)/src/cairo-features.h
 ifeq ($(CAIRO_HAS_TEE_SURFACE),1)
 	@echo "#define CAIRO_HAS_TEE_SURFACE 1" >> $(top_srcdir)/src/cairo-features.h
diff --git a/build/configure.ac.features b/build/configure.ac.features
index 4cccd9c..9c5680a 100644
--- a/build/configure.ac.features
+++ b/build/configure.ac.features
@@ -365,6 +365,7 @@ AC_DEFUN([CAIRO_REPORT],
 	echo "The following surface backends:"
 	echo "  Image:         yes (always builtin)"
 	echo "  Recording:     yes (always builtin)"
+	echo "  Mime:          yes (always builtin)"
 	echo "  Tee:           $use_tee"
 	echo "  XML:           $use_xml"
 	echo "  Skia:          $use_skia"
diff --git a/configure.ac b/configure.ac
index 8e6bc4d..793263f 100644
--- a/configure.ac
+++ b/configure.ac
@@ -612,6 +612,7 @@ CAIRO_ENABLE_SURFACE_BACKEND(image, image, always, [
 
 dnl ===========================================================================
 
+CAIRO_ENABLE_SURFACE_BACKEND(mime, mime, always)
 CAIRO_ENABLE_SURFACE_BACKEND(recording, recording, always)
 CAIRO_ENABLE_SURFACE_BACKEND(tee, tee, no)
 CAIRO_ENABLE_SURFACE_BACKEND(xml, xml, no, [
diff --git a/src/Makefile.sources b/src/Makefile.sources
index b5e4805..f22f7ed 100644
--- a/src/Makefile.sources
+++ b/src/Makefile.sources
@@ -148,6 +148,7 @@ cairo_sources = \
 	cairo-lzw.c \
 	cairo-matrix.c \
 	cairo-mesh-pattern-rasterizer.c \
+	cairo-mime-surface.c \
 	cairo-misc.c \
 	cairo-mutex.c \
 	cairo-observer.c \
diff --git a/src/Makefile.win32.features b/src/Makefile.win32.features
index 640df73..6e72fdb 100644
--- a/src/Makefile.win32.features
+++ b/src/Makefile.win32.features
@@ -520,6 +520,16 @@ enabled_cairo_private += $(cairo_image_private)
 enabled_cairo_cxx_sources += $(cairo_image_cxx_sources)
 enabled_cairo_sources += $(cairo_image_sources)
 
+supported_cairo_headers += $(cairo_mime_headers)
+all_cairo_headers += $(cairo_mime_headers)
+all_cairo_private += $(cairo_mime_private)
+all_cairo_cxx_sources += $(cairo_mime_cxx_sources)
+all_cairo_sources += $(cairo_mime_sources)
+enabled_cairo_headers += $(cairo_mime_headers)
+enabled_cairo_private += $(cairo_mime_private)
+enabled_cairo_cxx_sources += $(cairo_mime_cxx_sources)
+enabled_cairo_sources += $(cairo_mime_sources)
+
 supported_cairo_headers += $(cairo_recording_headers)
 all_cairo_headers += $(cairo_recording_headers)
 all_cairo_private += $(cairo_recording_private)
diff --git a/src/cairo-mime-surface.c b/src/cairo-mime-surface.c
new file mode 100644
index 0000000..d1ec974
--- /dev/null
+++ b/src/cairo-mime-surface.c
@@ -0,0 +1,347 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2011 Intel Corporation
+ *
+ * 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 Red Hat, Inc.
+ *
+ * Contributor(s):
+ *	Chris Wilson <chris at chris-wilson.co.uk>
+ */
+
+/**
+ * SECTION:cairo-mime-surface
+ * @Title: Callback Surfaces
+ * @Short_Description: Allows the user to provide a callback to supply image
+ * data upon demand
+ * @See_Also: #cairo_surface_t
+ *
+ */
+
+#include "cairoint.h"
+#include "cairo-error-private.h"
+
+typedef struct _cairo_mime_surface {
+    cairo_surface_t base;
+
+    cairo_rectangle_int_t extents;
+
+    cairo_mime_surface_acquire_t acquire;
+    cairo_mime_surface_release_t release;
+    cairo_mime_surface_snapshot_t snapshot;
+    cairo_mime_surface_destroy_t destroy;
+
+    /* an explicit pre-allocated member in preference to the general user-data */
+    void *user_data;
+} cairo_mime_surface_t;
+
+static cairo_status_t
+_cairo_mime_surface_finish (void *abstract_surface)
+{
+    cairo_mime_surface_t *surface = abstract_surface;
+
+    if (surface->destroy)
+	surface->destroy (&surface->base, surface->user_data);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_surface_t *
+_cairo_mime_surface_create_similar (void	       *abstract_other,
+				    cairo_content_t	content,
+				    int		width,
+				    int		height)
+{
+    cairo_mime_surface_t *other = abstract_other;
+    cairo_mime_surface_t *surface;
+
+    surface = (cairo_mime_surface_t *)
+	cairo_mime_surface_create (NULL, content, width, height);
+    if (likely (surface->base.status == CAIRO_STATUS_SUCCESS)) {
+	surface->acquire = other->acquire;
+	surface->release = other->release;
+	surface->snapshot = other->snapshot;
+	surface->destroy = other->destroy;
+    }
+
+    return &surface->base;
+}
+
+static cairo_bool_t
+_cairo_mime_surface_get_extents (void			  *abstract_surface,
+				 cairo_rectangle_int_t   *rectangle)
+{
+    cairo_mime_surface_t *surface = abstract_surface;
+
+    *rectangle = surface->extents;
+    return TRUE;
+}
+
+static cairo_status_t
+_cairo_mime_surface_acquire_source_image (void                    *abstract_surface,
+					  //cairo_surface_t	  *target,
+					  cairo_image_surface_t  **image_out,
+					  void                   **image_extra)
+{
+    cairo_mime_surface_t *mime = abstract_surface;
+    cairo_surface_t *acquired;
+
+    if (mime->acquire == NULL)
+	return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    acquired = mime->acquire (&mime->base, mime->user_data, NULL, NULL);
+
+    *image_out = (cairo_image_surface_t *) acquired;
+    *image_extra = NULL;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_mime_surface_release_source_image (void                   *abstract_surface,
+					  cairo_image_surface_t  *image,
+					  void                   *image_extra)
+{
+    cairo_mime_surface_t *mime = abstract_surface;
+
+    if (mime->release)
+	mime->release (&mime->base, mime->user_data, &image->base);
+}
+
+static cairo_surface_t *
+_cairo_mime_surface_snapshot (void *abstract_surface)
+{
+    cairo_mime_surface_t *mime = abstract_surface;
+
+    if (mime->snapshot == NULL)
+	return NULL;
+
+    return mime->snapshot (&mime->base, mime->user_data);
+}
+
+static const cairo_surface_backend_t cairo_mime_surface_backend = {
+    CAIRO_SURFACE_TYPE_RECORDING,
+    _cairo_mime_surface_finish,
+
+    NULL, //_cairo_default_context_create,
+
+    _cairo_mime_surface_create_similar,
+    NULL, /* create similar image */
+    NULL, /* map to image */
+    NULL, /* unmap image */
+
+    _cairo_mime_surface_acquire_source_image,
+    _cairo_mime_surface_release_source_image,
+    NULL, /* acquire_dest_image */
+    NULL, /* release_dest_image */
+    NULL, /* clone_similar */
+    NULL, /* composite */
+    NULL, /* fill_rectangles */
+    NULL, /* composite_trapezoids */
+    NULL, /* create_span_renderer */
+    NULL, /* check_span_renderer */
+    NULL, /* copy_page */
+    NULL, /* show_page */
+    _cairo_mime_surface_get_extents,
+    NULL, /* old_show_glyphs */
+    NULL, /* get_font_options */
+    NULL, /* flush */
+    NULL, /* mark_dirty_rectangle */
+    NULL, /* scaled_font_fini */
+    NULL, /* scaled_glyph_fini */
+
+
+    NULL, //_cairo_mime_surface_paint,
+    NULL, //_cairo_mime_surface_mask,
+    NULL, //_cairo_mime_surface_stroke,
+    NULL, //_cairo_mime_surface_fill,
+    NULL, // glyphs
+
+    _cairo_mime_surface_snapshot,
+};
+
+cairo_surface_t *
+cairo_mime_surface_create (void *data, cairo_content_t content, int width, int height)
+{
+    cairo_mime_surface_t *surface;
+
+    if (width < 0 || height < 0)
+	return _cairo_surface_create_in_error (CAIRO_STATUS_INVALID_SIZE);
+
+    if (! CAIRO_CONTENT_VALID (content))
+	return _cairo_surface_create_in_error (CAIRO_STATUS_INVALID_CONTENT);
+
+    surface = calloc (1, sizeof (*surface));
+    if (unlikely (surface == NULL))
+	return _cairo_surface_create_in_error (CAIRO_STATUS_NO_MEMORY);
+
+    _cairo_surface_init (&surface->base,
+			 &cairo_mime_surface_backend,
+			 NULL, /* device */
+			 content);
+
+    surface->extents.x = 0;
+    surface->extents.y = 0;
+    surface->extents.width  = width;
+    surface->extents.height = height;
+
+    surface->user_data = data;
+
+    return &surface->base;
+}
+
+void
+cairo_mime_surface_set_callback_data (cairo_surface_t *surface,
+				      void *data)
+{
+    cairo_mime_surface_t *mime;
+
+    if (CAIRO_REFERENCE_COUNT_IS_INVALID (&surface->ref_count))
+	return;
+
+    if (surface->backend != &cairo_mime_surface_backend)
+	return;
+
+    mime = (cairo_mime_surface_t *)surface;
+    mime->user_data = data;
+}
+
+void *
+cairo_mime_surface_get_callback_data (cairo_surface_t *surface)
+{
+    cairo_mime_surface_t *mime;
+
+    if (CAIRO_REFERENCE_COUNT_IS_INVALID (&surface->ref_count))
+	return NULL;
+
+    if (surface->backend != &cairo_mime_surface_backend)
+	return NULL;
+
+    mime = (cairo_mime_surface_t *)surface;
+    return mime->user_data;
+}
+
+void
+cairo_mime_surface_set_acquire (cairo_surface_t *surface,
+				cairo_mime_surface_acquire_t acquire,
+				cairo_mime_surface_release_t release)
+{
+    cairo_mime_surface_t *mime;
+
+    if (CAIRO_REFERENCE_COUNT_IS_INVALID (&surface->ref_count))
+	return;
+
+    if (surface->backend != &cairo_mime_surface_backend)
+	return;
+
+    mime = (cairo_mime_surface_t *)surface;
+    mime->acquire = acquire;
+    mime->release = release;
+}
+
+void
+cairo_mime_surface_get_acquire (cairo_surface_t *surface,
+				cairo_mime_surface_acquire_t *acquire,
+				cairo_mime_surface_release_t *release)
+{
+    cairo_mime_surface_t *mime;
+
+    if (CAIRO_REFERENCE_COUNT_IS_INVALID (&surface->ref_count))
+	return;
+
+    if (surface->backend != &cairo_mime_surface_backend)
+	return;
+
+    mime = (cairo_mime_surface_t *)surface;
+    if (acquire)
+	*acquire = mime->acquire;
+    if (release)
+	*release = mime->release;
+}
+
+void
+cairo_mime_surface_set_snapshot (cairo_surface_t *surface,
+				 cairo_mime_surface_snapshot_t snapshot)
+{
+    cairo_mime_surface_t *mime;
+
+    if (CAIRO_REFERENCE_COUNT_IS_INVALID (&surface->ref_count))
+	return;
+
+    if (surface->backend != &cairo_mime_surface_backend)
+	return;
+
+    mime = (cairo_mime_surface_t *)surface;
+    mime->snapshot = snapshot;
+}
+
+cairo_mime_surface_snapshot_t
+cairo_mime_surface_get_snapshot (cairo_surface_t *surface)
+{
+    cairo_mime_surface_t *mime;
+
+    if (CAIRO_REFERENCE_COUNT_IS_INVALID (&surface->ref_count))
+	return NULL;
+
+    if (surface->backend != &cairo_mime_surface_backend)
+	return NULL;
+
+    mime = (cairo_mime_surface_t *)surface;
+    return mime->snapshot;
+}
+
+void
+cairo_mime_surface_set_destroy (cairo_surface_t *surface,
+				cairo_mime_surface_destroy_t destroy)
+{
+    cairo_mime_surface_t *mime;
+
+    if (CAIRO_REFERENCE_COUNT_IS_INVALID (&surface->ref_count))
+	return;
+
+    if (surface->backend != &cairo_mime_surface_backend)
+	return;
+
+    mime = (cairo_mime_surface_t *)surface;
+    mime->destroy = destroy;
+}
+
+cairo_mime_surface_destroy_t
+cairo_mime_surface_get_destroy (cairo_surface_t *surface)
+{
+    cairo_mime_surface_t *mime;
+
+    if (CAIRO_REFERENCE_COUNT_IS_INVALID (&surface->ref_count))
+	return NULL;
+
+    if (surface->backend != &cairo_mime_surface_backend)
+	return NULL;
+
+    mime = (cairo_mime_surface_t *)surface;
+    return mime->destroy;
+}
diff --git a/src/cairo.h b/src/cairo.h
index 9d788cc..7987192 100644
--- a/src/cairo.h
+++ b/src/cairo.h
@@ -433,6 +433,24 @@ typedef cairo_status_t (*cairo_read_func_t) (void		*closure,
 					     unsigned char	*data,
 					     unsigned int	length);
 
+/**
+ * cairo_rectangle_int_t:
+ * @x: X coordinate of the left side of the rectangle
+ * @y: Y coordinate of the the top side of the rectangle
+ * @width: width of the rectangle
+ * @height: height of the rectangle
+ *
+ * A data structure for holding a rectangle with integer coordinates.
+ *
+ * Since: 1.10
+ **/
+
+typedef struct _cairo_rectangle_int {
+    int x, y;
+    int width, height;
+} cairo_rectangle_int_t;
+
+
 /* Functions for manipulating state objects */
 cairo_public cairo_t *
 cairo_create (cairo_surface_t *target);
@@ -2344,6 +2362,55 @@ cairo_recording_surface_ink_extents (cairo_surface_t *surface,
                                      double *width,
                                      double *height);
 
+/* Mime-surface (callback) functions */
+
+typedef cairo_surface_t *(*cairo_mime_surface_acquire_t) (cairo_surface_t *mime_surface,
+							  void *callback_data,
+							  cairo_surface_t *target,
+							  cairo_rectangle_int_t *sample_extents);
+
+typedef void (*cairo_mime_surface_release_t) (cairo_surface_t *mime_surface,
+					      void *callback_data,
+					      cairo_surface_t *image_surface);
+
+typedef cairo_surface_t *(*cairo_mime_surface_snapshot_t) (cairo_surface_t *mime_surface,
+							   void *callback_data);
+typedef void (*cairo_mime_surface_destroy_t) (cairo_surface_t *mime_surface,
+					      void *callback_data);
+
+cairo_public cairo_surface_t *
+cairo_mime_surface_create (void *data, cairo_content_t content, int width, int height);
+
+cairo_public void
+cairo_mime_surface_set_callback_data (cairo_surface_t *surface,
+				      void *data);
+
+cairo_public void *
+cairo_mime_surface_get_callback_data (cairo_surface_t *surface);
+
+cairo_public void
+cairo_mime_surface_set_acquire (cairo_surface_t *surface,
+				cairo_mime_surface_acquire_t acquire,
+				cairo_mime_surface_release_t release);
+
+cairo_public void
+cairo_mime_surface_get_acquire (cairo_surface_t *surface,
+				cairo_mime_surface_acquire_t *acquire,
+				cairo_mime_surface_release_t *release);
+cairo_public void
+cairo_mime_surface_set_snapshot (cairo_surface_t *surface,
+				 cairo_mime_surface_snapshot_t snapshot);
+
+cairo_public cairo_mime_surface_snapshot_t
+cairo_mime_surface_get_snapshot (cairo_surface_t *surface);
+
+cairo_public void
+cairo_mime_surface_set_destroy (cairo_surface_t *surface,
+				cairo_mime_surface_destroy_t destroy);
+
+cairo_public cairo_mime_surface_destroy_t
+cairo_mime_surface_get_destroy (cairo_surface_t *surface);
+
 /* Pattern creation functions */
 
 cairo_public cairo_pattern_t *
@@ -2671,23 +2738,6 @@ cairo_matrix_transform_point (const cairo_matrix_t *matrix,
  **/
 typedef struct _cairo_region cairo_region_t;
 
-/**
- * cairo_rectangle_int_t:
- * @x: X coordinate of the left side of the rectangle
- * @y: Y coordinate of the the top side of the rectangle
- * @width: width of the rectangle
- * @height: height of the rectangle
- *
- * A data structure for holding a rectangle with integer coordinates.
- *
- * Since: 1.10
- **/
-
-typedef struct _cairo_rectangle_int {
-    int x, y;
-    int width, height;
-} cairo_rectangle_int_t;
-
 typedef enum _cairo_region_overlap {
     CAIRO_REGION_OVERLAP_IN,		/* completely inside region */
     CAIRO_REGION_OVERLAP_OUT,		/* completely outside region */


More information about the cairo-commit mailing list