| /* 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 <behdad@behdad.org> |
| * Carl D. Worth <cworth@cworth.org> |
| * Chris Wilson <chris@chris-wilson.co.uk> |
| * Karl Tomlinson <karlt+@karlt.net>, 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" |
| |
| #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 NULL; |
| |
| 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; |
| 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_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; |
| |
| 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)) |
| return image->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_subimage (surface->connection, |
| surface->drawable, gc, |
| x, y, |
| width, height, |
| PIXMAN_FORMAT_BPP (image->pixman_format) / 8, |
| image->stride, |
| x, y, |
| image->depth, |
| image->data); |
| |
| } |
| } |
| 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; |
| } |
| |
| 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); |
| return _cairo_compositor_paint (compositor, surface, op, source, clip); |
| } |
| |
| 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); |
| return _cairo_compositor_mask (compositor, surface, op, source, mask, clip); |
| } |
| |
| 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; |
| const cairo_compositor_t *compositor = get_compositor (&surface); |
| return _cairo_compositor_stroke (compositor, surface, op, source, |
| path, style, ctm, ctm_inverse, |
| tolerance, antialias, clip); |
| } |
| |
| 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; |
| const cairo_compositor_t *compositor = get_compositor (&surface); |
| return _cairo_compositor_fill (compositor, surface, op, |
| source, path, fill_rule, |
| tolerance, antialias, clip); |
| } |
| |
| 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; |
| const cairo_compositor_t *compositor = get_compositor (&surface); |
| return _cairo_compositor_glyphs (compositor, surface, op, |
| source, glyphs, num_glyphs, |
| scaled_font, clip); |
| } |
| |
| 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; |
| |
| s = xcb_setup_roots_iterator (xcb_get_setup (connection)); |
| 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 |