[cairo-commit] 7 commits - src/cairo-cogl-surface.c src/cairoint.h src/cairo-path-stroke.c src/cairo-path-stroke-traps.c src/cairo-recording-surface.c src/cairo-rectangle.c src/cairo-scaled-font.c src/cairo-stroke-style.c src/cairo-traps.c src/cairo-traps-compositor.c src/cairo-traps-private.h src/cairo-xlib-source.c src/cairo-xlib-surface.c src/cairo-xlib-surface-shm.c src/Makefile.sources src/test-base-compositor-surface.c util/cairo-gobject

Chris Wilson ickle at kemper.freedesktop.org
Thu Jan 3 07:54:28 PST 2013


 src/Makefile.sources               |    1 
 src/cairo-cogl-surface.c           |    6 
 src/cairo-path-stroke-traps.c      | 1120 +++++++++++++++++++++++++++++++++++++
 src/cairo-path-stroke.c            |   12 
 src/cairo-recording-surface.c      |   12 
 src/cairo-rectangle.c              |    2 
 src/cairo-scaled-font.c            |    3 
 src/cairo-stroke-style.c           |   39 +
 src/cairo-traps-compositor.c       |   37 -
 src/cairo-traps-private.h          |    9 
 src/cairo-traps.c                  |  252 ++++++++
 src/cairo-xlib-source.c            |   37 -
 src/cairo-xlib-surface-shm.c       |    8 
 src/cairo-xlib-surface.c           |  100 ++-
 src/cairoint.h                     |   22 
 src/test-base-compositor-surface.c |    8 
 util/cairo-gobject/cairo-gobject.h |    2 
 17 files changed, 1601 insertions(+), 69 deletions(-)

New commits:
commit ae1724ced98b86aaf97c7be9c4294fa3823d7350
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Thu Jan 3 13:46:20 2013 +0000

    Add missing local slim proto for cairo_recording_surface_create
    
    Dependency introduced in 749ef6be4d11b95d666b0e5fe06df926b828d655
    
    Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>

diff --git a/src/cairoint.h b/src/cairoint.h
index 309987c..861e2f7 100644
--- a/src/cairoint.h
+++ b/src/cairoint.h
@@ -1916,6 +1916,7 @@ slim_hidden_proto (cairo_pattern_set_matrix);
 slim_hidden_proto (cairo_pop_group);
 slim_hidden_proto (cairo_push_group_with_content);
 slim_hidden_proto_no_warn (cairo_path_destroy);
+slim_hidden_proto (cairo_recording_surface_create);
 slim_hidden_proto (cairo_rel_line_to);
 slim_hidden_proto (cairo_restore);
 slim_hidden_proto (cairo_save);
commit 872a92b874270ac3b83b0e206fb5b15a7405502a
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Thu Jan 3 13:30:13 2013 +0000

    scaled-font: Mention ownership of returned object from get_font_face()
    
    As suggested by Simon Sapin.
    
    Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>

diff --git a/src/cairo-scaled-font.c b/src/cairo-scaled-font.c
index e7eb4cc..dc6a6fd 100644
--- a/src/cairo-scaled-font.c
+++ b/src/cairo-scaled-font.c
@@ -3004,7 +3004,8 @@ _cairo_scaled_font_get_max_scale (cairo_scaled_font_t *scaled_font)
  * hold true for all possible cases.
  *
  * Return value: The #cairo_font_face_t with which @scaled_font was
- * created.
+ * created.  This object is owned by cairo. To keep a reference to it,
+ * you must call cairo_scaled_font_reference().
  *
  * Since: 1.2
  **/
commit 734a541dc34565f40fe0ae4e93c81c4849198a79
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Thu Jan 3 13:01:34 2013 +0000

    xlib: Avoid copying the source twice if it is an image
    
    Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>

diff --git a/src/cairo-xlib-source.c b/src/cairo-xlib-source.c
index 42fc46a..e312222 100644
--- a/src/cairo-xlib-source.c
+++ b/src/cairo-xlib-source.c
@@ -46,7 +46,7 @@
 #include "cairo-xlib-surface-private.h"
 
 #include "cairo-error-private.h"
-#include "cairo-image-surface-private.h"
+#include "cairo-image-surface-inline.h"
 #include "cairo-paginated-private.h"
 #include "cairo-pattern-inline.h"
 #include "cairo-recording-surface-private.h"
@@ -897,8 +897,10 @@ surface_source (cairo_xlib_surface_t *dst,
     cairo_xlib_surface_t *xsrc;
     cairo_surface_pattern_t local_pattern;
     cairo_status_t status;
-    cairo_rectangle_int_t upload, limit, map_extents;
+    cairo_rectangle_int_t upload, limit;
     cairo_matrix_t m;
+    pixman_format_code_t format;
+    int draw_x, draw_y;
 
     src = pattern->surface;
     if (src->type == CAIRO_SURFACE_TYPE_IMAGE &&
@@ -952,18 +954,31 @@ prepare_shm_image:
 	}
     }
 
-    src = _cairo_xlib_surface_create_similar_shm (&dst->base,
-						  _cairo_format_from_content (pattern->surface->content),
-						  upload.width,
-						  upload.height);
+    if (_cairo_surface_is_image (src))
+	format = ((cairo_image_surface_t *)src)->pixman_format;
+    else
+	format = _cairo_format_to_pixman_format_code (_cairo_format_from_content (src->content));
+    src = _cairo_xlib_surface_create_shm (dst, format,
+					  upload.width, upload.height);
+    if (src == NULL) {
+	if (_cairo_surface_is_image (pattern->surface)) {
+	    draw_x = upload.x;
+	    draw_y = upload.y;
+	    src = cairo_surface_reference (pattern->surface);
+	    goto skip_paint;
+	}
+
+	src = _cairo_image_surface_create_with_pixman_format (NULL,
+							      format,
+							      upload.width,
+							      upload.height,
+							      0);
+    }
 
     _cairo_pattern_init_for_surface (&local_pattern, pattern->surface);
     cairo_matrix_init_translate (&local_pattern.base.matrix,
 				 upload.x, upload.y);
 
-    map_extents = upload;
-    map_extents.x = map_extents.y = 0;
-
     status = _cairo_surface_paint (src,
 				   CAIRO_OPERATOR_SOURCE,
 				   &local_pattern.base,
@@ -975,6 +990,8 @@ prepare_shm_image:
 	return _cairo_surface_create_in_error (status);
     }
 
+    draw_x = draw_y = 0;
+skip_paint:
     _cairo_pattern_init_static_copy (&local_pattern.base, &pattern->base);
     if (upload.x | upload.y) {
 	cairo_matrix_init_translate (&m, -upload.x, -upload.y);
@@ -1002,7 +1019,7 @@ prepare_shm_image:
     }
 
     status = _cairo_xlib_surface_draw_image (xsrc, (cairo_image_surface_t *)src,
-					     0, 0,
+					     draw_x, draw_y,
 					     upload.width, upload.height,
 					     0, 0);
     cairo_surface_destroy (src);
commit ecc8c28b24cb5fcd85aee5d4c82b9ad72c87fa69
Author: Kouhei Sutou <kou at clear-code.com>
Date:   Thu Jan 3 12:37:43 2013 +0000

    gobject: Add the correct macro name for the hint-metrics type
    
    s/CAIRO_GOBJECT_TYPE_HNT_METRICS/CAIRO_GOBJECT_TYPE_HINT_METRICS/
    
    However, as we have already released the broken headers, we need to
    preserve that mistake in case applications are already using. Since it
    is just a #define, there is little associated cost with carrying both
    the incorrect spelling and the corrected define.

diff --git a/util/cairo-gobject/cairo-gobject.h b/util/cairo-gobject/cairo-gobject.h
index f43a6d0..d57a735 100644
--- a/util/cairo-gobject/cairo-gobject.h
+++ b/util/cairo-gobject/cairo-gobject.h
@@ -137,7 +137,9 @@ cairo_gobject_subpixel_order_get_type (void);
 cairo_public GType
 cairo_gobject_hint_style_get_type (void);
 
+/* historical accident */
 #define CAIRO_GOBJECT_TYPE_HNT_METRICS cairo_gobject_hint_metrics_get_type ()
+#define CAIRO_GOBJECT_TYPE_HINT_METRICS cairo_gobject_hint_metrics_get_type ()
 cairo_public GType
 cairo_gobject_hint_metrics_get_type (void);
 
commit 5bc1b1f6aac108d9a3963352ad774bb4fcd69e28
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Fri Aug 24 17:22:34 2012 +0100

    stroke: Make the incremental trapezoid stroker optionally available again
    
    Whilst it cannot handle self-intersecting strokes (which includes the
    antialias region of neighbouring lines and joints), it is about 3x
    faster to use than the more robust algorithm. As some backends delegate
    the rendering, the quality may still be preserved and so they should be
    responsible for choosing the appropriate method for generation of the
    stroke geometry.
    
    Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>

diff --git a/src/Makefile.sources b/src/Makefile.sources
index d6793b0..4abf57d 100644
--- a/src/Makefile.sources
+++ b/src/Makefile.sources
@@ -195,6 +195,7 @@ cairo_sources = \
 	cairo-path-stroke.c \
 	cairo-path-stroke-boxes.c \
 	cairo-path-stroke-polygon.c \
+	cairo-path-stroke-traps.c \
 	cairo-path-stroke-tristrip.c \
 	cairo-pattern.c \
 	cairo-pen.c \
diff --git a/src/cairo-cogl-surface.c b/src/cairo-cogl-surface.c
index d192d75..27c3676 100644
--- a/src/cairo-cogl-surface.c
+++ b/src/cairo-cogl-surface.c
@@ -2037,8 +2037,10 @@ _cairo_cogl_stroke_to_primitive (cairo_cogl_surface_t	    *surface,
 
     _cairo_traps_init (&traps);
 
-    status = _cairo_path_fixed_stroke_to_traps (path, style, ctm, ctm_inverse, tolerance,
-						&traps);
+    status = _cairo_path_fixed_stroke_polygon_to_traps (path, style,
+							ctm, ctm_inverse,
+							tolerance,
+							&traps);
     if (unlikely (status))
 	goto BAIL;
 
diff --git a/src/cairo-path-stroke-traps.c b/src/cairo-path-stroke-traps.c
new file mode 100644
index 0000000..c9f7371
--- /dev/null
+++ b/src/cairo-path-stroke-traps.c
@@ -0,0 +1,1120 @@
+/* -*- 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 © 2013 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-box-inline.h"
+#include "cairo-path-fixed-private.h"
+#include "cairo-slope-private.h"
+#include "cairo-stroke-dash-private.h"
+#include "cairo-traps-private.h"
+
+#include <float.h>
+
+struct stroker {
+    const cairo_stroke_style_t	*style;
+
+    const cairo_matrix_t *ctm;
+    const cairo_matrix_t *ctm_inverse;
+    double spline_cusp_tolerance;
+    double half_line_width;
+    double tolerance;
+    double ctm_determinant;
+    cairo_bool_t ctm_det_positive;
+    cairo_line_join_t line_join;
+
+    cairo_traps_t *traps;
+
+    cairo_pen_t pen;
+
+    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 tight_bounds;
+    cairo_box_t line_bounds;
+    cairo_box_t join_bounds;
+};
+
+static cairo_status_t
+stroker_init (struct stroker		*stroker,
+	      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_traps_t		*traps)
+{
+    cairo_status_t status;
+
+    stroker->style = style;
+    stroker->ctm = ctm;
+    stroker->ctm_inverse = NULL;
+    if (! _cairo_matrix_is_identity (ctm_inverse))
+	stroker->ctm_inverse = ctm_inverse;
+    stroker->line_join = style->line_join;
+    stroker->half_line_width = style->line_width / 2.0;
+    stroker->tolerance = tolerance;
+    stroker->traps = traps;
+
+    /* To test whether we need to join two segments of a spline using
+     * a round-join or a bevel-join, we can inspect the angle between the
+     * two segments. If the difference between the chord distance
+     * (half-line-width times the cosine of the bisection angle) and the
+     * half-line-width itself is greater than tolerance then we need to
+     * inject a point.
+     */
+    stroker->spline_cusp_tolerance = 1 - tolerance / stroker->half_line_width;
+    stroker->spline_cusp_tolerance *= stroker->spline_cusp_tolerance;
+    stroker->spline_cusp_tolerance *= 2;
+    stroker->spline_cusp_tolerance -= 1;
+
+    stroker->ctm_determinant = _cairo_matrix_compute_determinant (stroker->ctm);
+    stroker->ctm_det_positive = stroker->ctm_determinant >= 0.0;
+
+    status = _cairo_pen_init (&stroker->pen,
+		              stroker->half_line_width,
+			      tolerance, ctm);
+    if (unlikely (status))
+	return status;
+
+    stroker->has_current_face = FALSE;
+    stroker->has_first_face = FALSE;
+    stroker->has_initial_sub_path = FALSE;
+
+    _cairo_stroker_dash_init (&stroker->dash, style);
+
+    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;
+
+	stroker->tight_bounds = traps->bounds;
+
+	_cairo_stroke_style_max_distance_from_path (stroker->style, path,
+						    stroker->ctm, &dx, &dy);
+
+	_cairo_stroke_style_max_line_distance_from_path (stroker->style, path,
+							 stroker->ctm, &dx, &dy);
+
+	fdx = _cairo_fixed_from_double (dx);
+	fdy = _cairo_fixed_from_double (dy);
+
+	stroker->line_bounds = stroker->tight_bounds;
+	stroker->line_bounds.p1.x -= fdx;
+	stroker->line_bounds.p2.x += fdx;
+	stroker->line_bounds.p1.y -= fdy;
+	stroker->line_bounds.p2.y += fdy;
+
+	_cairo_stroke_style_max_join_distance_from_path (stroker->style, path,
+							 stroker->ctm, &dx, &dy);
+
+	fdx = _cairo_fixed_from_double (dx);
+	fdy = _cairo_fixed_from_double (dy);
+
+	stroker->join_bounds = stroker->tight_bounds;
+	stroker->join_bounds.p1.x -= fdx;
+	stroker->join_bounds.p2.x += fdx;
+	stroker->join_bounds.p1.y -= fdy;
+	stroker->join_bounds.p2.y += fdy;
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+stroker_fini (struct stroker *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
+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 int
+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_bool_t
+stroker_intersects_join (const struct stroker *stroker,
+			 const cairo_point_t *in,
+			 const cairo_point_t *out)
+{
+    cairo_line_t segment;
+
+    if (! stroker->has_bounds)
+	return TRUE;
+
+    segment.p1 = *in;
+    segment.p2 = *out;
+    return _cairo_box_intersects_line_segment (&stroker->join_bounds, &segment);
+}
+
+static void
+join (struct stroker *stroker,
+      cairo_stroke_face_t *in,
+      cairo_stroke_face_t *out)
+{
+    int clockwise = join_is_clockwise (out, in);
+    cairo_point_t *inpt, *outpt;
+
+    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;
+    }
+
+    if (clockwise) {
+	inpt = &in->ccw;
+	outpt = &out->ccw;
+    } else {
+	inpt = &in->cw;
+	outpt = &out->cw;
+    }
+
+    if (! stroker_intersects_join (stroker, inpt, outpt))
+	    return;
+
+    switch (stroker->line_join) {
+    case CAIRO_LINE_JOIN_ROUND:
+	/* construct a fan around the common midpoint */
+	if ((in->dev_slope.x * out->dev_slope.x +
+	     in->dev_slope.y * out->dev_slope.y) < stroker->spline_cusp_tolerance)
+	{
+	    int start, stop;
+	    cairo_point_t tri[3];
+	    cairo_pen_t *pen = &stroker->pen;
+
+	    tri[0] = in->point;
+	    tri[1] = *inpt;
+	    if (clockwise) {
+		_cairo_pen_find_active_ccw_vertices (pen,
+						     &in->dev_vector, &out->dev_vector,
+						     &start, &stop);
+		while (start != stop) {
+		    tri[2] = in->point;
+		    translate_point (&tri[2], &pen->vertices[start].point);
+		    _cairo_traps_tessellate_triangle (stroker->traps, tri);
+		    tri[1] = tri[2];
+
+		    if (start-- == 0)
+			start += pen->num_vertices;
+		}
+	    } else {
+		_cairo_pen_find_active_cw_vertices (pen,
+						    &in->dev_vector, &out->dev_vector,
+						    &start, &stop);
+		while (start != stop) {
+		    tri[2] = in->point;
+		    translate_point (&tri[2], &pen->vertices[start].point);
+		    _cairo_traps_tessellate_triangle (stroker->traps, tri);
+		    tri[1] = tri[2];
+
+		    if (++start == pen->num_vertices)
+			start = 0;
+		}
+	    }
+	    tri[2] = *outpt;
+	    _cairo_traps_tessellate_triangle (stroker->traps, tri);
+	    break;
+	}
+
+    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 (slope_compare_sgn (fdx1, fdy1, mdx, mdy) !=
+		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;
+
+		_cairo_traps_tessellate_convex_quad (stroker->traps, quad);
+		break;
+	    }
+	}
+	/* fall through ... */
+    }
+
+    case CAIRO_LINE_JOIN_BEVEL: {
+	cairo_point_t tri[3];
+	tri[0] = in->point;
+	tri[1] = *inpt;
+	tri[2] = *outpt;
+
+	_cairo_traps_tessellate_triangle (stroker->traps, tri);
+	break;
+    }
+    }
+}
+
+static void
+add_cap (struct stroker *stroker, cairo_stroke_face_t *f)
+{
+    switch (stroker->style->line_cap) {
+    case CAIRO_LINE_CAP_ROUND: {
+	int start, stop;
+	cairo_slope_t in_slope, out_slope;
+	cairo_point_t tri[3];
+	cairo_pen_t *pen = &stroker->pen;
+
+	in_slope = f->dev_vector;
+	out_slope.dx = -in_slope.dx;
+	out_slope.dy = -in_slope.dy;
+	_cairo_pen_find_active_cw_vertices (pen, &in_slope, &out_slope,
+					    &start, &stop);
+	tri[0] = f->point;
+	tri[1] = f->cw;
+	while (start != stop) {
+	    tri[2] = f->point;
+	    translate_point (&tri[2], &pen->vertices[start].point);
+	    _cairo_traps_tessellate_triangle (stroker->traps, tri);
+
+	    tri[1] = tri[2];
+	    if (++start == pen->num_vertices)
+		start = 0;
+	}
+	tri[2] = f->ccw;
+	_cairo_traps_tessellate_triangle (stroker->traps, tri);
+	break;
+    }
+
+    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->half_line_width;
+	dy *= stroker->half_line_width;
+	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->cw;
+	quad[1].x = f->cw.x + fvector.dx;
+	quad[1].y = f->cw.y + fvector.dy;
+	quad[2].x = f->ccw.x + fvector.dx;
+	quad[2].y = f->ccw.y + fvector.dy;
+	quad[3] = f->ccw;
+
+	_cairo_traps_tessellate_convex_quad (stroker->traps, quad);
+	break;
+    }
+
+    case CAIRO_LINE_CAP_BUTT:
+    default:
+	break;
+    }
+}
+
+static void
+add_leading_cap (struct stroker     *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;
+
+    add_cap (stroker, &reversed);
+}
+
+static void
+add_trailing_cap (struct stroker *stroker, cairo_stroke_face_t *face)
+{
+    add_cap (stroker, face);
+}
+
+static inline double
+normalize_slope (double *dx, double *dy)
+{
+    double dx0 = *dx, dy0 = *dy;
+
+    if (dx0 == 0.0 && dy0 == 0.0)
+	return 0;
+
+    if (dx0 == 0.0) {
+	*dx = 0.0;
+	if (dy0 > 0.0) {
+	    *dy = 1.0;
+	    return dy0;
+	} else {
+	    *dy = -1.0;
+	    return -dy0;
+	}
+    } else if (dy0 == 0.0) {
+	*dy = 0.0;
+	if (dx0 > 0.0) {
+	    *dx = 1.0;
+	    return dx0;
+	} else {
+	    *dx = -1.0;
+	    return -dx0;
+	}
+    } else {
+	double mag = hypot (dx0, dy0);
+	*dx = dx0 / mag;
+	*dy = dy0 / mag;
+	return mag;
+    }
+}
+
+static void
+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;
+    double slope_dx, slope_dy;
+
+    slope_dx = _cairo_fixed_to_double (dev_slope->dx);
+    slope_dy = _cairo_fixed_to_double (dev_slope->dy);
+    face->length = 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
+     * 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_inverse) {
+	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->half_line_width;
+	    face_dy = slope_dx * stroker->half_line_width;
+	} else {
+	    face_dx = slope_dy * stroker->half_line_width;
+	    face_dy = - slope_dx * stroker->half_line_width;
+	}
+
+	/* back to device space */
+	cairo_matrix_transform_distance (stroker->ctm, &face_dx, &face_dy);
+    } else {
+	face_dx = - slope_dy * stroker->half_line_width;
+	face_dy = slope_dx * stroker->half_line_width;
+    }
+
+    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 void
+add_caps (struct stroker *stroker)
+{
+    /* 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 */
+	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 */
+	compute_face (&stroker->first_point, &slope, stroker, &face);
+
+	add_leading_cap (stroker, &face);
+	add_trailing_cap (stroker, &face);
+    }
+
+    if (stroker->has_first_face)
+	add_leading_cap (stroker, &stroker->first_face);
+
+    if (stroker->has_current_face)
+	add_trailing_cap (stroker, &stroker->current_face);
+}
+
+static cairo_bool_t
+stroker_intersects_edge (const struct stroker *stroker,
+			 const cairo_stroke_face_t *start,
+			 const cairo_stroke_face_t *end)
+{
+    cairo_box_t box;
+
+    if (! stroker->has_bounds)
+	return TRUE;
+
+    if (_cairo_box_contains_point (&stroker->tight_bounds, &start->cw))
+	return TRUE;
+    box.p2 = box.p1 = start->cw;
+
+    if (_cairo_box_contains_point (&stroker->tight_bounds, &start->ccw))
+	return TRUE;
+    _cairo_box_add_point (&box, &start->ccw);
+
+    if (_cairo_box_contains_point (&stroker->tight_bounds, &end->cw))
+	return TRUE;
+    _cairo_box_add_point (&box, &end->cw);
+
+    if (_cairo_box_contains_point (&stroker->tight_bounds, &end->ccw))
+	return TRUE;
+    _cairo_box_add_point (&box, &end->ccw);
+
+    return (box.p2.x > stroker->tight_bounds.p1.x &&
+	    box.p1.x < stroker->tight_bounds.p2.x &&
+	    box.p2.y > stroker->tight_bounds.p1.y &&
+	    box.p1.y < stroker->tight_bounds.p2.y);
+}
+
+static void
+add_sub_edge (struct stroker *stroker,
+	      const cairo_point_t *p1, const cairo_point_t *p2,
+	      const cairo_slope_t *dev_slope,
+	      cairo_stroke_face_t *start, cairo_stroke_face_t *end)
+{
+    cairo_point_t rectangle[4];
+
+    compute_face (p1, dev_slope, stroker, start);
+
+    *end = *start;
+    end->point = *p2;
+    rectangle[0].x = p2->x - p1->x;
+    rectangle[0].y = p2->y - p1->y;
+    translate_point (&end->ccw, &rectangle[0]);
+    translate_point (&end->cw, &rectangle[0]);
+
+    if (p1->x == p2->x && p1->y == p2->y)
+	return;
+
+    if (! stroker_intersects_edge (stroker, start, end))
+	return;
+
+    rectangle[0] = start->cw;
+    rectangle[1] = start->ccw;
+    rectangle[2] = end->ccw;
+    rectangle[3] = end->cw;
+
+    _cairo_traps_tessellate_convex_quad (stroker->traps, rectangle);
+}
+
+static cairo_status_t
+move_to (void *closure, const cairo_point_t *point)
+{
+    struct stroker *stroker = closure;
+
+    /* Cap the start and end of the previous sub path as needed */
+    add_caps (stroker);
+
+    stroker->first_point = *point;
+    stroker->current_face.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
+move_to_dashed (void *closure, const cairo_point_t *point)
+{
+    /* reset the dash pattern for new sub paths */
+    struct stroker *stroker = closure;
+
+    _cairo_stroker_dash_start (&stroker->dash);
+    return move_to (closure, point);
+}
+
+static cairo_status_t
+line_to (void *closure, const cairo_point_t *point)
+{
+    struct stroker *stroker = closure;
+    cairo_stroke_face_t start, end;
+    const cairo_point_t *p1 = &stroker->current_face.point;
+    const cairo_point_t *p2 = point;
+    cairo_slope_t dev_slope;
+
+    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);
+    add_sub_edge (stroker, p1, p2, &dev_slope, &start, &end);
+
+    if (stroker->has_current_face) {
+	/* Join with final face from previous segment */
+	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->current_face = end;
+    stroker->has_current_face = TRUE;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+/*
+ * Dashed lines.  Cap each dash end, join around turns when on
+ */
+static cairo_status_t
+line_to_dashed (void *closure, const cairo_point_t *point)
+{
+    struct stroker *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_face.point;
+    const cairo_point_t *p2 = point;
+    cairo_slope_t dev_slope;
+    cairo_line_t segment;
+    cairo_bool_t fully_in_bounds;
+
+    stroker->has_initial_sub_path = stroker->dash.dash_starts_on;
+
+    if (p1->x == p2->x && p1->y == p2->y)
+	return CAIRO_STATUS_SUCCESS;
+
+    fully_in_bounds = TRUE;
+    if (stroker->has_bounds &&
+	(! _cairo_box_contains_point (&stroker->join_bounds, p1) ||
+	 ! _cairo_box_contains_point (&stroker->join_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 (stroker->ctm_inverse)
+	cairo_matrix_transform_distance (stroker->ctm_inverse, &slope_dx, &slope_dy);
+    mag = normalize_slope (&slope_dx, &slope_dy);
+    if (mag <= DBL_EPSILON)
+	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 (stroker->dash.dash_on &&
+	    (fully_in_bounds ||
+	     (! stroker->has_first_face && stroker->dash.dash_starts_on) ||
+	     _cairo_box_intersects_line_segment (&stroker->join_bounds, &segment)))
+	{
+	    add_sub_edge (stroker,
+			  &segment.p1, &segment.p2,
+			  &dev_slope,
+			  &sub_start, &sub_end);
+
+	    if (stroker->has_current_face) {
+		/* Join with final face from previous segment */
+		join (stroker, &stroker->current_face, &sub_start);
+
+		stroker->has_current_face = FALSE;
+	    } 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 */
+		add_leading_cap (stroker, &sub_start);
+	    }
+
+	    if (remain) {
+		/* Cap dash end if not at end of segment */
+		add_trailing_cap (stroker, &sub_end);
+	    } else {
+		stroker->current_face = sub_end;
+		stroker->has_current_face = TRUE;
+	    }
+	} else {
+	    if (stroker->has_current_face) {
+		/* Cap final face from previous segment */
+		add_trailing_cap (stroker, &stroker->current_face);
+
+		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 beginning 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 degenerate caps can not be reproduced with regular
+	 * path stroking.
+	 * On the other hand, Acroread 7 also produces the degenerate caps.
+	 */
+	compute_face (point, &dev_slope, stroker, &stroker->current_face);
+
+	add_leading_cap (stroker, &stroker->current_face);
+
+	stroker->has_current_face = TRUE;
+    } else
+	stroker->current_face.point = *point;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+spline_to (void *closure,
+	   const cairo_point_t *point,
+	   const cairo_slope_t *tangent)
+{
+    struct stroker *stroker = closure;
+    cairo_stroke_face_t face;
+
+    if ((tangent->dx | tangent->dy) == 0) {
+	cairo_point_t t;
+
+	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;
+
+	join (stroker, &stroker->current_face, &face);
+    } else {
+	cairo_point_t rectangle[4];
+
+	compute_face (&stroker->current_face.point, tangent, stroker, &face);
+
+	join (stroker, &stroker->current_face, &face);
+
+	rectangle[0] = face.cw;
+	rectangle[1] = face.ccw;
+
+	rectangle[2].x = point->x - face.point.x;
+	rectangle[2].y = point->y - face.point.y;
+	face.point = *point;
+	translate_point (&face.ccw, &rectangle[2]);
+	translate_point (&face.cw, &rectangle[2]);
+
+	rectangle[2] = face.ccw;
+	rectangle[3] = face.cw;
+
+	_cairo_traps_tessellate_convex_quad (stroker->traps, rectangle);
+    }
+
+    stroker->current_face = face;
+
+    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_line_join_t line_join_save;
+    cairo_spline_t spline;
+    cairo_stroke_face_t face;
+    cairo_status_t status;
+
+    if (stroker->has_bounds &&
+	! _cairo_spline_intersects (&stroker->current_face.point, b, c, d,
+				    &stroker->line_bounds))
+	return line_to (closure, d);
+
+    if (! _cairo_spline_init (&spline, spline_to, stroker,
+			      &stroker->current_face.point, b, c, d))
+	return line_to (closure, d);
+
+    compute_face (&stroker->current_face.point, &spline.initial_slope,
+		  stroker, &face);
+
+    if (stroker->has_current_face) {
+	/* Join with final face from previous segment */
+	join (stroker, &stroker->current_face, &face);
+    } else {
+	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. */
+    line_join_save = stroker->line_join;
+    stroker->line_join = CAIRO_LINE_JOIN_ROUND;
+
+    status = _cairo_spline_decompose (&spline, stroker->tolerance);
+
+    stroker->line_join = line_join_save;
+
+    return status;
+}
+
+static cairo_status_t
+curve_to_dashed (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_spline_add_point_func_t func;
+    cairo_status_t status;
+
+    func = (cairo_spline_add_point_func_t)line_to_dashed;
+
+    if (stroker->has_bounds &&
+	! _cairo_spline_intersects (&stroker->current_face.point, b, c, b,
+				    &stroker->line_bounds))
+	return func (closure, d, NULL);
+
+    if (! _cairo_spline_init (&spline, func, stroker,
+			      &stroker->current_face.point, b, c, d))
+	return func (closure, d, NULL);
+
+    /* Temporarily modify the stroker to use round joins to guarantee
+     * smooth stroked curves. */
+    line_join_save = stroker->line_join;
+    stroker->line_join = CAIRO_LINE_JOIN_ROUND;
+
+    status = _cairo_spline_decompose (&spline, stroker->tolerance);
+
+    stroker->line_join = line_join_save;
+
+    return status;
+}
+
+static cairo_status_t
+_close_path (struct stroker *stroker)
+{
+    if (stroker->has_first_face && stroker->has_current_face) {
+	/* Join first and final faces of sub path */
+	join (stroker, &stroker->current_face, &stroker->first_face);
+    } else {
+	/* Cap the start and end of the sub path as needed */
+	add_caps (stroker);
+    }
+
+    stroker->has_initial_sub_path = FALSE;
+    stroker->has_first_face = FALSE;
+    stroker->has_current_face = FALSE;
+    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;
+
+    return _close_path (stroker);
+}
+
+static cairo_status_t
+close_path_dashed (void *closure)
+{
+    struct stroker *stroker = closure;
+    cairo_status_t status;
+
+    status = line_to_dashed (stroker, &stroker->first_point);
+    if (unlikely (status))
+	return status;
+
+    return _close_path (stroker);
+}
+
+cairo_int_status_t
+_cairo_path_fixed_stroke_to_traps (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_traps_t		*traps)
+{
+    struct stroker stroker;
+    cairo_status_t status;
+
+    status = stroker_init (&stroker, path, style,
+			   ctm, ctm_inverse, tolerance,
+			   traps);
+    if (unlikely (status))
+	return status;
+
+    if (stroker.dash.dashed)
+	status = _cairo_path_fixed_interpret (path,
+					      move_to_dashed,
+					      line_to_dashed,
+					      curve_to_dashed,
+					      close_path_dashed,
+					      &stroker);
+    else
+	status = _cairo_path_fixed_interpret (path,
+					      move_to,
+					      line_to,
+					      curve_to,
+					      close_path,
+					      &stroker);
+    assert(status == CAIRO_STATUS_SUCCESS);
+    add_caps (&stroker);
+
+    stroker_fini (&stroker);
+
+    return traps->status;
+}
diff --git a/src/cairo-path-stroke.c b/src/cairo-path-stroke.c
index a81e3bc..cd6b3a2 100644
--- a/src/cairo-path-stroke.c
+++ b/src/cairo-path-stroke.c
@@ -1341,12 +1341,12 @@ BAIL:
 }
 
 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_path_fixed_stroke_polygon_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;
diff --git a/src/cairo-recording-surface.c b/src/cairo-recording-surface.c
index 3bb5996..edbeed7 100644
--- a/src/cairo-recording-surface.c
+++ b/src/cairo-recording-surface.c
@@ -1539,12 +1539,12 @@ _cairo_recording_surface_get_path (cairo_surface_t    *abstract_surface,
 	    _cairo_traps_init (&traps);
 
 	    /* XXX call cairo_stroke_to_path() when that is implemented */
-	    status = _cairo_path_fixed_stroke_to_traps (&command->stroke.path,
-							&command->stroke.style,
-							&command->stroke.ctm,
-							&command->stroke.ctm_inverse,
-							command->stroke.tolerance,
-							&traps);
+	    status = _cairo_path_fixed_stroke_polygon_to_traps (&command->stroke.path,
+								&command->stroke.style,
+								&command->stroke.ctm,
+								&command->stroke.ctm_inverse,
+								command->stroke.tolerance,
+								&traps);
 
 	    if (status == CAIRO_INT_STATUS_SUCCESS)
 		status = _cairo_traps_path (&traps, path);
diff --git a/src/cairo-rectangle.c b/src/cairo-rectangle.c
index 47f2837..2d51d7b 100644
--- a/src/cairo-rectangle.c
+++ b/src/cairo-rectangle.c
@@ -189,7 +189,7 @@ _cairo_rectangle_union (cairo_rectangle_int_t *dst,
  */
 
 cairo_bool_t
-_cairo_box_intersects_line_segment (cairo_box_t *box, cairo_line_t *line)
+_cairo_box_intersects_line_segment (const cairo_box_t *box, cairo_line_t *line)
 {
     cairo_fixed_t t1=0, t2=0, t3=0, t4=0;
     cairo_int64_t t1y, t2y, t3x, t4x;
diff --git a/src/cairo-stroke-style.c b/src/cairo-stroke-style.c
index 3ebaf01..51c9414 100644
--- a/src/cairo-stroke-style.c
+++ b/src/cairo-stroke-style.c
@@ -127,6 +127,45 @@ _cairo_stroke_style_max_distance_from_path (const cairo_stroke_style_t *style,
     }
 }
 
+void
+_cairo_stroke_style_max_line_distance_from_path (const cairo_stroke_style_t *style,
+						 const cairo_path_fixed_t *path,
+						 const cairo_matrix_t *ctm,
+						 double *dx, double *dy)
+{
+    double style_expansion = 0.5 * style->line_width;
+    if (_cairo_matrix_has_unity_scale (ctm)) {
+	*dx = *dy = style_expansion;
+    } else {
+	*dx = style_expansion * hypot (ctm->xx, ctm->xy);
+	*dy = style_expansion * hypot (ctm->yy, ctm->yx);
+    }
+}
+
+void
+_cairo_stroke_style_max_join_distance_from_path (const cairo_stroke_style_t *style,
+						 const cairo_path_fixed_t *path,
+						 const cairo_matrix_t *ctm,
+						 double *dx, double *dy)
+{
+    double style_expansion = 0.5;
+
+    if (style->line_join == CAIRO_LINE_JOIN_MITER &&
+	! path->stroke_is_rectilinear &&
+	style_expansion < M_SQRT2 * style->miter_limit)
+    {
+	style_expansion = M_SQRT2 * style->miter_limit;
+    }
+
+    style_expansion *= style->line_width;
+
+    if (_cairo_matrix_has_unity_scale (ctm)) {
+	*dx = *dy = style_expansion;
+    } else {
+	*dx = style_expansion * hypot (ctm->xx, ctm->xy);
+	*dy = style_expansion * hypot (ctm->yy, ctm->yx);
+    }
+}
 /*
  * Computes the period of a dashed stroke style.
  * Returns 0 for non-dashed styles.
diff --git a/src/cairo-traps-compositor.c b/src/cairo-traps-compositor.c
index eeee20c..02fbe75 100644
--- a/src/cairo-traps-compositor.c
+++ b/src/cairo-traps-compositor.c
@@ -1570,7 +1570,7 @@ clip_and_composite_polygon (const cairo_traps_compositor_t *compositor,
 	 * The clip will trim that overestimate to our expectations.
 	 */
 	if (! extents->is_bounded)
-		flags |= FORCE_CLIP_REGION;
+	    flags |= FORCE_CLIP_REGION;
 
 	traps.antialias = antialias;
 	status = clip_and_composite (compositor, extents,
@@ -1791,7 +1791,8 @@ composite_traps_as_boxes (const cairo_traps_compositor_t *compositor,
 static cairo_int_status_t
 clip_and_composite_traps (const cairo_traps_compositor_t *compositor,
 			  cairo_composite_rectangles_t *extents,
-			  composite_traps_info_t *info)
+			  composite_traps_info_t *info,
+			  unsigned flags)
 {
     cairo_int_status_t status;
 
@@ -1801,10 +1802,10 @@ clip_and_composite_traps (const cairo_traps_compositor_t *compositor,
     if (unlikely (status != CAIRO_INT_STATUS_SUCCESS))
 	return status;
 
-    status = composite_traps_as_boxes (compositor, extents, info);
+    status = CAIRO_INT_STATUS_UNSUPPORTED;
+    if ((flags & FORCE_CLIP_REGION) == 0)
+	status = composite_traps_as_boxes (compositor, extents, info);
     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
@@ -2152,16 +2153,32 @@ _cairo_traps_compositor_stroke (const cairo_compositor_t *_compositor,
     }
 
     if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
+	cairo_int_status_t (*func) (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);
 	composite_traps_info_t info;
+	unsigned flags = 0;
+
+	if (antialias == CAIRO_ANTIALIAS_BEST || antialias == CAIRO_ANTIALIAS_GOOD) {
+	    func = _cairo_path_fixed_stroke_polygon_to_traps;
+	} else {
+	    func = _cairo_path_fixed_stroke_to_traps;
+	    if (extents->clip->num_boxes > 1 ||
+		extents->mask.width  > extents->unbounded.width ||
+		extents->mask.height > extents->unbounded.height)
+	    {
+		flags = NEED_CLIP_REGION | FORCE_CLIP_REGION;
+	    }
+	}
 
 	info.antialias = antialias;
 	_cairo_traps_init_with_clip (&info.traps, extents->clip);
-	status = _cairo_path_fixed_stroke_to_traps (path, style,
-						    ctm, ctm_inverse,
-						    tolerance,
-						    &info.traps);
+	status = func (path, style, ctm, ctm_inverse, tolerance, &info.traps);
 	if (likely (status == CAIRO_INT_STATUS_SUCCESS))
-	    status = clip_and_composite_traps (compositor, extents, &info);
+	    status = clip_and_composite_traps (compositor, extents, &info, flags);
 	_cairo_traps_fini (&info.traps);
     }
 
diff --git a/src/cairo-traps-private.h b/src/cairo-traps-private.h
index 62c0fe7..7fef062 100644
--- a/src/cairo-traps-private.h
+++ b/src/cairo-traps-private.h
@@ -47,6 +47,7 @@ CAIRO_BEGIN_DECLS
 struct _cairo_traps {
     cairo_status_t status;
 
+    cairo_box_t bounds;
     const cairo_box_t *limits;
     int num_limits;
 
@@ -89,6 +90,14 @@ _cairo_traps_fini (cairo_traps_t *traps);
 cairo_private void
 _cairo_traps_translate (cairo_traps_t *traps, int x, int y);
 
+cairo_private void
+_cairo_traps_tessellate_triangle (cairo_traps_t *traps,
+				  const cairo_point_t t[3]);
+
+cairo_private void
+_cairo_traps_tessellate_convex_quad (cairo_traps_t *traps,
+				     const cairo_point_t q[4]);
+
 cairo_private cairo_status_t
 _cairo_traps_tessellate_rectangle (cairo_traps_t *traps,
 				   const cairo_point_t *top_left,
diff --git a/src/cairo-traps.c b/src/cairo-traps.c
index 48eaf98..9f1d4a7 100644
--- a/src/cairo-traps.c
+++ b/src/cairo-traps.c
@@ -39,6 +39,7 @@
 
 #include "cairoint.h"
 
+#include "cairo-box-inline.h"
 #include "cairo-boxes-private.h"
 #include "cairo-error-private.h"
 #include "cairo-region-private.h"
@@ -73,8 +74,14 @@ _cairo_traps_limit (cairo_traps_t	*traps,
 		    const cairo_box_t	*limits,
 		    int			 num_limits)
 {
+    int i;
+
     traps->limits = limits;
     traps->num_limits = num_limits;
+
+    traps->bounds = limits[0];
+    for (i = 1; i < num_limits; i++)
+	_cairo_box_add_box (&traps->bounds, &limits[i]);
 }
 
 void
@@ -158,6 +165,245 @@ _cairo_traps_add_trap (cairo_traps_t *traps,
     trap->right = *right;
 }
 
+static void
+_cairo_traps_add_clipped_trap (cairo_traps_t *traps,
+			       cairo_fixed_t _top, cairo_fixed_t _bottom,
+			       cairo_line_t *_left, cairo_line_t *_right)
+{
+    /* Note: With the goofy trapezoid specification, (where an
+     * arbitrary two points on the lines can specified for the left
+     * and right edges), these limit checks would not work in
+     * general. For example, one can imagine a trapezoid entirely
+     * within the limits, but with two points used to specify the left
+     * edge entirely to the right of the limits.  Fortunately, for our
+     * purposes, cairo will never generate such a crazy
+     * trapezoid. Instead, cairo always uses for its points the
+     * extreme positions of the edge that are visible on at least some
+     * trapezoid. With this constraint, it's impossible for both
+     * points to be outside the limits while the relevant edge is
+     * entirely inside the limits.
+     */
+    if (traps->num_limits) {
+	const cairo_box_t *b = &traps->bounds;
+	cairo_fixed_t top = _top, bottom = _bottom;
+	cairo_line_t left = *_left, right = *_right;
+
+	/* Trivially reject if trapezoid is entirely to the right or
+	 * to the left of the limits. */
+	if (left.p1.x >= b->p2.x && left.p2.x >= b->p2.x)
+	    return;
+
+	if (right.p1.x <= b->p1.x && right.p2.x <= b->p1.x)
+	    return;
+
+	/* And reject if the trapezoid is entirely above or below */
+	if (top >= b->p2.y || bottom <= b->p1.y)
+	    return;
+
+	/* Otherwise, clip the trapezoid to the limits. We only clip
+	 * where an edge is entirely outside the limits. If we wanted
+	 * to be more clever, we could handle cases where a trapezoid
+	 * edge intersects the edge of the limits, but that would
+	 * require slicing this trapezoid into multiple trapezoids,
+	 * and I'm not sure the effort would be worth it. */
+	if (top < b->p1.y)
+	    top = b->p1.y;
+
+	if (bottom > b->p2.y)
+	    bottom = b->p2.y;
+
+	if (left.p1.x <= b->p1.x && left.p2.x <= b->p1.x)
+	    left.p1.x = left.p2.x = b->p1.x;
+
+	if (right.p1.x >= b->p2.x && right.p2.x >= b->p2.x)
+	    right.p1.x = right.p2.x = b->p2.x;
+
+	/* Trivial discards for empty trapezoids that are likely to
+	 * be produced by our tessellators (most notably convex_quad
+	 * when given a simple rectangle).
+	 */
+	if (top >= bottom)
+	    return;
+
+	/* cheap colinearity check */
+	if (right.p1.x <= left.p1.x && right.p1.y == left.p1.y &&
+	    right.p2.x <= left.p2.x && right.p2.y == left.p2.y)
+	    return;
+
+	_cairo_traps_add_trap (traps, top, bottom, &left, &right);
+    } else
+	_cairo_traps_add_trap (traps, _top, _bottom, _left, _right);
+}
+
+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;
+}
+
+void
+_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_clipped_trap (traps, q[a].y, q[b].y, &left, &right);
+	    left.p1  = q[b]; left.p2  = q[c];
+	    _cairo_traps_add_clipped_trap (traps, q[b].y, q[c].y, &left, &right);
+	    left.p1  = q[c]; left.p2  = q[d];
+	    _cairo_traps_add_clipped_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_clipped_trap (traps, q[a].y, q[b].y, &left, &right);
+	    right.p1 = q[b]; right.p2 = q[c];
+	    _cairo_traps_add_clipped_trap (traps, q[b].y, q[c].y, &left, &right);
+	    right.p1 = q[c]; right.p2 = q[d];
+	    _cairo_traps_add_clipped_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_clipped_trap (traps, q[a].y, q[b].y, &left, &right);
+	    left.p1  = q[b]; left.p2  = q[c];
+	    _cairo_traps_add_clipped_trap (traps, q[b].y, q[d].y, &left, &right);
+	    right.p1 = q[d]; right.p2 = q[c];
+	    _cairo_traps_add_clipped_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_clipped_trap (traps, q[a].y, q[b].y, &left, &right);
+	    right.p1 = q[b]; right.p2 = q[c];
+	    _cairo_traps_add_clipped_trap (traps, q[b].y, q[d].y, &left, &right);
+	    left.p1  = q[d]; left.p2  = q[c];
+	    _cairo_traps_add_clipped_trap (traps, q[d].y, q[c].y, &left, &right);
+	}
+    }
+}
+
+/* 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. */
+void
+_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];
+
+    _cairo_traps_tessellate_convex_quad (traps, quad);
+}
+
+
 /**
  * _cairo_traps_init_boxes:
  * @traps: a #cairo_traps_t
@@ -240,6 +486,9 @@ _cairo_traps_tessellate_rectangle (cairo_traps_t *traps,
 	cairo_bool_t reversed;
 	int n;
 
+	if (top >= traps->bounds.p2.y || bottom <= traps->bounds.p1.y)
+	    return CAIRO_STATUS_SUCCESS;
+
 	/* support counter-clockwise winding for rectangular tessellation */
 	reversed = top_left->x > bottom_right->x;
 	if (reversed) {
@@ -247,6 +496,9 @@ _cairo_traps_tessellate_rectangle (cairo_traps_t *traps,
 	    left.p1.x = left.p2.x = bottom_right->x;
 	}
 
+	if (left.p1.x >= traps->bounds.p2.x || right.p1.x <= traps->bounds.p1.x)
+	    return CAIRO_STATUS_SUCCESS;
+
 	for (n = 0; n < traps->num_limits; n++) {
 	    const cairo_box_t *limits = &traps->limits[n];
 	    cairo_line_t _left, _right;
diff --git a/src/cairoint.h b/src/cairoint.h
index 0a925d1..309987c 100644
--- a/src/cairoint.h
+++ b/src/cairoint.h
@@ -337,7 +337,7 @@ _cairo_rectangle_union (cairo_rectangle_int_t *dst,
 			const cairo_rectangle_int_t *src);
 
 cairo_private cairo_bool_t
-_cairo_box_intersects_line_segment (cairo_box_t *box,
+_cairo_box_intersects_line_segment (const cairo_box_t *box,
 	                            cairo_line_t *line) cairo_pure;
 
 cairo_private cairo_bool_t
@@ -1086,6 +1086,14 @@ _cairo_path_fixed_stroke_to_traps (const cairo_path_fixed_t	*path,
 				   double		 tolerance,
 				   cairo_traps_t	*traps);
 
+cairo_private cairo_int_status_t
+_cairo_path_fixed_stroke_polygon_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_status_t
 _cairo_path_fixed_stroke_to_shaper (cairo_path_fixed_t	*path,
 				   const cairo_stroke_style_t	*stroke_style,
@@ -1232,6 +1240,17 @@ _cairo_stroke_style_max_distance_from_path (const cairo_stroke_style_t *style,
 					    const cairo_path_fixed_t *path,
                                             const cairo_matrix_t *ctm,
                                             double *dx, double *dy);
+cairo_private void
+_cairo_stroke_style_max_line_distance_from_path (const cairo_stroke_style_t *style,
+						 const cairo_path_fixed_t *path,
+						 const cairo_matrix_t *ctm,
+						 double *dx, double *dy);
+
+cairo_private void
+_cairo_stroke_style_max_join_distance_from_path (const cairo_stroke_style_t *style,
+						 const cairo_path_fixed_t *path,
+						 const cairo_matrix_t *ctm,
+						 double *dx, double *dy);
 
 cairo_private double
 _cairo_stroke_style_dash_period (const cairo_stroke_style_t *style);
diff --git a/src/test-base-compositor-surface.c b/src/test-base-compositor-surface.c
index f8fa517..ff84b10 100644
--- a/src/test-base-compositor-surface.c
+++ b/src/test-base-compositor-surface.c
@@ -662,10 +662,10 @@ base_compositor_stroke (const cairo_compositor_t *_compositor,
 
     info.antialias = antialias;
     _cairo_traps_init_with_clip (&info.traps, extents->clip);
-    status = _cairo_path_fixed_stroke_to_traps (path, style,
-						ctm, ctm_inverse,
-						tolerance,
-						&info.traps);
+    status = _cairo_path_fixed_stroke_polygon_to_traps (path, style,
+							ctm, ctm_inverse,
+							tolerance,
+							&info.traps);
     if (likely (status == CAIRO_INT_STATUS_SUCCESS))
 	status = trim_extents_to_traps (extents, &info.traps);
     if (likely (status == CAIRO_INT_STATUS_SUCCESS))
commit 74941f822015cc50cd8477d0cf97f1a70dbff60b
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Wed Jan 2 22:27:55 2013 +0000

    xlib: Use SHM transport for ordinary image uploads
    
    In theory this should just save a single copy, however PutImage will
    break up requests into a series of scanlines requests which is less
    efficient than the single-shot transfer provided by ShmPutImage.
    
    Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>

diff --git a/src/cairo-xlib-surface.c b/src/cairo-xlib-surface.c
index e9e647a..dbc677e 100644
--- a/src/cairo-xlib-surface.c
+++ b/src/cairo-xlib-surface.c
@@ -1090,9 +1090,10 @@ _cairo_xlib_surface_draw_image (cairo_xlib_surface_t   *surface,
     XImage ximage;
     cairo_format_masks_t image_masks;
     int native_byte_order = _cairo_is_little_endian () ? LSBFirst : MSBFirst;
+    cairo_surface_t *shm_image = NULL;
     pixman_image_t *pixman_image = NULL;
     cairo_status_t status;
-    cairo_bool_t own_data;
+    cairo_bool_t own_data = FALSE;
     cairo_bool_t is_rgb_image;
     GC gc;
 
@@ -1127,9 +1128,41 @@ _cairo_xlib_surface_draw_image (cairo_xlib_surface_t   *surface,
 	ximage.bits_per_pixel = image_masks.bpp;
 	ximage.bytes_per_line = image->stride;
 	ximage.data = (char *)image->data;
-	if (image->base.device == surface->base.device)
+	if (image->base.device != surface->base.device) {
+	    /* If PutImage will break the image up into chunks, prefer to
+	     * send it all in one pass with ShmPutImage.  For larger images,
+	     * it is further advantageous to reduce the number of copies,
+	     * albeit at the expense of more SHM bookkeeping.
+	     */
+	    int max_request_size = XExtendedMaxRequestSize (display->display);
+	    if (max_request_size == 0)
+		max_request_size = XMaxRequestSize (display->display);
+	    if (max_request_size > 8192)
+		max_request_size = 8192;
+	    if (image->stride * image->height > max_request_size) {
+		shm_image = _cairo_xlib_surface_create_shm__image (surface,
+								   image->pixman_format,
+								   image->width,
+								   image->height);
+		if (shm_image && shm_image->status == CAIRO_STATUS_SUCCESS) {
+		    cairo_image_surface_t *clone = (cairo_image_surface_t *) shm_image;
+		    if (clone->stride == image->stride) {
+			memcpy (clone->data, image->data, clone->stride * clone->height);
+		    } else {
+			pixman_image_composite32 (PIXMAN_OP_SRC,
+						  image->pixman_image, NULL, clone->pixman_image,
+						  0, 0,
+						  0, 0,
+						  0, 0,
+						  image->width, image->height);
+		    }
+		    ximage.obdata = _cairo_xlib_shm_surface_get_obdata (shm_image);
+		    ximage.data = (char *)clone->data;
+		    ximage.bytes_per_line = clone->stride;
+		}
+	    }
+	} else
 	    ximage.obdata = _cairo_xlib_shm_surface_get_obdata (&image->base);
-	own_data = FALSE;
 
 	ret = XInitImage (&ximage);
 	assert (ret != 0);
@@ -1147,29 +1180,48 @@ _cairo_xlib_surface_draw_image (cairo_xlib_surface_t   *surface,
         ret = _pixman_format_from_masks (&image_masks, &intermediate_format);
         assert (ret);
 
-	own_data = FALSE;
-
-        pixman_image = pixman_image_create_bits (intermediate_format,
-                                                 width, height, NULL, 0);
-        if (pixman_image == NULL) {
-	    status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
-            goto BAIL;
-        }
+	shm_image = _cairo_xlib_surface_create_shm__image (surface,
+							   intermediate_format,
+							   width, height);
+	if (shm_image && shm_image->status == CAIRO_STATUS_SUCCESS) {
+	    cairo_image_surface_t *clone = (cairo_image_surface_t *) shm_image;
+
+	    pixman_image_composite32 (PIXMAN_OP_SRC,
+				      image->pixman_image,
+				      NULL,
+				      clone->pixman_image,
+				      src_x, src_y,
+				      0, 0,
+				      0, 0,
+				      width, height);
+
+	    ximage.data = (char *) clone->data;
+	    ximage.obdata = _cairo_xlib_shm_surface_get_obdata (&clone->base);
+	    ximage.bytes_per_line = clone->stride;
+	} else {
+	    pixman_image = pixman_image_create_bits (intermediate_format,
+						     width, height, NULL, 0);
+	    if (pixman_image == NULL) {
+		status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+		goto BAIL;
+	    }
 
-        pixman_image_composite32 (PIXMAN_OP_SRC,
-                                  image->pixman_image,
-                                  NULL,
-                                  pixman_image,
-                                  src_x, src_y,
-                                  0, 0,
-                                  0, 0,
-                                  width, height);
+	    pixman_image_composite32 (PIXMAN_OP_SRC,
+				      image->pixman_image,
+				      NULL,
+				      pixman_image,
+				      src_x, src_y,
+				      0, 0,
+				      0, 0,
+				      width, height);
+
+	    ximage.data = (char *) pixman_image_get_data (pixman_image);
+	    ximage.bytes_per_line = pixman_image_get_stride (pixman_image);
+	}
 
 	ximage.width = width;
 	ximage.height = height;
 	ximage.bits_per_pixel = image_masks.bpp;
-	ximage.data = (char *) pixman_image_get_data (pixman_image);
-	ximage.bytes_per_line = pixman_image_get_stride (pixman_image);
 
 	ret = XInitImage (&ximage);
 	assert (ret != 0);
@@ -1195,7 +1247,6 @@ _cairo_xlib_surface_draw_image (cairo_xlib_surface_t   *surface,
 	ximage.bytes_per_line = stride;
 	ximage.data = _cairo_malloc_ab (stride, ximage.height);
 	if (unlikely (ximage.data == NULL)) {
-            own_data = FALSE;
 	    status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
             goto BAIL;
         }
@@ -1296,7 +1347,8 @@ _cairo_xlib_surface_draw_image (cairo_xlib_surface_t   *surface,
 
     if (ximage.obdata)
 	XShmPutImage (display->display, surface->drawable, gc, &ximage,
-		      src_x, src_y, dst_x, dst_y, width, height, TRUE);
+		      src_x, src_y, dst_x, dst_y, width, height,
+		      shm_image == NULL);
     else
 	XPutImage (display->display, surface->drawable, gc, &ximage,
 		   src_x, src_y, dst_x, dst_y, width, height);
@@ -1308,6 +1360,8 @@ _cairo_xlib_surface_draw_image (cairo_xlib_surface_t   *surface,
 
     if (own_data)
 	free (ximage.data);
+    if (shm_image)
+	cairo_surface_destroy (shm_image);
     if (pixman_image)
         pixman_image_unref (pixman_image);
 
commit bf2a04c5ab91c93d4d188afd030b3004c67a180f
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Thu Jan 3 12:24:14 2013 +0000

    xlib/shm: Fix typo in creation of a SHM image
    
    Pass along the size the caller requests, not the size of the related
    drawable.
    
    Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>

diff --git a/src/cairo-xlib-surface-shm.c b/src/cairo-xlib-surface-shm.c
index ea047b6..1e2c6e6 100644
--- a/src/cairo-xlib-surface-shm.c
+++ b/src/cairo-xlib-surface-shm.c
@@ -1135,9 +1135,8 @@ _cairo_xlib_surface_create_shm (cairo_xlib_surface_t *other,
 
     surface = NULL;
     if (has_shm (other))
-	surface = &_cairo_xlib_shm_surface_create (other, format,
-						   width, height, FALSE,
-						   has_shm_pixmaps (other))->image.base;
+	surface = &_cairo_xlib_shm_surface_create (other, format, width, height,
+						   FALSE, has_shm_pixmaps (other))->image.base;
 
     return surface;
 }
@@ -1150,8 +1149,7 @@ _cairo_xlib_surface_create_shm__image (cairo_xlib_surface_t *surface,
     if (! has_shm(surface))
 	return NULL;
 
-    return &_cairo_xlib_shm_surface_create (surface, format,
-					    surface->width, surface->height,
+    return &_cairo_xlib_shm_surface_create (surface, format, width, height,
 					    TRUE, 0)->image.base;
 }
 


More information about the cairo-commit mailing list