[cairo-commit] 2 commits - src/cairo-fixed-private.h src/cairo-path-stroke.c

Martin Robinson mrobinson at kemper.freedesktop.org
Thu Mar 14 10:37:17 PDT 2013


 src/cairo-fixed-private.h |   34 ++++++++++++
 src/cairo-path-stroke.c   |  126 +++++++++++++++++++++++++++++++++++++++++++---
 2 files changed, 152 insertions(+), 8 deletions(-)

New commits:
commit 2c2dccf5a4d298c866f7c0faed2e10c65252c168
Author: Martin Robinson <mrobinson at igalia.com>
Date:   Tue Mar 12 15:17:19 2013 -0700

    stroke: Use round-joins near inflection points of splines
    
    Similar to b7bd5ae4f3da44131261711bb236cd7aa24a3ae3, but applied to the
    fallback stroke shaper.

diff --git a/src/cairo-path-stroke.c b/src/cairo-path-stroke.c
index 6fae093..4d4ede8 100644
--- a/src/cairo-path-stroke.c
+++ b/src/cairo-path-stroke.c
@@ -54,6 +54,7 @@ typedef struct cairo_stroker {
     const cairo_matrix_t *ctm_inverse;
     double half_line_width;
     double tolerance;
+    double spline_cusp_tolerance;
     double ctm_determinant;
     cairo_bool_t ctm_det_positive;
 
@@ -137,6 +138,18 @@ _cairo_stroker_init (cairo_stroker_t		*stroker,
     stroker->tolerance = tolerance;
     stroker->half_line_width = stroke_style->line_width / 2.0;
 
+    /* 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;
 
@@ -1012,6 +1025,29 @@ _cairo_stroker_spline_to (void *closure,
 
     assert (stroker->has_current_face);
 
+    if ((new_face.dev_slope.x * stroker->current_face.dev_slope.x +
+         new_face.dev_slope.y * stroker->current_face.dev_slope.y) < stroker->spline_cusp_tolerance) {
+
+	const cairo_point_t *inpt, *outpt;
+	int clockwise = _cairo_stroker_join_is_clockwise (&new_face,
+							  &stroker->current_face);
+
+	if (clockwise) {
+	    inpt = &stroker->current_face.cw;
+	    outpt = &new_face.cw;
+	} else {
+	    inpt = &stroker->current_face.ccw;
+	    outpt = &new_face.ccw;
+	}
+
+	_tessellate_fan (stroker,
+			 &stroker->current_face.dev_vector,
+			 &new_face.dev_vector,
+			 &stroker->current_face.point,
+			 inpt, outpt,
+			 clockwise);
+    }
+
     if (_slow_segment_intersection (&stroker->current_face.cw,
 				    &stroker->current_face.ccw,
 				    &new_face.cw,
commit aadece05fb1cf80f0a1138368f4664e878a59204
Author: Martin Robinson <mrobinson at igalia.com>
Date:   Tue Mar 12 15:16:01 2013 -0700

    stroke: Fix large line widths for fallback stroke shaper
    
    Fix the test case line-width-tolerance for the fallback stroke shaper.
    Instead of drawing quads between spline points, draw triangle based on
    the actual spline edges. This roughly follows the approach of the
    tristrip and polygonal shapers.

diff --git a/src/cairo-fixed-private.h b/src/cairo-fixed-private.h
index b6cc6be..352ca39 100644
--- a/src/cairo-fixed-private.h
+++ b/src/cairo-fixed-private.h
@@ -352,6 +352,40 @@ _cairo_edge_compute_intersection_x_for_y (const cairo_point_t *p1,
     return x;
 }
 
+/* Intersect two segments based on the algorithm described at
+ * http://paulbourke.net/geometry/pointlineplane/. This implementation
+ * uses floating point math. */
+static inline cairo_bool_t
+_slow_segment_intersection (const cairo_point_t *seg1_p1,
+			    const cairo_point_t *seg1_p2,
+			    const cairo_point_t *seg2_p1,
+			    const cairo_point_t *seg2_p2,
+			    cairo_point_t *intersection)
+{
+    double denominator, u_a, u_b;
+    double seg1_dx, seg1_dy, seg2_dx, seg2_dy, seg_start_dx, seg_start_dy;
+
+    seg1_dx = _cairo_fixed_to_double (seg1_p2->x - seg1_p1->x);
+    seg1_dy = _cairo_fixed_to_double (seg1_p2->y - seg1_p1->y);
+    seg2_dx = _cairo_fixed_to_double (seg2_p2->x - seg2_p1->x);
+    seg2_dy = _cairo_fixed_to_double (seg2_p2->y - seg2_p1->y);
+    denominator = (seg2_dy * seg1_dx) - (seg2_dx * seg1_dy);
+    if (denominator == 0)
+	return FALSE;
+
+    seg_start_dx = _cairo_fixed_to_double (seg1_p1->x - seg2_p1->x);
+    seg_start_dy = _cairo_fixed_to_double (seg1_p1->y - seg2_p1->y);
+    u_a = ((seg2_dx * seg_start_dy) - (seg2_dy * seg_start_dx)) / denominator;
+    u_b = ((seg1_dx * seg_start_dy) - (seg1_dy * seg_start_dx)) / denominator;
+
+    if (u_a <= 0 || u_a >= 1 || u_b <= 0 || u_b >= 1)
+	return FALSE;
+
+    intersection->x = seg1_p1->x + _cairo_fixed_from_double ((u_a * seg1_dx));
+    intersection->y = seg1_p1->y + _cairo_fixed_from_double ((u_a * seg1_dy));
+    return TRUE;
+}
+
 #else
 # error Please define multiplication and other operands for your fixed-point type size
 #endif
diff --git a/src/cairo-path-stroke.c b/src/cairo-path-stroke.c
index cd6b3a2..6fae093 100644
--- a/src/cairo-path-stroke.c
+++ b/src/cairo-path-stroke.c
@@ -761,9 +761,12 @@ _compute_normalized_device_slope (double *dx, double *dy,
 }
 
 static void
-_compute_face (const cairo_point_t *point, cairo_slope_t *dev_slope,
-	       double slope_dx, double slope_dy,
-	       cairo_stroker_t *stroker, cairo_stroke_face_t *face)
+_compute_face (const cairo_point_t *point,
+	       const cairo_slope_t *dev_slope,
+	       double slope_dx,
+	       double slope_dy,
+	       cairo_stroker_t *stroker,
+	       cairo_stroke_face_t *face)
 {
     double face_dx, face_dy;
     cairo_point_t offset_ccw, offset_cw;
@@ -979,6 +982,68 @@ _cairo_stroker_line_to (void *closure,
     return CAIRO_STATUS_SUCCESS;
 }
 
+static cairo_status_t
+_cairo_stroker_spline_to (void *closure,
+			  const cairo_point_t *point,
+			  const cairo_slope_t *tangent)
+{
+    cairo_stroker_t *stroker = closure;
+    cairo_stroke_face_t new_face;
+    double slope_dx, slope_dy;
+    cairo_point_t points[3];
+    cairo_point_t intersect_point;
+
+    stroker->has_initial_sub_path = TRUE;
+
+    if (stroker->current_point.x == point->x &&
+	stroker->current_point.y == point->y)
+	return CAIRO_STATUS_SUCCESS;
+
+    slope_dx = _cairo_fixed_to_double (tangent->dx);
+    slope_dy = _cairo_fixed_to_double (tangent->dy);
+
+    if (! _compute_normalized_device_slope (&slope_dx, &slope_dy,
+					    stroker->ctm_inverse, NULL))
+	return CAIRO_STATUS_SUCCESS;
+
+    _compute_face (point, tangent,
+		   slope_dx, slope_dy,
+		   stroker, &new_face);
+
+    assert (stroker->has_current_face);
+
+    if (_slow_segment_intersection (&stroker->current_face.cw,
+				    &stroker->current_face.ccw,
+				    &new_face.cw,
+				    &new_face.ccw,
+				    &intersect_point)) {
+	points[0] = stroker->current_face.ccw;
+	points[1] = new_face.ccw;
+	points[2] = intersect_point;
+	stroker->add_triangle (stroker->closure, points);
+
+	points[0] = stroker->current_face.cw;
+	points[1] = new_face.cw;
+	stroker->add_triangle (stroker->closure, points);
+    } else {
+	points[0] = stroker->current_face.ccw;
+	points[1] = stroker->current_face.cw;
+	points[2] = new_face.cw;
+	stroker->add_triangle (stroker->closure, points);
+
+	points[0] = stroker->current_face.ccw;
+	points[1] = new_face.cw;
+	points[2] = new_face.ccw;
+	stroker->add_triangle (stroker->closure, points);
+    }
+
+    stroker->current_face = new_face;
+    stroker->has_current_face = TRUE;
+    stroker->current_point = *point;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
 /*
  * Dashed lines.  Cap each dash end, join around turns when on
  */
@@ -1135,18 +1200,27 @@ _cairo_stroker_curve_to (void *closure,
     cairo_line_join_t line_join_save;
     cairo_stroke_face_t face;
     double slope_dx, slope_dy;
-    cairo_path_fixed_line_to_func_t *line_to;
+    cairo_spline_add_point_func_t line_to;
+    cairo_spline_add_point_func_t spline_to;
     cairo_status_t status = CAIRO_STATUS_SUCCESS;
 
     line_to = stroker->dash.dashed ?
-	_cairo_stroker_line_to_dashed :
-	_cairo_stroker_line_to;
+	(cairo_spline_add_point_func_t) _cairo_stroker_line_to_dashed :
+	(cairo_spline_add_point_func_t) _cairo_stroker_line_to;
+
+    /* spline_to is only capable of rendering non-degenerate splines. */
+    spline_to = stroker->dash.dashed ?
+	(cairo_spline_add_point_func_t) _cairo_stroker_line_to_dashed :
+	(cairo_spline_add_point_func_t) _cairo_stroker_spline_to;
 
     if (! _cairo_spline_init (&spline,
-			      (cairo_spline_add_point_func_t)line_to, stroker,
+			      spline_to,
+			      stroker,
 			      &stroker->current_point, b, c, d))
     {
-	return line_to (closure, d);
+	cairo_slope_t fallback_slope;
+	_cairo_slope_init (&fallback_slope, &stroker->current_point, d);
+	return line_to (closure, d, &fallback_slope);
     }
 
     /* If the line width is so small that the pen is reduced to a


More information about the cairo-commit mailing list