[cairo-commit] src/cairoint.h src/cairo-matrix.c src/cairo-path-stroke.c src/cairo-pen.c src/cairo-stroke-style.c test/leaky-dashed-rectangle.pdf.ref.png test/leaky-dashed-rectangle.ps2.ref.png test/leaky-dashed-rectangle.ps3.ref.png test/leaky-dashed-rectangle.ps.ref.png test/leaky-dashed-rectangle.ref.png test/Makefile.am

Chris Wilson ickle at kemper.freedesktop.org
Thu Jan 29 07:00:37 PST 2009


 dev/null                                |binary
 src/cairo-matrix.c                      |    3 
 src/cairo-path-stroke.c                 |  492 ++++++++++++++++++++++++++------
 src/cairo-pen.c                         |   11 
 src/cairo-stroke-style.c                |   18 -
 src/cairoint.h                          |   11 
 test/Makefile.am                        |    4 
 test/leaky-dashed-rectangle.pdf.ref.png |binary
 test/leaky-dashed-rectangle.ps.ref.png  |binary
 test/leaky-dashed-rectangle.ref.png     |binary
 10 files changed, 437 insertions(+), 102 deletions(-)

New commits:
commit dd4276c6618aa250637e4499bc7cb0a35b24448c
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Sun Nov 30 13:49:24 2008 +0000

    [stroker] Rectilinear dashing.
    
    Extend the rectilinear stroker to handle dashes, so that for pixel-aligned
    dashed strokes we completely avoid tessellation overhead.

diff --git a/src/cairo-matrix.c b/src/cairo-matrix.c
index b617472..b9e7290 100644
--- a/src/cairo-matrix.c
+++ b/src/cairo-matrix.c
@@ -839,7 +839,8 @@ _cairo_matrix_is_pixel_exact (const cairo_matrix_t *matrix)
 /* determine the length of the major axis of a circle of the given radius
    after applying the transformation matrix. */
 double
-_cairo_matrix_transformed_circle_major_axis (cairo_matrix_t *matrix, double radius)
+_cairo_matrix_transformed_circle_major_axis (const cairo_matrix_t *matrix,
+					     double radius)
 {
     double  a, b, c, d, f, g, h, i, j;
 
diff --git a/src/cairo-path-stroke.c b/src/cairo-path-stroke.c
index 2fba174..d6e5790 100644
--- a/src/cairo-path-stroke.c
+++ b/src/cairo-path-stroke.c
@@ -38,11 +38,23 @@
 #include "cairoint.h"
 #include "cairo-path-fixed-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;
+    cairo_stroke_style_t *style;
 
-    cairo_matrix_t *ctm;
-    cairo_matrix_t *ctm_inverse;
+    const cairo_matrix_t *ctm;
+    const cairo_matrix_t *ctm_inverse;
     double tolerance;
     double ctm_determinant;
     cairo_bool_t ctm_det_positive;
@@ -62,62 +74,77 @@ typedef struct cairo_stroker {
     cairo_bool_t has_first_face;
     cairo_stroke_face_t first_face;
 
-    cairo_bool_t dashed;
-    unsigned int dash_index;
-    cairo_bool_t dash_on;
-    cairo_bool_t dash_starts_on;
-    double dash_remain;
+    cairo_stroker_dash_t dash;
 
     cairo_bool_t has_bounds;
     cairo_box_t bounds;
 } cairo_stroker_t;
 
 static void
-_cairo_stroker_start_dash (cairo_stroker_t *stroker)
+_cairo_stroker_dash_start (cairo_stroker_dash_t *dash)
 {
     double offset;
     cairo_bool_t on = TRUE;
     unsigned int i = 0;
 
-    offset = stroker->style->dash_offset;
+    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 >= stroker->style->dash[i]) {
-	offset -= stroker->style->dash[i];
+    while (offset > 0.0 && offset >= dash->dashes[i]) {
+	offset -= dash->dashes[i];
 	on = !on;
-	if (++i == stroker->style->num_dashes)
+	if (++i == dash->num_dashes)
 	    i = 0;
     }
-    stroker->dashed = TRUE;
-    stroker->dash_index = i;
-    stroker->dash_on = stroker->dash_starts_on = on;
-    stroker->dash_remain = stroker->style->dash[i] - offset;
+
+    dash->dash_index = i;
+    dash->dash_on = dash->dash_starts_on = on;
+    dash->dash_remain = dash->dashes[i] - offset;
 }
 
 static void
-_cairo_stroker_step_dash (cairo_stroker_t *stroker, double step)
+_cairo_stroker_dash_step (cairo_stroker_dash_t *dash, double step)
 {
-    stroker->dash_remain -= step;
-    if (stroker->dash_remain <= 0) {
-	stroker->dash_index++;
-	if (stroker->dash_index == stroker->style->num_dashes)
-	    stroker->dash_index = 0;
-	stroker->dash_on = !stroker->dash_on;
-	stroker->dash_remain = stroker->style->dash[stroker->dash_index];
+    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,
 		     cairo_stroke_style_t	*stroke_style,
-		     cairo_matrix_t		*ctm,
-		     cairo_matrix_t		*ctm_inverse,
+		     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;
@@ -137,10 +164,7 @@ _cairo_stroker_init (cairo_stroker_t		*stroker,
     stroker->has_first_face = FALSE;
     stroker->has_initial_sub_path = FALSE;
 
-    if (stroker->style->dash)
-	_cairo_stroker_start_dash (stroker);
-    else
-	stroker->dashed = FALSE;
+    _cairo_stroker_dash_init (&stroker->dash, stroke_style);
 
     stroker->has_bounds = _cairo_traps_get_limit (traps, &stroker->bounds);
     if (stroker->has_bounds) {
@@ -151,14 +175,15 @@ _cairo_stroker_init (cairo_stroker_t		*stroker,
 	double dx, dy;
 	cairo_fixed_t fdx, fdy;
 
-	_cairo_stroke_style_max_distance_from_path (stroker->style, stroker->ctm, &dx, &dy);
+	_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;
 
+	fdy = _cairo_fixed_from_double (dy);
 	stroker->bounds.p1.y -= fdy;
 	stroker->bounds.p2.y += fdy;
     }
@@ -222,11 +247,11 @@ _cairo_stroker_join (cairo_stroker_t *stroker, cairo_stroke_face_t *in, cairo_st
     }
 
     if (clockwise) {
-    	inpt = &in->ccw;
-    	outpt = &out->ccw;
+	inpt = &in->ccw;
+	outpt = &out->ccw;
     } else {
-    	inpt = &in->cw;
-    	outpt = &out->cw;
+	inpt = &in->cw;
+	outpt = &out->cw;
     }
 
     switch (stroker->style->line_join) {
@@ -257,7 +282,7 @@ _cairo_stroker_join (cairo_stroker_t *stroker, cairo_stroke_face_t *in, cairo_st
 	    tri[2] = in->point;
 	    _translate_point (&tri[2], &pen->vertices[i].point);
 	    status = _cairo_traps_tessellate_triangle (stroker->traps, tri);
-	    if (status)
+	    if (unlikely (status))
 		return status;
 	    tri[1] = tri[2];
 	    i += step;
@@ -297,7 +322,7 @@ _cairo_stroker_join (cairo_stroker_t *stroker, cairo_stroke_face_t *in, cairo_st
 	 *	    |   \
 	 *	    |    \
 	 *	    |     \
-	 * 	  miter    \
+	 *	  miter    \
 	 *	 length     \
 	 *	    |        \
 	 *	    |        .\
@@ -464,7 +489,7 @@ _cairo_stroker_add_cap (cairo_stroker_t *stroker, cairo_stroke_face_t *f)
 	    tri[2] = f->point;
 	    _translate_point (&tri[2], &pen->vertices[i].point);
 	    status = _cairo_traps_tessellate_triangle (stroker->traps, tri);
-	    if (status)
+	    if (unlikely (status))
 		return status;
 	    tri[1] = tri[2];
 	}
@@ -543,7 +568,9 @@ _cairo_stroker_add_trailing_cap (cairo_stroker_t     *stroker,
 }
 
 static inline cairo_bool_t
-_compute_normalized_device_slope (double *dx, double *dy, cairo_matrix_t *ctm_inverse, double *mag_out)
+_compute_normalized_device_slope (double *dx, double *dy,
+				  const cairo_matrix_t *ctm_inverse,
+				  double *mag_out)
 {
     double dx0 = *dx, dy0 = *dy;
     double mag;
@@ -575,7 +602,7 @@ _compute_normalized_device_slope (double *dx, double *dy, cairo_matrix_t *ctm_in
 	    *dx = -1.0;
 	}
     } else {
-	mag = sqrt (dx0 * dx0 + dy0 * dy0);
+	mag = hypot (dx0, dy0);
 	*dx = dx0 / mag;
 	*dy = dy0 / mag;
     }
@@ -716,8 +743,8 @@ static cairo_status_t
 _cairo_stroker_move_to (void *closure,
 			const cairo_point_t *point)
 {
-    cairo_status_t status;
     cairo_stroker_t *stroker = closure;
+    cairo_status_t status;
 
     /* Cap the start and end of the previous sub path as needed */
     status = _cairo_stroker_add_caps (stroker);
@@ -738,9 +765,10 @@ 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_start_dash (stroker);
+
+    /* reset the dash pattern for new sub paths */
+    _cairo_stroker_dash_start (&stroker->dash);
 
     return _cairo_stroker_move_to (closure, point);
 }
@@ -810,7 +838,7 @@ _cairo_stroker_line_to_dashed (void *closure,
     cairo_bool_t fully_in_bounds;
     cairo_status_t status;
 
-    stroker->has_initial_sub_path = stroker->dash_starts_on;
+    stroker->has_initial_sub_path = stroker->dash.dash_starts_on;
 
     if (p1->x == p2->x && p1->y == p2->y)
 	return CAIRO_STATUS_SUCCESS;
@@ -837,7 +865,7 @@ _cairo_stroker_line_to_dashed (void *closure,
     remain = mag;
     segment.p1 = *p1;
     while (remain) {
-	step_length = MIN (stroker->dash_remain, remain);
+	step_length = MIN (stroker->dash.dash_remain, remain);
 	remain -= step_length;
 	dx2 = slope_dx * (mag - remain);
 	dy2 = slope_dy * (mag - remain);
@@ -845,9 +873,9 @@ _cairo_stroker_line_to_dashed (void *closure,
 	segment.p2.x = _cairo_fixed_from_double (dx2) + p1->x;
 	segment.p2.y = _cairo_fixed_from_double (dy2) + p1->y;
 
-	if (stroker->dash_on &&
+	if (stroker->dash.dash_on &&
 	    (fully_in_bounds ||
-	     (! stroker->has_first_face && stroker->dash_starts_on) ||
+	     (! stroker->has_first_face && stroker->dash.dash_starts_on) ||
 	     _cairo_box_intersects_line_segment (&stroker->bounds, &segment)))
 	{
 	    status = _cairo_stroker_add_sub_edge (stroker,
@@ -858,7 +886,8 @@ _cairo_stroker_line_to_dashed (void *closure,
 	    if (unlikely (status))
 		return status;
 
-	    if (stroker->has_current_face) {
+	    if (stroker->has_current_face)
+	    {
 		/* Join with final face from previous segment */
 		status = _cairo_stroker_join (stroker,
 					      &stroker->current_face,
@@ -867,11 +896,16 @@ _cairo_stroker_line_to_dashed (void *closure,
 		    return status;
 
 		stroker->has_current_face = FALSE;
-	    } else if (! stroker->has_first_face && stroker->dash_starts_on) {
+	    }
+	    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 {
+	    }
+	    else
+	    {
 		/* Cap dash start if not connecting to a previous segment */
 		status = _cairo_stroker_add_leading_cap (stroker, &sub_start);
 		if (unlikely (status))
@@ -899,11 +933,11 @@ _cairo_stroker_line_to_dashed (void *closure,
 	    }
 	}
 
-	_cairo_stroker_step_dash (stroker, step_length);
+	_cairo_stroker_dash_step (&stroker->dash, step_length);
 	segment.p1 = segment.p2;
     }
 
-    if (stroker->dash_on && ! stroker->has_current_face) {
+    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.
 	 *
@@ -1048,9 +1082,7 @@ _cairo_stroker_curve_to_dashed (void *closure,
     cairo_status_t status;
 
     if (! _cairo_spline_init (&spline,
-			      stroker->dashed ?
-			      _cairo_stroker_line_to_dashed :
-			      _cairo_stroker_line_to,
+			      _cairo_stroker_line_to_dashed,
 			      stroker,
 			      a, b, c, d))
     {
@@ -1080,7 +1112,7 @@ _cairo_stroker_close_path (void *closure)
     cairo_status_t status;
     cairo_stroker_t *stroker = closure;
 
-    if (stroker->dashed)
+    if (stroker->dash.dashed)
 	status = _cairo_stroker_line_to_dashed (stroker, &stroker->first_point);
     else
 	status = _cairo_stroker_line_to (stroker, &stroker->first_point);
@@ -1109,14 +1141,14 @@ _cairo_stroker_close_path (void *closure)
 static cairo_int_status_t
 _cairo_path_fixed_stroke_rectilinear (cairo_path_fixed_t	*path,
 				      cairo_stroke_style_t	*stroke_style,
-				      cairo_matrix_t		*ctm,
+				      const cairo_matrix_t	*ctm,
 				      cairo_traps_t		*traps);
 
 cairo_status_t
 _cairo_path_fixed_stroke_to_traps (cairo_path_fixed_t	*path,
 				   cairo_stroke_style_t	*stroke_style,
-				   cairo_matrix_t	*ctm,
-				   cairo_matrix_t	*ctm_inverse,
+				   const cairo_matrix_t	*ctm,
+				   const cairo_matrix_t	*ctm_inverse,
 				   double		 tolerance,
 				   cairo_traps_t	*traps)
 {
@@ -1169,26 +1201,56 @@ BAIL:
     return status;
 }
 
-typedef struct _cairo_rectilinear_stroker
-{
+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 {
     cairo_stroke_style_t *stroke_style;
+    const cairo_matrix_t *ctm;
+
     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;
-    cairo_line_t *segments;
-    cairo_line_t segments_embedded[8]; /* common case is a single rectangle */
+    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 *box)
+{
+    stroker->has_bounds = TRUE;
+    stroker->bounds = *box;
+
+    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 void
 _cairo_rectilinear_stroker_init (cairo_rectilinear_stroker_t	*stroker,
 				 cairo_stroke_style_t		*stroke_style,
+				 const cairo_matrix_t		*ctm,
 				 cairo_traps_t			*traps)
 {
     stroker->stroke_style = stroke_style;
+    stroker->ctm = ctm;
+
     stroker->half_line_width =
 	_cairo_fixed_from_double (stroke_style->line_width / 2.0);
     stroker->traps = traps;
@@ -1196,6 +1258,10 @@ _cairo_rectilinear_stroker_init (cairo_rectilinear_stroker_t	*stroker,
     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;
 }
 
 static void
@@ -1206,25 +1272,27 @@ _cairo_rectilinear_stroker_fini (cairo_rectilinear_stroker_t	*stroker)
 }
 
 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_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 (stroker->num_segments == stroker->segments_size) {
 	int new_size = stroker->segments_size * 2;
-	cairo_line_t *new_segments;
+	segment_t *new_segments;
 
 	if (stroker->segments == stroker->segments_embedded) {
-	    new_segments = _cairo_malloc_ab (new_size, sizeof (cairo_line_t));
+	    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 (cairo_line_t));
+		    stroker->num_segments * sizeof (segment_t));
 	} else {
 	    new_segments = _cairo_realloc_ab (stroker->segments,
-					      new_size, sizeof (cairo_line_t));
+					      new_size, sizeof (segment_t));
 	    if (unlikely (new_segments == NULL))
 		return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 	}
@@ -1235,6 +1303,8 @@ _cairo_rectilinear_stroker_add_segment (cairo_rectilinear_stroker_t	*stroker,
 
     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;
@@ -1359,16 +1429,142 @@ _cairo_rectilinear_stroker_emit_segments (cairo_rectilinear_stroker_t *stroker)
 }
 
 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;
+	    }
+
+	    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;
+
+	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;
 
-    status = _cairo_rectilinear_stroker_emit_segments (stroker);
+    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;
 
@@ -1391,7 +1587,9 @@ _cairo_rectilinear_stroker_line_to (void		*closure,
     if (a->x == b->x && a->y == b->y)
 	return CAIRO_STATUS_SUCCESS;
 
-    status = _cairo_rectilinear_stroker_add_segment (stroker, a, b);
+    status = _cairo_rectilinear_stroker_add_segment (stroker, a, b,
+						     a->y == b->y,
+						     TRUE);
 
     stroker->current_point = *b;
     stroker->open_sub_path = TRUE;
@@ -1400,6 +1598,110 @@ _cairo_rectilinear_stroker_line_to (void		*closure,
 }
 
 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. */
+    if (! (a->x == b->x || a->y == b->y))
+	return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    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;
@@ -1409,14 +1711,22 @@ _cairo_rectilinear_stroker_close_path (void *closure)
     if (! stroker->open_sub_path)
 	return CAIRO_STATUS_SUCCESS;
 
-    status = _cairo_rectilinear_stroker_line_to (stroker,
-						 &stroker->first_point);
+    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;
 
-    status = _cairo_rectilinear_stroker_emit_segments (stroker);
+    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;
 
@@ -1426,16 +1736,16 @@ _cairo_rectilinear_stroker_close_path (void *closure)
 static cairo_int_status_t
 _cairo_path_fixed_stroke_rectilinear (cairo_path_fixed_t	*path,
 				      cairo_stroke_style_t	*stroke_style,
-				      cairo_matrix_t		*ctm,
+				      const cairo_matrix_t	*ctm,
 				      cairo_traps_t		*traps)
 {
     cairo_rectilinear_stroker_t rectilinear_stroker;
     cairo_int_status_t status;
 
     /* This special-case rectilinear stroker only supports
-     * miter-joined lines (not curves) and no dashing and a
-     * translation-only matrix (though it could probably be extended
-     * to support a matrix with uniform, integer scaling).
+     * 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
@@ -1452,8 +1762,6 @@ _cairo_path_fixed_stroke_rectilinear (cairo_path_fixed_t	*path,
      * which we round for safety. */
     if (stroke_style->miter_limit < M_SQRT2)
 	return CAIRO_INT_STATUS_UNSUPPORTED;
-    if (stroke_style->dash)
-	return CAIRO_INT_STATUS_UNSUPPORTED;
     if (! (stroke_style->line_cap == CAIRO_LINE_CAP_BUTT ||
 	   stroke_style->line_cap == CAIRO_LINE_CAP_SQUARE))
     {
@@ -1465,11 +1773,20 @@ _cairo_path_fixed_stroke_rectilinear (cairo_path_fixed_t	*path,
 	return CAIRO_INT_STATUS_UNSUPPORTED;
     }
 
-    _cairo_rectilinear_stroker_init (&rectilinear_stroker, stroke_style, traps);
+    _cairo_rectilinear_stroker_init (&rectilinear_stroker,
+				     stroke_style,
+				     ctm,
+				     traps);
+    if (traps->has_limits) {
+	_cairo_rectilinear_stroker_limit (&rectilinear_stroker,
+					  &traps->limits);
+    }
 
     status = _cairo_path_fixed_interpret (path,
 					  CAIRO_DIRECTION_FORWARD,
 					  _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,
@@ -1477,7 +1794,10 @@ _cairo_path_fixed_stroke_rectilinear (cairo_path_fixed_t	*path,
     if (unlikely (status))
 	goto BAIL;
 
-    status = _cairo_rectilinear_stroker_emit_segments (&rectilinear_stroker);
+    if (rectilinear_stroker.dash.dashed)
+	status = _cairo_rectilinear_stroker_emit_segments_dashed (&rectilinear_stroker);
+    else
+	status = _cairo_rectilinear_stroker_emit_segments (&rectilinear_stroker);
 
 BAIL:
     _cairo_rectilinear_stroker_fini (&rectilinear_stroker);
diff --git a/src/cairo-pen.c b/src/cairo-pen.c
index d543765..9d5e895 100644
--- a/src/cairo-pen.c
+++ b/src/cairo-pen.c
@@ -39,7 +39,9 @@
 #include "cairoint.h"
 
 static int
-_cairo_pen_vertices_needed (double tolerance, double radius, cairo_matrix_t *matrix);
+_cairo_pen_vertices_needed (double tolerance,
+			    double radius,
+			    const cairo_matrix_t *matrix);
 
 static void
 _cairo_pen_compute_slopes (cairo_pen_t *pen);
@@ -48,7 +50,7 @@ cairo_status_t
 _cairo_pen_init (cairo_pen_t	*pen,
 		 double		 radius,
 		 double		 tolerance,
-		 cairo_matrix_t	*ctm)
+		 const cairo_matrix_t	*ctm)
 {
     int i;
     int reflect;
@@ -258,7 +260,7 @@ doesn't matter where on the circle the error is computed.
 static int
 _cairo_pen_vertices_needed (double	    tolerance,
 			    double	    radius,
-			    cairo_matrix_t  *matrix)
+			    const cairo_matrix_t  *matrix)
 {
     /*
      * the pen is a circle that gets transformed to an ellipse by matrix.
@@ -266,7 +268,8 @@ _cairo_pen_vertices_needed (double	    tolerance,
      * we don't need the minor axis length.
      */
 
-    double  major_axis = _cairo_matrix_transformed_circle_major_axis(matrix, radius);
+    double  major_axis = _cairo_matrix_transformed_circle_major_axis (matrix,
+								      radius);
 
     /*
      * compute number of vertices needed
diff --git a/src/cairo-stroke-style.c b/src/cairo-stroke-style.c
index edc4f88..d808ad8 100644
--- a/src/cairo-stroke-style.c
+++ b/src/cairo-stroke-style.c
@@ -95,9 +95,19 @@ _cairo_stroke_style_max_distance_from_path (const cairo_stroke_style_t *style,
                                             const cairo_matrix_t *ctm,
                                             double *dx, double *dy)
 {
-    double style_expansion = MAX(style->line_cap == CAIRO_LINE_CAP_SQUARE ? M_SQRT1_2 : 0.5,
-                                 style->line_join == CAIRO_LINE_JOIN_MITER ? style->miter_limit : 0.5);
+    double style_expansion = 0.5;
 
-    *dx = style->line_width * style_expansion * (fabs(ctm->xx) + fabs(ctm->xy));
-    *dy = style->line_width * style_expansion * (fabs(ctm->yy) + fabs(ctm->yx));
+    if (style->line_cap == CAIRO_LINE_CAP_SQUARE)
+	style_expansion = M_SQRT1_2;
+
+    if (style->line_join == CAIRO_LINE_JOIN_MITER &&
+	style_expansion < style->miter_limit)
+    {
+	style_expansion = style->miter_limit;
+    }
+
+    style_expansion *= style->line_width;
+
+    *dx = style_expansion * (fabs (ctm->xx) + fabs (ctm->xy));
+    *dy = style_expansion * (fabs (ctm->yy) + fabs (ctm->yx));
 }
diff --git a/src/cairoint.h b/src/cairoint.h
index fe9ea5f..8115c81 100644
--- a/src/cairoint.h
+++ b/src/cairoint.h
@@ -1599,8 +1599,8 @@ _cairo_path_fixed_fill_to_traps (cairo_path_fixed_t *path,
 cairo_private cairo_status_t
 _cairo_path_fixed_stroke_to_traps (cairo_path_fixed_t	*path,
 				   cairo_stroke_style_t	*stroke_style,
-				   cairo_matrix_t	*ctm,
-				   cairo_matrix_t	*ctm_inverse,
+				   const cairo_matrix_t	*ctm,
+				   const cairo_matrix_t	*ctm_inverse,
 				   double		 tolerance,
 				   cairo_traps_t	*traps);
 
@@ -2173,7 +2173,7 @@ cairo_private cairo_status_t
 _cairo_pen_init (cairo_pen_t	*pen,
 		 double		 radius,
 		 double		 tolerance,
-		 cairo_matrix_t	*ctm);
+		 const cairo_matrix_t	*ctm);
 
 cairo_private void
 _cairo_pen_init_empty (cairo_pen_t *pen);
@@ -2298,7 +2298,7 @@ _cairo_matrix_compute_determinant (const cairo_matrix_t *matrix);
 
 cairo_private cairo_status_t
 _cairo_matrix_compute_basis_scale_factors (const cairo_matrix_t *matrix,
-				     double *sx, double *sy, int x_major);
+					   double *sx, double *sy, int x_major);
 
 cairo_private cairo_bool_t
 _cairo_matrix_is_identity (const cairo_matrix_t *matrix);
@@ -2314,7 +2314,8 @@ cairo_private cairo_bool_t
 _cairo_matrix_is_pixel_exact (const cairo_matrix_t *matrix);
 
 cairo_private double
-_cairo_matrix_transformed_circle_major_axis(cairo_matrix_t *matrix, double radius);
+_cairo_matrix_transformed_circle_major_axis (const cairo_matrix_t *matrix,
+					     double radius);
 
 cairo_private void
 _cairo_matrix_to_pixman_matrix (const cairo_matrix_t	*matrix,
diff --git a/test/Makefile.am b/test/Makefile.am
index 8c849ba..0b53116 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -634,8 +634,8 @@ REFERENCE_IMAGES = \
 	leaky-dash.quartz.ref.png	\
 	leaky-dash.ref.png	\
 	leaky-dashed-rectangle.ref.png		\
-	leaky-dashed-rectangle.ps2.ref.png	\
-	leaky-dashed-rectangle.ps3.ref.png	\
+	leaky-dashed-rectangle.pdf.ref.png	\
+	leaky-dashed-rectangle.ps.ref.png	\
 	leaky-dashed-stroke.ref.png		\
 	leaky-dashed-stroke.ps2.ref.png		\
 	leaky-dashed-stroke.ps3.ref.png		\
diff --git a/test/leaky-dashed-rectangle.pdf.ref.png b/test/leaky-dashed-rectangle.pdf.ref.png
new file mode 100644
index 0000000..690cb36
Binary files /dev/null and b/test/leaky-dashed-rectangle.pdf.ref.png differ
diff --git a/test/leaky-dashed-rectangle.ps.ref.png b/test/leaky-dashed-rectangle.ps.ref.png
new file mode 100644
index 0000000..e432de2
Binary files /dev/null and b/test/leaky-dashed-rectangle.ps.ref.png differ
diff --git a/test/leaky-dashed-rectangle.ps2.ref.png b/test/leaky-dashed-rectangle.ps2.ref.png
deleted file mode 100644
index e432de2..0000000
Binary files a/test/leaky-dashed-rectangle.ps2.ref.png and /dev/null differ
diff --git a/test/leaky-dashed-rectangle.ps3.ref.png b/test/leaky-dashed-rectangle.ps3.ref.png
deleted file mode 100644
index e432de2..0000000
Binary files a/test/leaky-dashed-rectangle.ps3.ref.png and /dev/null differ
diff --git a/test/leaky-dashed-rectangle.ref.png b/test/leaky-dashed-rectangle.ref.png
index 332d2fd..c0ba7b2 100644
Binary files a/test/leaky-dashed-rectangle.ref.png and b/test/leaky-dashed-rectangle.ref.png differ


More information about the cairo-commit mailing list