[cairo-commit] src/cairo-xlib-display.c src/cairo-xlib-private.h src/cairo-xlib-surface.c

Chris Wilson ickle at kemper.freedesktop.org
Sat Jun 25 12:15:52 PDT 2011


 src/cairo-xlib-display.c |  134 ++++++++++++++++++++++++++++++++++++++++-------
 src/cairo-xlib-private.h |    4 +
 src/cairo-xlib-surface.c |  111 +++++++++++++++++++++++++++++++-------
 3 files changed, 211 insertions(+), 38 deletions(-)

New commits:
commit 8996287d94b159c922282e0b6434b0637f8f32aa
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Sat Jun 25 20:02:47 2011 +0100

    xlib: Create an exact match for the image surface if possible using Xrender
    
    In order to defer the pixel conversion till as late in the pipeline as
    possible, we want to try and preserve the pixman image format whilst
    uploading the pixel data. To do this, we want to create an XRender
    surface with a matching PictFormat to the source image. Then we need to
    make sure we take the quick path through _draw_image_surface for none
    and direct conversions.
    
    Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>

diff --git a/src/cairo-xlib-display.c b/src/cairo-xlib-display.c
index 1372d9e..cce69ff 100644
--- a/src/cairo-xlib-display.c
+++ b/src/cairo-xlib-display.c
@@ -547,6 +547,116 @@ _cairo_xlib_display_acquire (cairo_device_t *device, cairo_xlib_display_t **disp
 }
 
 XRenderPictFormat *
+_cairo_xlib_display_get_xrender_format_for_pixman(cairo_xlib_display_t *display,
+						  pixman_format_code_t format)
+{
+    Display *dpy = display->display;
+    XRenderPictFormat tmpl;
+    int mask;
+
+#define MASK(x) ((1<<(x))-1)
+
+    tmpl.depth = PIXMAN_FORMAT_DEPTH(format);
+    mask = PictFormatType | PictFormatDepth;
+
+    switch (PIXMAN_FORMAT_TYPE(format)) {
+    case PIXMAN_TYPE_ARGB:
+	tmpl.type = PictTypeDirect;
+
+	tmpl.direct.alphaMask = MASK(PIXMAN_FORMAT_A(format));
+	if (PIXMAN_FORMAT_A(format))
+	    tmpl.direct.alpha = (PIXMAN_FORMAT_R(format) +
+				 PIXMAN_FORMAT_G(format) +
+				 PIXMAN_FORMAT_B(format));
+
+	tmpl.direct.redMask = MASK(PIXMAN_FORMAT_R(format));
+	tmpl.direct.red = (PIXMAN_FORMAT_G(format) +
+			   PIXMAN_FORMAT_B(format));
+
+	tmpl.direct.greenMask = MASK(PIXMAN_FORMAT_G(format));
+	tmpl.direct.green = PIXMAN_FORMAT_B(format);
+
+	tmpl.direct.blueMask = MASK(PIXMAN_FORMAT_B(format));
+	tmpl.direct.blue = 0;
+
+	mask |= PictFormatRed | PictFormatRedMask;
+	mask |= PictFormatGreen | PictFormatGreenMask;
+	mask |= PictFormatBlue | PictFormatBlueMask;
+	mask |= PictFormatAlpha | PictFormatAlphaMask;
+	break;
+
+    case PIXMAN_TYPE_ABGR:
+	tmpl.type = PictTypeDirect;
+
+	tmpl.direct.alphaMask = MASK(PIXMAN_FORMAT_A(format));
+	if (tmpl.direct.alphaMask)
+	    tmpl.direct.alpha = (PIXMAN_FORMAT_B(format) +
+				 PIXMAN_FORMAT_G(format) +
+				 PIXMAN_FORMAT_R(format));
+
+	tmpl.direct.blueMask = MASK(PIXMAN_FORMAT_B(format));
+	tmpl.direct.blue = (PIXMAN_FORMAT_G(format) +
+			    PIXMAN_FORMAT_R(format));
+
+	tmpl.direct.greenMask = MASK(PIXMAN_FORMAT_G(format));
+	tmpl.direct.green = PIXMAN_FORMAT_R(format);
+
+	tmpl.direct.redMask = MASK(PIXMAN_FORMAT_R(format));
+	tmpl.direct.red = 0;
+
+	mask |= PictFormatRed | PictFormatRedMask;
+	mask |= PictFormatGreen | PictFormatGreenMask;
+	mask |= PictFormatBlue | PictFormatBlueMask;
+	mask |= PictFormatAlpha | PictFormatAlphaMask;
+	break;
+
+    case PIXMAN_TYPE_BGRA:
+	tmpl.type = PictTypeDirect;
+
+	tmpl.direct.blueMask = MASK(PIXMAN_FORMAT_B(format));
+	tmpl.direct.blue = (PIXMAN_FORMAT_BPP(format) - PIXMAN_FORMAT_B(format));
+
+	tmpl.direct.greenMask = MASK(PIXMAN_FORMAT_G(format));
+	tmpl.direct.green = (PIXMAN_FORMAT_BPP(format) - PIXMAN_FORMAT_B(format) -
+			     PIXMAN_FORMAT_G(format));
+
+	tmpl.direct.redMask = MASK(PIXMAN_FORMAT_R(format));
+	tmpl.direct.red = (PIXMAN_FORMAT_BPP(format) - PIXMAN_FORMAT_B(format) -
+			   PIXMAN_FORMAT_G(format) - PIXMAN_FORMAT_R(format));
+
+	tmpl.direct.alphaMask = MASK(PIXMAN_FORMAT_A(format));
+	tmpl.direct.alpha = 0;
+
+	mask |= PictFormatRed | PictFormatRedMask;
+	mask |= PictFormatGreen | PictFormatGreenMask;
+	mask |= PictFormatBlue | PictFormatBlueMask;
+	mask |= PictFormatAlpha | PictFormatAlphaMask;
+	break;
+
+    case PIXMAN_TYPE_A:
+	tmpl.type = PictTypeDirect;
+
+	tmpl.direct.alpha = 0;
+	tmpl.direct.alphaMask = MASK(PIXMAN_FORMAT_A(format));
+
+	mask |= PictFormatAlpha | PictFormatAlphaMask;
+	break;
+
+    case PIXMAN_TYPE_COLOR:
+    case PIXMAN_TYPE_GRAY:
+	/* XXX Find matching visual/colormap */
+	tmpl.type = PictTypeIndexed;
+	//tmpl.colormap = screen->visuals[PIXMAN_FORMAT_VIS(format)].vid;
+	//mask |= PictFormatColormap;
+	return NULL;
+    }
+#undef MASK
+
+    /* XXX caching? */
+    return XRenderFindFormat(dpy, mask, &tmpl, 1);
+}
+
+XRenderPictFormat *
 _cairo_xlib_display_get_xrender_format (cairo_xlib_display_t	*display,
 	                                cairo_format_t		 format)
 {
@@ -569,26 +679,14 @@ _cairo_xlib_display_get_xrender_format (cairo_xlib_display_t	*display,
 	    pict_format = PictStandardA8; break;
 	case CAIRO_FORMAT_RGB24:
 	    pict_format = PictStandardRGB24; break;
-	case CAIRO_FORMAT_RGB16_565: {
-	    Visual *visual = NULL;
-	    Screen *screen = DefaultScreenOfDisplay(display->display);
-	    int j;
-	    for (j = 0; j < screen->ndepths; j++) {
-	        Depth *d = &screen->depths[j];
-	        if (d->depth == 16 && d->nvisuals && &d->visuals[0]) {
-	            if (d->visuals[0].red_mask   == 0xf800 &&
-	                d->visuals[0].green_mask == 0x7e0 &&
-	                d->visuals[0].blue_mask  == 0x1f)
-	                visual = &d->visuals[0];
-	            break;
-	        }
-	    }
-	    if (!visual)
-	        return NULL;
-	    xrender_format = XRenderFindVisualFormat(display->display, visual);
+	case CAIRO_FORMAT_RGB16_565:
+	    xrender_format = _cairo_xlib_display_get_xrender_format_for_pixman(display,
+									       PIXMAN_r5g6b5);
 	    break;
-	}
 	case CAIRO_FORMAT_RGB30:
+	    xrender_format = _cairo_xlib_display_get_xrender_format_for_pixman(display,
+									       PIXMAN_x2r10g10b10);
+	    break;
 	case CAIRO_FORMAT_INVALID:
 	default:
 	    ASSERT_NOT_REACHED;
diff --git a/src/cairo-xlib-private.h b/src/cairo-xlib-private.h
index a44a932..47d535b 100644
--- a/src/cairo-xlib-private.h
+++ b/src/cairo-xlib-private.h
@@ -161,6 +161,10 @@ cairo_private XRenderPictFormat *
 _cairo_xlib_display_get_xrender_format (cairo_xlib_display_t	*display,
 	                                cairo_format_t		 format);
 
+cairo_private XRenderPictFormat *
+_cairo_xlib_display_get_xrender_format_for_pixman (cairo_xlib_display_t *display,
+						   pixman_format_code_t format);
+
 cairo_private cairo_status_t
 _cairo_xlib_screen_get (Display *dpy,
 			Screen *screen,
diff --git a/src/cairo-xlib-surface.c b/src/cairo-xlib-surface.c
index 1747be2..8585208 100644
--- a/src/cairo-xlib-surface.c
+++ b/src/cairo-xlib-surface.c
@@ -1157,6 +1157,7 @@ _draw_image_surface (cairo_xlib_surface_t   *surface,
     pixman_image_t *pixman_image = NULL;
     cairo_status_t status;
     cairo_bool_t own_data;
+    cairo_bool_t is_rgb_image;
     GC gc;
 
     ximage.width = image->width;
@@ -1176,7 +1177,26 @@ _draw_image_surface (cairo_xlib_surface_t   *surface,
     if (unlikely (status))
         return status;
 
-    if (!_pixman_format_to_masks (image->pixman_format, &image_masks))
+    is_rgb_image = _pixman_format_to_masks (image->pixman_format, &image_masks);
+
+    if (is_rgb_image &&
+	(image_masks.alpha_mask == surface->a_mask || surface->a_mask == 0) &&
+	(image_masks.red_mask   == surface->r_mask || surface->r_mask == 0) &&
+	(image_masks.green_mask == surface->g_mask || surface->g_mask == 0) &&
+	(image_masks.blue_mask  == surface->b_mask || surface->b_mask == 0))
+    {
+	int ret;
+
+	ximage.bits_per_pixel = image_masks.bpp;
+	ximage.bytes_per_line = image->stride;
+	ximage.data = (char *)image->data;
+	own_data = FALSE;
+
+	ret = XInitImage (&ximage);
+	assert (ret != 0);
+    }
+    else if (!is_rgb_image ||
+	     (surface->visual == NULL || surface->visual->class == TrueColor))
     {
         pixman_format_code_t intermediate_format;
         int ret;
@@ -1217,21 +1237,6 @@ _draw_image_surface (cairo_xlib_surface_t   *surface,
 	ret = XInitImage (&ximage);
 	assert (ret != 0);
     }
-    else if ((image_masks.alpha_mask == surface->a_mask || surface->a_mask == 0) &&
-             (image_masks.red_mask   == surface->r_mask || surface->r_mask == 0) &&
-             (image_masks.green_mask == surface->g_mask || surface->g_mask == 0) &&
-             (image_masks.blue_mask  == surface->b_mask || surface->b_mask == 0))
-    {
-	int ret;
-
-	ximage.bits_per_pixel = image_masks.bpp;
-	ximage.bytes_per_line = image->stride;
-	ximage.data = (char *)image->data;
-	own_data = FALSE;
-
-	ret = XInitImage (&ximage);
-	assert (ret != 0);
-    }
     else
     {
 	unsigned int stride, rowstride;
@@ -1467,6 +1472,73 @@ _cairo_xlib_surface_same_screen (cairo_xlib_surface_t *dst,
     return dst->screen == src->screen;
 }
 
+static cairo_xlib_surface_t *
+_cairo_xlib_surface_create_similar_for_image(cairo_xlib_surface_t *other,
+					     cairo_image_surface_t *image,
+					     int width, int height)
+{
+    XRenderPictFormat *xrender_format;
+    cairo_xlib_display_t *display;
+    cairo_xlib_surface_t *surface;
+    cairo_status_t status;
+    Visual *visual;
+    Pixmap pix;
+
+    /* Try to create a surface using an xrender format that exactly matches
+     * the image data so that we can upload the pixels and perform any
+     * conversion on the GPU.
+     */
+
+    if (!CAIRO_SURFACE_RENDER_HAS_CREATE_PICTURE (other)) {
+fallback:
+	return (cairo_xlib_surface_t *)
+	    _cairo_xlib_surface_create_similar (other,
+						image->base.content,
+						width, height);
+    }
+
+    status = _cairo_xlib_display_acquire (other->base.device, &display);
+    if (unlikely (status))
+	return (cairo_xlib_surface_t *) _cairo_surface_create_in_error(status);
+
+    if (image->format != CAIRO_FORMAT_INVALID)
+	    xrender_format =
+		_cairo_xlib_display_get_xrender_format (display, image->format);
+    else
+	xrender_format =
+	    _cairo_xlib_display_get_xrender_format_for_pixman (display,
+							       image->pixman_format);
+    if (xrender_format == NULL) {
+	cairo_device_release (&display->base);
+	goto fallback;
+    }
+
+    pix = XCreatePixmap (display->display, other->drawable,
+			 width, height, xrender_format->depth);
+
+    if (xrender_format == other->xrender_format)
+	visual = other->visual;
+    else
+	visual = _visual_for_xrender_format(other->screen->screen,
+					    xrender_format);
+
+    surface = (cairo_xlib_surface_t *)
+	_cairo_xlib_surface_create_internal (other->screen, pix, visual,
+					     xrender_format,
+					     width, height,
+					     xrender_format->depth);
+
+    if (unlikely (surface->base.status)) {
+	XFreePixmap (display->display, pix);
+	cairo_device_release (&display->base);
+	return surface;
+    }
+
+    surface->owns_pixmap = TRUE;
+    cairo_device_release (&display->base);
+    return surface;
+}
+
 static cairo_status_t
 _cairo_xlib_surface_clone_similar (void			*abstract_surface,
 				   cairo_surface_t	*src,
@@ -1498,10 +1570,9 @@ _cairo_xlib_surface_clone_similar (void			*abstract_surface,
 	if (width > XLIB_COORD_MAX || height > XLIB_COORD_MAX)
 	    return UNSUPPORTED ("roi too large for xlib");
 
-	clone = (cairo_xlib_surface_t *)
-	    _cairo_xlib_surface_create_similar (surface,
-						image_src->base.content,
-						width, height);
+	clone = _cairo_xlib_surface_create_similar_for_image(surface,
+							     image_src,
+							     width, height);
 	if (clone == NULL)
 	    return UNSUPPORTED ("unhandled image format, no similar surface");
 


More information about the cairo-commit mailing list