[cairo-commit] 2 commits - src/cairoint.h src/cairo-pattern.c src/cairo-xcb-surface-render.c src/cairo-xlib-surface.c

Andrea Canciani ranma42 at kemper.freedesktop.org
Fri Mar 18 01:51:58 PDT 2011


 src/cairo-pattern.c            |   29 +++++++++++++++++++++++++++
 src/cairo-xcb-surface-render.c |   44 ++++++++++++++++++++++++++++++++---------
 src/cairo-xlib-surface.c       |   33 ++++++++++++++++++++++++------
 src/cairoint.h                 |    3 ++
 4 files changed, 93 insertions(+), 16 deletions(-)

New commits:
commit 6521bab6e8d8c44e8a790ec6e10ae160ecaf8bc9
Author: Andrea Canciani <ranma42 at gmail.com>
Date:   Sat Mar 5 10:59:22 2011 +0100

    xcb,xlib: Fallback upon generic radial gradients
    
    The RENDER specification requires radial gradients to have the first
    circle completely inside the second one, but the error is not actually
    generated.
    
    The implementation produces the expected results if either circle
    contains the other one, so only fall back in these cases.

diff --git a/src/cairo-pattern.c b/src/cairo-pattern.c
index 000b1ee..5663a9b 100644
--- a/src/cairo-pattern.c
+++ b/src/cairo-pattern.c
@@ -2631,6 +2631,35 @@ _extend_range (double range[2], double value, cairo_bool_t valid)
     return TRUE;
 }
 
+/**
+ * _cairo_radial_pattern_focus_is_inside
+ *
+ * Returns %TRUE if and only if the focus point exists and is
+ * contained in one of the two extreme circles. This condition is
+ * equivalent to one of the two extreme circles being completely
+ * contained in the other one.
+ *
+ * Note: if the focus is on the border of one of the two circles (in
+ * which case the circles are tangent in the focus point), it is not
+ * considered as contained in the circle, hence this function returns
+ * %FALSE.
+ *
+ **/
+cairo_bool_t
+_cairo_radial_pattern_focus_is_inside (const cairo_radial_pattern_t *radial)
+{
+    double cx, cy, cr, dx, dy, dr;
+
+    cx = radial->cd1.center.x;
+    cy = radial->cd1.center.y;
+    cr = radial->cd1.radius;
+    dx = radial->cd2.center.x - cx;
+    dy = radial->cd2.center.y - cy;
+    dr = radial->cd2.radius   - cr;
+
+    return dx*dx + dy*dy < dr*dr;
+}
+
 static void
 _cairo_radial_pattern_box_to_parameter (const cairo_radial_pattern_t *radial,
 					double x0, double y0,
diff --git a/src/cairo-xcb-surface-render.c b/src/cairo-xcb-surface-render.c
index c7fcaa4..9c377e3 100644
--- a/src/cairo-xcb-surface-render.c
+++ b/src/cairo-xcb-surface-render.c
@@ -379,6 +379,14 @@ _pattern_is_supported (uint32_t flags,
     } else { /* gradient */
 	if ((flags & CAIRO_XCB_RENDER_HAS_GRADIENTS) == 0)
 	    return FALSE;
+
+	/* The RENDER specification says that the inner circle has to be
+	 * completely contained inside the outer one. */
+	if (pattern->type == CAIRO_PATTERN_TYPE_RADIAL &&
+	    ! _cairo_radial_pattern_focus_is_inside ((cairo_radial_pattern_t *) pattern))
+	{
+	    return FALSE;
+	}
     }
 
     return pattern->type != CAIRO_PATTERN_TYPE_MESH;
diff --git a/src/cairo-xlib-surface.c b/src/cairo-xlib-surface.c
index a0ad14a..3f5d0ea 100644
--- a/src/cairo-xlib-surface.c
+++ b/src/cairo-xlib-surface.c
@@ -2105,6 +2105,12 @@ _cairo_xlib_surface_acquire_pattern_surface (cairo_xlib_display_t *display,
 	    if (dst->buggy_gradients)
 		break;
 
+	    /* The RENDER specification says that the inner circle has
+	     * to be completely contained inside the outer one. */
+	    if (pattern->type == CAIRO_PATTERN_TYPE_RADIAL &&
+		! _cairo_radial_pattern_focus_is_inside ((cairo_radial_pattern_t *) gradient))
+		break;
+
 	    assert (gradient->n_stops > 0);
 	    n_stops = MAX (gradient->n_stops, 2);
 
diff --git a/src/cairoint.h b/src/cairoint.h
index 1aaca2a..0bfea8f 100644
--- a/src/cairoint.h
+++ b/src/cairoint.h
@@ -2247,6 +2247,9 @@ _cairo_gradient_pattern_fit_to_range (const cairo_gradient_pattern_t *gradient,
 				      cairo_matrix_t                 *out_matrix,
 				      cairo_circle_double_t	      out_circle[2]);
 
+cairo_bool_t
+_cairo_radial_pattern_focus_is_inside (const cairo_radial_pattern_t *radial);
+
 cairo_private cairo_bool_t
 _cairo_pattern_is_opaque_solid (const cairo_pattern_t *pattern);
 
commit 426fe6fadffc50f1845b114fc3f41c27f96e8e79
Author: Andrea Canciani <ranma42 at gmail.com>
Date:   Sat Mar 5 10:59:09 2011 +0100

    xcb,xlib: Fix 1-stop gradients
    
    The RENDER specification does not specify the constraints on the
    gradient stops, but its implementation returns an error if less than 2
    stops are used.
    
    Xlib and XCB can work around this because gradients with just one stop
    are by-definition the same as gradients with that stop repeated twice.
    
    Fixes radial-gradient-one-stop.

diff --git a/src/cairo-xcb-surface-render.c b/src/cairo-xcb-surface-render.c
index 1d2d45e..c7fcaa4 100644
--- a/src/cairo-xcb-surface-render.c
+++ b/src/cairo-xcb-surface-render.c
@@ -735,26 +735,30 @@ _render_to_picture (cairo_xcb_surface_t *target,
 
 static xcb_render_fixed_t *
 _gradient_to_xcb (const cairo_gradient_pattern_t *gradient,
+		  unsigned int *n_stops,
 		  char *buf, unsigned int buflen)
 {
     xcb_render_fixed_t *stops;
     xcb_render_color_t *colors;
     unsigned int i;
 
-    if (gradient->n_stops * (sizeof (xcb_render_fixed_t) + sizeof (xcb_render_color_t)) < buflen)
+    assert (gradient->n_stops > 0);
+    *n_stops = MAX (gradient->n_stops, 2);
+
+    if (*n_stops * (sizeof (xcb_render_fixed_t) + sizeof (xcb_render_color_t)) < buflen)
     {
 	stops = (xcb_render_fixed_t *) buf;
     }
     else
     {
 	stops =
-	    _cairo_malloc_ab (gradient->n_stops,
+	    _cairo_malloc_ab (*n_stops,
 			      sizeof (xcb_render_fixed_t) + sizeof (xcb_render_color_t));
 	if (unlikely (stops == NULL))
 	    return NULL;
     }
 
-    colors = (xcb_render_color_t *) (stops + gradient->n_stops);
+    colors = (xcb_render_color_t *) (stops + *n_stops);
     for (i = 0; i < gradient->n_stops; i++) {
 	stops[i] =
 	    _cairo_fixed_16_16_from_double (gradient->stops[i].offset);
@@ -765,6 +769,18 @@ _gradient_to_xcb (const cairo_gradient_pattern_t *gradient,
 	colors[i].alpha = gradient->stops[i].color.alpha_short;
     }
 
+    /* RENDER does not support gradients with less than 2 stops. If a
+     * gradient has only a single stop, duplicate it to make RENDER
+     * happy. */
+    if (gradient->n_stops == 1) {
+	stops[1] = _cairo_fixed_16_16_from_double (gradient->stops[0].offset);
+
+	colors[1].red   = gradient->stops[0].color.red_short;
+	colors[1].green = gradient->stops[0].color.green_short;
+	colors[1].blue  = gradient->stops[0].color.blue_short;
+	colors[1].alpha = gradient->stops[0].color.alpha_short;
+    }
+
     return stops;
 }
 
@@ -781,6 +797,7 @@ _cairo_xcb_linear_picture (cairo_xcb_surface_t *target,
     cairo_circle_double_t extremes[2];
     cairo_xcb_picture_t *picture;
     cairo_status_t status;
+    unsigned int n_stops;
 
     _cairo_gradient_pattern_fit_to_range (&pattern->base, PIXMAN_MAX_INT >> 1, &matrix, extremes);
 
@@ -789,7 +806,7 @@ _cairo_xcb_linear_picture (cairo_xcb_surface_t *target,
     if (picture != NULL)
 	goto setup_picture;
 
-    stops = _gradient_to_xcb (&pattern->base, buf, sizeof (buf));
+    stops = _gradient_to_xcb (&pattern->base, &n_stops, buf, sizeof (buf));
     if (unlikely (stops == NULL))
 	return (cairo_xcb_picture_t *) _cairo_surface_create_in_error (CAIRO_STATUS_NO_MEMORY);
 
@@ -804,7 +821,7 @@ _cairo_xcb_linear_picture (cairo_xcb_surface_t *target,
     }
     picture->filter = CAIRO_FILTER_DEFAULT;
 
-    colors = (xcb_render_color_t *) (stops + pattern->base.n_stops);
+    colors = (xcb_render_color_t *) (stops + n_stops);
 
     p1.x = _cairo_fixed_16_16_from_double (extremes[0].center.x);
     p1.y = _cairo_fixed_16_16_from_double (extremes[0].center.y);
@@ -814,7 +831,7 @@ _cairo_xcb_linear_picture (cairo_xcb_surface_t *target,
     _cairo_xcb_connection_render_create_linear_gradient (target->connection,
 							 picture->picture,
 							 p1, p2,
-							 pattern->base.n_stops,
+							 n_stops,
 							 stops, colors);
 
     if (stops != (xcb_render_fixed_t *) buf)
@@ -855,6 +872,7 @@ _cairo_xcb_radial_picture (cairo_xcb_surface_t *target,
     cairo_circle_double_t extremes[2];
     cairo_xcb_picture_t *picture;
     cairo_status_t status;
+    unsigned int n_stops;
 
     _cairo_gradient_pattern_fit_to_range (&pattern->base, PIXMAN_MAX_INT >> 1, &matrix, extremes);
 
@@ -863,7 +881,7 @@ _cairo_xcb_radial_picture (cairo_xcb_surface_t *target,
     if (picture != NULL)
 	goto setup_picture;
 
-    stops = _gradient_to_xcb (&pattern->base, buf, sizeof (buf));
+    stops = _gradient_to_xcb (&pattern->base, &n_stops, buf, sizeof (buf));
     if (unlikely (stops == NULL))
 	return (cairo_xcb_picture_t *) _cairo_surface_create_in_error (CAIRO_STATUS_NO_MEMORY);
 
@@ -878,7 +896,7 @@ _cairo_xcb_radial_picture (cairo_xcb_surface_t *target,
     }
     picture->filter = CAIRO_FILTER_DEFAULT;
 
-    colors = (xcb_render_color_t *) (stops + pattern->base.n_stops);
+    colors = (xcb_render_color_t *) (stops + n_stops);
 
     p1.x = _cairo_fixed_16_16_from_double (extremes[0].center.x);
     p1.y = _cairo_fixed_16_16_from_double (extremes[0].center.y);
@@ -891,7 +909,7 @@ _cairo_xcb_radial_picture (cairo_xcb_surface_t *target,
     _cairo_xcb_connection_render_create_radial_gradient (target->connection,
 							 picture->picture,
 							 p1, p2, r1, r2,
-							 pattern->base.n_stops,
+							 n_stops,
 							 stops, colors);
 
     if (stops != (xcb_render_fixed_t *) buf)
diff --git a/src/cairo-xlib-surface.c b/src/cairo-xlib-surface.c
index 8b59804..a0ad14a 100644
--- a/src/cairo-xlib-surface.c
+++ b/src/cairo-xlib-surface.c
@@ -2100,28 +2100,28 @@ _cairo_xlib_surface_acquire_pattern_surface (cairo_xlib_display_t *display,
 	    XRenderColor *colors;
 	    XRenderPictFormat *format;
 	    Picture picture;
-	    unsigned int i;
+	    unsigned int i, n_stops;
 
 	    if (dst->buggy_gradients)
 		break;
 
-	    if (gradient->n_stops < 2) /* becomes a solid */
-		break;
+	    assert (gradient->n_stops > 0);
+	    n_stops = MAX (gradient->n_stops, 2);
 
-	    if (gradient->n_stops < sizeof (buf) / (sizeof (XFixed) + sizeof (XRenderColor)))
+	    if (n_stops < sizeof (buf) / (sizeof (XFixed) + sizeof (XRenderColor)))
 	    {
 		stops = (XFixed *) buf;
 	    }
 	    else
 	    {
 		stops =
-		    _cairo_malloc_ab (gradient->n_stops,
+		    _cairo_malloc_ab (n_stops,
 				      sizeof (XFixed) + sizeof (XRenderColor));
 		if (unlikely (stops == NULL))
 		    return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 	    }
 
-	    colors = (XRenderColor *) (stops + gradient->n_stops);
+	    colors = (XRenderColor *) (stops + n_stops);
 	    for (i = 0; i < gradient->n_stops; i++) {
 		stops[i] =
 		    _cairo_fixed_16_16_from_double (gradient->stops[i].offset);
@@ -2132,6 +2132,19 @@ _cairo_xlib_surface_acquire_pattern_surface (cairo_xlib_display_t *display,
 		colors[i].alpha = gradient->stops[i].color.alpha_short;
 	    }
 
+	    /* RENDER does not support gradients with less than 2
+	     * stops. If a gradient has only a single stop, duplicate
+	     * it to make RENDER happy. */
+	    if (gradient->n_stops == 1) {
+		stops[1] =
+		    _cairo_fixed_16_16_from_double (gradient->stops[0].offset);
+
+		colors[1].red   = gradient->stops[0].color.red_short;
+		colors[1].green = gradient->stops[0].color.green_short;
+		colors[1].blue  = gradient->stops[0].color.blue_short;
+		colors[1].alpha = gradient->stops[0].color.alpha_short;
+	    }
+
 #if 0
 	    /* For some weird reason the X server is sometimes getting
 	     * CreateGradient requests with bad length. So far I've only seen
@@ -2157,7 +2170,7 @@ _cairo_xlib_surface_acquire_pattern_surface (cairo_xlib_display_t *display,
 
 		picture = XRenderCreateLinearGradient (display->display, &grad,
 						       stops, colors,
-						       gradient->n_stops);
+						       n_stops);
 	    } else {
 		XRadialGradient grad;
 
@@ -2170,7 +2183,7 @@ _cairo_xlib_surface_acquire_pattern_surface (cairo_xlib_display_t *display,
 
 		picture = XRenderCreateRadialGradient (display->display, &grad,
 						       stops, colors,
-						       gradient->n_stops);
+						       n_stops);
 	    }
 
 	    if (stops != (XFixed *) buf)


More information about the cairo-commit mailing list