/* cairo - a vector graphics library with display and print output * * Copyright © 2002 University of Southern California * Copyright © 2009 Intel Corporation * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is University of Southern * California. * * Contributor(s): * Behdad Esfahbod * Carl D. Worth * Chris Wilson * Karl Tomlinson , Mozilla Corporation */ #include "cairoint.h" #include "cairo-xcb.h" #include "cairo-xcb-private.h" #include "cairo-composite-rectangles-private.h" #include "cairo-default-context-private.h" #include "cairo-image-surface-inline.h" #include "cairo-list-inline.h" #include "cairo-surface-backend-private.h" #include "cairo-compositor-private.h" #include "cairo-surface-shadow-private.h" #if CAIRO_HAS_XLIB_XCB_FUNCTIONS slim_hidden_proto (cairo_xcb_surface_create); slim_hidden_proto (cairo_xcb_surface_create_for_bitmap); slim_hidden_proto (cairo_xcb_surface_create_with_xrender_format); #endif /** * SECTION:cairo-xcb * @Title: XCB Surfaces * @Short_Description: X Window System rendering using the XCB library * @See_Also: #cairo_surface_t * * The XCB surface is used to render cairo graphics to X Window System * windows and pixmaps using the XCB library. * * Note that the XCB surface automatically takes advantage of the X render * extension if it is available. **/ /** * CAIRO_HAS_XCB_SURFACE: * * Defined if the xcb surface backend is available. * This macro can be used to conditionally compile backend-specific code. * * Since: 1.12 **/ cairo_surface_t * _cairo_xcb_surface_create_similar (void *abstract_other, cairo_content_t content, int width, int height) { cairo_xcb_surface_t *other = abstract_other; cairo_xcb_surface_t *surface; cairo_xcb_connection_t *connection; xcb_pixmap_t pixmap; cairo_status_t status; if (unlikely(width > XLIB_COORD_MAX || height > XLIB_COORD_MAX || width <= 0 || height <= 0)) return cairo_image_surface_create (_cairo_format_from_content (content), width, height); if ((other->connection->flags & CAIRO_XCB_HAS_RENDER) == 0) return _cairo_xcb_surface_create_similar_image (other, _cairo_format_from_content (content), width, height); connection = other->connection; status = _cairo_xcb_connection_acquire (connection); if (unlikely (status)) return _cairo_surface_create_in_error (status); if (content == other->base.content) { pixmap = _cairo_xcb_connection_create_pixmap (connection, other->depth, other->drawable, width, height); surface = (cairo_xcb_surface_t *) _cairo_xcb_surface_create_internal (other->screen, pixmap, TRUE, other->pixman_format, other->xrender_format, width, height); } else { cairo_format_t format; pixman_format_code_t pixman_format; /* XXX find a compatible xrender format */ switch (content) { case CAIRO_CONTENT_ALPHA: pixman_format = PIXMAN_a8; format = CAIRO_FORMAT_A8; break; case CAIRO_CONTENT_COLOR: pixman_format = PIXMAN_x8r8g8b8; format = CAIRO_FORMAT_RGB24; break; default: ASSERT_NOT_REACHED; case CAIRO_CONTENT_COLOR_ALPHA: pixman_format = PIXMAN_a8r8g8b8; format = CAIRO_FORMAT_ARGB32; break; } pixmap = _cairo_xcb_connection_create_pixmap (connection, PIXMAN_FORMAT_DEPTH (pixman_format), other->drawable, width, height); surface = (cairo_xcb_surface_t *) _cairo_xcb_surface_create_internal (other->screen, pixmap, TRUE, pixman_format, connection->standard_formats[format], width, height); } if (unlikely (surface->base.status)) _cairo_xcb_connection_free_pixmap (connection, pixmap); _cairo_xcb_connection_release (connection); return &surface->base; } cairo_surface_t * _cairo_xcb_surface_create_similar_image (void *abstract_other, cairo_format_t format, int width, int height) { cairo_xcb_surface_t *other = abstract_other; cairo_xcb_connection_t *connection = other->connection; cairo_xcb_shm_info_t *shm_info; cairo_image_surface_t *image; cairo_status_t status; pixman_format_code_t pixman_format; if (unlikely(width > XLIB_COORD_MAX || height > XLIB_COORD_MAX || width <= 0 || height <= 0)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE)); pixman_format = _cairo_format_to_pixman_format_code (format); status = _cairo_xcb_shm_image_create (connection, pixman_format, width, height, &image, &shm_info); if (unlikely (status)) return _cairo_surface_create_in_error (status); if (! image->base.is_clear) { memset (image->data, 0, image->stride * image->height); image->base.is_clear = TRUE; } return &image->base; } static cairo_status_t _cairo_xcb_surface_finish (void *abstract_surface) { cairo_xcb_surface_t *surface = abstract_surface; cairo_status_t status; if (surface->fallback != NULL) { cairo_surface_finish (&surface->fallback->base); cairo_surface_destroy (&surface->fallback->base); } _cairo_boxes_fini (&surface->fallback_damage); cairo_list_del (&surface->link); status = _cairo_xcb_connection_acquire (surface->connection); if (status == CAIRO_STATUS_SUCCESS) { if (surface->picture != XCB_NONE) { _cairo_xcb_connection_render_free_picture (surface->connection, surface->picture); } if (surface->owns_pixmap) _cairo_xcb_connection_free_pixmap (surface->connection, surface->drawable); _cairo_xcb_connection_release (surface->connection); } _cairo_xcb_connection_destroy (surface->connection); return status; } static void _destroy_image (pixman_image_t *image, void *data) { free (data); } #if CAIRO_HAS_XCB_SHM_FUNCTIONS static cairo_surface_t * _cairo_xcb_surface_create_shm_image (cairo_xcb_connection_t *connection, pixman_format_code_t pixman_format, int width, int height, cairo_bool_t might_reuse, cairo_xcb_shm_info_t **shm_info_out) { cairo_surface_t *image; cairo_xcb_shm_info_t *shm_info; cairo_int_status_t status; size_t stride; *shm_info_out = NULL; stride = CAIRO_STRIDE_FOR_WIDTH_BPP (width, PIXMAN_FORMAT_BPP (pixman_format)); status = _cairo_xcb_connection_allocate_shm_info (connection, stride * height, might_reuse, &shm_info); if (unlikely (status)) { if (status == CAIRO_INT_STATUS_UNSUPPORTED) return NULL; return _cairo_surface_create_in_error (status); } image = _cairo_image_surface_create_with_pixman_format (shm_info->mem, pixman_format, width, height, stride); if (unlikely (image->status)) { _cairo_xcb_shm_info_destroy (shm_info); return image; } status = _cairo_user_data_array_set_data (&image->user_data, (const cairo_user_data_key_t *) connection, shm_info, (cairo_destroy_func_t) _cairo_xcb_shm_info_destroy); if (unlikely (status)) { cairo_surface_destroy (image); _cairo_xcb_shm_info_destroy (shm_info); return _cairo_surface_create_in_error (status); } *shm_info_out = shm_info; return image; } #endif static cairo_surface_t * _get_shm_image (cairo_xcb_surface_t *surface, int x, int y, int width, int height) { #if CAIRO_HAS_XCB_SHM_FUNCTIONS cairo_xcb_shm_info_t *shm_info; cairo_surface_t *image; cairo_status_t status; if ((surface->connection->flags & CAIRO_XCB_HAS_SHM) == 0) return NULL; image = _cairo_xcb_surface_create_shm_image (surface->connection, surface->pixman_format, width, height, TRUE, &shm_info); if (unlikely (image == NULL || image->status)) goto done; status = _cairo_xcb_connection_shm_get_image (surface->connection, surface->drawable, x, y, width, height, shm_info->shm, shm_info->offset); if (unlikely (status)) { cairo_surface_destroy (image); image = _cairo_surface_create_in_error (status); } done: return image; #else return NULL; #endif } static cairo_surface_t * _get_image (cairo_xcb_surface_t *surface, cairo_bool_t use_shm, int x, int y, int width, int height) { cairo_surface_t *image = NULL; cairo_xcb_connection_t *connection; xcb_get_image_reply_t *reply; cairo_int_status_t status; assert (surface->fallback == NULL); assert (x >= 0); assert (y >= 0); assert (x + width <= surface->width); assert (y + height <= surface->height); if (surface->deferred_clear) { image = _cairo_image_surface_create_with_pixman_format (NULL, surface->pixman_format, width, height, 0); if (surface->deferred_clear_color.alpha_short > 0x00ff) { cairo_solid_pattern_t solid; _cairo_pattern_init_solid (&solid, &surface->deferred_clear_color); status = _cairo_surface_paint (image, CAIRO_OPERATOR_SOURCE, &solid.base, NULL); if (unlikely (status)) { cairo_surface_destroy (image); image = _cairo_surface_create_in_error (status); } } return image; } connection = surface->connection; status = _cairo_xcb_connection_acquire (connection); if (unlikely (status)) return _cairo_surface_create_in_error (status); if (use_shm) { image = _get_shm_image (surface, x, y, width, height); if (image) { if (image->status == CAIRO_STATUS_SUCCESS) { _cairo_xcb_connection_release (connection); return image; } cairo_surface_destroy (image); } } reply =_cairo_xcb_connection_get_image (connection, surface->drawable, x, y, width, height); if (reply == NULL && ! surface->owns_pixmap) { /* xcb_get_image_t from a window is dangerous because it can * produce errors if the window is unmapped or partially * outside the screen. We could check for errors and * retry, but to keep things simple, we just create a * temporary pixmap * * If we hit this fallback too often, we should remember so and * skip the round-trip from the above GetImage request, * similar to what cairo-xlib does. */ xcb_pixmap_t pixmap; xcb_gcontext_t gc; gc = _cairo_xcb_screen_get_gc (surface->screen, surface->drawable, surface->depth); pixmap = _cairo_xcb_connection_create_pixmap (connection, surface->depth, surface->drawable, width, height); /* XXX IncludeInferiors? */ _cairo_xcb_connection_copy_area (connection, surface->drawable, pixmap, gc, x, y, 0, 0, width, height); _cairo_xcb_screen_put_gc (surface->screen, surface->depth, gc); reply = _cairo_xcb_connection_get_image (connection, pixmap, 0, 0, width, height); _cairo_xcb_connection_free_pixmap (connection, pixmap); } if (unlikely (reply == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto FAIL; } /* XXX byte swap */ /* XXX format conversion */ assert (reply->depth == surface->depth); image = _cairo_image_surface_create_with_pixman_format (xcb_get_image_data (reply), surface->pixman_format, width, height, CAIRO_STRIDE_FOR_WIDTH_BPP (width, PIXMAN_FORMAT_BPP (surface->pixman_format))); status = image->status; if (unlikely (status)) { free (reply); goto FAIL; } /* XXX */ pixman_image_set_destroy_function (((cairo_image_surface_t *)image)->pixman_image, _destroy_image, reply); _cairo_xcb_connection_release (connection); return image; FAIL: cairo_surface_destroy (image); _cairo_xcb_connection_release (connection); return _cairo_surface_create_in_error (status); } static cairo_surface_t * _cairo_xcb_surface_source (void *abstract_surface, cairo_rectangle_int_t *extents) { cairo_xcb_surface_t *surface = abstract_surface; if (extents) { extents->x = extents->y = 0; extents->width = surface->width; extents->height = surface->height; } return &surface->base; } static cairo_status_t _cairo_xcb_surface_acquire_source_image (void *abstract_surface, cairo_image_surface_t **image_out, void **image_extra) { cairo_xcb_surface_t *surface = abstract_surface; cairo_surface_t *image; cairo_int_status_t status = CAIRO_INT_STATUS_SUCCESS; if (surface->fallback != NULL) { image = cairo_surface_reference (&surface->fallback->base); goto DONE; } image = _cairo_surface_has_snapshot (&surface->base, &_cairo_image_surface_backend); if (image != NULL) { image = cairo_surface_reference (image); goto DONE; } image = _get_image (surface, FALSE, 0, 0, surface->width, surface->height); if (unlikely (image->status)) { status = image->status; cairo_surface_destroy (image); return status; } _cairo_surface_attach_snapshot (&surface->base, image, NULL); DONE: *image_out = (cairo_image_surface_t *) image; *image_extra = NULL; return CAIRO_STATUS_SUCCESS; } static void _cairo_xcb_surface_release_source_image (void *abstract_surface, cairo_image_surface_t *image, void *image_extra) { cairo_surface_destroy (&image->base); } cairo_bool_t _cairo_xcb_surface_get_extents (void *abstract_surface, cairo_rectangle_int_t *extents) { cairo_xcb_surface_t *surface = abstract_surface; extents->x = extents->y = 0; extents->width = surface->width; extents->height = surface->height; return TRUE; } static void _cairo_xcb_surface_get_font_options (void *abstract_surface, cairo_font_options_t *options) { cairo_xcb_surface_t *surface = abstract_surface; *options = *_cairo_xcb_screen_get_font_options (surface->screen); } static cairo_status_t _put_shm_image (cairo_xcb_surface_t *surface, xcb_gcontext_t gc, cairo_image_surface_t *image) { #if CAIRO_HAS_XCB_SHM_FUNCTIONS cairo_xcb_shm_info_t *shm_info; shm_info = _cairo_user_data_array_get_data (&image->base.user_data, (const cairo_user_data_key_t *) surface->connection); if (shm_info == NULL) return CAIRO_INT_STATUS_UNSUPPORTED; _cairo_xcb_connection_shm_put_image (surface->connection, surface->drawable, gc, surface->width, surface->height, 0, 0, image->width, image->height, image->base.device_transform_inverse.x0, image->base.device_transform_inverse.y0, image->depth, shm_info->shm, shm_info->offset); return CAIRO_STATUS_SUCCESS; #else return CAIRO_INT_STATUS_UNSUPPORTED; #endif } static cairo_status_t _put_image (cairo_xcb_surface_t *surface, cairo_image_surface_t *image) { cairo_int_status_t status = CAIRO_INT_STATUS_SUCCESS; /* XXX track damaged region? */ status = _cairo_xcb_connection_acquire (surface->connection); if (unlikely (status)) return status; if (image->pixman_format == surface->pixman_format) { xcb_gcontext_t gc; assert (image->depth == surface->depth); assert (image->stride == (int) CAIRO_STRIDE_FOR_WIDTH_BPP (image->width, PIXMAN_FORMAT_BPP (image->pixman_format))); gc = _cairo_xcb_screen_get_gc (surface->screen, surface->drawable, surface->depth); status = _put_shm_image (surface, gc, image); if (status == CAIRO_INT_STATUS_UNSUPPORTED) { _cairo_xcb_connection_put_image (surface->connection, surface->drawable, gc, image->width, image->height, image->base.device_transform_inverse.x0, image->base.device_transform_inverse.y0, image->depth, image->stride, image->data); status = CAIRO_STATUS_SUCCESS; } _cairo_xcb_screen_put_gc (surface->screen, surface->depth, gc); } else { ASSERT_NOT_REACHED; } _cairo_xcb_connection_release (surface->connection); return status; } static cairo_int_status_t _put_shm_image_boxes (cairo_xcb_surface_t *surface, cairo_image_surface_t *image, xcb_gcontext_t gc, cairo_boxes_t *boxes) { #if CAIRO_HAS_XCB_SHM_FUNCTIONS cairo_xcb_shm_info_t *shm_info; shm_info = _cairo_user_data_array_get_data (&image->base.user_data, (const cairo_user_data_key_t *) surface->connection); if (shm_info != NULL) { struct _cairo_boxes_chunk *chunk; for (chunk = &boxes->chunks; chunk; chunk = chunk->next) { int i; for (i = 0; i < chunk->count; i++) { cairo_box_t *b = &chunk->base[i]; int x = _cairo_fixed_integer_part (b->p1.x); int y = _cairo_fixed_integer_part (b->p1.y); int width = _cairo_fixed_integer_part (b->p2.x - b->p1.x); int height = _cairo_fixed_integer_part (b->p2.y - b->p1.y); _cairo_xcb_connection_shm_put_image (surface->connection, surface->drawable, gc, surface->width, surface->height, x, y, width, height, x, y, image->depth, shm_info->shm, shm_info->offset); } } } return CAIRO_INT_STATUS_SUCCESS; #endif return CAIRO_INT_STATUS_UNSUPPORTED; } static cairo_status_t _put_image_boxes (cairo_xcb_surface_t *surface, cairo_image_surface_t *image, cairo_boxes_t *boxes) { cairo_int_status_t status = CAIRO_INT_STATUS_SUCCESS; xcb_gcontext_t gc; if (boxes->num_boxes == 0) return CAIRO_STATUS_SUCCESS; /* XXX track damaged region? */ status = _cairo_xcb_connection_acquire (surface->connection); if (unlikely (status)) return status; assert (image->pixman_format == surface->pixman_format); assert (image->depth == surface->depth); assert (image->stride == (int) CAIRO_STRIDE_FOR_WIDTH_BPP (image->width, PIXMAN_FORMAT_BPP (image->pixman_format))); gc = _cairo_xcb_screen_get_gc (surface->screen, surface->drawable, surface->depth); status = _put_shm_image_boxes (surface, image, gc, boxes); if (status == CAIRO_INT_STATUS_UNSUPPORTED) { struct _cairo_boxes_chunk *chunk; for (chunk = &boxes->chunks; chunk; chunk = chunk->next) { int i; for (i = 0; i < chunk->count; i++) { cairo_box_t *b = &chunk->base[i]; int x = _cairo_fixed_integer_part (b->p1.x); int y = _cairo_fixed_integer_part (b->p1.y); int width = _cairo_fixed_integer_part (b->p2.x - b->p1.x); int height = _cairo_fixed_integer_part (b->p2.y - b->p1.y); _cairo_xcb_connection_put_image (surface->connection, surface->drawable, gc, width, height, x, y, image->depth, image->stride, image->data + x * PIXMAN_FORMAT_BPP (image->pixman_format) / 8 + y * image->stride); } } status = CAIRO_STATUS_SUCCESS; } _cairo_xcb_screen_put_gc (surface->screen, surface->depth, gc); _cairo_xcb_connection_release (surface->connection); return status; } static cairo_status_t _cairo_xcb_surface_flush (void *abstract_surface, unsigned flags) { cairo_xcb_surface_t *surface = abstract_surface; cairo_status_t status; if (flags) return CAIRO_STATUS_SUCCESS; if (likely (surface->fallback == NULL)) { status = CAIRO_STATUS_SUCCESS; if (! surface->base.finished && surface->deferred_clear) status = _cairo_xcb_surface_clear (surface); return status; } status = surface->base.status; if (status == CAIRO_STATUS_SUCCESS && (! surface->base._finishing || ! surface->owns_pixmap)) { status = cairo_surface_status (&surface->fallback->base); if (status == CAIRO_STATUS_SUCCESS) status = _cairo_bentley_ottmann_tessellate_boxes (&surface->fallback_damage, CAIRO_FILL_RULE_WINDING, &surface->fallback_damage); if (status == CAIRO_STATUS_SUCCESS) status = _put_image_boxes (surface, surface->fallback, &surface->fallback_damage); if (status == CAIRO_STATUS_SUCCESS && ! surface->base._finishing) { _cairo_surface_attach_snapshot (&surface->base, &surface->fallback->base, cairo_surface_finish); } } _cairo_boxes_clear (&surface->fallback_damage); cairo_surface_destroy (&surface->fallback->base); surface->fallback = NULL; return status; } static cairo_image_surface_t * _cairo_xcb_surface_map_to_image (void *abstract_surface, const cairo_rectangle_int_t *extents) { cairo_xcb_surface_t *surface = abstract_surface; cairo_surface_t *image; cairo_status_t status; if (surface->fallback) return _cairo_surface_map_to_image (&surface->fallback->base, extents); image = _get_image (surface, TRUE, extents->x, extents->y, extents->width, extents->height); status = cairo_surface_status (image); if (unlikely (status)) { cairo_surface_destroy(image); return _cairo_image_surface_create_in_error (status); } /* Do we have a deferred clear and this image surface does NOT cover the * whole xcb surface? Have to apply the clear in that case, else * uploading the image will handle the problem for us. */ if (surface->deferred_clear && ! (extents->width == surface->width && extents->height == surface->height)) { status = _cairo_xcb_surface_clear (surface); if (unlikely (status)) { cairo_surface_destroy(image); return _cairo_image_surface_create_in_error (status); } } surface->deferred_clear = FALSE; cairo_surface_set_device_offset (image, -extents->x, -extents->y); return (cairo_image_surface_t *) image; } static cairo_int_status_t _cairo_xcb_surface_unmap (void *abstract_surface, cairo_image_surface_t *image) { cairo_xcb_surface_t *surface = abstract_surface; cairo_int_status_t status; if (surface->fallback) return _cairo_surface_unmap_image (&surface->fallback->base, image); status = _put_image (abstract_surface, image); cairo_surface_finish (&image->base); cairo_surface_destroy (&image->base); return status; } static cairo_surface_t * _cairo_xcb_surface_fallback (cairo_xcb_surface_t *surface, cairo_composite_rectangles_t *composite) { cairo_image_surface_t *image; cairo_status_t status; status = _cairo_composite_rectangles_add_to_damage (composite, &surface->fallback_damage); if (unlikely (status)) return _cairo_surface_create_in_error (status); if (surface->fallback) return &surface->fallback->base; image = (cairo_image_surface_t *) _get_image (surface, TRUE, 0, 0, surface->width, surface->height); /* If there was a deferred clear, _get_image applied it */ if (image->base.status == CAIRO_STATUS_SUCCESS) { surface->deferred_clear = FALSE; surface->fallback = image; } else { cairo_surface_destroy (&image->base); } return &surface->fallback->base; } static cairo_int_status_t _cairo_xcb_fallback_compositor_paint (const cairo_compositor_t *compositor, cairo_composite_rectangles_t *extents) { cairo_xcb_surface_t *surface = (cairo_xcb_surface_t *) extents->surface; cairo_surface_t *fallback = _cairo_xcb_surface_fallback (surface, extents); return _cairo_surface_paint (fallback, extents->op, &extents->source_pattern.base, extents->clip); } static cairo_int_status_t _cairo_xcb_fallback_compositor_mask (const cairo_compositor_t *compositor, cairo_composite_rectangles_t *extents) { cairo_xcb_surface_t *surface = (cairo_xcb_surface_t *) extents->surface; cairo_surface_t *fallback = _cairo_xcb_surface_fallback (surface, extents); return _cairo_surface_mask (fallback, extents->op, &extents->source_pattern.base, &extents->mask_pattern.base, extents->clip); } static cairo_int_status_t _cairo_xcb_fallback_compositor_stroke (const cairo_compositor_t *compositor, cairo_composite_rectangles_t *extents, const cairo_path_fixed_t *path, const cairo_stroke_style_t *style, const cairo_matrix_t *ctm, const cairo_matrix_t *ctm_inverse, double tolerance, cairo_antialias_t antialias) { cairo_xcb_surface_t *surface = (cairo_xcb_surface_t *) extents->surface; cairo_surface_t *fallback = _cairo_xcb_surface_fallback (surface, extents); return _cairo_surface_stroke (fallback, extents->op, &extents->source_pattern.base, path, style, ctm, ctm_inverse, tolerance, antialias, extents->clip); } static cairo_int_status_t _cairo_xcb_fallback_compositor_fill (const cairo_compositor_t *compositor, cairo_composite_rectangles_t *extents, const cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, double tolerance, cairo_antialias_t antialias) { cairo_xcb_surface_t *surface = (cairo_xcb_surface_t *) extents->surface; cairo_surface_t *fallback = _cairo_xcb_surface_fallback (surface, extents); return _cairo_surface_fill (fallback, extents->op, &extents->source_pattern.base, path, fill_rule, tolerance, antialias, extents->clip); } static cairo_int_status_t _cairo_xcb_fallback_compositor_glyphs (const cairo_compositor_t *compositor, cairo_composite_rectangles_t *extents, cairo_scaled_font_t *scaled_font, cairo_glyph_t *glyphs, int num_glyphs, cairo_bool_t overlap) { cairo_xcb_surface_t *surface = (cairo_xcb_surface_t *) extents->surface; cairo_surface_t *fallback = _cairo_xcb_surface_fallback (surface, extents); return _cairo_surface_show_text_glyphs (fallback, extents->op, &extents->source_pattern.base, NULL, 0, glyphs, num_glyphs, NULL, 0, 0, scaled_font, extents->clip); } static const cairo_compositor_t _cairo_xcb_fallback_compositor = { &__cairo_no_compositor, _cairo_xcb_fallback_compositor_paint, _cairo_xcb_fallback_compositor_mask, _cairo_xcb_fallback_compositor_stroke, _cairo_xcb_fallback_compositor_fill, _cairo_xcb_fallback_compositor_glyphs, }; static const cairo_compositor_t _cairo_xcb_render_compositor = { &_cairo_xcb_fallback_compositor, _cairo_xcb_render_compositor_paint, _cairo_xcb_render_compositor_mask, _cairo_xcb_render_compositor_stroke, _cairo_xcb_render_compositor_fill, _cairo_xcb_render_compositor_glyphs, }; static inline const cairo_compositor_t * get_compositor (cairo_surface_t **s) { cairo_xcb_surface_t *surface = (cairo_xcb_surface_t * )*s; if (surface->fallback) { *s = &surface->fallback->base; return ((cairo_image_surface_t *) *s)->compositor; } return &_cairo_xcb_render_compositor; } static cairo_int_status_t _cairo_xcb_surface_paint (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_clip_t *clip) { cairo_surface_t *surface = abstract_surface; const cairo_compositor_t *compositor = get_compositor (&surface); cairo_int_status_t status; status = cairo_device_acquire (surface->device); if (unlikely (status)) return status; status = _cairo_surface_shadow_paint (surface, op, source, clip, &source->shadow); if (unlikely (status) || source->shadow.draw_shadow_only) { cairo_device_release (surface->device); return status; } status = _cairo_compositor_paint (compositor, surface, op, source, clip); cairo_device_release (surface->device); return status; } static cairo_int_status_t _cairo_xcb_surface_mask (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_pattern_t *mask, const cairo_clip_t *clip) { cairo_surface_t *surface = abstract_surface; const cairo_compositor_t *compositor = get_compositor (&surface); cairo_int_status_t status; status = cairo_device_acquire (surface->device); if (unlikely (status)) return status; status = _cairo_surface_shadow_mask (surface, op, source, mask, clip, &source->shadow); if (unlikely (status) || source->shadow.draw_shadow_only) { cairo_device_release (surface->device); return status; } status = _cairo_compositor_mask (compositor, surface, op, source, mask, clip); cairo_device_release (surface->device); return status; } static cairo_int_status_t _cairo_xcb_surface_stroke (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_path_fixed_t *path, const cairo_stroke_style_t *style, const cairo_matrix_t *ctm, const cairo_matrix_t *ctm_inverse, double tolerance, cairo_antialias_t antialias, const cairo_clip_t *clip) { cairo_surface_t *surface = abstract_surface; cairo_shadow_type_t shadow_type = source->shadow.type; const cairo_compositor_t *compositor = get_compositor (&surface); cairo_int_status_t status; status = cairo_device_acquire (surface->device); if (unlikely (status)) return status; if (shadow_type != CAIRO_SHADOW_INSET) status = _cairo_surface_shadow_stroke (surface, op, source, path, style, ctm, ctm_inverse, tolerance, antialias, clip, &source->shadow); if (unlikely (status)) { cairo_device_release (surface->device); return status; } if (shadow_type == CAIRO_SHADOW_DROP && source->shadow.draw_shadow_only) { cairo_device_release (surface->device); return status; } if (! source->shadow.draw_shadow_only) status = _cairo_compositor_stroke (compositor, surface, op, source, path, style, ctm, ctm_inverse, tolerance, antialias, clip); if (unlikely (status)) { cairo_device_release (surface->device); return status; } if (shadow_type == CAIRO_SHADOW_INSET) status = _cairo_surface_shadow_stroke (surface, op, source, path, style, ctm, ctm_inverse, tolerance, antialias, clip, &source->shadow); cairo_device_release (surface->device); return status; } static cairo_int_status_t _cairo_xcb_surface_fill (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_path_fixed_t*path, cairo_fill_rule_t fill_rule, double tolerance, cairo_antialias_t antialias, const cairo_clip_t *clip) { cairo_surface_t *surface = abstract_surface; cairo_shadow_type_t shadow_type = source->shadow.type; const cairo_compositor_t *compositor = get_compositor (&surface); cairo_int_status_t status; status = cairo_device_acquire (surface->device); if (unlikely (status)) return status; if (shadow_type != CAIRO_SHADOW_INSET) status = _cairo_surface_shadow_fill (surface, op, source, path, fill_rule, tolerance, antialias, clip, &source->shadow); if (unlikely (status)) { cairo_device_release (surface->device); return status; } if (shadow_type == CAIRO_SHADOW_DROP && source->shadow.draw_shadow_only) { cairo_device_release (surface->device); return status; } if (! source->shadow.draw_shadow_only) { if (! source->shadow.path_is_fill_with_spread || source->shadow.type != CAIRO_SHADOW_INSET) status = _cairo_compositor_fill (compositor, surface, op, source, path, fill_rule, tolerance, antialias, clip); else status = _cairo_compositor_paint (compositor, surface, op, source, clip); } if (unlikely (status)) { cairo_device_release (surface->device); return status; } if (shadow_type == CAIRO_SHADOW_INSET) status = _cairo_surface_shadow_fill (surface, op, source, path, fill_rule, tolerance, antialias, clip, &source->shadow); cairo_device_release (surface->device); return status; } static cairo_int_status_t _cairo_xcb_surface_glyphs (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, cairo_glyph_t *glyphs, int num_glyphs, cairo_scaled_font_t *scaled_font, const cairo_clip_t *clip) { cairo_surface_t *surface = abstract_surface; cairo_shadow_type_t shadow_type = source->shadow.type; const cairo_compositor_t *compositor = get_compositor (&surface); cairo_int_status_t status; status = cairo_device_acquire (surface->device); if (unlikely (status)) return status; if (shadow_type != CAIRO_SHADOW_INSET) status = _cairo_surface_shadow_glyphs (surface, op, source, scaled_font, glyphs, num_glyphs, clip, &source->shadow); if (unlikely (status)) { cairo_device_release (surface->device); return status; } if (shadow_type == CAIRO_SHADOW_DROP && source->shadow.draw_shadow_only) { cairo_device_release (surface->device); return status; } if (! source->shadow.draw_shadow_only) status = _cairo_compositor_glyphs (compositor, surface, op, source, glyphs, num_glyphs, scaled_font, clip); if (unlikely (status)) { cairo_device_release (surface->device); return status; } if (shadow_type == CAIRO_SHADOW_INSET) status = _cairo_surface_shadow_glyphs (surface, op, source, scaled_font, glyphs, num_glyphs, clip, &source->shadow); cairo_device_release (surface->device); return status; } const cairo_surface_backend_t _cairo_xcb_surface_backend = { CAIRO_SURFACE_TYPE_XCB, _cairo_xcb_surface_finish, _cairo_default_context_create, _cairo_xcb_surface_create_similar, _cairo_xcb_surface_create_similar_image, _cairo_xcb_surface_map_to_image, _cairo_xcb_surface_unmap, _cairo_xcb_surface_source, _cairo_xcb_surface_acquire_source_image, _cairo_xcb_surface_release_source_image, NULL, /* snapshot */ NULL, /* copy_page */ NULL, /* show_page */ _cairo_xcb_surface_get_extents, _cairo_xcb_surface_get_font_options, _cairo_xcb_surface_flush, NULL, _cairo_xcb_surface_paint, _cairo_xcb_surface_mask, _cairo_xcb_surface_stroke, _cairo_xcb_surface_fill, NULL, /* fill-stroke */ _cairo_xcb_surface_glyphs, }; cairo_surface_t * _cairo_xcb_surface_create_internal (cairo_xcb_screen_t *screen, xcb_drawable_t drawable, cairo_bool_t owns_pixmap, pixman_format_code_t pixman_format, xcb_render_pictformat_t xrender_format, int width, int height) { cairo_xcb_surface_t *surface; surface = malloc (sizeof (cairo_xcb_surface_t)); if (unlikely (surface == NULL)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); _cairo_surface_init (&surface->base, &_cairo_xcb_surface_backend, &screen->connection->device, _cairo_content_from_pixman_format (pixman_format)); surface->connection = _cairo_xcb_connection_reference (screen->connection); surface->screen = screen; cairo_list_add (&surface->link, &screen->surfaces); surface->drawable = drawable; surface->owns_pixmap = owns_pixmap; surface->deferred_clear = FALSE; surface->deferred_clear_color = *CAIRO_COLOR_TRANSPARENT; surface->width = width; surface->height = height; surface->depth = PIXMAN_FORMAT_DEPTH (pixman_format); surface->picture = XCB_NONE; if (screen->connection->force_precision != -1) surface->precision = screen->connection->force_precision; else surface->precision = XCB_RENDER_POLY_MODE_IMPRECISE; surface->pixman_format = pixman_format; surface->xrender_format = xrender_format; surface->fallback = NULL; _cairo_boxes_init (&surface->fallback_damage); return &surface->base; } static xcb_screen_t * _cairo_xcb_screen_from_visual (xcb_connection_t *connection, xcb_visualtype_t *visual, int *depth) { xcb_depth_iterator_t d; xcb_screen_iterator_t s; xcb_setup_t *set_up = NULL; set_up = xcb_get_setup (connection); if(set_up == NULL) return NULL; s = xcb_setup_roots_iterator (set_up); for (; s.rem; xcb_screen_next (&s)) { if (s.data->root_visual == visual->visual_id) { *depth = s.data->root_depth; return s.data; } d = xcb_screen_allowed_depths_iterator(s.data); for (; d.rem; xcb_depth_next (&d)) { xcb_visualtype_iterator_t v = xcb_depth_visuals_iterator (d.data); for (; v.rem; xcb_visualtype_next (&v)) { if (v.data->visual_id == visual->visual_id) { *depth = d.data->depth; return s.data; } } } } return NULL; } /** * cairo_xcb_surface_create: * @connection: an XCB connection * @drawable: an XCB drawable * @visual: the visual to use for drawing to @drawable. The depth * of the visual must match the depth of the drawable. * Currently, only TrueColor visuals are fully supported. * @width: the current width of @drawable * @height: the current height of @drawable * * Creates an XCB surface that draws to the given drawable. * The way that colors are represented in the drawable is specified * by the provided visual. * * Note: If @drawable is a Window, then the function * cairo_xcb_surface_set_size() must be called whenever the size of the * window changes. * * When @drawable is a Window containing child windows then drawing to * the created surface will be clipped by those child windows. When * the created surface is used as a source, the contents of the * children will be included. * * Return value: a pointer to the newly created surface. The caller * owns the surface and should call cairo_surface_destroy() when done * with it. * * This function always returns a valid pointer, but it will return a * pointer to a "nil" surface if an error such as out of memory * occurs. You can use cairo_surface_status() to check for this. * * Since: 1.12 **/ cairo_surface_t * cairo_xcb_surface_create (xcb_connection_t *connection, xcb_drawable_t drawable, xcb_visualtype_t *visual, int width, int height) { cairo_xcb_screen_t *screen; xcb_screen_t *xcb_screen; cairo_format_masks_t image_masks; pixman_format_code_t pixman_format; xcb_render_pictformat_t xrender_format; int depth; if (xcb_connection_has_error (connection)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_WRITE_ERROR)); if (unlikely (width > XLIB_COORD_MAX || height > XLIB_COORD_MAX)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE)); if (unlikely (width <= 0 || height <= 0)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE)); xcb_screen = _cairo_xcb_screen_from_visual (connection, visual, &depth); if (unlikely (xcb_screen == NULL)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_VISUAL)); image_masks.alpha_mask = 0; image_masks.red_mask = visual->red_mask; image_masks.green_mask = visual->green_mask; image_masks.blue_mask = visual->blue_mask; if (depth == 32) /* XXX visuals have no alpha! */ image_masks.alpha_mask = 0xffffffff & ~(visual->red_mask | visual->green_mask | visual->blue_mask); if (depth > 16) image_masks.bpp = 32; else if (depth > 8) image_masks.bpp = 16; else if (depth > 1) image_masks.bpp = 8; else image_masks.bpp = 1; if (! _pixman_format_from_masks (&image_masks, &pixman_format)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT)); screen = _cairo_xcb_screen_get (connection, xcb_screen); if (unlikely (screen == NULL)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); xrender_format = _cairo_xcb_connection_get_xrender_format_for_visual (screen->connection, visual->visual_id); return _cairo_xcb_surface_create_internal (screen, drawable, FALSE, pixman_format, xrender_format, width, height); } #if CAIRO_HAS_XLIB_XCB_FUNCTIONS slim_hidden_def (cairo_xcb_surface_create); #endif /** * cairo_xcb_surface_create_for_bitmap: * @connection: an XCB connection * @screen: the XCB screen associated with @bitmap * @bitmap: an XCB drawable (a Pixmap with depth 1) * @width: the current width of @bitmap * @height: the current height of @bitmap * * Creates an XCB surface that draws to the given bitmap. * This will be drawn to as a %CAIRO_FORMAT_A1 object. * * Return value: a pointer to the newly created surface. The caller * owns the surface and should call cairo_surface_destroy() when done * with it. * * This function always returns a valid pointer, but it will return a * pointer to a "nil" surface if an error such as out of memory * occurs. You can use cairo_surface_status() to check for this. * * Since: 1.12 **/ cairo_surface_t * cairo_xcb_surface_create_for_bitmap (xcb_connection_t *connection, xcb_screen_t *screen, xcb_pixmap_t bitmap, int width, int height) { cairo_xcb_screen_t *cairo_xcb_screen; if (xcb_connection_has_error (connection)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_WRITE_ERROR)); if (width > XLIB_COORD_MAX || height > XLIB_COORD_MAX) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE)); if (unlikely (width <= 0 || height <= 0)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE)); cairo_xcb_screen = _cairo_xcb_screen_get (connection, screen); if (unlikely (cairo_xcb_screen == NULL)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); return _cairo_xcb_surface_create_internal (cairo_xcb_screen, bitmap, FALSE, PIXMAN_a1, cairo_xcb_screen->connection->standard_formats[CAIRO_FORMAT_A1], width, height); } #if CAIRO_HAS_XLIB_XCB_FUNCTIONS slim_hidden_def (cairo_xcb_surface_create_for_bitmap); #endif /** * cairo_xcb_surface_create_with_xrender_format: * @connection: an XCB connection * @drawable: an XCB drawable * @screen: the XCB screen associated with @drawable * @format: the picture format to use for drawing to @drawable. The * depth of @format mush match the depth of the drawable. * @width: the current width of @drawable * @height: the current height of @drawable * * Creates an XCB surface that draws to the given drawable. * The way that colors are represented in the drawable is specified * by the provided picture format. * * Note: If @drawable is a Window, then the function * cairo_xcb_surface_set_size() must be called whenever the size of the * window changes. * * When @drawable is a Window containing child windows then drawing to * the created surface will be clipped by those child windows. When * the created surface is used as a source, the contents of the * children will be included. * * Return value: a pointer to the newly created surface. The caller * owns the surface and should call cairo_surface_destroy() when done * with it. * * This function always returns a valid pointer, but it will return a * pointer to a "nil" surface if an error such as out of memory * occurs. You can use cairo_surface_status() to check for this. * * Since: 1.12 **/ cairo_surface_t * cairo_xcb_surface_create_with_xrender_format (xcb_connection_t *connection, xcb_screen_t *screen, xcb_drawable_t drawable, xcb_render_pictforminfo_t *format, int width, int height) { cairo_xcb_screen_t *cairo_xcb_screen; cairo_format_masks_t image_masks; pixman_format_code_t pixman_format; if (xcb_connection_has_error (connection)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_WRITE_ERROR)); if (width > XLIB_COORD_MAX || height > XLIB_COORD_MAX) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE)); if (unlikely (width <= 0 || height <= 0)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE)); image_masks.alpha_mask = (unsigned long) format->direct.alpha_mask << format->direct.alpha_shift; image_masks.red_mask = (unsigned long) format->direct.red_mask << format->direct.red_shift; image_masks.green_mask = (unsigned long) format->direct.green_mask << format->direct.green_shift; image_masks.blue_mask = (unsigned long) format->direct.blue_mask << format->direct.blue_shift; #if 0 image_masks.bpp = format->depth; #else if (format->depth > 16) image_masks.bpp = 32; else if (format->depth > 8) image_masks.bpp = 16; else if (format->depth > 1) image_masks.bpp = 8; else image_masks.bpp = 1; #endif if (! _pixman_format_from_masks (&image_masks, &pixman_format)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT)); cairo_xcb_screen = _cairo_xcb_screen_get (connection, screen); if (unlikely (cairo_xcb_screen == NULL)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); return _cairo_xcb_surface_create_internal (cairo_xcb_screen, drawable, FALSE, pixman_format, format->id, width, height); } #if CAIRO_HAS_XLIB_XCB_FUNCTIONS slim_hidden_def (cairo_xcb_surface_create_with_xrender_format); #endif /* This does the necessary fixup when a surface's drawable or size changed. */ static void _drawable_changed (cairo_xcb_surface_t *surface) { _cairo_surface_set_error (&surface->base, _cairo_surface_begin_modification (&surface->base)); _cairo_boxes_clear (&surface->fallback_damage); cairo_surface_destroy (&surface->fallback->base); surface->deferred_clear = FALSE; surface->fallback = NULL; } /** * cairo_xcb_surface_set_size: * @surface: a #cairo_surface_t for the XCB backend * @width: the new width of the surface * @height: the new height of the surface * * Informs cairo of the new size of the XCB drawable underlying the * surface. For a surface created for a window (rather than a pixmap), * this function must be called each time the size of the window * changes. (For a subwindow, you are normally resizing the window * yourself, but for a toplevel window, it is necessary to listen for * ConfigureNotify events.) * * A pixmap can never change size, so it is never necessary to call * this function on a surface created for a pixmap. * * If cairo_surface_flush() wasn't called, some pending operations * might be discarded. * * Since: 1.12 **/ void cairo_xcb_surface_set_size (cairo_surface_t *abstract_surface, int width, int height) { cairo_xcb_surface_t *surface; if (unlikely (abstract_surface->status)) return; if (unlikely (abstract_surface->finished)) { _cairo_surface_set_error (abstract_surface, _cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); return; } if ( !_cairo_surface_is_xcb(abstract_surface)) { _cairo_surface_set_error (abstract_surface, _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH)); return; } if (width > XLIB_COORD_MAX || height > XLIB_COORD_MAX || width <= 0 || height <= 0) { _cairo_surface_set_error (abstract_surface, _cairo_error (CAIRO_STATUS_INVALID_SIZE)); return; } surface = (cairo_xcb_surface_t *) abstract_surface; _drawable_changed(surface); surface->width = width; surface->height = height; } #if CAIRO_HAS_XLIB_XCB_FUNCTIONS slim_hidden_def (cairo_xcb_surface_set_size); #endif /** * cairo_xcb_surface_set_drawable: * @surface: a #cairo_surface_t for the XCB backend * @drawable: the new drawable of the surface * @width: the new width of the surface * @height: the new height of the surface * * Informs cairo of the new drawable and size of the XCB drawable underlying the * surface. * * If cairo_surface_flush() wasn't called, some pending operations * might be discarded. * * Since: 1.12 **/ void cairo_xcb_surface_set_drawable (cairo_surface_t *abstract_surface, xcb_drawable_t drawable, int width, int height) { cairo_xcb_surface_t *surface; if (unlikely (abstract_surface->status)) return; if (unlikely (abstract_surface->finished)) { _cairo_surface_set_error (abstract_surface, _cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); return; } if ( !_cairo_surface_is_xcb(abstract_surface)) { _cairo_surface_set_error (abstract_surface, _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH)); return; } if (width > XLIB_COORD_MAX || height > XLIB_COORD_MAX || width <= 0 || height <= 0) { _cairo_surface_set_error (abstract_surface, _cairo_error (CAIRO_STATUS_INVALID_SIZE)); return; } surface = (cairo_xcb_surface_t *) abstract_surface; /* XXX: and what about this case? */ if (surface->owns_pixmap) return; _drawable_changed (surface); if (surface->drawable != drawable) { cairo_status_t status; status = _cairo_xcb_connection_acquire (surface->connection); if (unlikely (status)) return; if (surface->picture != XCB_NONE) { _cairo_xcb_connection_render_free_picture (surface->connection, surface->picture); surface->picture = XCB_NONE; } _cairo_xcb_connection_release (surface->connection); surface->drawable = drawable; } surface->width = width; surface->height = height; } #if CAIRO_HAS_XLIB_XCB_FUNCTIONS slim_hidden_def (cairo_xcb_surface_set_drawable); #endif