diff options
27 files changed, 11660 insertions, 2523 deletions
diff --git a/boilerplate/Makefile.win32.features b/boilerplate/Makefile.win32.features index 5c1e0119..8d564118 100644 --- a/boilerplate/Makefile.win32.features +++ b/boilerplate/Makefile.win32.features @@ -49,6 +49,16 @@ enabled_cairo_boilerplate_private += $(cairo_boilerplate_xcb_private) enabled_cairo_boilerplate_sources += $(cairo_boilerplate_xcb_sources) endif +unsupported_cairo_boilerplate_headers += $(cairo_boilerplate_xlib_xcb_headers) +all_cairo_boilerplate_headers += $(cairo_boilerplate_xlib_xcb_headers) +all_cairo_boilerplate_private += $(cairo_boilerplate_xlib_xcb_private) +all_cairo_boilerplate_sources += $(cairo_boilerplate_xlib_xcb_sources) +ifeq ($(CAIRO_HAS_XLIB_XCB_FUNCTIONS),1) +enabled_cairo_boilerplate_headers += $(cairo_boilerplate_xlib_xcb_headers) +enabled_cairo_boilerplate_private += $(cairo_boilerplate_xlib_xcb_private) +enabled_cairo_boilerplate_sources += $(cairo_boilerplate_xlib_xcb_sources) +endif + unsupported_cairo_boilerplate_headers += $(cairo_boilerplate_qt_headers) all_cairo_boilerplate_headers += $(cairo_boilerplate_qt_headers) all_cairo_boilerplate_private += $(cairo_boilerplate_qt_private) @@ -159,6 +169,16 @@ enabled_cairo_boilerplate_private += $(cairo_boilerplate_gallium_private) enabled_cairo_boilerplate_sources += $(cairo_boilerplate_gallium_sources) endif +unsupported_cairo_boilerplate_headers += $(cairo_boilerplate_xcb_drm_headers) +all_cairo_boilerplate_headers += $(cairo_boilerplate_xcb_drm_headers) +all_cairo_boilerplate_private += $(cairo_boilerplate_xcb_drm_private) +all_cairo_boilerplate_sources += $(cairo_boilerplate_xcb_drm_sources) +ifeq ($(CAIRO_HAS_XCB_DRM_FUNCTIONS),1) +enabled_cairo_boilerplate_headers += $(cairo_boilerplate_xcb_drm_headers) +enabled_cairo_boilerplate_private += $(cairo_boilerplate_xcb_drm_private) +enabled_cairo_boilerplate_sources += $(cairo_boilerplate_xcb_drm_sources) +endif + supported_cairo_boilerplate_headers += $(cairo_boilerplate_png_headers) all_cairo_boilerplate_headers += $(cairo_boilerplate_png_headers) all_cairo_boilerplate_private += $(cairo_boilerplate_png_private) diff --git a/boilerplate/cairo-boilerplate-xcb.c b/boilerplate/cairo-boilerplate-xcb.c index 0ab2026f..ed173e68 100644 --- a/boilerplate/cairo-boilerplate-xcb.c +++ b/boilerplate/cairo-boilerplate-xcb.c @@ -26,15 +26,16 @@ #include "cairo-boilerplate-private.h" -#include <cairo-xcb-xrender.h> - -#include <xcb/xcb_renderutil.h> +#include <cairo-xcb.h> static const cairo_user_data_key_t xcb_closure_key; typedef struct _xcb_target_closure { xcb_connection_t *c; - xcb_pixmap_t pixmap; + cairo_device_t *device; + uint32_t drawable; + cairo_bool_t is_pixmap; + cairo_surface_t *surface; } xcb_target_closure_t; static void @@ -42,8 +43,17 @@ _cairo_boilerplate_xcb_cleanup (void *closure) { xcb_target_closure_t *xtc = closure; - xcb_free_pixmap (xtc->c, xtc->pixmap); + if (xtc->is_pixmap) + xcb_free_pixmap (xtc->c, xtc->drawable); + else + xcb_destroy_window (xtc->c, xtc->drawable); + cairo_surface_destroy (xtc->surface); + + cairo_device_finish (xtc->device); + cairo_device_destroy (xtc->device); + xcb_disconnect (xtc->c); + free (xtc); } @@ -53,10 +63,42 @@ _cairo_boilerplate_xcb_synchronize (void *closure) xcb_target_closure_t *xtc = closure; free (xcb_get_image_reply (xtc->c, xcb_get_image (xtc->c, XCB_IMAGE_FORMAT_Z_PIXMAP, - xtc->pixmap, 0, 0, 1, 1, /* AllPlanes */ ~0UL), + xtc->drawable, 0, 0, 1, 1, /* AllPlanes */ -1), 0)); } +static xcb_render_pictforminfo_t * +find_depth (xcb_connection_t *connection, int depth, void **formats_out) +{ + xcb_render_query_pict_formats_reply_t *formats; + xcb_render_query_pict_formats_cookie_t cookie; + xcb_render_pictforminfo_iterator_t i; + + cookie = xcb_render_query_pict_formats (connection); + xcb_flush (connection); + + formats = xcb_render_query_pict_formats_reply (connection, cookie, 0); + if (formats == NULL) + return NULL; + + for (i = xcb_render_query_pict_formats_formats_iterator (formats); + i.rem; + xcb_render_pictforminfo_next (&i)) + { + if (XCB_RENDER_PICT_TYPE_DIRECT != i.data->type) + continue; + + if (depth != i.data->depth) + continue; + + *formats_out = formats; + return i.data; + } + + free (formats); + return NULL; +} + static cairo_surface_t * _cairo_boilerplate_xcb_create_surface (const char *name, cairo_content_t content, @@ -72,10 +114,11 @@ _cairo_boilerplate_xcb_create_surface (const char *name, xcb_target_closure_t *xtc; xcb_connection_t *c; xcb_render_pictforminfo_t *render_format; - xcb_pict_standard_t format; + int depth; xcb_void_cookie_t cookie; cairo_surface_t *surface; cairo_status_t status; + void *formats; *closure = xtc = xmalloc (sizeof (xcb_target_closure_t)); @@ -85,32 +128,279 @@ _cairo_boilerplate_xcb_create_surface (const char *name, height = 1; xtc->c = c = xcb_connect(NULL,NULL); - if (xcb_connection_has_error(c)) { - fprintf (stderr, "Failed to connect to X server through XCB\n"); + if (xcb_connection_has_error(c)) + return NULL; + + root = xcb_setup_roots_iterator(xcb_get_setup(c)).data; + + xtc->surface = NULL; + xtc->is_pixmap = TRUE; + xtc->drawable = xcb_generate_id (c); + switch (content) { + case CAIRO_CONTENT_COLOR: + depth = 24; + cookie = xcb_create_pixmap_checked (c, depth, + xtc->drawable, root->root, + width, height); + break; + + case CAIRO_CONTENT_COLOR_ALPHA: + depth = 32; + cookie = xcb_create_pixmap_checked (c, depth, + xtc->drawable, root->root, + width, height); + break; + + case CAIRO_CONTENT_ALPHA: /* would be XCB_PICT_STANDARD_A_8 */ + default: + xcb_disconnect (c); + free (xtc); + return NULL; + } + + /* slow, but sure */ + if (xcb_request_check (c, cookie) != NULL) { + xcb_disconnect (c); + free (xtc); + return NULL; + } + + render_format = find_depth (c, depth, &formats); + if (render_format == NULL) { + xcb_disconnect (c); + free (xtc); + return NULL; + } + + surface = cairo_xcb_surface_create_with_xrender_format (c, root, + xtc->drawable, + render_format, + width, height); + free (formats); + + xtc->device = cairo_device_reference (cairo_surface_get_device (surface)); + status = cairo_surface_set_user_data (surface, &xcb_closure_key, xtc, NULL); + if (status == CAIRO_STATUS_SUCCESS) + return surface; + + cairo_surface_destroy (surface); + + _cairo_boilerplate_xcb_cleanup (xtc); + return cairo_boilerplate_surface_create_in_error (status); +} + +static xcb_visualtype_t * +lookup_visual (xcb_screen_t *s, xcb_visualid_t visual) +{ + xcb_depth_iterator_t d; + + d = xcb_screen_allowed_depths_iterator (s); + 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) + return v.data; + } + } + + return 0; +} + +static cairo_surface_t * +_cairo_boilerplate_xcb_create_window (const char *name, + cairo_content_t content, + double width, + double height, + double max_width, + double max_height, + cairo_boilerplate_mode_t mode, + int id, + void **closure) +{ + xcb_target_closure_t *xtc; + xcb_connection_t *c; + xcb_screen_t *s; + xcb_void_cookie_t cookie; + cairo_surface_t *surface; + cairo_status_t status; + uint32_t values[] = { 1 }; + + *closure = xtc = xmalloc (sizeof (xcb_target_closure_t)); + + if (width == 0) + width = 1; + if (height == 0) + height = 1; + + xtc->c = c = xcb_connect(NULL,NULL); + if (xcb_connection_has_error(c)) + return NULL; + + xtc->surface = NULL; + + s = xcb_setup_roots_iterator (xcb_get_setup (c)).data; + xtc->is_pixmap = FALSE; + xtc->drawable = xcb_generate_id (c); + cookie = xcb_create_window_checked (c, + s->root_depth, + xtc->drawable, + s->root, + 0, 0, width, height, 0, + XCB_WINDOW_CLASS_INPUT_OUTPUT, + s->root_visual, + XCB_CW_OVERRIDE_REDIRECT, + values); + xcb_map_window (c, xtc->drawable); + + /* slow, but sure */ + if (xcb_request_check (c, cookie) != NULL) { + xcb_disconnect (c); + free (xtc); + return NULL; + } + + surface = cairo_xcb_surface_create (c, + xtc->drawable, + lookup_visual (s, s->root_visual), + width, height); + + xtc->device = cairo_device_reference (cairo_surface_get_device (surface)); + status = cairo_surface_set_user_data (surface, &xcb_closure_key, xtc, NULL); + if (status == CAIRO_STATUS_SUCCESS) + return surface; + + cairo_surface_destroy (surface); + + _cairo_boilerplate_xcb_cleanup (xtc); + return cairo_boilerplate_surface_create_in_error (status); +} + +static cairo_surface_t * +_cairo_boilerplate_xcb_create_window_db (const char *name, + cairo_content_t content, + double width, + double height, + double max_width, + double max_height, + cairo_boilerplate_mode_t mode, + int id, + void **closure) +{ + xcb_target_closure_t *xtc; + xcb_connection_t *c; + xcb_screen_t *s; + xcb_void_cookie_t cookie; + cairo_surface_t *surface; + cairo_status_t status; + uint32_t values[] = { 1 }; + + *closure = xtc = xmalloc (sizeof (xcb_target_closure_t)); + + if (width == 0) + width = 1; + if (height == 0) + height = 1; + + xtc->c = c = xcb_connect(NULL,NULL); + if (xcb_connection_has_error(c)) + return NULL; + + xtc->surface = NULL; + + s = xcb_setup_roots_iterator (xcb_get_setup (c)).data; + xtc->is_pixmap = FALSE; + xtc->drawable = xcb_generate_id (c); + cookie = xcb_create_window_checked (c, + s->root_depth, + xtc->drawable, + s->root, + 0, 0, width, height, 0, + XCB_WINDOW_CLASS_INPUT_OUTPUT, + s->root_visual, + XCB_CW_OVERRIDE_REDIRECT, + values); + xcb_map_window (c, xtc->drawable); + + /* slow, but sure */ + if (xcb_request_check (c, cookie) != NULL) { + xcb_disconnect (c); + free (xtc); return NULL; } + xtc->surface = cairo_xcb_surface_create (c, + xtc->drawable, + lookup_visual (s, s->root_visual), + width, height); + surface = cairo_surface_create_similar (xtc->surface, content, width, height); + + xtc->device = cairo_device_reference (cairo_surface_get_device (surface)); + status = cairo_surface_set_user_data (surface, &xcb_closure_key, xtc, NULL); + if (status == CAIRO_STATUS_SUCCESS) + return surface; + + cairo_surface_destroy (surface); + + _cairo_boilerplate_xcb_cleanup (xtc); + return cairo_boilerplate_surface_create_in_error (status); +} + +static cairo_surface_t * +_cairo_boilerplate_xcb_create_render_0_0 (const char *name, + cairo_content_t content, + double width, + double height, + double max_width, + double max_height, + cairo_boilerplate_mode_t mode, + int id, + void **closure) +{ + xcb_screen_t *root; + xcb_target_closure_t *xtc; + xcb_connection_t *c; + xcb_render_pictforminfo_t *render_format; + int depth; + xcb_void_cookie_t cookie; + cairo_surface_t *surface, *tmp; + cairo_status_t status; + void *formats; + + *closure = xtc = xmalloc (sizeof (xcb_target_closure_t)); + + if (width == 0) + width = 1; + if (height == 0) + height = 1; + + xtc->c = c = xcb_connect(NULL,NULL); + if (xcb_connection_has_error(c)) + return NULL; + root = xcb_setup_roots_iterator(xcb_get_setup(c)).data; - xtc->pixmap = xcb_generate_id (c); + xtc->surface = NULL; + xtc->is_pixmap = TRUE; + xtc->drawable = xcb_generate_id (c); switch (content) { case CAIRO_CONTENT_COLOR: - cookie = xcb_create_pixmap_checked (c, 24, - xtc->pixmap, root->root, + depth = 24; + cookie = xcb_create_pixmap_checked (c, depth, + xtc->drawable, root->root, width, height); - format = XCB_PICT_STANDARD_RGB_24; break; case CAIRO_CONTENT_COLOR_ALPHA: - cookie = xcb_create_pixmap_checked (c, 32, - xtc->pixmap, root->root, + depth = 32; + cookie = xcb_create_pixmap_checked (c, depth, + xtc->drawable, root->root, width, height); - format = XCB_PICT_STANDARD_ARGB_32; break; case CAIRO_CONTENT_ALPHA: /* would be XCB_PICT_STANDARD_A_8 */ default: - fprintf (stderr, "Invalid content for XCB test: %d\n", content); + xcb_disconnect (c); + free (xtc); return NULL; } @@ -121,24 +411,127 @@ _cairo_boilerplate_xcb_create_surface (const char *name, return NULL; } - render_format = xcb_render_util_find_standard_format (xcb_render_util_query_formats (c), format); - if (render_format->id == 0) + render_format = find_depth (c, depth, &formats); + if (render_format == NULL) { + xcb_disconnect (c); + free (xtc); return NULL; + } - surface = cairo_xcb_surface_create_with_xrender_format (c, xtc->pixmap, root, + tmp = cairo_xcb_surface_create_with_xrender_format (c, root, + xtc->drawable, + render_format, + width, height); + + cairo_xcb_device_debug_cap_xrender_version (cairo_surface_get_device (tmp), + 0, 0); + /* recreate with impaired connection */ + surface = cairo_xcb_surface_create_with_xrender_format (c, root, + xtc->drawable, render_format, width, height); + free (formats); + cairo_surface_destroy (tmp); + xtc->device = cairo_device_reference (cairo_surface_get_device (surface)); status = cairo_surface_set_user_data (surface, &xcb_closure_key, xtc, NULL); if (status == CAIRO_STATUS_SUCCESS) return surface; cairo_surface_destroy (surface); - surface = cairo_boilerplate_surface_create_in_error (status); _cairo_boilerplate_xcb_cleanup (xtc); + return cairo_boilerplate_surface_create_in_error (status); +} + +static cairo_surface_t * +_cairo_boilerplate_xcb_create_fallback (const char *name, + cairo_content_t content, + double width, + double height, + double max_width, + double max_height, + cairo_boilerplate_mode_t mode, + int id, + void **closure) +{ + xcb_target_closure_t *xtc; + xcb_connection_t *c; + xcb_screen_t *s; + xcb_void_cookie_t cookie; + cairo_surface_t *tmp, *surface; + cairo_status_t status; + uint32_t values[] = { 1 }; + + *closure = xtc = xmalloc (sizeof (xcb_target_closure_t)); + + if (width == 0) + width = 1; + if (height == 0) + height = 1; + + xtc->c = c = xcb_connect (NULL,NULL); + if (xcb_connection_has_error(c)) { + free (xtc); + return NULL; + } + + s = xcb_setup_roots_iterator (xcb_get_setup (c)).data; + if (width > s->width_in_pixels || height > s->height_in_pixels) { + xcb_disconnect (c); + free (xtc); + return NULL; + } + + xtc->surface = NULL; + xtc->is_pixmap = FALSE; + xtc->drawable = xcb_generate_id (c); + cookie = xcb_create_window_checked (c, + s->root_depth, + xtc->drawable, + s->root, + 0, 0, width, height, 0, + XCB_WINDOW_CLASS_INPUT_OUTPUT, + s->root_visual, + XCB_CW_OVERRIDE_REDIRECT, + values); + xcb_map_window (c, xtc->drawable); + + /* slow, but sure */ + if (xcb_request_check (c, cookie) != NULL) { + xcb_disconnect (c); + free (xtc); + return NULL; + } + + tmp = cairo_xcb_surface_create (c, + xtc->drawable, + lookup_visual (s, s->root_visual), + width, height); + if (cairo_surface_status (tmp)) { + xcb_disconnect (c); + free (xtc); + return tmp; + } + + cairo_xcb_device_debug_cap_xrender_version (cairo_surface_get_device (tmp), + -1, -1); + /* recreate with impaired connection */ + surface = cairo_xcb_surface_create (c, + xtc->drawable, + lookup_visual (s, s->root_visual), + width, height); + cairo_surface_destroy (tmp); - return surface; + xtc->device = cairo_device_reference (cairo_surface_get_device (surface)); + status = cairo_surface_set_user_data (surface, &xcb_closure_key, xtc, NULL); + if (status == CAIRO_STATUS_SUCCESS) + return surface; + + cairo_surface_destroy (surface); + + _cairo_boilerplate_xcb_cleanup (xtc); + return cairo_boilerplate_surface_create_in_error (status); } static cairo_status_t @@ -148,20 +541,39 @@ _cairo_boilerplate_xcb_finish_surface (cairo_surface_t *surface) &xcb_closure_key); xcb_generic_event_t *ev; - cairo_surface_flush (surface); + if (xtc->surface != NULL) { + cairo_t *cr; + + cr = cairo_create (xtc->surface); + cairo_surface_set_device_offset (surface, 0, 0); + cairo_set_source_surface (cr, surface, 0, 0); + cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE); + cairo_paint (cr); + cairo_destroy (cr); + + surface = xtc->surface; + } + cairo_surface_flush (surface); if (cairo_surface_status (surface)) return cairo_surface_status (surface); while ((ev = xcb_poll_for_event (xtc->c)) != NULL) { + cairo_status_t status = CAIRO_STATUS_SUCCESS; + if (ev->response_type == 0 /* trust me! */) { xcb_generic_error_t *error = (xcb_generic_error_t *) ev; - fprintf (stderr, "Detected error during xcb run: %d major=%d, minor=%d\n", + fprintf (stderr, + "Detected error during xcb run: %d major=%d, minor=%d\n", error->error_code, error->major_code, error->minor_code); + free (error); - return CAIRO_STATUS_WRITE_ERROR; + status = CAIRO_STATUS_WRITE_ERROR; } + + if (status) + return status; } if (xcb_connection_has_error (xtc->c)) @@ -197,5 +609,65 @@ static const cairo_boilerplate_target_t targets[] = { _cairo_boilerplate_xcb_cleanup, _cairo_boilerplate_xcb_synchronize }, + { + "xcb-window", "xlib", NULL, NULL, + CAIRO_SURFACE_TYPE_XCB, CAIRO_CONTENT_COLOR, 1, + "cairo_xcb_surface_create_with_xrender_format", + _cairo_boilerplate_xcb_create_window, + NULL, + _cairo_boilerplate_xcb_finish_surface, + _cairo_boilerplate_get_image_surface, + cairo_surface_write_to_png, + _cairo_boilerplate_xcb_cleanup, + _cairo_boilerplate_xcb_synchronize + }, + { + "xcb-window&", "xlib", NULL, NULL, + CAIRO_SURFACE_TYPE_XCB, CAIRO_CONTENT_COLOR, 1, + "cairo_xcb_surface_create_with_xrender_format", + _cairo_boilerplate_xcb_create_window_db, + NULL, + _cairo_boilerplate_xcb_finish_surface, + _cairo_boilerplate_get_image_surface, + cairo_surface_write_to_png, + _cairo_boilerplate_xcb_cleanup, + _cairo_boilerplate_xcb_synchronize + }, + { + "xcb-render-0.0", "xlib-fallback", NULL, NULL, + CAIRO_SURFACE_TYPE_XCB, CAIRO_CONTENT_COLOR_ALPHA, 1, + "cairo_xcb_surface_create_with_xrender_format", + _cairo_boilerplate_xcb_create_render_0_0, + NULL, + _cairo_boilerplate_xcb_finish_surface, + _cairo_boilerplate_get_image_surface, + cairo_surface_write_to_png, + _cairo_boilerplate_xcb_cleanup, + _cairo_boilerplate_xcb_synchronize + }, + { + "xcb-render-0.0", "xlib-fallback", NULL, NULL, + CAIRO_SURFACE_TYPE_XCB, CAIRO_CONTENT_COLOR, 1, + "cairo_xcb_surface_create_with_xrender_format", + _cairo_boilerplate_xcb_create_render_0_0, + NULL, + _cairo_boilerplate_xcb_finish_surface, + _cairo_boilerplate_get_image_surface, + cairo_surface_write_to_png, + _cairo_boilerplate_xcb_cleanup, + _cairo_boilerplate_xcb_synchronize + }, + { + "xcb-fallback", "xlib-fallback", NULL, NULL, + CAIRO_SURFACE_TYPE_XCB, CAIRO_CONTENT_COLOR, 1, + "cairo_xcb_surface_create_with_xrender_format", + _cairo_boilerplate_xcb_create_fallback, + NULL, + _cairo_boilerplate_xcb_finish_surface, + _cairo_boilerplate_get_image_surface, + cairo_surface_write_to_png, + _cairo_boilerplate_xcb_cleanup, + _cairo_boilerplate_xcb_synchronize + }, }; CAIRO_BOILERPLATE (xcb, targets) diff --git a/build/Makefile.win32.features b/build/Makefile.win32.features index bd314e9f..b8c40a85 100644 --- a/build/Makefile.win32.features +++ b/build/Makefile.win32.features @@ -3,6 +3,7 @@ CAIRO_HAS_XLIB_SURFACE=0 CAIRO_HAS_XLIB_XRENDER_SURFACE=0 CAIRO_HAS_XCB_SURFACE=0 +CAIRO_HAS_XLIB_XCB_FUNCTIONS=0 CAIRO_HAS_QT_SURFACE=0 CAIRO_HAS_QUARTZ_SURFACE=0 CAIRO_HAS_QUARTZ_FONT=0 @@ -14,6 +15,7 @@ CAIRO_HAS_OS2_SURFACE=0 CAIRO_HAS_BEOS_SURFACE=0 CAIRO_HAS_DRM_SURFACE=0 CAIRO_HAS_GALLIUM_SURFACE=0 +CAIRO_HAS_XCB_DRM_FUNCTIONS=0 CAIRO_HAS_PNG_FUNCTIONS=1 CAIRO_HAS_GL_SURFACE=0 CAIRO_HAS_GLITZ_SURFACE=0 diff --git a/build/Makefile.win32.features-h b/build/Makefile.win32.features-h index 7e49cd63..95e9386d 100644 --- a/build/Makefile.win32.features-h +++ b/build/Makefile.win32.features-h @@ -14,6 +14,9 @@ endif ifeq ($(CAIRO_HAS_XCB_SURFACE),1) @echo "#define CAIRO_HAS_XCB_SURFACE 1" >> src/cairo-features.h endif +ifeq ($(CAIRO_HAS_XLIB_XCB_FUNCTIONS),1) + @echo "#define CAIRO_HAS_XLIB_XCB_FUNCTIONS 1" >> src/cairo-features.h +endif ifeq ($(CAIRO_HAS_QT_SURFACE),1) @echo "#define CAIRO_HAS_QT_SURFACE 1" >> src/cairo-features.h endif @@ -47,6 +50,9 @@ endif ifeq ($(CAIRO_HAS_GALLIUM_SURFACE),1) @echo "#define CAIRO_HAS_GALLIUM_SURFACE 1" >> src/cairo-features.h endif +ifeq ($(CAIRO_HAS_XCB_DRM_FUNCTIONS),1) + @echo "#define CAIRO_HAS_XCB_DRM_FUNCTIONS 1" >> src/cairo-features.h +endif ifeq ($(CAIRO_HAS_PNG_FUNCTIONS),1) @echo "#define CAIRO_HAS_PNG_FUNCTIONS 1" >> src/cairo-features.h endif diff --git a/build/configure.ac.features b/build/configure.ac.features index e057e2c8..c55b4efa 100644 --- a/build/configure.ac.features +++ b/build/configure.ac.features @@ -393,6 +393,8 @@ AC_DEFUN([CAIRO_REPORT], echo " GLX functions: $use_glx" echo " EGL functions: $use_egl" echo " Eagle functions: $use_eagle" + echo " X11-xcb functions: $use_xlib_xcb" + echo " XCB-drm functions: $use_xcb_drm" echo "" echo "The following features and utilies:" echo " cairo-trace: $use_trace" diff --git a/configure.ac b/configure.ac index d704353f..f94d9c17 100644 --- a/configure.ac +++ b/configure.ac @@ -109,10 +109,22 @@ CAIRO_ENABLE_SURFACE_BACKEND(xlib_xrender, Xlib Xrender, auto, [ dnl =========================================================================== CAIRO_ENABLE_SURFACE_BACKEND(xcb, XCB, no, [ - xcb_REQUIRES="xcb >= 0.9.92 xcb-render >= 0.9.92 xcb-renderutil" - PKG_CHECK_MODULES(xcb, $xcb_REQUIRES, , [AC_MSG_RESULT(no) - use_xcb="no (requires $xcb_REQUIRES http://xcb.freedesktop.org)"]) + xcb_REQUIRES="xcb >= 0.9.92 xcb-render >= 0.9.92 xcb-shm xcb-dri2" + PKG_CHECK_MODULES(xcb, $xcb_REQUIRES, , + [AC_MSG_RESULT(no) + use_xcb="no (requires $xcb_REQUIRES http://xcb.freedesktop.org)"]) +]) + +CAIRO_ENABLE_FUNCTIONS(xlib_xcb, Xlib/XCB, no, [ + if test "x$use_xcb" == "xyes" -a "x$use_xlib" == "xyes"; then + xlib_xcb_REQUIRES="x11-xcb" + PKG_CHECK_MODULES(xlib_xcb, $xlib_xcb_REQUIRES, , + [use_xlib_xcb="no (requires $xlib_xcb_REQUIRES http://xcb.freedesktop.org)"]) + else + use_xlib_xcb="no (requires both --enable-xlib and --enable-xcb)" + fi ]) +AM_CONDITIONAL(BUILD_XLIB_XCB, test "x$use_xlib_xcb" = "xyes") dnl =========================================================================== @@ -241,6 +253,14 @@ CAIRO_ENABLE_SURFACE_BACKEND(gallium, Gallium3D, no, [ fi ]) +CAIRO_ENABLE_FUNCTIONS(xcb_drm, XCB/DRM, no, [ + if test "x$use_xcb" == "xyes" -a "x$use_drm" == "xyes"; then + use_xcb_drm="yes" + else + use_xcb_drm="no (requires both --enable-xcb and --enable-drm)" + fi +]) + dnl =========================================================================== CAIRO_ENABLE_FUNCTIONS(png, PNG, yes, [ diff --git a/src/Makefile.sources b/src/Makefile.sources index f6d4fe30..0f77f0ed 100644 --- a/src/Makefile.sources +++ b/src/Makefile.sources @@ -250,19 +250,33 @@ cairo_xlib_private = \ cairo-xlib-surface-private.h \ cairo-xlib-xrender-private.h \ $(NULL) +if BUILD_XLIB_XCB +cairo_xlib_sources = cairo-xlib-xcb-surface.c +else cairo_xlib_sources = \ cairo-xlib-display.c \ cairo-xlib-screen.c \ cairo-xlib-surface.c \ cairo-xlib-visual.c \ $(NULL) +endif cairo_xlib_xrender_headers = cairo-xlib-xrender.h -# XXX split xcb-xrender. or better yet, merge it into xcb. xcb is so recent -# that it's hard to imagine having xcb but not render. -cairo_xcb_headers = cairo-xcb.h cairo-xcb-xrender.h -cairo_xcb_sources = cairo-xcb-surface.c +cairo_xcb_headers = cairo-xcb.h +cairo_xcb_private = cairo-xcb-private.h +cairo_xcb_sources = \ + cairo-xcb-connection.c \ + cairo-xcb-connection-core.c \ + cairo-xcb-connection-render.c \ + cairo-xcb-connection-shm.c \ + cairo-xcb-screen.c \ + cairo-xcb-shm.c \ + cairo-xcb-surface.c \ + cairo-xcb-surface-cairo.c \ + cairo-xcb-surface-core.c \ + cairo-xcb-surface-render.c \ + $(NULL) cairo_qt_headers = cairo-qt.h cairo_qt_sources = cairo-qt-surface.cpp diff --git a/src/Makefile.win32.features b/src/Makefile.win32.features index 048c9211..35750579 100644 --- a/src/Makefile.win32.features +++ b/src/Makefile.win32.features @@ -63,6 +63,20 @@ ifeq ($(CAIRO_HAS_XCB_SURFACE),1) enabled_cairo_pkgconf += cairo-xcb.pc endif +unsupported_cairo_headers += $(cairo_xlib_xcb_headers) +all_cairo_headers += $(cairo_xlib_xcb_headers) +all_cairo_private += $(cairo_xlib_xcb_private) +all_cairo_sources += $(cairo_xlib_xcb_sources) +ifeq ($(CAIRO_HAS_XLIB_XCB_FUNCTIONS),1) +enabled_cairo_headers += $(cairo_xlib_xcb_headers) +enabled_cairo_private += $(cairo_xlib_xcb_private) +enabled_cairo_sources += $(cairo_xlib_xcb_sources) +endif +all_cairo_pkgconf += cairo-xlib-xcb.pc +ifeq ($(CAIRO_HAS_XLIB_XCB_FUNCTIONS),1) +enabled_cairo_pkgconf += cairo-xlib-xcb.pc +endif + unsupported_cairo_headers += $(cairo_qt_headers) all_cairo_headers += $(cairo_qt_headers) all_cairo_private += $(cairo_qt_private) @@ -217,6 +231,20 @@ ifeq ($(CAIRO_HAS_GALLIUM_SURFACE),1) enabled_cairo_pkgconf += cairo-gallium.pc endif +unsupported_cairo_headers += $(cairo_xcb_drm_headers) +all_cairo_headers += $(cairo_xcb_drm_headers) +all_cairo_private += $(cairo_xcb_drm_private) +all_cairo_sources += $(cairo_xcb_drm_sources) +ifeq ($(CAIRO_HAS_XCB_DRM_FUNCTIONS),1) +enabled_cairo_headers += $(cairo_xcb_drm_headers) +enabled_cairo_private += $(cairo_xcb_drm_private) +enabled_cairo_sources += $(cairo_xcb_drm_sources) +endif +all_cairo_pkgconf += cairo-xcb-drm.pc +ifeq ($(CAIRO_HAS_XCB_DRM_FUNCTIONS),1) +enabled_cairo_pkgconf += cairo-xcb-drm.pc +endif + supported_cairo_headers += $(cairo_png_headers) all_cairo_headers += $(cairo_png_headers) all_cairo_private += $(cairo_png_private) diff --git a/src/cairo-list-private.h b/src/cairo-list-private.h index b8254bb7..6d65bf1c 100644 --- a/src/cairo-list-private.h +++ b/src/cairo-list-private.h @@ -186,4 +186,11 @@ cairo_list_is_empty (const cairo_list_t *head) return head->next == head; } +static inline cairo_bool_t +cairo_list_is_singular (const cairo_list_t *head) +{ + cairo_list_validate (head); + return head->next == head || head->next == head->prev; +} + #endif /* CAIRO_LIST_PRIVATE_H */ diff --git a/src/cairo-mutex-list-private.h b/src/cairo-mutex-list-private.h index c84cebf6..aa2c8f80 100644 --- a/src/cairo-mutex-list-private.h +++ b/src/cairo-mutex-list-private.h @@ -54,6 +54,10 @@ CAIRO_MUTEX_DECLARE (_cairo_ft_unscaled_font_map_mutex) CAIRO_MUTEX_DECLARE (_cairo_xlib_display_mutex) #endif +#if CAIRO_HAS_XCB_SURFACE +CAIRO_MUTEX_DECLARE (_cairo_xcb_connections_mutex) +#endif + #if CAIRO_HAS_GL_SURFACE CAIRO_MUTEX_DECLARE (_cairo_gl_context_mutex) #endif diff --git a/src/cairo-pattern.c b/src/cairo-pattern.c index e77f1531..aae1aa57 100644 --- a/src/cairo-pattern.c +++ b/src/cairo-pattern.c @@ -2628,23 +2628,20 @@ _cairo_gradient_color_stops_hash (unsigned long hash, return hash; } -static unsigned long +unsigned long _cairo_linear_pattern_hash (unsigned long hash, - const cairo_pattern_t *pattern) + const cairo_linear_pattern_t *linear) { - const cairo_linear_pattern_t *linear = (cairo_linear_pattern_t *) pattern; - hash = _cairo_hash_bytes (hash, &linear->p1, sizeof (linear->p1)); hash = _cairo_hash_bytes (hash, &linear->p2, sizeof (linear->p2)); return _cairo_gradient_color_stops_hash (hash, &linear->base); } -static unsigned long -_cairo_radial_pattern_hash (unsigned long hash, const cairo_pattern_t *pattern) +unsigned long +_cairo_radial_pattern_hash (unsigned long hash, + const cairo_radial_pattern_t *radial) { - const cairo_radial_pattern_t *radial = (cairo_radial_pattern_t *) pattern; - hash = _cairo_hash_bytes (hash, &radial->c1, sizeof (radial->c1)); hash = _cairo_hash_bytes (hash, &radial->r1, sizeof (radial->r1)); hash = _cairo_hash_bytes (hash, &radial->c2, sizeof (radial->c2)); @@ -2689,9 +2686,9 @@ _cairo_pattern_hash (const cairo_pattern_t *pattern) case CAIRO_PATTERN_TYPE_SOLID: return _cairo_solid_pattern_hash (hash, pattern); case CAIRO_PATTERN_TYPE_LINEAR: - return _cairo_linear_pattern_hash (hash, pattern); + return _cairo_linear_pattern_hash (hash, (cairo_linear_pattern_t *) pattern); case CAIRO_PATTERN_TYPE_RADIAL: - return _cairo_radial_pattern_hash (hash, pattern); + return _cairo_radial_pattern_hash (hash, (cairo_radial_pattern_t *) pattern); case CAIRO_PATTERN_TYPE_SURFACE: return _cairo_surface_pattern_hash (hash, pattern); default: @@ -2768,13 +2765,10 @@ _cairo_gradient_color_stops_equal (const cairo_gradient_pattern_t *a, return TRUE; } -static cairo_bool_t -_cairo_linear_pattern_equal (const cairo_pattern_t *A, - const cairo_pattern_t *B) +cairo_bool_t +_cairo_linear_pattern_equal (const cairo_linear_pattern_t *a, + const cairo_linear_pattern_t *b) { - const cairo_linear_pattern_t *a = (cairo_linear_pattern_t *) A; - const cairo_linear_pattern_t *b = (cairo_linear_pattern_t *) B; - if (a->p1.x != b->p1.x) return FALSE; @@ -2790,13 +2784,10 @@ _cairo_linear_pattern_equal (const cairo_pattern_t *A, return _cairo_gradient_color_stops_equal (&a->base, &b->base); } -static cairo_bool_t -_cairo_radial_pattern_equal (const cairo_pattern_t *A, - const cairo_pattern_t *B) +cairo_bool_t +_cairo_radial_pattern_equal (const cairo_radial_pattern_t *a, + const cairo_radial_pattern_t *b) { - const cairo_radial_pattern_t *a = (cairo_radial_pattern_t *) A; - const cairo_radial_pattern_t *b = (cairo_radial_pattern_t *) B; - if (a->c1.x != b->c1.x) return FALSE; @@ -2858,9 +2849,11 @@ _cairo_pattern_equal (const cairo_pattern_t *a, const cairo_pattern_t *b) case CAIRO_PATTERN_TYPE_SOLID: return _cairo_solid_pattern_equal (a, b); case CAIRO_PATTERN_TYPE_LINEAR: - return _cairo_linear_pattern_equal (a, b); + return _cairo_linear_pattern_equal ((cairo_linear_pattern_t *) a, + (cairo_linear_pattern_t *) b); case CAIRO_PATTERN_TYPE_RADIAL: - return _cairo_radial_pattern_equal (a, b); + return _cairo_radial_pattern_equal ((cairo_radial_pattern_t *) a, + (cairo_radial_pattern_t *) b); case CAIRO_PATTERN_TYPE_SURFACE: return _cairo_surface_pattern_equal (a, b); default: diff --git a/src/cairo-xcb-connection-core.c b/src/cairo-xcb-connection-core.c new file mode 100644 index 00000000..22a340df --- /dev/null +++ b/src/cairo-xcb-connection-core.c @@ -0,0 +1,482 @@ +/* cairo - a vector graphics library with display and print output + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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. + * + * Contributor(s): + * Chris Wilson <chris@chris-wilson.co.uk> + */ + +#include "cairoint.h" + +#include "cairo-xcb-private.h" + +#include <xcb/xcbext.h> + +xcb_pixmap_t +_cairo_xcb_connection_create_pixmap (cairo_xcb_connection_t *connection, + uint8_t depth, + xcb_drawable_t drawable, + uint16_t width, + uint16_t height) +{ + struct { + uint8_t req; + uint8_t depth; + uint16_t len; + uint32_t pixmap; + uint32_t drawable; + uint16_t width, height; + } req; + struct iovec vec[1]; + + req.req = 53; + req.depth = depth; + req.len = sizeof (req) >> 2; + + req.pixmap = _cairo_xcb_connection_get_xid (connection); + req.drawable = drawable; + req.width = width; + req.height = height; + + vec[0].iov_base = &req; + vec[0].iov_len = sizeof (req); + + _cairo_xcb_connection_write (connection, vec, 1); + + return req.pixmap; +} + +void +_cairo_xcb_connection_free_pixmap (cairo_xcb_connection_t *connection, + xcb_pixmap_t pixmap) +{ + struct { + uint8_t req; + uint8_t pad; + uint16_t len; + uint32_t pixmap; + } req; + struct iovec vec[1]; + + req.req = 54; + req.len = sizeof (req) >> 2; + req.pixmap = pixmap; + + vec[0].iov_base = &req; + vec[0].iov_len = sizeof (req); + + _cairo_xcb_connection_write (connection, vec, 1); + _cairo_xcb_connection_put_xid (connection, pixmap); +} + +xcb_gcontext_t +_cairo_xcb_connection_create_gc (cairo_xcb_connection_t *connection, + xcb_drawable_t drawable, + uint32_t value_mask, + uint32_t *values) +{ + struct { + uint8_t req; + uint8_t pad; + uint16_t len; + uint32_t gc; + uint32_t drawable; + uint32_t mask; + } req; + struct iovec vec[2]; + int len = _cairo_popcount (value_mask) * 4; + + req.req = 55; + req.len = (sizeof (req) + len) >> 2; + req.gc = _cairo_xcb_connection_get_xid (connection); + req.drawable = drawable; + req.mask = value_mask; + + vec[0].iov_base = &req; + vec[0].iov_len = sizeof (req); + vec[1].iov_base = values; + vec[1].iov_len = len; + + _cairo_xcb_connection_write (connection, vec, 2); + + return req.gc; +} + +void +_cairo_xcb_connection_free_gc (cairo_xcb_connection_t *connection, + xcb_gcontext_t gc) +{ + struct { + uint8_t req; + uint8_t pad; + uint16_t len; + uint32_t gc; + } req; + struct iovec vec[1]; + + req.req = 60; + req.len = sizeof (req) >> 2; + req.gc = gc; + + vec[0].iov_base = &req; + vec[0].iov_len = sizeof (req); + + _cairo_xcb_connection_write (connection, vec, 1); + _cairo_xcb_connection_put_xid (connection, gc); +} + +void +_cairo_xcb_connection_change_gc (cairo_xcb_connection_t *connection, + xcb_gcontext_t gc, + uint32_t value_mask, + uint32_t *values) +{ + struct { + uint8_t req; + uint8_t pad; + uint16_t len; + uint32_t gc; + uint32_t mask; + } req; + struct iovec vec[2]; + int len = _cairo_popcount (value_mask) * 4; + + req.req = 56; + req.len = (sizeof (req) + len) >> 2; + req.gc = gc; + req.mask = value_mask; + + vec[0].iov_base = &req; + vec[0].iov_len = sizeof (req); + vec[1].iov_base = values; + vec[1].iov_len = len; + + _cairo_xcb_connection_write (connection, vec, 2); +} + +void +_cairo_xcb_connection_copy_area (cairo_xcb_connection_t *connection, + xcb_drawable_t src, + xcb_drawable_t dst, + xcb_gcontext_t gc, + int16_t src_x, + int16_t src_y, + int16_t dst_x, + int16_t dst_y, + uint16_t width, + uint16_t height) +{ + struct { + uint8_t req; + uint8_t pad; + uint16_t len; + uint32_t src; + uint32_t dst; + uint32_t gc; + int16_t src_x; + int16_t src_y; + int16_t dst_x; + int16_t dst_y; + uint16_t width; + uint16_t height; + } req; + struct iovec vec[1]; + + req.req = 62; + req.len = sizeof (req) >> 2; + req.src = src; + req.dst = dst; + req.gc = gc; + req.src_x = src_x; + req.src_y = src_y; + req.dst_x = dst_x; + req.dst_y = dst_y; + req.width = width; + req.height = height; + + vec[0].iov_base = &req; + vec[0].iov_len = sizeof (req); + + _cairo_xcb_connection_write (connection, vec, 1); +} + +void +_cairo_xcb_connection_poly_fill_rectangle (cairo_xcb_connection_t *connection, + xcb_drawable_t dst, + xcb_gcontext_t gc, + uint32_t num_rectangles, + xcb_rectangle_t *rectangles) +{ + struct { + uint8_t req; + uint8_t pad; + uint16_t len; + uint32_t dst; + uint32_t gc; + } req; + struct iovec vec[2]; + + req.req = 70; + req.len = (sizeof (req) + num_rectangles * sizeof (xcb_rectangle_t)) >> 2; + req.dst = dst; + req.gc = gc; + + vec[0].iov_base = &req; + vec[0].iov_len = sizeof (req); + vec[1].iov_base = rectangles; + vec[1].iov_len = num_rectangles * sizeof (xcb_rectangle_t); + + _cairo_xcb_connection_write (connection, vec, 2); +} + +void +_cairo_xcb_connection_put_image (cairo_xcb_connection_t *connection, + xcb_drawable_t dst, + xcb_gcontext_t gc, + uint16_t width, + uint16_t height, + int16_t dst_x, + int16_t dst_y, + uint8_t depth, + uint32_t stride, + void *data) +{ + struct { + uint8_t req; + uint8_t format; + uint16_t len; + uint32_t dst; + uint32_t gc; + uint16_t width; + uint16_t height; + int16_t dst_x; + int16_t dst_y; + uint8_t left; + uint8_t depth; + uint16_t pad; + } req; + struct iovec vec[3]; + uint32_t prefix[2]; + uint32_t length = height * stride; + uint32_t len = (sizeof (req) + length) >> 2; + + req.req = 72; + req.format = XCB_IMAGE_FORMAT_Z_PIXMAP; + req.len = 0; + req.dst = dst; + req.gc = gc; + req.width = width; + req.height = height; + req.dst_x = dst_x; + req.dst_y = dst_y; + req.left = 0; + req.depth = depth; + + if (len < connection->root->maximum_request_length) { + req.len = len; + + vec[0].iov_base = &req; + vec[0].iov_len = sizeof (req); + vec[1].iov_base = data; + vec[1].iov_len = length; + + _cairo_xcb_connection_write (connection, vec, 2); + } else if (len < connection->maximum_request_length) { + prefix[0] = *(uint32_t *) &req; + prefix[1] = len + 1; + vec[0].iov_base = prefix; + vec[0].iov_len = sizeof (prefix); + vec[1].iov_base = (uint32_t *) &req + 1; + vec[1].iov_len = sizeof (req) - 4; + vec[2].iov_base = data; + vec[2].iov_len = length; + + _cairo_xcb_connection_write (connection, vec, 3); + } else { + int rows; + + rows = (connection->maximum_request_length - sizeof (req) - 4) / stride; + if (rows > 0) { + do { + if (rows > height) + rows = height; + + length = rows * stride; + len = (sizeof (req) + 4 + length) >> 2; + + req.height = rows; + + prefix[0] = *(uint32_t *) &req; + prefix[1] = len; + + vec[0].iov_base = prefix; + vec[0].iov_len = sizeof (prefix); + vec[1].iov_base = (uint32_t *) &req + 1; + vec[1].iov_len = sizeof (req) - 4; + vec[2].iov_base = data; + vec[2].iov_len = length; + + /* note may modify vec */ + _cairo_xcb_connection_write (connection, vec, 3); + + height -= rows; + req.dst_y += rows; + data = (char *) data + length; + } while (height); + } else { + ASSERT_NOT_REACHED; + } + } +} + +void +_cairo_xcb_connection_put_subimage (cairo_xcb_connection_t *connection, + xcb_drawable_t dst, + xcb_gcontext_t gc, + int16_t src_x, + int16_t src_y, + uint16_t width, + uint16_t height, + uint16_t cpp, + uint16_t stride, + int16_t dst_x, + int16_t dst_y, + uint8_t depth, + void *_data) +{ + struct { + uint8_t req; + uint8_t format; + uint16_t len; + uint32_t dst; + uint32_t gc; + uint16_t width; + uint16_t height; + int16_t dst_x; + int16_t dst_y; + uint8_t left; + uint8_t depth; + uint16_t pad; + } req; + struct iovec vec_stack[CAIRO_STACK_ARRAY_LENGTH (struct iovec)]; + struct iovec *vec = vec_stack; + uint32_t prefix[2]; + uint32_t len = (sizeof (req) + cpp*width*height) >> 2; + uint8_t *data = _data; + int n; + + req.req = 72; + req.format = XCB_IMAGE_FORMAT_Z_PIXMAP; + req.len = 0; + req.dst = dst; + req.gc = gc; + req.width = width; + req.height = height; + req.dst_x = dst_x; + req.dst_y = dst_y; + req.left = 0; + req.depth = depth; + + if (height + 2 > ARRAY_LENGTH (vec_stack)) { + vec = _cairo_malloc_ab (height+2, sizeof (struct iovec)); + if (unlikely (vec == NULL)) { + /* XXX loop over ARRAY_LENGTH (vec_stack) */ + return; + } + } + + data += src_y * stride + src_x * cpp; + if (len < connection->root->maximum_request_length) { + req.len = len; + + vec[0].iov_base = &req; + vec[0].iov_len = sizeof (req); + + n = 1; + } else if (len < connection->maximum_request_length) { + prefix[0] = *(uint32_t *) &req; + prefix[1] = len + 1; + vec[0].iov_base = prefix; + vec[0].iov_len = sizeof (prefix); + vec[1].iov_base = (uint32_t *) &req + 1; + vec[1].iov_len = sizeof (req) - 4; + + n = 2; + } else { + ASSERT_NOT_REACHED; + } + + while (height--) { + vec[n].iov_base = data; + vec[n].iov_len = cpp * width; + data += stride; + n++; + } + + _cairo_xcb_connection_write (connection, vec, n); + + if (vec != vec_stack) + free (vec); +} + +cairo_status_t +_cairo_xcb_connection_get_image (cairo_xcb_connection_t *connection, + xcb_drawable_t src, + int16_t src_x, + int16_t src_y, + uint16_t width, + uint16_t height, + xcb_get_image_reply_t **reply) +{ + xcb_generic_error_t *error; + cairo_status_t status; + + *reply = xcb_get_image_reply (connection->xcb_connection, + xcb_get_image (connection->xcb_connection, + XCB_IMAGE_FORMAT_Z_PIXMAP, + src, + src_x, src_y, + width, height, + (uint32_t) -1), + + &error); + if (error) { + free (error); + + if (*reply) + free (*reply); + *reply = NULL; + } + + status = _cairo_xcb_connection_take_socket (connection); + if (unlikely (status)) { + if (*reply) + free (*reply); + *reply = NULL; + } + + return status; +} diff --git a/src/cairo-xcb-connection-render.c b/src/cairo-xcb-connection-render.c new file mode 100644 index 00000000..2ebd6b47 --- /dev/null +++ b/src/cairo-xcb-connection-render.c @@ -0,0 +1,969 @@ +/* cairo - a vector graphics library with display and print output + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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. + * + * Contributor(s): + * Chris Wilson <chris@chris-wilson.co.uk> + */ + +#include "cairoint.h" + +#include "cairo-xcb-private.h" + +#include <xcb/xcbext.h> + +#define X_RenderSpans 99 +#define XLIB_COORD_MAX 32767 + +void +_cairo_xcb_connection_render_create_picture (cairo_xcb_connection_t *connection, + xcb_render_picture_t picture, + xcb_drawable_t drawable, + xcb_render_pictformat_t format, + uint32_t value_mask, + uint32_t *value_list) +{ + struct { + uint8_t major; + uint8_t minor; + uint16_t length; + uint32_t picture; + uint32_t drawable; + uint32_t format; + uint32_t mask; + } req; + struct iovec vec[2]; + int len = _cairo_popcount (value_mask) * 4; + + COMPILE_TIME_ASSERT (sizeof (req) == 20); + + req.major = connection->render->major_opcode; + req.minor = 4; + req.length = (sizeof (req) + len) >> 2; + req.picture = picture; + req.drawable = drawable; + req.format = format; + req.mask = value_mask; + + vec[0].iov_base = &req; + vec[0].iov_len = sizeof (req); + vec[1].iov_base = value_list; + vec[1].iov_len = len; + + _cairo_xcb_connection_write (connection, vec, 1 + (len != 0)); +} + +void +_cairo_xcb_connection_render_change_picture (cairo_xcb_connection_t *connection, + xcb_render_picture_t picture, + uint32_t value_mask, + uint32_t *value_list) +{ + struct { + uint8_t major; + uint8_t minor; + uint16_t length; + uint32_t picture; + uint32_t mask; + } req; + struct iovec vec[2]; + int len = _cairo_popcount (value_mask) * 4; + + COMPILE_TIME_ASSERT (sizeof (req) == 12); + + req.major = connection->render->major_opcode; + req.minor = 5; + req.length = (sizeof (req) + len) >> 2; + req.picture = picture; + req.mask = value_mask; + + vec[0].iov_base = &req; + vec[0].iov_len = sizeof (req); + vec[1].iov_base = value_list; + vec[1].iov_len = len; + + _cairo_xcb_connection_write (connection, vec, 2); +} + +void +_cairo_xcb_connection_render_set_picture_clip_rectangles (cairo_xcb_connection_t *connection, + xcb_render_picture_t picture, + int16_t clip_x_origin, + int16_t clip_y_origin, + uint32_t rectangles_len, + xcb_rectangle_t *rectangles) +{ + struct { + uint8_t major; + uint8_t minor; + uint16_t length; + uint32_t picture; + uint16_t x; + uint16_t y; + } req; + struct iovec vec[2]; + int len = sizeof (xcb_rectangle_t) * rectangles_len; + + COMPILE_TIME_ASSERT (sizeof (req) == 12); + assert ((len + sizeof (req)) >> 2 < connection->root->maximum_request_length); + + req.major = connection->render->major_opcode; + req.minor = 6; + req.length = (sizeof (req) + len) >> 2; + req.picture = picture; + req.x = clip_x_origin; + req.y = clip_y_origin; + + vec[0].iov_base = &req; + vec[0].iov_len = sizeof (req); + vec[1].iov_base = rectangles; + vec[1].iov_len = len; + + _cairo_xcb_connection_write (connection, vec, 2); +} + +void +_cairo_xcb_connection_render_free_picture (cairo_xcb_connection_t *connection, + xcb_render_picture_t picture) +{ + struct { + uint8_t major; + uint8_t minor; + uint16_t length; + uint32_t picture; + } req; + struct iovec vec[1]; + + COMPILE_TIME_ASSERT (sizeof (req) == 8); + + req.major = connection->render->major_opcode; + req.minor = 7; + req.length = sizeof (req) >> 2; + req.picture = picture; + + vec[0].iov_base = &req; + vec[0].iov_len = sizeof (req); + + _cairo_xcb_connection_write (connection, vec, 1); + _cairo_xcb_connection_put_xid (connection, picture); +} + +void +_cairo_xcb_connection_render_composite (cairo_xcb_connection_t *connection, + uint8_t op, + xcb_render_picture_t src, + xcb_render_picture_t mask, + xcb_render_picture_t dst, + int16_t src_x, + int16_t src_y, + int16_t mask_x, + int16_t mask_y, + int16_t dst_x, + int16_t dst_y, + uint16_t width, + uint16_t height) +{ + struct { + uint8_t major; + uint8_t minor; + uint16_t length; + uint8_t op; + uint8_t pad1; + uint16_t pad2; + uint32_t src; + uint32_t mask; + uint32_t dst; + int16_t src_x; + int16_t src_y; + int16_t mask_x; + int16_t mask_y; + int16_t dst_x; + int16_t dst_y; + uint16_t width; + uint16_t height; + } req; + struct iovec vec[1]; + + COMPILE_TIME_ASSERT (sizeof (req) == 36); + + req.major = connection->render->major_opcode; + req.minor = 8; + req.length = sizeof (req) >> 2; + req.op = op; + req.src = src; + req.mask = mask; + req.dst = dst; + req.src_x = src_x; + req.src_y = src_y; + req.mask_x = mask_x; + req.mask_y = mask_y; + req.dst_x = dst_x; + req.dst_y = dst_y; + req.width = width; + req.height = height; + + vec[0].iov_base = &req; + vec[0].iov_len = sizeof (req); + + _cairo_xcb_connection_write (connection, vec, 1); +} + +void +_cairo_xcb_connection_render_trapezoids (cairo_xcb_connection_t *connection, + uint8_t op, + xcb_render_picture_t src, + xcb_render_picture_t dst, + xcb_render_pictformat_t mask_format, + int16_t src_x, + int16_t src_y, + uint32_t traps_len, + xcb_render_trapezoid_t *traps) +{ + struct { + uint8_t major; + uint8_t minor; + uint16_t length; + uint8_t op; + uint8_t pad1; + uint16_t pad2; + uint32_t src; + uint32_t dst; + uint32_t mask_format; + int16_t src_x; + int16_t src_y; + } req; + struct iovec vec[3]; + uint32_t prefix[2]; + uint32_t len = (sizeof (req) + traps_len * sizeof (xcb_render_trapezoid_t)) >> 2; + + COMPILE_TIME_ASSERT (sizeof (req) == 24); + + req.major = connection->render->major_opcode; + req.minor = 10; + req.length = 0; + req.op = op; + req.src = src; + req.dst = dst; + req.mask_format = mask_format; + req.src_x = src_x; + req.src_y = src_y; + + if (len < connection->root->maximum_request_length) { + req.length = len; + + vec[0].iov_base = &req; + vec[0].iov_len = sizeof (req); + vec[1].iov_base = traps; + vec[1].iov_len = traps_len * sizeof (xcb_render_trapezoid_t); + + _cairo_xcb_connection_write (connection, vec, 2); + } else { + prefix[0] = *(uint32_t *) &req; + prefix[1] = len + 1; + vec[0].iov_base = prefix; + vec[0].iov_len = sizeof (prefix); + vec[1].iov_base = (uint32_t *) &req + 1; + vec[1].iov_len = sizeof (req) - 4; + vec[2].iov_base = traps; + vec[2].iov_len = traps_len * sizeof (xcb_render_trapezoid_t); + + _cairo_xcb_connection_write (connection, vec, 3); + } +} + +void +_cairo_xcb_connection_render_spans (cairo_xcb_connection_t *connection, + xcb_render_picture_t dst, + int op, + xcb_render_picture_t src, + int16_t src_x, int16_t src_y, + int16_t dst_x, int16_t dst_y, + int16_t width, int16_t height, + uint32_t num_spans, + uint16_t *spans) +{ + struct { + uint8_t major; + uint8_t minor; + uint16_t length; + uint8_t op; + uint8_t pad1; + uint16_t pad2; + uint32_t src; + uint32_t dst; + int16_t src_x; + int16_t src_y; + int16_t dst_x; + int16_t dst_y; + uint16_t width; + uint16_t height; + } req; + struct iovec vec[3]; + uint32_t prefix[2]; + uint32_t len = (sizeof (req) + num_spans * sizeof (uint16_t)) >> 2; + + req.major = connection->render->major_opcode; + req.minor = X_RenderSpans; + req.length = 0; + + req.dst = dst; + req.op = op; + req.src = src; + req.src_x = src_x; + req.src_y = src_y; + req.dst_x = dst_x; + req.dst_y = dst_y; + req.width = width; + req.height = height; + + if (len < connection->root->maximum_request_length) { + req.length = len; + + vec[0].iov_base = &req; + vec[0].iov_len = sizeof (req); + vec[1].iov_base = spans; + vec[1].iov_len = num_spans * sizeof (uint16_t); + + _cairo_xcb_connection_write (connection, vec, 2); + } else { + prefix[0] = *(uint32_t *) &req; + prefix[1] = len + 1; + vec[0].iov_base = prefix; + vec[0].iov_len = sizeof (prefix); + vec[1].iov_base = (uint32_t *) &req + 1; + vec[1].iov_len = sizeof (req) - 4; + vec[2].iov_base = spans; + vec[2].iov_len = num_spans * sizeof (uint16_t); + + _cairo_xcb_connection_write (connection, vec, 3); + } +} + +void +_cairo_xcb_connection_render_create_glyph_set (cairo_xcb_connection_t *connection, + xcb_render_glyphset_t id, + xcb_render_pictformat_t format) +{ + struct { + uint8_t major; + uint8_t minor; + uint16_t length; + uint32_t gsid; + uint32_t format; + } req; + struct iovec vec[1]; + + COMPILE_TIME_ASSERT (sizeof (req) == 12); + + req.major = connection->render->major_opcode; + req.minor = 17; + req.length = sizeof (req) >> 2; + req.gsid = id; + req.format = format; + + vec[0].iov_base = &req; + vec[0].iov_len = sizeof (req); + + _cairo_xcb_connection_write (connection, vec, 1); +} + +void +_cairo_xcb_connection_render_free_glyph_set (cairo_xcb_connection_t *connection, + xcb_render_glyphset_t glyphset) +{ + struct { + uint8_t major; + uint8_t minor; + uint16_t length; + uint32_t gsid; + } req; + struct iovec vec[1]; + + COMPILE_TIME_ASSERT (sizeof (req) == 8); + + req.major = connection->render->major_opcode; + req.minor = 19; + req.length = sizeof (req) >> 2; + req.gsid = glyphset; + + vec[0].iov_base = &req; + vec[0].iov_len = sizeof (req); + + _cairo_xcb_connection_write (connection, vec, 1); + _cairo_xcb_connection_put_xid (connection, glyphset); +} + +void +_cairo_xcb_connection_render_add_glyphs (cairo_xcb_connection_t *connection, + xcb_render_glyphset_t glyphset, + uint32_t num_glyphs, + uint32_t *glyphs_id, + xcb_render_glyphinfo_t *glyphs, + uint32_t data_len, + uint8_t *data) +{ + struct { + uint8_t major; + uint8_t minor; + uint16_t length; + uint32_t gsid; + uint32_t num_glyphs; + } req; + struct iovec vec[5]; + uint32_t prefix[2]; + uint32_t len = (sizeof (req) + num_glyphs * (sizeof (uint32_t) + sizeof (xcb_render_glyphinfo_t)) + data_len) >> 2; + int cnt; + + COMPILE_TIME_ASSERT (sizeof (req) == 12); + + req.major = connection->render->major_opcode; + req.minor = 20; + req.length = 0; + req.gsid = glyphset; + req.num_glyphs = num_glyphs; + + if (len < connection->root->maximum_request_length) { + req.length = len; + + vec[0].iov_base = &req; + vec[0].iov_len = sizeof (req); + + cnt = 1; + } else { + prefix[0] = *(uint32_t *) &req; + prefix[1] = len + 1; + vec[0].iov_base = prefix; + vec[0].iov_len = sizeof (prefix); + vec[1].iov_base = (uint32_t *) &req + 1; + vec[1].iov_len = sizeof (req) - 4; + + cnt = 2; + } + + vec[cnt].iov_base = glyphs_id; + vec[cnt].iov_len = num_glyphs * sizeof (uint32_t); + cnt++; + + vec[cnt].iov_base = glyphs; + vec[cnt].iov_len = num_glyphs * sizeof (xcb_render_glyphinfo_t); + cnt++; + + vec[cnt].iov_base = data; + vec[cnt].iov_len = data_len; + cnt++; + + _cairo_xcb_connection_write (connection, vec, cnt); +} + +void +_cairo_xcb_connection_render_free_glyphs (cairo_xcb_connection_t *connection, + xcb_render_glyphset_t glyphset, + uint32_t num_glyphs, + xcb_render_glyph_t *glyphs) +{ + struct { + uint8_t major; + uint8_t minor; + uint16_t length; + uint32_t gsid; + } req; + struct iovec vec[2]; + + COMPILE_TIME_ASSERT (sizeof (req) == 8); + assert ( (sizeof (req) + num_glyphs * sizeof (uint32_t)) >> 2 < connection->root->maximum_request_length); + + req.major = connection->render->major_opcode; + req.minor = 22; + req.length = (sizeof (req) + num_glyphs * sizeof (uint32_t)) >> 2; + req.gsid = glyphset; + + vec[0].iov_base = &req; + vec[0].iov_len = sizeof (req); + vec[1].iov_base = glyphs; + vec[1].iov_len = num_glyphs * sizeof (uint32_t); + + _cairo_xcb_connection_write (connection, vec, 2); +} + +void +_cairo_xcb_connection_render_composite_glyphs_8 (cairo_xcb_connection_t *connection, + uint8_t op, + xcb_render_picture_t src, + xcb_render_picture_t dst, + xcb_render_pictformat_t mask_format, + xcb_render_glyphset_t glyphset, + int16_t src_x, + int16_t src_y, + uint32_t glyphcmds_len, + uint8_t *glyphcmds) +{ + struct { + uint8_t major; + uint8_t minor; + uint16_t length; + uint8_t op; + uint8_t pad1; + uint16_t pad2; + uint32_t src; + uint32_t dst; + uint32_t mask_format; + uint32_t glyphset; + int16_t src_x; + int16_t src_y; + } req; + struct iovec vec[3]; + uint32_t prefix[2]; + int len; + + COMPILE_TIME_ASSERT (sizeof (req) == 28); + + req.major = connection->render->major_opcode; + req.minor = 23; + req.length = 0; + req.op = op; + req.src = src; + req.dst = dst; + req.mask_format = mask_format; + req.glyphset = glyphset; + req.src_x = src_x; + req.src_y = src_y; + + len = (sizeof (req) + glyphcmds_len) >> 2; + if (len < connection->root->maximum_request_length) { + req.length = len; + + vec[0].iov_base = &req; + vec[0].iov_len = sizeof (req); + + len = 1; + } else { + prefix[0] = *(uint32_t *) &req; + prefix[1] = len + 1; + vec[0].iov_base = prefix; + vec[0].iov_len = sizeof (prefix); + vec[1].iov_base = (uint32_t *) &req + 1; + vec[1].iov_len = sizeof (req) - 4; + + len = 2; + } + + vec[len].iov_base = glyphcmds; + vec[len].iov_len = glyphcmds_len; + len++; + + _cairo_xcb_connection_write (connection, vec, len); +} + +void +_cairo_xcb_connection_render_composite_glyphs_16 (cairo_xcb_connection_t *connection, + uint8_t op, + xcb_render_picture_t src, + xcb_render_picture_t dst, + xcb_render_pictformat_t mask_format, + xcb_render_glyphset_t glyphset, + int16_t src_x, + int16_t src_y, + uint32_t glyphcmds_len, + uint8_t *glyphcmds) +{ + struct { + uint8_t major; + uint8_t minor; + uint16_t length; + uint8_t op; + uint8_t pad1; + uint16_t pad2; + uint32_t src; + uint32_t dst; + uint32_t mask_format; + uint32_t glyphset; + int16_t src_x; + int16_t src_y; + } req; + struct iovec vec[3]; + uint32_t prefix[2]; + int len; + + COMPILE_TIME_ASSERT (sizeof (req) == 28); + + req.major = connection->render->major_opcode; + req.minor = 24; + req.length = 0; + req.op = op; + req.src = src; + req.dst = dst; + req.mask_format = mask_format; + req.glyphset = glyphset; + req.src_x = src_x; + req.src_y = src_y; + + len = (sizeof (req) + glyphcmds_len) >> 2; + if (len < connection->root->maximum_request_length) { + req.length = len; + + vec[0].iov_base = &req; + vec[0].iov_len = sizeof (req); + + len = 1; + } else { + prefix[0] = *(uint32_t *) &req; + prefix[1] = len + 1; + vec[0].iov_base = prefix; + vec[0].iov_len = sizeof (prefix); + vec[1].iov_base = (uint32_t *) &req + 1; + vec[1].iov_len = sizeof (req) - 4; + + len = 2; + } + + vec[len].iov_base = glyphcmds; + vec[len].iov_len = glyphcmds_len; + len++; + + _cairo_xcb_connection_write (connection, vec, len); +} + +void +_cairo_xcb_connection_render_composite_glyphs_32 (cairo_xcb_connection_t *connection, + uint8_t op, + xcb_render_picture_t src, + xcb_render_picture_t dst, + xcb_render_pictformat_t mask_format, + xcb_render_glyphset_t glyphset, + int16_t src_x, + int16_t src_y, + uint32_t glyphcmds_len, + uint8_t *glyphcmds) +{ + struct { + uint8_t major; + uint8_t minor; + uint16_t length; + uint8_t op; + uint8_t pad1; + uint16_t pad2; + uint32_t src; + uint32_t dst; + uint32_t mask_format; + uint32_t glyphset; + int16_t src_x; + int16_t src_y; + } req; + struct iovec vec[2]; + uint32_t prefix[2]; + int len; + + COMPILE_TIME_ASSERT (sizeof (req) == 28); + + req.major = connection->render->major_opcode; + req.minor = 25; + req.length = 0; + req.op = op; + req.src = src; + req.dst = dst; + req.mask_format = mask_format; + req.glyphset = glyphset; + req.src_x = src_x; + req.src_y = src_y; + + len = (sizeof (req) + glyphcmds_len) >> 2; + if (len < connection->root->maximum_request_length) { + req.length = len; + + vec[0].iov_base = &req; + vec[0].iov_len = sizeof (req); + + len = 1; + } else { + prefix[0] = *(uint32_t *) &req; + prefix[1] = len + 1; + vec[0].iov_base = prefix; + vec[0].iov_len = sizeof (prefix); + vec[1].iov_base = (uint32_t *) &req + 1; + vec[1].iov_len = sizeof (req) - 4; + + len = 2; + } + + vec[len].iov_base = glyphcmds; + vec[len].iov_len = glyphcmds_len; + len++; + + _cairo_xcb_connection_write (connection, vec, len); +} + +void +_cairo_xcb_connection_render_fill_rectangles (cairo_xcb_connection_t *connection, + uint8_t op, + xcb_render_picture_t dst, + xcb_render_color_t color, + uint32_t num_rects, + xcb_rectangle_t *rects) +{ + struct { + uint8_t major; + uint8_t minor; + uint16_t length; + uint8_t op; + uint8_t pad1; + uint16_t pad2; + uint32_t dst; + xcb_render_color_t color; + } req; + struct iovec vec[2]; + uint32_t len = (sizeof (req) + num_rects * sizeof (xcb_rectangle_t)) >> 2; + + COMPILE_TIME_ASSERT (sizeof (req) == 20); + assert(len < connection->root->maximum_request_length); + + req.major = connection->render->major_opcode; + req.minor = 26; + req.length = (sizeof (req) + num_rects * sizeof (xcb_rectangle_t)) >> 2; + req.op = op; + req.dst = dst; + req.color = color; + + vec[0].iov_base = &req; + vec[0].iov_len = sizeof (req); + vec[1].iov_base = rects; + vec[1].iov_len = num_rects * sizeof (xcb_rectangle_t); + + _cairo_xcb_connection_write (connection, vec, 2); +} + +void +_cairo_xcb_connection_render_set_picture_transform (cairo_xcb_connection_t *connection, + xcb_render_picture_t picture, + xcb_render_transform_t *transform) +{ + struct { + uint8_t major; + uint8_t minor; + uint16_t length; + uint32_t picture; + } req; + struct iovec vec[2]; + + req.major = connection->render->major_opcode; + req.minor = 28; + req.length = (sizeof (req) + sizeof (xcb_render_transform_t)) >> 2; + req.picture = picture; + + vec[0].iov_base = &req; + vec[0].iov_len = sizeof (req); + vec[1].iov_base = transform; + vec[1].iov_len = sizeof (xcb_render_transform_t); + + _cairo_xcb_connection_write (connection, vec, 2); +} + +void +_cairo_xcb_connection_render_set_picture_filter (cairo_xcb_connection_t *connection, + xcb_render_picture_t picture, + uint16_t filter_len, + char *filter) +{ + struct { + uint8_t major; + uint8_t minor; + uint16_t length; + uint32_t picture; + uint16_t nbytes; + uint16_t pad; + } req; + struct iovec vec[2]; + + req.nbytes = filter_len; + filter_len = (filter_len + 3) & ~3; + + req.major = connection->render->major_opcode; + req.minor = 30; + req.length = (sizeof (req) + filter_len) >> 2; + req.picture = picture; + + vec[0].iov_base = &req; + vec[0].iov_len = sizeof (req); + vec[1].iov_base = filter; + vec[1].iov_len = filter_len; + + _cairo_xcb_connection_write (connection, vec, 2); +} + +void +_cairo_xcb_connection_render_create_solid_fill (cairo_xcb_connection_t *connection, + xcb_render_picture_t picture, + xcb_render_color_t color) +{ + struct { + uint8_t major; + uint8_t minor; + uint16_t length; + uint32_t picture; + xcb_render_color_t color; + } req; + struct iovec vec[1]; + + COMPILE_TIME_ASSERT (sizeof (req) == 16); + + req.major = connection->render->major_opcode; + req.minor = 33; + req.length = sizeof (req) >> 2; + req.picture = picture; + req.color = color; + + vec[0].iov_base = &req; + vec[0].iov_len = sizeof (req); + + _cairo_xcb_connection_write (connection, vec, 1); +} + +void +_cairo_xcb_connection_render_create_linear_gradient (cairo_xcb_connection_t *connection, + xcb_render_picture_t picture, + xcb_render_pointfix_t p1, + xcb_render_pointfix_t p2, + uint32_t num_stops, + xcb_render_fixed_t *stops, + xcb_render_color_t *colors) +{ + struct { + uint8_t major; + uint8_t minor; + uint16_t length; + uint32_t picture; + xcb_render_pointfix_t p1, p2; + uint32_t num_stops; + } req; + struct iovec vec[3]; + + COMPILE_TIME_ASSERT (sizeof (req) == 28); + assert((sizeof (req) + num_stops * (sizeof (xcb_render_fixed_t) + sizeof (xcb_render_color_t))) >> 2 < connection->root->maximum_request_length); + + req.major = connection->render->major_opcode; + req.minor = 34; + req.length = (sizeof (req) + num_stops * (sizeof (xcb_render_fixed_t) + sizeof (xcb_render_color_t))) >> 2; + req.picture = picture; + req.p1 = p1; + req.p2 = p2; + req.num_stops = num_stops; + + vec[0].iov_base = &req; + vec[0].iov_len = sizeof (req); + vec[1].iov_base = stops; + vec[1].iov_len = num_stops * sizeof (xcb_render_fixed_t); + vec[2].iov_base = colors; + vec[2].iov_len = num_stops * sizeof (xcb_render_color_t); + + _cairo_xcb_connection_write (connection, vec, 3); +} + +void +_cairo_xcb_connection_render_create_radial_gradient (cairo_xcb_connection_t *connection, + xcb_render_picture_t picture, + xcb_render_pointfix_t inner, + xcb_render_pointfix_t outer, + xcb_render_fixed_t inner_radius, + xcb_render_fixed_t outer_radius, + uint32_t num_stops, + xcb_render_fixed_t *stops, + xcb_render_color_t *colors) +{ + struct { + uint8_t major; + uint8_t minor; + uint16_t length; + uint32_t picture; + xcb_render_pointfix_t inner; + xcb_render_pointfix_t outer; + xcb_render_fixed_t inner_radius; + xcb_render_fixed_t outer_radius; + uint32_t num_stops; + } req; + struct iovec vec[3]; + + COMPILE_TIME_ASSERT (sizeof (req) == 36); + assert((sizeof (req) + num_stops * (sizeof (xcb_render_fixed_t) + sizeof (xcb_render_color_t))) >> 2 < connection->root->maximum_request_length); + + req.major = connection->render->major_opcode; + req.minor = 35; + req.length = (sizeof (req) + num_stops * (sizeof (xcb_render_fixed_t) + sizeof (xcb_render_color_t))) >> 2; + req.picture = picture; + req.inner = inner; + req.outer = outer; + req.inner_radius = inner_radius; + req.outer_radius = outer_radius; + req.num_stops = num_stops; + + vec[0].iov_base = &req; + vec[0].iov_len = sizeof (req); + vec[1].iov_base = stops; + vec[1].iov_len = num_stops * sizeof (xcb_render_fixed_t); + vec[2].iov_base = colors; + vec[2].iov_len = num_stops * sizeof (xcb_render_color_t); + + _cairo_xcb_connection_write (connection, vec, 3); +} + +void +_cairo_xcb_connection_render_create_conical_gradient (cairo_xcb_connection_t *connection, + xcb_render_picture_t picture, + xcb_render_pointfix_t center, + xcb_render_fixed_t angle, + uint32_t num_stops, + xcb_render_fixed_t *stops, + xcb_render_color_t *colors) +{ + struct { + uint8_t major; + uint8_t minor; + uint16_t length; + uint32_t picture; + xcb_render_pointfix_t center; + xcb_render_fixed_t angle; + uint32_t num_stops; + } req; + struct iovec vec[3]; + + COMPILE_TIME_ASSERT (sizeof (req) == 24); + assert((sizeof (req) + num_stops * (sizeof (xcb_render_fixed_t) + sizeof (xcb_render_color_t))) >> 2 < connection->root->maximum_request_length); + + req.major = connection->render->major_opcode; + req.minor = 36; + req.length = (sizeof (req) + num_stops * (sizeof (xcb_render_fixed_t) + sizeof (xcb_render_color_t))) >> 2; + req.picture = picture; + req.center = center; + req.angle = angle; + req.num_stops = num_stops; + + vec[0].iov_base = &req; + vec[0].iov_len = sizeof (req); + vec[1].iov_base = stops; + vec[1].iov_len = num_stops * sizeof (xcb_render_fixed_t); + vec[2].iov_base = colors; + vec[2].iov_len = num_stops * sizeof (xcb_render_color_t); + + _cairo_xcb_connection_write (connection, vec, 3); +} diff --git a/src/cairo-xcb-connection-shm.c b/src/cairo-xcb-connection-shm.c new file mode 100644 index 00000000..2c13b1ba --- /dev/null +++ b/src/cairo-xcb-connection-shm.c @@ -0,0 +1,194 @@ +/* cairo - a vector graphics library with display and print output + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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. + * + * Contributor(s): + * Chris Wilson <chris@chris-wilson.co.uk> + */ + +#include "cairoint.h" + +#include "cairo-xcb-private.h" + +#include <xcb/xcbext.h> +#include <xcb/shm.h> + +uint32_t +_cairo_xcb_connection_shm_attach (cairo_xcb_connection_t *connection, + uint32_t id, + cairo_bool_t readonly) +{ + struct { + uint8_t req; + uint8_t shm_req; + uint16_t length; + uint32_t segment; + uint32_t id; + uint8_t readonly; + uint8_t pad1; + uint16_t pad2; + } req; + struct iovec vec[1]; + + COMPILE_TIME_ASSERT (sizeof (req) == 16); + + req.req = connection->shm->major_opcode; + req.shm_req = 1; + req.length = sizeof (req) >> 2; + req.segment = _cairo_xcb_connection_get_xid (connection); + req.id = id; + req.readonly = readonly; + + vec[0].iov_base = &req; + vec[0].iov_len = sizeof (req); + + _cairo_xcb_connection_write (connection, vec, 1); + return req.segment; +} + +uint64_t +_cairo_xcb_connection_shm_put_image (cairo_xcb_connection_t *connection, + xcb_drawable_t dst, + xcb_gcontext_t gc, + uint16_t total_width, + uint16_t total_height, + int16_t src_x, + int16_t src_y, + uint16_t width, + uint16_t height, + int16_t dst_x, + int16_t dst_y, + uint8_t depth, + uint32_t shm, + uint32_t offset) +{ + struct { + uint8_t req; + uint8_t shm_req; + uint16_t len; + uint32_t dst; + uint32_t gc; + uint16_t total_width; + uint16_t total_height; + int16_t src_x; + int16_t src_y; + uint16_t src_width; + uint16_t src_height; + int16_t dst_x; + int16_t dst_y; + uint8_t depth; + uint8_t format; + uint8_t send_event; + uint8_t pad; + uint32_t shm; + uint32_t offset; + } req; + struct iovec vec[2]; + + req.req = connection->shm->major_opcode; + req.shm_req = 3; + req.len = sizeof (req) >> 2; + req.dst = dst; + req.gc = gc; + req.total_width = total_width; + req.total_height = total_height; + req.src_x = src_x; + req.src_y = src_y; + req.src_width = width; + req.src_height = height; + req.dst_x = dst_x; + req.dst_y = dst_y; + req.depth = depth; + req.format = XCB_IMAGE_FORMAT_Z_PIXMAP; + req.send_event = 0; + req.shm = shm; + req.offset = offset; + + vec[0].iov_base = &req; + vec[0].iov_len = sizeof (req); + + _cairo_xcb_connection_write (connection, vec, 1); + return connection->seqno; +} + +cairo_status_t +_cairo_xcb_connection_shm_get_image (cairo_xcb_connection_t *connection, + xcb_drawable_t src, + int16_t src_x, + int16_t src_y, + uint16_t width, + uint16_t height, + uint32_t shmseg, + uint32_t offset) +{ + xcb_shm_get_image_reply_t *reply; + xcb_generic_error_t *error; + + reply = xcb_shm_get_image_reply (connection->xcb_connection, + xcb_shm_get_image (connection->xcb_connection, + src, + src_x, src_y, + width, height, + (uint32_t) -1, + XCB_IMAGE_FORMAT_Z_PIXMAP, + shmseg, offset), + &error); + free (reply); + + if (error) { + /* an error here should be impossible */ + free (error); + return _cairo_error (CAIRO_STATUS_READ_ERROR); + } + + return _cairo_xcb_connection_take_socket (connection); +} + +void +_cairo_xcb_connection_shm_detach (cairo_xcb_connection_t *connection, + uint32_t segment) +{ + struct { + uint8_t req; + uint8_t shm_req; + uint16_t length; + uint32_t segment; + } req; + struct iovec vec[1]; + + COMPILE_TIME_ASSERT (sizeof (req) == 8); + + req.req = connection->shm->major_opcode; + req.shm_req = 2; + req.length = sizeof (req) >> 2; + req.segment = segment; + + vec[0].iov_base = &req; + vec[0].iov_len = sizeof (req); + + _cairo_xcb_connection_write (connection, vec, 1); + _cairo_xcb_connection_put_xid (connection, segment); +} diff --git a/src/cairo-xcb-connection.c b/src/cairo-xcb-connection.c new file mode 100644 index 00000000..6721e923 --- /dev/null +++ b/src/cairo-xcb-connection.c @@ -0,0 +1,867 @@ +/* Cairo - a vector graphics library with display and print output + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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. + * + * Authors: + * Chris Wilson <chris@chris-wilson.co.uk> + */ + + +#include "cairoint.h" + +#include "cairo-xcb-private.h" +#include "cairo-hash-private.h" +#include "cairo-freelist-private.h" +#include "cairo-list-private.h" + +#include <sys/ipc.h> +#include <sys/shm.h> +#include <xcb/xcbext.h> +#include <xcb/bigreq.h> +#include <xcb/dri2.h> +#include <xcb/shm.h> +#include <errno.h> + +typedef struct _cairo_xcb_xrender_format { + cairo_hash_entry_t key; + xcb_render_pictformat_t xrender_format; +} cairo_xcb_xrender_format_t; + +typedef struct _cairo_xcb_xid { + cairo_list_t link; + uint32_t xid; +} cairo_xcb_xid_t; + +#define XCB_RENDER_AT_LEAST(V, major, minor) \ + (((V)->major_version > major) || \ + (((V)->major_version == major) && ((V)->minor_version >= minor))) + +#define XCB_RENDER_HAS_CREATE_PICTURE(surface) XCB_RENDER_AT_LEAST((surface), 0, 0) +#define XCB_RENDER_HAS_COMPOSITE(surface) XCB_RENDER_AT_LEAST((surface), 0, 0) +#define XCB_RENDER_HAS_COMPOSITE_TEXT(surface) XCB_RENDER_AT_LEAST((surface), 0, 0) + +#define XCB_RENDER_HAS_FILL_RECTANGLES(surface) XCB_RENDER_AT_LEAST((surface), 0, 1) + +#define XCB_RENDER_HAS_DISJOINT(surface) XCB_RENDER_AT_LEAST((surface), 0, 2) +#define XCB_RENDER_HAS_CONJOINT(surface) XCB_RENDER_AT_LEAST((surface), 0, 2) + +#define XCB_RENDER_HAS_TRAPEZOIDS(surface) XCB_RENDER_AT_LEAST((surface), 0, 4) +#define XCB_RENDER_HAS_TRIANGLES(surface) XCB_RENDER_AT_LEAST((surface), 0, 4) +#define XCB_RENDER_HAS_TRISTRIP(surface) XCB_RENDER_AT_LEAST((surface), 0, 4) +#define XCB_RENDER_HAS_TRIFAN(surface) XCB_RENDER_AT_LEAST((surface), 0, 4) +#define XCB_RENDER_HAS_SPANS(surface) XCB_RENDER_AT_LEAST((surface), 0, 12) + +#define XCB_RENDER_HAS_PICTURE_TRANSFORM(surface) XCB_RENDER_AT_LEAST((surface), 0, 6) +#define XCB_RENDER_HAS_FILTERS(surface) XCB_RENDER_AT_LEAST((surface), 0, 6) + +#define XCB_RENDER_HAS_EXTENDED_REPEAT(surface) XCB_RENDER_AT_LEAST((surface), 0, 10) +#define XCB_RENDER_HAS_GRADIENTS(surface) XCB_RENDER_AT_LEAST((surface), 0, 10) + +#define XCB_RENDER_HAS_PDF_OPERATORS(surface) XCB_RENDER_AT_LEAST((surface), 0, 11) + +static cairo_list_t connections; + +static cairo_status_t +_cairo_xcb_connection_find_visual_formats (cairo_xcb_connection_t *connection, + const xcb_render_query_pict_formats_reply_t *formats) +{ + xcb_render_pictscreen_iterator_t screens; + xcb_render_pictdepth_iterator_t depths; + xcb_render_pictvisual_iterator_t visuals; + + for (screens = xcb_render_query_pict_formats_screens_iterator (formats); + screens.rem; + xcb_render_pictscreen_next (&screens)) + { + for (depths = xcb_render_pictscreen_depths_iterator (screens.data); + depths.rem; + xcb_render_pictdepth_next (&depths)) + { + for (visuals = xcb_render_pictdepth_visuals_iterator (depths.data); + visuals.rem; + xcb_render_pictvisual_next (&visuals)) + { + cairo_xcb_xrender_format_t *f; + cairo_status_t status; + + f = malloc (sizeof (cairo_xcb_xrender_format_t)); + if (unlikely (f == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + f->key.hash = visuals.data->visual; + f->xrender_format = visuals.data->format; + status = _cairo_hash_table_insert (connection->visual_to_xrender_format, + &f->key); + if (unlikely (status)) + return status; + } + } + } + + return CAIRO_STATUS_SUCCESS; +} + +#if 0 +static xcb_format_t * +find_format_for_depth (const xcb_setup_t *setup, uint8_t depth) +{ + xcb_format_t *fmt = xcb_setup_pixmap_formats (setup); + xcb_format_t *fmtend = fmt + xcb_setup_pixmap_formats_length (setup); + + for (; fmt != fmtend; ++fmt) + if (fmt->depth == depth) + return fmt; + + return 0; +} +#endif + +static cairo_status_t +_cairo_xcb_connection_parse_xrender_formats (cairo_xcb_connection_t *connection, + const xcb_render_query_pict_formats_reply_t *formats) +{ + xcb_render_pictforminfo_iterator_t i; + cairo_status_t status; + + for (i = xcb_render_query_pict_formats_formats_iterator (formats); + i.rem; + xcb_render_pictforminfo_next (&i)) + { + cairo_format_masks_t masks; + pixman_format_code_t pixman_format; + + if (i.data->type != XCB_RENDER_PICT_TYPE_DIRECT) + continue; + + masks.alpha_mask = + (unsigned long) i.data->direct.alpha_mask << i.data->direct.alpha_shift; + masks.red_mask = + (unsigned long) i.data->direct.red_mask << i.data->direct.red_shift; + masks.green_mask = + (unsigned long) i.data->direct.green_mask << i.data->direct.green_shift; + masks.blue_mask = + (unsigned long) i.data->direct.blue_mask << i.data->direct.blue_shift; + masks.bpp = i.data->depth; + + if (_pixman_format_from_masks (&masks, &pixman_format)) { + cairo_hash_entry_t key; + + key.hash = pixman_format; + if (! _cairo_hash_table_lookup (connection->xrender_formats, &key)) { + cairo_xcb_xrender_format_t *f; + + f = malloc (sizeof (cairo_xcb_xrender_format_t)); + if (unlikely (f == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + f->key.hash = pixman_format; + f->xrender_format = i.data->id; + status = _cairo_hash_table_insert (connection->xrender_formats, + &f->key); + if (unlikely (status)) + return status; + +#if 0 + printf ("xrender %x -> (%lx, %lx, %lx, %lx, %d) %x [%d, %d]\n", + i.data->id, + masks.alpha_mask, + masks.red_mask, + masks.green_mask, + masks.blue_mask, + masks.bpp, + pixman_format, + PIXMAN_FORMAT_DEPTH(pixman_format), + PIXMAN_FORMAT_BPP(pixman_format)); +#endif + } + } + } + + status = _cairo_xcb_connection_find_visual_formats (connection, formats); + if (unlikely (status)) + return status; + + connection->standard_formats[CAIRO_FORMAT_A1] = + _cairo_xcb_connection_get_xrender_format (connection, PIXMAN_a1); + + connection->standard_formats[CAIRO_FORMAT_A8] = + _cairo_xcb_connection_get_xrender_format (connection, PIXMAN_a8); + + connection->standard_formats[CAIRO_FORMAT_RGB24] = + _cairo_xcb_connection_get_xrender_format (connection, + PIXMAN_FORMAT (24, + PIXMAN_TYPE_ARGB, + 0, 8, 8, 8)); + if (connection->standard_formats[CAIRO_FORMAT_RGB24] == XCB_NONE) { + connection->standard_formats[CAIRO_FORMAT_RGB24] = + _cairo_xcb_connection_get_xrender_format (connection, + PIXMAN_FORMAT (24, PIXMAN_TYPE_ABGR, + 0, 8, 8, 8)); + } + + connection->standard_formats[CAIRO_FORMAT_ARGB32] = + _cairo_xcb_connection_get_xrender_format (connection, PIXMAN_a8r8g8b8); + if (connection->standard_formats[CAIRO_FORMAT_ARGB32] == XCB_NONE) { + connection->standard_formats[CAIRO_FORMAT_ARGB32] = + _cairo_xcb_connection_get_xrender_format (connection, PIXMAN_a8b8g8r8); + } + + return CAIRO_STATUS_SUCCESS; +} + +/* + * We require support for depth 1, 8, 24 and 32 pixmaps + */ +#define DEPTH_MASK(d) (1 << ((d) - 1)) +#define REQUIRED_DEPTHS (DEPTH_MASK(1) | \ + DEPTH_MASK(8) | \ + DEPTH_MASK(24) | \ + DEPTH_MASK(32)) +static cairo_bool_t +pixmap_depths_usable (cairo_xcb_connection_t *connection, + uint32_t missing, + xcb_drawable_t root) +{ + xcb_connection_t *c = connection->xcb_connection; + xcb_void_cookie_t create_cookie[32]; + xcb_pixmap_t pixmap; + cairo_bool_t success = TRUE; + int depth, i, j; + + pixmap = _cairo_xcb_connection_get_xid (connection); + + for (depth = 1, i = 0; depth <= 32; depth++) { + if (missing & DEPTH_MASK(depth)) { + create_cookie[i] = xcb_create_pixmap_checked (c, depth, pixmap, root, 1, 1); + xcb_free_pixmap (c, pixmap); + if (!create_cookie[i].sequence) { + success = FALSE; + break; + } + i++; + } + } + + for (j = 0; j < i; j++) { + xcb_generic_error_t *create_error = xcb_request_check (c, create_cookie[j]); + success &= create_error == NULL; + free (create_error); + } + + _cairo_xcb_connection_put_xid (connection, pixmap); + + return success; +} + +static cairo_bool_t +has_required_depths (cairo_xcb_connection_t *connection) +{ + xcb_screen_iterator_t screens; + + for (screens = xcb_setup_roots_iterator (connection->root); + screens.rem; + xcb_screen_next (&screens)) + { + xcb_depth_iterator_t depths; + uint32_t missing = REQUIRED_DEPTHS; + + for (depths = xcb_screen_allowed_depths_iterator (screens.data); + depths.rem; + xcb_depth_next (&depths)) + { + missing &= ~DEPTH_MASK (depths.data->depth); + } + if (missing == 0) + continue; + + /* + * Ok, this is ugly. It should be sufficient at this + * point to just return false, but Xinerama is broken at + * this point and only advertises depths which have an + * associated visual. Of course, the other depths still + * work, but the only way to find out is to try them. + */ + if (! pixmap_depths_usable (connection, missing, screens.data->root)) + return FALSE; + } + + return TRUE; +} + +static cairo_status_t +_cairo_xcb_connection_query_render (cairo_xcb_connection_t *connection) +{ + xcb_connection_t *c = connection->xcb_connection; + xcb_render_query_version_cookie_t version_cookie; + xcb_render_query_pict_formats_cookie_t formats_cookie; + xcb_render_query_version_reply_t *version; + xcb_render_query_pict_formats_reply_t *formats; + cairo_status_t status; + cairo_bool_t present; + + version_cookie = xcb_render_query_version (c, 0, 10); + formats_cookie = xcb_render_query_pict_formats (c); + + present = has_required_depths (connection); + version = xcb_render_query_version_reply (c, version_cookie, 0); + formats = xcb_render_query_pict_formats_reply (c, formats_cookie, 0); + if (! present || version == NULL || formats == NULL) { + free (version); + free (formats); + return CAIRO_STATUS_SUCCESS; + } + + /* always true if the extension is present (i.e. >= 0.0) */ + connection->flags |= CAIRO_XCB_HAS_RENDER; + connection->flags |= CAIRO_XCB_RENDER_HAS_COMPOSITE; + connection->flags |= CAIRO_XCB_RENDER_HAS_COMPOSITE_GLYPHS; + + if (XCB_RENDER_HAS_FILL_RECTANGLES (version)) + connection->flags |= CAIRO_XCB_RENDER_HAS_FILL_RECTANGLES; + + if (XCB_RENDER_HAS_TRAPEZOIDS (version)) + connection->flags |= CAIRO_XCB_RENDER_HAS_COMPOSITE_TRAPEZOIDS; + + if (XCB_RENDER_HAS_PICTURE_TRANSFORM (version)) + connection->flags |= CAIRO_XCB_RENDER_HAS_PICTURE_TRANSFORM; + + if (XCB_RENDER_HAS_FILTERS (version)) + connection->flags |= CAIRO_XCB_RENDER_HAS_FILTERS; + + if (XCB_RENDER_HAS_PDF_OPERATORS (version)) + connection->flags |= CAIRO_XCB_RENDER_HAS_PDF_OPERATORS; + + if (XCB_RENDER_HAS_EXTENDED_REPEAT (version)) + connection->flags |= CAIRO_XCB_RENDER_HAS_EXTENDED_REPEAT; + + if (XCB_RENDER_HAS_GRADIENTS (version)) + connection->flags |= CAIRO_XCB_RENDER_HAS_GRADIENTS; + + free (version); + + status = _cairo_xcb_connection_parse_xrender_formats (connection, formats); + free (formats); + + return status; +} + +#if 0 +static void +_cairo_xcb_connection_query_cairo (cairo_xcb_connection_t *connection) +{ + xcb_connection_t *c = connection->xcb_connection; + xcb_cairo_query_version_reply_t *version; + + version = xcb_cairo_query_version_reply (c, + xcb_cairo_query_version (c, 0, 0), + 0); + + free (version); +} +#endif + +static cairo_bool_t +can_use_shm (cairo_xcb_connection_t *connection) +{ + cairo_bool_t success = TRUE; + xcb_connection_t *c = connection->xcb_connection; + xcb_void_cookie_t cookie[2]; + xcb_generic_error_t *error; + int shmid; + uint32_t shmseg; + void *ptr; + + shmid = shmget (IPC_PRIVATE, 0x1000, IPC_CREAT | 0600); + if (shmid == -1) + return FALSE; + + ptr = shmat (shmid, NULL, 0); + if (ptr == (char *) -1) { + shmctl (shmid, IPC_RMID, NULL); + return FALSE; + } + + shmseg = _cairo_xcb_connection_get_xid (connection); + cookie[0] = xcb_shm_attach_checked (c, shmseg, shmid, FALSE); + cookie[1] = xcb_shm_detach_checked (c, shmseg); + _cairo_xcb_connection_put_xid (connection, shmseg); + + error = xcb_request_check (c, cookie[0]); + if (error != NULL) + success = FALSE; + + error = xcb_request_check (c, cookie[1]); + if (error != NULL) + success = FALSE; + + shmctl (shmid, IPC_RMID, NULL); + shmdt (ptr); + + return success; +} + +static void +_cairo_xcb_connection_query_shm (cairo_xcb_connection_t *connection) +{ + xcb_connection_t *c = connection->xcb_connection; + xcb_shm_query_version_reply_t *version; + + version = xcb_shm_query_version_reply (c, xcb_shm_query_version (c), 0); + if (version == NULL) + return; + + free (version); + + if (can_use_shm (connection)) + connection->flags |= CAIRO_XCB_HAS_SHM; +} + +#if CAIRO_HAS_XCB_DRM_FUNCTIONS +static void +_cairo_xcb_connection_query_dri2 (cairo_xcb_connection_t *connection) +{ + xcb_connection_t *c = connection->xcb_connection; + xcb_dri2_query_version_reply_t *version; + + version = xcb_dri2_query_version_reply (c, + xcb_dri2_query_version (c, + XCB_DRI2_MAJOR_VERSION, + XCB_DRI2_MINOR_VERSION), + 0); + if (version == NULL) + return; + + free (version); + + connection->flags |= CAIRO_XCB_HAS_DRI2; +} +#endif + +static void +_device_flush (void *device) +{ + cairo_xcb_connection_t *connection = device; + cairo_xcb_screen_t *screen; + cairo_status_t status; + + status = cairo_device_acquire (&connection->device); + if (unlikely (status)) + return; + + CAIRO_MUTEX_LOCK (connection->screens_mutex); + cairo_list_foreach_entry (screen, cairo_xcb_screen_t, + &connection->screens, link) + { + if (screen->device != NULL) + cairo_device_flush (screen->device); + } + CAIRO_MUTEX_UNLOCK (connection->screens_mutex); + + xcb_flush (connection->xcb_connection); + + cairo_device_release (&connection->device); +} + +static cairo_bool_t +_xrender_formats_equal (const void *A, const void *B) +{ + const cairo_xcb_xrender_format_t *a = A, *b = B; + return a->key.hash == b->key.hash; +} + +static void +_pluck_xrender_format (void *entry, + void *closure) +{ + _cairo_hash_table_remove (closure, entry); + free (entry); +} + +static void +_device_finish (void *device) +{ + cairo_xcb_connection_t *connection = device; + + if (! cairo_list_is_empty (&connection->link)) { + CAIRO_MUTEX_LOCK (_cairo_xcb_connections_mutex); + cairo_list_del (&connection->link); + CAIRO_MUTEX_UNLOCK (_cairo_xcb_connections_mutex); + } + + while (! cairo_list_is_empty (&connection->fonts)) { + cairo_xcb_font_t *font; + + font = cairo_list_first_entry (&connection->fonts, + cairo_xcb_font_t, + link); + _cairo_xcb_font_finish (font); + } + + while (! cairo_list_is_empty (&connection->screens)) { + cairo_xcb_screen_t *screen; + + screen = cairo_list_first_entry (&connection->screens, + cairo_xcb_screen_t, + link); + _cairo_xcb_screen_finish (screen); + } +} + +static void +_device_destroy (void *device) +{ + cairo_xcb_connection_t *connection = device; + + _cairo_hash_table_foreach (connection->xrender_formats, + _pluck_xrender_format, connection->xrender_formats); + _cairo_hash_table_destroy (connection->xrender_formats); + + _cairo_hash_table_foreach (connection->visual_to_xrender_format, + _pluck_xrender_format, + connection->visual_to_xrender_format); + _cairo_hash_table_destroy (connection->visual_to_xrender_format); + + _cairo_xcb_connection_shm_mem_pools_fini (connection); + _cairo_freepool_fini (&connection->shm_info_freelist); + + _cairo_freepool_fini (&connection->xid_pool); + + CAIRO_MUTEX_FINI (connection->shm_mutex); + CAIRO_MUTEX_FINI (connection->screens_mutex); + + free (connection); +} + +static const cairo_device_backend_t _cairo_xcb_device_backend = { + CAIRO_DEVICE_TYPE_XCB, + + NULL, NULL, /* lock, unlock */ + + _device_flush, + _device_finish, + _device_destroy, +}; + +cairo_xcb_connection_t * +_cairo_xcb_connection_get (xcb_connection_t *xcb_connection) +{ + cairo_xcb_connection_t *connection; + const xcb_query_extension_reply_t *ext; + cairo_status_t status; + + CAIRO_MUTEX_LOCK (_cairo_xcb_connections_mutex); + if (connections.next == NULL) { + /* XXX _cairo_init () */ + cairo_list_init (&connections); + } + + cairo_list_foreach_entry (connection, + cairo_xcb_connection_t, + &connections, + link) + { + if (connection->xcb_connection == xcb_connection) { + /* Maintain MRU order. */ + if (connections.next != &connection->link) + cairo_list_move (&connection->link, &connections); + + goto unlock; + } + } + + connection = malloc (sizeof (cairo_xcb_connection_t)); + if (unlikely (connection == NULL)) + goto unlock; + + _cairo_device_init (&connection->device, &_cairo_xcb_device_backend); + CAIRO_MUTEX_INIT (connection->shm_mutex); + CAIRO_MUTEX_INIT (connection->screens_mutex); + + connection->xcb_connection = xcb_connection; + connection->has_socket = FALSE; + + xcb_prefetch_extension_data (xcb_connection, &xcb_big_requests_id); + xcb_prefetch_extension_data (xcb_connection, &xcb_shm_id); + xcb_prefetch_extension_data (xcb_connection, &xcb_render_id); +#if 0 + xcb_prefetch_extension_data (xcb_connection, &xcb_cairo_id); +#endif +#if CAIRO_HAS_XCB_DRM_FUNCTIONS + xcb_prefetch_extension_data (xcb_connection, &xcb_dri2_id); +#endif + + xcb_prefetch_maximum_request_length (xcb_connection); + + cairo_list_init (&connection->fonts); + cairo_list_init (&connection->screens); + cairo_list_add (&connection->link, &connections); + connection->xrender_formats = _cairo_hash_table_create (_xrender_formats_equal); + connection->visual_to_xrender_format = _cairo_hash_table_create (_xrender_formats_equal); + + cairo_list_init (&connection->free_xids); + _cairo_freepool_init (&connection->xid_pool, + sizeof (cairo_xcb_xid_t)); + + cairo_list_init (&connection->shm_pools); + _cairo_freepool_init (&connection->shm_info_freelist, + sizeof (cairo_xcb_shm_info_t)); + + connection->maximum_request_length = + xcb_get_maximum_request_length (xcb_connection); + + connection->flags = 0; + + connection->root = xcb_get_setup (xcb_connection); + connection->render = NULL; + ext = xcb_get_extension_data (xcb_connection, &xcb_render_id); + if (ext != NULL && ext->present) { + status = _cairo_xcb_connection_query_render (connection); + if (unlikely (status)) { + _cairo_xcb_connection_destroy (connection); + connection = NULL; + goto unlock; + } + + connection->render = ext; + } + +#if 0 + ext = xcb_get_extension_data (connection, &xcb_cairo_id); + if (ext != NULL && ext->present) + _cairo_xcb_connection_query_cairo (connection); +#endif + + connection->shm = NULL; + ext = xcb_get_extension_data (xcb_connection, &xcb_shm_id); + if (ext != NULL && ext->present) { + _cairo_xcb_connection_query_shm (connection); + connection->shm = ext; + } + + connection->dri2 = NULL; +#if CAIRO_HAS_XCB_DRM_FUNCTIONS + ext = xcb_get_extension_data (xcb_connection, &xcb_dri2_id); + if (ext != NULL && ext->present) { + _cairo_xcb_connection_query_dri2 (connection); + connection->dri2 = ext; + } +#endif + +unlock: + CAIRO_MUTEX_UNLOCK (_cairo_xcb_connections_mutex); + + return connection; +} + +xcb_render_pictformat_t +_cairo_xcb_connection_get_xrender_format (cairo_xcb_connection_t *connection, + pixman_format_code_t pixman_format) +{ + cairo_hash_entry_t key; + cairo_xcb_xrender_format_t *format; + + key.hash = pixman_format; + format = _cairo_hash_table_lookup (connection->xrender_formats, &key); + return format ? format->xrender_format : XCB_NONE; +} + +xcb_render_pictformat_t +_cairo_xcb_connection_get_xrender_format_for_visual (cairo_xcb_connection_t *connection, + const xcb_visualid_t visual) +{ + cairo_hash_entry_t key; + cairo_xcb_xrender_format_t *format; + + key.hash = visual; + format = _cairo_hash_table_lookup (connection->visual_to_xrender_format, &key); + return format ? format->xrender_format : XCB_NONE; +} + +void +_cairo_xcb_connection_put_xid (cairo_xcb_connection_t *connection, + uint32_t xid) +{ + cairo_xcb_xid_t *cache; + + assert (CAIRO_MUTEX_IS_LOCKED (connection->mutex)); + cache = _cairo_freepool_alloc (&connection->xid_pool); + if (likely (cache != NULL)) { + cache->xid = xid; + cairo_list_add (&cache->link, &connection->free_xids); + } +} + +uint32_t +_cairo_xcb_connection_get_xid (cairo_xcb_connection_t *connection) +{ + uint32_t xid; + + assert (CAIRO_MUTEX_IS_LOCKED (connection->mutex)); + if (! cairo_list_is_empty (&connection->free_xids)) { + cairo_xcb_xid_t *cache; + + cache = cairo_list_first_entry (&connection->free_xids, + cairo_xcb_xid_t, + link); + xid = cache->xid; + + cairo_list_del (&cache->link); + _cairo_freepool_free (&connection->xid_pool, cache); + } else { + xid = xcb_generate_id (connection->xcb_connection); + } + + return xid; +} + +static void +_cairo_xcb_return_socket (void *closure) +{ + cairo_xcb_connection_t *connection = closure; + + CAIRO_MUTEX_LOCK (connection->device.mutex); + connection->has_socket = FALSE; + CAIRO_MUTEX_UNLOCK (connection->device.mutex); +} + +cairo_status_t +_cairo_xcb_connection_take_socket (cairo_xcb_connection_t *connection) +{ + assert (CAIRO_MUTEX_IS_LOCKED (connection->mutex)); + + if (unlikely (connection->device.status)) + return connection->device.status; + + if (! connection->has_socket) { + if (! xcb_take_socket (connection->xcb_connection, + _cairo_xcb_return_socket, + connection, + 0, &connection->seqno)) + { + return connection->device.status = _cairo_error (CAIRO_STATUS_WRITE_ERROR); + } + + connection->has_socket = TRUE; + } + + return CAIRO_STATUS_SUCCESS; +} + +/* public (debug) interface */ + +void +cairo_xcb_device_debug_cap_xshm_version (cairo_device_t *device, + int major_version, + int minor_version) +{ + cairo_xcb_connection_t *connection = (cairo_xcb_connection_t *) device; + cairo_status_t status; + + if (device->backend->type != CAIRO_DEVICE_TYPE_XCB) { + status = _cairo_device_set_error (device, CAIRO_STATUS_DEVICE_TYPE_MISMATCH); + return; + } + + /* clear any flags that are inappropriate for the desired version */ + if (major_version < 0 && minor_version < 0) { + connection->flags &= ~(CAIRO_XCB_HAS_SHM); + } +} + +void +cairo_xcb_device_debug_cap_xrender_version (cairo_device_t *device, + int major_version, + int minor_version) +{ + cairo_xcb_connection_t *connection = (cairo_xcb_connection_t *) device; + cairo_status_t status; + + if (device->backend->type != CAIRO_DEVICE_TYPE_XCB) { + status = _cairo_device_set_error (device, CAIRO_STATUS_DEVICE_TYPE_MISMATCH); + return; + } + + /* clear any flags that are inappropriate for the desired version */ + if (major_version < 0 && minor_version < 0) { + connection->flags &= ~(CAIRO_XCB_HAS_RENDER | + CAIRO_XCB_RENDER_HAS_COMPOSITE | + CAIRO_XCB_RENDER_HAS_COMPOSITE_GLYPHS | + CAIRO_XCB_RENDER_HAS_FILL_RECTANGLES | + CAIRO_XCB_RENDER_HAS_COMPOSITE_TRAPEZOIDS | + CAIRO_XCB_RENDER_HAS_PICTURE_TRANSFORM | + CAIRO_XCB_RENDER_HAS_FILTERS | + CAIRO_XCB_RENDER_HAS_PDF_OPERATORS | + CAIRO_XCB_RENDER_HAS_EXTENDED_REPEAT | + CAIRO_XCB_RENDER_HAS_GRADIENTS); + } else { + xcb_render_query_version_reply_t version; + + version.major_version = major_version; + version.minor_version = minor_version; + + if (! XCB_RENDER_HAS_FILL_RECTANGLES (&version)) + connection->flags &= ~CAIRO_XCB_RENDER_HAS_FILL_RECTANGLES; + + if (! XCB_RENDER_HAS_TRAPEZOIDS (&version)) + connection->flags &= ~CAIRO_XCB_RENDER_HAS_COMPOSITE_TRAPEZOIDS; + + if (! XCB_RENDER_HAS_PICTURE_TRANSFORM (&version)) + connection->flags &= ~CAIRO_XCB_RENDER_HAS_PICTURE_TRANSFORM; + + if (! XCB_RENDER_HAS_FILTERS (&version)) + connection->flags &= ~CAIRO_XCB_RENDER_HAS_FILTERS; + + if (! XCB_RENDER_HAS_PDF_OPERATORS (&version)) + connection->flags &= ~CAIRO_XCB_RENDER_HAS_PDF_OPERATORS; + + if (! XCB_RENDER_HAS_EXTENDED_REPEAT (&version)) + connection->flags &= ~CAIRO_XCB_RENDER_HAS_EXTENDED_REPEAT; + + if (! XCB_RENDER_HAS_GRADIENTS (&version)) + connection->flags &= ~CAIRO_XCB_RENDER_HAS_GRADIENTS; + } +} + +#if 0 +void +cairo_xcb_device_debug_cap_xcairo_version (cairo_device_t *device, + int major_version, + int minor_version) +{ + cairo_xcb_connection_t *connection = (cairo_xcb_connection_t *) device; + cairo_status_t status; + + if (device->backend->type != CAIRO_DEVICE_TYPE_XCB) { + status = _cairo_device_set_error (device, CAIRO_STATUS_DEVICE_TYPE_MISMATCH); + return; + } + + /* clear any flags that are inappropriate for the desired version */ + if (major_version < 0 && minor_version < 0) { + connection->flags &= ~(CAIRO_XCB_HAS_CAIRO); + } +} +#endif diff --git a/src/cairo-xcb-private.h b/src/cairo-xcb-private.h new file mode 100644 index 00000000..18a485cf --- /dev/null +++ b/src/cairo-xcb-private.h @@ -0,0 +1,760 @@ +/* Cairo - a vector graphics library with display and print output + * + * Copyright © 2005 Red Hat, Inc. + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 Red Hat, Inc. + * + * Contributors(s): + * Chris Wilson <chris@chris-wilson.co.uk> + */ + +#ifndef CAIRO_XCB_PRIVATE_H +#define CAIRO_XCB_PRIVATE_H + +#include "cairo-xcb.h" + +#include "cairo-cache-private.h" +#include "cairo-compiler-private.h" +#include "cairo-device-private.h" +#include "cairo-error-private.h" +#include "cairo-freelist-private.h" +#include "cairo-list-private.h" +#include "cairo-mutex-private.h" +#include "cairo-reference-count-private.h" +#include "cairo-spans-private.h" +#include "cairo-surface-private.h" + +#include <xcb/xcb.h> +#include <xcb/render.h> +#include <xcb/xcbext.h> +#include <pixman.h> + +typedef struct _cairo_xcb_connection cairo_xcb_connection_t; +typedef struct _cairo_xcb_font cairo_xcb_font_t; +typedef struct _cairo_xcb_screen cairo_xcb_screen_t; +typedef struct _cairo_xcb_surface cairo_xcb_surface_t; +typedef struct _cairo_xcb_shm_mem_pool cairo_xcb_shm_mem_pool_t; +typedef struct _cairo_xcb_shm_info cairo_xcb_shm_info_t; + +struct _cairo_xcb_shm_info { + cairo_xcb_connection_t *connection; + uint32_t shm; + uint32_t offset; + uint64_t seqno; + void *mem; + cairo_xcb_shm_mem_pool_t *pool; +}; + +struct _cairo_xcb_surface { + cairo_surface_t base; + cairo_surface_t *fallback; + + cairo_xcb_connection_t *connection; + cairo_xcb_screen_t *screen; + + cairo_surface_t *drm; + cairo_bool_t marked_dirty; + + xcb_drawable_t drawable; + cairo_bool_t owns_pixmap; + int use_pixmap; + + int width; + int height; + int depth; + + unsigned int flags; + xcb_render_picture_t picture; + xcb_render_pictformat_t xrender_format; + pixman_format_code_t pixman_format; + + cairo_list_t link; +}; + +#if CAIRO_HAS_XLIB_XCB_FUNCTIONS +typedef struct _cairo_xlib_xcb_surface { + cairo_surface_t base; + + cairo_xcb_surface_t *xcb; + + /* original settings for query */ + void *display; + void *screen; + void *visual; + void *format; +} cairo_xlib_xcb_surface_t; +#endif + + +enum { + GLYPHSET_INDEX_ARGB32, + GLYPHSET_INDEX_A8, + GLYPHSET_INDEX_A1, + NUM_GLYPHSETS +}; + +typedef struct _cairo_xcb_font_glyphset_free_glyphs { + xcb_render_glyphset_t glyphset; + int glyph_count; + xcb_render_glyph_t glyph_indices[128]; +} cairo_xcb_font_glyphset_free_glyphs_t; + +typedef struct _cairo_xcb_font_glyphset_info { + xcb_render_glyphset_t glyphset; + cairo_format_t format; + xcb_render_pictformat_t xrender_format; + cairo_xcb_font_glyphset_free_glyphs_t *pending_free_glyphs; +} cairo_xcb_font_glyphset_info_t; + +struct _cairo_xcb_font { + cairo_scaled_font_t *scaled_font; + cairo_xcb_connection_t *connection; + cairo_xcb_font_glyphset_info_t glyphset_info[NUM_GLYPHSETS]; + cairo_list_t link; +}; + +struct _cairo_xcb_screen { + cairo_xcb_connection_t *connection; + + xcb_screen_t *xcb_screen; + cairo_device_t *device; + + xcb_gcontext_t gc[4]; + int gc_depths; /* 4 x uint8_t */ + + cairo_surface_t *stock_colors[CAIRO_STOCK_NUM_COLORS]; + struct { + cairo_surface_t *picture; + cairo_color_t color; + } solid_cache[16]; + int solid_cache_size; + + cairo_cache_t surface_pattern_cache; + cairo_cache_t linear_pattern_cache; + cairo_cache_t radial_pattern_cache; + cairo_freelist_t pattern_cache_entry_freelist; + + cairo_list_t link; + cairo_list_t surfaces; +}; + +struct _cairo_xcb_connection { + cairo_device_t device; + + xcb_connection_t *xcb_connection; + cairo_bool_t has_socket; + + xcb_render_pictformat_t standard_formats[5]; + cairo_hash_table_t *xrender_formats; + cairo_hash_table_t *visual_to_xrender_format; + + unsigned int maximum_request_length; + unsigned int flags; + + const xcb_setup_t *root; + const xcb_query_extension_reply_t *render; + const xcb_query_extension_reply_t *shm; + const xcb_query_extension_reply_t *dri2; + uint64_t seqno; + + cairo_list_t free_xids; + cairo_freepool_t xid_pool; + + cairo_mutex_t shm_mutex; + cairo_list_t shm_pools; + cairo_freepool_t shm_info_freelist; + + cairo_mutex_t screens_mutex; + cairo_list_t screens; + + cairo_list_t fonts; + + cairo_list_t link; +}; + +enum { + CAIRO_XCB_HAS_RENDER = 0x0001, + CAIRO_XCB_RENDER_HAS_FILL_RECTANGLES = 0x0002, + CAIRO_XCB_RENDER_HAS_COMPOSITE = 0x0004, + CAIRO_XCB_RENDER_HAS_COMPOSITE_TRAPEZOIDS = 0x0008, + CAIRO_XCB_RENDER_HAS_COMPOSITE_SPANS = 0x0010, + CAIRO_XCB_RENDER_HAS_COMPOSITE_GLYPHS = 0x0020, + CAIRO_XCB_RENDER_HAS_PICTURE_TRANSFORM = 0x0040, + CAIRO_XCB_RENDER_HAS_FILTERS = 0x0080, + CAIRO_XCB_RENDER_HAS_PDF_OPERATORS = 0x0100, + CAIRO_XCB_RENDER_HAS_EXTENDED_REPEAT = 0x0200, + CAIRO_XCB_RENDER_HAS_GRADIENTS = 0x0400, + + CAIRO_XCB_HAS_CAIRO = 0x10000, + + CAIRO_XCB_HAS_DRI2 = 0x40000000, + CAIRO_XCB_HAS_SHM = 0x80000000 +}; + +#define CAIRO_XCB_SHM_SMALL_IMAGE 8192 + +cairo_private extern const cairo_surface_backend_t _cairo_xcb_surface_backend; + +cairo_private cairo_xcb_connection_t * +_cairo_xcb_connection_get (xcb_connection_t *connection); + +static inline cairo_xcb_connection_t * +_cairo_xcb_connection_reference (cairo_xcb_connection_t *connection) +{ + return (cairo_xcb_connection_t *) cairo_device_reference (&connection->device); +} + +cairo_private xcb_render_pictformat_t +_cairo_xcb_connection_get_xrender_format (cairo_xcb_connection_t *connection, + pixman_format_code_t pixman_format); + +cairo_private xcb_render_pictformat_t +_cairo_xcb_connection_get_xrender_format_for_visual (cairo_xcb_connection_t *connection, + const xcb_visualid_t visual); + +static inline cairo_status_t cairo_warn +_cairo_xcb_connection_acquire (cairo_xcb_connection_t *connection) +{ + return cairo_device_acquire (&connection->device); +} + +cairo_private cairo_status_t +_cairo_xcb_connection_take_socket (cairo_xcb_connection_t *connection); + +cairo_private uint32_t +_cairo_xcb_connection_get_xid (cairo_xcb_connection_t *connection); + +cairo_private void +_cairo_xcb_connection_put_xid (cairo_xcb_connection_t *connection, + uint32_t xid); + +static inline void +_cairo_xcb_connection_release (cairo_xcb_connection_t *connection) +{ + cairo_device_release (&connection->device); +} + +static inline void +_cairo_xcb_connection_destroy (cairo_xcb_connection_t *connection) +{ + cairo_device_destroy (&connection->device); +} + +cairo_private cairo_int_status_t +_cairo_xcb_connection_allocate_shm_info (cairo_xcb_connection_t *display, + size_t size, + cairo_xcb_shm_info_t **shm_info_out); + +cairo_private void +_cairo_xcb_shm_info_destroy (cairo_xcb_shm_info_t *shm_info); + +cairo_private void +_cairo_xcb_connection_shm_mem_pools_fini (cairo_xcb_connection_t *connection); + +cairo_private void +_cairo_xcb_font_finish (cairo_xcb_font_t *font); + +cairo_private cairo_xcb_screen_t * +_cairo_xcb_screen_get (xcb_connection_t *connection, + xcb_screen_t *screen); + +cairo_private void +_cairo_xcb_screen_finish (cairo_xcb_screen_t *screen); + +cairo_private xcb_gcontext_t +_cairo_xcb_screen_get_gc (cairo_xcb_screen_t *screen, + xcb_drawable_t drawable, + int depth); + +cairo_private void +_cairo_xcb_screen_put_gc (cairo_xcb_screen_t *screen, int depth, xcb_gcontext_t gc); + +cairo_private cairo_status_t +_cairo_xcb_screen_store_surface_picture (cairo_xcb_screen_t *screen, + cairo_surface_t *picture, + unsigned int size); +cairo_private void +_cairo_xcb_screen_remove_surface_picture (cairo_xcb_screen_t *screen, + cairo_surface_t *picture); + +cairo_private cairo_status_t +_cairo_xcb_screen_store_linear_picture (cairo_xcb_screen_t *screen, + const cairo_linear_pattern_t *linear, + cairo_surface_t *picture); + +cairo_private cairo_surface_t * +_cairo_xcb_screen_lookup_linear_picture (cairo_xcb_screen_t *screen, + const cairo_linear_pattern_t *linear); + +cairo_private cairo_status_t +_cairo_xcb_screen_store_radial_picture (cairo_xcb_screen_t *screen, + const cairo_radial_pattern_t *radial, + cairo_surface_t *picture); + +cairo_private cairo_surface_t * +_cairo_xcb_screen_lookup_radial_picture (cairo_xcb_screen_t *screen, + const cairo_radial_pattern_t *radial); + +cairo_private cairo_surface_t * +_cairo_xcb_surface_create_similar_image (cairo_xcb_surface_t *other, + cairo_content_t content, + int width, int height); + +cairo_private cairo_surface_t * +_cairo_xcb_surface_create_similar (void *abstract_other, + cairo_content_t content, + int width, + int height); + +cairo_private 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_private cairo_int_status_t +_cairo_xcb_surface_cairo_paint (cairo_xcb_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_clip_t *clip); + +cairo_private cairo_int_status_t +_cairo_xcb_surface_cairo_mask (cairo_xcb_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_pattern_t *mask, + cairo_clip_t *clip); + +cairo_private cairo_int_status_t +_cairo_xcb_surface_cairo_stroke (cairo_xcb_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + 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_clip_t *clip); + +cairo_private cairo_int_status_t +_cairo_xcb_surface_cairo_fill (cairo_xcb_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias, + cairo_clip_t *clip); + +cairo_private cairo_int_status_t +_cairo_xcb_surface_cairo_glyphs (cairo_xcb_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_scaled_font_t *scaled_font, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_clip_t *clip); + +cairo_private cairo_int_status_t +_cairo_xcb_surface_render_paint (cairo_xcb_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_clip_t *clip); + +cairo_private cairo_int_status_t +_cairo_xcb_surface_render_mask (cairo_xcb_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_pattern_t *mask, + cairo_clip_t *clip); + +cairo_private cairo_int_status_t +_cairo_xcb_surface_render_stroke (cairo_xcb_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + 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_clip_t *clip); + +cairo_private cairo_int_status_t +_cairo_xcb_surface_render_fill (cairo_xcb_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias, + cairo_clip_t *clip); + +cairo_private cairo_int_status_t +_cairo_xcb_surface_render_glyphs (cairo_xcb_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_scaled_font_t *scaled_font, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_clip_t *clip); +cairo_private void +_cairo_xcb_surface_scaled_font_fini (cairo_scaled_font_t *scaled_font); + +cairo_private void +_cairo_xcb_surface_scaled_glyph_fini (cairo_scaled_glyph_t *scaled_glyph, + cairo_scaled_font_t *scaled_font); + +cairo_private cairo_status_t +_cairo_xcb_surface_core_copy_boxes (cairo_xcb_surface_t *dst, + const cairo_pattern_t *src_pattern, + const cairo_rectangle_int_t *extents, + const cairo_boxes_t *boxes); + +cairo_private cairo_status_t +_cairo_xcb_surface_core_fill_boxes (cairo_xcb_surface_t *dst, + const cairo_color_t *color, + cairo_boxes_t *boxes); + +static inline void +_cairo_xcb_connection_write (cairo_xcb_connection_t *connection, + struct iovec *vec, + int count) +{ + if (unlikely (connection->device.status)) + return; + + connection->seqno++; + if (unlikely (! xcb_writev (connection->xcb_connection, vec, count, 1))) + connection->device.status = _cairo_error (CAIRO_STATUS_WRITE_ERROR); +} + +cairo_private xcb_pixmap_t +_cairo_xcb_connection_create_pixmap (cairo_xcb_connection_t *connection, + uint8_t depth, + xcb_drawable_t drawable, + uint16_t width, + uint16_t height); + +cairo_private void +_cairo_xcb_connection_free_pixmap (cairo_xcb_connection_t *connection, + xcb_pixmap_t pixmap); + +cairo_private xcb_gcontext_t +_cairo_xcb_connection_create_gc (cairo_xcb_connection_t *connection, + xcb_drawable_t drawable, + uint32_t value_mask, + uint32_t *values); + +cairo_private void +_cairo_xcb_connection_free_gc (cairo_xcb_connection_t *connection, + xcb_gcontext_t gc); + +cairo_private void +_cairo_xcb_connection_change_gc (cairo_xcb_connection_t *connection, + xcb_gcontext_t gc, + uint32_t value_mask, + uint32_t *values); + +cairo_private void +_cairo_xcb_connection_copy_area (cairo_xcb_connection_t *connection, + xcb_drawable_t src, + xcb_drawable_t dst, + xcb_gcontext_t gc, + int16_t src_x, + int16_t src_y, + int16_t dst_x, + int16_t dst_y, + uint16_t width, + uint16_t height); + +cairo_private void +_cairo_xcb_connection_put_image (cairo_xcb_connection_t *connection, + xcb_drawable_t dst, + xcb_gcontext_t gc, + uint16_t width, + uint16_t height, + int16_t dst_x, + int16_t dst_y, + uint8_t depth, + uint32_t length, + void *data); + +cairo_private void +_cairo_xcb_connection_put_subimage (cairo_xcb_connection_t *connection, + xcb_drawable_t dst, + xcb_gcontext_t gc, + int16_t src_x, + int16_t src_y, + uint16_t width, + uint16_t height, + uint16_t cpp, + uint16_t stride, + int16_t dst_x, + int16_t dst_y, + uint8_t depth, + void *data); + +cairo_private cairo_status_t +_cairo_xcb_connection_get_image (cairo_xcb_connection_t *connection, + xcb_drawable_t src, + int16_t src_x, + int16_t src_y, + uint16_t width, + uint16_t height, + xcb_get_image_reply_t **reply); + +cairo_private void +_cairo_xcb_connection_poly_fill_rectangle (cairo_xcb_connection_t *connection, + xcb_drawable_t dst, + xcb_gcontext_t gc, + uint32_t num_rectangles, + xcb_rectangle_t *rectangles); + +cairo_private uint32_t +_cairo_xcb_connection_shm_attach (cairo_xcb_connection_t *connection, + uint32_t id, + cairo_bool_t readonly); + +cairo_private uint64_t +_cairo_xcb_connection_shm_put_image (cairo_xcb_connection_t *connection, + xcb_drawable_t dst, + xcb_gcontext_t gc, + uint16_t total_width, + uint16_t total_height, + int16_t src_x, + int16_t src_y, + uint16_t width, + uint16_t height, + int16_t dst_x, + int16_t dst_y, + uint8_t depth, + uint32_t shm, + uint32_t offset); + +cairo_private cairo_status_t +_cairo_xcb_connection_shm_get_image (cairo_xcb_connection_t *connection, + xcb_drawable_t src, + int16_t src_x, + int16_t src_y, + uint16_t width, + uint16_t height, + uint32_t shmseg, + uint32_t offset); + +cairo_private void +_cairo_xcb_connection_shm_detach (cairo_xcb_connection_t *connection, + uint32_t segment); + +cairo_private void +_cairo_xcb_connection_render_spans (cairo_xcb_connection_t *connection, + xcb_render_picture_t dst, + int op, + xcb_render_picture_t src, + int16_t src_x, int16_t src_y, + int16_t dst_x, int16_t dst_y, + int16_t width, int16_t height, + unsigned int length, + uint16_t *spans); +cairo_private void +_cairo_xcb_connection_render_create_picture (cairo_xcb_connection_t *connection, + xcb_render_picture_t picture, + xcb_drawable_t drawable, + xcb_render_pictformat_t format, + uint32_t value_mask, + uint32_t *value_list); + +cairo_private void +_cairo_xcb_connection_render_change_picture (cairo_xcb_connection_t *connection, + xcb_render_picture_t picture, + uint32_t value_mask, + uint32_t *value_list); + +cairo_private void +_cairo_xcb_connection_render_set_picture_clip_rectangles (cairo_xcb_connection_t *connection, + xcb_render_picture_t picture, + int16_t clip_x_origin, + int16_t clip_y_origin, + uint32_t rectangles_len, + xcb_rectangle_t *rectangles); + +cairo_private void +_cairo_xcb_connection_render_free_picture (cairo_xcb_connection_t *connection, + xcb_render_picture_t picture); + +cairo_private void +_cairo_xcb_connection_render_composite (cairo_xcb_connection_t *connection, + uint8_t op, + xcb_render_picture_t src, + xcb_render_picture_t mask, + xcb_render_picture_t dst, + int16_t src_x, + int16_t src_y, + int16_t mask_x, + int16_t mask_y, + int16_t dst_x, + int16_t dst_y, + uint16_t width, + uint16_t height); + +cairo_private void +_cairo_xcb_connection_render_trapezoids (cairo_xcb_connection_t *connection, + uint8_t op, + xcb_render_picture_t src, + xcb_render_picture_t dst, + xcb_render_pictformat_t mask_format, + int16_t src_x, + int16_t src_y, + uint32_t traps_len, + xcb_render_trapezoid_t *traps); + +cairo_private void +_cairo_xcb_connection_render_create_glyph_set (cairo_xcb_connection_t *connection, + xcb_render_glyphset_t id, + xcb_render_pictformat_t format); + +cairo_private void +_cairo_xcb_connection_render_free_glyph_set (cairo_xcb_connection_t *connection, + xcb_render_glyphset_t glyphset); + +cairo_private void +_cairo_xcb_connection_render_add_glyphs (cairo_xcb_connection_t *connection, + xcb_render_glyphset_t glyphset, + uint32_t num_glyphs, + uint32_t *glyphs_id, + xcb_render_glyphinfo_t *glyphs, + uint32_t data_len, + uint8_t *data); + +cairo_private void +_cairo_xcb_connection_render_free_glyphs (cairo_xcb_connection_t *connection, + xcb_render_glyphset_t glyphset, + uint32_t num_glyphs, + xcb_render_glyph_t *glyphs); + +cairo_private void +_cairo_xcb_connection_render_composite_glyphs_8 (cairo_xcb_connection_t *connection, + uint8_t op, + xcb_render_picture_t src, + xcb_render_picture_t dst, + xcb_render_pictformat_t mask_format, + xcb_render_glyphset_t glyphset, + int16_t src_x, + int16_t src_y, + uint32_t glyphcmds_len, + uint8_t *glyphcmds); + +cairo_private void +_cairo_xcb_connection_render_composite_glyphs_16 (cairo_xcb_connection_t *connection, + uint8_t op, + xcb_render_picture_t src, + xcb_render_picture_t dst, + xcb_render_pictformat_t mask_format, + xcb_render_glyphset_t glyphset, + int16_t src_x, + int16_t src_y, + uint32_t glyphcmds_len, + uint8_t *glyphcmds); + +cairo_private void +_cairo_xcb_connection_render_composite_glyphs_32 (cairo_xcb_connection_t *connection, + uint8_t op, + xcb_render_picture_t src, + xcb_render_picture_t dst, + xcb_render_pictformat_t mask_format, + xcb_render_glyphset_t glyphset, + int16_t src_x, + int16_t src_y, + uint32_t glyphcmds_len, + uint8_t *glyphcmds); + +cairo_private void +_cairo_xcb_connection_render_fill_rectangles (cairo_xcb_connection_t *connection, + uint8_t op, + xcb_render_picture_t dst, + xcb_render_color_t color, + uint32_t num_rects, + xcb_rectangle_t *rects); + +cairo_private void +_cairo_xcb_connection_render_set_picture_transform (cairo_xcb_connection_t *connection, + xcb_render_picture_t picture, + xcb_render_transform_t *transform); + +cairo_private void +_cairo_xcb_connection_render_set_picture_filter (cairo_xcb_connection_t *connection, + xcb_render_picture_t picture, + uint16_t filter_len, + char *filter); + +cairo_private void +_cairo_xcb_connection_render_create_solid_fill (cairo_xcb_connection_t *connection, + xcb_render_picture_t picture, + xcb_render_color_t color); + +cairo_private void +_cairo_xcb_connection_render_create_linear_gradient (cairo_xcb_connection_t *connection, + xcb_render_picture_t picture, + xcb_render_pointfix_t p1, + xcb_render_pointfix_t p2, + uint32_t num_stops, + xcb_render_fixed_t *stops, + xcb_render_color_t *colors); + +cairo_private void +_cairo_xcb_connection_render_create_radial_gradient (cairo_xcb_connection_t *connection, + xcb_render_picture_t picture, + xcb_render_pointfix_t inner, + xcb_render_pointfix_t outer, + xcb_render_fixed_t inner_radius, + xcb_render_fixed_t outer_radius, + uint32_t num_stops, + xcb_render_fixed_t *stops, + xcb_render_color_t *colors); + +cairo_private void +_cairo_xcb_connection_render_create_conical_gradient (cairo_xcb_connection_t *c, + xcb_render_picture_t picture, + xcb_render_pointfix_t center, + xcb_render_fixed_t angle, + uint32_t num_stops, + xcb_render_fixed_t *stops, + xcb_render_color_t *colors); +#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); +slim_hidden_proto (cairo_xcb_surface_set_size); +#endif + +#endif /* CAIRO_XCB_PRIVATE_H */ diff --git a/src/cairo-xcb-screen.c b/src/cairo-xcb-screen.c new file mode 100644 index 00000000..06c07a55 --- /dev/null +++ b/src/cairo-xcb-screen.c @@ -0,0 +1,518 @@ +/* Cairo - a vector graphics library with display and print output + * + * Copyright © 2008 Chris Wilson + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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. + * + * Authors: + * Chris Wilson <chris@chris-wilson.co.uk> + */ + +#include "cairoint.h" + +#include "cairo-xcb-private.h" + +struct pattern_cache_entry { + cairo_cache_entry_t key; + cairo_xcb_screen_t *screen; + cairo_pattern_union_t pattern; + cairo_surface_t *picture; +}; + +void +_cairo_xcb_screen_finish (cairo_xcb_screen_t *screen) +{ + int i; + + CAIRO_MUTEX_LOCK (screen->connection->screens_mutex); + cairo_list_del (&screen->link); + CAIRO_MUTEX_UNLOCK (screen->connection->screens_mutex); + + while (! cairo_list_is_empty (&screen->surfaces)) { + cairo_surface_t *surface; + + surface = &cairo_list_first_entry (&screen->surfaces, + cairo_xcb_surface_t, + link)->base; + + cairo_surface_reference (surface); + cairo_surface_finish (surface); + cairo_surface_destroy (surface); + } + + for (i = 0; i < screen->solid_cache_size; i++) + cairo_surface_destroy (screen->solid_cache[i].picture); + + for (i = 0; i < ARRAY_LENGTH (screen->stock_colors); i++) + cairo_surface_destroy (screen->stock_colors[i]); + + _cairo_cache_fini (&screen->surface_pattern_cache); + _cairo_cache_fini (&screen->linear_pattern_cache); + _cairo_cache_fini (&screen->radial_pattern_cache); + _cairo_freelist_fini (&screen->pattern_cache_entry_freelist); + + cairo_device_finish (screen->device); + cairo_device_destroy (screen->device); + + free (screen); +} + +static cairo_bool_t +_surface_pattern_cache_entry_equal (const void *A, const void *B) +{ + const struct pattern_cache_entry *a = A, *b = B; + + return a->key.hash == b->key.hash; +} + +static cairo_bool_t +_linear_pattern_cache_entry_equal (const void *A, const void *B) +{ + const struct pattern_cache_entry *a = A, *b = B; + + if (a->key.hash != b->key.hash) + return FALSE; + + return _cairo_linear_pattern_equal (&a->pattern.gradient.linear, + &b->pattern.gradient.linear); +} + +static cairo_bool_t +_radial_pattern_cache_entry_equal (const void *A, const void *B) +{ + const struct pattern_cache_entry *a = A, *b = B; + + if (a->key.hash != b->key.hash) + return FALSE; + + return _cairo_radial_pattern_equal (&a->pattern.gradient.radial, + &b->pattern.gradient.radial); +} + +static void +_surface_cache_entry_destroy (void *closure) +{ + struct pattern_cache_entry *entry = closure; + + cairo_surface_finish (entry->picture); + cairo_surface_destroy (entry->picture); + _cairo_freelist_free (&entry->screen->pattern_cache_entry_freelist, entry); +} + +static void +_pattern_cache_entry_destroy (void *closure) +{ + struct pattern_cache_entry *entry = closure; + + _cairo_pattern_fini (&entry->pattern.base); + cairo_surface_destroy (entry->picture); + _cairo_freelist_free (&entry->screen->pattern_cache_entry_freelist, entry); +} + +#if CAIRO_HAS_DRM_SURFACE && CAIRO_HAS_XCB_DRM_FUNCTIONS +#include "drm/cairo-drm-private.h" + +#include <drm/drm.h> +#include <sys/ioctl.h> +#include <xcb/dri2.h> +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> + +static int drm_magic (int fd, uint32_t *magic) +{ + drm_auth_t auth; + + if (ioctl (fd, DRM_IOCTL_GET_MAGIC, &auth)) + return -errno; + + *magic = auth.magic; + return 0; +} + +static cairo_device_t * +_xcb_drm_device (xcb_connection_t *xcb_connection, + xcb_screen_t *xcb_screen) +{ + cairo_device_t *device = NULL; + xcb_dri2_connect_reply_t *connect; + drm_magic_t magic; + int fd; + + connect = xcb_dri2_connect_reply (xcb_connection, + xcb_dri2_connect (xcb_connection, + xcb_screen->root, + 0), + 0); + if (connect == NULL) + return NULL; + + fd = open (xcb_dri2_connect_device_name (connect), O_RDWR); + free (connect); + + if (fd < 0) + return NULL; + + device = cairo_drm_device_get_for_fd (fd); + close (fd); + + if (device != NULL) { + xcb_dri2_authenticate_reply_t *authenticate; + + if (drm_magic (((cairo_drm_device_t *) device)->fd, &magic) < 0) { + cairo_device_destroy (device); + return NULL; + } + + authenticate = xcb_dri2_authenticate_reply (xcb_connection, + xcb_dri2_authenticate (xcb_connection, + xcb_screen->root, + magic), + 0); + if (authenticate == NULL) { + cairo_device_destroy (device); + return NULL; + } + + free (authenticate); + } + + return device; +} +#else +static cairo_device_t * +_xcb_drm_device (xcb_connection_t *xcb_connection, + xcb_screen_t *xcb_screen) +{ + return NULL; +} +#endif + +cairo_xcb_screen_t * +_cairo_xcb_screen_get (xcb_connection_t *xcb_connection, + xcb_screen_t *xcb_screen) +{ + cairo_xcb_connection_t *connection; + cairo_xcb_screen_t *screen; + cairo_status_t status; + int i; + + connection = _cairo_xcb_connection_get (xcb_connection); + if (unlikely (connection == NULL)) + return NULL; + + CAIRO_MUTEX_LOCK (connection->screens_mutex); + + cairo_list_foreach_entry (screen, + cairo_xcb_screen_t, + &connection->screens, + link) + { + if (screen->xcb_screen == xcb_screen) { + /* Maintain list in MRU order */ + if (&screen->link != connection->screens.next) + cairo_list_move (&screen->link, &connection->screens); + + goto unlock; + } + } + + screen = malloc (sizeof (cairo_xcb_screen_t)); + if (unlikely (screen == NULL)) + goto unlock; + + screen->connection = connection; + screen->xcb_screen = xcb_screen; + + if (connection->flags & CAIRO_XCB_HAS_DRI2) + screen->device = _xcb_drm_device (xcb_connection, xcb_screen); + else + screen->device = NULL; + + screen->gc_depths = 0; + memset (screen->gc, 0, sizeof (screen->gc)); + + screen->solid_cache_size = 0; + for (i = 0; i < ARRAY_LENGTH (screen->stock_colors); i++) + screen->stock_colors[i] = NULL; + + status = _cairo_cache_init (&screen->surface_pattern_cache, + _surface_pattern_cache_entry_equal, + NULL, + _surface_cache_entry_destroy, + 16*1024*1024); + if (unlikely (status)) + goto error_screen; + + status = _cairo_cache_init (&screen->linear_pattern_cache, + _linear_pattern_cache_entry_equal, + NULL, + _pattern_cache_entry_destroy, + 16); + if (unlikely (status)) + goto error_surface; + + status = _cairo_cache_init (&screen->radial_pattern_cache, + _radial_pattern_cache_entry_equal, + NULL, + _pattern_cache_entry_destroy, + 4); + if (unlikely (status)) + goto error_linear; + + _cairo_freelist_init (&screen->pattern_cache_entry_freelist, + sizeof (struct pattern_cache_entry)); + + cairo_list_add (&screen->link, &connection->screens); + cairo_list_init (&screen->surfaces); + +unlock: + CAIRO_MUTEX_UNLOCK (connection->screens_mutex); + + return screen; + +error_surface: + _cairo_cache_fini (&screen->surface_pattern_cache); +error_linear: + _cairo_cache_fini (&screen->linear_pattern_cache); +error_screen: + cairo_device_destroy (screen->device); + free (screen); + CAIRO_MUTEX_UNLOCK (connection->screens_mutex); + + return NULL; +} + +static xcb_gcontext_t +_create_gc (cairo_xcb_screen_t *screen, + xcb_drawable_t drawable) +{ + uint32_t values[] = { 0 }; + + return _cairo_xcb_connection_create_gc (screen->connection, drawable, + XCB_GC_GRAPHICS_EXPOSURES, + values); +} + +xcb_gcontext_t +_cairo_xcb_screen_get_gc (cairo_xcb_screen_t *screen, + xcb_drawable_t drawable, + int depth) +{ + int i; + + assert (CAIRO_MUTEX_IS_LOCKED (screen->connection->mutex)); + + for (i = 0; i < ARRAY_LENGTH (screen->gc); i++) { + if (((screen->gc_depths >> (8*i)) & 0xff) == depth) { + screen->gc_depths &= ~(0xff << (8*i)); + return screen->gc[i]; + } + } + + return _create_gc (screen, drawable); +} + +void +_cairo_xcb_screen_put_gc (cairo_xcb_screen_t *screen, int depth, xcb_gcontext_t gc) +{ + int i; + + assert (CAIRO_MUTEX_IS_LOCKED (screen->connection->mutex)); + + for (i = 0; i < ARRAY_LENGTH (screen->gc); i++) { + if (((screen->gc_depths >> (8*i)) & 0xff) == 0) + break; + } + + if (i == ARRAY_LENGTH (screen->gc)) { + /* perform random substitution to ensure fair caching over depths */ + i = rand () % ARRAY_LENGTH (screen->gc); + _cairo_xcb_connection_free_gc (screen->connection, screen->gc[i]); + } + + screen->gc[i] = gc; + screen->gc_depths &= ~(0xff << (8*i)); + screen->gc_depths |= depth << (8*i); +} + +cairo_status_t +_cairo_xcb_screen_store_surface_picture (cairo_xcb_screen_t *screen, + cairo_surface_t *picture, + unsigned int size) +{ + struct pattern_cache_entry *entry; + cairo_status_t status; + + assert (CAIRO_MUTEX_IS_LOCKED (screen->connection->mutex)); + + entry = _cairo_freelist_alloc (&screen->pattern_cache_entry_freelist); + if (unlikely (entry == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + entry->key.hash = picture->unique_id; + entry->key.size = size; + + entry->picture = cairo_surface_reference (picture); + entry->screen = screen; + + status = _cairo_cache_insert (&screen->surface_pattern_cache, + &entry->key); + if (unlikely (status)) { + _cairo_freelist_free (&screen->pattern_cache_entry_freelist, entry); + return status; + } + + return CAIRO_STATUS_SUCCESS; +} + +void +_cairo_xcb_screen_remove_surface_picture (cairo_xcb_screen_t *screen, + cairo_surface_t *picture) +{ + struct pattern_cache_entry tmpl; + struct pattern_cache_entry *entry; + + assert (CAIRO_MUTEX_IS_LOCKED (screen->connection->mutex)); + + tmpl.key.hash = picture->unique_id; + + entry = _cairo_cache_lookup (&screen->surface_pattern_cache, &tmpl.key); + if (entry != NULL) + _cairo_cache_remove (&screen->surface_pattern_cache, &entry->key); +} + +cairo_status_t +_cairo_xcb_screen_store_linear_picture (cairo_xcb_screen_t *screen, + const cairo_linear_pattern_t *linear, + cairo_surface_t *picture) +{ + struct pattern_cache_entry *entry; + cairo_status_t status; + + assert (CAIRO_MUTEX_IS_LOCKED (screen->connection->mutex)); + + entry = _cairo_freelist_alloc (&screen->pattern_cache_entry_freelist); + if (unlikely (entry == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + entry->key.hash = _cairo_linear_pattern_hash (_CAIRO_HASH_INIT_VALUE, linear); + entry->key.size = 1; + + status = _cairo_pattern_init_copy (&entry->pattern.base, &linear->base.base); + if (unlikely (status)) { + _cairo_freelist_free (&screen->pattern_cache_entry_freelist, entry); + return status; + } + + entry->picture = cairo_surface_reference (picture); + entry->screen = screen; + + status = _cairo_cache_insert (&screen->linear_pattern_cache, + &entry->key); + if (unlikely (status)) { + cairo_surface_destroy (picture); + _cairo_freelist_free (&screen->pattern_cache_entry_freelist, entry); + return status; + } + + return CAIRO_STATUS_SUCCESS; +} + +cairo_surface_t * +_cairo_xcb_screen_lookup_linear_picture (cairo_xcb_screen_t *screen, + const cairo_linear_pattern_t *linear) +{ + cairo_surface_t *picture = NULL; + struct pattern_cache_entry tmpl; + struct pattern_cache_entry *entry; + + assert (CAIRO_MUTEX_IS_LOCKED (screen->connection->mutex)); + + tmpl.key.hash = _cairo_linear_pattern_hash (_CAIRO_HASH_INIT_VALUE, linear); + _cairo_pattern_init_static_copy (&tmpl.pattern.base, &linear->base.base); + + entry = _cairo_cache_lookup (&screen->linear_pattern_cache, &tmpl.key); + if (entry != NULL) + picture = cairo_surface_reference (entry->picture); + + return picture; +} + +cairo_status_t +_cairo_xcb_screen_store_radial_picture (cairo_xcb_screen_t *screen, + const cairo_radial_pattern_t *radial, + cairo_surface_t *picture) +{ + struct pattern_cache_entry *entry; + cairo_status_t status; + + assert (CAIRO_MUTEX_IS_LOCKED (screen->connection->mutex)); + + entry = _cairo_freelist_alloc (&screen->pattern_cache_entry_freelist); + if (unlikely (entry == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + entry->key.hash = _cairo_radial_pattern_hash (_CAIRO_HASH_INIT_VALUE, radial); + entry->key.size = 1; + + status = _cairo_pattern_init_copy (&entry->pattern.base, &radial->base.base); + if (unlikely (status)) { + _cairo_freelist_free (&screen->pattern_cache_entry_freelist, entry); + return status; + } + + entry->picture = cairo_surface_reference (picture); + entry->screen = screen; + + status = _cairo_cache_insert (&screen->radial_pattern_cache, &entry->key); + if (unlikely (status)) { + cairo_surface_destroy (picture); + _cairo_freelist_free (&screen->pattern_cache_entry_freelist, entry); + return status; + } + + return CAIRO_STATUS_SUCCESS; +} + +cairo_surface_t * +_cairo_xcb_screen_lookup_radial_picture (cairo_xcb_screen_t *screen, + const cairo_radial_pattern_t *radial) +{ + cairo_surface_t *picture = NULL; + struct pattern_cache_entry tmpl; + struct pattern_cache_entry *entry; + + assert (CAIRO_MUTEX_IS_LOCKED (screen->connection->mutex)); + + tmpl.key.hash = _cairo_radial_pattern_hash (_CAIRO_HASH_INIT_VALUE, radial); + _cairo_pattern_init_static_copy (&tmpl.pattern.base, &radial->base.base); + + entry = _cairo_cache_lookup (&screen->radial_pattern_cache, &tmpl.key); + if (entry != NULL) + picture = cairo_surface_reference (entry->picture); + + return picture; +} diff --git a/src/cairo-xcb-shm.c b/src/cairo-xcb-shm.c new file mode 100644 index 00000000..7a47f338 --- /dev/null +++ b/src/cairo-xcb-shm.c @@ -0,0 +1,576 @@ +/* Cairo - a vector graphics library with display and print output + * + * Copyright © 2007 Chris Wilson + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 Red Hat, Inc. + * + * Contributors(s): + * Chris Wilson <chris@chris-wilson.co.uk> + */ + +#include "cairoint.h" + +#include "cairo-xcb-private.h" + +#include <xcb/shm.h> +#include <sys/ipc.h> +#include <sys/shm.h> +#include <errno.h> + +/* a simple buddy allocator for memory pools + * XXX fragmentation? use Doug Lea's malloc? + */ + +typedef struct _cairo_xcb_shm_mem_block cairo_xcb_shm_mem_block_t; + +struct _cairo_xcb_shm_mem_block { + unsigned int bits; + cairo_list_t link; +}; + +struct _cairo_xcb_shm_mem_pool { + int shmid; + uint32_t shmseg; + + char *base; + unsigned int nBlocks; + cairo_xcb_shm_mem_block_t *blocks; + cairo_list_t free[32]; + unsigned char *map; + + unsigned int min_bits; /* Minimum block size is 1 << min_bits */ + unsigned int num_sizes; + + size_t free_bytes; + size_t max_bytes; + unsigned int max_free_bits; + + cairo_list_t link; +}; + +#define BITTEST(p, n) ((p)->map[(n) >> 3] & (128 >> ((n) & 7))) +#define BITSET(p, n) ((p)->map[(n) >> 3] |= (128 >> ((n) & 7))) +#define BITCLEAR(p, n) ((p)->map[(n) >> 3] &= ~(128 >> ((n) & 7))) + +static void +clear_bits (cairo_xcb_shm_mem_pool_t *pi, size_t first, size_t last) +{ + size_t i, n = last; + size_t first_full = (first + 7) & ~7; + size_t past_full = last & ~7; + size_t bytes; + + if (n > first_full) + n = first_full; + for (i = first; i < n; i++) + BITCLEAR (pi, i); + + if (past_full > first_full) { + bytes = past_full - first_full; + bytes = bytes >> 3; + memset (pi->map + (first_full >> 3), 0, bytes); + } + + if (past_full < n) + past_full = n; + for (i = past_full; i < last; i++) + BITCLEAR (pi, i); +} + +static void +free_bits (cairo_xcb_shm_mem_pool_t *pi, + size_t start, + unsigned int bits, + cairo_bool_t clear) +{ + cairo_xcb_shm_mem_block_t *block; + + if (clear) + clear_bits (pi, start, start + (1 << bits)); + + block = pi->blocks + start; + block->bits = bits; + + cairo_list_add (&block->link, &pi->free[bits]); + + pi->free_bytes += 1 << (bits + pi->min_bits); + if (bits > pi->max_free_bits) + pi->max_free_bits = bits; +} + +/* Add a chunk to the free list */ +static void +free_blocks (cairo_xcb_shm_mem_pool_t *pi, + size_t first, + size_t last, + cairo_bool_t clear) +{ + size_t i; + size_t bits = 0; + size_t len = 1; + + i = first; + while (i < last) { + /* To avoid cost quadratic in the number of different + * blocks produced from this chunk of store, we have to + * use the size of the previous block produced from this + * chunk as the starting point to work out the size of the + * next block we can produce. If you look at the binary + * representation of the starting points of the blocks + * produced, you can see that you first of all increase the + * size of the blocks produced up to some maximum as the + * address dealt with gets offsets added on which zap out + * low order bits, then decrease as the low order bits of the + * final block produced get added in. E.g. as you go from + * 001 to 0111 you generate blocks + * of size 001 at 001 taking you to 010 + * of size 010 at 010 taking you to 100 + * of size 010 at 100 taking you to 110 + * of size 001 at 110 taking you to 111 + * So the maximum total cost of the loops below this comment + * is one trip from the lowest blocksize to the highest and + * back again. + */ + while (bits < pi->num_sizes - 1) { + size_t next_bits = bits + 1; + size_t next_len = len << 1; + + if (i + next_bits > last) { + /* off end of chunk to be freed */ + break; + } + + if (i & (next_len - 1)) /* block would not be on boundary */ + break; + + bits = next_bits; + len = next_len; + } + + do { + if (i + len > last) /* off end of chunk to be freed */ + continue; + + if (i & (len - 1)) /* block would not be on boundary */ + continue; + + /* OK */ + break; + + bits--; + len >>=1; + } while (len > 0); + + if (len == 0) + break; + + free_bits (pi, i, bits, clear); + i += len; + } +} + +static cairo_status_t +_cairo_xcb_shm_mem_pool_init (cairo_xcb_shm_mem_pool_t *pi, + size_t bytes, + unsigned int min_bits, + unsigned int num_sizes) +{ + size_t setBits; + int i; + + assert ((((unsigned long) pi->base) & ((1 << min_bits) - 1)) == 0); + assert (num_sizes < ARRAY_LENGTH (pi->free)); + + pi->free_bytes = 0; + pi->max_bytes = bytes; + pi->max_free_bits = 0; + + setBits = bytes >> min_bits; + pi->blocks = calloc (setBits, sizeof (cairo_xcb_shm_mem_block_t)); + if (pi->blocks == NULL) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + pi->nBlocks = setBits; + pi->min_bits = min_bits; + pi->num_sizes = num_sizes; + + for (i = 0; i < ARRAY_LENGTH (pi->free); i++) + cairo_list_init (&pi->free[i]); + + pi->map = malloc ((setBits + 7) >> 3); + if (pi->map == NULL) { + free (pi->blocks); + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + memset (pi->map, -1, (setBits + 7) >> 3); + clear_bits (pi, 0, setBits); + + /* Now add all blocks to the free list */ + free_blocks (pi, 0, setBits, 1); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_xcb_shm_mem_block_t * +get_buddy (cairo_xcb_shm_mem_pool_t *pi, + size_t offset, + unsigned int bits) +{ + cairo_xcb_shm_mem_block_t *block; + + assert (offset + (1 << bits) <= pi->nBlocks); + + if (BITTEST (pi, offset + (1 << bits) - 1)) + return NULL; /* buddy is allocated */ + + block = pi->blocks + offset; + if (block->bits != bits) + return NULL; /* buddy is partially allocated */ + + return block; +} + +static void +merge_buddies (cairo_xcb_shm_mem_pool_t *pi, + cairo_xcb_shm_mem_block_t *block, + unsigned int max_bits) +{ + size_t block_offset = block_offset = block - pi->blocks; + unsigned int bits = block->bits; + + while (bits < max_bits - 1) { + /* while you can, merge two blocks and get a legal block size */ + size_t buddy_offset = block_offset ^ (1 << bits); + + block = get_buddy (pi, buddy_offset, bits); + if (block == NULL) + break; + + cairo_list_del (&block->link); + + /* Merged block starts at buddy */ + if (buddy_offset < block_offset) + block_offset = buddy_offset; + + bits++; + } + + block = pi->blocks + block_offset; + block->bits = bits; + cairo_list_add (&block->link, &pi->free[bits]); + + if (bits > pi->max_free_bits) + pi->max_free_bits = bits; +} + +/* attempt to merge all available buddies up to a particular size */ +static unsigned int +merge_bits (cairo_xcb_shm_mem_pool_t *pi, + unsigned int max_bits) +{ + cairo_xcb_shm_mem_block_t *block, *buddy, *next; + unsigned int bits; + + for (bits = 0; bits < max_bits - 1; bits++) { + cairo_list_foreach_entry_safe (block, next, + cairo_xcb_shm_mem_block_t, + &pi->free[bits], + link) + { + size_t buddy_offset = (block - pi->blocks) ^ (1 << bits); + + buddy = get_buddy (pi, buddy_offset, bits); + if (buddy == NULL) + continue; + + if (buddy == next) { + next = cairo_container_of (buddy->link.next, + cairo_xcb_shm_mem_block_t, + link); + } + + cairo_list_del (&block->link); + merge_buddies (pi, block, max_bits); + } + } + + return pi->max_free_bits; +} + +/* find store for 1 << bits blocks */ +static void * +buddy_malloc (cairo_xcb_shm_mem_pool_t *pi, + unsigned int bits) +{ + unsigned int b; + size_t offset; + size_t past; + cairo_xcb_shm_mem_block_t *block; + + if (bits > pi->max_free_bits && bits > merge_bits (pi, bits)) + return NULL; + + /* Find a list with blocks big enough on it */ + block = NULL; + for (b = bits; b <= pi->max_free_bits; b++) { + if (! cairo_list_is_empty (&pi->free[b])) { + block = cairo_list_first_entry (&pi->free[b], + cairo_xcb_shm_mem_block_t, + link); + break; + } + } + assert (block != NULL); + + cairo_list_del (&block->link); + + while (cairo_list_is_empty (&pi->free[pi->max_free_bits])) { + if (--pi->max_free_bits == 0) + break; + } + + /* Mark end of allocated area */ + offset = block - pi->blocks; + past = offset + (1 << bits); + BITSET (pi, past - 1); + block->bits = bits; + + /* If we used a larger free block than we needed, free the rest */ + pi->free_bytes -= 1 << (b + pi->min_bits); + free_blocks (pi, past, offset + (1 << b), 0); + + return pi->base + ((block - pi->blocks) << pi->min_bits); +} + +static void * +_cairo_xcb_shm_mem_pool_malloc (cairo_xcb_shm_mem_pool_t *pi, + size_t bytes) +{ + unsigned int bits; + size_t size; + + size = 1 << pi->min_bits; + for (bits = 0; size < bytes; bits++) + size <<= 1; + if (bits >= pi->num_sizes) + return NULL; + + return buddy_malloc (pi, bits); +} + +static void +_cairo_xcb_shm_mem_pool_free (cairo_xcb_shm_mem_pool_t *pi, + char *storage) +{ + size_t block_offset; + cairo_xcb_shm_mem_block_t *block; + + block_offset = (storage - pi->base) >> pi->min_bits; + block = pi->blocks + block_offset; + + BITCLEAR (pi, block_offset + ((1 << block->bits) - 1)); + pi->free_bytes += 1 << (block->bits + pi->min_bits); + + merge_buddies (pi, block, pi->num_sizes); +} + +static void +_cairo_xcb_shm_mem_pool_destroy (cairo_xcb_shm_mem_pool_t *pool) +{ + shmdt (pool->base); + cairo_list_del (&pool->link); + + free (pool->map); + free (pool->blocks); + free (pool); +} + +cairo_int_status_t +_cairo_xcb_connection_allocate_shm_info (cairo_xcb_connection_t *connection, + size_t size, + cairo_xcb_shm_info_t **shm_info_out) +{ + cairo_xcb_shm_info_t *shm_info; + cairo_xcb_shm_mem_pool_t *pool, *next; + size_t bytes, maxbits = 16, minbits = 8; + void *mem = NULL; + cairo_status_t status; + + assert (connection->flags & CAIRO_XCB_HAS_SHM); + + CAIRO_MUTEX_LOCK (connection->shm_mutex); + cairo_list_foreach_entry_safe (pool, next, cairo_xcb_shm_mem_pool_t, + &connection->shm_pools, link) + { + if (pool->free_bytes > size) { + mem = _cairo_xcb_shm_mem_pool_malloc (pool, size); + if (mem != NULL) { + /* keep the active pools towards the front */ + cairo_list_move (&pool->link, &connection->shm_pools); + goto allocate_shm_info; + } + } + /* scan for old, unused pools */ + if (pool->free_bytes == pool->max_bytes) { + _cairo_xcb_connection_shm_detach (connection, + pool->shmseg); + _cairo_xcb_shm_mem_pool_destroy (pool); + } + } + + pool = malloc (sizeof (cairo_xcb_shm_mem_pool_t)); + if (unlikely (pool == NULL)) { + CAIRO_MUTEX_UNLOCK (connection->shm_mutex); + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + bytes = 1 << maxbits; + while (bytes <= size) + bytes <<= 1, maxbits++; + bytes <<= 3; + + do { + pool->shmid = shmget (IPC_PRIVATE, bytes, IPC_CREAT | 0600); + if (pool->shmid != -1) + break; + + if (errno == EINVAL && bytes > size) { + bytes >>= 1; + continue; + } + } while (FALSE); + if (pool->shmid == -1) { + int err = errno; + if (! (err == EINVAL || err == ENOMEM)) + connection->flags &= ~CAIRO_XCB_HAS_SHM; + free (pool); + CAIRO_MUTEX_UNLOCK (connection->shm_mutex); + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + pool->base = shmat (pool->shmid, NULL, 0); + if (unlikely (pool->base == (char *) -1)) { + shmctl (pool->shmid, IPC_RMID, NULL); + free (pool); + CAIRO_MUTEX_UNLOCK (connection->shm_mutex); + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + status = _cairo_xcb_shm_mem_pool_init (pool, + bytes, + minbits, + maxbits - minbits + 1); + if (unlikely (status)) { + shmdt (pool->base); + free (pool); + CAIRO_MUTEX_UNLOCK (connection->shm_mutex); + return status; + } + + pool->shmseg = _cairo_xcb_connection_shm_attach (connection, pool->shmid, FALSE); + shmctl (pool->shmid, IPC_RMID, NULL); + + cairo_list_add (&pool->link, &connection->shm_pools); + mem = _cairo_xcb_shm_mem_pool_malloc (pool, size); + + allocate_shm_info: + shm_info = _cairo_freepool_alloc (&connection->shm_info_freelist); + if (unlikely (shm_info == NULL)) { + _cairo_xcb_shm_mem_pool_free (pool, mem); + CAIRO_MUTEX_UNLOCK (connection->shm_mutex); + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + shm_info->connection = connection; + shm_info->pool = pool; + shm_info->shm = pool->shmseg; + shm_info->offset = (char *) mem - (char *) pool->base; + shm_info->mem = mem; + + /* scan for old, unused pools */ + cairo_list_foreach_entry_safe (pool, next, cairo_xcb_shm_mem_pool_t, + &connection->shm_pools, link) + { + if (pool->free_bytes == pool->max_bytes) { + _cairo_xcb_connection_shm_detach (connection, + pool->shmseg); + _cairo_xcb_shm_mem_pool_destroy (pool); + } + } + CAIRO_MUTEX_UNLOCK (connection->shm_mutex); + + *shm_info_out = shm_info; + return CAIRO_STATUS_SUCCESS; +} + +void +_cairo_xcb_shm_info_destroy (cairo_xcb_shm_info_t *shm_info) +{ + cairo_xcb_connection_t *connection = shm_info->connection; + + CAIRO_MUTEX_LOCK (connection->shm_mutex); + + _cairo_xcb_shm_mem_pool_free (shm_info->pool, shm_info->mem); + _cairo_freepool_free (&connection->shm_info_freelist, shm_info); + + /* scan for old, unused pools - hold at least one in reserve */ + if (! cairo_list_is_singular (&connection->shm_pools) && + _cairo_xcb_connection_take_socket (connection) == CAIRO_STATUS_SUCCESS) + { + cairo_xcb_shm_mem_pool_t *pool, *next; + cairo_list_t head; + + cairo_list_init (&head); + cairo_list_move (connection->shm_pools.next, &head); + + cairo_list_foreach_entry_safe (pool, next, cairo_xcb_shm_mem_pool_t, + &connection->shm_pools, link) + { + if (pool->free_bytes == pool->max_bytes) { + _cairo_xcb_connection_shm_detach (connection, pool->shmseg); + _cairo_xcb_shm_mem_pool_destroy (pool); + } + } + + cairo_list_move (head.next, &connection->shm_pools); + } + + CAIRO_MUTEX_UNLOCK (connection->shm_mutex); +} + +void +_cairo_xcb_connection_shm_mem_pools_fini (cairo_xcb_connection_t *connection) +{ + while (! cairo_list_is_empty (&connection->shm_pools)) { + _cairo_xcb_shm_mem_pool_destroy (cairo_list_first_entry (&connection->shm_pools, + cairo_xcb_shm_mem_pool_t, + link)); + } +} diff --git a/src/cairo-xcb-surface-cairo.c b/src/cairo-xcb-surface-cairo.c new file mode 100644 index 00000000..c8305cf7 --- /dev/null +++ b/src/cairo-xcb-surface-cairo.c @@ -0,0 +1,94 @@ +/* cairo - a vector graphics library with display and print output + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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. + * + * Contributor(s): + * Chris Wilson <chris@chris-wilson.co.uk> + */ + +#include "cairoint.h" + +#include "cairo-clip-private.h" +#include "cairo-xcb-private.h" + +cairo_int_status_t +_cairo_xcb_surface_cairo_paint (cairo_xcb_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_clip_t *clip) +{ + return CAIRO_INT_STATUS_UNSUPPORTED; +} + +cairo_int_status_t +_cairo_xcb_surface_cairo_mask (cairo_xcb_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_pattern_t *mask, + cairo_clip_t *clip) +{ + return CAIRO_INT_STATUS_UNSUPPORTED; +} + +cairo_int_status_t +_cairo_xcb_surface_cairo_stroke (cairo_xcb_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + 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_clip_t *clip) +{ + return CAIRO_INT_STATUS_UNSUPPORTED; +} + +cairo_int_status_t +_cairo_xcb_surface_cairo_fill (cairo_xcb_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias, + cairo_clip_t *clip) +{ + return CAIRO_INT_STATUS_UNSUPPORTED; +} + +cairo_int_status_t +_cairo_xcb_surface_cairo_glyphs (cairo_xcb_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_scaled_font_t *scaled_font, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_clip_t *clip) +{ + return CAIRO_INT_STATUS_UNSUPPORTED; +} diff --git a/src/cairo-xcb-surface-core.c b/src/cairo-xcb-surface-core.c new file mode 100644 index 00000000..6f1002a8 --- /dev/null +++ b/src/cairo-xcb-surface-core.c @@ -0,0 +1,613 @@ +/* cairo - a vector graphics library with display and print output + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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. + * + * Contributor(s): + * Chris Wilson <chris@chris-wilson.co.uk> + */ + +#include "cairoint.h" + +#include "cairo-boxes-private.h" +#include "cairo-xcb-private.h" + +/* XXX dithering */ + +typedef struct _cairo_xcb_pixmap { + cairo_surface_t base; + + cairo_xcb_connection_t *connection; + cairo_xcb_screen_t *screen; + + cairo_surface_t *owner; + xcb_pixmap_t pixmap; + int width; + int height; + int depth; + int x0, y0; + cairo_bool_t repeat; +} cairo_xcb_pixmap_t; + +static cairo_status_t +_cairo_xcb_pixmap_finish (void *abstract_surface) +{ + cairo_xcb_pixmap_t *surface = abstract_surface; + cairo_status_t status; + + if (surface->owner != NULL) { + cairo_surface_destroy (surface->owner); + } else { + status = _cairo_xcb_connection_acquire (surface->connection); + if (unlikely (status)) + return status; + + if (_cairo_xcb_connection_take_socket (surface->connection) == CAIRO_STATUS_SUCCESS) { + _cairo_xcb_connection_free_pixmap (surface->connection, + surface->pixmap); + } + _cairo_xcb_connection_release (surface->connection); + } + + return CAIRO_STATUS_SUCCESS; +} + +static const cairo_surface_backend_t _cairo_xcb_pixmap_backend = { + CAIRO_SURFACE_TYPE_XCB, + NULL, + _cairo_xcb_pixmap_finish, +}; + +static cairo_xcb_pixmap_t * +_cairo_xcb_pixmap_create (cairo_xcb_surface_t *target, + int width, int height) +{ + cairo_xcb_pixmap_t *surface; + + surface = malloc (sizeof (cairo_xcb_pixmap_t)); + if (unlikely (surface == NULL)) + return (cairo_xcb_pixmap_t *) + _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + + _cairo_surface_init (&surface->base, + &_cairo_xcb_pixmap_backend, + NULL, + target->base.content); + + surface->connection = target->connection; + surface->screen = target->screen; + surface->owner = NULL; + surface->width = width; + surface->height = height; + surface->depth = target->depth; + surface->x0 = surface->y0 = 0; + surface->repeat = FALSE; + + surface->pixmap = + _cairo_xcb_connection_create_pixmap (surface->connection, + surface->depth, + target->drawable, + width, height); + + return surface; +} + +static cairo_xcb_pixmap_t * +_cairo_xcb_pixmap_copy (cairo_xcb_surface_t *target) +{ + cairo_xcb_pixmap_t *surface; + + surface = malloc (sizeof (cairo_xcb_pixmap_t)); + if (unlikely (surface == NULL)) + return (cairo_xcb_pixmap_t *) + _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + + _cairo_surface_init (&surface->base, + &_cairo_xcb_pixmap_backend, + NULL, + target->base.content); + + surface->connection = target->connection; + surface->screen = target->screen; + surface->pixmap = target->drawable; + surface->owner = cairo_surface_reference (&target->base); + surface->width = target->width; + surface->height = target->height; + surface->depth = target->depth; + surface->x0 = surface->y0 = 0; + surface->repeat = FALSE; + + return surface; +} + +static cairo_status_t +_cairo_xcb_shm_image_create (cairo_xcb_connection_t *connection, + pixman_format_code_t pixman_format, + int width, int height, + cairo_image_surface_t **image_out, + cairo_xcb_shm_info_t **shm_info_out) +{ + cairo_surface_t *image = NULL; + cairo_xcb_shm_info_t *shm_info = NULL; + cairo_status_t status; + + if ((connection->flags & CAIRO_XCB_HAS_SHM)) { + size_t size, stride; + + stride = CAIRO_STRIDE_FOR_WIDTH_BPP (width, PIXMAN_FORMAT_BPP (pixman_format)); + size = stride * height; + if (size > CAIRO_XCB_SHM_SMALL_IMAGE) { + status = _cairo_xcb_connection_allocate_shm_info (connection, + size, &shm_info); + if (unlikely (status)) + return status; + + image = _cairo_image_surface_create_with_pixman_format (shm_info->mem, + pixman_format, + width, height, + stride); + status = image->status; + if (unlikely (status)) { + _cairo_xcb_shm_info_destroy (shm_info); + return status; + } + + 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 status; + } + } + } + + if (image == NULL) { + image = _cairo_image_surface_create_with_pixman_format (NULL, + pixman_format, + width, height, + 0); + status = image->status; + if (unlikely (status)) + return status; + } + + + *image_out = (cairo_image_surface_t *) image; + *shm_info_out = shm_info; + return CAIRO_STATUS_SUCCESS; +} + +static cairo_xcb_pixmap_t * +_pixmap_from_image (cairo_xcb_surface_t *target, + xcb_render_pictformat_t format, + cairo_image_surface_t *image, + cairo_xcb_shm_info_t *shm_info) +{ + xcb_gcontext_t gc; + cairo_xcb_pixmap_t *pixmap; + + pixmap = _cairo_xcb_pixmap_create (target, + image->width, + image->height); + if (unlikely (pixmap->base.status)) + return pixmap; + + gc = _cairo_xcb_screen_get_gc (target->screen, pixmap->pixmap, image->depth); + + if (shm_info != NULL) { + shm_info->seqno = + _cairo_xcb_connection_shm_put_image (target->connection, + pixmap->pixmap, gc, + image->width, image->height, + 0, 0, + image->width, image->height, + 0, 0, + image->depth, + shm_info->shm, + shm_info->offset); + } else { + int len; + + /* Do we need to trim the image? */ + len = CAIRO_STRIDE_FOR_WIDTH_BPP (image->width, + PIXMAN_FORMAT_BPP (image->pixman_format)); + if (len == image->stride) { + _cairo_xcb_connection_put_image (target->connection, + pixmap->pixmap, gc, + image->width, image->height, + 0, 0, + image->depth, + image->stride, + image->data); + } else { + _cairo_xcb_connection_put_subimage (target->connection, + pixmap->pixmap, gc, + 0, 0, + image->width, image->height, + PIXMAN_FORMAT_BPP (image->pixman_format) / 8, + image->stride, + 0, 0, + image->depth, + image->data); + + } + } + + _cairo_xcb_screen_put_gc (target->screen, image->depth, gc); + + return pixmap; +} + +static cairo_xcb_pixmap_t * +_render_to_pixmap (cairo_xcb_surface_t *target, + const cairo_pattern_t *pattern, + const cairo_rectangle_int_t *extents) +{ + cairo_image_surface_t *image; + cairo_xcb_shm_info_t *shm_info; + cairo_pattern_union_t copy; + cairo_status_t status; + cairo_xcb_pixmap_t *pixmap; + + status = _cairo_xcb_shm_image_create (target->screen->connection, + target->pixman_format, + extents->width, extents->height, + &image, &shm_info); + if (unlikely (status)) + return (cairo_xcb_pixmap_t *) _cairo_surface_create_in_error (status); + + _cairo_pattern_init_static_copy (©.base, pattern); + cairo_matrix_translate (©.base.matrix, -extents->x, -extents->y); + status = _cairo_surface_paint (&image->base, + CAIRO_OPERATOR_SOURCE, + ©.base, + NULL); + if (unlikely (status)) { + cairo_surface_destroy (&image->base); + return (cairo_xcb_pixmap_t *) _cairo_surface_create_in_error (status); + } + + pixmap = _pixmap_from_image (target, target->xrender_format, image, shm_info); + cairo_surface_destroy (&image->base); + + if (unlikely (pixmap->base.status)) + return pixmap; + + pixmap->x0 = -extents->x; + pixmap->y0 = -extents->y; + return pixmap; +} + +static cairo_xcb_pixmap_t * +_copy_to_pixmap (cairo_xcb_surface_t *source) +{ + cairo_xcb_pixmap_t *pixmap; + + /* If the source may be a window, we need to copy it and its children + * via a temporary pixmap so that we can IncludeInferiors on the source + * and use ClipByChildren on the destination. + */ + if (source->owns_pixmap) { + pixmap = _cairo_xcb_pixmap_copy (source); + if (unlikely (pixmap->base.status)) + return pixmap; + } else { + uint32_t values[1]; + xcb_gcontext_t gc; + + pixmap = _cairo_xcb_pixmap_create (source, + source->width, + source->height); + if (unlikely (pixmap->base.status)) + return pixmap; + + gc = _cairo_xcb_screen_get_gc (source->screen, + pixmap->pixmap, + pixmap->depth); + + values[0] = TRUE; + _cairo_xcb_connection_change_gc (pixmap->connection, gc, + XCB_GC_SUBWINDOW_MODE, values); + + _cairo_xcb_connection_copy_area (pixmap->connection, + source->drawable, + pixmap->pixmap, gc, + 0, 0, + 0, 0, + source->width, + source->height); + + values[0] = FALSE; + _cairo_xcb_connection_change_gc (pixmap->connection, gc, + XCB_GC_SUBWINDOW_MODE, values); + + _cairo_xcb_screen_put_gc (source->screen, + pixmap->depth, + gc); + } + + return pixmap; +} +static cairo_xcb_pixmap_t * +_cairo_xcb_surface_pixmap (cairo_xcb_surface_t *target, + const cairo_surface_pattern_t *pattern, + const cairo_rectangle_int_t *extents, + int tx, int ty) +{ + cairo_surface_t *source; + cairo_xcb_pixmap_t *pixmap; + cairo_status_t status; + + source = pattern->surface; + pixmap = (cairo_xcb_pixmap_t *) + _cairo_surface_has_snapshot (source, &_cairo_xcb_pixmap_backend); + if (pixmap != NULL && pixmap->screen == target->screen) + return (cairo_xcb_pixmap_t *) cairo_surface_reference (&pixmap->base); + + if (source->type == CAIRO_SURFACE_TYPE_XCB && + ((cairo_xcb_surface_t *) source)->screen == target->screen) + { + cairo_xcb_surface_t *xcb_source = (cairo_xcb_surface_t *) source; + + if (xcb_source->depth == target->depth) + pixmap = _copy_to_pixmap (xcb_source); + } +#if CAIRO_HAS_XLIB_XCB_FUNCTIONS + else if (source->type == CAIRO_SURFACE_TYPE_XLIB && + ((cairo_xlib_xcb_surface_t *) source)->xcb->screen == target->screen) + { + cairo_xcb_surface_t *xcb_source = ((cairo_xlib_xcb_surface_t *) source)->xcb; + + if (xcb_source->depth == target->depth) + pixmap = _copy_to_pixmap (xcb_source); + } +#endif + + if (pixmap == NULL) { + cairo_rectangle_int_t rect; + + if (! _cairo_surface_get_extents (source, &rect)) { + rect.x = rect.y = 0; + rect.width = target->width; + rect.height = target->height; + } + + pixmap = _render_to_pixmap (target, &pattern->base, &rect); + } + + if (unlikely (pixmap->base.status)) + return pixmap; + + status = _cairo_surface_attach_snapshot (source, &pixmap->base, NULL); + if (unlikely (status)) { + cairo_surface_destroy (&pixmap->base); + return (cairo_xcb_pixmap_t *) _cairo_surface_create_in_error (status); + } + + if (pattern->base.extend != CAIRO_EXTEND_NONE) { + if (extents->x < 0 || extents->y < 0 || + extents->x + extents->width > pixmap->width || + extents->y + extents->height > pixmap->height) + { + pixmap->repeat = TRUE; + } + } + + pixmap->x0 += tx; + pixmap->y0 += ty; + + return pixmap; +} + +static cairo_xcb_pixmap_t * +_cairo_xcb_pixmap_for_pattern (cairo_xcb_surface_t *target, + const cairo_pattern_t *pattern, + const cairo_rectangle_int_t *extents) +{ + int tx, ty; + + switch (pattern->type) { + case CAIRO_PATTERN_TYPE_SURFACE: + /* Core can only perform a native, unscaled blit, but can handle tiles */ + if (_cairo_matrix_is_integer_translation (&pattern->matrix, &tx, &ty)) { + switch (pattern->extend) { + case CAIRO_EXTEND_NONE: + case CAIRO_EXTEND_REPEAT: + return _cairo_xcb_surface_pixmap (target, + (cairo_surface_pattern_t *) pattern, + extents, tx, ty); + + default: + case CAIRO_EXTEND_PAD: + case CAIRO_EXTEND_REFLECT: + break; + } + } + /* fallthrough */ + case CAIRO_PATTERN_TYPE_LINEAR: + case CAIRO_PATTERN_TYPE_RADIAL: + return _render_to_pixmap (target, pattern, extents); + + default: + case CAIRO_PATTERN_TYPE_SOLID: + ASSERT_NOT_REACHED; + return NULL; + } +} + +cairo_status_t +_cairo_xcb_surface_core_copy_boxes (cairo_xcb_surface_t *dst, + const cairo_pattern_t *src_pattern, + const cairo_rectangle_int_t *extents, + const cairo_boxes_t *boxes) +{ + cairo_xcb_pixmap_t *src; + const struct _cairo_boxes_chunk *chunk; + xcb_gcontext_t gc; + cairo_status_t status; + + status = _cairo_xcb_connection_acquire (dst->connection); + if (unlikely (status)) + return status; + + status = _cairo_xcb_connection_take_socket (dst->connection); + if (unlikely (status)) + goto CLEANUP_CONNECTION; + + src = _cairo_xcb_pixmap_for_pattern (dst, src_pattern, extents); + status = src->base.status; + if (unlikely (status)) + goto CLEANUP_CONNECTION; + + assert (src->depth == dst->depth); + + gc = _cairo_xcb_screen_get_gc (dst->screen, src->pixmap, src->depth); + + if (src->repeat) { + uint32_t mask = + XCB_GC_FILL_STYLE | + XCB_GC_TILE | + XCB_GC_TILE_STIPPLE_ORIGIN_X | + XCB_GC_TILE_STIPPLE_ORIGIN_Y; + uint32_t values[] = { + XCB_FILL_STYLE_TILED, + src->pixmap, + - src->x0, - src->y0, + }; + xcb_rectangle_t *xcb_rects; + + _cairo_xcb_connection_change_gc (dst->connection, gc, mask, values); + + for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) { + xcb_rects = (xcb_rectangle_t *) chunk->base; + int i; + + for (i = 0; i < chunk->count; i++) { + int x1 = _cairo_fixed_integer_round (chunk->base[i].p1.x); + int x2 = _cairo_fixed_integer_round (chunk->base[i].p2.x); + int y1 = _cairo_fixed_integer_round (chunk->base[i].p1.y); + int y2 = _cairo_fixed_integer_round (chunk->base[i].p2.y); + + xcb_rects[i].x = x1; + xcb_rects[i].y = y1; + xcb_rects[i].width = x2 - x1; + xcb_rects[i].height = y2 - y1; + } + _cairo_xcb_connection_poly_fill_rectangle (dst->connection, + dst->drawable, + gc, chunk->count, xcb_rects); + } + + values[0] = 0; + _cairo_xcb_connection_change_gc (dst->connection, gc, XCB_GC_FILL_STYLE, values); + } else { + for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) { + int i; + + for (i = 0; i < chunk->count; i++) { + int x1 = _cairo_fixed_integer_round (chunk->base[i].p1.x); + int x2 = _cairo_fixed_integer_round (chunk->base[i].p2.x); + int y1 = _cairo_fixed_integer_round (chunk->base[i].p1.y); + int y2 = _cairo_fixed_integer_round (chunk->base[i].p2.y); + + _cairo_xcb_connection_copy_area (dst->connection, + src->pixmap, + dst->drawable, gc, + src->x0 + x1, + src->y0 + y1, + x1, y1, + x2 - x2, y2 - x2); + } + } + } + + _cairo_xcb_screen_put_gc (dst->screen, src->depth, gc); + cairo_surface_destroy (&src->base); + + CLEANUP_CONNECTION: + _cairo_xcb_connection_release (dst->connection); + + return status; +} + +cairo_status_t +_cairo_xcb_surface_core_fill_boxes (cairo_xcb_surface_t *dst, + const cairo_color_t *color, + cairo_boxes_t *boxes) +{ + struct _cairo_boxes_chunk *chunk; + xcb_gcontext_t gc; + cairo_status_t status; + + status = _cairo_xcb_connection_acquire (dst->connection); + if (unlikely (status)) + return status; + + status = _cairo_xcb_connection_take_socket (dst->connection); + if (unlikely (status)) { + _cairo_xcb_connection_release (dst->connection); + return status; + } + + gc = _cairo_xcb_screen_get_gc (dst->screen, dst->drawable, dst->depth); + +#if 0 + xcb_pixmap_t source; + + source = _dither_source (dst, color); + XSetTSOrigin (surface->dpy, gc, 0, 0); + XSetTile (surface->dpy, gc, source); +#endif + + for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) { + xcb_rectangle_t *xcb_rects; + int i; + + xcb_rects = (xcb_rectangle_t *) chunk->base; + for (i = 0; i < chunk->count; i++) { + int x1 = _cairo_fixed_integer_round (chunk->base[i].p1.x); + int x2 = _cairo_fixed_integer_round (chunk->base[i].p2.x); + int y1 = _cairo_fixed_integer_round (chunk->base[i].p1.y); + int y2 = _cairo_fixed_integer_round (chunk->base[i].p2.y); + + xcb_rects[i].x = x1; + xcb_rects[i].y = y1; + xcb_rects[i].width = x2 - x1; + xcb_rects[i].height = y2 - y1; + } + + _cairo_xcb_connection_poly_fill_rectangle (dst->connection, + dst->drawable, gc, + chunk->count, xcb_rects); + } + + _cairo_xcb_screen_put_gc (dst->screen, dst->depth, gc); + _cairo_xcb_connection_release (dst->connection); + + return CAIRO_STATUS_SUCCESS; +} diff --git a/src/cairo-xcb-xrender.h b/src/cairo-xcb-surface-private.h index 09c60973..1ef49003 100644 --- a/src/cairo-xcb-xrender.h +++ b/src/cairo-xcb-surface-private.h @@ -1,6 +1,6 @@ -/* cairo - a vector graphics library with display and print output +/* 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 @@ -25,39 +25,13 @@ * 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): - * Carl D. Worth <cworth@cworth.org> + * Contributors(s): + * Chris Wilson <chris@chris-wilson.co.uk> */ -#ifndef CAIRO_XCB_XRENDER_H -#define CAIRO_XCB_XRENDER_H - -#include "cairo.h" - -#if CAIRO_HAS_XCB_SURFACE - -#include <xcb/xcb.h> -#include <xcb/render.h> - -CAIRO_BEGIN_DECLS - -cairo_public cairo_surface_t * -cairo_xcb_surface_create_with_xrender_format (xcb_connection_t *c, - xcb_drawable_t drawable, - xcb_screen_t *screen, - xcb_render_pictforminfo_t *format, - int width, - int height); - -CAIRO_END_DECLS +#ifndef CAIRO_XCB_SURFACE_PRIVATE_H +#define CAIRO_XCB_SURFACE_PRIVATE_H -#else /* CAIRO_HAS_XCB_SURFACE */ -# error Cairo was not compiled with support for the xcb backend -#endif /* CAIRO_HAS_XCB_SURFACE */ +#include "cairo-xcb-private.h" -#endif /* CAIRO_XCB_XRENDER_H */ +#endif diff --git a/src/cairo-xcb-surface-render.c b/src/cairo-xcb-surface-render.c new file mode 100644 index 00000000..e07f575c --- /dev/null +++ b/src/cairo-xcb-surface-render.c @@ -0,0 +1,4471 @@ +/* cairo - a vector graphics library with display and print output + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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. + * + * Contributor(s): + * Chris Wilson <chris@chris-wilson.co.uk> + */ + +#include "cairoint.h" + +#include "cairo-boxes-private.h" +#include "cairo-clip-private.h" +#include "cairo-composite-rectangles-private.h" +#include "cairo-region-private.h" +#include "cairo-surface-offset-private.h" +#include "cairo-surface-snapshot-private.h" +#include "cairo-surface-subsurface-private.h" +#include "cairo-xcb-private.h" + +#if CAIRO_HAS_XCB_DRM_FUNCTIONS && CAIRO_HAS_DRM_SURFACE +#include "drm/cairo-drm-private.h" +#endif + +#define PIXMAN_MAX_INT ((pixman_fixed_1 >> 1) - pixman_fixed_e) /* need to ensure deltas also fit */ + +typedef struct _cairo_xcb_picture { + cairo_surface_t base; + + cairo_surface_t *owner; + + cairo_xcb_connection_t *connection; + cairo_xcb_screen_t *screen; + xcb_render_picture_t picture; + xcb_render_pictformat_t xrender_format; + pixman_format_code_t pixman_format; + + int width, height; + + cairo_extend_t extend; + cairo_filter_t filter; + cairo_bool_t has_component_alpha; + xcb_render_transform_t transform; + + int x0, y0; + int x, y; +} cairo_xcb_picture_t; + +static void +_cairo_xcb_surface_ensure_picture (cairo_xcb_surface_t *surface); + +static uint32_t +hars_petruska_f54_1_random (void) +{ +#define rol(x,k) ((x << k) | (x >> (32-k))) + static uint32_t x; + return x = (x ^ rol (x, 5) ^ rol (x, 24)) + 0x37798849; +#undef rol +} + +static cairo_status_t +_cairo_xcb_picture_finish (void *abstract_surface) +{ + cairo_xcb_picture_t *surface = abstract_surface; + cairo_status_t status; + + if (surface->owner != NULL) { + cairo_surface_destroy (surface->owner); + } else { + status = _cairo_xcb_connection_acquire (surface->connection); + if (unlikely (status)) + return status; + + if (_cairo_xcb_connection_take_socket (surface->connection) == CAIRO_STATUS_SUCCESS) { + _cairo_xcb_connection_render_free_picture (surface->connection, + surface->picture); + } + _cairo_xcb_connection_release (surface->connection); + } + + return CAIRO_STATUS_SUCCESS; +} + +static const cairo_surface_backend_t _cairo_xcb_picture_backend = { + CAIRO_SURFACE_TYPE_XCB, + NULL, + _cairo_xcb_picture_finish, +}; + +static const struct xcb_render_transform_t identity_transform = { + 1 << 16, 0, 0, + 0, 1 << 16, 0, + 0, 0, 1 << 16, +}; + +static cairo_xcb_picture_t * +_cairo_xcb_picture_create (cairo_xcb_screen_t *screen, + pixman_format_code_t pixman_format, + xcb_render_pictformat_t xrender_format, + int width, int height) +{ + cairo_xcb_picture_t *surface; + + surface = malloc (sizeof (cairo_xcb_picture_t)); + if (unlikely (surface == NULL)) + return (cairo_xcb_picture_t *) + _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + + _cairo_surface_init (&surface->base, + &_cairo_xcb_picture_backend, + NULL, + _cairo_content_from_pixman_format (pixman_format)); + + surface->connection = screen->connection; + surface->screen = screen; + surface->owner = NULL; + surface->picture = _cairo_xcb_connection_get_xid (screen->connection); + surface->pixman_format = pixman_format; + surface->xrender_format = xrender_format; + + surface->x0 = surface->y0 = 0; + surface->x = surface->y = 0; + surface->width = width; + surface->height = height; + + surface->transform = identity_transform; + surface->extend = CAIRO_EXTEND_NONE; + surface->filter = CAIRO_FILTER_NEAREST; + surface->has_component_alpha = FALSE; + + return surface; +} + +static cairo_xcb_picture_t * +_cairo_xcb_picture_copy (cairo_xcb_surface_t *target) +{ + cairo_xcb_picture_t *surface; + + surface = malloc (sizeof (cairo_xcb_picture_t)); + if (unlikely (surface == NULL)) + return (cairo_xcb_picture_t *) + _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + + _cairo_surface_init (&surface->base, + &_cairo_xcb_picture_backend, + NULL, + target->base.content); + + surface->connection = target->connection; + surface->screen = target->screen; + surface->owner = cairo_surface_reference (&target->base); + _cairo_xcb_surface_ensure_picture (target); + surface->picture = target->picture; + surface->pixman_format = target->pixman_format; + surface->xrender_format = target->xrender_format; + + surface->x0 = surface->y0 = 0; + surface->x = surface->y = 0; + surface->width = target->width; + surface->height = target->height; + + surface->transform = identity_transform; + surface->extend = CAIRO_EXTEND_NONE; + surface->filter = CAIRO_FILTER_NEAREST; + surface->has_component_alpha = FALSE; + + return surface; +} + +static inline cairo_bool_t +_operator_is_supported (uint32_t flags, cairo_operator_t op) +{ + if (op <= CAIRO_OPERATOR_SATURATE) + return TRUE; + + return flags & CAIRO_XCB_RENDER_HAS_PDF_OPERATORS; +} + +static int +_render_operator (cairo_operator_t op) +{ +#define C(x,y) case CAIRO_OPERATOR_##x: return XCB_RENDER_PICT_OP_##y + switch (op) { + C(CLEAR, CLEAR); + C(SOURCE, SRC); + + C(OVER, OVER); + C(IN, IN); + C(OUT, OUT); + C(ATOP, ATOP); + + C(DEST, DST); + C(DEST_OVER, OVER_REVERSE); + C(DEST_IN, IN_REVERSE); + C(DEST_OUT, OUT_REVERSE); + C(DEST_ATOP, ATOP_REVERSE); + + C(XOR, XOR); + C(ADD, ADD); + C(SATURATE, SATURATE); + + case CAIRO_OPERATOR_MULTIPLY: + case CAIRO_OPERATOR_SCREEN: + case CAIRO_OPERATOR_OVERLAY: + case CAIRO_OPERATOR_DARKEN: + case CAIRO_OPERATOR_LIGHTEN: + case CAIRO_OPERATOR_COLOR_DODGE: + case CAIRO_OPERATOR_COLOR_BURN: + case CAIRO_OPERATOR_HARD_LIGHT: + case CAIRO_OPERATOR_SOFT_LIGHT: + case CAIRO_OPERATOR_DIFFERENCE: + case CAIRO_OPERATOR_EXCLUSION: + case CAIRO_OPERATOR_HSL_HUE: + case CAIRO_OPERATOR_HSL_SATURATION: + case CAIRO_OPERATOR_HSL_COLOR: + case CAIRO_OPERATOR_HSL_LUMINOSITY: + default: + ASSERT_NOT_REACHED; + return XCB_RENDER_PICT_OP_OVER; + } +} + +static void +_cairo_xcb_surface_set_clip_region (cairo_xcb_surface_t *surface, + cairo_region_t *region) +{ + xcb_rectangle_t rects[CAIRO_STACK_ARRAY_LENGTH (xcb_rectangle_t)]; + int i, num_rects; + + num_rects = cairo_region_num_rectangles (region); + assert (num_rects < ARRAY_LENGTH (rects)); /* XXX somebody will! */ + + for (i = 0; i < num_rects; i++) { + cairo_rectangle_int_t rect; + + cairo_region_get_rectangle (region, i, &rect); + + rects[i].x = rect.x; + rects[i].y = rect.y; + rects[i].width = rect.width; + rects[i].height = rect.height; + } + + _cairo_xcb_connection_render_set_picture_clip_rectangles (surface->connection, + surface->picture, + 0, 0, + num_rects, rects); +} + +static void +_cairo_xcb_surface_clear_clip_region (cairo_xcb_surface_t *surface) +{ + uint32_t values[] = { XCB_NONE }; + _cairo_xcb_connection_render_change_picture (surface->connection, + surface->picture, + XCB_RENDER_CP_CLIP_MASK, values); +} + +static void +_cairo_xcb_surface_ensure_picture (cairo_xcb_surface_t *surface) +{ + if (surface->picture == XCB_NONE) { + surface->picture = _cairo_xcb_connection_get_xid (surface->connection); + _cairo_xcb_connection_render_create_picture (surface->connection, + surface->picture, + surface->drawable, + surface->xrender_format, + 0, NULL); + } +} + +static cairo_xcb_picture_t * +_picture_from_image (cairo_xcb_surface_t *target, + xcb_render_pictformat_t format, + cairo_image_surface_t *image, + cairo_xcb_shm_info_t *shm_info) +{ + xcb_pixmap_t pixmap; + xcb_gcontext_t gc; + cairo_xcb_picture_t *picture; + + pixmap = _cairo_xcb_connection_create_pixmap (target->connection, + image->depth, + target->drawable, + image->width, image->height); + + gc = _cairo_xcb_screen_get_gc (target->screen, pixmap, image->depth); + + if (shm_info != NULL) { + shm_info->seqno = + _cairo_xcb_connection_shm_put_image (target->connection, + pixmap, gc, + image->width, image->height, + 0, 0, + image->width, image->height, + 0, 0, + image->depth, + shm_info->shm, + shm_info->offset); + } else { + int len; + + /* Do we need to trim the image? */ + len = CAIRO_STRIDE_FOR_WIDTH_BPP (image->width, PIXMAN_FORMAT_BPP (image->pixman_format)); + if (len == image->stride) { + _cairo_xcb_connection_put_image (target->connection, + pixmap, gc, + image->width, image->height, + 0, 0, + image->depth, + image->stride, + image->data); + } else { + _cairo_xcb_connection_put_subimage (target->connection, + pixmap, gc, + 0, 0, + image->width, image->height, + PIXMAN_FORMAT_BPP (image->pixman_format) / 8, + image->stride, + 0, 0, + image->depth, + image->data); + + } + } + + _cairo_xcb_screen_put_gc (target->screen, image->depth, gc); + + picture = _cairo_xcb_picture_create (target->screen, + image->pixman_format, format, + image->width, image->height); + if (likely (picture->base.status == CAIRO_STATUS_SUCCESS)) { + _cairo_xcb_connection_render_create_picture (target->connection, + picture->picture, pixmap, format, + 0, 0); + } + + _cairo_xcb_connection_free_pixmap (target->connection, pixmap); + + return picture; +} + +static cairo_bool_t +_pattern_is_supported (uint32_t flags, + const cairo_pattern_t *pattern) + +{ + if (pattern->type == CAIRO_PATTERN_TYPE_SOLID) + return TRUE; + + if (! _cairo_matrix_is_integer_translation (&pattern->matrix, NULL, NULL)) { + if ((flags & CAIRO_XCB_RENDER_HAS_PICTURE_TRANSFORM) == 0) + return FALSE; + } + + switch (pattern->extend) { + default: + ASSERT_NOT_REACHED; + case CAIRO_EXTEND_NONE: + case CAIRO_EXTEND_REPEAT: + break; + case CAIRO_EXTEND_PAD: + case CAIRO_EXTEND_REFLECT: + if ((flags & CAIRO_XCB_RENDER_HAS_EXTENDED_REPEAT) == 0) + return FALSE; + } + + if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) { + cairo_filter_t filter; + + filter = pattern->filter; + if (_cairo_matrix_has_unity_scale (&pattern->matrix) && + _cairo_matrix_is_integer_translation (&pattern->matrix, NULL, NULL)) + { + filter = CAIRO_FILTER_NEAREST; + } + + if (! (filter == CAIRO_FILTER_NEAREST || filter == CAIRO_FILTER_FAST)) { + if ((flags & CAIRO_XCB_RENDER_HAS_FILTERS) == 0) + return FALSE; + } + } else { /* gradient */ + if ((flags & CAIRO_XCB_RENDER_HAS_GRADIENTS) == 0) + return FALSE; + } + + return TRUE; +} + +static double +_pixman_nearest_sample (double d) +{ + return ceil (d - .5); +} + +static cairo_bool_t +_nearest_sample (const cairo_matrix_t *m, + cairo_filter_t filter, + double *tx, double *ty) +{ + *tx = m->x0; + *ty = m->y0; + if ((filter == CAIRO_FILTER_FAST || filter == CAIRO_FILTER_NEAREST) + && _cairo_matrix_has_unity_scale (m)) + { + *tx = _pixman_nearest_sample (*tx); + *ty = _pixman_nearest_sample (*ty); + } + else + { + if (*tx != floor (*tx) || *ty != floor (*ty)) + return FALSE; + } + return fabs (*tx) < PIXMAN_MAX_INT && fabs (*ty) < PIXMAN_MAX_INT; +} + +static void +_cairo_xcb_picture_set_matrix (cairo_xcb_picture_t *picture, + const cairo_matrix_t *matrix, + cairo_filter_t filter, + double xc, double yc) +{ + cairo_matrix_t m; + double tx, ty; + + m = *matrix; + if (_nearest_sample (&m, filter, &tx, &ty)) + m.x0 = m.y0 = 0; + else + tx = ty = 0; + + if (! _cairo_matrix_is_identity (&m)) { + xcb_render_transform_t transform; + cairo_matrix_t inv; + cairo_status_t status; + + inv = m; + status = cairo_matrix_invert (&inv); + assert (status == CAIRO_STATUS_SUCCESS); + + if (m.x0 != 0. || m.y0 != 0.) { + double x, y; + + /* pixman also limits the [xy]_offset to 16 bits so evenly + * spread the bits between the two. + */ + x = floor (inv.x0 / 2); + y = floor (inv.y0 / 2); + tx = -x; + ty = -y; + cairo_matrix_init_translate (&inv, x, y); + cairo_matrix_multiply (&m, &inv, &m); + } else { + if (tx != 0. || ty != 0.) + cairo_matrix_transform_point (&inv, &tx, &ty); + } + + /* Casting between pixman_transform_t and XTransform is safe because + * they happen to be the exact same type. + */ + _cairo_matrix_to_pixman_matrix (&m, + (pixman_transform_t *) &transform, xc, yc); + + if (memcmp (&picture->transform, &transform, sizeof (xcb_render_transform_t))) { + _cairo_xcb_connection_render_set_picture_transform (picture->connection, + picture->picture, + &transform); + + picture->transform = transform; + } + } + + picture->x = picture->x0 + tx; + picture->y = picture->y0 + ty; +} + +static void +_cairo_xcb_picture_set_filter (cairo_xcb_picture_t *picture, + cairo_filter_t filter) +{ + const char *render_filter; + int len; + + if (picture->filter == filter) + return; + + switch (filter) { + case CAIRO_FILTER_FAST: + render_filter = "fast"; + len = strlen ("fast"); + break; + + case CAIRO_FILTER_GOOD: + render_filter = "good"; + len = strlen ("good"); + break; + + case CAIRO_FILTER_BEST: + render_filter = "best"; + len = strlen ("best"); + break; + + case CAIRO_FILTER_NEAREST: + render_filter = "nearest"; + len = strlen ("nearest"); + break; + + case CAIRO_FILTER_BILINEAR: + render_filter = "bilinear"; + len = strlen ("bilinear"); + break; + + default: + ASSERT_NOT_REACHED; + case CAIRO_FILTER_GAUSSIAN: + render_filter = "best"; + len = strlen ("best"); + break; + } + + _cairo_xcb_connection_render_set_picture_filter (picture->connection, + picture->picture, + len, (char *) render_filter); + picture->filter = filter; +} + +static void +_cairo_xcb_picture_set_extend (cairo_xcb_picture_t *picture, + cairo_extend_t extend) +{ + uint32_t pa[1]; + + if (picture->extend == extend) + return; + + switch (extend) { + default: + ASSERT_NOT_REACHED; + case CAIRO_EXTEND_NONE: + pa[0] = XCB_RENDER_REPEAT_NONE; + break; + + case CAIRO_EXTEND_REPEAT: + pa[0] = XCB_RENDER_REPEAT_NORMAL; + break; + + case CAIRO_EXTEND_REFLECT: + pa[0] = XCB_RENDER_REPEAT_REFLECT; + break; + + case CAIRO_EXTEND_PAD: + pa[0] = XCB_RENDER_REPEAT_PAD; + break; + } + + _cairo_xcb_connection_render_change_picture (picture->connection, + picture->picture, + XCB_RENDER_CP_REPEAT, pa); + picture->extend = extend; +} + +static void +_cairo_xcb_picture_set_component_alpha (cairo_xcb_picture_t *picture, + cairo_bool_t ca) +{ + uint32_t pa[1]; + + if (picture->has_component_alpha == ca) + return; + + pa[0] = ca; + + _cairo_xcb_connection_render_change_picture (picture->connection, + picture->picture, + XCB_RENDER_CP_COMPONENT_ALPHA, + pa); + picture->has_component_alpha = ca; +} + +static cairo_xcb_picture_t * +_solid_picture (cairo_xcb_surface_t *target, + const cairo_color_t *color) +{ + xcb_render_color_t xcb_color; + xcb_render_pictformat_t xrender_format; + cairo_xcb_picture_t *picture; + + xcb_color.red = color->red_short; + xcb_color.green = color->green_short; + xcb_color.blue = color->blue_short; + xcb_color.alpha = color->alpha_short; + + xrender_format = target->screen->connection->standard_formats[CAIRO_FORMAT_ARGB32]; + picture = _cairo_xcb_picture_create (target->screen, + PIXMAN_a8r8g8b8, + xrender_format, + -1, -1); + if (unlikely (picture->base.status)) + return picture; + + if (target->flags & CAIRO_XCB_RENDER_HAS_GRADIENTS) { + _cairo_xcb_connection_render_create_solid_fill (target->connection, + picture->picture, + xcb_color); + } else { + xcb_pixmap_t pixmap; + uint32_t values[] = { XCB_RENDER_REPEAT_NORMAL }; + + pixmap = _cairo_xcb_connection_create_pixmap (target->connection, + 32, target->drawable, 1, 1); + _cairo_xcb_connection_render_create_picture (target->connection, + picture->picture, + pixmap, + xrender_format, + XCB_RENDER_CP_REPEAT, + values); + if (target->flags & CAIRO_XCB_RENDER_HAS_FILL_RECTANGLES) { + xcb_rectangle_t rect; + + rect.x = rect.y = 0; + rect.width = rect.height = 1; + + _cairo_xcb_connection_render_fill_rectangles (picture->connection, + XCB_RENDER_PICT_OP_SRC, + picture->picture, + xcb_color, 1, &rect); + } else { + xcb_gcontext_t gc; + uint32_t pixel; + + gc = _cairo_xcb_screen_get_gc (target->screen, pixmap, 32); + + /* XXX byte ordering? */ + pixel = ((color->alpha_short >> 8) << 24) | + ((color->red_short >> 8) << 16) | + ((color->green_short >> 8) << 8) | + ((color->blue_short >> 8) << 0); + + _cairo_xcb_connection_put_image (target->connection, + pixmap, gc, + 1, 1, 0, 0, + 32, 4, &pixel); + + _cairo_xcb_screen_put_gc (target->screen, 32, gc); + } + + _cairo_xcb_connection_free_pixmap (target->connection, pixmap); + } + + return picture; +} + +static cairo_xcb_picture_t * +_cairo_xcb_transparent_picture (cairo_xcb_surface_t *target) +{ + cairo_xcb_picture_t *picture; + + picture = (cairo_xcb_picture_t *) target->screen->stock_colors[CAIRO_STOCK_TRANSPARENT]; + if (picture == NULL) { + picture = _solid_picture (target, CAIRO_COLOR_TRANSPARENT); + target->screen->stock_colors[CAIRO_STOCK_TRANSPARENT] = &picture->base; + } + + return (cairo_xcb_picture_t *) cairo_surface_reference (&picture->base); +} + +static cairo_xcb_picture_t * +_cairo_xcb_black_picture (cairo_xcb_surface_t *target) +{ + cairo_xcb_picture_t *picture; + + picture = (cairo_xcb_picture_t *) target->screen->stock_colors[CAIRO_STOCK_BLACK]; + if (picture == NULL) { + picture = _solid_picture (target, CAIRO_COLOR_BLACK); + target->screen->stock_colors[CAIRO_STOCK_BLACK] = &picture->base; + } + + return (cairo_xcb_picture_t *) cairo_surface_reference (&picture->base); +} + +static cairo_xcb_picture_t * +_cairo_xcb_white_picture (cairo_xcb_surface_t *target) +{ + cairo_xcb_picture_t *picture; + + picture = (cairo_xcb_picture_t *) target->screen->stock_colors[CAIRO_STOCK_WHITE]; + if (picture == NULL) { + picture = _solid_picture (target, CAIRO_COLOR_WHITE); + target->screen->stock_colors[CAIRO_STOCK_WHITE] = &picture->base; + } + + return (cairo_xcb_picture_t *) cairo_surface_reference (&picture->base); +} + +static cairo_xcb_picture_t * +_cairo_xcb_solid_picture (cairo_xcb_surface_t *target, + const cairo_solid_pattern_t *pattern) +{ + cairo_xcb_picture_t *picture; + cairo_xcb_screen_t *screen; + int i, n_cached; + + if (pattern->color.alpha_short <= 0x00ff) + return _cairo_xcb_transparent_picture (target); + + if (pattern->color.alpha_short >= 0xff00) { + if (pattern->color.red_short <= 0x00ff && + pattern->color.green_short <= 0x00ff && + pattern->color.blue_short <= 0x00ff) + { + return _cairo_xcb_black_picture (target); + } + + if (pattern->color.red_short >= 0xff00 && + pattern->color.green_short >= 0xff00 && + pattern->color.blue_short >= 0xff00) + { + return _cairo_xcb_white_picture (target); + } + } + + screen = target->screen; + n_cached = screen->solid_cache_size; + for (i = 0; i < n_cached; i++) { + if (_cairo_color_equal (&screen->solid_cache[i].color, &pattern->color)) { + return (cairo_xcb_picture_t *) cairo_surface_reference (screen->solid_cache[i].picture); + } + } + + picture = _solid_picture (target, &pattern->color); + if (unlikely (picture->base.status)) + return picture; + + if (screen->solid_cache_size < ARRAY_LENGTH (screen->solid_cache)) { + i = screen->solid_cache_size++; + } else { + i = hars_petruska_f54_1_random () % ARRAY_LENGTH (screen->solid_cache); + cairo_surface_destroy (screen->solid_cache[i].picture); + } + screen->solid_cache[i].picture = cairo_surface_reference (&picture->base); + screen->solid_cache[i].color = pattern->color; + + return picture; +} + +static cairo_status_t +_cairo_xcb_shm_image_create (cairo_xcb_connection_t *connection, + pixman_format_code_t pixman_format, + int width, int height, + cairo_image_surface_t **image_out, + cairo_xcb_shm_info_t **shm_info_out) +{ + cairo_surface_t *image = NULL; + cairo_xcb_shm_info_t *shm_info = NULL; + cairo_status_t status; + + if ((connection->flags & CAIRO_XCB_HAS_SHM)) { + size_t size, stride; + + stride = CAIRO_STRIDE_FOR_WIDTH_BPP (width, PIXMAN_FORMAT_BPP (pixman_format)); + size = stride * height; + if (size > CAIRO_XCB_SHM_SMALL_IMAGE) { + status = _cairo_xcb_connection_allocate_shm_info (connection, + size, &shm_info); + if (unlikely (status)) + return status; + + image = _cairo_image_surface_create_with_pixman_format (shm_info->mem, + pixman_format, + width, height, + stride); + status = image->status; + if (unlikely (status)) { + _cairo_xcb_shm_info_destroy (shm_info); + return status; + } + + 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 status; + } + } + } + + if (image == NULL) { + image = _cairo_image_surface_create_with_pixman_format (NULL, + pixman_format, + width, height, + 0); + status = image->status; + if (unlikely (status)) + return status; + } + + *image_out = (cairo_image_surface_t *) image; + *shm_info_out = shm_info; + return CAIRO_STATUS_SUCCESS; +} + +static cairo_xcb_picture_t * +_render_to_picture (cairo_xcb_surface_t *target, + const cairo_pattern_t *pattern, + const cairo_rectangle_int_t *extents) +{ + cairo_image_surface_t *image; + cairo_xcb_shm_info_t *shm_info; + cairo_pattern_union_t copy; + cairo_status_t status; + cairo_xcb_picture_t *picture; + pixman_format_code_t pixman_format; + xcb_render_pictformat_t xrender_format; + + /* XXX handle extend modes via tiling? */ + /* XXX alpha-only masks? */ + + pixman_format = PIXMAN_a8r8g8b8; + xrender_format = target->screen->connection->standard_formats[CAIRO_FORMAT_ARGB32]; + + status = _cairo_xcb_shm_image_create (target->screen->connection, + pixman_format, + extents->width, extents->height, + &image, &shm_info); + if (unlikely (status)) + return (cairo_xcb_picture_t *) _cairo_surface_create_in_error (status); + + _cairo_pattern_init_static_copy (©.base, pattern); + cairo_matrix_translate (©.base.matrix, extents->x, extents->y); + status = _cairo_surface_paint (&image->base, + CAIRO_OPERATOR_SOURCE, + ©.base, + NULL); + if (unlikely (status)) { + cairo_surface_destroy (&image->base); + return (cairo_xcb_picture_t *) _cairo_surface_create_in_error (status); + } + + picture = _picture_from_image (target, xrender_format, image, shm_info); + cairo_surface_destroy (&image->base); + + if (unlikely (picture->base.status)) + return picture; + + _cairo_xcb_picture_set_component_alpha (picture, pattern->has_component_alpha); + picture->x = -extents->x; + picture->y = -extents->y; + + return picture; +} + +static xcb_render_fixed_t * +_gradient_to_xcb (const cairo_gradient_pattern_t *gradient, + char *buf, unsigned int buflen) +{ + xcb_render_fixed_t *stops; + xcb_render_color_t *colors; + unsigned int i; + + if (gradient->n_stops * (sizeof (xcb_render_fixed_t) + sizeof (xcb_render_color_t)) < buflen) + { + stops = (xcb_render_fixed_t *) buf; + } + else + { + stops = + _cairo_malloc_ab (gradient->n_stops, + sizeof (xcb_render_fixed_t) + sizeof (xcb_render_color_t)); + if (unlikely (stops == NULL)) + return NULL; + } + + colors = (xcb_render_color_t *) (stops + gradient->n_stops); + for (i = 0; i < gradient->n_stops; i++) { + stops[i] = + _cairo_fixed_16_16_from_double (gradient->stops[i].offset); + + colors[i].red = gradient->stops[i].color.red_short; + colors[i].green = gradient->stops[i].color.green_short; + colors[i].blue = gradient->stops[i].color.blue_short; + colors[i].alpha = gradient->stops[i].color.alpha_short; + } + + return stops; +} + +static cairo_xcb_picture_t * +_cairo_xcb_linear_picture (cairo_xcb_surface_t *target, + const cairo_linear_pattern_t *pattern, + const cairo_rectangle_int_t *extents) +{ + char buf[CAIRO_STACK_BUFFER_SIZE]; + cairo_fixed_t xdim, ydim; + xcb_render_fixed_t *stops; + xcb_render_color_t *colors; + xcb_render_pointfix_t p1, p2; + cairo_matrix_t matrix = pattern->base.base.matrix; + cairo_xcb_picture_t *picture; + cairo_status_t status; + + picture = (cairo_xcb_picture_t *) + _cairo_xcb_screen_lookup_linear_picture (target->screen, pattern); + if (picture != NULL) + goto setup_picture; + + stops = _gradient_to_xcb (&pattern->base, buf, sizeof (buf)); + if (unlikely (stops == NULL)) + return (cairo_xcb_picture_t *) _cairo_surface_create_in_error (CAIRO_STATUS_NO_MEMORY); + + picture = _cairo_xcb_picture_create (target->screen, + target->screen->connection->standard_formats[CAIRO_FORMAT_ARGB32], + PIXMAN_a8r8g8b8, + -1, -1); + if (unlikely (picture->base.status)) { + if (stops != (xcb_render_fixed_t *) buf) + free (stops); + return picture; + } + picture->filter = CAIRO_FILTER_DEFAULT; + + xdim = pattern->p2.x - pattern->p1.x; + ydim = pattern->p2.y - pattern->p1.y; + + /* + * Transform the matrix to avoid overflow when converting between + * cairo_fixed_t and pixman_fixed_t (without incurring performance + * loss when the transformation is unnecessary). + * + * XXX: Consider converting out-of-range co-ordinates and transforms. + * Having a function to compute the required transformation to + * "normalize" a given bounding box would be generally useful - + * cf linear patterns, gradient patterns, surface patterns... + */ +#define PIXMAN_MAX_INT ((pixman_fixed_1 >> 1) - pixman_fixed_e) /* need to ensure deltas also fit */ + if (unlikely (_cairo_fixed_integer_ceil (xdim) > PIXMAN_MAX_INT || + _cairo_fixed_integer_ceil (ydim) > PIXMAN_MAX_INT)) + { + double sf; + + if (xdim > ydim) + sf = PIXMAN_MAX_INT / _cairo_fixed_to_double (xdim); + else + sf = PIXMAN_MAX_INT / _cairo_fixed_to_double (ydim); + + p1.x = _cairo_fixed_16_16_from_double (_cairo_fixed_to_double (pattern->p1.x) * sf); + p1.y = _cairo_fixed_16_16_from_double (_cairo_fixed_to_double (pattern->p1.y) * sf); + p2.x = _cairo_fixed_16_16_from_double (_cairo_fixed_to_double (pattern->p2.x) * sf); + p2.y = _cairo_fixed_16_16_from_double (_cairo_fixed_to_double (pattern->p2.y) * sf); + + cairo_matrix_scale (&matrix, sf, sf); + } + else + { + p1.x = _cairo_fixed_to_16_16 (pattern->p1.x); + p1.y = _cairo_fixed_to_16_16 (pattern->p1.y); + p2.x = _cairo_fixed_to_16_16 (pattern->p2.x); + p2.y = _cairo_fixed_to_16_16 (pattern->p2.y); + } + + colors = (xcb_render_color_t *) (stops + pattern->base.n_stops); + _cairo_xcb_connection_render_create_linear_gradient (target->connection, + picture->picture, + p1, p2, + pattern->base.n_stops, + stops, colors); + + if (stops != (xcb_render_fixed_t *) buf) + free (stops); + + status = _cairo_xcb_screen_store_linear_picture (target->screen, + pattern, + &picture->base); + if (unlikely (status)) { + cairo_surface_destroy (&picture->base); + return (cairo_xcb_picture_t *) _cairo_surface_create_in_error (status); + } + +setup_picture: + _cairo_xcb_picture_set_matrix (picture, &matrix, + pattern->base.base.filter, + extents->x + extents->width/2., + extents->y + extents->height/2.); + _cairo_xcb_picture_set_filter (picture, pattern->base.base.filter); + _cairo_xcb_picture_set_extend (picture, pattern->base.base.extend); + _cairo_xcb_picture_set_component_alpha (picture, + pattern->base.base.has_component_alpha); + + return picture; +} + +static cairo_xcb_picture_t * +_cairo_xcb_radial_picture (cairo_xcb_surface_t *target, + const cairo_radial_pattern_t *pattern, + const cairo_rectangle_int_t *extents) +{ + char buf[CAIRO_STACK_BUFFER_SIZE]; + xcb_render_fixed_t *stops; + xcb_render_color_t *colors; + xcb_render_pointfix_t c1, c2; + xcb_render_fixed_t r1, r2; + cairo_xcb_picture_t *picture; + cairo_status_t status; + + picture = (cairo_xcb_picture_t *) + _cairo_xcb_screen_lookup_radial_picture (target->screen, pattern); + if (picture != NULL) + goto setup_picture; + + stops = _gradient_to_xcb (&pattern->base, buf, sizeof (buf)); + if (unlikely (stops == NULL)) + return (cairo_xcb_picture_t *) _cairo_surface_create_in_error (CAIRO_STATUS_NO_MEMORY); + + picture = _cairo_xcb_picture_create (target->screen, + target->screen->connection->standard_formats[CAIRO_FORMAT_ARGB32], + PIXMAN_a8r8g8b8, + -1, -1); + if (unlikely (picture->base.status)) { + if (stops != (xcb_render_fixed_t *) buf) + free (stops); + return picture; + } + picture->filter = CAIRO_FILTER_DEFAULT; + + c1.x = _cairo_fixed_to_16_16 (pattern->c1.x); + c1.y = _cairo_fixed_to_16_16 (pattern->c1.y); + r1 = _cairo_fixed_to_16_16 (pattern->r1); + c2.x = _cairo_fixed_to_16_16 (pattern->c2.x); + c2.y = _cairo_fixed_to_16_16 (pattern->c2.y); + r2 = _cairo_fixed_to_16_16 (pattern->r2); + + colors = (xcb_render_color_t *) (stops + pattern->base.n_stops); + _cairo_xcb_connection_render_create_radial_gradient (target->connection, + picture->picture, + c1, c2, r1, r2, + pattern->base.n_stops, + stops, colors); + + if (stops != (xcb_render_fixed_t *) buf) + free (stops); + + status = _cairo_xcb_screen_store_radial_picture (target->screen, + pattern, + &picture->base); + if (unlikely (status)) { + cairo_surface_destroy (&picture->base); + return (cairo_xcb_picture_t *) _cairo_surface_create_in_error (status); + } + +setup_picture: + _cairo_xcb_picture_set_matrix (picture, &pattern->base.base.matrix, + pattern->base.base.filter, + extents->x + extents->width/2., + extents->y + extents->height/2.); + _cairo_xcb_picture_set_filter (picture, pattern->base.base.filter); + _cairo_xcb_picture_set_extend (picture, pattern->base.base.extend); + _cairo_xcb_picture_set_component_alpha (picture, + pattern->base.base.has_component_alpha); + + return picture; +} + +static void +_decouple_cached_picture (cairo_surface_t *surface) +{ + cairo_xcb_picture_t *picture = (cairo_xcb_picture_t *) surface; + + if (! picture->base.finished) + _cairo_xcb_screen_remove_surface_picture (picture->screen, &picture->base); +} + +static cairo_xcb_picture_t * +_copy_to_picture (cairo_xcb_surface_t *source, + cairo_bool_t force) +{ + cairo_xcb_picture_t *picture; + uint32_t values[] = { 0, 1 }; + + /* XXX two level device locking, ensure we release the xcb device mutex? */ + if (source->drm != NULL) + cairo_surface_flush (source->drm); + + if (source->owns_pixmap && ! force) { + picture = _cairo_xcb_picture_copy (source); + } else { + picture = _cairo_xcb_picture_create (source->screen, + source->xrender_format, + source->pixman_format, + source->width, + source->height); + if (unlikely (picture->base.status)) + return picture; + + _cairo_xcb_connection_render_create_picture (source->connection, + picture->picture, + source->drawable, + source->xrender_format, + XCB_RENDER_CP_GRAPHICS_EXPOSURE | + XCB_RENDER_CP_SUBWINDOW_MODE, + values); + } + + return picture; +} + +static cairo_xcb_picture_t * +_cairo_xcb_surface_picture (cairo_xcb_surface_t *target, + const cairo_surface_pattern_t *pattern, + const cairo_rectangle_int_t *extents) +{ + cairo_surface_t *source; + cairo_xcb_picture_t *picture; + cairo_filter_t filter; + cairo_extend_t extend; + cairo_status_t status; + + source = pattern->surface; + if (source->is_clear) { + if (source->content & CAIRO_CONTENT_ALPHA) + return _cairo_xcb_transparent_picture (target); + else + return _cairo_xcb_black_picture (target); + } + + picture = (cairo_xcb_picture_t *) + _cairo_surface_has_snapshot (source, &_cairo_xcb_picture_backend); + if (picture != NULL) { + if (picture->screen == target->screen) { + picture = (cairo_xcb_picture_t *) cairo_surface_reference (&picture->base); + goto setup_picture; + } + } + + if (source->type == CAIRO_SURFACE_TYPE_XCB) + { + if (source->backend->type == CAIRO_SURFACE_TYPE_XCB) { + if (((cairo_xcb_surface_t *) source)->screen == target->screen) { + picture = _copy_to_picture ((cairo_xcb_surface_t *) source, FALSE); + if (unlikely (picture->base.status)) + return picture; + } + } else if (source->backend->type == CAIRO_INTERNAL_SURFACE_TYPE_SUBSURFACE) { + cairo_surface_subsurface_t *sub = (cairo_surface_subsurface_t *) source; + cairo_xcb_surface_t *xcb = (cairo_xcb_surface_t *) sub->target; + + /* XXX repeat interval with source clipping? */ + if (FALSE && xcb->screen == target->screen) { + xcb_rectangle_t rect; + + picture = _copy_to_picture (xcb, TRUE); + if (unlikely (picture->base.status)) + return picture; + + rect.x = sub->extents.x; + rect.y = sub->extents.y; + rect.width = sub->extents.width; + rect.height = sub->extents.height; + + _cairo_xcb_connection_render_set_picture_clip_rectangles (xcb->connection, + picture->picture, + 0, 0, + 1, &rect); + picture->x0 = rect.x; + picture->y0 = rect.y; + picture->width = rect.width; + picture->height = rect.height; + } + } else if (source->backend->type == CAIRO_INTERNAL_SURFACE_TYPE_SNAPSHOT) { + cairo_surface_snapshot_t *snap = (cairo_surface_snapshot_t *) source; + cairo_xcb_surface_t *xcb = (cairo_xcb_surface_t *) snap->target; + + if (xcb->screen == target->screen) { + picture = _copy_to_picture (xcb, TRUE); + if (unlikely (picture->base.status)) + return picture; + } + } + } +#if CAIRO_HAS_XLIB_XCB_FUNCTIONS + else if (source->type == CAIRO_SURFACE_TYPE_XLIB) + { + if (source->backend->type == CAIRO_SURFACE_TYPE_XLIB) { + if (((cairo_xlib_xcb_surface_t *) source)->xcb->screen == target->screen) { + picture = _copy_to_picture (((cairo_xlib_xcb_surface_t *) source)->xcb, + FALSE); + if (unlikely (picture->base.status)) + return picture; + } + } else if (source->backend->type == CAIRO_INTERNAL_SURFACE_TYPE_SUBSURFACE) { + cairo_surface_subsurface_t *sub = (cairo_surface_subsurface_t *) source; + cairo_xcb_surface_t *xcb = ((cairo_xlib_xcb_surface_t *) sub->target)->xcb; + + if (FALSE && xcb->screen == target->screen) { + xcb_rectangle_t rect; + + picture = _copy_to_picture (xcb, TRUE); + if (unlikely (picture->base.status)) + return picture; + + rect.x = sub->extents.x; + rect.y = sub->extents.y; + rect.width = sub->extents.width; + rect.height = sub->extents.height; + + _cairo_xcb_connection_render_set_picture_clip_rectangles (xcb->connection, + picture->picture, + 0, 0, + 1, &rect); + picture->x0 = rect.x; + picture->y0 = rect.y; + picture->width = rect.width; + picture->height = rect.height; + } + } else if (source->backend->type == CAIRO_INTERNAL_SURFACE_TYPE_SNAPSHOT) { + cairo_surface_snapshot_t *snap = (cairo_surface_snapshot_t *) source; + cairo_xcb_surface_t *xcb = ((cairo_xlib_xcb_surface_t *) snap->target)->xcb; + + if (xcb->screen == target->screen) { + picture = _copy_to_picture (xcb, TRUE); + if (unlikely (picture->base.status)) + return picture; + } + } + } +#endif +#if CAIRO_HAS_XCB_DRM_FUNCTIONS && CAIRO_HAS_DRM_SURFACE + else if (source->type == CAIRO_SURFACE_TYPE_DRM && + target->drm != NULL && + target->drm->device == source->device) + { + cairo_drm_surface_t *drm = (cairo_drm_surface_t *) source; + cairo_xcb_surface_t *tmp; + xcb_pixmap_t pixmap; + pixman_format_code_t pixman_format; + cairo_surface_pattern_t pattern; + + /* XXX XRenderCreatePictureForNative: + * Copy the source to a temporary pixmap if possible. + * Still cheaper than pushing the image via the CPU. + */ + + switch (drm->format) { + case CAIRO_FORMAT_A1: + pixman_format = PIXMAN_a1; + break; + case CAIRO_FORMAT_A8: + pixman_format = PIXMAN_a8; + break; + case CAIRO_FORMAT_RGB24: + pixman_format = PIXMAN_x8r8g8b8; + break; + default: + case CAIRO_FORMAT_ARGB32: + pixman_format = PIXMAN_a8r8g8b8; + break; + } + + pixmap = + _cairo_xcb_connection_create_pixmap (target->connection, + PIXMAN_FORMAT_DEPTH (pixman_format), + target->drawable, + drm->width, drm->height); + + tmp = (cairo_xcb_surface_t *) + _cairo_xcb_surface_create_internal (target->screen, + pixmap, TRUE, + pixman_format, + target->connection->standard_formats[drm->format], + drm->width, drm->height); + if (unlikely (tmp->base.status)) { + _cairo_xcb_connection_free_pixmap (target->connection, pixmap); + return (cairo_xcb_picture_t *) tmp; + } + + _cairo_pattern_init_for_surface (&pattern, source); + status = _cairo_surface_paint (&tmp->base, + CAIRO_OPERATOR_SOURCE, + &pattern.base, + NULL); + _cairo_pattern_fini (&pattern.base); + + if (unlikely (status)) { + cairo_surface_destroy (&tmp->base); + return (cairo_xcb_picture_t *) _cairo_surface_create_in_error (status); + } + + picture = _copy_to_picture (tmp, FALSE); + cairo_surface_destroy (&tmp->base); + + if (unlikely (picture->base.status)) + return picture; + } +#endif +#if CAIRO_HAS_GL_FUNCTIONS + else if (source->type == CAIRO_SURFACE_TYPE_GL) + { + /* pixmap from texture */ + } +#endif + + if (picture == NULL) { + cairo_image_surface_t *image; + void *image_extra; + cairo_status_t status; + + status = _cairo_surface_acquire_source_image (source, &image, &image_extra); + if (unlikely (status)) + return (cairo_xcb_picture_t *) _cairo_surface_create_in_error (status); + + if (image->format != CAIRO_FORMAT_INVALID) { + xcb_render_pictformat_t format; + + format = target->screen->connection->standard_formats[image->format]; + + picture = _picture_from_image (target, format, image, NULL); + _cairo_surface_release_source_image (source, image, image_extra); + } else { + cairo_image_surface_t *conv; + cairo_format_t format; + xcb_render_pictformat_t render_format; + + /* XXX XRenderPutImage! */ + + switch (image->base.content) { + case CAIRO_CONTENT_ALPHA: + format = CAIRO_FORMAT_A8; + break; + case CAIRO_CONTENT_COLOR: + format = CAIRO_FORMAT_RGB24; + break; + case CAIRO_CONTENT_COLOR_ALPHA: + format = CAIRO_FORMAT_ARGB32; + break; + } + + conv = _cairo_image_surface_coerce (image, format); + _cairo_surface_release_source_image (source, image, image_extra); + if (unlikely (conv->base.status)) + return (cairo_xcb_picture_t *) conv; + + render_format = target->screen->connection->standard_formats[format]; + picture = _picture_from_image (target, render_format, conv, NULL); + cairo_surface_destroy (&conv->base); + } + + if (unlikely (picture->base.status)) + return picture; + } + + status = _cairo_xcb_screen_store_surface_picture (target->screen, + &picture->base, + CAIRO_STRIDE_FOR_WIDTH_BPP (picture->width, + PIXMAN_FORMAT_BPP (picture->pixman_format)) + * picture->height); + if (unlikely (status)) { + cairo_surface_destroy (&picture->base); + return (cairo_xcb_picture_t *) _cairo_surface_create_in_error (status); + } + + status = _cairo_surface_attach_snapshot (source, + &picture->base, + _decouple_cached_picture); + if (unlikely (status)) { + cairo_surface_destroy (&picture->base); + return (cairo_xcb_picture_t *) _cairo_surface_create_in_error (status); + } + +setup_picture: + filter = pattern->base.filter; + if (filter != CAIRO_FILTER_NEAREST && + _cairo_matrix_has_unity_scale (&pattern->base.matrix) && + _cairo_fixed_is_integer (_cairo_fixed_from_double (pattern->base.matrix.x0)) && + _cairo_fixed_is_integer (_cairo_fixed_from_double (pattern->base.matrix.y0))) + { + filter = CAIRO_FILTER_NEAREST; + } + _cairo_xcb_picture_set_filter (picture, filter); + + _cairo_xcb_picture_set_matrix (picture, + &pattern->base.matrix, filter, + extents->x + extents->width/2., + extents->y + extents->height/2.); + + + extend = pattern->base.extend; + if (extents->x >= 0 && extents->x + extents->width <= picture->width && + extents->y >= 0 && extents->y + extents->height <= picture->height) + { + extend = CAIRO_EXTEND_NONE; + } + _cairo_xcb_picture_set_extend (picture, extend); + _cairo_xcb_picture_set_component_alpha (picture, pattern->base.has_component_alpha); + + return picture; +} + +static cairo_xcb_picture_t * +_cairo_xcb_picture_for_pattern (cairo_xcb_surface_t *target, + const cairo_pattern_t *pattern, + const cairo_rectangle_int_t *extents) +{ + if (pattern == NULL) + return _cairo_xcb_white_picture (target); + + if (! _pattern_is_supported (target->flags, pattern)) + return _render_to_picture (target, pattern, extents); + + switch (pattern->type) { + case CAIRO_PATTERN_TYPE_SOLID: + return _cairo_xcb_solid_picture (target, (cairo_solid_pattern_t *) pattern); + + case CAIRO_PATTERN_TYPE_LINEAR: + return _cairo_xcb_linear_picture (target, + (cairo_linear_pattern_t *) pattern, + extents); + + case CAIRO_PATTERN_TYPE_RADIAL: + return _cairo_xcb_radial_picture (target, + (cairo_radial_pattern_t *) pattern, + extents); + + case CAIRO_PATTERN_TYPE_SURFACE: + return _cairo_xcb_surface_picture (target, + (cairo_surface_pattern_t *) pattern, + extents); + default: + ASSERT_NOT_REACHED; + return _render_to_picture (target, pattern, extents); + } +} + +COMPILE_TIME_ASSERT (sizeof (xcb_rectangle_t) <= sizeof (cairo_box_t)); + +static cairo_status_t +_render_fill_boxes (void *abstract_dst, + cairo_operator_t op, + const cairo_color_t *color, + cairo_boxes_t *boxes) +{ + cairo_xcb_surface_t *dst = abstract_dst; + xcb_rectangle_t stack_xrects[CAIRO_STACK_ARRAY_LENGTH (sizeof (xcb_rectangle_t))]; + xcb_rectangle_t *xrects = stack_xrects; + xcb_render_color_t render_color; + int render_op = _render_operator (op); + struct _cairo_boxes_chunk *chunk; + int max_count; + + render_color.red = color->red_short; + render_color.green = color->green_short; + render_color.blue = color->blue_short; + render_color.alpha = color->alpha_short; + + max_count = 0; + for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) { + if (chunk->count > max_count) + max_count = chunk->count; + } + if (max_count > ARRAY_LENGTH (stack_xrects)) { + xrects = _cairo_malloc_ab (max_count, sizeof (xcb_rectangle_t)); + if (unlikely (xrects == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) { + int i, j; + + for (i = j = 0; i < chunk->count; i++) { + int x1 = _cairo_fixed_integer_round (chunk->base[i].p1.x); + int y1 = _cairo_fixed_integer_round (chunk->base[i].p1.y); + int x2 = _cairo_fixed_integer_round (chunk->base[i].p2.x); + int y2 = _cairo_fixed_integer_round (chunk->base[i].p2.y); + + if (x2 > x1 && y2 > y1) { + xrects[j].x = x1; + xrects[j].y = y1; + xrects[j].width = x2 - x1; + xrects[j].height = y2 - y1; + j++; + } + } + + if (j) { + _cairo_xcb_connection_render_fill_rectangles + (dst->connection, + render_op, dst->picture, + render_color, j, xrects); + } + } + + if (xrects != stack_xrects) + free (xrects); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_render_composite_boxes (cairo_xcb_surface_t *dst, + cairo_operator_t op, + const cairo_pattern_t *src_pattern, + const cairo_pattern_t *mask_pattern, + const cairo_rectangle_int_t *extents, + const cairo_boxes_t *boxes) +{ + cairo_xcb_picture_t *src, *mask; + const struct _cairo_boxes_chunk *chunk; + int render_op; + + render_op = _render_operator (op); + + if (src_pattern == NULL) { + src_pattern = mask_pattern; + mask_pattern = NULL; + } + + src = _cairo_xcb_picture_for_pattern (dst, src_pattern, extents); + if (unlikely (src->base.status)) + return src->base.status; + + if (mask_pattern != NULL) { + mask = _cairo_xcb_picture_for_pattern (dst, mask_pattern, extents); + if (unlikely (mask->base.status)) { + cairo_surface_destroy (&src->base); + return mask->base.status; + } + + for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) { + const cairo_box_t *box = chunk->base; + int i; + + for (i = 0; i < chunk->count; i++) { + int x = _cairo_fixed_integer_round (box[i].p1.x); + int y = _cairo_fixed_integer_round (box[i].p1.y); + int width = _cairo_fixed_integer_round (box[i].p2.x) - x; + int height = _cairo_fixed_integer_round (box[i].p2.y) - y; + + if (width && height) { + _cairo_xcb_connection_render_composite (dst->connection, + render_op, + src->picture, + mask->picture, + dst->picture, + x + src->x, + y + src->y, + x + mask->x, + y + mask->y, + x, y, + width, height); + } + } + } + + cairo_surface_destroy (&mask->base); + } else { + for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) { + const cairo_box_t *box = chunk->base; + int i; + + for (i = 0; i < chunk->count; i++) { + int x = _cairo_fixed_integer_round (box[i].p1.x); + int y = _cairo_fixed_integer_round (box[i].p1.y); + int width = _cairo_fixed_integer_round (box[i].p2.x) - x; + int height = _cairo_fixed_integer_round (box[i].p2.y) - y; + + if (width && height) { + _cairo_xcb_connection_render_composite (dst->connection, + render_op, + src->picture, + XCB_NONE, + dst->picture, + x + src->x, + y + src->y, + 0, 0, + x, y, + width, height); + } + } + } + } + + cairo_surface_destroy (&src->base); + + return CAIRO_STATUS_SUCCESS; +} + + +#define CAIRO_FIXED_16_16_MIN _cairo_fixed_from_int (-32768) +#define CAIRO_FIXED_16_16_MAX _cairo_fixed_from_int (32767) + +static cairo_bool_t +_line_exceeds_16_16 (const cairo_line_t *line) +{ + return + line->p1.x <= CAIRO_FIXED_16_16_MIN || + line->p1.x >= CAIRO_FIXED_16_16_MAX || + + line->p2.x <= CAIRO_FIXED_16_16_MIN || + line->p2.x >= CAIRO_FIXED_16_16_MAX || + + line->p1.y <= CAIRO_FIXED_16_16_MIN || + line->p1.y >= CAIRO_FIXED_16_16_MAX || + + line->p2.y <= CAIRO_FIXED_16_16_MIN || + line->p2.y >= CAIRO_FIXED_16_16_MAX; +} + +static void +_project_line_x_onto_16_16 (const cairo_line_t *line, + cairo_fixed_t top, + cairo_fixed_t bottom, + xcb_render_linefix_t *out) +{ + cairo_point_double_t p1, p2; + double m; + + p1.x = _cairo_fixed_to_double (line->p1.x); + p1.y = _cairo_fixed_to_double (line->p1.y); + + p2.x = _cairo_fixed_to_double (line->p2.x); + p2.y = _cairo_fixed_to_double (line->p2.y); + + m = (p2.x - p1.x) / (p2.y - p1.y); + out->p1.x = _cairo_fixed_16_16_from_double (p1.x + m * _cairo_fixed_to_double (top - line->p1.y)); + out->p2.x = _cairo_fixed_16_16_from_double (p1.x + m * _cairo_fixed_to_double (bottom - line->p1.y)); +} + +typedef struct { + cairo_traps_t traps; + cairo_antialias_t antialias; +} composite_traps_info_t; + +COMPILE_TIME_ASSERT (sizeof (xcb_render_trapezoid_t) <= sizeof (cairo_trapezoid_t)); + +static cairo_status_t +_composite_traps (void *closure, + cairo_xcb_surface_t *dst, + cairo_operator_t op, + const cairo_pattern_t *pattern, + int dst_x, int dst_y, + const cairo_rectangle_int_t *extents, + cairo_region_t *clip_region) +{ + composite_traps_info_t *info = closure; + const cairo_traps_t *traps = &info->traps; + cairo_xcb_picture_t *src; + cairo_format_t format; + xcb_render_pictformat_t xrender_format; + xcb_render_trapezoid_t *xtraps; + int render_reference_x, render_reference_y; + int i; + + src = _cairo_xcb_picture_for_pattern (dst, pattern, extents); + if (unlikely (src->base.status)) + return src->base.status; + + if (info->antialias == CAIRO_ANTIALIAS_NONE) + format = CAIRO_FORMAT_A1; + else + format = CAIRO_FORMAT_A8; + xrender_format = dst->screen->connection->standard_formats[format]; + + xtraps = (xcb_render_trapezoid_t *) traps->traps; + for (i = 0; i < traps->num_traps; i++) { + cairo_trapezoid_t t = traps->traps[i]; + + /* top/bottom will be clamped to surface bounds */ + xtraps[i].top = _cairo_fixed_to_16_16 (t.top); + xtraps[i].top -= dst_y << 16; + xtraps[i].bottom = _cairo_fixed_to_16_16 (t.bottom); + xtraps[i].bottom -= dst_y << 16; + + /* However, all the other coordinates will have been left untouched so + * as not to introduce numerical error. Recompute them if they + * exceed the 16.16 limits. + */ + if (unlikely (_line_exceeds_16_16 (&t.left))) { + _project_line_x_onto_16_16 (&t.left, + t.top, + t.bottom, + &xtraps[i].left); + xtraps[i].left.p1.y = xtraps[i].top; + xtraps[i].left.p2.y = xtraps[i].bottom; + } else { + xtraps[i].left.p1.x = _cairo_fixed_to_16_16 (t.left.p1.x); + xtraps[i].left.p1.y = _cairo_fixed_to_16_16 (t.left.p1.y); + xtraps[i].left.p2.x = _cairo_fixed_to_16_16 (t.left.p2.x); + xtraps[i].left.p2.y = _cairo_fixed_to_16_16 (t.left.p2.y); + } + xtraps[i].left.p1.x -= dst_x << 16; + xtraps[i].left.p1.y -= dst_y << 16; + xtraps[i].left.p2.x -= dst_x << 16; + xtraps[i].left.p2.y -= dst_y << 16; + + if (unlikely (_line_exceeds_16_16 (&t.right))) { + _project_line_x_onto_16_16 (&t.right, + t.top, + t.bottom, + &xtraps[i].right); + xtraps[i].right.p1.y = xtraps[i].top; + xtraps[i].right.p2.y = xtraps[i].bottom; + } else { + xtraps[i].right.p1.x = _cairo_fixed_to_16_16 (t.right.p1.x); + xtraps[i].right.p1.y = _cairo_fixed_to_16_16 (t.right.p1.y); + xtraps[i].right.p2.x = _cairo_fixed_to_16_16 (t.right.p2.x); + xtraps[i].right.p2.y = _cairo_fixed_to_16_16 (t.right.p2.y); + } + xtraps[i].right.p1.x -= dst_x << 16; + xtraps[i].right.p1.y -= dst_y << 16; + xtraps[i].right.p2.x -= dst_x << 16; + xtraps[i].right.p2.y -= dst_y << 16; + } + + if (xtraps[0].left.p1.y < xtraps[0].left.p2.y) { + render_reference_x = xtraps[0].left.p1.x >> 16; + render_reference_y = xtraps[0].left.p1.y >> 16; + } else { + render_reference_x = xtraps[0].left.p2.x >> 16; + render_reference_y = xtraps[0].left.p2.y >> 16; + } + + _cairo_xcb_connection_render_trapezoids (dst->connection, + _render_operator (op), + src->picture, + dst->picture, + xrender_format, + src->x + render_reference_x, + src->y + render_reference_y, + traps->num_traps, xtraps); + + cairo_surface_destroy (&src->base); + + return CAIRO_STATUS_SUCCESS; +} + +/* low-level composite driver */ + +typedef cairo_status_t +(*xcb_draw_func_t) (void *closure, + cairo_xcb_surface_t *dst, + cairo_operator_t op, + const cairo_pattern_t *src, + int dst_x, + int dst_y, + const cairo_rectangle_int_t *extents, + cairo_region_t *clip_region); + +static cairo_xcb_surface_t * +_create_composite_mask (cairo_clip_t *clip, + xcb_draw_func_t draw_func, + void *draw_closure, + cairo_xcb_surface_t *dst, + const cairo_rectangle_int_t*extents) +{ + cairo_xcb_surface_t *surface; + cairo_bool_t clip_surface = FALSE; + cairo_status_t status; + + if (clip != NULL) { + cairo_region_t *clip_region; + + status = _cairo_clip_get_region (clip, &clip_region); + assert (! _cairo_status_is_error (status)); + clip_surface = status == CAIRO_INT_STATUS_UNSUPPORTED; + } + + surface = (cairo_xcb_surface_t *) + _cairo_xcb_surface_create_similar (dst, CAIRO_CONTENT_ALPHA, + extents->width, extents->height); + if (unlikely (surface->base.status)) + return surface; + + _cairo_xcb_surface_ensure_picture (surface); + + if (surface->flags & CAIRO_XCB_RENDER_HAS_FILL_RECTANGLES) { + xcb_render_color_t clear; + xcb_rectangle_t xrect; + + clear.red = clear.green = clear.blue = clear.alpha = 0; + + xrect.x = xrect.y = 0; + xrect.width = extents->width; + xrect.height = extents->height; + + _cairo_xcb_connection_render_fill_rectangles (surface->connection, + XCB_RENDER_PICT_OP_CLEAR, + surface->picture, + clear, 1, &xrect); + } else { + status = _cairo_xcb_surface_render_paint (surface, + CAIRO_OPERATOR_CLEAR, + &_cairo_pattern_clear.base, + NULL); + if (unlikely (status)) { + cairo_surface_destroy (&surface->base); + return (cairo_xcb_surface_t *) _cairo_surface_create_in_error (status); + } + } + + /* Is it worth setting the clip region here? */ + status = draw_func (draw_closure, surface, + CAIRO_OPERATOR_ADD, NULL, + extents->x, extents->y, + extents, NULL); + if (unlikely (status)) { + cairo_surface_destroy (&surface->base); + return (cairo_xcb_surface_t *) _cairo_surface_create_in_error (status); + } + + if (clip_surface) { + status = _cairo_clip_combine_with_surface (clip, &surface->base, + extents->x, extents->y); + if (unlikely (status)) { + cairo_surface_destroy (&surface->base); + return (cairo_xcb_surface_t *) _cairo_surface_create_in_error (status); + } + } + + return surface; +} + +/* Handles compositing with a clip surface when the operator allows + * us to combine the clip with the mask + */ +static cairo_status_t +_clip_and_composite_with_mask (cairo_clip_t *clip, + cairo_operator_t op, + const cairo_pattern_t *pattern, + xcb_draw_func_t draw_func, + void *draw_closure, + cairo_xcb_surface_t *dst, + const cairo_rectangle_int_t*extents) +{ + cairo_xcb_surface_t *mask; + cairo_xcb_picture_t *src; + + mask = _create_composite_mask (clip, draw_func, draw_closure, dst, extents); + if (unlikely (mask->base.status)) + return mask->base.status; + + if (pattern != NULL || dst->base.content != CAIRO_CONTENT_ALPHA) { + src = _cairo_xcb_picture_for_pattern (dst, pattern, extents); + if (unlikely (src->base.status)) { + cairo_surface_destroy (&mask->base); + return src->base.status; + } + + _cairo_xcb_connection_render_composite (dst->connection, + _render_operator (op), + src->picture, + mask->picture, + dst->picture, + extents->x + src->x, extents->y + src->y, + 0, 0, + extents->x, extents->y, + extents->width, extents->height); + + cairo_surface_destroy (&src->base); + } else { + _cairo_xcb_connection_render_composite (dst->connection, + _render_operator (op), + mask->picture, + XCB_NONE, + dst->picture, + 0, 0, + 0, 0, + extents->x, extents->y, + extents->width, extents->height); + } + cairo_surface_destroy (&mask->base); + + return CAIRO_STATUS_SUCCESS; +} + +/* Handles compositing with a clip surface when we have to do the operation + * in two pieces and combine them together. + */ +static cairo_status_t +_clip_and_composite_combine (cairo_clip_t *clip, + cairo_operator_t op, + const cairo_pattern_t *pattern, + xcb_draw_func_t draw_func, + void *draw_closure, + cairo_xcb_surface_t *dst, + const cairo_rectangle_int_t*extents) +{ + cairo_xcb_surface_t *tmp; + cairo_surface_t *clip_surface; + xcb_render_picture_t clip_picture; + cairo_status_t status; + + tmp = (cairo_xcb_surface_t *) + _cairo_xcb_surface_create_similar (dst, dst->base.content, + extents->width, extents->height); + if (unlikely (tmp->base.status)) + return tmp->base.status; + + _cairo_xcb_surface_ensure_picture (tmp); + + if (pattern == NULL) { + status = (*draw_func) (draw_closure, tmp, + CAIRO_OPERATOR_ADD, NULL, + extents->x, extents->y, + extents, NULL); + } else { + /* Initialize the temporary surface from the destination surface */ + if (! dst->base.is_clear || + (dst->flags & CAIRO_XCB_RENDER_HAS_FILL_RECTANGLES) == 0) + { + /* XCopyArea may actually be quicker here. + * A good driver should translate if appropriate. + */ + _cairo_xcb_connection_render_composite (dst->connection, + XCB_RENDER_PICT_OP_SRC, + dst->picture, + XCB_NONE, + tmp->picture, + extents->x, extents->y, + 0, 0, + 0, 0, + extents->width, extents->height); + } + else + { + xcb_render_color_t clear; + xcb_rectangle_t xrect; + + clear.red = clear.green = clear.blue = clear.alpha = 0; + + xrect.x = xrect.y = 0; + xrect.width = extents->width; + xrect.height = extents->height; + + _cairo_xcb_connection_render_fill_rectangles (dst->connection, + XCB_RENDER_PICT_OP_CLEAR, + dst->picture, + clear, 1, &xrect); + } + + status = (*draw_func) (draw_closure, tmp, op, pattern, + extents->x, extents->y, + extents, NULL); + } + if (unlikely (status)) + goto CLEANUP_SURFACE; + + clip_surface = _cairo_clip_get_surface (clip, &dst->base); + if (unlikely (clip_surface->status)) + goto CLEANUP_SURFACE; + + clip_picture = ((cairo_xcb_surface_t *) clip_surface)->picture; + assert (clip_picture != XCB_NONE); + + if (dst->base.is_clear) { + _cairo_xcb_connection_render_composite (dst->connection, + XCB_RENDER_PICT_OP_SRC, + tmp->picture, clip_picture, dst->picture, + 0, 0, + 0, 0, + extents->x, extents->y, + extents->width, extents->height); + } else { + /* Punch the clip out of the destination */ + _cairo_xcb_connection_render_composite (dst->connection, + XCB_RENDER_PICT_OP_OUT_REVERSE, + clip_picture, XCB_NONE, dst->picture, + extents->x - clip->path->extents.x, + extents->y - clip->path->extents.y, + 0, 0, + extents->x, extents->y, + extents->width, extents->height); + + /* Now add the two results together */ + _cairo_xcb_connection_render_composite (dst->connection, + XCB_RENDER_PICT_OP_ADD, + tmp->picture, clip_picture, dst->picture, + 0, 0, + extents->x - clip->path->extents.x, + extents->y - clip->path->extents.y, + extents->x, extents->y, + extents->width, extents->height); + } + + CLEANUP_SURFACE: + cairo_surface_destroy (&tmp->base); + + return status; +} + +/* Handles compositing for %CAIRO_OPERATOR_SOURCE, which is special; it's + * defined as (src IN mask IN clip) ADD (dst OUT (mask IN clip)) + */ +static cairo_status_t +_clip_and_composite_source (cairo_clip_t *clip, + const cairo_pattern_t *pattern, + xcb_draw_func_t draw_func, + void *draw_closure, + cairo_xcb_surface_t *dst, + const cairo_rectangle_int_t *extents) +{ + cairo_xcb_surface_t *mask; + cairo_xcb_picture_t *src; + + /* Create a surface that is mask IN clip */ + mask = _create_composite_mask (clip, draw_func, draw_closure, dst, extents); + if (unlikely (mask->base.status)) + return mask->base.status; + + src = _cairo_xcb_picture_for_pattern (dst, pattern, extents); + if (unlikely (src->base.status)) { + cairo_surface_destroy (&mask->base); + return src->base.status; + } + + if (dst->base.is_clear) { + _cairo_xcb_connection_render_composite (dst->connection, + XCB_RENDER_PICT_OP_SRC, + src->picture, + mask->picture, + dst->picture, + extents->x + src->x, extents->y + src->y, + 0, 0, + extents->x, extents->y, + extents->width, extents->height); + } else { + /* Compute dest' = dest OUT (mask IN clip) */ + _cairo_xcb_connection_render_composite (dst->connection, + XCB_RENDER_PICT_OP_OUT_REVERSE, + mask->picture, + XCB_NONE, + dst->picture, + 0, 0, 0, 0, + extents->x, extents->y, + extents->width, extents->height); + + /* Now compute (src IN (mask IN clip)) ADD dest' */ + _cairo_xcb_connection_render_composite (dst->connection, + XCB_RENDER_PICT_OP_ADD, + src->picture, + mask->picture, + dst->picture, + extents->x + src->x, extents->y + src->y, + 0, 0, + extents->x, extents->y, + extents->width, extents->height); + } + + cairo_surface_destroy (&src->base); + cairo_surface_destroy (&mask->base); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_bool_t +can_reduce_alpha_op (cairo_operator_t op) +{ + int iop = op; + switch (iop) { + case CAIRO_OPERATOR_OVER: + case CAIRO_OPERATOR_SOURCE: + case CAIRO_OPERATOR_ADD: + return TRUE; + default: + return FALSE; + } +} + +static cairo_bool_t +reduce_alpha_op (cairo_surface_t *dst, + cairo_operator_t op, + const cairo_pattern_t *pattern) +{ + return dst->is_clear && + dst->content == CAIRO_CONTENT_ALPHA && + _cairo_pattern_is_opaque_solid (pattern) && + can_reduce_alpha_op (op); +} + +static cairo_status_t +_cairo_xcb_surface_fixup_unbounded (cairo_xcb_surface_t *dst, + const cairo_composite_rectangles_t *rects) +{ + xcb_rectangle_t xrects[4]; + int n; + + if (rects->bounded.width == rects->unbounded.width && + rects->bounded.height == rects->unbounded.height) + { + return CAIRO_STATUS_SUCCESS; + } + + n = 0; + /* top */ + if (rects->bounded.y != rects->unbounded.y) { + xrects[n].x = rects->unbounded.x; + xrects[n].width = rects->unbounded.width; + xrects[n].y = rects->unbounded.y; + xrects[n].height = rects->bounded.y - rects->unbounded.y; + n++; + } + /* left */ + if (rects->bounded.x != rects->unbounded.x) { + xrects[n].x = rects->unbounded.x; + xrects[n].width = rects->bounded.x - rects->unbounded.x; + xrects[n].y = rects->bounded.y; + xrects[n].height = rects->bounded.height; + n++; + } + /* right */ + if (rects->bounded.x + rects->bounded.width != rects->unbounded.x + rects->unbounded.width) { + xrects[n].x = rects->bounded.x + rects->bounded.width; + xrects[n].width = rects->unbounded.x + rects->unbounded.width - xrects[n].x; + xrects[n].y = rects->bounded.y; + xrects[n].height = rects->bounded.height; + n++; + } + /* bottom */ + if (rects->bounded.y + rects->bounded.height != rects->unbounded.y + rects->unbounded.height) { + xrects[n].x = rects->unbounded.x; + xrects[n].width = rects->unbounded.width; + xrects[n].y = rects->bounded.y + rects->bounded.height; + xrects[n].height = rects->unbounded.y + rects->unbounded.height - xrects[n].y; + n++; + } + + if (dst->flags & CAIRO_XCB_RENDER_HAS_FILL_RECTANGLES) { + xcb_render_color_t color; + + color.red = 0; + color.green = 0; + color.blue = 0; + color.alpha = 0; + + _cairo_xcb_connection_render_fill_rectangles (dst->connection, + XCB_RENDER_PICT_OP_CLEAR, + dst->picture, + color, n, xrects); + } else { + int i; + cairo_xcb_picture_t *src; + + src = _cairo_xcb_transparent_picture (dst); + if (unlikely (src->base.status)) + return src->base.status; + + for (i = 0; i < n; i++) { + _cairo_xcb_connection_render_composite (dst->connection, + XCB_RENDER_PICT_OP_CLEAR, + src->picture, XCB_NONE, dst->picture, + 0, 0, + 0, 0, + xrects[i].x, xrects[i].y, + xrects[i].width, xrects[i].height); + } + cairo_surface_destroy (&src->base); + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_xcb_surface_fixup_unbounded_with_mask (cairo_xcb_surface_t *dst, + const cairo_composite_rectangles_t *rects, + cairo_clip_t *clip) +{ + cairo_xcb_surface_t *mask; + int mask_x, mask_y; + + mask = (cairo_xcb_surface_t *) _cairo_clip_get_surface (clip, &dst->base); + if (unlikely (mask->base.status)) + return mask->base.status; + + mask_x = - clip->path->extents.x; + mask_y = - clip->path->extents.y; + + /* top */ + if (rects->bounded.y != rects->unbounded.y) { + int x = rects->unbounded.x; + int y = rects->unbounded.y; + int width = rects->unbounded.width; + int height = rects->bounded.y - y; + + _cairo_xcb_connection_render_composite (dst->connection, + XCB_RENDER_PICT_OP_OUT_REVERSE, + mask->picture, XCB_NONE, dst->picture, + x + mask_x, y + mask_y, + 0, 0, + x, y, + width, height); + } + + /* left */ + if (rects->bounded.x != rects->unbounded.x) { + int x = rects->unbounded.x; + int y = rects->bounded.y; + int width = rects->bounded.x - x; + int height = rects->bounded.height; + + _cairo_xcb_connection_render_composite (dst->connection, + XCB_RENDER_PICT_OP_OUT_REVERSE, + mask->picture, XCB_NONE, dst->picture, + x + mask_x, y + mask_y, + 0, 0, + x, y, + width, height); + } + + /* right */ + if (rects->bounded.x + rects->bounded.width != rects->unbounded.x + rects->unbounded.width) { + int x = rects->bounded.x + rects->bounded.width; + int y = rects->bounded.y; + int width = rects->unbounded.x + rects->unbounded.width - x; + int height = rects->bounded.height; + + _cairo_xcb_connection_render_composite (dst->connection, + XCB_RENDER_PICT_OP_OUT_REVERSE, + mask->picture, XCB_NONE, dst->picture, + x + mask_x, y + mask_y, + 0, 0, + x, y, + width, height); + } + + /* bottom */ + if (rects->bounded.y + rects->bounded.height != rects->unbounded.y + rects->unbounded.height) { + int x = rects->unbounded.x; + int y = rects->bounded.y + rects->bounded.height; + int width = rects->unbounded.width; + int height = rects->unbounded.y + rects->unbounded.height - y; + + _cairo_xcb_connection_render_composite (dst->connection, + XCB_RENDER_PICT_OP_OUT_REVERSE, + mask->picture, XCB_NONE, dst->picture, + x + mask_x, y + mask_y, + 0, 0, + x, y, + width, height); + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_xcb_surface_fixup_unbounded_boxes (cairo_xcb_surface_t *dst, + const cairo_composite_rectangles_t *extents, + cairo_region_t *clip_region, + cairo_boxes_t *boxes) +{ + cairo_boxes_t clear; + cairo_box_t box; + cairo_status_t status; + struct _cairo_boxes_chunk *chunk; + int i; + + if (boxes->num_boxes <= 1 && clip_region == NULL) + return _cairo_xcb_surface_fixup_unbounded (dst, extents); + + _cairo_boxes_init (&clear); + + box.p1.x = _cairo_fixed_from_int (extents->unbounded.x + extents->unbounded.width); + box.p1.y = _cairo_fixed_from_int (extents->unbounded.y); + box.p2.x = _cairo_fixed_from_int (extents->unbounded.x); + box.p2.y = _cairo_fixed_from_int (extents->unbounded.y + extents->unbounded.height); + + if (clip_region == NULL) { + cairo_boxes_t tmp; + + _cairo_boxes_init (&tmp); + + status = _cairo_boxes_add (&tmp, &box); + assert (status == CAIRO_STATUS_SUCCESS); + + tmp.chunks.next = &boxes->chunks; + tmp.num_boxes += boxes->num_boxes; + + status = _cairo_bentley_ottmann_tessellate_boxes (&tmp, + CAIRO_FILL_RULE_WINDING, + &clear); + + tmp.chunks.next = NULL; + } else { + pixman_box32_t *pbox; + + pbox = pixman_region32_rectangles (&clip_region->rgn, &i); + _cairo_boxes_limit (&clear, (cairo_box_t *) pbox, i); + + status = _cairo_boxes_add (&clear, &box); + assert (status == CAIRO_STATUS_SUCCESS); + + for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) { + for (i = 0; i < chunk->count; i++) { + status = _cairo_boxes_add (&clear, &chunk->base[i]); + if (unlikely (status)) { + _cairo_boxes_fini (&clear); + return status; + } + } + } + + status = _cairo_bentley_ottmann_tessellate_boxes (&clear, + CAIRO_FILL_RULE_WINDING, + &clear); + } + + if (likely (status == CAIRO_STATUS_SUCCESS)) { + status = _render_fill_boxes (dst, + CAIRO_OPERATOR_CLEAR, + CAIRO_COLOR_TRANSPARENT, + boxes); + } + + _cairo_boxes_fini (&clear); + + return status; +} + +static cairo_status_t +_clip_and_composite (cairo_xcb_surface_t *dst, + cairo_operator_t op, + const cairo_pattern_t *src, + xcb_draw_func_t draw_func, + void *draw_closure, + const cairo_composite_rectangles_t*extents, + cairo_clip_t *clip) +{ + cairo_status_t status; + cairo_region_t *clip_region = NULL; + cairo_bool_t need_clip_surface = FALSE; + + if (clip != NULL) { + status = _cairo_clip_get_region (clip, &clip_region); + if (unlikely (status == CAIRO_INT_STATUS_NOTHING_TO_DO)) + return CAIRO_STATUS_SUCCESS; + + assert (! _cairo_status_is_error (status)); + need_clip_surface = status == CAIRO_INT_STATUS_UNSUPPORTED; + + if (clip_region != NULL && cairo_region_num_rectangles (clip_region) == 1) + clip_region = NULL; + } + + status = _cairo_xcb_connection_acquire (dst->connection); + if (unlikely (status)) + return status; + + status = _cairo_xcb_connection_take_socket (dst->connection); + if (unlikely (status)) { + _cairo_xcb_connection_release (dst->connection); + return status; + } + + _cairo_xcb_surface_ensure_picture (dst); + + if (clip_region != NULL) + _cairo_xcb_surface_set_clip_region (dst, clip_region); + + if (reduce_alpha_op (&dst->base, op, src)) { + op = CAIRO_OPERATOR_ADD; + src = NULL; + } + + if (op == CAIRO_OPERATOR_SOURCE) { + status = _clip_and_composite_source (clip, src, + draw_func, draw_closure, + dst, &extents->bounded); + } else { + if (op == CAIRO_OPERATOR_CLEAR) { + op = CAIRO_OPERATOR_DEST_OUT; + src = NULL; + } + + if (need_clip_surface) { + if (extents->is_bounded) { + status = _clip_and_composite_with_mask (clip, op, src, + draw_func, draw_closure, + dst, &extents->bounded); + } else { + status = _clip_and_composite_combine (clip, op, src, + draw_func, draw_closure, + dst, &extents->bounded); + } + } else { + status = draw_func (draw_closure, + dst, op, src, + 0, 0, + &extents->bounded, + clip_region); + } + } + + if (status == CAIRO_STATUS_SUCCESS && ! extents->is_bounded) { + if (need_clip_surface) + status = _cairo_xcb_surface_fixup_unbounded_with_mask (dst, extents, clip); + else + status = _cairo_xcb_surface_fixup_unbounded (dst, extents); + } + + if (clip_region != NULL) + _cairo_xcb_surface_clear_clip_region (dst); + + _cairo_xcb_connection_release (dst->connection); + + return status; +} + +static cairo_status_t +_core_boxes (cairo_xcb_surface_t *dst, + cairo_operator_t op, + const cairo_pattern_t *src, + cairo_boxes_t *boxes, + cairo_antialias_t antialias, + cairo_clip_t *clip, + const cairo_composite_rectangles_t *extents) +{ + if (antialias != CAIRO_ANTIALIAS_NONE) { + if (! boxes->is_pixel_aligned) + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + if (clip != NULL) { + cairo_region_t *clip_region; + cairo_status_t status; + + status = _cairo_clip_get_region (clip, &clip_region); + assert (status == CAIRO_STATUS_SUCCESS || CAIRO_INT_STATUS_UNSUPPORTED); + + if (status == CAIRO_INT_STATUS_UNSUPPORTED) + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + if (op == CAIRO_OPERATOR_CLEAR) + return _cairo_xcb_surface_core_fill_boxes (dst, CAIRO_COLOR_TRANSPARENT, boxes); + + if (op == CAIRO_OPERATOR_OVER) { + if (_cairo_pattern_is_opaque (src, &extents->bounded)) + op = CAIRO_OPERATOR_SOURCE; + } + if (op != CAIRO_OPERATOR_SOURCE) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (src->type == CAIRO_PATTERN_TYPE_SOLID) { + return _cairo_xcb_surface_core_fill_boxes (dst, + &((cairo_solid_pattern_t *) src)->color, + boxes); + } + + return _cairo_xcb_surface_core_copy_boxes (dst, src, &extents->bounded, boxes); +} + +static cairo_status_t +_composite_boxes (cairo_xcb_surface_t *dst, + cairo_operator_t op, + const cairo_pattern_t *src, + cairo_boxes_t *boxes, + cairo_antialias_t antialias, + cairo_clip_t *clip, + const cairo_composite_rectangles_t *extents) +{ + cairo_bool_t need_clip_mask = FALSE; + cairo_region_t *clip_region = NULL; + cairo_status_t status; + + /* If the boxes are not pixel-aligned, we will need to compute a real mask */ + if (antialias != CAIRO_ANTIALIAS_NONE) { + if (! boxes->is_pixel_aligned) + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + if (clip != NULL) { + status = _cairo_clip_get_region (clip, &clip_region); + assert (status == CAIRO_STATUS_SUCCESS || CAIRO_INT_STATUS_UNSUPPORTED); + + need_clip_mask = status == CAIRO_INT_STATUS_UNSUPPORTED; + if (need_clip_mask && + (! extents->is_bounded || op == CAIRO_OPERATOR_SOURCE)) + { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + if (clip_region != NULL && cairo_region_num_rectangles (clip_region) == 1) + clip_region = NULL; + } + + status = _cairo_xcb_connection_acquire (dst->connection); + if (unlikely (status)) + return status; + + status = _cairo_xcb_connection_take_socket (dst->connection); + if (unlikely (status)) { + _cairo_xcb_connection_release (dst->connection); + return status; + } + + _cairo_xcb_surface_ensure_picture (dst); + if (dst->flags & CAIRO_XCB_RENDER_HAS_FILL_RECTANGLES && ! need_clip_mask && + (op == CAIRO_OPERATOR_CLEAR || src->type == CAIRO_PATTERN_TYPE_SOLID)) + { + const cairo_color_t *color; + + if (op == CAIRO_OPERATOR_CLEAR) + color = CAIRO_COLOR_TRANSPARENT; + else + color = &((cairo_solid_pattern_t *) src)->color; + + if (! (op == CAIRO_OPERATOR_IN && color->alpha >= 0xff00)) + status = _render_fill_boxes (dst, op, color, boxes); + } + else + { + cairo_surface_pattern_t mask; + + if (need_clip_mask) { + cairo_surface_t *clip_surface; + + clip_surface = _cairo_clip_get_surface (clip, &dst->base); + if (unlikely (clip_surface->status)) + return clip_surface->status; + + _cairo_pattern_init_for_surface (&mask, clip_surface); + mask.base.filter = CAIRO_FILTER_NEAREST; + cairo_matrix_init_translate (&mask.base.matrix, + -clip->path->extents.x, + -clip->path->extents.y); + + if (op == CAIRO_OPERATOR_CLEAR) { + src = NULL; + op = CAIRO_OPERATOR_DEST_OUT; + } + } + + status = _render_composite_boxes (dst, op, src, + need_clip_mask ? &mask.base : NULL, + &extents->bounded, boxes); + } + + if (status == CAIRO_STATUS_SUCCESS && ! extents->is_bounded) { + status = + _cairo_xcb_surface_fixup_unbounded_boxes (dst, extents, + clip_region, boxes); + } + + _cairo_xcb_connection_release (dst->connection); + + return status; +} + +static cairo_status_t +_clip_and_composite_boxes (cairo_xcb_surface_t *dst, + cairo_operator_t op, + const cairo_pattern_t *src, + cairo_boxes_t *boxes, + cairo_antialias_t antialias, + const cairo_composite_rectangles_t *extents, + cairo_clip_t *clip) +{ + composite_traps_info_t info; + cairo_status_t status; + + if (boxes->num_boxes == 0 && extents->is_bounded) + return CAIRO_STATUS_SUCCESS; + + if ((dst->flags & CAIRO_XCB_RENDER_HAS_COMPOSITE) == 0) + return _core_boxes (dst, op, src, boxes, antialias, clip, extents); + + /* Use a fast path if the boxes are pixel aligned */ + status = _composite_boxes (dst, op, src, boxes, antialias, clip, extents); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; + + if ((dst->flags & CAIRO_XCB_RENDER_HAS_COMPOSITE_TRAPEZOIDS) == 0) + return CAIRO_INT_STATUS_UNSUPPORTED; + + /* Otherwise render via a mask and composite in the usual fashion. */ + status = _cairo_traps_init_boxes (&info.traps, boxes); + if (unlikely (status)) + return status; + + info.antialias = antialias; + return _clip_and_composite (dst, op, src, + _composite_traps, &info, + extents, clip); +} + +static cairo_bool_t +_mono_edge_is_vertical (const cairo_line_t *line) +{ + return _cairo_fixed_integer_round (line->p1.x) == _cairo_fixed_integer_round (line->p2.x); +} + +static cairo_bool_t +_traps_are_pixel_aligned (cairo_traps_t *traps, + cairo_antialias_t antialias) +{ + int i; + + if (antialias == CAIRO_ANTIALIAS_NONE) { + for (i = 0; i < traps->num_traps; i++) { + if (! _mono_edge_is_vertical (&traps->traps[i].left) || + ! _mono_edge_is_vertical (&traps->traps[i].right)) + { + traps->maybe_region = FALSE; + return FALSE; + } + } + } else { + for (i = 0; i < traps->num_traps; i++) { + if (traps->traps[i].left.p1.x != traps->traps[i].left.p2.x || + traps->traps[i].right.p1.x != traps->traps[i].right.p2.x || + ! _cairo_fixed_is_integer (traps->traps[i].top) || + ! _cairo_fixed_is_integer (traps->traps[i].bottom) || + ! _cairo_fixed_is_integer (traps->traps[i].left.p1.x) || + ! _cairo_fixed_is_integer (traps->traps[i].right.p1.x)) + { + traps->maybe_region = FALSE; + return FALSE; + } + } + } + + return TRUE; +} + +static void +_boxes_for_traps (cairo_boxes_t *boxes, + cairo_traps_t *traps) +{ + int i; + + _cairo_boxes_init (boxes); + + boxes->num_boxes = traps->num_traps; + boxes->chunks.base = (cairo_box_t *) traps->traps; + boxes->chunks.count = traps->num_traps; + boxes->chunks.size = traps->num_traps; + + for (i = 0; i < traps->num_traps; i++) { + cairo_fixed_t x1 = traps->traps[i].left.p1.x; + cairo_fixed_t x2 = traps->traps[i].right.p1.x; + cairo_fixed_t y1 = traps->traps[i].top; + cairo_fixed_t y2 = traps->traps[i].bottom; + + boxes->chunks.base[i].p1.x = x1; + boxes->chunks.base[i].p1.y = y1; + boxes->chunks.base[i].p2.x = x2; + boxes->chunks.base[i].p2.y = y2; + + if (boxes->is_pixel_aligned) { + boxes->is_pixel_aligned = + _cairo_fixed_is_integer (x1) && _cairo_fixed_is_integer (y1) && + _cairo_fixed_is_integer (x2) && _cairo_fixed_is_integer (y2); + } + } +} + +typedef struct _cairo_xcb_surface_span_renderer { + cairo_span_renderer_t base; + + void *spans; + unsigned len; + unsigned size; + uint16_t spans_embedded[1024]; +} cairo_xcb_surface_span_renderer_t; + +static cairo_status_t +_cairo_xcb_surface_span_renderer_accumulate (void *abstract_renderer, + int y, + int height, + const cairo_half_open_span_t *spans, + unsigned num_spans) +{ + cairo_xcb_surface_span_renderer_t *renderer = abstract_renderer; + uint16_t *u16; + int len; + + len = 4 * (2 + num_spans); + if (renderer->size < renderer->len + len) { + char *new_spans; + + do { + renderer->size <<= 1; + } while (renderer->size < renderer->len + len); + + if (renderer->spans == renderer->spans_embedded) { + new_spans = malloc (renderer->size); + if (unlikely (new_spans == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + memcpy (new_spans, renderer->spans, renderer->len); + } else { + new_spans = realloc (renderer->spans, renderer->size); + if (unlikely (new_spans == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + renderer->spans = new_spans; + } + + u16 = (uint16_t *) ((char *) renderer->spans + renderer->len); + *u16++ = y; + *u16++ = height; + *u16++ = num_spans; + *u16++ = 0; + while (num_spans--) { + *u16++ = spans->x; + *u16++ = spans->coverage * 0x0101; + spans++; + } + renderer->len += len; + + return CAIRO_STATUS_SUCCESS; +} + +typedef struct { + cairo_polygon_t *polygon; + cairo_fill_rule_t fill_rule; + cairo_antialias_t antialias; +} composite_spans_info_t; + +static cairo_status_t +_composite_spans (void *closure, + cairo_xcb_surface_t *dst, + cairo_operator_t op, + const cairo_pattern_t *pattern, + int dst_x, + int dst_y, + const cairo_rectangle_int_t *extents, + cairo_region_t *clip_region) +{ + composite_spans_info_t *info = closure; + cairo_xcb_surface_span_renderer_t renderer; + cairo_scan_converter_t *converter; + cairo_status_t status; + cairo_xcb_picture_t *src; + + renderer.base.render_rows = _cairo_xcb_surface_span_renderer_accumulate; + renderer.spans = renderer.spans_embedded; + renderer.size = ARRAY_LENGTH (renderer.spans_embedded); + renderer.len = 0; + + converter = _cairo_tor_scan_converter_create (extents->x, + extents->x + extents->width, + extents->y, + extents->y + extents->height, + info->fill_rule); + status = converter->add_polygon (converter, info->polygon); + if (unlikely (status)) + goto CLEANUP_RENDERER; + + status = converter->generate (converter, &renderer.base); + if (unlikely (status)) + goto CLEANUP_CONVERTER; + + src = _cairo_xcb_picture_for_pattern (dst, pattern, extents); + status = src->base.status; + if (unlikely (status)) + goto CLEANUP_CONVERTER; + + _cairo_xcb_connection_render_spans (dst->connection, + dst->picture, + _render_operator (op), + src->picture, + extents->x + src->x, extents->y + src->y, + extents->x + dst_x, extents->y + dst_y, + extents->width, extents->height, + renderer.len >> 1, renderer.spans); + cairo_surface_destroy (&src->base); + + CLEANUP_CONVERTER: + converter->destroy (converter); + CLEANUP_RENDERER: + if (renderer.spans != renderer.spans_embedded) + free (renderer.spans); + + return status; +} + +static cairo_status_t +_composite_mask (void *closure, + cairo_xcb_surface_t *dst, + cairo_operator_t op, + const cairo_pattern_t *src_pattern, + int dst_x, + int dst_y, + const cairo_rectangle_int_t *extents, + cairo_region_t *clip_region) +{ + const cairo_pattern_t *mask_pattern = closure; + cairo_xcb_picture_t *src, *mask = NULL; + + if (src_pattern != NULL) { + src = _cairo_xcb_picture_for_pattern (dst, src_pattern, extents); + if (unlikely (src->base.status)) + return src->base.status; + + mask = _cairo_xcb_picture_for_pattern (dst, mask_pattern, extents); + if (unlikely (mask->base.status)) { + cairo_surface_destroy (&src->base); + return mask->base.status; + } + + _cairo_xcb_connection_render_composite (dst->connection, + _render_operator (op), + src->picture, + mask->picture, + dst->picture, + extents->x + src->x, extents->y + src->y, + extents->x + mask->x, extents->y + mask->y, + extents->x - dst_x, extents->y - dst_y, + extents->width, extents->height); + cairo_surface_destroy (&mask->base); + cairo_surface_destroy (&src->base); + } else { + src = _cairo_xcb_picture_for_pattern (dst, mask_pattern, extents); + if (unlikely (src->base.status)) + return src->base.status; + + _cairo_xcb_connection_render_composite (dst->connection, + _render_operator (op), + src->picture, + XCB_NONE, + dst->picture, + extents->x + src->x, extents->y + src->y, + 0, 0, + extents->x - dst_x, extents->y - dst_y, + extents->width, extents->height); + cairo_surface_destroy (&src->base); + } + + return CAIRO_STATUS_SUCCESS; +} + +/* high level rasteriser -> compositor */ + +static cairo_bool_t +box_is_aligned (const cairo_box_t *box) +{ + return + _cairo_fixed_is_integer (box->p1.x) && + _cairo_fixed_is_integer (box->p1.y) && + _cairo_fixed_is_integer (box->p2.x) && + _cairo_fixed_is_integer (box->p2.y); +} + +static inline cairo_status_t +_clip_to_boxes (cairo_clip_t **clip, + const cairo_composite_rectangles_t *extents, + cairo_box_t **boxes, + int *num_boxes) +{ + cairo_status_t status; + const cairo_rectangle_int_t *rect; + + rect = extents->is_bounded ? &extents->bounded: &extents->unbounded; + + if (*clip == NULL) + goto EXTENTS; + + status = _cairo_clip_rectangle (*clip, rect); + if (unlikely (status)) + return status; + + status = _cairo_clip_get_boxes (*clip, boxes, num_boxes); + switch ((int) status) { + case CAIRO_STATUS_SUCCESS: + if (extents->is_bounded || (*num_boxes == 1 && box_is_aligned (*boxes))) + *clip = NULL; + goto DONE; + + case CAIRO_INT_STATUS_UNSUPPORTED: + goto EXTENTS; + + default: + return status; + } + + EXTENTS: + status = CAIRO_STATUS_SUCCESS; + _cairo_box_from_rectangle (&(*boxes)[0], rect); + *num_boxes = 1; + DONE: + return status; +} + +static cairo_clip_path_t * +_clip_get_single_path (cairo_clip_t *clip) +{ + cairo_clip_path_t *iter = clip->path; + cairo_clip_path_t *path = NULL; + + do { + if ((iter->flags & CAIRO_CLIP_PATH_IS_BOX) == 0) { + if (path != NULL) + return FALSE; + + path = iter; + } + iter = iter->prev; + } while (iter != NULL); + + return path; +} + +cairo_int_status_t +_cairo_xcb_surface_render_paint (cairo_xcb_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_clip_t *clip) +{ + cairo_composite_rectangles_t extents; + cairo_boxes_t boxes; + cairo_box_t *clip_boxes = boxes.boxes_embedded; + cairo_clip_t local_clip; + cairo_clip_path_t *clip_path; + cairo_bool_t have_clip = FALSE; + int num_boxes = ARRAY_LENGTH (boxes.boxes_embedded); + cairo_status_t status; + + if (unlikely (! _operator_is_supported (surface->flags, op))) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if ((surface->flags & (CAIRO_XCB_RENDER_HAS_COMPOSITE_SPANS | + CAIRO_XCB_RENDER_HAS_COMPOSITE_TRAPEZOIDS | + CAIRO_XCB_RENDER_HAS_COMPOSITE)) == 0) + { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + status = _cairo_composite_rectangles_init_for_paint (&extents, + surface->width, + surface->height, + op, source, + clip); + if (unlikely (status)) + return status; + + if (_cairo_clip_contains_rectangle (clip, &extents)) + clip = NULL; + + if (clip != NULL) { + clip = _cairo_clip_init_copy (&local_clip, clip); + have_clip = TRUE; + } + + status = _clip_to_boxes (&clip, &extents, &clip_boxes, &num_boxes); + if (unlikely (status)) { + if (have_clip) + _cairo_clip_fini (&local_clip); + + return status; + } + + if (clip != NULL && + extents.is_bounded && + (clip_path = _clip_get_single_path (clip)) != NULL) + { + status = _cairo_xcb_surface_render_fill (surface, op, source, + &clip_path->path, + clip_path->fill_rule, + clip_path->tolerance, + clip_path->antialias, + NULL); + } + else + { + _cairo_boxes_init_for_array (&boxes, clip_boxes, num_boxes); + status = _clip_and_composite_boxes (surface, op, source, + &boxes, CAIRO_ANTIALIAS_DEFAULT, + &extents, clip); + if (clip_boxes != boxes.boxes_embedded) + free (clip_boxes); + } + + if (have_clip) + _cairo_clip_fini (&local_clip); + + return status; +} + +cairo_int_status_t +_cairo_xcb_surface_render_mask (cairo_xcb_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_pattern_t *mask, + cairo_clip_t *clip) +{ + cairo_composite_rectangles_t extents; + cairo_clip_t local_clip; + cairo_bool_t have_clip = FALSE; + cairo_status_t status; + + if (unlikely (! _operator_is_supported (surface->flags, op))) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if ((surface->flags & CAIRO_XCB_RENDER_HAS_COMPOSITE) == 0) + return CAIRO_INT_STATUS_UNSUPPORTED; + + status = _cairo_composite_rectangles_init_for_mask (&extents, + surface->width, surface->height, + op, source, mask, clip); + if (unlikely (status)) + return status; + + if (_cairo_clip_contains_rectangle (clip, &extents)) + clip = NULL; + + if (clip != NULL && extents.is_bounded) { + clip = _cairo_clip_init_copy (&local_clip, clip); + status = _cairo_clip_rectangle (clip, &extents.bounded); + if (unlikely (status)) { + _cairo_clip_fini (&local_clip); + return status; + } + have_clip = TRUE; + } + + status = _clip_and_composite (surface, op, source, + _composite_mask, (void *) mask, + &extents, clip); + + if (have_clip) + _cairo_clip_fini (&local_clip); + + return status; +} + +typedef struct { + cairo_polygon_t polygon; + cairo_fill_rule_t fill_rule; + cairo_antialias_t antialias; +} composite_polygon_info_t; + +static cairo_status_t +_cairo_xcb_surface_render_composite_polygon (cairo_xcb_surface_t *dst, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_polygon_t *polygon, + cairo_antialias_t antialias, + cairo_fill_rule_t fill_rule, + cairo_composite_rectangles_t *extents, + cairo_clip_t *clip) +{ + composite_traps_info_t traps; + cairo_bool_t clip_surface = FALSE; + cairo_status_t status; + cairo_bool_t is_not_empty; + + if (polygon->num_edges == 0) { + status = CAIRO_STATUS_SUCCESS; + + if (! extents->is_bounded) { + cairo_region_t *clip_region = NULL; + + if (clip != NULL) { + status = _cairo_clip_get_region (clip, &clip_region); + clip_surface = status == CAIRO_INT_STATUS_UNSUPPORTED; + + if (clip_region != NULL && cairo_region_num_rectangles (clip_region) == 1) + clip_region = NULL; + } + + if (clip_surface == FALSE) { + if (clip_region != NULL) + _cairo_xcb_surface_set_clip_region (dst, clip_region); + + status = _cairo_xcb_surface_fixup_unbounded (dst, extents); + + if (clip_region != NULL) + _cairo_xcb_surface_clear_clip_region (dst); + } else { + status = _cairo_xcb_surface_fixup_unbounded_with_mask (dst, + extents, + clip); + } + } + + return status; + } + + _cairo_box_round_to_rectangle (&polygon->extents, &extents->mask); + is_not_empty = _cairo_rectangle_intersect (&extents->bounded, &extents->mask); + if (extents->is_bounded && ! is_not_empty) + return CAIRO_STATUS_SUCCESS; + + if (dst->flags & CAIRO_XCB_RENDER_HAS_COMPOSITE_SPANS) { + composite_spans_info_t spans; + + spans.polygon = polygon; + spans.fill_rule = CAIRO_FILL_RULE_WINDING; + spans.antialias = antialias; + + return _clip_and_composite (dst, op, source, + _composite_spans, &spans, + extents, clip); + } + + _cairo_traps_init (&traps.traps); + + status = _cairo_bentley_ottmann_tessellate_polygon (&traps.traps, polygon, fill_rule); + if (unlikely (status)) + goto CLEANUP_TRAPS; + + if (clip != NULL) { + cairo_region_t *clip_region; + + status = _cairo_clip_get_region (clip, &clip_region); + clip_surface = status == CAIRO_INT_STATUS_UNSUPPORTED; + } + + if (traps.traps.has_intersections) { + if (traps.traps.is_rectangular) + status = _cairo_bentley_ottmann_tessellate_rectangular_traps (&traps.traps, CAIRO_FILL_RULE_WINDING); + else if (traps.traps.is_rectilinear) + status = _cairo_bentley_ottmann_tessellate_rectilinear_traps (&traps.traps, CAIRO_FILL_RULE_WINDING); + else + status = _cairo_bentley_ottmann_tessellate_traps (&traps.traps, CAIRO_FILL_RULE_WINDING); + if (unlikely (status)) + goto CLEANUP_TRAPS; + } + + /* Use a fast path if the trapezoids consist of a simple region, + * but we can only do this if we do not have a clip surface, or can + * substitute the mask with the clip. + */ + if (traps.traps.maybe_region && + _traps_are_pixel_aligned (&traps.traps, antialias) && + (! clip_surface || + (extents->is_bounded && op != CAIRO_OPERATOR_SOURCE))) + { + cairo_boxes_t boxes; + + _boxes_for_traps (&boxes, &traps.traps); + status = _clip_and_composite_boxes (dst, op, source, + &boxes, antialias, + extents, clip); + } + else + { + /* Otherwise render the trapezoids to a mask and composite in the usual + * fashion. + */ + traps.antialias = antialias; + status = _clip_and_composite (dst, op, source, + _composite_traps, &traps, + extents, clip); + } + +CLEANUP_TRAPS: + _cairo_traps_fini (&traps.traps); + + return status; +} + +static cairo_int_status_t +_cairo_xcb_surface_render_stroke_as_polygon (cairo_xcb_surface_t *dst, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_path_fixed_t *path, + const cairo_stroke_style_t *stroke_style, + const cairo_matrix_t *ctm, + const cairo_matrix_t *ctm_inverse, + double tolerance, + cairo_antialias_t antialias, + cairo_clip_t *clip, + const cairo_box_t *clip_boxes, + int num_boxes, + cairo_composite_rectangles_t *extents) +{ + cairo_polygon_t polygon; + cairo_status_t status; + + _cairo_polygon_init (&polygon); + _cairo_polygon_limit (&polygon, clip_boxes, num_boxes); + + status = _cairo_path_fixed_stroke_to_polygon (path, + stroke_style, + ctm, ctm_inverse, + tolerance, + &polygon); + if (likely (status == CAIRO_STATUS_SUCCESS)) { + status = _cairo_xcb_surface_render_composite_polygon (dst, op, source, + &polygon, antialias, + CAIRO_FILL_RULE_WINDING, + extents, clip); + } + + _cairo_polygon_fini (&polygon); + + return status; +} + +static void +_clear_image (cairo_surface_t *surface) +{ + cairo_image_surface_t *image = (cairo_image_surface_t *) surface; + memset (image->data, 0, image->stride * image->height); + surface->is_clear = TRUE; +} + +static cairo_status_t +_cairo_xcb_surface_render_stroke_via_mask (cairo_xcb_surface_t *dst, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_path_fixed_t *path, + const cairo_stroke_style_t *stroke_style, + const cairo_matrix_t *ctm, + const cairo_matrix_t *ctm_inverse, + double tolerance, + cairo_antialias_t antialias, + cairo_clip_t *clip, + const cairo_composite_rectangles_t *extents) +{ + cairo_surface_t *image; + cairo_status_t status; + int x, y; + + x = extents->bounded.x; + y = extents->bounded.y; + image = _cairo_xcb_surface_create_similar_image (dst, CAIRO_CONTENT_ALPHA, + extents->bounded.width, + extents->bounded.height); + if (unlikely (image->status)) + return image->status; + + _clear_image (image); + + status = _cairo_surface_offset_stroke (image, x, y, + CAIRO_OPERATOR_ADD, + &_cairo_pattern_white.base, + path, stroke_style, + ctm, ctm_inverse, + tolerance, antialias, + NULL); + if (likely (status == CAIRO_STATUS_SUCCESS)) { + cairo_surface_pattern_t mask; + + _cairo_pattern_init_for_surface (&mask, image); + mask.base.filter = CAIRO_FILTER_NEAREST; + + cairo_matrix_init_translate (&mask.base.matrix, -x, -y); + status = _clip_and_composite (dst, op, source, + _composite_mask, (void *) &mask.base, + extents, clip); + _cairo_pattern_fini (&mask.base); + } + + cairo_surface_finish (image); + cairo_surface_destroy (image); + + return status; +} + +cairo_int_status_t +_cairo_xcb_surface_render_stroke (cairo_xcb_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + 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_clip_t *clip) +{ + cairo_composite_rectangles_t extents; + cairo_box_t boxes_stack[32], *clip_boxes = boxes_stack; + int num_boxes = ARRAY_LENGTH (boxes_stack); + cairo_clip_t local_clip; + cairo_bool_t have_clip = FALSE; + cairo_status_t status; + + if (unlikely (! _operator_is_supported (surface->flags, op))) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if ((surface->flags & (CAIRO_XCB_RENDER_HAS_COMPOSITE_SPANS | + CAIRO_XCB_RENDER_HAS_COMPOSITE_TRAPEZOIDS | + CAIRO_XCB_RENDER_HAS_COMPOSITE)) == 0) + { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + status = _cairo_composite_rectangles_init_for_stroke (&extents, + surface->width, + surface->height, + op, source, + path, style, ctm, + clip); + if (unlikely (status)) + return status; + + if (_cairo_clip_contains_rectangle (clip, &extents)) + clip = NULL; + + if (clip != NULL) { + clip = _cairo_clip_init_copy (&local_clip, clip); + have_clip = TRUE; + } + + status = _clip_to_boxes (&clip, &extents, &clip_boxes, &num_boxes); + if (unlikely (status)) { + if (have_clip) + _cairo_clip_fini (&local_clip); + + return status; + } + + status = CAIRO_INT_STATUS_UNSUPPORTED; + if (path->is_rectilinear) { + cairo_boxes_t boxes; + + _cairo_boxes_init (&boxes); + _cairo_boxes_limit (&boxes, clip_boxes, num_boxes); + + status = _cairo_path_fixed_stroke_rectilinear_to_boxes (path, + style, + ctm, + &boxes); + if (likely (status == CAIRO_STATUS_SUCCESS)) { + status = _clip_and_composite_boxes (surface, op, source, + &boxes, antialias, + &extents, clip); + } + + _cairo_boxes_fini (&boxes); + } + + if (status == CAIRO_INT_STATUS_UNSUPPORTED) { + if (surface->flags & (CAIRO_XCB_RENDER_HAS_COMPOSITE_SPANS | CAIRO_XCB_RENDER_HAS_COMPOSITE_TRAPEZOIDS)) { + status = _cairo_xcb_surface_render_stroke_as_polygon (surface, op, source, + path, style, + ctm, ctm_inverse, + tolerance, antialias, + clip, clip_boxes, num_boxes, + &extents); + } else if (surface->flags & CAIRO_XCB_RENDER_HAS_COMPOSITE) { + status = _cairo_xcb_surface_render_stroke_via_mask (surface, op, source, + path, style, + ctm, ctm_inverse, + tolerance, antialias, + have_clip ? &local_clip : NULL, + &extents); + } else { + ASSERT_NOT_REACHED; + } + } + + if (clip_boxes != boxes_stack) + free (clip_boxes); + + if (have_clip) + _cairo_clip_fini (&local_clip); + + return status; +} + +static cairo_status_t +_cairo_xcb_surface_render_fill_as_polygon (cairo_xcb_surface_t *dst, + cairo_operator_t op, + const cairo_pattern_t*source, + cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias, + cairo_clip_t *clip, + cairo_box_t *clip_boxes, + int num_boxes, + cairo_composite_rectangles_t *extents) +{ + cairo_polygon_t polygon; + cairo_status_t status; + + _cairo_polygon_init (&polygon); + _cairo_polygon_limit (&polygon, clip_boxes, num_boxes); + + status = _cairo_path_fixed_fill_to_polygon (path, tolerance, &polygon); + if (likely (status == CAIRO_STATUS_SUCCESS)) { + status = _cairo_xcb_surface_render_composite_polygon (dst, op, source, + &polygon, antialias, + fill_rule, + extents, clip); + } + + _cairo_polygon_fini (&polygon); + + return status; +} + +static cairo_status_t +_cairo_xcb_surface_render_fill_via_mask (cairo_xcb_surface_t *dst, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias, + cairo_clip_t *clip, + const cairo_composite_rectangles_t *extents) +{ + cairo_surface_t *image; + cairo_status_t status; + int x, y; + + x = extents->bounded.x; + y = extents->bounded.y; + image = _cairo_xcb_surface_create_similar_image (dst, + CAIRO_CONTENT_ALPHA, + extents->bounded.width, + extents->bounded.height); + if (unlikely (image->status)) + return image->status; + + _clear_image (image); + + status = _cairo_surface_offset_fill (image, x, y, + CAIRO_OPERATOR_ADD, + &_cairo_pattern_white.base, + path, fill_rule, tolerance, antialias, + NULL); + if (likely (status == CAIRO_STATUS_SUCCESS)) { + cairo_surface_pattern_t mask; + + _cairo_pattern_init_for_surface (&mask, image); + mask.base.filter = CAIRO_FILTER_NEAREST; + + cairo_matrix_init_translate (&mask.base.matrix, -x, -y); + status = _clip_and_composite (dst, op, source, + _composite_mask, (void *) &mask.base, + extents, clip); + + _cairo_pattern_fini (&mask.base); + } + + cairo_surface_finish (image); + cairo_surface_destroy (image); + + return status; +} + +cairo_int_status_t +_cairo_xcb_surface_render_fill (cairo_xcb_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias, + cairo_clip_t *clip) +{ + cairo_composite_rectangles_t extents; + cairo_box_t boxes_stack[32], *clip_boxes = boxes_stack; + int num_boxes = ARRAY_LENGTH (boxes_stack); + cairo_clip_t local_clip; + cairo_bool_t have_clip = FALSE; + cairo_status_t status; + + if (unlikely (! _operator_is_supported (surface->flags, op))) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if ((surface->flags & (CAIRO_XCB_RENDER_HAS_COMPOSITE_SPANS | + CAIRO_XCB_RENDER_HAS_COMPOSITE_TRAPEZOIDS | + CAIRO_XCB_RENDER_HAS_COMPOSITE)) == 0) + { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + status = _cairo_composite_rectangles_init_for_fill (&extents, + surface->width, + surface->height, + op, source, path, + clip); + if (unlikely (status)) + return status; + + if (_cairo_clip_contains_rectangle (clip, &extents)) + clip = NULL; + + if (clip != NULL) { + clip = _cairo_clip_init_copy (&local_clip, clip); + have_clip = TRUE; + } + + status = _clip_to_boxes (&clip, &extents, &clip_boxes, &num_boxes); + if (unlikely (status)) { + if (have_clip) + _cairo_clip_fini (&local_clip); + + return status; + } + + status = CAIRO_INT_STATUS_UNSUPPORTED; + if (_cairo_path_fixed_is_rectilinear_fill (path)) { + cairo_boxes_t boxes; + + _cairo_boxes_init (&boxes); + _cairo_boxes_limit (&boxes, clip_boxes, num_boxes); + + status = _cairo_path_fixed_fill_rectilinear_to_boxes (path, + fill_rule, + &boxes); + if (likely (status == CAIRO_STATUS_SUCCESS)) { + status = _clip_and_composite_boxes (surface, op, source, + &boxes, antialias, + &extents, clip); + } + + _cairo_boxes_fini (&boxes); + } + + if (status == CAIRO_INT_STATUS_UNSUPPORTED) { + if (surface->flags & (CAIRO_XCB_RENDER_HAS_COMPOSITE_SPANS | CAIRO_XCB_RENDER_HAS_COMPOSITE_TRAPEZOIDS)) { + status = _cairo_xcb_surface_render_fill_as_polygon (surface, op, source, path, + fill_rule, tolerance, antialias, + clip, clip_boxes, num_boxes, + &extents); + } else if (surface->flags & CAIRO_XCB_RENDER_HAS_COMPOSITE) { + status = _cairo_xcb_surface_render_fill_via_mask (surface, op, source, path, + fill_rule, tolerance, antialias, + have_clip ? &local_clip : NULL, + &extents); + } else { + ASSERT_NOT_REACHED; + } + } + + if (clip_boxes != boxes_stack) + free (clip_boxes); + + if (have_clip) + _cairo_clip_fini (&local_clip); + + return status; +} + +static cairo_status_t +_cairo_xcb_surface_render_glyphs_via_mask (cairo_xcb_surface_t *dst, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_scaled_font_t *scaled_font, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_clip_t *clip, + const cairo_composite_rectangles_t *extents) +{ + cairo_surface_t *image; + cairo_content_t content; + cairo_status_t status; + int x, y; + + content = CAIRO_CONTENT_ALPHA; + if (scaled_font->options.antialias == CAIRO_ANTIALIAS_SUBPIXEL) + content = CAIRO_CONTENT_COLOR_ALPHA; + + x = extents->bounded.x; + y = extents->bounded.y; + image = _cairo_xcb_surface_create_similar_image (dst, content, + extents->bounded.width, + extents->bounded.height); + if (unlikely (image->status)) + return image->status; + + _clear_image (image); + + status = _cairo_surface_offset_glyphs (image, x, y, + CAIRO_OPERATOR_ADD, + &_cairo_pattern_white.base, + scaled_font, glyphs, num_glyphs, + NULL); + if (likely (status == CAIRO_STATUS_SUCCESS)) { + cairo_surface_pattern_t mask; + + _cairo_pattern_init_for_surface (&mask, image); + mask.base.filter = CAIRO_FILTER_NEAREST; + if (content & CAIRO_CONTENT_COLOR) + mask.base.has_component_alpha = TRUE; + + cairo_matrix_init_translate (&mask.base.matrix, -x, -y); + status = _clip_and_composite (dst, op, source, + _composite_mask, (void *) &mask.base, + extents, clip); + + _cairo_pattern_fini (&mask.base); + } + + cairo_surface_finish (image); + cairo_surface_destroy (image); + + return status; +} + +/* Build a struct of the same size of #cairo_glyph_t that can be used both as + * an input glyph with double coordinates, and as "working" glyph with + * integer from-current-point offsets. */ +typedef union { + cairo_glyph_t d; + unsigned long index; + struct { + unsigned long index; + int x; + int y; + } i; +} cairo_xcb_glyph_t; + +/* compile-time assert that #cairo_xcb_glyph_t is the same size as #cairo_glyph_t */ +COMPILE_TIME_ASSERT (sizeof (cairo_xcb_glyph_t) == sizeof (cairo_glyph_t)); + +typedef struct { + cairo_scaled_font_t *font; + cairo_xcb_glyph_t *glyphs; + int num_glyphs; +} composite_glyphs_info_t; + +static cairo_status_t +_can_composite_glyphs (cairo_xcb_surface_t *dst, + cairo_scaled_font_t *scaled_font, + cairo_glyph_t *glyphs, + int num_glyphs) +{ + cairo_status_t status; + const int max_glyph_size = dst->connection->maximum_request_length - 64; + int i; + + /* first scan for oversized glyphs, and fallback in that case */ + for (i = 0; i < num_glyphs; i++) { + cairo_scaled_glyph_t *scaled_glyph; + int width, height, len; + + status = _cairo_scaled_glyph_lookup (scaled_font, + glyphs[i].index, + CAIRO_SCALED_GLYPH_INFO_METRICS, + &scaled_glyph); + if (unlikely (status)) + return status; + + /* XRenderAddGlyph does not handle a glyph surface larger than + * the extended maximum XRequest size. + */ + width = + _cairo_fixed_integer_ceil (scaled_glyph->bbox.p2.x - scaled_glyph->bbox.p1.x); + height = + _cairo_fixed_integer_ceil (scaled_glyph->bbox.p2.y - scaled_glyph->bbox.p1.y); + len = CAIRO_STRIDE_FOR_WIDTH_BPP (width, 32) * height; + if (len >= max_glyph_size) + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + return CAIRO_STATUS_SUCCESS; +} + +/* Start a new element for the first glyph, + * or for any glyph that has unexpected position, + * or if current element has too many glyphs + * (Xrender limits each element to 252 glyphs, we limit them to 128) + * + * These same conditions need to be mirrored between + * _cairo_xcb_surface_emit_glyphs and _emit_glyph_chunks + */ +#define _start_new_glyph_elt(count, glyph) \ + (((count) & 127) == 0 || (glyph)->i.x || (glyph)->i.y) + +/* sz_xGlyphtElt required alignment to a 32-bit boundary, so ensure we have + * enough room for padding */ +typedef struct { + uint8_t len; + uint8_t pad1; + uint16_t pad2; + int16_t deltax; + int16_t deltay; +} x_glyph_elt_t; +#define _cairo_sz_x_glyph_elt_t (sizeof (x_glyph_elt_t) + 4) + +static cairo_xcb_font_glyphset_info_t * +_cairo_xcb_scaled_glyph_get_glyphset_info (cairo_scaled_glyph_t *scaled_glyph) +{ + return scaled_glyph->surface_private; +} + +static void +_cairo_xcb_scaled_glyph_set_glyphset_info (cairo_scaled_glyph_t *scaled_glyph, + cairo_xcb_font_glyphset_info_t *glyphset_info) +{ + scaled_glyph->surface_private = glyphset_info; +} + +static cairo_status_t +_cairo_xcb_surface_font_init (cairo_xcb_connection_t *connection, + cairo_scaled_font_t *scaled_font) +{ + cairo_xcb_font_t *font_private; + int i; + + font_private = malloc (sizeof (cairo_xcb_font_t)); + if (unlikely (font_private == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + font_private->scaled_font = scaled_font; + font_private->connection = _cairo_xcb_connection_reference (connection); + for (i = 0; i < NUM_GLYPHSETS; i++) { + cairo_xcb_font_glyphset_info_t *glyphset_info = &font_private->glyphset_info[i]; + switch (i) { + case GLYPHSET_INDEX_ARGB32: glyphset_info->format = CAIRO_FORMAT_ARGB32; break; + case GLYPHSET_INDEX_A8: glyphset_info->format = CAIRO_FORMAT_A8; break; + case GLYPHSET_INDEX_A1: glyphset_info->format = CAIRO_FORMAT_A1; break; + default: ASSERT_NOT_REACHED; break; + } + glyphset_info->xrender_format = 0; + glyphset_info->glyphset = XCB_NONE; + glyphset_info->pending_free_glyphs = NULL; + } + + scaled_font->surface_private = font_private; + scaled_font->surface_backend = &_cairo_xcb_surface_backend; + + cairo_list_add (&font_private->link, &connection->fonts); + + return CAIRO_STATUS_SUCCESS; +} + +static void +_cairo_xcb_font_destroy (cairo_xcb_font_t *font) +{ + int i; + + for (i = 0; i < NUM_GLYPHSETS; i++) { + cairo_xcb_font_glyphset_info_t *glyphset_info; + + glyphset_info = &font->glyphset_info[i]; + + if (glyphset_info->pending_free_glyphs != NULL) + free (glyphset_info->pending_free_glyphs); + } + + cairo_list_del (&font->link); + _cairo_xcb_connection_destroy (font->connection); + + free (font); +} + +void +_cairo_xcb_font_finish (cairo_xcb_font_t *font) +{ + cairo_scaled_font_t *scaled_font; + + scaled_font = font->scaled_font; + + CAIRO_MUTEX_LOCK (scaled_font->mutex); + scaled_font->surface_private = NULL; + _cairo_scaled_font_reset_cache (scaled_font); + CAIRO_MUTEX_UNLOCK (scaled_font->mutex); + + _cairo_xcb_font_destroy (font); +} + +void +_cairo_xcb_surface_scaled_font_fini (cairo_scaled_font_t *scaled_font) +{ + cairo_xcb_font_t *font_private; + cairo_xcb_connection_t *connection; + cairo_bool_t have_connection; + cairo_status_t status; + int i; + + font_private = scaled_font->surface_private; + if (font_private == NULL) + return; + + connection = font_private->connection; + + status = _cairo_xcb_connection_acquire (connection); + have_connection = status == CAIRO_STATUS_SUCCESS; + if (likely (have_connection)) + status = _cairo_xcb_connection_take_socket (connection); + + for (i = 0; i < NUM_GLYPHSETS; i++) { + cairo_xcb_font_glyphset_info_t *glyphset_info; + + glyphset_info = &font_private->glyphset_info[i]; + if (glyphset_info->glyphset && status == CAIRO_STATUS_SUCCESS) { + _cairo_xcb_connection_render_free_glyph_set (connection, + glyphset_info->glyphset); + } + } + + if (have_connection) + _cairo_xcb_connection_release (connection); + + + _cairo_xcb_font_destroy (font_private); +} + +static void +_cairo_xcb_render_free_glyphs (cairo_xcb_connection_t *connection, + cairo_xcb_font_glyphset_free_glyphs_t *to_free) +{ + _cairo_xcb_connection_render_free_glyphs (connection, + to_free->glyphset, + to_free->glyph_count, + to_free->glyph_indices); +} + +void +_cairo_xcb_surface_scaled_glyph_fini (cairo_scaled_glyph_t *scaled_glyph, + cairo_scaled_font_t *scaled_font) +{ + cairo_xcb_font_t *font_private; + cairo_xcb_font_glyphset_info_t *glyphset_info; + + if (scaled_font->finished) + return; + + font_private = scaled_font->surface_private; + glyphset_info = _cairo_xcb_scaled_glyph_get_glyphset_info (scaled_glyph); + if (font_private != NULL && glyphset_info != NULL) { + cairo_xcb_font_glyphset_free_glyphs_t *to_free; + + to_free = glyphset_info->pending_free_glyphs; + if (to_free != NULL && + to_free->glyph_count == ARRAY_LENGTH (to_free->glyph_indices)) + { + _cairo_xcb_render_free_glyphs (font_private->connection, to_free); + to_free = glyphset_info->pending_free_glyphs = NULL; + } + + if (to_free == NULL) { + to_free = malloc (sizeof (cairo_xcb_font_glyphset_free_glyphs_t)); + if (unlikely (to_free == NULL)) { + _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); + return; /* XXX cannot propagate failure */ + } + + to_free->glyphset = glyphset_info->glyphset; + to_free->glyph_count = 0; + glyphset_info->pending_free_glyphs = to_free; + } + + to_free->glyph_indices[to_free->glyph_count++] = + _cairo_scaled_glyph_index (scaled_glyph); + } +} + +static cairo_bool_t +_native_byte_order_lsb (void) +{ + int x = 1; + + return *((char *) &x) == 1; +} + +static cairo_xcb_font_glyphset_info_t * +_cairo_xcb_scaled_font_get_glyphset_info_for_format (cairo_scaled_font_t *scaled_font, + cairo_format_t format) +{ + cairo_xcb_font_t *font_private; + cairo_xcb_font_glyphset_info_t *glyphset_info; + int glyphset_index; + + switch (format) { + default: + case CAIRO_FORMAT_RGB24: + case CAIRO_FORMAT_ARGB32: glyphset_index = GLYPHSET_INDEX_ARGB32; break; + case CAIRO_FORMAT_A8: glyphset_index = GLYPHSET_INDEX_A8; break; + case CAIRO_FORMAT_A1: glyphset_index = GLYPHSET_INDEX_A1; break; + } + + font_private = scaled_font->surface_private; + glyphset_info = &font_private->glyphset_info[glyphset_index]; + if (glyphset_info->glyphset == XCB_NONE) { + cairo_xcb_connection_t *connection = font_private->connection; + + glyphset_info->glyphset = _cairo_xcb_connection_get_xid (font_private->connection); + glyphset_info->xrender_format = + connection->standard_formats[glyphset_info->format]; + + _cairo_xcb_connection_render_create_glyph_set (font_private->connection, + glyphset_info->glyphset, + glyphset_info->xrender_format); + } + + return glyphset_info; +} + +static cairo_bool_t +_cairo_xcb_glyphset_info_has_pending_free_glyph ( + cairo_xcb_font_glyphset_info_t *glyphset_info, + unsigned long glyph_index) +{ + if (glyphset_info->pending_free_glyphs != NULL) { + cairo_xcb_font_glyphset_free_glyphs_t *to_free; + int i; + + to_free = glyphset_info->pending_free_glyphs; + for (i = 0; i < to_free->glyph_count; i++) { + if (to_free->glyph_indices[i] == glyph_index) { + to_free->glyph_count--; + memmove (&to_free->glyph_indices[i], + &to_free->glyph_indices[i+1], + (to_free->glyph_count - i) * sizeof (to_free->glyph_indices[0])); + return TRUE; + } + } + } + + return FALSE; +} + +static cairo_xcb_font_glyphset_info_t * +_cairo_xcb_scaled_font_get_glyphset_info_for_pending_free_glyph ( + cairo_scaled_font_t *scaled_font, + unsigned long glyph_index, + cairo_image_surface_t *surface) +{ + cairo_xcb_font_t *font_private; + int i; + + font_private = scaled_font->surface_private; + if (font_private == NULL) + return NULL; + + if (surface != NULL) { + switch (surface->format) { + default: + case CAIRO_FORMAT_RGB24: + case CAIRO_FORMAT_ARGB32: i = GLYPHSET_INDEX_ARGB32; break; + case CAIRO_FORMAT_A8: i = GLYPHSET_INDEX_A8; break; + case CAIRO_FORMAT_A1: i = GLYPHSET_INDEX_A1; break; + } + + if (_cairo_xcb_glyphset_info_has_pending_free_glyph ( + &font_private->glyphset_info[i], + glyph_index)) + { + return &font_private->glyphset_info[i]; + } + } else { + for (i = 0; i < NUM_GLYPHSETS; i++) { + if (_cairo_xcb_glyphset_info_has_pending_free_glyph ( + &font_private->glyphset_info[i], + glyph_index)) + { + return &font_private->glyphset_info[i]; + } + } + } + + return NULL; +} +static cairo_status_t +_cairo_xcb_surface_add_glyph (cairo_xcb_connection_t *connection, + cairo_scaled_font_t *scaled_font, + cairo_scaled_glyph_t **scaled_glyph_out) +{ + xcb_render_glyphinfo_t glyph_info; + uint32_t glyph_index; + uint8_t *data; + cairo_status_t status = CAIRO_STATUS_SUCCESS; + cairo_scaled_glyph_t *scaled_glyph = *scaled_glyph_out; + cairo_image_surface_t *glyph_surface = scaled_glyph->surface; + cairo_bool_t already_had_glyph_surface; + cairo_xcb_font_glyphset_info_t *glyphset_info; + + glyph_index = _cairo_scaled_glyph_index (scaled_glyph); + + /* check to see if we have a pending XRenderFreeGlyph for this glyph */ + glyphset_info = _cairo_xcb_scaled_font_get_glyphset_info_for_pending_free_glyph (scaled_font, glyph_index, glyph_surface); + if (glyphset_info != NULL) { + _cairo_xcb_scaled_glyph_set_glyphset_info (scaled_glyph, glyphset_info); + return CAIRO_STATUS_SUCCESS; + } + + if (glyph_surface == NULL) { + status = _cairo_scaled_glyph_lookup (scaled_font, + glyph_index, + CAIRO_SCALED_GLYPH_INFO_METRICS | + CAIRO_SCALED_GLYPH_INFO_SURFACE, + scaled_glyph_out); + if (unlikely (status)) + return status; + + scaled_glyph = *scaled_glyph_out; + glyph_surface = scaled_glyph->surface; + already_had_glyph_surface = FALSE; + } else { + already_had_glyph_surface = TRUE; + } + + if (scaled_font->surface_private == NULL) { + status = _cairo_xcb_surface_font_init (connection, scaled_font); + if (unlikely (status)) + return status; + } + + glyphset_info = _cairo_xcb_scaled_font_get_glyphset_info_for_format (scaled_font, + glyph_surface->format); + + /* If the glyph surface has zero height or width, we create + * a clear 1x1 surface, to avoid various X server bugs. + */ + if (glyph_surface->width == 0 || glyph_surface->height == 0) { + cairo_surface_t *tmp_surface; + + tmp_surface = cairo_image_surface_create (glyphset_info->format, 1, 1); + status = tmp_surface->status; + if (unlikely (status)) + goto BAIL; + + tmp_surface->device_transform = glyph_surface->base.device_transform; + tmp_surface->device_transform_inverse = glyph_surface->base.device_transform_inverse; + + glyph_surface = (cairo_image_surface_t *) tmp_surface; + } + + /* If the glyph format does not match the font format, then we + * create a temporary surface for the glyph image with the font's + * format. + */ + if (glyph_surface->format != glyphset_info->format) { + glyph_surface = _cairo_image_surface_coerce (glyph_surface, + glyphset_info->format); + status = glyph_surface->base.status; + if (unlikely (status)) + goto BAIL; + } + + /* XXX: FRAGILE: We're ignore device_transform scaling here. A bug? */ + glyph_info.x = _cairo_lround (glyph_surface->base.device_transform.x0); + glyph_info.y = _cairo_lround (glyph_surface->base.device_transform.y0); + glyph_info.width = glyph_surface->width; + glyph_info.height = glyph_surface->height; + glyph_info.x_off = scaled_glyph->x_advance; + glyph_info.y_off = scaled_glyph->y_advance; + + data = glyph_surface->data; + + /* flip formats around */ + switch (scaled_glyph->surface->format) { + case CAIRO_FORMAT_A1: + /* local bitmaps are always stored with bit == byte */ + if (_native_byte_order_lsb() != (connection->root->bitmap_format_bit_order == XCB_IMAGE_ORDER_LSB_FIRST)) { + int c = glyph_surface->stride * glyph_surface->height; + const uint8_t *d; + uint8_t *new, *n; + + new = malloc (c); + if (unlikely (new == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto BAIL; + } + + n = new; + d = data; + do { + uint8_t b = *d++; + b = ((b << 1) & 0xaa) | ((b >> 1) & 0x55); + b = ((b << 2) & 0xcc) | ((b >> 2) & 0x33); + b = ((b << 4) & 0xf0) | ((b >> 4) & 0x0f); + *n++ = b; + } while (--c); + data = new; + } + break; + + case CAIRO_FORMAT_A8: + break; + + case CAIRO_FORMAT_ARGB32: + if (_native_byte_order_lsb() != (connection->root->image_byte_order == XCB_IMAGE_ORDER_LSB_FIRST)) { + unsigned int c = glyph_surface->stride * glyph_surface->height / 4; + const uint32_t *d; + uint32_t *new, *n; + + new = malloc (4 * c); + if (unlikely (new == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto BAIL; + } + + n = new; + d = (uint32_t *) data; + do { + *n++ = bswap_32 (*d); + d++; + } while (--c); + data = (uint8_t *) new; + } + break; + + case CAIRO_FORMAT_RGB24: + default: + ASSERT_NOT_REACHED; + break; + } + /* XXX assume X server wants pixman padding. Xft assumes this as well */ + + _cairo_xcb_connection_render_add_glyphs (connection, + glyphset_info->glyphset, + 1, &glyph_index, &glyph_info, + glyph_surface->stride * glyph_surface->height, + data); + + if (data != glyph_surface->data) + free (data); + + _cairo_xcb_scaled_glyph_set_glyphset_info (scaled_glyph, glyphset_info); + + BAIL: + if (glyph_surface != scaled_glyph->surface) + cairo_surface_destroy (&glyph_surface->base); + + /* If the scaled glyph didn't already have a surface attached + * to it, release the created surface now that we have it + * uploaded to the X server. If the surface has already been + * there (e.g. because image backend requested it), leave it in + * the cache + */ + if (! already_had_glyph_surface) + _cairo_scaled_glyph_set_surface (scaled_glyph, scaled_font, NULL); + + return status; +} + +typedef void (*cairo_xcb_render_composite_text_func_t) + (cairo_xcb_connection_t *connection, + uint8_t op, + xcb_render_picture_t src, + xcb_render_picture_t dst, + xcb_render_pictformat_t mask_format, + xcb_render_glyphset_t glyphset, + int16_t src_x, + int16_t src_y, + uint32_t len, + uint8_t *cmd); + + +static cairo_status_t +_emit_glyphs_chunk (cairo_xcb_surface_t *dst, + cairo_operator_t op, + cairo_xcb_picture_t *src, + /* info for this chunk */ + cairo_xcb_glyph_t *glyphs, + int num_glyphs, + int width, + int estimated_req_size, + cairo_xcb_font_glyphset_info_t *glyphset_info) +{ + cairo_xcb_render_composite_text_func_t composite_text_func; + uint8_t stack_buf[CAIRO_STACK_BUFFER_SIZE]; + uint8_t *buf = stack_buf; + x_glyph_elt_t *elt = NULL; /* silence compiler */ + uint32_t len; + int i; + + if (estimated_req_size > ARRAY_LENGTH (stack_buf)) { + buf = malloc (estimated_req_size); + if (unlikely (buf == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + len = 0; + for (i = 0; i < num_glyphs; i++) { + if (_start_new_glyph_elt (i, &glyphs[i])) { + if (len & 3) + len += 4 - (len & 3); + + elt = (x_glyph_elt_t *) (buf + len); + elt->len = 0; + elt->deltax = glyphs[i].i.x; + elt->deltay = glyphs[i].i.y; + len += sizeof (x_glyph_elt_t); + } + + switch (width) { + case 1: *(uint8_t *) (buf + len) = glyphs[i].index; break; + case 2: *(uint16_t *) (buf + len) = glyphs[i].index; break; + default: + case 4: *(uint32_t *) (buf + len) = glyphs[i].index; break; + } + len += width; + elt->len++; + } + if (len & 3) + len += 4 - (len & 3); + + switch (width) { + case 1: + composite_text_func = _cairo_xcb_connection_render_composite_glyphs_8; + break; + case 2: + composite_text_func = _cairo_xcb_connection_render_composite_glyphs_16; + break; + default: + case 4: + composite_text_func = _cairo_xcb_connection_render_composite_glyphs_32; + break; + } + composite_text_func (dst->connection, + _render_operator (op), + src->picture, + dst->picture, + glyphset_info->xrender_format, + glyphset_info->glyphset, + src->x + glyphs[0].i.x, + src->y + glyphs[0].i.y, + len, buf); + + if (buf != stack_buf) + free (buf); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_composite_glyphs (void *closure, + cairo_xcb_surface_t *dst, + cairo_operator_t op, + const cairo_pattern_t *pattern, + int dst_x, int dst_y, + const cairo_rectangle_int_t *extents, + cairo_region_t *clip_region) +{ + composite_glyphs_info_t *info = closure; + cairo_scaled_glyph_t *glyph_cache[64]; + cairo_status_t status = CAIRO_STATUS_SUCCESS; + cairo_fixed_t x = 0, y = 0; + cairo_xcb_font_glyphset_info_t *glyphset_info = NULL, *this_glyphset_info; + const unsigned int max_request_size = dst->connection->maximum_request_length - 64; + cairo_xcb_picture_t *src; + + unsigned long max_index = 0; + int width = 1; + + unsigned int request_size = 0; + int i; + + src = _cairo_xcb_picture_for_pattern (dst, pattern, extents); + if (unlikely (src->base.status)) + return src->base.status; + + memset (glyph_cache, 0, sizeof (glyph_cache)); + + for (i = 0; i < info->num_glyphs; i++) { + cairo_scaled_glyph_t *scaled_glyph; + unsigned long glyph_index = info->glyphs[i].index; + int cache_index = glyph_index % ARRAY_LENGTH (glyph_cache); + int old_width = width; + int this_x, this_y; + + scaled_glyph = glyph_cache[cache_index]; + if (scaled_glyph == NULL || + _cairo_scaled_glyph_index (scaled_glyph) != glyph_index) + { + status = _cairo_scaled_glyph_lookup (info->font, + glyph_index, + CAIRO_SCALED_GLYPH_INFO_METRICS, + &scaled_glyph); + if (unlikely (status)) + return status; + + /* Send unseen glyphs to the server */ + if (_cairo_xcb_scaled_glyph_get_glyphset_info (scaled_glyph) == NULL) { + status = _cairo_xcb_surface_add_glyph (dst->connection, + info->font, + &scaled_glyph); + if (unlikely (status)) + return status; + } + + glyph_cache[cache_index] = scaled_glyph; + } + + /* Glyph skipping: + * + * We skip any glyphs that have troublesome coordinates. We want + * to make sure that (glyph2.x - (glyph1.x + glyph1.width)) fits in + * a signed 16bit integer, otherwise it will overflow in the render + * protocol. + * To ensure this, we'll make sure that (glyph2.x - glyph1.x) fits in + * a signed 15bit integer. The trivial option would be to allow + * coordinates -8192..8192, but that's kinda dull. It probably will + * take a decade or so to get monitors 8192x4096 or something. A + * negative value of -8192 on the other hand, is absolutely useless. + * Note that we do want to allow some negative positions. The glyph + * may start off the screen but part of it make it to the screen. + * Anyway, we will allow positions in the range -4096..122887. That + * will buy us a few more years before this stops working. + */ + this_x = _cairo_lround (info->glyphs[i].d.x) - dst_x; + this_y = _cairo_lround (info->glyphs[i].d.y) - dst_y; + assert (! (((this_x+4096) | (this_y+4096)) & ~0x3fffu)); + + this_glyphset_info = _cairo_xcb_scaled_glyph_get_glyphset_info (scaled_glyph); + if (glyphset_info == NULL) + glyphset_info = this_glyphset_info; + + /* Update max glyph index */ + if (glyph_index > max_index) { + max_index = glyph_index; + if (max_index >= 65536) + width = 4; + else if (max_index >= 256) + width = 2; + if (width != old_width) + request_size += (width - old_width) * i; + } + + /* If we will pass the max request size by adding this glyph, + * flush current glyphs. Note that we account for a + * possible element being added below. + * + * Also flush if changing glyphsets, as Xrender limits one mask + * format per request, so we can either break up, or use a + * wide-enough mask format. We do the former. One reason to + * prefer the latter is the fact that Xserver ADDs all glyphs + * to the mask first, and then composes that to final surface, + * though it's not a big deal. + */ + if (request_size + width > max_request_size - _cairo_sz_x_glyph_elt_t || + this_glyphset_info != glyphset_info) + { + status = _emit_glyphs_chunk (dst, op, src, + info->glyphs, i, + old_width, request_size, + glyphset_info); + if (unlikely (status)) + return status; + + info->glyphs += i; + info->num_glyphs -= i; + i = 0; + + max_index = info->glyphs[0].index; + width = max_index < 256 ? 1 : max_index < 65536 ? 2 : 4; + + request_size = 0; + + x = y = 0; + glyphset_info = this_glyphset_info; + } + + /* Convert absolute glyph position to relative-to-current-point + * position */ + info->glyphs[i].i.x = this_x - x; + info->glyphs[i].i.y = this_y - y; + + /* Start a new element for the first glyph, + * or for any glyph that has unexpected position, + * or if current element has too many glyphs. + * + * These same conditions are mirrored in _emit_glyphs_chunk(). + */ + if (_start_new_glyph_elt (i, &info->glyphs[i])) + request_size += _cairo_sz_x_glyph_elt_t; + + /* adjust current-position */ + x = this_x + scaled_glyph->x_advance; + y = this_y + scaled_glyph->y_advance; + + request_size += width; + } + + if (i) { + status = _emit_glyphs_chunk (dst, op, src, + info->glyphs, i, + width, request_size, + glyphset_info); + } + + return status; +} + +cairo_int_status_t +_cairo_xcb_surface_render_glyphs (cairo_xcb_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_scaled_font_t *scaled_font, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_clip_t *clip) +{ + cairo_composite_rectangles_t extents; + cairo_clip_t local_clip; + cairo_bool_t have_clip = FALSE; + cairo_status_t status; + + if (unlikely (! _operator_is_supported (surface->flags, op))) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if ((surface->flags & (CAIRO_XCB_RENDER_HAS_COMPOSITE_GLYPHS | CAIRO_XCB_RENDER_HAS_COMPOSITE)) == 0) + return CAIRO_INT_STATUS_UNSUPPORTED; + + status = _cairo_composite_rectangles_init_for_glyphs (&extents, + surface->width, + surface->height, + op, source, + scaled_font, + glyphs, num_glyphs, + clip, NULL); + if (unlikely (status)) + return status; + + if (_cairo_clip_contains_rectangle (clip, &extents)) + clip = NULL; + + if (clip != NULL) { + clip = _cairo_clip_init_copy (&local_clip, clip); + have_clip = TRUE; + } + + if (clip != NULL && extents.is_bounded) { + clip = _cairo_clip_init_copy (&local_clip, clip); + status = _cairo_clip_rectangle (clip, &extents.bounded); + if (unlikely (status)) { + _cairo_clip_fini (&local_clip); + return status; + } + have_clip = TRUE; + } + + status = CAIRO_INT_STATUS_UNSUPPORTED; + if (surface->flags & CAIRO_XCB_RENDER_HAS_COMPOSITE_GLYPHS) { + _cairo_scaled_font_freeze_cache (scaled_font); + + status = _can_composite_glyphs (surface, scaled_font, glyphs, num_glyphs); + if (likely (status == CAIRO_STATUS_SUCCESS)) { + composite_glyphs_info_t info; + + info.font = scaled_font; + info.glyphs = (cairo_xcb_glyph_t *) glyphs; + info.num_glyphs = num_glyphs; + + status = _clip_and_composite (surface, op, source, + _composite_glyphs, &info, + &extents, clip); + } + + _cairo_scaled_font_thaw_cache (scaled_font); + } + + if (status == CAIRO_INT_STATUS_UNSUPPORTED) { + assert (surface->flags & CAIRO_XCB_RENDER_HAS_COMPOSITE); + status = + _cairo_xcb_surface_render_glyphs_via_mask (surface, op, source, + scaled_font, glyphs, num_glyphs, + have_clip ? &local_clip : NULL, + &extents); + } + + if (have_clip) + _cairo_clip_fini (&local_clip); + + return status; +} diff --git a/src/cairo-xcb-surface.c b/src/cairo-xcb-surface.c index 666abc99..2e08c641 100644 --- a/src/cairo-xcb-surface.c +++ b/src/cairo-xcb-surface.c @@ -1,6 +1,7 @@ /* 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 @@ -31,308 +32,215 @@ * 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-xrender.h" -#include "cairo-clip-private.h" -#include "cairo-error-private.h" -#include "cairo-freelist-private.h" -#include "cairo-list-private.h" -#include <xcb/xcb_renderutil.h> - -#define AllPlanes ((unsigned long)~0L) - -slim_hidden_proto (cairo_xcb_surface_create_with_xrender_format); - -/* - * Instead of taking two round trips for each blending request, - * assume that if a particular drawable fails GetImage that it will - * fail for a "while"; use temporary pixmaps to avoid the errors - */ - -#define CAIRO_ASSUME_PIXMAP 20 -typedef struct cairo_xcb_surface { - cairo_surface_t base; - - xcb_connection_t *dpy; - xcb_screen_t *screen; - - xcb_gcontext_t gc; - xcb_drawable_t drawable; - cairo_bool_t owns_pixmap; - xcb_visualtype_t *visual; - - int use_pixmap; - - int render_major; - int render_minor; - - int width; - int height; - int depth; - - cairo_bool_t have_clip_rects; - xcb_rectangle_t *clip_rects; - int num_clip_rects; - cairo_region_t *clip_region; - - xcb_render_picture_t src_picture, dst_picture; - xcb_render_pictforminfo_t xrender_format; - - cairo_list_t to_be_checked; - cairo_freepool_t cookie_pool; -} cairo_xcb_surface_t; - -typedef struct _cairo_xcb_cookie { - cairo_list_t link; - xcb_void_cookie_t xcb; -} cairo_xcb_cookie_t; - -#define CAIRO_SURFACE_RENDER_AT_LEAST(surface, major, minor) \ - (((surface)->render_major > major) || \ - (((surface)->render_major == major) && ((surface)->render_minor >= minor))) - -#define CAIRO_SURFACE_RENDER_HAS_CREATE_PICTURE(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 0) -#define CAIRO_SURFACE_RENDER_HAS_COMPOSITE(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 0) -#define CAIRO_SURFACE_RENDER_HAS_COMPOSITE_TEXT(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 0) +#include "cairo-xcb.h" +#include "cairo-xcb-private.h" -#define CAIRO_SURFACE_RENDER_HAS_FILL_RECTANGLE(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 1) -#define CAIRO_SURFACE_RENDER_HAS_FILL_RECTANGLES(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 1) +#include <xcb/dri2.h> -#define CAIRO_SURFACE_RENDER_HAS_DISJOINT(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 2) -#define CAIRO_SURFACE_RENDER_HAS_CONJOINT(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 2) +#define AllPlanes ((unsigned) -1) +#define CAIRO_ASSUME_PIXMAP 20 +#define XLIB_COORD_MAX 32767 -#define CAIRO_SURFACE_RENDER_HAS_TRAPEZOIDS(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 4) -#define CAIRO_SURFACE_RENDER_HAS_TRIANGLES(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 4) -#define CAIRO_SURFACE_RENDER_HAS_TRISTRIP(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 4) -#define CAIRO_SURFACE_RENDER_HAS_TRIFAN(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 4) +#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 -#define CAIRO_SURFACE_RENDER_HAS_PICTURE_TRANSFORM(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 6) -#define CAIRO_SURFACE_RENDER_HAS_FILTERS(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 6) -#define CAIRO_SURFACE_RENDER_HAS_REPEAT_PAD(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 10) -#define CAIRO_SURFACE_RENDER_HAS_REPEAT_REFLECT(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 10) +#if CAIRO_HAS_DRM_SURFACE && CAIRO_HAS_XCB_DRM_FUNCTIONS +#include "drm/cairo-drm-private.h" +#endif static cairo_status_t -_cairo_xcb_surface_ensure_gc (cairo_xcb_surface_t *surface); - -static int -_CAIRO_FORMAT_DEPTH (cairo_format_t format) +_cairo_xcb_surface_create_similar_shm (cairo_xcb_surface_t *other, + pixman_format_code_t pixman_format, + int width, int height, + cairo_surface_t **out) { - switch (format) { - case CAIRO_FORMAT_A1: - return 1; - case CAIRO_FORMAT_A8: - return 8; - case CAIRO_FORMAT_RGB24: - return 24; - case CAIRO_FORMAT_ARGB32: - default: - return 32; - } -} + size_t size, stride; + cairo_xcb_shm_info_t *shm_info; + cairo_status_t status; + cairo_surface_t *image; -static cairo_status_t -_cairo_xcb_add_cookie_to_be_checked (cairo_xcb_surface_t *surface, - xcb_void_cookie_t xcb) -{ - cairo_xcb_cookie_t *cookie; + stride = CAIRO_STRIDE_FOR_WIDTH_BPP (width, PIXMAN_FORMAT_BPP (pixman_format)); + size = stride * height; + if (size < CAIRO_XCB_SHM_SMALL_IMAGE) + return CAIRO_INT_STATUS_UNSUPPORTED; - cookie = _cairo_freepool_alloc (&surface->cookie_pool); - if (unlikely (cookie == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); + status = _cairo_xcb_connection_allocate_shm_info (other->connection, + size, &shm_info); + if (unlikely (status)) + return status; + + image = _cairo_image_surface_create_with_pixman_format (shm_info->mem, + pixman_format, + width, height, + stride); + status = image->status; + if (unlikely (status)) { + _cairo_xcb_shm_info_destroy (shm_info); + return status; + } - cookie->xcb = xcb; - cairo_list_add_tail (&cookie->link, &surface->to_be_checked); + status = _cairo_user_data_array_set_data (&image->user_data, + (const cairo_user_data_key_t *) other->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 status; + } + *out = image; return CAIRO_STATUS_SUCCESS; } -static xcb_render_pictforminfo_t * -_CAIRO_FORMAT_TO_XRENDER_FORMAT(xcb_connection_t *dpy, cairo_format_t format) +cairo_surface_t * +_cairo_xcb_surface_create_similar_image (cairo_xcb_surface_t *other, + cairo_content_t content, + int width, int height) { - xcb_pict_standard_t std_format; - switch (format) { - case CAIRO_FORMAT_A1: - std_format = XCB_PICT_STANDARD_A_1; break; - case CAIRO_FORMAT_A8: - std_format = XCB_PICT_STANDARD_A_8; break; - case CAIRO_FORMAT_RGB24: - std_format = XCB_PICT_STANDARD_RGB_24; break; - case CAIRO_FORMAT_ARGB32: + cairo_surface_t *image = NULL; + pixman_format_code_t pixman_format; + + /* XXX choose pixman_format from connection->image_formats */ + switch (content) { + case CAIRO_CONTENT_ALPHA: + pixman_format = PIXMAN_a8; + break; + case CAIRO_CONTENT_COLOR: + pixman_format = PIXMAN_x8r8g8b8; + break; default: - std_format = XCB_PICT_STANDARD_ARGB_32; break; + ASSERT_NOT_REACHED; + case CAIRO_CONTENT_COLOR_ALPHA: + pixman_format = PIXMAN_a8r8g8b8; + break; } - return xcb_render_util_find_standard_format (xcb_render_util_query_formats (dpy), std_format); -} -static cairo_content_t -_xcb_render_format_to_content (xcb_render_pictforminfo_t *xrender_format) -{ - cairo_bool_t xrender_format_has_alpha; - cairo_bool_t xrender_format_has_color; - - /* This only happens when using a non-Render server. Let's punt - * and say there's no alpha here. */ - if (xrender_format == NULL) - return CAIRO_CONTENT_COLOR; - - xrender_format_has_alpha = (xrender_format->direct.alpha_mask != 0); - xrender_format_has_color = (xrender_format->direct.red_mask != 0 || - xrender_format->direct.green_mask != 0 || - xrender_format->direct.blue_mask != 0); - - if (xrender_format_has_alpha) - if (xrender_format_has_color) - return CAIRO_CONTENT_COLOR_ALPHA; - else - return CAIRO_CONTENT_ALPHA; - else - return CAIRO_CONTENT_COLOR; -} - -static cairo_status_t -_cairo_xcb_surface_set_gc_clip_rects (cairo_xcb_surface_t *surface) -{ - if (surface->have_clip_rects) { - xcb_void_cookie_t cookie; + if (other->flags & CAIRO_XCB_HAS_SHM) { + cairo_status_t status; - cookie = xcb_set_clip_rectangles_checked (surface->dpy, - XCB_CLIP_ORDERING_YX_SORTED, surface->gc, - 0, 0, - surface->num_clip_rects, - surface->clip_rects); + status = _cairo_xcb_surface_create_similar_shm (other, + pixman_format, + width, height, + &image); + if (_cairo_status_is_error (status)) + return _cairo_surface_create_in_error (status); + } - return _cairo_xcb_add_cookie_to_be_checked (surface, cookie); + if (image == NULL) { + image = _cairo_image_surface_create_with_pixman_format (NULL, + pixman_format, + width, height, + 0); } - return CAIRO_STATUS_SUCCESS; + return image; } -static cairo_status_t -_cairo_xcb_surface_set_picture_clip_rects (cairo_xcb_surface_t *surface) +cairo_surface_t * +_cairo_xcb_surface_create_similar (void *abstract_other, + cairo_content_t content, + int width, + int height) { - if (surface->have_clip_rects) { - xcb_void_cookie_t cookie; + 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; - cookie = xcb_render_set_picture_clip_rectangles_checked (surface->dpy, - surface->dst_picture, - 0, 0, - surface->num_clip_rects, - surface->clip_rects); + if (width > XLIB_COORD_MAX || height > XLIB_COORD_MAX) + return NULL; - return _cairo_xcb_add_cookie_to_be_checked (surface, cookie); - } + if (width <= 0 || height <= 0) + return NULL; - return CAIRO_STATUS_SUCCESS; -} +#if CAIRO_HAS_DRM_SURFACE && CAIRO_HAS_XCB_DRM_FUNCTIONS + if (other->drm != NULL) { + cairo_surface_t *drm; -static cairo_status_t -_cairo_xcb_surface_set_clip_region (void *abstract_surface, - cairo_region_t *region) -{ - cairo_xcb_surface_t *surface = abstract_surface; + drm = _cairo_drm_surface_create_similar (other->drm, content, width, height); + if (drm != NULL) + return drm; + } +#endif - if (region == surface->clip_region) - return CAIRO_STATUS_SUCCESS; + if ((other->flags & CAIRO_XCB_HAS_RENDER) == 0) + return _cairo_xcb_surface_create_similar_image (other, content, width, height); - cairo_region_destroy (surface->clip_region); - region = cairo_region_reference (region); + connection = other->connection; + status = _cairo_xcb_connection_acquire (connection); + if (unlikely (status)) + return _cairo_surface_create_in_error (status); - if (surface->clip_rects) { - free (surface->clip_rects); - surface->clip_rects = NULL; + status =_cairo_xcb_connection_take_socket (connection); + if (unlikely (status)) { + _cairo_xcb_connection_release (connection); + return _cairo_surface_create_in_error (status); } - surface->have_clip_rects = FALSE; - surface->num_clip_rects = 0; - - if (region == NULL) { - uint32_t none[] = { XCB_NONE }; - if (surface->gc) - xcb_change_gc (surface->dpy, surface->gc, XCB_GC_CLIP_MASK, none); + if (content == other->base.content) { + pixmap = _cairo_xcb_connection_create_pixmap (connection, + other->depth, + other->drawable, + width, height); - if (surface->xrender_format.id != XCB_NONE && surface->dst_picture) - xcb_render_change_picture (surface->dpy, surface->dst_picture, - XCB_RENDER_CP_CLIP_MASK, none); + surface = (cairo_xcb_surface_t *) + _cairo_xcb_surface_create_internal (other->screen, + pixmap, TRUE, + other->pixman_format, + other->xrender_format, + width, height); } else { - xcb_rectangle_t *rects = NULL; - int n_rects, i; - - n_rects = cairo_region_num_rectangles (region); - - if (n_rects > 0) { - rects = _cairo_malloc_ab (n_rects, sizeof(xcb_rectangle_t)); - if (rects == NULL) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } else { - rects = NULL; - } - - for (i = 0; i < n_rects; i++) { - cairo_rectangle_int_t rect; - - cairo_region_get_rectangle (region, i, &rect); - - rects[i].x = rect.x; - rects[i].y = rect.y; - rects[i].width = rect.width; - rects[i].height = rect.height; + 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; } - surface->have_clip_rects = TRUE; - surface->clip_rects = rects; - surface->num_clip_rects = n_rects; - - if (surface->gc) - _cairo_xcb_surface_set_gc_clip_rects (surface); - - if (surface->dst_picture) - _cairo_xcb_surface_set_picture_clip_rects (surface); - } - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_surface_t * -_cairo_xcb_surface_create_similar (void *abstract_src, - cairo_content_t content, - int width, - int height) -{ - cairo_xcb_surface_t *src = abstract_src; - xcb_connection_t *dpy = src->dpy; - xcb_pixmap_t pixmap; - cairo_xcb_surface_t *surface; - cairo_format_t format = _cairo_format_from_content (content); - xcb_render_pictforminfo_t *xrender_format; + pixmap = _cairo_xcb_connection_create_pixmap (connection, + PIXMAN_FORMAT_DEPTH (pixman_format), + other->drawable, + width, height); - /* As a good first approximation, if the display doesn't have COMPOSITE, - * we're better off using image surfaces for all temporary operations - */ - if (!CAIRO_SURFACE_RENDER_HAS_COMPOSITE (src)) { - return cairo_image_surface_create (format, width, height); + surface = (cairo_xcb_surface_t *) + _cairo_xcb_surface_create_internal (other->screen, + pixmap, TRUE, + pixman_format, + connection->standard_formats[format], + width, height); } - pixmap = xcb_generate_id (dpy); - xcb_create_pixmap (dpy, _CAIRO_FORMAT_DEPTH (format), - pixmap, src->drawable, - width <= 0 ? 1 : width, - height <= 0 ? 1 : height); - - xrender_format = _CAIRO_FORMAT_TO_XRENDER_FORMAT (dpy, format); - /* XXX: what to do if xrender_format is null? */ - surface = (cairo_xcb_surface_t *) - cairo_xcb_surface_create_with_xrender_format (dpy, pixmap, src->screen, - xrender_format, - width, height); - if (surface->base.status) - return &surface->base; + if (unlikely (surface->base.status)) + _cairo_xcb_connection_free_pixmap (connection, pixmap); - surface->owns_pixmap = TRUE; + _cairo_xcb_connection_release (connection); return &surface->base; } @@ -341,1826 +249,958 @@ static cairo_status_t _cairo_xcb_surface_finish (void *abstract_surface) { cairo_xcb_surface_t *surface = abstract_surface; + cairo_status_t status; - if (surface->dst_picture != XCB_NONE) - xcb_render_free_picture (surface->dpy, surface->dst_picture); - - if (surface->src_picture != XCB_NONE) - xcb_render_free_picture (surface->dpy, surface->src_picture); - - if (surface->owns_pixmap) - xcb_free_pixmap (surface->dpy, surface->drawable); + assert (surface->fallback == NULL); - if (surface->gc != XCB_NONE) - xcb_free_gc (surface->dpy, surface->gc); + cairo_list_del (&surface->link); - free (surface->clip_rects); - cairo_region_destroy (surface->clip_region); + if (surface->drm != NULL) { + cairo_surface_finish (surface->drm); + cairo_surface_destroy (surface->drm); - _cairo_freepool_fini (&surface->cookie_pool); + xcb_dri2_destroy_drawable (surface->connection->xcb_connection, + surface->drawable); + } - surface->dpy = NULL; + status = _cairo_xcb_connection_acquire (surface->connection); + if (status == CAIRO_STATUS_SUCCESS) { + if (_cairo_xcb_connection_take_socket (surface->connection) == CAIRO_STATUS_SUCCESS) { + if (surface->picture != XCB_NONE) { + _cairo_xcb_connection_render_free_picture (surface->connection, + surface->picture); + } - return CAIRO_STATUS_SUCCESS; -} + if (surface->owns_pixmap) + _cairo_xcb_connection_free_pixmap (surface->connection, surface->drawable); + } + _cairo_xcb_connection_release (surface->connection); + } -static int -_bits_per_pixel(xcb_connection_t *c, int depth) -{ - xcb_format_t *fmt = xcb_setup_pixmap_formats(xcb_get_setup(c)); - xcb_format_t *fmtend = fmt + xcb_setup_pixmap_formats_length(xcb_get_setup(c)); - - for(; fmt != fmtend; ++fmt) - if(fmt->depth == depth) - return fmt->bits_per_pixel; - - if(depth <= 4) - return 4; - if(depth <= 8) - return 8; - if(depth <= 16) - return 16; - return 32; -} + _cairo_xcb_connection_destroy (surface->connection); -static int -_bytes_per_line(xcb_connection_t *c, int width, int bpp) -{ - int bitmap_pad = xcb_get_setup(c)->bitmap_format_scanline_pad; - return ((bpp * width + bitmap_pad - 1) & -bitmap_pad) >> 3; + return status; } -static cairo_bool_t -_CAIRO_MASK_FORMAT (cairo_format_masks_t *masks, cairo_format_t *format) +static void +_destroy_image (pixman_image_t *image, void *data) { - switch (masks->bpp) { - case 32: - if (masks->alpha_mask == 0xff000000 && - masks->red_mask == 0x00ff0000 && - masks->green_mask == 0x0000ff00 && - masks->blue_mask == 0x000000ff) - { - *format = CAIRO_FORMAT_ARGB32; - return TRUE; - } - if (masks->alpha_mask == 0x00000000 && - masks->red_mask == 0x00ff0000 && - masks->green_mask == 0x0000ff00 && - masks->blue_mask == 0x000000ff) - { - *format = CAIRO_FORMAT_RGB24; - return TRUE; - } - break; - case 8: - if (masks->alpha_mask == 0xff) - { - *format = CAIRO_FORMAT_A8; - return TRUE; - } - break; - case 1: - if (masks->alpha_mask == 0x1) - { - *format = CAIRO_FORMAT_A1; - return TRUE; - } - break; - } - return FALSE; + free (data); } -static cairo_status_t -_get_image_surface (cairo_xcb_surface_t *surface, - cairo_rectangle_int_t *interest_rect, - cairo_image_surface_t **image_out, - cairo_rectangle_int_t *image_rect) +static cairo_int_status_t +_cairo_xcb_surface_create_shm_image (cairo_xcb_surface_t *target, + cairo_image_surface_t **image_out, + cairo_xcb_shm_info_t **shm_info_out) { cairo_image_surface_t *image; - xcb_get_image_reply_t *imagerep; - int bpp, bytes_per_line; - cairo_rectangle_int_t extents; - unsigned char *data; - cairo_format_masks_t masks; - cairo_format_t format; - - extents.x = 0; - extents.y = 0; - extents.width = surface->width; - extents.height = surface->height; - - if (interest_rect) { - if (! _cairo_rectangle_intersect (&extents, interest_rect)) { - *image_out = NULL; - return CAIRO_STATUS_SUCCESS; - } - } - - if (image_rect) - *image_rect = extents; - - /* XXX: This should try to use the XShm extension if available */ - - if (surface->use_pixmap == 0) - { - xcb_generic_error_t *error; - - imagerep = xcb_get_image_reply (surface->dpy, - xcb_get_image (surface->dpy, - XCB_IMAGE_FORMAT_Z_PIXMAP, - surface->drawable, - extents.x, extents.y, - extents.width, extents.height, - AllPlanes), - &error); - - /* If we get an error, the surface must have been a window, - * so retry with the safe code path. - */ - if (error) - surface->use_pixmap = CAIRO_ASSUME_PIXMAP; - } - else - { - surface->use_pixmap--; - imagerep = NULL; - } - - if (!imagerep) - { - /* 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 - */ - xcb_pixmap_t pixmap; - cairo_xcb_cookie_t *cookies[2]; - cairo_status_t status; - - status = _cairo_xcb_surface_ensure_gc (surface); - if (unlikely (status)) - return status; - - status = _cairo_freepool_alloc_array (&surface->cookie_pool, - ARRAY_LENGTH (cookies), - (void **) cookies); - if (unlikely (status)) - return status; - - pixmap = xcb_generate_id (surface->dpy); - cookies[0]->xcb = xcb_create_pixmap_checked (surface->dpy, - surface->depth, - pixmap, - surface->drawable, - extents.width, extents.height); - cairo_list_add_tail (&cookies[0]->link, &surface->to_be_checked); - - cookies[1]->xcb = xcb_copy_area_checked (surface->dpy, - surface->drawable, - pixmap, surface->gc, - extents.x, extents.y, - 0, 0, - extents.width, extents.height); - cairo_list_add_tail (&cookies[1]->link, &surface->to_be_checked); - - imagerep = xcb_get_image_reply (surface->dpy, - xcb_get_image (surface->dpy, - XCB_IMAGE_FORMAT_Z_PIXMAP, - pixmap, - extents.x, extents.y, - extents.width, extents.height, - AllPlanes), - 0); - - xcb_free_pixmap (surface->dpy, pixmap); - - } - if (unlikely (imagerep == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); + cairo_xcb_shm_info_t *shm_info; + cairo_status_t status; + size_t size, stride; - bpp = _bits_per_pixel(surface->dpy, imagerep->depth); - bytes_per_line = _bytes_per_line(surface->dpy, surface->width, bpp); + if ((target->flags & CAIRO_XCB_HAS_SHM) == 0) + return CAIRO_INT_STATUS_UNSUPPORTED; - data = _cairo_malloc_ab (surface->height, bytes_per_line); - if (data == NULL) { - free (imagerep); - return _cairo_error (CAIRO_STATUS_NO_MEMORY); + stride = CAIRO_STRIDE_FOR_WIDTH_BPP (target->width, + PIXMAN_FORMAT_BPP (target->pixman_format)); + size = stride * target->height; + if (size < CAIRO_XCB_SHM_SMALL_IMAGE) { + target->flags &= ~CAIRO_XCB_HAS_SHM; + return CAIRO_INT_STATUS_UNSUPPORTED; } - memcpy (data, xcb_get_image_data (imagerep), bytes_per_line * surface->height); - free (imagerep); + status = _cairo_xcb_connection_allocate_shm_info (target->screen->connection, + size, &shm_info); + if (unlikely (status)) + return status; - /* - * Compute the pixel format masks from either an xcb_visualtype_t or - * a xcb_render_pctforminfo_t, failing we assume the drawable is an - * alpha-only pixmap as it could only have been created that way - * through the cairo_xlib_surface_create_for_bitmap function. - */ - if (surface->visual) { - masks.bpp = bpp; - masks.alpha_mask = 0; - masks.red_mask = surface->visual->red_mask; - masks.green_mask = surface->visual->green_mask; - masks.blue_mask = surface->visual->blue_mask; - } else if (surface->xrender_format.id != XCB_NONE) { - masks.bpp = bpp; - masks.red_mask = (unsigned long)surface->xrender_format.direct.red_mask << surface->xrender_format.direct.red_shift; - masks.green_mask = (unsigned long)surface->xrender_format.direct.green_mask << surface->xrender_format.direct.green_shift; - masks.blue_mask = (unsigned long)surface->xrender_format.direct.blue_mask << surface->xrender_format.direct.blue_shift; - masks.alpha_mask = (unsigned long)surface->xrender_format.direct.alpha_mask << surface->xrender_format.direct.alpha_shift; - } else { - masks.bpp = bpp; - masks.red_mask = 0; - masks.green_mask = 0; - masks.blue_mask = 0; - if (surface->depth < 32) - masks.alpha_mask = (1 << surface->depth) - 1; - else - masks.alpha_mask = 0xffffffff; + image = (cairo_image_surface_t *) + _cairo_image_surface_create_with_pixman_format (shm_info->mem, + target->pixman_format, + target->width, + target->height, + stride); + status = image->base.status; + if (unlikely (status)) { + _cairo_xcb_shm_info_destroy (shm_info); + return status; } - /* - * Prefer to use a standard pixman format instead of the - * general masks case. - */ - if (_CAIRO_MASK_FORMAT (&masks, &format)) { - image = (cairo_image_surface_t *) - cairo_image_surface_create_for_data (data, - format, - extents.width, - extents.height, - bytes_per_line); - if (image->base.status) - goto FAIL; - } else { - /* - * XXX This can't work. We must convert the data to one of the - * supported pixman formats. Pixman needs another function - * which takes data in an arbitrary format and converts it - * to something supported by that library. - */ - ASSERT_NOT_REACHED; - goto FAIL; + status = _cairo_user_data_array_set_data (&image->base.user_data, + (const cairo_user_data_key_t *) target->connection, + shm_info, + (cairo_destroy_func_t) _cairo_xcb_shm_info_destroy); + if (unlikely (status)) { + cairo_surface_destroy (&image->base); + _cairo_xcb_shm_info_destroy (shm_info); + return status; } - /* Let the surface take ownership of the data */ - _cairo_image_surface_assume_ownership_of_data (image); - *image_out = image; + *shm_info_out = shm_info; return CAIRO_STATUS_SUCCESS; - - FAIL: - free (data); - return _cairo_error (CAIRO_STATUS_NO_MEMORY); } static cairo_status_t -_cairo_xcb_surface_ensure_src_picture (cairo_xcb_surface_t *surface) +_get_shm_image (cairo_xcb_surface_t *surface, + cairo_image_surface_t **image_out) { - if (!surface->src_picture) { - xcb_void_cookie_t cookie; + cairo_image_surface_t *image; + cairo_xcb_shm_info_t *shm_info; + cairo_status_t status; - surface->src_picture = xcb_generate_id (surface->dpy); - cookie = xcb_render_create_picture_checked (surface->dpy, - surface->src_picture, - surface->drawable, - surface->xrender_format.id, - 0, NULL); + status = _cairo_xcb_surface_create_shm_image (surface, &image, &shm_info); + if (unlikely (status)) + return status; - return _cairo_xcb_add_cookie_to_be_checked (surface, cookie); + if (! surface->base.is_clear) { + status = _cairo_xcb_connection_shm_get_image (surface->connection, + surface->drawable, + 0, 0, + surface->width, + surface->height, + shm_info->shm, + shm_info->offset); + if (unlikely (status)) + return status; + } else { + memset (image->data, 0, image->stride * image->height); } + *image_out = image; return CAIRO_STATUS_SUCCESS; } static cairo_status_t -_cairo_xcb_surface_ensure_dst_picture (cairo_xcb_surface_t *surface) +_get_image (cairo_xcb_surface_t *surface, + cairo_bool_t use_shm, + cairo_image_surface_t **image_out) { - if (!surface->dst_picture) { - xcb_void_cookie_t cookie; + cairo_image_surface_t *image; + cairo_xcb_connection_t *connection; + xcb_get_image_reply_t *reply; + cairo_status_t status; + + connection = surface->connection; + + status = _cairo_xcb_connection_acquire (connection); + if (unlikely (status)) + return status; - surface->dst_picture = xcb_generate_id (surface->dpy); - cookie = xcb_render_create_picture_checked (surface->dpy, - surface->dst_picture, - surface->drawable, - surface->xrender_format.id, - 0, NULL); + status = _cairo_xcb_connection_take_socket (connection); + if (unlikely (status)) + goto FAIL; - return _cairo_xcb_add_cookie_to_be_checked (surface, cookie); + if (use_shm) { + status = _get_shm_image (surface, image_out); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + goto FAIL; } - return CAIRO_STATUS_SUCCESS; -} + if (surface->base.is_clear) { + image = (cairo_image_surface_t *) + _cairo_image_surface_create_with_pixman_format (NULL, + surface->pixman_format, + surface->width, + surface->height, + 0); + status = image->base.status; + *image_out = image; + goto FAIL; + } -static cairo_status_t -_cairo_xcb_surface_ensure_gc (cairo_xcb_surface_t *surface) -{ - xcb_void_cookie_t cookie; + if (surface->use_pixmap == 0) { + status = _cairo_xcb_connection_get_image (connection, + surface->drawable, + 0, 0, + surface->width, + surface->height, + &reply); + if (unlikely (status)) + goto FAIL; + } else { + surface->use_pixmap--; + reply = NULL; + } - if (surface->gc) - return CAIRO_STATUS_SUCCESS; + 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 + */ + 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, + surface->width, + surface->height); + + /* XXX IncludeInferiors? */ + _cairo_xcb_connection_copy_area (connection, + surface->drawable, + pixmap, gc, + 0, 0, + 0, 0, + surface->width, + surface->height); - surface->gc = xcb_generate_id(surface->dpy); - cookie = xcb_create_gc_checked (surface->dpy, surface->gc, surface->drawable, 0, 0); - _cairo_xcb_surface_set_gc_clip_rects (surface); + _cairo_xcb_screen_put_gc (surface->screen, surface->depth, gc); - return _cairo_xcb_add_cookie_to_be_checked (surface, cookie); -} + status = _cairo_xcb_connection_get_image (connection, + pixmap, + 0, 0, + surface->width, + surface->height, + &reply); + _cairo_xcb_connection_free_pixmap (connection, pixmap); -static cairo_status_t -_draw_image_surface (cairo_xcb_surface_t *surface, - cairo_image_surface_t *image, - int src_x, - int src_y, - int width, - int height, - int dst_x, - int dst_y) -{ - int bpp, bpl; - uint32_t data_len; - uint8_t *data, left_pad=0; - xcb_void_cookie_t cookie; - - /* equivalent of XPutImage(..., src_x,src_y, dst_x,dst_y, width,height); */ - /* XXX: assumes image and surface formats and depths are the same */ - /* XXX: assumes depth is a multiple of 8 (not bitmap) */ - - /* fit src_{x,y,width,height} within image->{0,0,width,height} */ - if (src_x < 0) { - width += src_x; - src_x = 0; + if (unlikely (status)) + goto FAIL; } - if (src_y < 0) { - height += src_y; - src_y = 0; + + if (unlikely (reply == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto FAIL; } - if (width + src_x > image->width) - width = image->width - src_x; - if (height + src_y > image->height) - height = image->height - src_y; - if (width <= 0 || height <= 0) - return CAIRO_STATUS_SUCCESS; - bpp = _bits_per_pixel(surface->dpy, image->depth); - /* XXX: could use bpl = image->stride? */ - bpl = _bytes_per_line(surface->dpy, image->width, bpp); + /* XXX byte swap */ + /* XXX format conversion */ + assert (reply->depth == surface->depth); - if (src_x == 0 && width == image->width) { - /* can work in-place */ - data_len = height * bpl; - data = image->data + src_y * bpl; - } else { - /* must copy {src_x,src_y,width,height} into new data */ - int line = 0; - uint8_t *data_line, *image_line; - int data_bpl = _bytes_per_line(surface->dpy, width, bpp); - data_len = height * data_bpl; - data_line = data = malloc(data_len); - if (data == NULL) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - image_line = image->data + src_y * bpl + (src_x * bpp / 8); - while (line++ < height) { - memcpy(data_line, image_line, data_bpl); - data_line += data_bpl; - image_line += bpl; - } + image = (cairo_image_surface_t *) + _cairo_image_surface_create_with_pixman_format + (xcb_get_image_data (reply), + surface->pixman_format, + surface->width, + surface->height, + CAIRO_STRIDE_FOR_WIDTH_BPP (surface->width, + PIXMAN_FORMAT_BPP (surface->pixman_format))); + status = image->base.status; + if (unlikely (status)) { + free (reply); + goto FAIL; } - _cairo_xcb_surface_ensure_gc (surface); - cookie = xcb_put_image_checked (surface->dpy, XCB_IMAGE_FORMAT_Z_PIXMAP, - surface->drawable, surface->gc, - width, height, - dst_x, dst_y, - left_pad, image->depth, - data_len, data); - - if (data < image->data || data >= image->data + image->height * bpl) - free (data); - - return _cairo_xcb_add_cookie_to_be_checked (surface, cookie); -} -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_image_surface_t *image; - cairo_status_t status; + assert (xcb_get_image_data_length (reply) == image->height * image->stride); - status = _get_image_surface (surface, NULL, &image, NULL); - if (status) - return status; + pixman_image_set_destroy_function (image->pixman_image, _destroy_image, reply); - *image_out = image; - *image_extra = NULL; + _cairo_xcb_connection_release (connection); + + /* synchronisation point */ + surface->marked_dirty = FALSE; + *image_out = image; return CAIRO_STATUS_SUCCESS; + +FAIL: + _cairo_xcb_connection_release (connection); + return status; } -static cairo_surface_t * -_cairo_xcb_surface_snapshot (void *abstract_surface) +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_image_surface_t *image; cairo_status_t status; - status = _get_image_surface (surface, NULL, &image, NULL); - if (unlikely (status)) - return _cairo_surface_create_in_error (status); + if (surface->drm != NULL && ! surface->marked_dirty) { + return _cairo_surface_acquire_source_image (surface->drm, + image_out, image_extra); + } - return &image->base; -} + if (surface->fallback != NULL) { + image = (cairo_image_surface_t *) cairo_surface_reference (surface->fallback); + goto DONE; + } -static void -_cairo_xcb_surface_release_source_image (void *abstract_surface, - cairo_image_surface_t *image, - void *image_extra) -{ - cairo_surface_destroy (&image->base); -} + image = (cairo_image_surface_t *) + _cairo_surface_has_snapshot (&surface->base, + &_cairo_image_surface_backend); + if (image != NULL) { + image = (cairo_image_surface_t *) cairo_surface_reference (&image->base); + goto DONE; + } -static cairo_status_t -_cairo_xcb_surface_acquire_dest_image (void *abstract_surface, - cairo_rectangle_int_t *interest_rect, - cairo_image_surface_t **image_out, - cairo_rectangle_int_t *image_rect_out, - void **image_extra) -{ - cairo_xcb_surface_t *surface = abstract_surface; - cairo_image_surface_t *image; - cairo_status_t status; + status = _get_image (surface, FALSE, &image); + if (unlikely (status)) + return status; - status = _get_image_surface (surface, interest_rect, &image, image_rect_out); - if (status) + status = _cairo_surface_attach_snapshot (&surface->base, &image->base, NULL); + if (unlikely (status)) { + cairo_surface_destroy (&image->base); return status; + } +DONE: *image_out = image; *image_extra = NULL; - return CAIRO_STATUS_SUCCESS; } static void -_cairo_xcb_surface_release_dest_image (void *abstract_surface, - cairo_rectangle_int_t *interest_rect, - cairo_image_surface_t *image, - cairo_rectangle_int_t *image_rect, - void *image_extra) +_cairo_xcb_surface_release_source_image (void *abstract_surface, + cairo_image_surface_t *image, + void *image_extra) { cairo_xcb_surface_t *surface = abstract_surface; - /* ignore errors */ - _draw_image_surface (surface, image, 0, 0, image->width, image->height, - image_rect->x, image_rect->y); + if (surface->drm != NULL && ! surface->marked_dirty) { + return _cairo_surface_release_source_image (surface->drm, + image, image_extra); + } cairo_surface_destroy (&image->base); } -/* - * Return whether two xcb surfaces share the same - * screen. Both core and Render drawing require this - * when using multiple drawables in an operation. - */ static cairo_bool_t -_cairo_xcb_surface_same_screen (cairo_xcb_surface_t *dst, - cairo_xcb_surface_t *src) -{ - return dst->dpy == src->dpy && dst->screen == src->screen; -} - -static cairo_status_t -_cairo_xcb_surface_clone_similar (void *abstract_surface, - cairo_surface_t *src, - cairo_content_t content, - int src_x, - int src_y, - int width, - int height, - int *clone_offset_x, - int *clone_offset_y, - cairo_surface_t **clone_out) +_cairo_xcb_surface_get_extents (void *abstract_surface, + cairo_rectangle_int_t *extents) { cairo_xcb_surface_t *surface = abstract_surface; - cairo_xcb_surface_t *clone; - - if (src->backend == surface->base.backend ) { - cairo_xcb_surface_t *xcb_src = (cairo_xcb_surface_t *)src; - - if (_cairo_xcb_surface_same_screen(surface, xcb_src)) { - *clone_offset_x = 0; - *clone_offset_y = 0; - *clone_out = cairo_surface_reference (src); - - return CAIRO_STATUS_SUCCESS; - } - } else if (_cairo_surface_is_image (src)) { - cairo_image_surface_t *image_src = (cairo_image_surface_t *)src; - cairo_content_t content = _cairo_content_from_format (image_src->format); - cairo_status_t status; - - if (surface->base.status) - return surface->base.status; - - clone = (cairo_xcb_surface_t *) - _cairo_xcb_surface_create_similar (surface, content, width, height); - if (clone == NULL) - return CAIRO_INT_STATUS_UNSUPPORTED; - if (clone->base.status) - return clone->base.status; - - status = _draw_image_surface (clone, image_src, - src_x, src_y, - width, height, - 0, 0); - if (status) { - cairo_surface_destroy (&clone->base); - return status; - } - - *clone_offset_x = src_x; - *clone_offset_y = src_y; - *clone_out = &clone->base; - - return CAIRO_STATUS_SUCCESS; - } - return CAIRO_INT_STATUS_UNSUPPORTED; + extents->x = extents->y = 0; + extents->width = surface->width; + extents->height = surface->height; + return TRUE; } -static cairo_status_t -_cairo_xcb_surface_set_matrix (cairo_xcb_surface_t *surface, - cairo_matrix_t *matrix) +static void +_cairo_xcb_surface_get_font_options (void *abstract_surface, + cairo_font_options_t *options) { - xcb_render_transform_t xtransform; - xcb_void_cookie_t cookie; - - xtransform.matrix11 = _cairo_fixed_16_16_from_double (matrix->xx); - xtransform.matrix12 = _cairo_fixed_16_16_from_double (matrix->xy); - xtransform.matrix13 = _cairo_fixed_16_16_from_double (matrix->x0); - - xtransform.matrix21 = _cairo_fixed_16_16_from_double (matrix->yx); - xtransform.matrix22 = _cairo_fixed_16_16_from_double (matrix->yy); - xtransform.matrix23 = _cairo_fixed_16_16_from_double (matrix->y0); - - xtransform.matrix31 = 0; - xtransform.matrix32 = 0; - xtransform.matrix33 = 1 << 16; - - if (!CAIRO_SURFACE_RENDER_HAS_PICTURE_TRANSFORM (surface)) - { - static const xcb_render_transform_t identity = { - 1 << 16, 0x00000, 0x00000, - 0x00000, 1 << 16, 0x00000, - 0x00000, 0x00000, 1 << 16 - }; - - if (memcmp (&xtransform, &identity, sizeof (xcb_render_transform_t)) == 0) - return CAIRO_STATUS_SUCCESS; - - return CAIRO_INT_STATUS_UNSUPPORTED; - } - - cookie = xcb_render_set_picture_transform_checked (surface->dpy, - surface->src_picture, - xtransform); - return _cairo_xcb_add_cookie_to_be_checked (surface, cookie); + /* XXX copy from xlib */ + _cairo_font_options_init_default (options); } static cairo_status_t -_cairo_xcb_surface_set_filter (cairo_xcb_surface_t *surface, - cairo_filter_t filter) +_put_shm_image (cairo_xcb_surface_t *surface, + xcb_gcontext_t gc, + cairo_image_surface_t *image) { - const char *render_filter; - xcb_void_cookie_t cookie; - - if (!CAIRO_SURFACE_RENDER_HAS_FILTERS (surface)) - { - if (filter == CAIRO_FILTER_FAST || filter == CAIRO_FILTER_NEAREST) - return CAIRO_STATUS_SUCCESS; + 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; - } - - switch (filter) { - case CAIRO_FILTER_FAST: - render_filter = "fast"; - break; - case CAIRO_FILTER_GOOD: - render_filter = "good"; - break; - case CAIRO_FILTER_BEST: - render_filter = "best"; - break; - case CAIRO_FILTER_NEAREST: - render_filter = "nearest"; - break; - case CAIRO_FILTER_BILINEAR: - render_filter = "bilinear"; - break; - case CAIRO_FILTER_GAUSSIAN: - default: - render_filter = "best"; - break; - } - cookie = xcb_render_set_picture_filter_checked (surface->dpy, surface->src_picture, - strlen(render_filter), render_filter, - 0, NULL); + shm_info->seqno = + _cairo_xcb_connection_shm_put_image (surface->connection, + surface->drawable, + gc, + surface->width, surface->height, + 0, 0, + image->width, image->height, + 0, 0, + image->depth, + shm_info->shm, + shm_info->offset); - return _cairo_xcb_add_cookie_to_be_checked (surface, cookie); + return CAIRO_STATUS_SUCCESS; } static cairo_status_t -_cairo_xcb_surface_set_repeat (cairo_xcb_surface_t *surface, cairo_extend_t extend) +_put_image (cairo_xcb_surface_t *surface, + cairo_image_surface_t *image) { - uint32_t mask = XCB_RENDER_CP_REPEAT; - uint32_t pa[1]; - xcb_void_cookie_t cookie; - - switch (extend) { - case CAIRO_EXTEND_NONE: - pa[0] = XCB_RENDER_REPEAT_NONE; - break; - - case CAIRO_EXTEND_REPEAT: - pa[0] = XCB_RENDER_REPEAT_NORMAL; - break; - - case CAIRO_EXTEND_REFLECT: - if (!CAIRO_SURFACE_RENDER_HAS_REPEAT_REFLECT(surface)) - return CAIRO_INT_STATUS_UNSUPPORTED; - - pa[0] = XCB_RENDER_REPEAT_REFLECT; - break; - - case CAIRO_EXTEND_PAD: - if (!CAIRO_SURFACE_RENDER_HAS_REPEAT_PAD(surface)) - return CAIRO_INT_STATUS_UNSUPPORTED; - - pa[0] = XCB_RENDER_REPEAT_PAD; - break; - - default: - return CAIRO_INT_STATUS_UNSUPPORTED; - } - - cookie = xcb_render_change_picture_checked (surface->dpy, surface->src_picture, - mask, pa); - return _cairo_xcb_add_cookie_to_be_checked (surface, cookie); -} + cairo_status_t status = CAIRO_STATUS_SUCCESS; -static cairo_int_status_t -_cairo_xcb_surface_set_attributes (cairo_xcb_surface_t *surface, - cairo_surface_attributes_t *attributes) -{ - cairo_int_status_t status; + /* XXX track damaged region? */ - status = _cairo_xcb_surface_ensure_src_picture (surface); - if (status) + status = _cairo_xcb_connection_acquire (surface->connection); + if (unlikely (status)) return status; - status = _cairo_xcb_surface_set_matrix (surface, &attributes->matrix); - if (status) + status = _cairo_xcb_connection_take_socket (surface->connection); + if (unlikely (status)) { + _cairo_xcb_connection_release (surface->connection); return status; + } - status = _cairo_xcb_surface_set_repeat (surface, attributes->extend); - if (status) - return status; + if (image->pixman_format == surface->pixman_format) { + xcb_gcontext_t gc; + + assert (image->width == surface->width); + assert (image->height == surface->height); + 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, + 0, 0, + image->depth, + image->stride, + image->data); + status = CAIRO_STATUS_SUCCESS; + } - status = _cairo_xcb_surface_set_filter (surface, attributes->filter); - if (status) - return status; + _cairo_xcb_screen_put_gc (surface->screen, surface->depth, gc); + } else { + ASSERT_NOT_REACHED; + } - return CAIRO_STATUS_SUCCESS; + _cairo_xcb_connection_release (surface->connection); + return status; } -/* Checks whether we can can directly draw from src to dst with - * the core protocol: either with CopyArea or using src as a - * a tile in a GC. - */ -static cairo_bool_t -_surfaces_compatible (cairo_xcb_surface_t *dst, - cairo_xcb_surface_t *src) +static cairo_status_t +_cairo_xcb_surface_flush (void *abstract_surface) { - /* same screen */ - if (!_cairo_xcb_surface_same_screen (dst, src)) - return FALSE; - - /* same depth (for core) */ - if (src->depth != dst->depth) - return FALSE; + cairo_xcb_surface_t *surface = abstract_surface; + cairo_status_t status = CAIRO_STATUS_SUCCESS; - /* if Render is supported, match picture formats */ - if (src->xrender_format.id != dst->xrender_format.id) - return FALSE; - else if (src->xrender_format.id != XCB_NONE) - return TRUE; + if (surface->drm != NULL && ! surface->marked_dirty) + return surface->drm->backend->flush (surface->drm); - /* Without Render, match visuals instead */ - if (src->visual == dst->visual) - return TRUE; + if (likely (surface->fallback == NULL)) + return CAIRO_STATUS_SUCCESS; - return FALSE; -} + if (! surface->base.finished) { + status = _put_image (surface, + (cairo_image_surface_t *) surface->fallback); -static cairo_bool_t -_surface_has_alpha (cairo_xcb_surface_t *surface) -{ - if (surface->xrender_format.id != XCB_NONE) { - if (surface->xrender_format.type == XCB_RENDER_PICT_TYPE_DIRECT && - surface->xrender_format.direct.alpha_mask != 0) - return TRUE; - else - return FALSE; - } else { + if (status == CAIRO_STATUS_SUCCESS) + status = cairo_surface_status (surface->fallback); - /* In the no-render case, we never have alpha */ - return FALSE; + if (status == CAIRO_STATUS_SUCCESS) { + status = _cairo_surface_attach_snapshot (&surface->base, + surface->fallback, + cairo_surface_finish); + } } -} -/* Returns true if the given operator and source-alpha combination - * requires alpha compositing to complete. - */ -static cairo_bool_t -_operator_needs_alpha_composite (cairo_operator_t op, - cairo_bool_t surface_has_alpha) -{ - if (op == CAIRO_OPERATOR_SOURCE || - (!surface_has_alpha && - (op == CAIRO_OPERATOR_OVER || - op == CAIRO_OPERATOR_ATOP || - op == CAIRO_OPERATOR_IN))) - return FALSE; + cairo_surface_destroy (surface->fallback); + surface->fallback = NULL; - return TRUE; + return status; } -/* There is a bug in most older X servers with compositing using a - * untransformed repeating source pattern when the source is in off-screen - * video memory, and another with repeated transformed images using a - * general transform matrix. When these bugs could be triggered, we need a - * fallback: in the common case where we have no transformation and the - * source and destination have the same format/visual, we can do the - * operation using the core protocol for the first bug, otherwise, we need - * a software fallback. - * - * We can also often optimize a compositing operation by calling XCopyArea - * for some common cases where there is no alpha compositing to be done. - * We figure that out here as well. - */ -typedef enum { - DO_RENDER, /* use render */ - DO_XCOPYAREA, /* core protocol XCopyArea optimization/fallback */ - DO_XTILE, /* core protocol XSetTile optimization/fallback */ - DO_UNSUPPORTED /* software fallback */ -} composite_operation_t; - -/* Initial check for the render bugs; we need to recheck for the - * offscreen-memory bug after we turn patterns into surfaces, since that - * may introduce a repeating pattern for gradient patterns. We don't need - * to check for the repeat+transform bug because gradient surfaces aren't - * transformed. - * - * All we do here is reject cases where we *know* are going to - * hit the bug and won't be able to use a core protocol fallback. - */ -static composite_operation_t -_categorize_composite_operation (cairo_xcb_surface_t *dst, - cairo_operator_t op, - const cairo_pattern_t *src_pattern, - cairo_bool_t have_mask) - +static cairo_status_t +_cairo_xcb_surface_mark_dirty (void *abstract_surface, + int x, int y, + int width, int height) { -#if XXX_BUGGY_REPEAT - if (!dst->buggy_repeat) - return DO_RENDER; - - if (src_pattern->type == CAIRO_PATTERN_TYPE_SURFACE) - { - cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *)src_pattern; - - if (_cairo_matrix_is_integer_translation (&src_pattern->matrix, NULL, NULL) && - src_pattern->extend == CAIRO_EXTEND_REPEAT) - { - /* This is the case where we have the bug involving - * untransformed repeating source patterns with off-screen - * video memory; reject some cases where a core protocol - * fallback is impossible. - */ - if (have_mask || - !(op == CAIRO_OPERATOR_SOURCE || op == CAIRO_OPERATOR_OVER)) - return DO_UNSUPPORTED; - - if (_cairo_surface_is_xcb (surface_pattern->surface)) { - cairo_xcb_surface_t *src = (cairo_xcb_surface_t *)surface_pattern->surface; - - if (op == CAIRO_OPERATOR_OVER && _surface_has_alpha (src)) - return DO_UNSUPPORTED; - - /* If these are on the same screen but otherwise incompatible, - * make a copy as core drawing can't cross depths and doesn't - * work rightacross visuals of the same depth - */ - if (_cairo_xcb_surface_same_screen (dst, src) && - !_surfaces_compatible (dst, src)) - return DO_UNSUPPORTED; - } - } - - /* Check for the other bug involving repeat patterns with general - * transforms. */ - if (!_cairo_matrix_is_integer_translation (&src_pattern->matrix, NULL, NULL) && - src_pattern->extend == CAIRO_EXTEND_REPEAT) - return DO_UNSUPPORTED; - } -#endif - return DO_RENDER; + cairo_xcb_surface_t *surface = abstract_surface; + surface->marked_dirty = TRUE; + return CAIRO_STATUS_SUCCESS; } -/* Recheck for composite-repeat once we've turned patterns into Xlib surfaces - * If we end up returning DO_UNSUPPORTED here, we're throwing away work we - * did to turn gradients into a pattern, but most of the time we can handle - * that case with core protocol fallback. - * - * Also check here if we can just use XCopyArea, instead of going through - * Render. - */ -static composite_operation_t -_recategorize_composite_operation (cairo_xcb_surface_t *dst, - cairo_operator_t op, - cairo_xcb_surface_t *src, - cairo_surface_attributes_t *src_attr, - cairo_bool_t have_mask) +static cairo_surface_t * +_cairo_xcb_surface_map_to_image (cairo_xcb_surface_t *surface) { - cairo_bool_t is_integer_translation = - _cairo_matrix_is_integer_translation (&src_attr->matrix, NULL, NULL); - cairo_bool_t needs_alpha_composite = - _operator_needs_alpha_composite (op, _surface_has_alpha (src)); - - if (!have_mask && - is_integer_translation && - src_attr->extend == CAIRO_EXTEND_NONE && - !needs_alpha_composite && - _surfaces_compatible(src, dst)) - { - return DO_XCOPYAREA; - } -#if XXX_BUGGY_REPEAT - if (!dst->buggy_repeat) - return DO_RENDER; - - if (is_integer_translation && - src_attr->extend == CAIRO_EXTEND_REPEAT && - (src->width != 1 || src->height != 1)) - { - if (!have_mask && - !needs_alpha_composite && - _surfaces_compatible (dst, src)) - { - return DO_XTILE; - } + cairo_status_t status; + cairo_image_surface_t *image; - return DO_UNSUPPORTED; - } -#endif - return DO_RENDER; -} + status = _get_image (surface, TRUE, &image); + if (unlikely (status)) + return _cairo_surface_create_in_error (status); -static int -_render_operator (cairo_operator_t op) -{ - switch (op) { - case CAIRO_OPERATOR_CLEAR: - return XCB_RENDER_PICT_OP_CLEAR; - case CAIRO_OPERATOR_SOURCE: - return XCB_RENDER_PICT_OP_SRC; - case CAIRO_OPERATOR_DEST: - return XCB_RENDER_PICT_OP_DST; - case CAIRO_OPERATOR_OVER: - return XCB_RENDER_PICT_OP_OVER; - case CAIRO_OPERATOR_DEST_OVER: - return XCB_RENDER_PICT_OP_OVER_REVERSE; - case CAIRO_OPERATOR_IN: - return XCB_RENDER_PICT_OP_IN; - case CAIRO_OPERATOR_DEST_IN: - return XCB_RENDER_PICT_OP_IN_REVERSE; - case CAIRO_OPERATOR_OUT: - return XCB_RENDER_PICT_OP_OUT; - case CAIRO_OPERATOR_DEST_OUT: - return XCB_RENDER_PICT_OP_OUT_REVERSE; - case CAIRO_OPERATOR_ATOP: - return XCB_RENDER_PICT_OP_ATOP; - case CAIRO_OPERATOR_DEST_ATOP: - return XCB_RENDER_PICT_OP_ATOP_REVERSE; - case CAIRO_OPERATOR_XOR: - return XCB_RENDER_PICT_OP_XOR; - case CAIRO_OPERATOR_ADD: - return XCB_RENDER_PICT_OP_ADD; - case CAIRO_OPERATOR_SATURATE: - return XCB_RENDER_PICT_OP_SATURATE; - default: - return XCB_RENDER_PICT_OP_OVER; - } + return &image->base; } static cairo_int_status_t -_cairo_xcb_surface_composite (cairo_operator_t op, - const cairo_pattern_t *src_pattern, - const cairo_pattern_t *mask_pattern, - void *abstract_dst, - int src_x, - int src_y, - int mask_x, - int mask_y, - int dst_x, - int dst_y, - unsigned int width, - unsigned int height, - cairo_region_t *clip_region) +_cairo_xcb_surface_paint (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_clip_t *clip) { - cairo_surface_attributes_t src_attr, mask_attr; - cairo_xcb_surface_t *dst = abstract_dst; - cairo_xcb_surface_t *src; - cairo_xcb_surface_t *mask; - cairo_int_status_t status; - composite_operation_t operation; - int itx, ity; - cairo_bool_t is_integer_translation; - xcb_void_cookie_t cookie; - - if (!CAIRO_SURFACE_RENDER_HAS_COMPOSITE (dst)) - return CAIRO_INT_STATUS_UNSUPPORTED; - - operation = _categorize_composite_operation (dst, op, src_pattern, - mask_pattern != NULL); - if (operation == DO_UNSUPPORTED) - return CAIRO_INT_STATUS_UNSUPPORTED; - - status = _cairo_pattern_acquire_surfaces (src_pattern, mask_pattern, - &dst->base, - src_x, src_y, - mask_x, mask_y, - width, height, - CAIRO_PATTERN_ACQUIRE_NO_REFLECT, - (cairo_surface_t **) &src, - (cairo_surface_t **) &mask, - &src_attr, &mask_attr); - if (status) - return status; - - operation = _recategorize_composite_operation (dst, op, src, &src_attr, - mask_pattern != NULL); - if (operation == DO_UNSUPPORTED) { - status = CAIRO_INT_STATUS_UNSUPPORTED; - goto BAIL; - } - - status = _cairo_xcb_surface_set_clip_region (dst, clip_region); - if (unlikely (status)) - goto BAIL; + cairo_xcb_surface_t *surface = abstract_surface; + cairo_status_t status; - status = _cairo_xcb_surface_set_attributes (src, &src_attr); - if (status) - goto BAIL; + if (surface->drm != NULL && ! surface->marked_dirty) + return _cairo_surface_paint (surface->drm, op, source, clip); - switch (operation) - { - case DO_RENDER: - status = _cairo_xcb_surface_ensure_dst_picture (dst); - if (unlikely (status)) - goto BAIL; - - if (mask) { - status = _cairo_xcb_surface_set_attributes (mask, &mask_attr); - if (unlikely (status)) - goto BAIL; - - cookie = xcb_render_composite_checked (dst->dpy, - _render_operator (op), - src->src_picture, - mask->src_picture, - dst->dst_picture, - src_x + src_attr.x_offset, - src_y + src_attr.y_offset, - mask_x + mask_attr.x_offset, - mask_y + mask_attr.y_offset, - dst_x, dst_y, - width, height); - } else { - static xcb_render_picture_t maskpict = { XCB_NONE }; - - cookie = xcb_render_composite_checked (dst->dpy, - _render_operator (op), - src->src_picture, - maskpict, - dst->dst_picture, - src_x + src_attr.x_offset, - src_y + src_attr.y_offset, - 0, 0, - dst_x, dst_y, - width, height); - } - break; - - case DO_XCOPYAREA: - status = _cairo_xcb_surface_ensure_gc (dst); - if (unlikely (status)) + if (surface->fallback == NULL) { + status = _cairo_xcb_surface_cairo_paint (surface, op, source, clip); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) return status; - cookie = xcb_copy_area_checked (dst->dpy, - src->drawable, - dst->drawable, - dst->gc, - src_x + src_attr.x_offset, - src_y + src_attr.y_offset, - dst_x, dst_y, - width, height); - break; - - case DO_XTILE: - /* This case is only used for bug fallbacks, though it is theoretically - * applicable to the case where we don't have the RENDER extension as - * well. - * - * We've checked that we have a repeating unscaled source in - * _recategorize_composite_operation. - */ - - status = _cairo_xcb_surface_ensure_gc (dst); - if (unlikely (status)) + status = _cairo_xcb_surface_render_paint (surface, op, source, clip); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) return status; - is_integer_translation = - _cairo_matrix_is_integer_translation (&src_attr.matrix, &itx, &ity); - assert (is_integer_translation == TRUE); - { - uint32_t mask = XCB_GC_FILL_STYLE | XCB_GC_TILE - | XCB_GC_TILE_STIPPLE_ORIGIN_X - | XCB_GC_TILE_STIPPLE_ORIGIN_Y; - uint32_t values[] = { - XCB_FILL_STYLE_TILED, src->drawable, - - (itx + src_attr.x_offset), - - (ity + src_attr.y_offset) - }; - xcb_rectangle_t rect = { dst_x, dst_y, width, height }; - - xcb_change_gc( dst->dpy, dst->gc, mask, values ); - cookie = xcb_poly_fill_rectangle_checked (dst->dpy, - dst->drawable, - dst->gc, - 1, &rect); - } - break; - - case DO_UNSUPPORTED: - default: - ASSERT_NOT_REACHED; - } - - status = _cairo_xcb_add_cookie_to_be_checked (dst, cookie); - if (unlikely (status)) - goto BAIL; - - if (!_cairo_operator_bounded_by_source (op)) { - status = _cairo_surface_composite_fixup_unbounded (&dst->base, - &src_attr, src->width, src->height, - mask ? &mask_attr : NULL, - mask ? mask->width : 0, - mask ? mask->height : 0, - src_x, src_y, - mask_x, mask_y, - dst_x, dst_y, width, height, - clip_region); + surface->fallback = _cairo_xcb_surface_map_to_image (surface); } - BAIL: - if (mask) - _cairo_pattern_release_surface (mask_pattern, &mask->base, &mask_attr); - - _cairo_pattern_release_surface (src_pattern, &src->base, &src_attr); - - return status; + return _cairo_surface_paint (surface->fallback, op, source, clip); } static cairo_int_status_t -_cairo_xcb_surface_fill_rectangles (void *abstract_surface, - cairo_operator_t op, - const cairo_color_t * color, - cairo_rectangle_int_t *rects, - int num_rects) +_cairo_xcb_surface_mask (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_pattern_t *mask, + cairo_clip_t *clip) { cairo_xcb_surface_t *surface = abstract_surface; - xcb_render_color_t render_color; - xcb_rectangle_t static_xrects[16]; - xcb_rectangle_t *xrects = static_xrects; cairo_status_t status; - xcb_void_cookie_t cookie; - int i; - - if (!CAIRO_SURFACE_RENDER_HAS_FILL_RECTANGLE (surface)) - return CAIRO_INT_STATUS_UNSUPPORTED; - render_color.red = color->red_short; - render_color.green = color->green_short; - render_color.blue = color->blue_short; - render_color.alpha = color->alpha_short; + if (surface->drm != NULL && ! surface->marked_dirty) + return _cairo_surface_mask (surface->drm, op, source, mask, clip); - status = _cairo_xcb_surface_set_clip_region (surface, NULL); - assert (status == CAIRO_STATUS_SUCCESS); - - if (num_rects > ARRAY_LENGTH(static_xrects)) { - xrects = _cairo_malloc_ab (num_rects, sizeof(xcb_rectangle_t)); - if (xrects == NULL) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } + if (surface->fallback == NULL) { + status = _cairo_xcb_surface_cairo_mask (surface, + op, source, mask, clip); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; - for (i = 0; i < num_rects; i++) { - xrects[i].x = rects[i].x; - xrects[i].y = rects[i].y; - xrects[i].width = rects[i].width; - xrects[i].height = rects[i].height; - } + status = _cairo_xcb_surface_render_mask (surface, + op, source, mask, clip); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; - status = _cairo_xcb_surface_ensure_dst_picture (surface); - if (unlikely (status)) { - if (xrects != static_xrects) - free (xrects); - return status; + surface->fallback = _cairo_xcb_surface_map_to_image (surface); } - cookie = xcb_render_fill_rectangles_checked (surface->dpy, - _render_operator (op), - surface->dst_picture, - render_color, num_rects, xrects); - - if (xrects != static_xrects) - free (xrects); - - return _cairo_xcb_add_cookie_to_be_checked (surface, cookie); + return _cairo_surface_mask (surface->fallback, + op, source, mask, + clip); } -/* Creates an A8 picture of size @width x @height, initialized with @color - */ -static cairo_status_t -_create_a8_picture (cairo_xcb_surface_t *surface, - xcb_render_color_t *color, - int width, - int height, - cairo_bool_t repeat, - xcb_render_picture_t *out) -{ - uint32_t values[] = { TRUE }; - uint32_t mask = repeat ? XCB_RENDER_CP_REPEAT : 0; - - xcb_pixmap_t pixmap; - xcb_render_picture_t picture; - xcb_render_pictforminfo_t *format; - xcb_rectangle_t rect = { 0, 0, width, height }; - - cairo_xcb_cookie_t *cookie[3]; - cairo_status_t status; - - status = _cairo_freepool_alloc_array (&surface->cookie_pool, - ARRAY_LENGTH (cookie), - (void **) cookie); - if (unlikely (status)) - return status; - - pixmap = xcb_generate_id (surface->dpy); - picture = xcb_generate_id (surface->dpy); - - cookie[0]->xcb = xcb_create_pixmap_checked (surface->dpy, 8, pixmap, surface->drawable, - width <= 0 ? 1 : width, - height <= 0 ? 1 : height); - cairo_list_add_tail (&cookie[0]->link, &surface->to_be_checked); - - format = _CAIRO_FORMAT_TO_XRENDER_FORMAT (surface->dpy, CAIRO_FORMAT_A8); - cookie[1]->xcb = xcb_render_create_picture_checked (surface->dpy, - picture, pixmap, format->id, - mask, values); - cairo_list_add_tail (&cookie[1]->link, &surface->to_be_checked); - - cookie[2]->xcb = xcb_render_fill_rectangles_checked (surface->dpy, - XCB_RENDER_PICT_OP_SRC, - picture, *color, 1, &rect); - cairo_list_add_tail (&cookie[2]->link, &surface->to_be_checked); - - xcb_free_pixmap (surface->dpy, pixmap); - - *out = picture; - return CAIRO_STATUS_SUCCESS; -} - -/* Creates a temporary mask for the trapezoids covering the area - * [@dst_x, @dst_y, @width, @height] of the destination surface. - */ -static cairo_status_t -_create_trapezoid_mask (cairo_xcb_surface_t *dst, - cairo_trapezoid_t *traps, - int num_traps, - int dst_x, - int dst_y, - int width, - int height, - xcb_render_pictforminfo_t *pict_format, - xcb_render_picture_t *mask_picture_out) +static cairo_int_status_t +_cairo_xcb_surface_stroke (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + 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_clip_t *clip) { - xcb_render_color_t transparent = { 0, 0, 0, 0 }; - xcb_render_color_t solid = { 0xffff, 0xffff, 0xffff, 0xffff }; - xcb_render_picture_t mask_picture, solid_picture; - xcb_render_trapezoid_t *offset_traps; - xcb_void_cookie_t cookie; + cairo_xcb_surface_t *surface = abstract_surface; cairo_status_t status; - int i; - - /* This would be considerably simpler using XRenderAddTraps(), but since - * we are only using this in the unbounded-operator case, we stick with - * XRenderCompositeTrapezoids, which is available on older versions - * of RENDER rather than conditionalizing. We should still hit an - * optimization that avoids creating another intermediate surface on - * the servers that have XRenderAddTraps(). - */ - status = _create_a8_picture (dst, &transparent, width, height, FALSE, &mask_picture); - if (unlikely (status)) - return status; - status = _create_a8_picture (dst, &solid, 1, 1, TRUE, &solid_picture); - if (unlikely (status)) { - xcb_render_free_picture (dst->dpy, mask_picture); - return status; + if (surface->drm != NULL && ! surface->marked_dirty) { + return _cairo_surface_stroke (surface->drm, + op, source, + path, style, + ctm, ctm_inverse, + tolerance, antialias, + clip); } - offset_traps = _cairo_malloc_ab (num_traps, sizeof (xcb_render_trapezoid_t)); - if (offset_traps == NULL) { - xcb_render_free_picture (dst->dpy, solid_picture); - xcb_render_free_picture (dst->dpy, mask_picture); - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } + if (surface->fallback == NULL) { + status = _cairo_xcb_surface_cairo_stroke (surface, op, source, + path, style, + ctm, ctm_inverse, + tolerance, antialias, + clip); - for (i = 0; i < num_traps; i++) { - offset_traps[i].top = _cairo_fixed_to_16_16(traps[i].top) - 0x10000 * dst_y; - offset_traps[i].bottom = _cairo_fixed_to_16_16(traps[i].bottom) - 0x10000 * dst_y; - offset_traps[i].left.p1.x = _cairo_fixed_to_16_16(traps[i].left.p1.x) - 0x10000 * dst_x; - offset_traps[i].left.p1.y = _cairo_fixed_to_16_16(traps[i].left.p1.y) - 0x10000 * dst_y; - offset_traps[i].left.p2.x = _cairo_fixed_to_16_16(traps[i].left.p2.x) - 0x10000 * dst_x; - offset_traps[i].left.p2.y = _cairo_fixed_to_16_16(traps[i].left.p2.y) - 0x10000 * dst_y; - offset_traps[i].right.p1.x = _cairo_fixed_to_16_16(traps[i].right.p1.x) - 0x10000 * dst_x; - offset_traps[i].right.p1.y = _cairo_fixed_to_16_16(traps[i].right.p1.y) - 0x10000 * dst_y; - offset_traps[i].right.p2.x = _cairo_fixed_to_16_16(traps[i].right.p2.x) - 0x10000 * dst_x; - offset_traps[i].right.p2.y = _cairo_fixed_to_16_16(traps[i].right.p2.y) - 0x10000 * dst_y; - } + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; - cookie = xcb_render_trapezoids_checked (dst->dpy, XCB_RENDER_PICT_OP_ADD, - solid_picture, mask_picture, - pict_format->id, - 0, 0, - num_traps, offset_traps); + status = _cairo_xcb_surface_render_stroke (surface, op, source, + path, style, + ctm, ctm_inverse, + tolerance, antialias, + clip); - xcb_render_free_picture (dst->dpy, solid_picture); - free (offset_traps); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; - status = _cairo_xcb_add_cookie_to_be_checked (dst, cookie); - if (unlikely (status)) { - xcb_render_free_picture (dst->dpy, mask_picture); - return status; + surface->fallback = _cairo_xcb_surface_map_to_image (surface); } - *mask_picture_out = mask_picture; - return CAIRO_STATUS_SUCCESS; + return _cairo_surface_stroke (surface->fallback, + op, source, + path, style, + ctm, ctm_inverse, + tolerance, antialias, + clip); } static cairo_int_status_t -_cairo_xcb_surface_composite_trapezoids (cairo_operator_t op, - const cairo_pattern_t *pattern, - void *abstract_dst, - cairo_antialias_t antialias, - int src_x, - int src_y, - int dst_x, - int dst_y, - unsigned int width, - unsigned int height, - cairo_trapezoid_t *traps, - int num_traps, - cairo_region_t *clip_region) +_cairo_xcb_surface_fill (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias, + cairo_clip_t *clip) { - cairo_surface_attributes_t attributes; - cairo_xcb_surface_t *dst = abstract_dst; - cairo_xcb_surface_t *src; - cairo_int_status_t status; - composite_operation_t operation; - int render_reference_x, render_reference_y; - int render_src_x, render_src_y; - int cairo_format; - xcb_render_pictforminfo_t *render_format; - - if (!CAIRO_SURFACE_RENDER_HAS_TRAPEZOIDS (dst)) - return CAIRO_INT_STATUS_UNSUPPORTED; - - operation = _categorize_composite_operation (dst, op, pattern, TRUE); - if (operation == DO_UNSUPPORTED) - return CAIRO_INT_STATUS_UNSUPPORTED; - - status = _cairo_pattern_acquire_surface (pattern, &dst->base, - src_x, src_y, width, height, - CAIRO_PATTERN_ACQUIRE_NO_REFLECT, - (cairo_surface_t **) &src, - &attributes); - if (status) - return status; - - operation = _recategorize_composite_operation (dst, op, src, &attributes, TRUE); - if (operation == DO_UNSUPPORTED) { - status = CAIRO_INT_STATUS_UNSUPPORTED; - goto BAIL; - } - - switch (antialias) { - case CAIRO_ANTIALIAS_NONE: - cairo_format = CAIRO_FORMAT_A1; - break; - case CAIRO_ANTIALIAS_GRAY: - case CAIRO_ANTIALIAS_SUBPIXEL: - case CAIRO_ANTIALIAS_DEFAULT: - default: - cairo_format = CAIRO_FORMAT_A8; - break; - } - render_format = _CAIRO_FORMAT_TO_XRENDER_FORMAT (dst->dpy, cairo_format); - /* XXX: what to do if render_format is null? */ + cairo_xcb_surface_t *surface = abstract_surface; + cairo_status_t status; - if (traps[0].left.p1.y < traps[0].left.p2.y) { - render_reference_x = _cairo_fixed_integer_floor (traps[0].left.p1.x); - render_reference_y = _cairo_fixed_integer_floor (traps[0].left.p1.y); - } else { - render_reference_x = _cairo_fixed_integer_floor (traps[0].left.p2.x); - render_reference_y = _cairo_fixed_integer_floor (traps[0].left.p2.y); + if (surface->drm != NULL && ! surface->marked_dirty) { + return _cairo_surface_fill (surface->drm, + op, source, + path, fill_rule, + tolerance, antialias, + clip); } - render_src_x = src_x + render_reference_x - dst_x; - render_src_y = src_y + render_reference_y - dst_y; - - status = _cairo_xcb_surface_ensure_dst_picture (dst); - if (unlikely (status)) - goto BAIL; - - status = _cairo_xcb_surface_set_clip_region (dst, clip_region); - if (unlikely (status)) - goto BAIL; - - status = _cairo_xcb_surface_set_attributes (src, &attributes); - if (status) - goto BAIL; - - if (!_cairo_operator_bounded_by_mask (op)) { - xcb_void_cookie_t cookie; - - /* xcb_render_composite+trapezoids() creates a mask only large enough for the - * trapezoids themselves, but if the operator is unbounded, then we need - * to actually composite all the way out to the bounds, so we create - * the mask and composite ourselves. There actually would - * be benefit to doing this in all cases, since RENDER implementations - * will frequently create a too temporary big mask, ignoring destination - * bounds and clip. (xcb_render_add_traps() could be used to make creating - * the mask somewhat cheaper.) - */ - xcb_render_picture_t mask_picture = 0; /* silence compiler */ - - status = _create_trapezoid_mask (dst, traps, num_traps, - dst_x, dst_y, width, height, - render_format, - &mask_picture); - if (status) - goto BAIL; - - cookie = xcb_render_composite_checked (dst->dpy, - _render_operator (op), - src->src_picture, - mask_picture, - dst->dst_picture, - src_x + attributes.x_offset, - src_y + attributes.y_offset, - 0, 0, - dst_x, dst_y, - width, height); - xcb_render_free_picture (dst->dpy, mask_picture); - - status = _cairo_xcb_add_cookie_to_be_checked (dst, cookie); - if (unlikely (status)) - goto BAIL; + if (surface->fallback == NULL) { + status = _cairo_xcb_surface_cairo_fill (surface, op, source, + path, fill_rule, + tolerance, antialias, + clip); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; - status = _cairo_surface_composite_shape_fixup_unbounded (&dst->base, - &attributes, src->width, src->height, - width, height, - src_x, src_y, - 0, 0, - dst_x, dst_y, width, height, - clip_region); + status = _cairo_xcb_surface_render_fill (surface, op, source, + path, fill_rule, + tolerance, antialias, + clip); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; - } else { - xcb_render_trapezoid_t xtraps_stack[CAIRO_STACK_ARRAY_LENGTH (xcb_render_trapezoid_t)]; - xcb_render_trapezoid_t *xtraps = xtraps_stack; - xcb_void_cookie_t cookie; - int i; - - if (num_traps > ARRAY_LENGTH(xtraps_stack)) { - xtraps = _cairo_malloc_ab (num_traps, sizeof(xcb_render_trapezoid_t)); - if (xtraps == NULL) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto BAIL; - } - } - - for (i = 0; i < num_traps; i++) { - xtraps[i].top = _cairo_fixed_to_16_16(traps[i].top); - xtraps[i].bottom = _cairo_fixed_to_16_16(traps[i].bottom); - xtraps[i].left.p1.x = _cairo_fixed_to_16_16(traps[i].left.p1.x); - xtraps[i].left.p1.y = _cairo_fixed_to_16_16(traps[i].left.p1.y); - xtraps[i].left.p2.x = _cairo_fixed_to_16_16(traps[i].left.p2.x); - xtraps[i].left.p2.y = _cairo_fixed_to_16_16(traps[i].left.p2.y); - xtraps[i].right.p1.x = _cairo_fixed_to_16_16(traps[i].right.p1.x); - xtraps[i].right.p1.y = _cairo_fixed_to_16_16(traps[i].right.p1.y); - xtraps[i].right.p2.x = _cairo_fixed_to_16_16(traps[i].right.p2.x); - xtraps[i].right.p2.y = _cairo_fixed_to_16_16(traps[i].right.p2.y); - } - - cookie = xcb_render_trapezoids_checked (dst->dpy, - _render_operator (op), - src->src_picture, dst->dst_picture, - render_format->id, - render_src_x + attributes.x_offset, - render_src_y + attributes.y_offset, - num_traps, xtraps); - - if (xtraps != xtraps_stack) - free (xtraps); - - status = _cairo_xcb_add_cookie_to_be_checked (dst, cookie); + surface->fallback = _cairo_xcb_surface_map_to_image (surface); } - BAIL: - _cairo_pattern_release_surface (pattern, &src->base, &attributes); - - return status; + return _cairo_surface_fill (surface->fallback, + op, source, + path, fill_rule, + tolerance, antialias, + clip); } -static cairo_bool_t -_cairo_xcb_surface_get_extents (void *abstract_surface, - cairo_rectangle_int_t *rectangle) -{ - cairo_xcb_surface_t *surface = abstract_surface; - - rectangle->x = 0; - rectangle->y = 0; - - rectangle->width = surface->width; - rectangle->height = surface->height; - - return TRUE; -} - -static cairo_status_t -_cairo_xcb_surface_flush (void *abstract_surface) +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, + cairo_clip_t *clip, + int *num_remaining) { cairo_xcb_surface_t *surface = abstract_surface; - cairo_status_t status = CAIRO_STATUS_SUCCESS; - - while (! cairo_list_is_empty (&surface->to_be_checked)) { - cairo_xcb_cookie_t *cookie; - xcb_generic_error_t *error; - - cookie = cairo_list_first_entry (&surface->to_be_checked, - cairo_xcb_cookie_t, - link); + cairo_status_t status; - error = xcb_request_check (surface->dpy, cookie->xcb); - if (error != NULL) { -#if 0 - /* XXX */ - fprintf (stderr, "Delayed error detected: %d, major=%d, minor=%d, seqno=%d\n", - error->error_code, - error->major_code, - error->minor_code, - error->sequence); -#endif - if (status == CAIRO_STATUS_SUCCESS) - status = _cairo_error (CAIRO_STATUS_WRITE_ERROR); /* XXX CAIRO_STATUS_CONNECTION_ERROR */ - } + *num_remaining = 0; - cairo_list_del (&cookie->link); - _cairo_freepool_free (&surface->cookie_pool, cookie); + if (surface->drm != NULL && ! surface->marked_dirty) { + return _cairo_surface_show_text_glyphs (surface->drm, + op, source, + NULL, 0, + glyphs, num_glyphs, + NULL, 0, 0, + scaled_font, + clip); } - return status; -} - -/* XXX: _cairo_xcb_surface_get_font_options */ - -static void -_cairo_xcb_surface_scaled_font_fini (cairo_scaled_font_t *scaled_font); - -static void -_cairo_xcb_surface_scaled_glyph_fini (cairo_scaled_glyph_t *scaled_glyph, - cairo_scaled_font_t *scaled_font); - -static cairo_int_status_t -_cairo_xcb_surface_show_glyphs (void *abstract_dst, - cairo_operator_t op, - const cairo_pattern_t *src_pattern, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_scaled_font_t *scaled_font, - cairo_clip_t *clip, - int *remaining_glyphs); - -static cairo_bool_t -_cairo_xcb_surface_is_similar (void *surface_a, - void *surface_b, - cairo_content_t content) -{ - cairo_xcb_surface_t *a = surface_a; - cairo_xcb_surface_t *b = surface_b; - xcb_render_pictforminfo_t *xrender_format; - - /* XXX: disable caching by the solid pattern cache until we implement - * display notification to avoid issuing xcb calls from the wrong thread - * or accessing the surface after the Display has been closed. - */ - return FALSE; + if (surface->fallback == NULL) { + status = _cairo_xcb_surface_cairo_glyphs (surface, + op, source, + scaled_font, glyphs, num_glyphs, + clip); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; - if (! _cairo_xcb_surface_same_screen (a, b)) - return FALSE; + status = _cairo_xcb_surface_render_glyphs (surface, + op, source, + scaled_font, glyphs, num_glyphs, + clip); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; - /* now check that the target is a similar format */ - xrender_format = _CAIRO_FORMAT_TO_XRENDER_FORMAT (b->dpy, - _cairo_format_from_content (content)); + surface->fallback = _cairo_xcb_surface_map_to_image (surface); + } - return a->xrender_format.id == xrender_format->id; + return _cairo_surface_show_text_glyphs (surface->fallback, + op, source, + NULL, 0, + glyphs, num_glyphs, + NULL, 0, 0, + scaled_font, + clip); } -/* XXX: move this to the bottom of the file, XCB and Xlib */ - -static const cairo_surface_backend_t cairo_xcb_surface_backend = { +const cairo_surface_backend_t _cairo_xcb_surface_backend = { CAIRO_SURFACE_TYPE_XCB, _cairo_xcb_surface_create_similar, _cairo_xcb_surface_finish, _cairo_xcb_surface_acquire_source_image, _cairo_xcb_surface_release_source_image, + NULL, NULL, NULL, /* dest acquire/release/clone */ - _cairo_xcb_surface_acquire_dest_image, - _cairo_xcb_surface_release_dest_image, - - _cairo_xcb_surface_clone_similar, - _cairo_xcb_surface_composite, - _cairo_xcb_surface_fill_rectangles, - _cairo_xcb_surface_composite_trapezoids, - NULL, /* create_span_renderer */ - NULL, /* check_span_renderer */ + NULL, /* composite */ + NULL, /* fill */ + NULL, /* trapezoids */ + NULL, /* span */ + NULL, /* check-span */ NULL, /* copy_page */ NULL, /* show_page */ - _cairo_xcb_surface_get_extents, - NULL, /* old_show_glyphs */ - NULL, /* get_font_options */ + NULL, /* old-glyphs */ + _cairo_xcb_surface_get_font_options, + _cairo_xcb_surface_flush, - NULL, /* mark_dirty_rectangle */ + _cairo_xcb_surface_mark_dirty, _cairo_xcb_surface_scaled_font_fini, _cairo_xcb_surface_scaled_glyph_fini, - NULL, /* paint */ - NULL, /* mask */ - NULL, /* stroke */ - NULL, /* fill */ - _cairo_xcb_surface_show_glyphs, - - _cairo_xcb_surface_snapshot, - - _cairo_xcb_surface_is_similar, + _cairo_xcb_surface_paint, + _cairo_xcb_surface_mask, + _cairo_xcb_surface_stroke, + _cairo_xcb_surface_fill, + _cairo_xcb_surface_glyphs, }; -/** - * _cairo_surface_is_xcb: - * @surface: a #cairo_surface_t - * - * Checks if a surface is a #cairo_xcb_surface_t - * - * Return value: True if the surface is an xcb surface - **/ -static cairo_bool_t -_cairo_surface_is_xcb (cairo_surface_t *surface) +#if CAIRO_HAS_DRM_SURFACE && CAIRO_HAS_XCB_DRM_FUNCTIONS +static cairo_surface_t * +_xcb_drm_create_surface_for_drawable (cairo_xcb_connection_t *connection, + cairo_xcb_screen_t *screen, + xcb_drawable_t drawable, + pixman_format_code_t pixman_format, + int width, int height) { - return surface->backend == &cairo_xcb_surface_backend; + uint32_t attachments[] = { XCB_DRI2_ATTACHMENT_BUFFER_FRONT_LEFT }; + xcb_dri2_get_buffers_reply_t *buffers; + xcb_dri2_dri2_buffer_t *buffer; + cairo_surface_t *surface; + + if (! _cairo_drm_size_is_valid (screen->device, width, height)) + return NULL; + + xcb_dri2_create_drawable (connection->xcb_connection, + drawable); + + buffers = xcb_dri2_get_buffers_reply (connection->xcb_connection, + xcb_dri2_get_buffers (connection->xcb_connection, + drawable, 1, + ARRAY_LENGTH (attachments), + attachments), + 0); + if (buffers == NULL) { + xcb_dri2_destroy_drawable (connection->xcb_connection, + drawable); + return NULL; + } + + /* If the drawable is a window, we expect to receive an extra fake front, + * which would involve copying on each flush - contrary to the user + * expectations. But that is likely to be preferable to mixing drm/xcb + * operations. + */ + buffer = xcb_dri2_get_buffers_buffers (buffers); + if (buffers->count == 1 && buffer[0].attachment == XCB_DRI2_ATTACHMENT_BUFFER_FRONT_LEFT) { + assert (buffer[0].cpp == PIXMAN_FORMAT_BPP (pixman_format) / 8); + surface = cairo_drm_surface_create_for_name (screen->device, + buffer[0].name, + _cairo_format_from_pixman_format (pixman_format), + width, height, + buffer[0].pitch); + } else { + xcb_dri2_destroy_drawable (connection->xcb_connection, + drawable); + surface = NULL; + } + free (buffers); + + return surface; } +#else + static cairo_surface_t * -_cairo_xcb_surface_create_internal (xcb_connection_t *dpy, - xcb_drawable_t drawable, - xcb_screen_t *screen, - xcb_visualtype_t *visual, - xcb_render_pictforminfo_t *xrender_format, - int width, - int height, - int depth) +_xcb_drm_create_surface_for_drawable (cairo_xcb_connection_t *connection, + cairo_xcb_screen_t *screen, + xcb_drawable_t drawable, + pixman_format_code_t pixman_format, + int width, int height) +{ + return NULL; +} + +#endif + +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; - const xcb_query_extension_reply_t *er; - const xcb_render_query_version_reply_t *r = NULL; surface = malloc (sizeof (cairo_xcb_surface_t)); - if (surface == NULL) + if (unlikely (surface == NULL)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); - if (xrender_format) { - depth = xrender_format->depth; - } else if (visual) { - xcb_depth_iterator_t depths; - xcb_visualtype_iterator_t visuals; - - /* This is ugly, but we have to walk over all visuals - * for the screen to find the depth. - */ - depths = xcb_screen_allowed_depths_iterator(screen); - for(; depths.rem; xcb_depth_next(&depths)) - { - visuals = xcb_depth_visuals_iterator(depths.data); - for(; visuals.rem; xcb_visualtype_next(&visuals)) - { - if(visuals.data->visual_id == visual->visual_id) - { - depth = depths.data->depth; - goto found; - } - } - } - found: - ; - } - - er = xcb_get_extension_data(dpy, &xcb_render_id); - if(er && er->present) { - r = xcb_render_util_query_version(dpy); - } - if (r) { - surface->render_major = r->major_version; - surface->render_minor = r->minor_version; - } else { - surface->render_major = -1; - surface->render_minor = -1; - } - - if (CAIRO_SURFACE_RENDER_HAS_CREATE_PICTURE (surface)) { - if (!xrender_format) { - if (visual) { - const xcb_render_query_pict_formats_reply_t *formats; - xcb_render_pictvisual_t *pict_visual; - formats = xcb_render_util_query_formats (dpy); - pict_visual = xcb_render_util_find_visual_format (formats, visual->visual_id); - if (pict_visual) { - xcb_render_pictforminfo_t template; - template.id = pict_visual->format; - xrender_format = xcb_render_util_find_format (formats, XCB_PICT_FORMAT_ID, &template, 0); - } - } else if (depth == 1) { - xrender_format = _CAIRO_FORMAT_TO_XRENDER_FORMAT (dpy, CAIRO_FORMAT_A1); - } - } - } else { - xrender_format = NULL; - } - _cairo_surface_init (&surface->base, - &cairo_xcb_surface_backend, - NULL, /* device */ - _xcb_render_format_to_content (xrender_format)); + &_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->dpy = dpy; + surface->fallback = NULL; - surface->gc = XCB_NONE; surface->drawable = drawable; - surface->screen = screen; - surface->owns_pixmap = FALSE; + surface->owns_pixmap = owns_pixmap; surface->use_pixmap = 0; - surface->width = width; - surface->height = height; - /* XXX: set buggy_repeat based on ServerVendor and VendorRelease */ + surface->width = width; + surface->height = height; + surface->depth = PIXMAN_FORMAT_DEPTH (pixman_format); - surface->dst_picture = XCB_NONE; - surface->src_picture = XCB_NONE; + surface->picture = XCB_NONE; - surface->visual = visual; - surface->xrender_format.id = XCB_NONE; - if (xrender_format) surface->xrender_format = *xrender_format; - surface->depth = depth; + surface->pixman_format = pixman_format; + surface->xrender_format = xrender_format; - surface->have_clip_rects = FALSE; - surface->clip_rects = NULL; - surface->num_clip_rects = 0; - surface->clip_region = NULL; + surface->flags = screen->connection->flags; - cairo_list_init (&surface->to_be_checked); - _cairo_freepool_init (&surface->cookie_pool, - sizeof (cairo_xcb_cookie_t)); + surface->marked_dirty = FALSE; + surface->drm = NULL; + if (screen->device != NULL) { + surface->drm = _xcb_drm_create_surface_for_drawable (surface->connection, + surface->screen, + drawable, + pixman_format, + width, height); + } return &surface->base; } static xcb_screen_t * -_cairo_xcb_screen_from_visual (xcb_connection_t *c, xcb_visualtype_t *visual) +_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_roots_iterator(xcb_get_setup(c)); - for (; s.rem; xcb_screen_next(&s)) - { - if (s.data->root_visual == visual->visual_id) + 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) + 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: - * @c: 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. - * - * Return value: the newly created surface - **/ cairo_surface_t * -cairo_xcb_surface_create (xcb_connection_t *c, - xcb_drawable_t drawable, - xcb_visualtype_t *visual, - int width, - int height) +cairo_xcb_surface_create (xcb_connection_t *xcb_connection, + xcb_drawable_t drawable, + xcb_visualtype_t *visual, + int width, + int height) { - xcb_screen_t *screen = _cairo_xcb_screen_from_visual (c, visual); + 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 (screen == NULL) - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_VISUAL)); + if (xcb_connection_has_error (xcb_connection)) + return _cairo_surface_create_in_error (CAIRO_STATUS_WRITE_ERROR); + + if (unlikely (width > XLIB_COORD_MAX || height > XLIB_COORD_MAX)) + return _cairo_surface_create_in_error (CAIRO_STATUS_INVALID_SIZE); + + xcb_screen = _cairo_xcb_screen_from_visual (xcb_connection, visual, &depth); + if (unlikely (xcb_screen == NULL)) + return _cairo_surface_create_in_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 > 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; - return _cairo_xcb_surface_create_internal (c, drawable, screen, - visual, NULL, - width, height, 0); + 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 (xcb_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: - * @c: an XCB connection - * @bitmap: an XCB Pixmap (a depth-1 pixmap) - * @screen: an XCB Screen - * @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: the newly created surface - **/ cairo_surface_t * -cairo_xcb_surface_create_for_bitmap (xcb_connection_t *c, - xcb_pixmap_t bitmap, - xcb_screen_t *screen, - int width, - int height) +cairo_xcb_surface_create_for_bitmap (xcb_connection_t *xcb_connection, + xcb_screen_t *xcb_screen, + xcb_pixmap_t bitmap, + int width, + int height) { - return _cairo_xcb_surface_create_internal (c, bitmap, screen, - NULL, NULL, - width, height, 1); + cairo_xcb_screen_t *screen; + + if (xcb_connection_has_error (xcb_connection)) + return _cairo_surface_create_in_error (CAIRO_STATUS_WRITE_ERROR); + + if (width > XLIB_COORD_MAX || height > XLIB_COORD_MAX) + return _cairo_surface_create_in_error (CAIRO_STATUS_INVALID_SIZE); + + screen = _cairo_xcb_screen_get (xcb_connection, xcb_screen); + if (unlikely (screen == NULL)) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + + return _cairo_xcb_surface_create_internal (screen, bitmap, FALSE, + PIXMAN_a1, + 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: - * @c: an XCB connection + * @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 @@ -2179,18 +1219,61 @@ cairo_xcb_surface_create_for_bitmap (xcb_connection_t *c, * Return value: the newly created surface. **/ cairo_surface_t * -cairo_xcb_surface_create_with_xrender_format (xcb_connection_t *c, +cairo_xcb_surface_create_with_xrender_format (xcb_connection_t *xcb_connection, + xcb_screen_t *xcb_screen, xcb_drawable_t drawable, - xcb_screen_t *screen, xcb_render_pictforminfo_t *format, int width, int height) { - return _cairo_xcb_surface_create_internal (c, drawable, screen, - NULL, format, - width, height, 0); + cairo_xcb_screen_t *screen; + cairo_format_masks_t image_masks; + pixman_format_code_t pixman_format; + + if (xcb_connection_has_error (xcb_connection)) + return _cairo_surface_create_in_error (CAIRO_STATUS_WRITE_ERROR); + + if (width > XLIB_COORD_MAX || height > XLIB_COORD_MAX) + return _cairo_surface_create_in_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)); + + screen = _cairo_xcb_screen_get (xcb_connection, xcb_screen); + if (unlikely (screen == NULL)) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + + return _cairo_xcb_surface_create_internal (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 /** * cairo_xcb_surface_set_size: @@ -2210,613 +1293,28 @@ slim_hidden_def (cairo_xcb_surface_create_with_xrender_format); **/ void cairo_xcb_surface_set_size (cairo_surface_t *abstract_surface, - int width, - int height) + int width, + int height) { - cairo_xcb_surface_t *surface = (cairo_xcb_surface_t *) abstract_surface; + cairo_xcb_surface_t *surface; cairo_status_t status_ignored; - if (! _cairo_surface_is_xcb (abstract_surface)) { + if (abstract_surface->type != CAIRO_SURFACE_TYPE_XCB) { status_ignored = _cairo_surface_set_error (abstract_surface, - CAIRO_STATUS_SURFACE_TYPE_MISMATCH); + CAIRO_STATUS_SURFACE_TYPE_MISMATCH); return; } - surface->width = width; - surface->height = height; -} - -/* - * Glyph rendering support - */ - -typedef struct _cairo_xcb_surface_font_private { - xcb_connection_t *dpy; - xcb_render_glyphset_t glyphset; - cairo_format_t format; - xcb_render_pictforminfo_t *xrender_format; -} cairo_xcb_surface_font_private_t; - -static cairo_status_t -_cairo_xcb_surface_font_init (xcb_connection_t *dpy, - cairo_scaled_font_t *scaled_font, - cairo_format_t format) -{ - cairo_xcb_surface_font_private_t *font_private; - - font_private = malloc (sizeof (cairo_xcb_surface_font_private_t)); - if (!font_private) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - font_private->dpy = dpy; - font_private->format = format; - font_private->xrender_format = _CAIRO_FORMAT_TO_XRENDER_FORMAT(dpy, format); - font_private->glyphset = xcb_generate_id(dpy); - - /* XXX checking, adding to CloseDisplay */ - xcb_render_create_glyph_set (dpy, - font_private->glyphset, - font_private->xrender_format->id); - - scaled_font->surface_private = font_private; - scaled_font->surface_backend = &cairo_xcb_surface_backend; - - return CAIRO_STATUS_SUCCESS; -} - -static void -_cairo_xcb_surface_scaled_font_fini (cairo_scaled_font_t *scaled_font) -{ - cairo_xcb_surface_font_private_t *font_private = scaled_font->surface_private; - - if (font_private) { - xcb_render_free_glyph_set (font_private->dpy, font_private->glyphset); - free (font_private); - } -} - -static void -_cairo_xcb_surface_scaled_glyph_fini (cairo_scaled_glyph_t *scaled_glyph, - cairo_scaled_font_t *scaled_font) -{ - cairo_xcb_surface_font_private_t *font_private = scaled_font->surface_private; - - if (font_private != NULL && scaled_glyph->surface_private != NULL) { - xcb_render_glyph_t glyph_index = _cairo_scaled_glyph_index(scaled_glyph); - xcb_render_free_glyphs (font_private->dpy, - font_private->glyphset, - 1, &glyph_index); - } -} - -static cairo_bool_t -_native_byte_order_lsb (void) -{ - int x = 1; - - return *((char *) &x) == 1; -} - -static cairo_status_t -_cairo_xcb_surface_add_glyph (cairo_xcb_surface_t *dst, - cairo_scaled_font_t *scaled_font, - cairo_scaled_glyph_t *scaled_glyph) -{ - xcb_render_glyphinfo_t glyph_info; - xcb_render_glyph_t glyph_index; - unsigned char *data; - cairo_status_t status = CAIRO_STATUS_SUCCESS; - cairo_xcb_surface_font_private_t *font_private; - cairo_image_surface_t *glyph_surface = scaled_glyph->surface; - xcb_void_cookie_t cookie; - - if (scaled_font->surface_private == NULL) { - status = _cairo_xcb_surface_font_init (dst->dpy, scaled_font, - glyph_surface->format); - if (status) - return status; - } - font_private = scaled_font->surface_private; - - /* If the glyph format does not match the font format, then we - * create a temporary surface for the glyph image with the font's - * format. - */ - if (glyph_surface->format != font_private->format) { - cairo_surface_pattern_t pattern; - cairo_surface_t *tmp_surface; - - tmp_surface = cairo_image_surface_create (font_private->format, - glyph_surface->width, - glyph_surface->height); - status = tmp_surface->status; - if (unlikely (status)) - goto BAIL; - - tmp_surface->device_transform = glyph_surface->base.device_transform; - tmp_surface->device_transform_inverse = glyph_surface->base.device_transform_inverse; - - _cairo_pattern_init_for_surface (&pattern, &glyph_surface->base); - status = _cairo_surface_paint (tmp_surface, - CAIRO_OPERATOR_SOURCE, &pattern.base, - NULL); - _cairo_pattern_fini (&pattern.base); - - glyph_surface = (cairo_image_surface_t *) tmp_surface; - - if (unlikely (status)) - goto BAIL; - } - - /* XXX: FRAGILE: We're ignore device_transform scaling here. A bug? */ - glyph_info.x = _cairo_lround (glyph_surface->base.device_transform.x0); - glyph_info.y = _cairo_lround (glyph_surface->base.device_transform.y0); - glyph_info.width = glyph_surface->width; - glyph_info.height = glyph_surface->height; - glyph_info.x_off = 0; - glyph_info.y_off = 0; - - data = glyph_surface->data; - - /* flip formats around */ - switch (scaled_glyph->surface->format) { - case CAIRO_FORMAT_A1: - /* local bitmaps are always stored with bit == byte */ - if (_native_byte_order_lsb() != (xcb_get_setup(dst->dpy)->bitmap_format_bit_order == XCB_IMAGE_ORDER_LSB_FIRST)) { - int c = glyph_surface->stride * glyph_surface->height; - unsigned char *d; - unsigned char *new, *n; - - new = malloc (c); - if (!new) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto BAIL; - } - n = new; - d = data; - while (c--) - { - char b = *d++; - b = ((b << 1) & 0xaa) | ((b >> 1) & 0x55); - b = ((b << 2) & 0xcc) | ((b >> 2) & 0x33); - b = ((b << 4) & 0xf0) | ((b >> 4) & 0x0f); - *n++ = b; - } - data = new; - } - break; - case CAIRO_FORMAT_A8: - break; - case CAIRO_FORMAT_ARGB32: - if (_native_byte_order_lsb() != (xcb_get_setup(dst->dpy)->image_byte_order == XCB_IMAGE_ORDER_LSB_FIRST)) { - unsigned int c = glyph_surface->stride * glyph_surface->height; - unsigned char *d; - unsigned char *new, *n; - - new = malloc (c); - if (new == NULL) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto BAIL; - } - n = new; - d = data; - while (c >= 4) - { - n[3] = d[0]; - n[2] = d[1]; - n[1] = d[2]; - n[0] = d[3]; - d += 4; - n += 4; - c -= 4; - } - data = new; - } - break; - case CAIRO_FORMAT_RGB24: - default: - ASSERT_NOT_REACHED; - break; - } - /* XXX assume X server wants pixman padding. Xft assumes this as well */ - - glyph_index = _cairo_scaled_glyph_index (scaled_glyph); - - cookie = xcb_render_add_glyphs_checked (dst->dpy, font_private->glyphset, - 1, &glyph_index, &glyph_info, - glyph_surface->stride * glyph_surface->height, - data); - - if (data != glyph_surface->data) - free (data); - - status = _cairo_xcb_add_cookie_to_be_checked (dst, cookie); - - BAIL: - if (glyph_surface != scaled_glyph->surface) - cairo_surface_destroy (&glyph_surface->base); - - return status; -} - -#define N_STACK_BUF 1024 - -static cairo_status_t -_cairo_xcb_surface_show_glyphs_8 (cairo_xcb_surface_t *dst, - cairo_operator_t op, - cairo_xcb_surface_t *src, - int src_x_offset, int src_y_offset, - const cairo_glyph_t *glyphs, - int num_glyphs, - cairo_scaled_font_t *scaled_font) -{ - cairo_xcb_surface_font_private_t *font_private = scaled_font->surface_private; - xcb_render_util_composite_text_stream_t *stream; - xcb_void_cookie_t cookie; - int i; - int thisX, thisY; - int lastX = 0, lastY = 0; - uint8_t glyph; - - stream = xcb_render_util_composite_text_stream (font_private->glyphset, num_glyphs, 0); - - for (i = 0; i < num_glyphs; ++i) { - thisX = _cairo_lround (glyphs[i].x); - thisY = _cairo_lround (glyphs[i].y); - glyph = glyphs[i].index; - xcb_render_util_glyphs_8 (stream, thisX - lastX, thisY - lastY, 1, &glyph); - lastX = thisX; - lastY = thisY; - } - - cookie = xcb_render_util_composite_text_checked (dst->dpy, - _render_operator (op), - src->src_picture, - dst->dst_picture, - font_private->xrender_format->id, - src_x_offset + _cairo_lround (glyphs[0].x), - src_y_offset + _cairo_lround (glyphs[0].y), - stream); - - xcb_render_util_composite_text_free (stream); - - return _cairo_xcb_add_cookie_to_be_checked (dst, cookie); -} - -static cairo_status_t -_cairo_xcb_surface_show_glyphs_16 (cairo_xcb_surface_t *dst, - cairo_operator_t op, - cairo_xcb_surface_t *src, - int src_x_offset, int src_y_offset, - const cairo_glyph_t *glyphs, - int num_glyphs, - cairo_scaled_font_t *scaled_font) -{ - cairo_xcb_surface_font_private_t *font_private = scaled_font->surface_private; - xcb_render_util_composite_text_stream_t *stream; - xcb_void_cookie_t cookie; - int i; - int thisX, thisY; - int lastX = 0, lastY = 0; - uint16_t glyph; - - stream = xcb_render_util_composite_text_stream (font_private->glyphset, num_glyphs, 0); - - for (i = 0; i < num_glyphs; ++i) { - thisX = _cairo_lround (glyphs[i].x); - thisY = _cairo_lround (glyphs[i].y); - glyph = glyphs[i].index; - xcb_render_util_glyphs_16 (stream, thisX - lastX, thisY - lastY, 1, &glyph); - lastX = thisX; - lastY = thisY; - } - - cookie = xcb_render_util_composite_text_checked (dst->dpy, - _render_operator (op), - src->src_picture, - dst->dst_picture, - font_private->xrender_format->id, - src_x_offset + _cairo_lround (glyphs[0].x), - src_y_offset + _cairo_lround (glyphs[0].y), - stream); - - xcb_render_util_composite_text_free (stream); - - return _cairo_xcb_add_cookie_to_be_checked (dst, cookie); -} - -static cairo_status_t -_cairo_xcb_surface_show_glyphs_32 (cairo_xcb_surface_t *dst, - cairo_operator_t op, - cairo_xcb_surface_t *src, - int src_x_offset, int src_y_offset, - const cairo_glyph_t *glyphs, - int num_glyphs, - cairo_scaled_font_t *scaled_font) -{ - cairo_xcb_surface_font_private_t *font_private = scaled_font->surface_private; - xcb_render_util_composite_text_stream_t *stream; - xcb_void_cookie_t cookie; - int i; - int thisX, thisY; - int lastX = 0, lastY = 0; - uint32_t glyph; - - stream = xcb_render_util_composite_text_stream (font_private->glyphset, num_glyphs, 0); - - for (i = 0; i < num_glyphs; ++i) { - thisX = _cairo_lround (glyphs[i].x); - thisY = _cairo_lround (glyphs[i].y); - glyph = glyphs[i].index; - xcb_render_util_glyphs_32 (stream, thisX - lastX, thisY - lastY, 1, &glyph); - lastX = thisX; - lastY = thisY; - } - - cookie = xcb_render_util_composite_text_checked (dst->dpy, - _render_operator (op), - src->src_picture, - dst->dst_picture, - font_private->xrender_format->id, - src_x_offset + _cairo_lround (glyphs[0].x), - src_y_offset + _cairo_lround (glyphs[0].y), - stream); - - xcb_render_util_composite_text_free (stream); - - return _cairo_xcb_add_cookie_to_be_checked (dst, cookie); -} - -typedef cairo_status_t (*cairo_xcb_surface_show_glyphs_func_t) - (cairo_xcb_surface_t *, cairo_operator_t, cairo_xcb_surface_t *, int, int, - const cairo_glyph_t *, int, cairo_scaled_font_t *); - -static cairo_bool_t -_cairo_xcb_surface_owns_font (cairo_xcb_surface_t *dst, - cairo_scaled_font_t *scaled_font) -{ - cairo_xcb_surface_font_private_t *font_private; - - font_private = scaled_font->surface_private; - if ((scaled_font->surface_backend != NULL && - scaled_font->surface_backend != &cairo_xcb_surface_backend) || - (font_private != NULL && font_private->dpy != dst->dpy)) - { - return FALSE; - } - - return TRUE; -} - -static cairo_status_t -_cairo_xcb_surface_emit_glyphs (cairo_xcb_surface_t *dst, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_scaled_font_t *scaled_font, - cairo_operator_t op, - cairo_xcb_surface_t *src, - cairo_surface_attributes_t *attributes, - int *remaining_glyphs) -{ - cairo_scaled_glyph_t *scaled_glyph; - int i, o; - unsigned long max_index = 0; - cairo_status_t status; - cairo_glyph_t *output_glyphs; - const cairo_glyph_t *glyphs_chunk; - int glyphs_remaining, chunk_size, max_chunk_size; - cairo_xcb_surface_show_glyphs_func_t show_glyphs_func; - - /* We make a copy of the glyphs so that we can elide any size-zero - * glyphs to workaround an X server bug, (present in at least Xorg - * 7.1 without EXA). */ - output_glyphs = _cairo_malloc_ab (num_glyphs, sizeof (cairo_glyph_t)); - if (output_glyphs == NULL) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - for (i = 0, o = 0; i < num_glyphs; i++) { - if (glyphs[i].index > max_index) - max_index = glyphs[i].index; - status = _cairo_scaled_glyph_lookup (scaled_font, - glyphs[i].index, - CAIRO_SCALED_GLYPH_INFO_SURFACE, - &scaled_glyph); - if (status) { - free (output_glyphs); - return status; - } - - /* Don't put any size-zero glyphs into output_glyphs to avoid - * an X server bug which stops rendering glyphs after the - * first size-zero glyph. */ - if (scaled_glyph->surface->width && scaled_glyph->surface->height) { - output_glyphs[o++] = glyphs[i]; - if (scaled_glyph->surface_private == NULL) { - _cairo_xcb_surface_add_glyph (dst, scaled_font, scaled_glyph); - scaled_glyph->surface_private = (void *) 1; - } - } - } - num_glyphs = o; - - status = _cairo_xcb_surface_ensure_dst_picture (dst); - if (status) { - free (output_glyphs); - return status; - } - - max_chunk_size = xcb_get_maximum_request_length (dst->dpy); - if (max_index < 256) { - /* XXX: these are all the same size! (28) */ - max_chunk_size -= sizeof(xcb_render_composite_glyphs_8_request_t); - show_glyphs_func = _cairo_xcb_surface_show_glyphs_8; - } else if (max_index < 65536) { - max_chunk_size -= sizeof(xcb_render_composite_glyphs_16_request_t); - show_glyphs_func = _cairo_xcb_surface_show_glyphs_16; - } else { - max_chunk_size -= sizeof(xcb_render_composite_glyphs_32_request_t); - show_glyphs_func = _cairo_xcb_surface_show_glyphs_32; - } - /* XXX: I think this is wrong; this is only the header size (2 longs) */ - /* but should also include the glyph (1 long) */ - /* max_chunk_size /= sz_xGlyphElt; */ - max_chunk_size /= 3*sizeof(uint32_t); - - for (glyphs_remaining = num_glyphs, glyphs_chunk = output_glyphs; - glyphs_remaining; - glyphs_remaining -= chunk_size, glyphs_chunk += chunk_size) - { - chunk_size = MIN (glyphs_remaining, max_chunk_size); - - status = show_glyphs_func (dst, op, src, - attributes->x_offset, attributes->y_offset, - glyphs_chunk, chunk_size, scaled_font); - if (status) { - free (output_glyphs); - return status; - } - } - - /* We wouldn't want to leak memory, would we? */ - free(output_glyphs); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_int_status_t -_cairo_xcb_surface_show_glyphs (void *abstract_dst, - cairo_operator_t op, - const cairo_pattern_t *src_pattern, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_scaled_font_t *scaled_font, - cairo_clip_t *clip, - int *remaining_glyphs) -{ - cairo_int_status_t status = CAIRO_STATUS_SUCCESS; - cairo_xcb_surface_t *dst = abstract_dst; - - composite_operation_t operation; - cairo_surface_attributes_t attributes; - cairo_xcb_surface_t *src = NULL; - - cairo_region_t *clip_region = NULL; - - if (!CAIRO_SURFACE_RENDER_HAS_COMPOSITE_TEXT (dst) || dst->xrender_format.id == XCB_NONE) - return CAIRO_INT_STATUS_UNSUPPORTED; - - /* Just let unbounded operators go through the fallback code - * instead of trying to do the fixups here */ - if (!_cairo_operator_bounded_by_mask (op)) - return CAIRO_INT_STATUS_UNSUPPORTED; - - /* Render <= 0.10 seems to have a bug with PictOpSrc and glyphs -- - * the solid source seems to be multiplied by the glyph mask, and - * then the entire thing is copied to the destination surface, - * including the fully transparent "background" of the rectangular - * glyph surface. */ - if (op == CAIRO_OPERATOR_SOURCE && - !CAIRO_SURFACE_RENDER_AT_LEAST(dst, 0, 11)) - return CAIRO_INT_STATUS_UNSUPPORTED; - - /* We can only use our code if we either have no clip or - * have a real native clip region set. If we're using - * fallback clip masking, we have to go through the full - * fallback path. - */ - if (clip != NULL) { - status = _cairo_clip_get_region (clip, &clip_region); - assert (status != CAIRO_INT_STATUS_NOTHING_TO_DO); - if (status) - return status; - } - - operation = _categorize_composite_operation (dst, op, src_pattern, TRUE); - if (operation == DO_UNSUPPORTED) - return CAIRO_INT_STATUS_UNSUPPORTED; - - if (! _cairo_xcb_surface_owns_font (dst, scaled_font)) - return CAIRO_INT_STATUS_UNSUPPORTED; - - /* After passing all those tests, we're now committed to rendering - * these glyphs or to fail trying. We first upload any glyphs to - * the X server that it doesn't have already, then we draw - * them. We tie into the scaled_font's glyph cache and remove - * glyphs from the X server when they are ejected from the - * scaled_font cache. - */ - - /* PictOpClear doesn't seem to work with CompositeText; it seems to ignore - * the mask (the glyphs). This code below was executed as a side effect - * of going through the _clip_and_composite fallback code for old_show_glyphs, - * so PictOpClear was never used with CompositeText before. - */ - if (op == CAIRO_OPERATOR_CLEAR) { - src_pattern = &_cairo_pattern_white.base; - op = CAIRO_OPERATOR_DEST_OUT; - } - - if (src_pattern->type == CAIRO_PATTERN_TYPE_SOLID) { - status = _cairo_pattern_acquire_surface (src_pattern, &dst->base, - 0, 0, 1, 1, - CAIRO_PATTERN_ACQUIRE_NONE, - (cairo_surface_t **) &src, - &attributes); - } else { - cairo_rectangle_int_t glyph_extents; - - status = _cairo_scaled_font_glyph_device_extents (scaled_font, - glyphs, - num_glyphs, - &glyph_extents, - NULL); - if (status) - goto BAIL; - - status = _cairo_pattern_acquire_surface (src_pattern, &dst->base, - glyph_extents.x, glyph_extents.y, - glyph_extents.width, glyph_extents.height, - CAIRO_PATTERN_ACQUIRE_NO_REFLECT, - (cairo_surface_t **) &src, - &attributes); - } - - if (status) - goto BAIL; - - operation = _recategorize_composite_operation (dst, op, src, &attributes, TRUE); - if (operation == DO_UNSUPPORTED) { - status = CAIRO_INT_STATUS_UNSUPPORTED; - goto BAIL; - } - - status = _cairo_xcb_surface_set_clip_region (dst, clip_region); - if (unlikely (status)) - goto BAIL; - - status = _cairo_xcb_surface_set_attributes (src, &attributes); - if (status) - goto BAIL; - - /* Send all unsent glyphs to the server, and count the max of the glyph indices */ - _cairo_scaled_font_freeze_cache (scaled_font); - - if (_cairo_xcb_surface_owns_font (dst, scaled_font)) { - status = _cairo_xcb_surface_emit_glyphs (dst, - glyphs, num_glyphs, - scaled_font, - op, - src, - &attributes, - remaining_glyphs); - } else { - status = CAIRO_INT_STATUS_UNSUPPORTED; + if (width > XLIB_COORD_MAX || height > XLIB_COORD_MAX) { + status_ignored = _cairo_surface_set_error (abstract_surface, + CAIRO_STATUS_INVALID_SIZE); + return; } - _cairo_scaled_font_thaw_cache (scaled_font); - - BAIL: - if (src) - _cairo_pattern_release_surface (src_pattern, &src->base, &attributes); - return status; + surface = (cairo_xcb_surface_t *) abstract_surface; + surface->width = width; + surface->height = height; } +#if CAIRO_HAS_XLIB_XCB_FUNCTIONS +slim_hidden_def (cairo_xcb_surface_set_size); +#endif diff --git a/src/cairo-xcb.h b/src/cairo-xcb.h index 1b6d2e69..a1e9da17 100644 --- a/src/cairo-xcb.h +++ b/src/cairo-xcb.h @@ -1,6 +1,7 @@ /* 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 @@ -32,6 +33,7 @@ * * Contributor(s): * Carl D. Worth <cworth@cworth.org> + * Chris Wilson <chris@chris-wilson.co.uk> */ #ifndef CAIRO_XCB_H @@ -42,28 +44,49 @@ #if CAIRO_HAS_XCB_SURFACE #include <xcb/xcb.h> +#include <xcb/render.h> CAIRO_BEGIN_DECLS cairo_public cairo_surface_t * -cairo_xcb_surface_create (xcb_connection_t *c, +cairo_xcb_surface_create (xcb_connection_t *connection, xcb_drawable_t drawable, - xcb_visualtype_t *visual, - int width, - int height); + xcb_visualtype_t *visual, + int width, + int height); cairo_public cairo_surface_t * -cairo_xcb_surface_create_for_bitmap (xcb_connection_t *c, - xcb_pixmap_t bitmap, - xcb_screen_t *screen, - int width, - int height); +cairo_xcb_surface_create_for_bitmap (xcb_connection_t *connection, + xcb_screen_t *screen, + xcb_pixmap_t bitmap, + int width, + int height); + +cairo_public 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_public void cairo_xcb_surface_set_size (cairo_surface_t *surface, int width, int height); +/* debug interface */ + +cairo_public void +cairo_xcb_device_debug_cap_xshm_version (cairo_device_t *device, + int major_version, + int minor_version); + +cairo_public void +cairo_xcb_device_debug_cap_xrender_version (cairo_device_t *device, + int major_version, + int minor_version); + CAIRO_END_DECLS #else /* CAIRO_HAS_XCB_SURFACE */ diff --git a/src/cairo-xlib-xcb-surface.c b/src/cairo-xlib-xcb-surface.c new file mode 100644 index 00000000..a8e0cde7 --- /dev/null +++ b/src/cairo-xlib-xcb-surface.c @@ -0,0 +1,515 @@ +/* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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): + * Carl D. Worth <cworth@cworth.org> + * Chris Wilson <chris@chris-wilson.co.uk> + */ + +#include "cairoint.h" + +#include "cairo-xlib.h" +#include "cairo-xcb.h" + +#include "cairo-xcb-private.h" +#include "cairo-xlib-xrender-private.h" + +#include <X11/Xlib-xcb.h> + +static cairo_surface_t * +_cairo_xlib_xcb_surface_create (void *dpy, + void *scr, + void *visual, + void *format, + cairo_surface_t *xcb); + +static cairo_surface_t * +_cairo_xlib_xcb_surface_create_similar (void *abstract_other, + cairo_content_t content, + int width, + int height) +{ + cairo_xlib_xcb_surface_t *other = abstract_other; + cairo_surface_t *xcb; + + xcb = other->xcb->base.backend->create_similar (other->xcb, content, width, height); + if (unlikely (xcb == NULL || xcb->status)) + return xcb; + + return _cairo_xlib_xcb_surface_create (other->display, other->screen, NULL, NULL, xcb); +} + +static cairo_status_t +_cairo_xlib_xcb_surface_finish (void *abstract_surface) +{ + cairo_xlib_xcb_surface_t *surface = abstract_surface; + cairo_status_t status; + + cairo_surface_finish (&surface->xcb->base); + status = surface->xcb->base.status; + cairo_surface_destroy (&surface->xcb->base); + + return status; +} + +static cairo_status_t +_cairo_xlib_xcb_surface_acquire_source_image (void *abstract_surface, + cairo_image_surface_t **image_out, + void **image_extra) +{ + cairo_xlib_xcb_surface_t *surface = abstract_surface; + return _cairo_surface_acquire_source_image (&surface->xcb->base, + image_out, image_extra); +} + +static void +_cairo_xlib_xcb_surface_release_source_image (void *abstract_surface, + cairo_image_surface_t *image_out, + void *image_extra) +{ + cairo_xlib_xcb_surface_t *surface = abstract_surface; + _cairo_surface_release_source_image (&surface->xcb->base, image_out, image_extra); +} + +static cairo_bool_t +_cairo_xlib_xcb_surface_get_extents (void *abstract_surface, + cairo_rectangle_int_t *extents) +{ + cairo_xlib_xcb_surface_t *surface = abstract_surface; + return _cairo_surface_get_extents (&surface->xcb->base, extents); +} + +static void +_cairo_xlib_xcb_surface_get_font_options (void *abstract_surface, + cairo_font_options_t *options) +{ + cairo_xlib_xcb_surface_t *surface = abstract_surface; + surface->xcb->base.backend->get_font_options (surface->xcb, options); +} + +static cairo_int_status_t +_cairo_xlib_xcb_surface_paint (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_clip_t *clip) +{ + cairo_xlib_xcb_surface_t *surface = abstract_surface; + return surface->xcb->base.backend->paint (surface->xcb, op, source, clip); +} + +static cairo_int_status_t +_cairo_xlib_xcb_surface_mask (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_pattern_t *mask, + cairo_clip_t *clip) +{ + cairo_xlib_xcb_surface_t *surface = abstract_surface; + return surface->xcb->base.backend->mask (surface->xcb, op, source, mask, clip); +} + +static cairo_int_status_t +_cairo_xlib_xcb_surface_stroke (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + 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_clip_t *clip) +{ + cairo_xlib_xcb_surface_t *surface = abstract_surface; + return surface->xcb->base.backend->stroke (surface->xcb, + op, source, path, style, + ctm, ctm_inverse, + tolerance, antialias, clip); +} + +static cairo_int_status_t +_cairo_xlib_xcb_surface_fill (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias, + cairo_clip_t *clip) +{ + cairo_xlib_xcb_surface_t *surface = abstract_surface; + return surface->xcb->base.backend->fill (surface->xcb, + op, source, path, + fill_rule, tolerance, antialias, + clip); +} + +static cairo_int_status_t +_cairo_xlib_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, + cairo_clip_t *clip, + int *num_remaining) +{ + cairo_xlib_xcb_surface_t *surface = abstract_surface; + return surface->xcb->base.backend->show_glyphs (surface->xcb, op, source, + glyphs, num_glyphs, scaled_font, + clip, num_remaining); +} + +static cairo_status_t +_cairo_xlib_xcb_surface_flush (void *abstract_surface) +{ + cairo_xlib_xcb_surface_t *surface = abstract_surface; + return surface->xcb->base.backend->flush (surface->xcb); +} + +static cairo_status_t +_cairo_xlib_xcb_surface_mark_dirty (void *abstract_surface, + int x, int y, + int width, int height) +{ + cairo_xlib_xcb_surface_t *surface = abstract_surface; + return surface->xcb->base.backend->mark_dirty_rectangle (surface->xcb, x, y, width, height); +} + +static const cairo_surface_backend_t _cairo_xlib_xcb_surface_backend = { + CAIRO_SURFACE_TYPE_XLIB, + _cairo_xlib_xcb_surface_create_similar, + _cairo_xlib_xcb_surface_finish, + _cairo_xlib_xcb_surface_acquire_source_image, + _cairo_xlib_xcb_surface_release_source_image, + NULL, NULL, NULL, /* dest acquire/release/clone */ + + NULL, /* composite */ + NULL, /* fill */ + NULL, /* trapezoids */ + NULL, /* span */ + NULL, /* check-span */ + + NULL, /* copy_page */ + NULL, /* show_page */ + _cairo_xlib_xcb_surface_get_extents, + NULL, /* old-glyphs */ + _cairo_xlib_xcb_surface_get_font_options, + + _cairo_xlib_xcb_surface_flush, + _cairo_xlib_xcb_surface_mark_dirty, + NULL, NULL, /* font/glyph fini */ + + _cairo_xlib_xcb_surface_paint, + _cairo_xlib_xcb_surface_mask, + _cairo_xlib_xcb_surface_stroke, + _cairo_xlib_xcb_surface_fill, + _cairo_xlib_xcb_surface_glyphs, +}; + +static cairo_surface_t * +_cairo_xlib_xcb_surface_create (void *dpy, + void *scr, + void *visual, + void *format, + cairo_surface_t *xcb) +{ + cairo_xlib_xcb_surface_t *surface; + + if (unlikely (xcb->status)) + return xcb; + + surface = malloc (sizeof (*surface)); + if (unlikely (surface == NULL)) { + cairo_surface_destroy (xcb); + return _cairo_surface_create_in_error (CAIRO_STATUS_NO_MEMORY); + } + + _cairo_surface_init (&surface->base, + &_cairo_xlib_xcb_surface_backend, + xcb->device, + xcb->content); + + surface->display = dpy; + surface->screen = scr; + surface->visual = visual; + surface->format = format; + surface->xcb = (cairo_xcb_surface_t *) xcb; + + return &surface->base; +} + +static Screen * +_cairo_xlib_screen_from_visual (Display *dpy, Visual *visual) +{ + int s, d, v; + + for (s = 0; s < ScreenCount (dpy); s++) { + Screen *screen; + + screen = ScreenOfDisplay (dpy, s); + if (visual == DefaultVisualOfScreen (screen)) + return screen; + + for (d = 0; d < screen->ndepths; d++) { + Depth *depth; + + depth = &screen->depths[d]; + for (v = 0; v < depth->nvisuals; v++) + if (visual == &depth->visuals[v]) + return screen; + } + } + + return NULL; +} + +cairo_surface_t * +cairo_xlib_surface_create (Display *dpy, + Drawable drawable, + Visual *visual, + int width, + int height) +{ + Screen *scr; + xcb_visualtype_t xcb_visual; + + scr = _cairo_xlib_screen_from_visual (dpy, visual); + if (scr == NULL) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_VISUAL)); + + xcb_visual.visual_id = visual->visualid; +#if defined(__cplusplus) || defined(c_plusplus) + xcb_visual._class = visual->c_class; +#else + xcb_visual._class = visual->class; +#endif + xcb_visual.bits_per_rgb_value = visual->bits_per_rgb; + xcb_visual.colormap_entries = visual->map_entries; + xcb_visual.red_mask = visual->red_mask; + xcb_visual.green_mask = visual->green_mask; + xcb_visual.blue_mask = visual->blue_mask; + + return _cairo_xlib_xcb_surface_create (dpy, scr, visual, NULL, + cairo_xcb_surface_create (XGetXCBConnection (dpy), + drawable, + &xcb_visual, + width, height)); +} + +cairo_surface_t * +cairo_xlib_surface_create_for_bitmap (Display *dpy, + Pixmap bitmap, + Screen *scr, + int width, + int height) +{ + return _cairo_xlib_xcb_surface_create (dpy, scr, NULL, NULL, + cairo_xcb_surface_create_for_bitmap (XGetXCBConnection (dpy), + (xcb_screen_t *) scr, + bitmap, + width, height)); +} + +#if CAIRO_HAS_XLIB_XRENDER_SURFACE +cairo_surface_t * +cairo_xlib_surface_create_with_xrender_format (Display *dpy, + Drawable drawable, + Screen *scr, + XRenderPictFormat *format, + int width, + int height) +{ + xcb_render_pictforminfo_t xcb_format; + + xcb_format.id = format->id; + xcb_format.type = format->type; + xcb_format.depth = format->depth; + xcb_format.direct.red_shift = format->direct.red; + xcb_format.direct.red_mask = format->direct.redMask; + xcb_format.direct.green_shift = format->direct.green; + xcb_format.direct.green_mask = format->direct.greenMask; + xcb_format.direct.blue_shift = format->direct.blue; + xcb_format.direct.blue_mask = format->direct.blueMask; + xcb_format.direct.alpha_shift = format->direct.alpha; + xcb_format.direct.alpha_mask = format->direct.alphaMask; + xcb_format.colormap = format->colormap; + + return _cairo_xlib_xcb_surface_create (dpy, scr, NULL, format, + cairo_xcb_surface_create_with_xrender_format (XGetXCBConnection (dpy), + (xcb_screen_t *) scr, + drawable, + &xcb_format, + width, height)); +} + +XRenderPictFormat * +cairo_xlib_surface_get_xrender_format (cairo_surface_t *surface) +{ + cairo_xlib_xcb_surface_t *xlib_surface = (cairo_xlib_xcb_surface_t *) surface; + + /* Throw an error for a non-xlib surface */ + if (surface->type != CAIRO_SURFACE_TYPE_XLIB) { + _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); + return NULL; + } + + return xlib_surface->format; +} +#endif + +void +cairo_xlib_surface_set_size (cairo_surface_t *abstract_surface, + int width, + int height) +{ + cairo_xlib_xcb_surface_t *surface = (cairo_xlib_xcb_surface_t *) abstract_surface; + cairo_status_t status; + + if (surface->base.type != CAIRO_SURFACE_TYPE_XLIB) { + status = _cairo_surface_set_error (abstract_surface, + CAIRO_STATUS_SURFACE_TYPE_MISMATCH); + return; + } + + cairo_xcb_surface_set_size (&surface->xcb->base, width, height); +} + +void +cairo_xlib_surface_set_drawable (cairo_surface_t *abstract_surface, + Drawable drawable, + int width, + int height) +{ + cairo_xlib_xcb_surface_t *surface = (cairo_xlib_xcb_surface_t *)abstract_surface; + cairo_status_t status; + + if (surface->base.type != CAIRO_SURFACE_TYPE_XLIB) { + status = _cairo_surface_set_error (abstract_surface, + CAIRO_STATUS_SURFACE_TYPE_MISMATCH); + return; + } + + ASSERT_NOT_REACHED; +} + +Display * +cairo_xlib_surface_get_display (cairo_surface_t *abstract_surface) +{ + cairo_xlib_xcb_surface_t *surface = (cairo_xlib_xcb_surface_t *) abstract_surface; + + if (surface->base.type != CAIRO_SURFACE_TYPE_XLIB) { + _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); + return NULL; + } + + return surface->display; +} + +Drawable +cairo_xlib_surface_get_drawable (cairo_surface_t *abstract_surface) +{ + cairo_xlib_xcb_surface_t *surface = (cairo_xlib_xcb_surface_t *) abstract_surface; + + if (surface->base.type != CAIRO_SURFACE_TYPE_XLIB) { + _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); + return 0; + } + + return surface->xcb->drawable; +} + +Screen * +cairo_xlib_surface_get_screen (cairo_surface_t *abstract_surface) +{ + cairo_xlib_xcb_surface_t *surface = (cairo_xlib_xcb_surface_t *) abstract_surface; + + if (surface->base.type != CAIRO_SURFACE_TYPE_XLIB) { + _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); + return NULL; + } + + return surface->screen; +} + +Visual * +cairo_xlib_surface_get_visual (cairo_surface_t *abstract_surface) +{ + cairo_xlib_xcb_surface_t *surface = (cairo_xlib_xcb_surface_t *) abstract_surface; + + if (surface->base.type != CAIRO_SURFACE_TYPE_XLIB) { + _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); + return NULL; + } + + return surface->visual; +} + +int +cairo_xlib_surface_get_depth (cairo_surface_t *abstract_surface) +{ + cairo_xlib_xcb_surface_t *surface = (cairo_xlib_xcb_surface_t *) abstract_surface; + + if (surface->base.type != CAIRO_SURFACE_TYPE_XLIB) { + _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); + return 0; + } + + return surface->xcb->depth; +} + +int +cairo_xlib_surface_get_width (cairo_surface_t *abstract_surface) +{ + cairo_xlib_xcb_surface_t *surface = (cairo_xlib_xcb_surface_t *) abstract_surface; + + if (surface->base.type != CAIRO_SURFACE_TYPE_XLIB) { + _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); + return 0; + } + + return surface->xcb->width; +} + +int +cairo_xlib_surface_get_height (cairo_surface_t *abstract_surface) +{ + cairo_xlib_xcb_surface_t *surface = (cairo_xlib_xcb_surface_t *) abstract_surface; + + if (surface->base.type != CAIRO_SURFACE_TYPE_XLIB) { + _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); + return 0; + } + + return surface->xcb->height; +} diff --git a/src/cairoint.h b/src/cairoint.h index 0892bbbd..acc5769e 100644 --- a/src/cairoint.h +++ b/src/cairoint.h @@ -2555,9 +2555,25 @@ cairo_private unsigned long _cairo_pattern_hash (const cairo_pattern_t *pattern); cairo_private unsigned long +_cairo_linear_pattern_hash (unsigned long hash, + const cairo_linear_pattern_t *linear); + +cairo_private unsigned long +_cairo_radial_pattern_hash (unsigned long hash, + const cairo_radial_pattern_t *radial); + +cairo_private cairo_bool_t +_cairo_linear_pattern_equal (const cairo_linear_pattern_t *a, + const cairo_linear_pattern_t *b); + +cairo_private unsigned long _cairo_pattern_size (const cairo_pattern_t *pattern); cairo_private cairo_bool_t +_cairo_radial_pattern_equal (const cairo_radial_pattern_t *a, + const cairo_radial_pattern_t *b); + +cairo_private cairo_bool_t _cairo_pattern_equal (const cairo_pattern_t *a, const cairo_pattern_t *b); diff --git a/test/xcb-surface-source.c b/test/xcb-surface-source.c index debe93e3..d359cf2c 100644 --- a/test/xcb-surface-source.c +++ b/test/xcb-surface-source.c @@ -26,7 +26,6 @@ #include "cairo-test.h" #if CAIRO_HAS_XCB_SURFACE #include <cairo-xcb.h> -#include <cairo-xcb-xrender.h> #endif #include "surface-source.c" |