[cairo-commit] 6 commits - src/cairo-gstate.c src/cairo-path-stroke.c src/cairo-quartz-surface.c src/cairo-stroke-style.c

Andrea Canciani ranma42 at kemper.freedesktop.org
Mon Feb 1 00:57:18 PST 2010


 src/cairo-gstate.c         |   13 +++++++
 src/cairo-path-stroke.c    |   24 +++-----------
 src/cairo-quartz-surface.c |   32 ++++++------------
 src/cairo-stroke-style.c   |   76 ++++++++++++++++++++++++++++++++++++++++++---
 4 files changed, 101 insertions(+), 44 deletions(-)

New commits:
commit ab605214d151098ad153e8bf74ae0ca71a34c963
Author: Andrea Canciani <ranma42 at gmail.com>
Date:   Mon Feb 1 09:27:16 2010 +0100

    Partially revert "Improve stroking of densely dashed styles"
    
    This reverts commit 26e9f149063b9e1fdb54fc54fccbefdf04a68190 on
    cairo-path-stroke.
    The changes in cairo-path-stroke are not needed anymore since dash
    pattern approximation is now done in gstate before passing the dash
    pattern to the backend.

diff --git a/src/cairo-path-stroke.c b/src/cairo-path-stroke.c
index f9d7de1..b5cd2ed 100644
--- a/src/cairo-path-stroke.c
+++ b/src/cairo-path-stroke.c
@@ -53,7 +53,6 @@ typedef struct _cairo_stroker_dash {
 
     double dash_offset;
     const double *dashes;
-    double approximate_dashes[2];
     unsigned int num_dashes;
 } cairo_stroker_dash_t;
 
@@ -140,25 +139,15 @@ _cairo_stroker_dash_step (cairo_stroker_dash_t *dash, double step)
 
 static void
 _cairo_stroker_dash_init (cairo_stroker_dash_t *dash,
-			  const cairo_stroke_style_t *style,
-			  const cairo_matrix_t *ctm,
-			  double tolerance)
+			  const cairo_stroke_style_t *style)
 {
     dash->dashed = style->dash != NULL;
     if (! dash->dashed)
 	return;
 
-    if (_cairo_stroke_style_dash_can_approximate (style, ctm, tolerance)) {
-	_cairo_stroke_style_dash_approximate (style, ctm, tolerance,
-					      &dash->dash_offset,
-					      dash->approximate_dashes,
-					      &dash->num_dashes);
-	dash->dashes = dash->approximate_dashes;
-    } else {
-	dash->dashes = style->dash;
-	dash->num_dashes = style->num_dashes;
-	dash->dash_offset = style->dash_offset;
-    }
+    dash->dashes = style->dash;
+    dash->num_dashes = style->num_dashes;
+    dash->dash_offset = style->dash_offset;
 
     _cairo_stroker_dash_start (dash);
 }
@@ -192,7 +181,7 @@ _cairo_stroker_init (cairo_stroker_t		*stroker,
     stroker->has_first_face = FALSE;
     stroker->has_initial_sub_path = FALSE;
 
-    _cairo_stroker_dash_init (&stroker->dash, stroke_style, ctm, tolerance);
+    _cairo_stroker_dash_init (&stroker->dash, stroke_style);
 
     stroker->add_external_edge = NULL;
 
@@ -1534,8 +1523,7 @@ _cairo_rectilinear_stroker_init (cairo_rectilinear_stroker_t	*stroker,
     stroker->segments_size = ARRAY_LENGTH (stroker->segments_embedded);
     stroker->num_segments = 0;
 
-    /* Assume 2*EPSILON tolerance */
-    _cairo_stroker_dash_init (&stroker->dash, stroke_style, ctm, _cairo_fixed_to_double (2 * CAIRO_FIXED_EPSILON));
+    _cairo_stroker_dash_init (&stroker->dash, stroke_style);
 
     stroker->has_bounds = FALSE;
 
commit 8ffe0fc3825f0f6bc4d06607f6819ea8c2c2040b
Author: Andrea Canciani <ranma42 at gmail.com>
Date:   Sat Jan 30 09:29:48 2010 +0100

    Revert "[quartz] Approximate dense dashing patterns"
    
    This reverts commit 27701ed8447d4c21b7bf9709a2fc21690b3cdc96.
    Approximation is now done in gstate.

diff --git a/src/cairo-quartz-surface.c b/src/cairo-quartz-surface.c
index 1128536..bbfff37 100644
--- a/src/cairo-quartz-surface.c
+++ b/src/cairo-quartz-surface.c
@@ -2054,30 +2054,20 @@ _cairo_quartz_surface_stroke (void *abstract_surface,
 #define STATIC_DASH 32
 	float sdash[STATIC_DASH];
 	float *fdash = sdash;
-	double offset = style->dash_offset;
 	unsigned int max_dashes = style->num_dashes;
 	unsigned int k;
 
-	if (_cairo_stroke_style_dash_can_approximate (style, ctm, tolerance)) {
-	    double approximate_dashes[2];
-	    _cairo_stroke_style_dash_approximate (style, ctm, tolerance,
-						  &offset,
-						  approximate_dashes,
-						  &max_dashes);
-	    sdash[0] = approximate_dashes[0];
-	    sdash[1] = approximate_dashes[1];
-	} else {
-	    if (style->num_dashes%2)
-		max_dashes *= 2;
-	    if (max_dashes > STATIC_DASH)
-		fdash = _cairo_malloc_ab (max_dashes, sizeof (float));
-	    if (fdash == NULL)
-		return _cairo_error (CAIRO_STATUS_NO_MEMORY);
-
-	    for (k = 0; k < max_dashes; k++)
-		fdash[k] = (float) style->dash[k % style->num_dashes];
-	}
-	CGContextSetLineDash (surface->cgContext, offset, fdash, max_dashes);
+	if (style->num_dashes%2)
+	    max_dashes *= 2;
+	if (max_dashes > STATIC_DASH)
+	    fdash = _cairo_malloc_ab (max_dashes, sizeof (float));
+	if (fdash == NULL)
+	    return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+	for (k = 0; k < max_dashes; k++)
+	    fdash[k] = (float) style->dash[k % style->num_dashes];
+
+	CGContextSetLineDash (surface->cgContext, style->dash_offset, fdash, max_dashes);
 	if (fdash != sdash)
 	    free (fdash);
     } else
commit 2e91648bcba06832dfa92bd742c32b2f5c1ad989
Author: Andrea Canciani <ranma42 at gmail.com>
Date:   Fri Jan 15 15:46:20 2010 +0100

    Approximate dash patterns in gstate
    
    Since approximation is only done when the pattern is sub-tolerance,
    it is acceptable to apply it even for vector backends.
    By doing it once and for all backends, backends are guaranteed to have
    non-degenerate input.
    Acked-by: Adrian Johnson <ajohnson at redneon.com>

diff --git a/src/cairo-gstate.c b/src/cairo-gstate.c
index 8bb7e71..19d80e8 100644
--- a/src/cairo-gstate.c
+++ b/src/cairo-gstate.c
@@ -1113,6 +1113,8 @@ cairo_status_t
 _cairo_gstate_stroke (cairo_gstate_t *gstate, cairo_path_fixed_t *path)
 {
     cairo_pattern_union_t source_pattern;
+    cairo_stroke_style_t style;
+    double dash[2];
     cairo_clip_t clip;
     cairo_status_t status;
 
@@ -1128,13 +1130,22 @@ _cairo_gstate_stroke (cairo_gstate_t *gstate, cairo_path_fixed_t *path)
     if (_clipped (gstate))
 	return CAIRO_STATUS_SUCCESS;
 
+    memcpy (&style, &gstate->stroke_style, sizeof (gstate->stroke_style));
+    if (_cairo_stroke_style_dash_can_approximate (&gstate->stroke_style, &gstate->ctm, gstate->tolerance)) {
+        style.dash = dash;
+        _cairo_stroke_style_dash_approximate (&gstate->stroke_style, &gstate->ctm, gstate->tolerance,
+					      &style.dash_offset,
+					      style.dash,
+					      &style.num_dashes);
+    }
+
     _cairo_gstate_copy_transformed_source (gstate, &source_pattern.base);
 
     status = _cairo_surface_stroke (gstate->target,
 				    _reduce_op (gstate, &source_pattern),
 				    &source_pattern.base,
 				    path,
-				    &gstate->stroke_style,
+				    &style,
 				    &gstate->ctm,
 				    &gstate->ctm_inverse,
 				    gstate->tolerance,
commit 67f666896cbfd0ac2728c7aa1325a7d0b2c8282d
Author: Andrea Canciani <ranma42 at gmail.com>
Date:   Sun Jan 24 22:17:18 2010 +0100

    Improve comments style consistency
    
    Each line of a multi-line comment should begin with '*'.

diff --git a/src/cairo-stroke-style.c b/src/cairo-stroke-style.c
index 9a18e74..8b88106 100644
--- a/src/cairo-stroke-style.c
+++ b/src/cairo-stroke-style.c
@@ -239,8 +239,8 @@ _cairo_stroke_style_dash_approximate (const cairo_stroke_style_t *style,
     scale = tolerance / _cairo_matrix_transformed_circle_major_axis (ctm, 1.0);
 
     /* 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. */
+     * offset reaches zero.  Otherwise when an initial dash
+     * segment shrinks to zero it will be skipped over. */
     offset = style->dash_offset;
     while (offset > 0.0 && offset >= style->dash[i]) {
 	offset -= style->dash[i];
commit 45c795a1820e0317b123e7782cefed6ade8996d7
Author: Andrea Canciani <ranma42 at gmail.com>
Date:   Fri Jan 15 10:44:58 2010 +0100

    Improve dash pattern approximation
    
    Dash pattern approximation was taking the caps style into account
    only for coverage computation, but not when computing the new
    pattern, thus the computed approximation had a higher coverage if
    the caps style was SQUARE or ROUND.
    
    Reviewed-by: M. Joonas Pihlaja <jpihlaja at cc.helsinki.fi>

diff --git a/src/cairo-stroke-style.c b/src/cairo-stroke-style.c
index 28dc5ff..9a18e74 100644
--- a/src/cairo-stroke-style.c
+++ b/src/cairo-stroke-style.c
@@ -251,8 +251,60 @@ _cairo_stroke_style_dash_approximate (const cairo_stroke_style_t *style,
 
     *num_dashes = 2;
 
-    dashes[0] = scale * coverage;
-    dashes[1] = scale * (1.0 - coverage);
+    /*
+     * We want to create a new dash pattern with the same relative coverage,
+     * but composed of just 2 elements with total length equal to scale.
+     * Based on the formula in _cairo_stroke_style_dash_stroked:
+     * scale * coverage = dashes[0] + cap_scale * MIN (dashes[1], line_width)
+     *                  = MIN (dashes[0] + cap_scale * (scale - dashes[0]),
+     *                         dashes[0] + cap_scale * line_width) = 
+     *                  = MIN (dashes[0] * (1 - cap_scale) + cap_scale * scale,
+     *	                       dashes[0] + cap_scale * line_width)
+     *
+     * Solving both cases we get:
+     *   dashes[0] = scale * (coverage - cap_scale) / (1 - cap_scale)
+     *	  when scale - dashes[0] <= line_width
+     *	dashes[0] = scale * coverage - cap_scale * line_width
+     *	  when scale - dashes[0] > line_width.
+     *
+     * Comparing the two cases we get:
+     *   second > first
+     *   second > scale * (coverage - cap_scale) / (1 - cap_scale)
+     *   second - cap_scale * second - scale * coverage + scale * cap_scale > 0
+     * 	 (scale * coverage - cap_scale * line_width) - cap_scale * second - scale * coverage + scale * cap_scale > 0
+     *   - line_width - second + scale > 0
+     *   scale - second > line_width
+     * which is the condition for the second solution to be the valid one.
+     * So when second > first, the second solution is the correct one (i.e.
+     * the solution is always MAX (first, second).
+     */
+    switch (style->line_cap) {
+    default:
+        ASSERT_NOT_REACHED;
+	dashes[0] = 0.0;
+	break;
+
+    case CAIRO_LINE_CAP_BUTT:
+        /* Simplified formula (substituting 0 for cap_scale): */
+        dashes[0] = scale * coverage;
+	break;
+
+    case CAIRO_LINE_CAP_ROUND:
+        dashes[0] = MAX(scale * (coverage - ROUND_MINSQ_APPROXIMATION) / (1.0 - ROUND_MINSQ_APPROXIMATION),
+			scale * coverage - ROUND_MINSQ_APPROXIMATION * style->line_width);
+	break;
+
+    case CAIRO_LINE_CAP_SQUARE:
+        /*
+	 * Special attention is needed to handle the case cap_scale == 1 (since the first solution
+	 * is either indeterminate or -inf in this case). Since dash lengths are always >=0, using
+	 * 0 as first solution always leads to the correct solution.
+	 */
+        dashes[0] = MAX(0.0, scale * coverage - style->line_width);
+	break;
+    }
+
+    dashes[1] = scale - dashes[0];
 
     *dash_offset = on ? 0.0 : dashes[0];
 }
commit 8d7841048b079ce2a582ff17c90e82e0081e5f42
Author: Andrea Canciani <ranma42 at gmail.com>
Date:   Sun Jan 17 19:00:47 2010 +0100

    Round caps coverage extimate explanation
    
    Comment on how the round caps coverage has been computed, explaining
    the complete procedure. The comments doesn't contain intermediate
    (verbose and ugly) results, but when executed in a symbolic math
    program (sage, for example) computes the expected results.
    
    Reviewed-by: M. Joonas Pihlaja <jpihlaja at cc.helsinki.fi>

diff --git a/src/cairo-stroke-style.c b/src/cairo-stroke-style.c
index 6ef85f4..28dc5ff 100644
--- a/src/cairo-stroke-style.c
+++ b/src/cairo-stroke-style.c
@@ -145,6 +145,22 @@ _cairo_stroke_style_dash_period (const cairo_stroke_style_t *style)
 /*
  * Coefficient of the linear approximation (minimizing square difference)
  * of the surface covered by round caps
+ *
+ * This can be computed in the following way:
+ * the area inside the circle with radius w/2 and the region -d/2 <= x <= d/2 is:
+ *   f(w,d) = 2 * integrate (sqrt (w*w/4 - x*x), x, -d/2, d/2)
+ * The square difference to a generic linear approximation (c*d) in the range (0,w) would be:
+ *   integrate ((f(w,d) - c*d)^2, d, 0, w)
+ * To minimize this difference it is sufficient to find a solution of the differential with
+ * respect to c:
+ *   solve ( diff (integrate ((f(w,d) - c*d)^2, d, 0, w), c), c)
+ * Which leads to c = 9/32*pi*w
+ * Since we're not interested in the true area, but just in a coverage extimate,
+ * we always divide the real area by the line width (w).
+ * The same computation for square caps would be
+ *   f(w,d) = 2 * integrate(w/2, x, -d/2, d/2)
+ *   c = 1*w
+ * but in this case it would not be an approximation, since f is already linear in d.
  */
 #define ROUND_MINSQ_APPROXIMATION (9*M_PI/32)
 


More information about the cairo-commit mailing list