[cairo-commit] 3 commits - src/cairo-path-stroke.c src/cairo-scaled-font.c

Carl Worth cworth at kemper.freedesktop.org
Fri Jan 11 12:52:38 PST 2008


 src/cairo-path-stroke.c |  139 +++++++++++++++++++-----------------------------
 src/cairo-scaled-font.c |   50 ++++++++++++++++-
 2 files changed, 104 insertions(+), 85 deletions(-)

New commits:
commit bcf0c442b91b553b226c78d254906f7127013391
Merge: ecb8958... 8a6a0b4...
Author: Carl Worth <cworth at cworth.org>
Date:   Fri Jan 11 12:30:15 2008 -0800

    Merge branch 'master' of git.cairographics.org:/git/cairo into cairo

commit ecb895803b9d2a3fd142f4a2c694ca08c5581f0e
Author: Carl Worth <cworth at cworth.org>
Date:   Fri Jan 11 12:28:49 2008 -0800

    Migrate glyph mask to A8 in case of mixed-format glyphs.
    
    This fixes the remaining image-backend problems with bug 13479:
    
    	Ugly Courier New font with cairo 1.4.12
    	https://bugs.freedesktop.org/show_bug.cgi?id=13479
    
    although the xlib-backend had been fixed previously.
    
    Specifically, if an A1 glyph is first encountered, then subsequent
    glyphs will still be rendered with antialiasing, (previously they
    would be rendered very poorly without antialiasing).
    
    Similarly, if the first glyph encountered has component-alpha
    sub-pixel antialiasing and then an A1 or A8 glyph is encountered
    then all glyphs will rendered in A8 (grayscale antialiasing).
    Previously, the non-subpixel-antialiased glyphs would not appear
    at all.

diff --git a/src/cairo-scaled-font.c b/src/cairo-scaled-font.c
index 2fd1fff..1ad10b9 100644
--- a/src/cairo-scaled-font.c
+++ b/src/cairo-scaled-font.c
@@ -1170,6 +1170,7 @@ _cairo_scaled_font_show_glyphs (cairo_scaled_font_t    *scaled_font,
 {
     cairo_status_t status;
     cairo_surface_t *mask = NULL;
+    cairo_format_t mask_format;
     cairo_surface_pattern_t mask_pattern;
     int i;
 
@@ -1218,9 +1219,11 @@ _cairo_scaled_font_show_glyphs (cairo_scaled_font_t    *scaled_font,
 
 	glyph_surface = scaled_glyph->surface;
 
-	/* Create the mask using the format from the first glyph */
+	/* To start, create the mask using the format from the first
+	 * glyph. Later we'll deal with different formats. */
 	if (mask == NULL) {
-	    mask = cairo_image_surface_create (glyph_surface->format,
+	    mask_format = glyph_surface->format;
+	    mask = cairo_image_surface_create (mask_format,
 					       width, height);
 	    if (mask->status) {
 		status = mask->status;
@@ -1234,10 +1237,51 @@ _cairo_scaled_font_show_glyphs (cairo_scaled_font_t    *scaled_font,
 						    width, height);
 	    if (status)
 		goto CLEANUP_MASK;
-	    if (glyph_surface->format == CAIRO_FORMAT_ARGB32)
+	    if (mask_format == CAIRO_FORMAT_ARGB32)
 		pixman_image_set_component_alpha (((cairo_image_surface_t*) mask)->
 						  pixman_image, TRUE);
+	}
+
+	/* If we have glyphs of different formats, then the only thing
+	 * we can easily do is to migrate to an A8 mask. This is
+	 * sub-optimal if there are any component-alpha ARGB32 glyphs,
+	 * but pixman doesn't actually give us anyoperators that will
+	 * correctly ADD to a component-alpha mask. So here we are. */
+	if (glyph_surface->format != mask_format &&
+	    mask_format != CAIRO_FORMAT_A8)
+	{
+	    cairo_surface_t *new_mask;
+	    cairo_surface_pattern_t mask_pattern;
+
+	    mask_format = CAIRO_FORMAT_A8;
+	    new_mask = cairo_image_surface_create (mask_format,
+						   width, height);
+	    if (new_mask->status) {
+		status = new_mask->status;
+		cairo_surface_destroy (new_mask);
+		goto CLEANUP_MASK;
+	    }
+
+	    _cairo_pattern_init_for_surface (&mask_pattern, mask);
+
+	    status = _cairo_surface_composite (CAIRO_OPERATOR_SOURCE,
+					       &mask_pattern.base,
+					       NULL,
+					       new_mask,
+					       0, 0,
+					       0, 0,
+					       0, 0,
+					       width, height);
+
+	    _cairo_pattern_fini (&mask_pattern.base);
+
+	    if (status) {
+		cairo_surface_destroy (new_mask);
+		goto CLEANUP_MASK;
+	    }
 
+	    cairo_surface_destroy (mask);
+	    mask = new_mask;
 	}
 
 	/* round glyph locations to the nearest pixel */
commit 7cf9a6e4e39b18f4967afdb7c3c71eca5f4ba8c7
Author: Keith Packard <keithp at keithp.com>
Date:   Thu Jan 3 18:37:53 2008 -0800

    Directly check the miter corner to detect wild miters.
    
    The original test for wild miters would only work with a square transform
    (and, in fact, the original code required an identity transform). Instead of
    fixing that, I replaced it with a more obvious test which makes sure the
    miter corner lies between the two faces and not out in space somewhere.

diff --git a/src/cairo-path-stroke.c b/src/cairo-path-stroke.c
index fb12f15..773e9fd 100644
--- a/src/cairo-path-stroke.c
+++ b/src/cairo-path-stroke.c
@@ -205,6 +205,22 @@ _cairo_stroker_face_clockwise (cairo_stroke_face_t *in, cairo_stroke_face_t *out
     return _cairo_slope_clockwise (&in_slope, &out_slope);
 }
 
+/**
+ * _cairo_slope_compare_sgn
+ *
+ * Return -1, 0 or 1 depending on the relative slopes of
+ * two lines.
+ */
+static int
+_cairo_slope_compare_sgn (double dx1, double dy1, double dx2, double dy2)
+{
+    double  c = (dx1 * dy2 - dx2 * dy1);
+
+    if (c > 0) return 1;
+    if (c < 0) return -1;
+    return 0;
+}
+
 static cairo_status_t
 _cairo_stroker_join (cairo_stroker_t *stroker, cairo_stroke_face_t *in, cairo_stroke_face_t *out)
 {
@@ -272,9 +288,6 @@ _cairo_stroker_join (cairo_stroker_t *stroker, cairo_stroke_face_t *in, cairo_st
 	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;
-	double tolerance_squared = stroker->tolerance * stroker->tolerance;
-	double line_width_squared = (stroker->style->line_width *
-				     stroker->style->line_width);
 
 	/* Check the miter limit -- lines meeting at an acute angle
 	 * can generate long miters, the limit converts them to bevel
@@ -332,84 +345,17 @@ _cairo_stroker_join (cairo_stroker_t *stroker, cairo_stroke_face_t *in, cairo_st
 	 *
 	 *	2 <= ml² (1 - in · out)
 	 *
-	 *
-	 * That gives us the condition to avoid generating miters that
-	 * are too large from angles that are too large. But we also
-	 * need to avoid generating miters when the angle is very small.
-	 *
-	 * The miter formed from a tiny angle is also tiny, so the
-	 * miter limit is not a concern. But with a tiny angle we will
-	 * be computing the intersection of two lines that are very
-	 * near parallel. Also, the limits of the fixed-point grid on
-	 * the input face coordinates mean that the resulting
-	 * intersection could be wildly wrong. (See the
-	 * get-path-extents test case for a call to cairo_arc that
-	 * results in two problematic faces.)
-	 *
-	 * Fortunately we can also derive an expression for when using
-	 * a bevel join instead of a miter will introduce an error no
-	 * larger than the tolerance. Consider the same join from
-	 * before but with the miter now chopped off and replaced with
-	 * a bevel join. The drawing is zoomed in a bit again, the
-	 * point marked as '*' is the center of the stroke---the point
-	 * where the two line segments of interest intersect:
-	 *
-	 *    ----- .
-	 *    ^     ..
-	 *    |     . .
-	 *    |     .  .
-	 *   1/2    .   .
-	 *  miter   .    .         |
-	 *  length  .     .        |
-	 *    |     .______.    ___v___
-	 *    |     |     . \   1/2 bevel
-	 *    v     |  .     \   width
-	 *    ----  *         \ -------
-	 *	    |          \   ^
-	 *
-	 *
-	 * The length of interest here is the vertical length of the
-	 * miter that is eliminated. It's length can be obtained by
-	 * starting with 1/2 the miter length and the subtracting off
-	 * the vertical length that is included by the bevel join,
-	 * (here termed 1/2 bevel width). To determine this new bevel
-	 * width, we have a small right triangle shown, the hypotenuse
-	 * of which has a length of 1/2 the line width, and the small
-	 * angle at the upper right of the figure is psi/2.
-	 *
-	 * So we have:
-	 *
-	 *	sin (psi/2) = (bevel_width / 2) / (line_width / 2)
-	 *
-	 * And we can determine when the miter is required by
-	 * calculating when the eliminated portion of the miter is
-	 * greater than the tolerance:
-	 *
-	 *	(miter_length / 2) - (bevel_width / 2) > tolerance
-	 *
-	 * Substituting in the above expressions for miter_length and
-	 * bevel_width:
-	 *
-	 *	(line_width/2) / sin (psi/2) - (line_width/2) * sin (psi/2) > tolerance
-	 *	1 / sin(psi/2) - sin (psi/2) > 2 * tolerance / line_width
-	 *	1 / sin²(psi/2) -2 +  sin²(psi/2) > 4 * (tolerance/line_width)²
-	 *
-	 * Use identity: sin²(psi/2) = (1-cos(psi))/2
-
-	 *	2/(1 - cos(psi)) - 2 + (1-cos(psi))/2 > 4 * (tolerance/line_width)²
-	 *	4/(1 - cos(psi)) - 4 + (1-cos(psi)) > 8 * (tolerance/line_width)²
-	 *	4/(1 - cos(psi)) + (1-cos(psi)) > 8 * ((tolerance/line_width)² + 0.5)
 	 */
-	if ((2 <= ml * ml * (1 - in_dot_out)) &&
-	    ((8 * (tolerance_squared / line_width_squared + 0.5)) <
-	     4 / (1 - in_dot_out) + (1 - in_dot_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
@@ -447,17 +393,46 @@ _cairo_stroker_join (cairo_stroker_t *stroker, cairo_stroke_face_t *in, cairo_st
 		mx = (my - y2) * dx2 / dy2 + x2;
 
 	    /*
-	     * Draw the quadrilateral
+	     * 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.
 	     */
-	    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;
+	    ix = _cairo_fixed_to_double (in->point.x);
+	    iy = _cairo_fixed_to_double (in->point.y);
 
-	    return _cairo_traps_tessellate_convex_quad (stroker->traps, quad);
+	    /* slope of one face */
+	    fdx1 = x1 - ix; fdy1 = y1 - iy;
+
+	    /* slope of the other face */
+	    fdx2 = x2 - ix; fdy2 = y2 - iy;
+
+	    /* slope from the intersection to the miter point */
+	    mdx = mx - ix; mdy = my - iy;
+
+	    /*
+	     * Make sure the miter point line lies between the two
+	     * faces by comparing the slopes
+	     */
+	    if (_cairo_slope_compare_sgn (fdx1, fdy1, mdx, mdy) !=
+		_cairo_slope_compare_sgn (fdx2, fdy2, mdx, mdy))
+	    {
+		/*
+		 * Draw the quadrilateral
+		 */
+		outer.x = _cairo_fixed_from_double (mx);
+		outer.y = _cairo_fixed_from_double (my);
+
+		quad[0] = in->point;
+		quad[1] = *inpt;
+		quad[2] = outer;
+		quad[3] = *outpt;
+
+		return _cairo_traps_tessellate_convex_quad (stroker->traps, quad);
+	    }
 	}
 	/* fall through ... */
     }


More information about the cairo-commit mailing list