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.
This commit is contained in:
Andrea Canciani 2011-03-05 10:59:09 +01:00
parent c42d7f7acf
commit 426fe6fadf
2 changed files with 48 additions and 17 deletions

View file

@ -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)

View file

@ -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)