| /* 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., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA |
| * You should have received a copy of the MPL along with this library |
| * in the file COPYING-MPL-1.1 |
| * |
| * The contents of this file are subject to the Mozilla Public License |
| * Version 1.1 (the "License"); you may not use this file except in |
| * compliance with the License. You may obtain a copy of the License at |
| * http://www.mozilla.org/MPL/ |
| * |
| * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY |
| * OF ANY KIND, either express or implied. See the LGPL or the MPL for |
| * the specific language governing rights and limitations. |
| * |
| * 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 */ |
| |
| /** |
| * SECTION:cairo-xcb-xrender |
| * @Title: XCB Surfaces |
| * @Short_Description: X Window System rendering using the XCB library and the X Render extension |
| * @See_Also: #cairo_surface_t |
| * |
| * The XCB surface is used to render cairo graphics to X Window System |
| * windows and pixmaps using the XCB library and its X Render extension. |
| * |
| * Note that the XCB surface automatically takes advantage of the X Render |
| * extension if it is available. |
| */ |
| |
| typedef struct _cairo_xcb_picture { |
| cairo_surface_t base; |
| |
| 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 inline cairo_xcb_connection_t * |
| _picture_to_connection (cairo_xcb_picture_t *picture) |
| { |
| return (cairo_xcb_connection_t *) picture->base.device; |
| } |
| |
| 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_xcb_connection_t *connection = _picture_to_connection (surface); |
| cairo_status_t status; |
| |
| status = _cairo_xcb_connection_acquire (connection); |
| if (unlikely (status)) |
| return status; |
| |
| _cairo_xcb_connection_render_free_picture (connection, surface->picture); |
| |
| _cairo_xcb_connection_release (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, |
| &screen->connection->device, |
| _cairo_content_from_pixman_format (pixman_format)); |
| |
| surface->screen = screen; |
| 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 inline cairo_bool_t |
| _operator_is_supported (uint32_t flags, cairo_operator_t op) |
| { |
| if (op <= CAIRO_OPERATOR_SATURATE) |
| return TRUE; |
| |
| /* TODO Support pdf operators via CAIRO_XCB_RENDER_HAS_PDF_OPERATORS */ |
| return FALSE; |
| } |
| |
| 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) |
| { |
| assert (surface->fallback == NULL); |
| 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) { |
| _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 pattern->type != CAIRO_PATTERN_TYPE_MESH; |
| } |
| |
| 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) |
| { |
| xcb_render_transform_t transform; |
| pixman_transform_t *pixman_transform; |
| cairo_status_t ignored; |
| |
| /* Casting between pixman_transform_t and xcb_render_transform_t is safe |
| * because they happen to be the exact same type. |
| */ |
| pixman_transform = (pixman_transform_t *) &transform; |
| |
| picture->x = picture->x0; |
| picture->y = picture->y0; |
| ignored = _cairo_matrix_to_pixman_matrix_offset (matrix, filter, xc, yc, |
| pixman_transform, |
| &picture->x, &picture->y); |
| |
| if (memcmp (&picture->transform, &transform, sizeof (xcb_render_transform_t))) { |
| _cairo_xcb_connection_render_set_picture_transform (_picture_to_connection (picture), |
| picture->picture, |
| &transform); |
| |
| picture->transform = transform; |
| } |
| } |
| |
| 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_to_connection (picture), |
| 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_to_connection (picture), |
| 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_to_connection (picture), |
| 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_to_connection (picture), |
| 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_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]; |
| xcb_render_fixed_t *stops; |
| xcb_render_color_t *colors; |
| xcb_render_pointfix_t p1, p2; |
| cairo_matrix_t matrix; |
| cairo_circle_double_t extremes[2]; |
| cairo_xcb_picture_t *picture; |
| cairo_status_t status; |
| |
| _cairo_gradient_pattern_fit_to_range (&pattern->base, PIXMAN_MAX_INT >> 1, &matrix, extremes); |
| |
| 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; |
| |
| colors = (xcb_render_color_t *) (stops + pattern->base.n_stops); |
| |
| p1.x = _cairo_fixed_16_16_from_double (extremes[0].center.x); |
| p1.y = _cairo_fixed_16_16_from_double (extremes[0].center.y); |
| p2.x = _cairo_fixed_16_16_from_double (extremes[1].center.x); |
| p2.y = _cairo_fixed_16_16_from_double (extremes[1].center.y); |
| |
| _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 p1, p2; |
| xcb_render_fixed_t r1, r2; |
| cairo_matrix_t matrix; |
| cairo_circle_double_t extremes[2]; |
| cairo_xcb_picture_t *picture; |
| cairo_status_t status; |
| |
| _cairo_gradient_pattern_fit_to_range (&pattern->base, PIXMAN_MAX_INT >> 1, &matrix, extremes); |
| |
| 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; |
| |
| colors = (xcb_render_color_t *) (stops + pattern->base.n_stops); |
| |
| p1.x = _cairo_fixed_16_16_from_double (extremes[0].center.x); |
| p1.y = _cairo_fixed_16_16_from_double (extremes[0].center.y); |
| p2.x = _cairo_fixed_16_16_from_double (extremes[1].center.x); |
| p2.y = _cairo_fixed_16_16_from_double (extremes[1].center.y); |
| |
| r1 = _cairo_fixed_16_16_from_double (extremes[0].radius); |
| r2 = _cairo_fixed_16_16_from_double (extremes[1].radius); |
| |
| _cairo_xcb_connection_render_create_radial_gradient (target->connection, |
| picture->picture, |
| p1, p2, 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, &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 * |
| _copy_to_picture (cairo_xcb_surface_t *source, |
| cairo_bool_t force) |
| { |
| cairo_xcb_picture_t *picture; |
| uint32_t values[] = { 0, 1 }; |
| |
| if (source->deferred_clear) { |
| cairo_status_t status = _cairo_xcb_surface_clear (source); |
| if (unlikely (status)) |
| return (cairo_xcb_picture_t *) _cairo_surface_create_in_error (status); |
| } |
| |
| /* XXX two level device locking, ensure we release the xcb device mutex? */ |
| if (source->drm != NULL) |
| cairo_surface_flush (source->drm); |
| |
| 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 = pattern->surface; |
| cairo_xcb_picture_t *picture; |
| cairo_filter_t filter; |
| |
| { |
| cairo_xcb_surface_t *snapshot; |
| |
| snapshot = (cairo_xcb_surface_t *) |
| _cairo_surface_has_snapshot (source, &_cairo_xcb_surface_backend); |
| if (snapshot != NULL) { |
| if (snapshot->screen == target->screen) |
| source = &snapshot->base; |
| } |
| } |
| |
| 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; |
| } |
| picture = NULL; |
| } |
| |
| if (source->type == CAIRO_SURFACE_TYPE_XCB && ((cairo_xcb_surface_t *) source)->fallback == NULL) |
| { |
| 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_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 && ((cairo_xlib_xcb_surface_t *) source)->xcb->fallback == NULL) |
| { |
| 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_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; |
| xcb_render_pictformat_t render_format; |
| |
| /* XXX XRenderPutImage! */ |
| |
| conv = _cairo_image_surface_coerce (image); |
| _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[conv->format]; |
| picture = _picture_from_image (target, render_format, conv, NULL); |
| cairo_surface_destroy (&conv->base); |
| } |
| |
| if (unlikely (picture->base.status)) |
| return picture; |
| } |
| |
| _cairo_surface_attach_snapshot (source, |
| &picture->base, |
| NULL); |
| |
| 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.); |
| |
| |
| _cairo_xcb_picture_set_extend (picture, pattern->base.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); |
| case CAIRO_PATTERN_TYPE_MESH: |
| 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_down (chunk->base[i].p1.x); |
| int y1 = _cairo_fixed_integer_round_down (chunk->base[i].p1.y); |
| int x2 = _cairo_fixed_integer_round_down (chunk->base[i].p2.x); |
| int y2 = _cairo_fixed_integer_round_down (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; |
| } |
| |
| /* pixel aligned, non-overlapping boxes */ |
| 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; |
| xcb_rectangle_t stack_boxes[CAIRO_STACK_ARRAY_LENGTH (xcb_rectangle_t)]; |
| xcb_rectangle_t *clip_boxes; |
| cairo_rectangle_int_t stack_extents; |
| cairo_status_t status; |
| int num_boxes; |
| int render_op; |
| |
| render_op = _render_operator (op); |
| |
| if (src_pattern == NULL) { |
| src_pattern = mask_pattern; |
| mask_pattern = NULL; |
| } |
| |
| /* amalgamate into a single Composite call by setting a clip region */ |
| clip_boxes = stack_boxes; |
| if (boxes->num_boxes > ARRAY_LENGTH (stack_boxes)) { |
| clip_boxes = _cairo_malloc_ab (boxes->num_boxes, sizeof (xcb_rectangle_t)); |
| if (unlikely (clip_boxes == NULL)) |
| return _cairo_error (CAIRO_STATUS_NO_MEMORY); |
| } |
| |
| src = _cairo_xcb_picture_for_pattern (dst, src_pattern, extents); |
| status = src->base.status; |
| if (unlikely (status)) |
| goto cleanup_boxes; |
| |
| num_boxes = 0; |
| 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_down (box[i].p1.x); |
| int y = _cairo_fixed_integer_round_down (box[i].p1.y); |
| int width = _cairo_fixed_integer_round_down (box[i].p2.x) - x; |
| int height = _cairo_fixed_integer_round_down (box[i].p2.y) - y; |
| |
| if (width && height) { |
| clip_boxes[num_boxes].x = x; |
| clip_boxes[num_boxes].y = y; |
| clip_boxes[num_boxes].width = width; |
| clip_boxes[num_boxes].height = height; |
| num_boxes++; |
| } |
| } |
| } |
| |
| if (num_boxes) { |
| if (num_boxes > 1) { |
| _cairo_xcb_connection_render_set_picture_clip_rectangles (dst->connection, |
| dst->picture, |
| 0, 0, |
| num_boxes, |
| clip_boxes); |
| } else { |
| stack_extents.x = clip_boxes[0].x; |
| stack_extents.y = clip_boxes[0].y; |
| stack_extents.width = clip_boxes[0].width; |
| stack_extents.height = clip_boxes[0].height; |
| extents = &stack_extents; |
| } |
| |
| if (mask_pattern != NULL) { |
| mask = _cairo_xcb_picture_for_pattern (dst, mask_pattern, extents); |
| status = mask->base.status; |
| if (unlikely (status)) |
| goto cleanup_clip; |
| |
| _cairo_xcb_connection_render_composite (dst->connection, |
| render_op, |
| src->picture, |
| mask->picture, |
| dst->picture, |
| src->x + extents->x, src->y + extents->y, |
| mask->x + extents->x, mask->y + extents->y, |
| extents->x, extents->y, |
| extents->width, extents->height); |
| |
| cairo_surface_destroy (&mask->base); |
| } else { |
| _cairo_xcb_connection_render_composite (dst->connection, |
| render_op, |
| src->picture, |
| XCB_NONE, |
| dst->picture, |
| src->x + extents->x, src->y + extents->y, |
| 0, 0, |
| extents->x, extents->y, |
| extents->width, extents->height); |
| } |
| |
| cleanup_clip: |
| |
| if (num_boxes > 1) |
| _cairo_xcb_surface_clear_clip_region (dst); |
| } |
| |
| cairo_surface_destroy (&src->base); |
| |
| cleanup_boxes: |
| |
| if (clip_boxes != stack_boxes) |
| free (clip_boxes); |
| |
| return status; |
| } |
| |
| |
| #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_clear (surface); |
| 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; |
| int clip_x, clip_y; |
| 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, &clip_x, &clip_y); |
| 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_x, |
| extents->y - clip_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_x, |
| extents->y - clip_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; |
| if (rects->bounded.width == 0 || rects->bounded.height == 0) { |
| xrects[n].x = rects->unbounded.x; |
| xrects[n].width = rects->unbounded.width; |
| xrects[n].y = rects->unbounded.y; |
| xrects[n].height = rects->unbounded.height; |
| n++; |
| } else { |
| /* 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, &mask_x, &mask_y); |
| if (unlikely (mask->base.status)) |
| return mask->base.status; |
| |
| /* 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, |
| &clear); |
| } |
| |
| _cairo_boxes_fini (&clear); |
| |
| return status; |
| } |
| |
| cairo_status_t |
| _cairo_xcb_surface_clear (cairo_xcb_surface_t *dst) |
| { |
| xcb_gcontext_t gc; |
| xcb_rectangle_t rect; |
| cairo_status_t status; |
| |
| status = _cairo_xcb_connection_acquire (dst->connection); |
| if (unlikely (status)) |
| return status; |
| |
| rect.x = rect.y = 0; |
| rect.width = dst->width; |
| rect.height = dst->height; |
| |
| if (dst->flags & CAIRO_XCB_RENDER_HAS_COMPOSITE) { |
| xcb_render_color_t transparent = { 0 }; |
| |
| _cairo_xcb_surface_ensure_picture (dst); |
| _cairo_xcb_connection_render_fill_rectangles (dst->connection, |
| XCB_RENDER_PICT_OP_CLEAR, |
| dst->picture, |
| transparent, |
| 1, &rect); |
| } else { |
| gc = _cairo_xcb_screen_get_gc (dst->screen, dst->drawable, dst->depth); |
| |
| _cairo_xcb_connection_poly_fill_rectangle (dst->connection, |
| dst->drawable, gc, |
| 1, &rect); |
| |
| _cairo_xcb_screen_put_gc (dst->screen, dst->depth, gc); |
| } |
| |
| _cairo_xcb_connection_release (dst->connection); |
| |
| dst->deferred_clear = FALSE; |
| return CAIRO_STATUS_SUCCESS; |
| } |
| |
| 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, |
| cairo_composite_rectangles_t*extents, |
| cairo_clip_t *clip) |
| { |
| cairo_status_t status; |
| cairo_region_t *clip_region = NULL; |
| cairo_region_t extents_region; |
| 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_rectangle_int_t rect; |
| cairo_bool_t is_empty; |
| |
| cairo_region_get_extents (clip_region, &rect); |
| is_empty = ! _cairo_rectangle_intersect (&extents->unbounded, &rect); |
| if (unlikely (is_empty)) |
| return CAIRO_STATUS_SUCCESS; |
| |
| is_empty = ! _cairo_rectangle_intersect (&extents->bounded, &rect); |
| if (unlikely (is_empty && extents->is_bounded)) |
| return CAIRO_STATUS_SUCCESS; |
| } |
| } else if (!extents->is_bounded) { |
| /* The X server will estimate the affected region of the unbounded |
| * operation and will apply the operation to that rectangle. |
| * However, there are cases where this estimate is too high (e.g. |
| * the test suite's clip-fill-{eo,nz}-unbounded tests). |
| */ |
| _cairo_region_init_rectangle (&extents_region, &extents->unbounded); |
| clip_region = &extents_region; |
| } |
| |
| status = _cairo_xcb_connection_acquire (dst->connection); |
| if (unlikely (status)) |
| return status; |
| |
| if (dst->deferred_clear) { |
| status = _cairo_xcb_surface_clear (dst); |
| 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); |
| if (clip_region == &extents_region) |
| _cairo_region_fini (&extents_region); |
| |
| _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 || |
| status == 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 (dst->base.is_clear || _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 || |
| status == 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; |
| |
| _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; |
| |
| status = _render_fill_boxes (dst, op, color, boxes); |
| } |
| else |
| { |
| cairo_surface_pattern_t mask; |
| |
| if (need_clip_mask) { |
| cairo_surface_t *clip_surface; |
| int clip_x, clip_y; |
| |
| clip_surface = _cairo_clip_get_surface (clip, &dst->base, &clip_x, &clip_y); |
| 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_x, |
| -clip_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 (need_clip_mask) |
| _cairo_pattern_fini (&mask.base); |
| } |
| |
| 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 |
| _upload_image_inplace (cairo_xcb_surface_t *surface, |
| const cairo_pattern_t *source, |
| const cairo_rectangle_int_t *extents) |
| { |
| const cairo_surface_pattern_t *pattern; |
| cairo_image_surface_t *image; |
| xcb_gcontext_t gc; |
| cairo_status_t status; |
| int tx, ty; |
| int len, bpp; |
| |
| if (source->type != CAIRO_PATTERN_TYPE_SURFACE) |
| return CAIRO_INT_STATUS_UNSUPPORTED; |
| |
| pattern = (const cairo_surface_pattern_t *) source; |
| if (pattern->surface->type != CAIRO_SURFACE_TYPE_IMAGE) |
| return CAIRO_INT_STATUS_UNSUPPORTED; |
| |
| { |
| cairo_xcb_surface_t *snapshot; |
| |
| snapshot = (cairo_xcb_surface_t *) |
| _cairo_surface_has_snapshot (pattern->surface, &_cairo_xcb_surface_backend); |
| if (snapshot != NULL) { |
| if (snapshot->screen == surface->screen) |
| return CAIRO_INT_STATUS_UNSUPPORTED; |
| } |
| } |
| |
| { |
| cairo_xcb_picture_t *snapshot; |
| |
| snapshot = (cairo_xcb_picture_t *) |
| _cairo_surface_has_snapshot (pattern->surface, &_cairo_xcb_picture_backend); |
| if (snapshot != NULL) { |
| if (snapshot->screen == surface->screen) |
| return CAIRO_INT_STATUS_UNSUPPORTED; |
| } |
| } |
| |
| image = (cairo_image_surface_t *) pattern->surface; |
| if (image->format == CAIRO_FORMAT_INVALID) |
| return CAIRO_INT_STATUS_UNSUPPORTED; |
| |
| if (image->depth != surface->depth) |
| return CAIRO_INT_STATUS_UNSUPPORTED; |
| |
| if (! _cairo_matrix_is_integer_translation (&source->matrix, &tx, &ty)) |
| return CAIRO_INT_STATUS_UNSUPPORTED; |
| |
| image = (cairo_image_surface_t *) pattern->surface; |
| if (source->extend != CAIRO_EXTEND_NONE && |
| (extents->x + tx < 0 || |
| extents->y + ty < 0 || |
| extents->x + tx + extents->width > image->width || |
| extents->y + ty + extents->height > image->height)) |
| { |
| return CAIRO_INT_STATUS_UNSUPPORTED; |
| } |
| |
| status = _cairo_xcb_connection_acquire (surface->connection); |
| if (unlikely (status)) |
| return status; |
| |
| gc = _cairo_xcb_screen_get_gc (surface->screen, surface->drawable, image->depth); |
| |
| /* Do we need to trim the image? */ |
| bpp = PIXMAN_FORMAT_BPP (image->pixman_format); |
| len = CAIRO_STRIDE_FOR_WIDTH_BPP (extents->width, bpp); |
| if (len == image->stride) { |
| _cairo_xcb_connection_put_image (surface->connection, |
| surface->drawable, gc, |
| extents->width, extents->height, |
| extents->x, extents->y, |
| image->depth, |
| image->stride, |
| image->data + |
| (extents->y + ty) * image->stride + |
| (extents->x + tx) * bpp/8); |
| } else { |
| _cairo_xcb_connection_put_subimage (surface->connection, |
| surface->drawable, gc, |
| extents->x + tx, extents->y + ty, |
| extents->width, extents->height, |
| bpp / 8, |
| image->stride, |
| extents->x, extents->y, |
| image->depth, |
| image->data); |
| |
| } |
| |
| _cairo_xcb_screen_put_gc (surface->screen, image->depth, gc); |
| _cairo_xcb_connection_release (surface->connection); |
| |
| if (surface->width == image->width && surface->height == image->height && |
| extents->width == image->width && extents->height == image->height) |
| { |
| _cairo_surface_attach_snapshot (&image->base, &surface->base, NULL); |
| } |
| |
| return CAIRO_STATUS_SUCCESS; |
| } |
| |
| 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, |
| 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 (clip == NULL && |
| (op == CAIRO_OPERATOR_SOURCE || (op == CAIRO_OPERATOR_OVER && dst->base.is_clear)) && |
| boxes->num_boxes == 1 && |
| extents->bounded.width == dst->width && |
| extents->bounded.height == dst->height) |
| { |
| op = CAIRO_OPERATOR_SOURCE; |
| dst->deferred_clear = FALSE; |
| |
| status = _upload_image_inplace (dst, src, &extents->bounded); |
| if (status != CAIRO_INT_STATUS_UNSUPPORTED) |
| return status; |
| } |
| |
| if (dst->deferred_clear) { |
| status = _cairo_xcb_surface_clear (dst); |
| if (unlikely (status)) { |
| return status; |
| } |
| |
| if (op == CAIRO_OPERATOR_OVER) |
| op = CAIRO_OPERATOR_SOURCE; |
| } |
| |
| if (clip == NULL && op == CAIRO_OPERATOR_SOURCE && boxes->num_boxes == 1) { |
| status = _upload_image_inplace (dst, src, &extents->bounded); |
| if (status != CAIRO_INT_STATUS_UNSUPPORTED) |
| return status; |
| } |
| |
| if ((dst->flags & CAIRO_XCB_RENDER_HAS_COMPOSITE) == 0) |
| return _core_boxes (dst, op, src, boxes, antialias, clip, extents); |
| |
| if (dst->deferred_clear) { |
| status = _cairo_xcb_surface_clear (dst); |
| if (unlikely (status)) |
| return status; |
| } |
| |
| /* 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; |
| status = _clip_and_composite (dst, op, src, |
| _composite_traps, &info, |
| extents, clip); |
| |
| _cairo_traps_fini (&info.traps); |
| return status; |
| } |
| |
| static cairo_bool_t |
| _mono_edge_is_vertical (const cairo_line_t *line) |
| { |
| return _cairo_fixed_integer_round_down (line->p1.x) == _cairo_fixed_integer_round_down (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, |
| cairo_antialias_t antialias) |
| { |
| 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; |
| |
| if (antialias != CAIRO_ANTIALIAS_NONE) { |
| for (i = 0; i < traps->num_traps; i++) { |
| /* Note the traps and boxes alias so we need to take the local copies first. */ |
| 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); |
| } |
| } |
| } else { |
| boxes->is_pixel_aligned = TRUE; |
| |
| for (i = 0; i < traps->num_traps; i++) { |
| /* Note the traps and boxes alias so we need to take the local copies first. */ |
| 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; |
| |
| /* round down here to match Pixman's behavior when using traps. */ |
| boxes->chunks.base[i].p1.x = _cairo_fixed_round_down (x1); |
| boxes->chunks.base[i].p1.y = _cairo_fixed_round_down (y1); |
| boxes->chunks.base[i].p2.x = _cairo_fixed_round_down (x2); |
| boxes->chunks.base[i].p2.y = _cairo_fixed_round_down (y2); |
| } |
| } |
| } |
| |
| 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_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_TRAPEZOIDS | |
| CAIRO_XCB_RENDER_HAS_COMPOSITE)) == 0) |
| { |
| return CAIRO_INT_STATUS_UNSUPPORTED; |
| } |
| |
| if (op == CAIRO_OPERATOR_CLEAR && clip == NULL) { |
| surface->deferred_clear = TRUE; |
| return CAIRO_STATUS_SUCCESS; |
| } |
| |
| status = _cairo_composite_rectangles_init_for_paint (&extents, |
| surface->width, |
| surface->height, |
| op, source, |
| clip); |
| if (unlikely (status)) |
| return status; |
| |
| if (_cairo_clip_contains_extents (clip, &extents)) |
| clip = NULL; |
| |
| if (clip != NULL) { |
| clip = _cairo_clip_init_copy (&local_clip, clip); |
| have_clip = TRUE; |
| } |
| |
| status = _cairo_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_extents (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; |
| } |
| |
| 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; |
| |
| _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, antialias); |
| 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, 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, |
| 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_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_extents (clip, &extents)) |
| clip = NULL; |
| |
| if (clip != NULL) { |
| clip = _cairo_clip_init_copy (&local_clip, clip); |
| have_clip = TRUE; |
| } |
| |
| status = _cairo_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_stroke_is_rectilinear (path)) { |
| 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_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, 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, |
| 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_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_extents (clip, &extents)) |
| clip = NULL; |
| |
| if (clip != NULL) { |
| clip = _cairo_clip_init_copy (&local_clip, clip); |
| have_clip = TRUE; |
| } |
| |
| status = _cairo_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_fill_is_rectilinear (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_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, |
| 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; |
| cairo_bool_t use_mask; |
| } composite_glyphs_info_t; |
| |
| static cairo_status_t |
| _can_composite_glyphs (cairo_xcb_surface_t *dst, |
| cairo_rectangle_int_t *extents, |
| cairo_scaled_font_t *scaled_font, |
| cairo_glyph_t *glyphs, |
| int *num_glyphs) |
| { |
| #define GLYPH_CACHE_SIZE 64 |
| cairo_box_t bbox_cache[GLYPH_CACHE_SIZE]; |
| unsigned long glyph_cache[GLYPH_CACHE_SIZE]; |
| #undef GLYPH_CACHE_SIZE |
| cairo_status_t status = CAIRO_STATUS_SUCCESS; |
| cairo_glyph_t *glyphs_end, *valid_glyphs; |
| const int max_glyph_size = dst->connection->maximum_request_length - 64; |
| |
| /* We must initialize the cache with values that cannot match the |
| * "hash" to guarantee that when compared for the first time they |
| * will result in a mismatch. The hash function is simply modulus, |
| * so we cannot use 0 in glyph_cache[0], but we can use it in all |
| * other array cells. |
| */ |
| memset (glyph_cache, 0, sizeof (glyph_cache)); |
| glyph_cache[0] = 1; |
| |
| /* Scan for oversized glyphs or glyphs outside the representable |
| * range and fallback in that case, discard glyphs outside of the |
| * image. |
| */ |
| valid_glyphs = glyphs; |
| for (glyphs_end = glyphs + *num_glyphs; glyphs != glyphs_end; glyphs++) { |
| double x1, y1, x2, y2; |
| cairo_scaled_glyph_t *scaled_glyph; |
| cairo_box_t *bbox; |
| int width, height, len; |
| int g; |
| |
| g = glyphs->index % ARRAY_LENGTH (glyph_cache); |
| if (glyph_cache[g] != glyphs->index) { |
| status = _cairo_scaled_glyph_lookup (scaled_font, |
| glyphs->index, |
| CAIRO_SCALED_GLYPH_INFO_METRICS, |
| &scaled_glyph); |
| if (unlikely (status)) |
| break; |
| |
| glyph_cache[g] = glyphs->index; |
| bbox_cache[g] = scaled_glyph->bbox; |
| } |
| bbox = &bbox_cache[g]; |
| |
| /* Drop glyphs outside the clipping */ |
| x1 = _cairo_fixed_to_double (bbox->p1.x); |
| y1 = _cairo_fixed_to_double (bbox->p1.y); |
| y2 = _cairo_fixed_to_double (bbox->p2.y); |
| x2 = _cairo_fixed_to_double (bbox->p2.x); |
| if (unlikely (glyphs->x + x2 <= extents->x || |
| glyphs->y + y2 <= extents->y || |
| glyphs->x + x1 >= extents->x + extents->width || |
| glyphs->y + y1 >= extents->y + extents->height)) |
| { |
| (*num_glyphs)--; |
| continue; |
| } |
| |
| /* XRenderAddGlyph does not handle a glyph surface larger than |
| * the extended maximum XRequest size. |
| */ |
| width = _cairo_fixed_integer_ceil (bbox->p2.x - bbox->p1.x); |
| height = _cairo_fixed_integer_ceil (bbox->p2.y - bbox->p1.y); |
| len = CAIRO_STRIDE_FOR_WIDTH_BPP (width, 32) * height; |
| if (unlikely (len >= max_glyph_size)) { |
| status = CAIRO_INT_STATUS_UNSUPPORTED; |
| break; |
| } |
| |
| /* The glyph coordinates must be representable in an int16_t. |
| * When possible, they will be expressed as an offset from the |
| * previous glyph, otherwise they will be an offset from the |
| * operation extents or from the surface origin. If the last |
| * two options are not valid, fallback. |
| */ |
| if (unlikely (glyphs->x > INT16_MAX || |
| glyphs->y > INT16_MAX || |
| glyphs->x - extents->x < INT16_MIN || |
| glyphs->y - extents->y < INT16_MIN)) |
| { |
| status = CAIRO_INT_STATUS_UNSUPPORTED; |
| break; |
| } |
| |
| |
| if (unlikely (valid_glyphs != glyphs)) |
| *valid_glyphs = *glyphs; |
| valid_glyphs++; |
| } |
| |
| if (unlikely (valid_glyphs != glyphs)) { |
| for (; glyphs != glyphs_end; glyphs++) { |
| *valid_glyphs = *glyphs; |
| valid_glyphs++; |
| } |
| } |
| |
| return status; |
| } |
| |
| /* 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; |
| |
| 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 int |
| _cairo_xcb_get_glyphset_index_for_format (cairo_format_t format) |
| { |
| if (format == CAIRO_FORMAT_A8) |
| return GLYPHSET_INDEX_A8; |
| if (format == CAIRO_FORMAT_A1) |
| return GLYPHSET_INDEX_A1; |
| |
| assert (format == CAIRO_FORMAT_ARGB32); |
| return GLYPHSET_INDEX_ARGB32; |
| } |
| |
| 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; |
| |
| glyphset_index = _cairo_xcb_get_glyphset_index_for_format (format); |
| 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) { |
| i = _cairo_xcb_get_glyphset_index_for_format (surface->format); |
| |
| 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_to_format (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 (_cairo_xcb_get_glyphset_index_for_format (scaled_glyph->surface->format)) { |
| case GLYPHSET_INDEX_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 GLYPHSET_INDEX_A8: |
| break; |
| |
| case GLYPHSET_INDEX_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; |
| |
| 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, |
| xcb_render_pictformat_t mask_format) |
| { |
| 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, |
| mask_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)) { |
| cairo_surface_destroy (&src->base); |
| 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)) { |
| cairo_surface_destroy (&src->base); |
| return status; |
| } |
| } |
| |
| glyph_cache[cache_index] = scaled_glyph; |
| } |
| |
| this_x = _cairo_lround (info->glyphs[i].d.x) - dst_x; |
| this_y = _cairo_lround (info->glyphs[i].d.y) - dst_y; |
| |
| 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 the glyph has a coordinate which cannot be represented |
| * as a 16-bit offset from the previous glyph, flush the |
| * current chunk. The current glyph will be the first one in |
| * the next chunk, thus its coordinates will be an offset from |
| * the destination origin. This offset is guaranteed to be |
| * representable as 16-bit offset in _can_composite_glyphs(). |
| */ |
| if (request_size + width > max_request_size - _cairo_sz_x_glyph_elt_t || |
| this_x - x > INT16_MAX || this_x - x < INT16_MIN || |
| this_y - y > INT16_MAX || this_y - y < INT16_MIN || |
| this_glyphset_info != glyphset_info) |
| { |
| status = _emit_glyphs_chunk (dst, op, src, |
| info->glyphs, i, |
| old_width, request_size, |
| glyphset_info, |
| info->use_mask ? glyphset_info->xrender_format : 0); |
| if (unlikely (status)) { |
| cairo_surface_destroy (&src->base); |
| 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, |
| info->use_mask ? glyphset_info->xrender_format : 0); |
| } |
| |
| cairo_surface_destroy (&src->base); |
| |
| return status; |
| } |
| |
| static cairo_bool_t |
| _surface_owns_font (cairo_xcb_surface_t *dst, |
| cairo_scaled_font_t *scaled_font) |
| { |
| cairo_xcb_font_t *font_private; |
| |
| font_private = scaled_font->surface_private; |
| if ((scaled_font->surface_backend != NULL && |
| scaled_font->surface_backend != dst->base.backend) || |
| (font_private != NULL && font_private->connection != dst->connection)) |
| { |
| return FALSE; |
| } |
| |
| return TRUE; |
| } |
| |
| 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; |
| cairo_bool_t overlap; |
| |
| 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, &overlap); |
| if (unlikely (status)) |
| return status; |
| |
| if (_cairo_clip_contains_rectangle (clip, &extents.mask)) |
| 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 = CAIRO_INT_STATUS_UNSUPPORTED; |
| if (surface->flags & CAIRO_XCB_RENDER_HAS_COMPOSITE_GLYPHS) { |
| _cairo_scaled_font_freeze_cache (scaled_font); |
| |
| if (_surface_owns_font (surface, scaled_font)) { |
| status = _can_composite_glyphs (surface, &extents.bounded, |
| 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; |
| info.use_mask = overlap || clip != NULL || ! extents.is_bounded; |
| |
| 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, |
| clip, &extents); |
| } |
| |
| if (have_clip) |
| _cairo_clip_fini (&local_clip); |
| |
| return status; |
| } |