diff --git a/src/cairo-xcb-connection-shm.c b/src/cairo-xcb-connection-shm.c index 7720bbb..d4f04c5 100644 --- a/src/cairo-xcb-connection-shm.c +++ b/src/cairo-xcb-connection-shm.c @@ -104,6 +104,23 @@ _cairo_xcb_connection_shm_get_image (cairo_xcb_connection_t *connection, } void +_cairo_xcb_connection_shm_create_pixmap (cairo_xcb_connection_t *connection, + xcb_pixmap_t pid, + xcb_drawable_t drawable, + uint16_t width, + uint16_t height, + uint8_t depth, + uint32_t shmseg, + uint32_t offset) +{ + assert (connection->flags & CAIRO_XCB_SHM_HAS_PIXMAPS); + xcb_shm_create_pixmap (connection->xcb_connection, + pid, drawable, + width, height, depth, + shmseg, offset); +} + +void _cairo_xcb_connection_shm_detach (cairo_xcb_connection_t *connection, uint32_t segment) { diff --git a/src/cairo-xcb-connection.c b/src/cairo-xcb-connection.c index b48add1..437719b 100644 --- a/src/cairo-xcb-connection.c +++ b/src/cairo-xcb-connection.c @@ -473,10 +473,14 @@ _cairo_xcb_connection_query_shm (cairo_xcb_connection_t *connection) if (version == NULL) return; - free (version); - - if (can_use_shm (connection)) + if (can_use_shm (connection)) { connection->flags |= CAIRO_XCB_HAS_SHM; + + if (version->shared_pixmaps) + connection->flags |= CAIRO_XCB_SHM_HAS_PIXMAPS; + } + + free (version); } #endif @@ -834,7 +838,7 @@ cairo_xcb_device_debug_cap_xshm_version (cairo_device_t *device, /* clear any flags that are inappropriate for the desired version */ if (major_version < 0 && minor_version < 0) { - connection->flags &= ~(CAIRO_XCB_HAS_SHM); + connection->flags &= ~(CAIRO_XCB_HAS_SHM | CAIRO_XCB_SHM_HAS_PIXMAPS); } } diff --git a/src/cairo-xcb-private.h b/src/cairo-xcb-private.h index 5f04803..8b361b5 100644 --- a/src/cairo-xcb-private.h +++ b/src/cairo-xcb-private.h @@ -249,6 +249,7 @@ enum { CAIRO_XCB_RENDER_HAS_GRADIENTS = 0x0200, CAIRO_XCB_HAS_SHM = 0x80000000, + CAIRO_XCB_SHM_HAS_PIXMAPS = 0x80000001, CAIRO_XCB_RENDER_MASK = CAIRO_XCB_HAS_RENDER | CAIRO_XCB_RENDER_HAS_FILL_RECTANGLES | @@ -260,7 +261,7 @@ enum { CAIRO_XCB_RENDER_HAS_PDF_OPERATORS | CAIRO_XCB_RENDER_HAS_EXTENDED_REPEAT | CAIRO_XCB_RENDER_HAS_GRADIENTS, - CAIRO_XCB_SHM_MASK = CAIRO_XCB_HAS_SHM + CAIRO_XCB_SHM_MASK = CAIRO_XCB_HAS_SHM | CAIRO_XCB_SHM_HAS_PIXMAPS }; #define CAIRO_XCB_SHM_SMALL_IMAGE 8192 @@ -560,6 +561,16 @@ _cairo_xcb_connection_shm_get_image (cairo_xcb_connection_t *connection, uint32_t offset); cairo_private void +_cairo_xcb_connection_shm_create_pixmap (cairo_xcb_connection_t *connection, + xcb_pixmap_t pid, + xcb_drawable_t drawable, + uint16_t width, + uint16_t height, + uint8_t depth, + uint32_t shmseg, + uint32_t offset); + +cairo_private void _cairo_xcb_connection_shm_detach (cairo_xcb_connection_t *connection, uint32_t segment); #else diff --git a/src/cairo-xcb-surface-render.c b/src/cairo-xcb-surface-render.c index 99efe87..a9bef10 100644 --- a/src/cairo-xcb-surface-render.c +++ b/src/cairo-xcb-surface-render.c @@ -2728,6 +2728,7 @@ static cairo_bool_t image_contains_box (cairo_box_t *box, void *closure) struct _image_upload_box { cairo_xcb_surface_t *surface; cairo_image_surface_t *image; + cairo_xcb_shm_info_t *shm_info; xcb_gcontext_t gc; int tx, ty; }; @@ -2742,6 +2743,7 @@ static cairo_bool_t image_upload_box (cairo_box_t *box, void *closure) int height = _cairo_fixed_integer_part (box->p2.y - box->p1.y); int bpp = PIXMAN_FORMAT_BPP (iub->image->pixman_format); int len = CAIRO_STRIDE_FOR_WIDTH_BPP (width, bpp); + /* XXX: Use iub->shm_info, if set */ if (len == iub->image->stride) { _cairo_xcb_connection_put_image (iub->surface->connection, iub->surface->drawable, @@ -2771,6 +2773,106 @@ static cairo_bool_t image_upload_box (cairo_box_t *box, void *closure) } static cairo_status_t +_upload_image_inplace_shm (cairo_xcb_surface_t *surface, + cairo_image_surface_t *image, + cairo_xcb_shm_info_t *shm_info, + const cairo_matrix_t *matrix, + cairo_boxes_t *boxes) +{ +#if CAIRO_HAS_XCB_SHM_FUNCTIONS + struct _image_contains_box icb; + struct _cairo_boxes_chunk *chunk; + xcb_pixmap_t pixmap; + xcb_render_picture_t picture; + xcb_render_pictformat_t xrender_format; + cairo_status_t status; + int tx, ty; + int i; + + if (! boxes->is_pixel_aligned) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (image->format == CAIRO_FORMAT_INVALID || image->format > CAIRO_FORMAT_A1) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (shm_info == NULL || shm_info->connection != surface->connection) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if ((surface->connection->flags & CAIRO_XCB_SHM_HAS_PIXMAPS) == 0) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if ((surface->connection->flags & CAIRO_XCB_RENDER_HAS_COMPOSITE) == 0) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (! _cairo_matrix_is_integer_translation (matrix, &tx, &ty)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + /* Check that the data is entirely within the image */ + icb.width = image->width; + icb.height = image->height; + icb.tx = tx; + icb.ty = ty; + if (! cairo_boxes_for_each_box (boxes, image_contains_box, &icb)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (surface->deferred_clear) { + status = _cairo_xcb_surface_clear (surface); + if (unlikely (status)) + return status; + } + + status = _cairo_xcb_connection_acquire (surface->connection); + if (unlikely (status)) + return status; + + pixmap = _cairo_xcb_connection_get_xid (surface->connection); + _cairo_xcb_connection_shm_create_pixmap (shm_info->connection, pixmap, surface->drawable, + image->width, image->height, image->depth, + shm_info->shm, shm_info->offset); + picture = _cairo_xcb_connection_get_xid (surface->connection); + xrender_format = shm_info->connection->standard_formats[image->format]; + _cairo_xcb_connection_render_create_picture (surface->connection, + picture, + pixmap, + xrender_format, + 0, NULL); + _cairo_xcb_connection_free_pixmap (surface->connection, + pixmap); + + _cairo_xcb_surface_ensure_picture (surface); + for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) { + cairo_box_t *box = &chunk->base[0]; + for (i = 0; i < chunk->count; i++) { + int x = _cairo_fixed_integer_round_down (box[i].p1.x); + int y = _cairo_fixed_integer_round_down (box[i].p1.y); + int width = _cairo_fixed_integer_round_down (box[i].p2.x) - x; + int height = _cairo_fixed_integer_round_down (box[i].p2.y) - y; + + _cairo_xcb_connection_render_composite (surface->connection, + XCB_RENDER_PICT_OP_SRC, + picture, + XCB_NONE, + surface->picture, + x + tx, y + ty, + 0, 0, + x, y, + width, height); + } + } + + _cairo_xcb_connection_render_free_picture (surface->connection, picture); + _cairo_xcb_connection_release (surface->connection); + + /* XXX: Get rid of this and instead do this in the shm's surface flush callback */ + free(xcb_get_input_focus_reply(surface->connection->xcb_connection, xcb_get_input_focus(surface->connection->xcb_connection), NULL)); + + return CAIRO_STATUS_SUCCESS; +#else + return CAIRO_INT_STATUS_UNSUPPORTED; +#endif +} + +static cairo_status_t _upload_image_inplace (cairo_xcb_surface_t *surface, const cairo_pattern_t *source, cairo_boxes_t *boxes) @@ -2779,7 +2881,8 @@ _upload_image_inplace (cairo_xcb_surface_t *surface, struct _image_contains_box icb; struct _image_upload_box iub; cairo_image_surface_t *image; - cairo_status_t status; + cairo_xcb_shm_info_t *shm_info; + cairo_int_status_t status; int tx, ty; if (! boxes->is_pixel_aligned) @@ -2808,6 +2911,15 @@ _upload_image_inplace (cairo_xcb_surface_t *surface, if (image->format == CAIRO_FORMAT_INVALID) return CAIRO_INT_STATUS_UNSUPPORTED; + shm_info = _cairo_user_data_array_get_data (&image->base.user_data, + (const cairo_user_data_key_t *) surface->connection); + if (shm_info && shm_info->connection != surface->connection) + shm_info = NULL; + + status = _upload_image_inplace_shm (surface, image, shm_info, &source->matrix, boxes); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; + if (image->depth != surface->depth) return CAIRO_INT_STATUS_UNSUPPORTED; @@ -2834,6 +2946,7 @@ _upload_image_inplace (cairo_xcb_surface_t *surface, iub.surface = surface; iub.image = image; + iub.shm_info = shm_info; iub.gc = _cairo_xcb_screen_get_gc (surface->screen, surface->drawable, image->depth);