/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */ /* cairo - a vector graphics library with display and print output * * Copyright © 2002 University of Southern California * Copyright © 2005 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is University of Southern * California. * * Contributor(s): * Carl D. Worth * Behdad Esfahbod * Chris Wilson * Karl Tomlinson , Mozilla Corporation */ /* Heed well the words of Owen Taylor: * "Any patch that works around a render bug, or claims to, without a * specific reference to the bug filed in bugzilla.freedesktop.org will * never pass approval." */ #include "cairoint.h" #include "cairo-xlib-private.h" #include "cairo-xlib-surface-private.h" #include "cairo-clip-private.h" #include "cairo-scaled-font-private.h" #include "cairo-region-private.h" #include /* for XDestroyImage */ #define XLIB_COORD_MAX 32767 #define DEBUG 0 #if DEBUG #define UNSUPPORTED(reason) \ fprintf (stderr, \ "cairo-xlib: hit unsupported operation %s(), line %d: %s\n", \ __FUNCTION__, __LINE__, reason), \ CAIRO_INT_STATUS_UNSUPPORTED #else #define UNSUPPORTED(reason) CAIRO_INT_STATUS_UNSUPPORTED #endif /* Xlib doesn't define a typedef, so define one ourselves */ typedef int (*cairo_xlib_error_func_t) (Display *display, XErrorEvent *event); static cairo_surface_t * _cairo_xlib_surface_create_internal (Display *dpy, Drawable drawable, Screen *screen, Visual *visual, XRenderPictFormat *xrender_format, int width, int height, int depth); static cairo_status_t _cairo_xlib_surface_ensure_gc (cairo_xlib_surface_t *surface, cairo_bool_t set_clip); static void _cairo_xlib_surface_maybe_put_gc (cairo_xlib_surface_t *surface); static void _cairo_xlib_surface_ensure_src_picture (cairo_xlib_surface_t *surface); static void _cairo_xlib_surface_ensure_dst_picture (cairo_xlib_surface_t *surface); static cairo_bool_t _cairo_surface_is_xlib (cairo_surface_t *surface); static cairo_bool_t _native_byte_order_lsb (void); static cairo_int_status_t _cairo_xlib_surface_show_glyphs (void *abstract_dst, cairo_operator_t op, const cairo_pattern_t *src_pattern, cairo_glyph_t *glyphs, int num_glyphs, cairo_scaled_font_t *scaled_font, cairo_clip_t *clip, int *remaining_glyphs); /* XXX temporarily used by cairo-qt-surface.c */ slim_hidden_proto (cairo_xlib_surface_create); slim_hidden_proto (cairo_xlib_surface_create_with_xrender_format); /* * Instead of taking two round trips for each blending request, * assume that if a particular drawable fails GetImage that it will * fail for a "while"; use temporary pixmaps to avoid the errors */ #define CAIRO_ASSUME_PIXMAP 20 static const XTransform identity = { { { 1 << 16, 0x00000, 0x00000 }, { 0x00000, 1 << 16, 0x00000 }, { 0x00000, 0x00000, 1 << 16 }, } }; #define CAIRO_SURFACE_RENDER_AT_LEAST(surface, major, minor) \ (((surface)->render_major > major) || \ (((surface)->render_major == major) && ((surface)->render_minor >= minor))) #define CAIRO_SURFACE_RENDER_HAS_CREATE_PICTURE(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 0) #define CAIRO_SURFACE_RENDER_HAS_COMPOSITE(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 0) #define CAIRO_SURFACE_RENDER_HAS_COMPOSITE_TEXT(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 0) #define CAIRO_SURFACE_RENDER_HAS_FILL_RECTANGLES(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 1) #define CAIRO_SURFACE_RENDER_HAS_DISJOINT(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 2) #define CAIRO_SURFACE_RENDER_HAS_CONJOINT(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 2) #define CAIRO_SURFACE_RENDER_HAS_TRAPEZOIDS(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 4) #define CAIRO_SURFACE_RENDER_HAS_TRIANGLES(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 4) #define CAIRO_SURFACE_RENDER_HAS_TRISTRIP(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 4) #define CAIRO_SURFACE_RENDER_HAS_TRIFAN(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 4) #define CAIRO_SURFACE_RENDER_HAS_PICTURE_TRANSFORM(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 6) #define CAIRO_SURFACE_RENDER_HAS_FILTERS(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 6) #define CAIRO_SURFACE_RENDER_HAS_EXTENDED_REPEAT(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 10) #define CAIRO_SURFACE_RENDER_HAS_GRADIENTS(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 10) #define CAIRO_SURFACE_RENDER_HAS_PDF_OPERATORS(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 11) #if RENDER_MAJOR > 0 || RENDER_MINOR >= 11 #define CAIRO_SURFACE_RENDER_SUPPORTS_OPERATOR(surface, op) \ (((op) <= CAIRO_OPERATOR_SATURATE) ? TRUE : (CAIRO_SURFACE_RENDER_HAS_PDF_OPERATORS (surface) ? (op) <= CAIRO_OPERATOR_HSL_LUMINOSITY : FALSE)) #else #define CAIRO_SURFACE_RENDER_SUPPORTS_OPERATOR(surface, op) \ ((op) <= CAIRO_OPERATOR_SATURATE) #endif static cairo_status_t _cairo_xlib_surface_set_clip_region (cairo_xlib_surface_t *surface, cairo_region_t *region) { cairo_bool_t had_clip_rects = surface->clip_region != NULL; if (had_clip_rects == FALSE && region == NULL) return CAIRO_STATUS_SUCCESS; if (surface->clip_region == region) return CAIRO_STATUS_SUCCESS; if (cairo_region_equal (surface->clip_region, region)) return CAIRO_STATUS_SUCCESS; cairo_region_destroy (surface->clip_region); surface->clip_region = cairo_region_reference (region); if (surface->clip_rects != surface->embedded_clip_rects) { free (surface->clip_rects); surface->clip_rects = surface->embedded_clip_rects; } surface->num_clip_rects = 0; if (region != NULL) { XRectangle *rects = NULL; int n_rects, i; n_rects = cairo_region_num_rectangles (region); if (n_rects > ARRAY_LENGTH (surface->embedded_clip_rects)) { rects = _cairo_malloc_ab (n_rects, sizeof (XRectangle)); if (unlikely (rects == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); } else { rects = surface->embedded_clip_rects; } for (i = 0; i < n_rects; i++) { cairo_rectangle_int_t rect; cairo_region_get_rectangle (region, i, &rect); rects[i].x = rect.x; rects[i].y = rect.y; rects[i].width = rect.width; rects[i].height = rect.height; } surface->clip_rects = rects; surface->num_clip_rects = n_rects; } surface->clip_dirty = CAIRO_XLIB_SURFACE_CLIP_DIRTY_ALL; return CAIRO_STATUS_SUCCESS; } static cairo_surface_t * _cairo_xlib_surface_create_similar_with_format (void *abstract_src, cairo_format_t format, int width, int height) { cairo_xlib_surface_t *src = abstract_src; Display *dpy = src->dpy; Pixmap pix; cairo_xlib_surface_t *surface; XRenderPictFormat *xrender_format; assert (width <= XLIB_COORD_MAX && height <= XLIB_COORD_MAX); /* As a good first approximation, if the display doesn't have even * the most elementary RENDER operation, then we're better off * using image surfaces for all temporary operations, so return NULL * and let the fallback code happen. */ if (! CAIRO_SURFACE_RENDER_HAS_COMPOSITE (src)) return NULL; xrender_format = _cairo_xlib_display_get_xrender_format (src->display, format); if (xrender_format == NULL) return NULL; pix = XCreatePixmap (dpy, src->drawable, width <= 0 ? 1 : width, height <= 0 ? 1 : height, xrender_format->depth); surface = (cairo_xlib_surface_t *) _cairo_xlib_surface_create_internal (dpy, pix, src->screen, NULL, xrender_format, width, height, xrender_format->depth); if (unlikely (surface->base.status)) { XFreePixmap (dpy, pix); return &surface->base; } surface->owns_pixmap = TRUE; return &surface->base; } static cairo_content_t _xrender_format_to_content (XRenderPictFormat *xrender_format) { cairo_bool_t xrender_format_has_alpha; cairo_bool_t xrender_format_has_color; /* This only happens when using a non-Render server. Let's punt * and say there's no alpha here. */ if (xrender_format == NULL) return CAIRO_CONTENT_COLOR; xrender_format_has_alpha = (xrender_format->direct.alphaMask != 0); xrender_format_has_color = (xrender_format->direct.redMask != 0 || xrender_format->direct.greenMask != 0 || xrender_format->direct.blueMask != 0); if (xrender_format_has_alpha) if (xrender_format_has_color) return CAIRO_CONTENT_COLOR_ALPHA; else return CAIRO_CONTENT_ALPHA; else return CAIRO_CONTENT_COLOR; } static cairo_surface_t * _cairo_xlib_surface_create_similar (void *abstract_src, cairo_content_t content, int width, int height) { cairo_xlib_surface_t *src = abstract_src; XRenderPictFormat *xrender_format; cairo_xlib_surface_t *surface; Pixmap pix; if (width > XLIB_COORD_MAX || height > XLIB_COORD_MAX) return NULL; _cairo_xlib_display_notify (src->display); /* If we never found an XRenderFormat or if it isn't compatible * with the content being requested, then we fallback to just * constructing a cairo_format_t instead, (which will fairly * arbitrarily pick a visual/depth for the similar surface. */ xrender_format = src->xrender_format; if (xrender_format == NULL || _xrender_format_to_content (xrender_format) != content) { return _cairo_xlib_surface_create_similar_with_format (abstract_src, _cairo_format_from_content (content), width, height); } /* We've got a compatible XRenderFormat now, which means the * similar surface will match the existing surface as closely in * visual/depth etc. as possible. */ pix = XCreatePixmap (src->dpy, src->drawable, width <= 0 ? 1 : width, height <= 0 ? 1 : height, xrender_format->depth); surface = (cairo_xlib_surface_t *) _cairo_xlib_surface_create_internal (src->dpy, pix, src->screen, src->visual, xrender_format, width, height, xrender_format->depth); if (unlikely (surface->base.status)) { XFreePixmap (src->dpy, pix); return &surface->base; } surface->owns_pixmap = TRUE; return &surface->base; } static cairo_status_t _cairo_xlib_surface_finish (void *abstract_surface) { cairo_xlib_surface_t *surface = abstract_surface; cairo_xlib_display_t *display = surface->display; cairo_status_t status = CAIRO_STATUS_SUCCESS; if (surface->owns_pixmap) { cairo_status_t status2; if (surface->dst_picture != None) { status2 = _cairo_xlib_display_queue_resource (display, XRenderFreePicture, surface->dst_picture); if (status == CAIRO_STATUS_SUCCESS) status = status2; } if (surface->src_picture != None) { status2 = _cairo_xlib_display_queue_resource (display, XRenderFreePicture, surface->src_picture); if (status == CAIRO_STATUS_SUCCESS) status = status2; } status2 = _cairo_xlib_display_queue_resource (display, (cairo_xlib_notify_resource_func) XFreePixmap, surface->drawable); if (status == CAIRO_STATUS_SUCCESS) status = status2; } else { if (surface->dst_picture != None) XRenderFreePicture (surface->dpy, surface->dst_picture); if (surface->src_picture != None) XRenderFreePicture (surface->dpy, surface->src_picture); } if (surface->gc != NULL) { _cairo_xlib_screen_put_gc (surface->screen_info, surface->depth, surface->gc, surface->gc_has_clip_rects); surface->gc = NULL; } if (surface->clip_rects != surface->embedded_clip_rects) free (surface->clip_rects); if (surface->screen_info != NULL) _cairo_xlib_screen_info_destroy (surface->screen_info); if (surface->display != NULL) { _cairo_xlib_remove_close_display_hook (surface->display, &surface->close_display_hook); _cairo_xlib_display_destroy (surface->display); } cairo_region_destroy (surface->clip_region); surface->dpy = NULL; return status; } static int _noop_error_handler (Display *display, XErrorEvent *event) { return False; /* return value is ignored */ } static void _swap_ximage_2bytes (XImage *ximage) { int i, j; char *line = ximage->data; for (j = ximage->height; j; j--) { uint16_t *p = (uint16_t *) line; for (i = ximage->width; i; i--) { *p = bswap_16 (*p); p++; } line += ximage->bytes_per_line; } } static void _swap_ximage_3bytes (XImage *ximage) { int i, j; char *line = ximage->data; for (j = ximage->height; j; j--) { uint8_t *p = (uint8_t *) line; for (i = ximage->width; i; i--) { uint8_t tmp; tmp = p[2]; p[2] = p[0]; p[0] = tmp; p += 3; } line += ximage->bytes_per_line; } } static void _swap_ximage_4bytes (XImage *ximage) { int i, j; char *line = ximage->data; for (j = ximage->height; j; j--) { uint32_t *p = (uint32_t *) line; for (i = ximage->width; i; i--) { *p = bswap_32 (*p); p++; } line += ximage->bytes_per_line; } } static void _swap_ximage_nibbles (XImage *ximage) { int i, j; char *line = ximage->data; for (j = ximage->height; j; j--) { uint8_t *p = (uint8_t *) line; for (i = (ximage->width + 1) / 2; i; i--) { *p = ((*p >> 4) & 0xf) | ((*p << 4) & ~0xf); p++; } line += ximage->bytes_per_line; } } static void _swap_ximage_bits (XImage *ximage) { int i, j; char *line = ximage->data; int unit = ximage->bitmap_unit; int line_bytes = ((ximage->width + unit - 1) & ~(unit - 1)) / 8; for (j = ximage->height; j; j--) { char *p = line; for (i = line_bytes; i; i--) { char b = *p; b = ((b << 1) & 0xaa) | ((b >> 1) & 0x55); b = ((b << 2) & 0xcc) | ((b >> 2) & 0x33); b = ((b << 4) & 0xf0) | ((b >> 4) & 0x0f); *p = b; p++; } line += ximage->bytes_per_line; } } static void _swap_ximage_to_native (XImage *ximage) { int unit_bytes = 0; int native_byte_order = _native_byte_order_lsb () ? LSBFirst : MSBFirst; if (ximage->bits_per_pixel == 1 && ximage->bitmap_bit_order != native_byte_order) { _swap_ximage_bits (ximage); if (ximage->bitmap_bit_order == ximage->byte_order) return; } if (ximage->byte_order == native_byte_order) return; switch (ximage->bits_per_pixel) { case 1: unit_bytes = ximage->bitmap_unit / 8; break; case 4: _swap_ximage_nibbles (ximage); /* fall-through */ case 8: case 16: case 20: case 24: case 28: case 30: case 32: unit_bytes = (ximage->bits_per_pixel + 7) / 8; break; default: /* This could be hit on some rare but possible cases. */ ASSERT_NOT_REACHED; } switch (unit_bytes) { case 1: break; case 2: _swap_ximage_2bytes (ximage); break; case 3: _swap_ximage_3bytes (ximage); break; case 4: _swap_ximage_4bytes (ximage); break; default: ASSERT_NOT_REACHED; } } /* Given a mask, (with a single sequence of contiguous 1 bits), return * the number of 1 bits in 'width' and the number of 0 bits to its * right in 'shift'. */ static void _characterize_field (uint32_t mask, int *width, int *shift) { *width = _cairo_popcount (mask); /* The final '& 31' is to force a 0 mask to result in 0 shift. */ *shift = _cairo_popcount ((mask - 1) & ~mask) & 31; } /* Convert a field of 'width' bits to 'new_width' bits with correct * rounding. */ static inline uint32_t _resize_field (uint32_t field, int width, int new_width) { if (width == 0) return 0; if (width >= new_width) { return field >> (width - new_width); } else { uint32_t result = field << (new_width - width); while (width < new_width) { result |= result >> width; width <<= 1; } return result; } } static inline uint32_t _adjust_field (uint32_t field, int adjustment) { return MIN (255, MAX(0, (int)field + adjustment)); } /* Given a shifted field value, (described by 'width' and 'shift), * resize it 8-bits and return that value. * * Note that the original field value must not have any non-field bits * set. */ static inline uint32_t _field_to_8 (uint32_t field, int width, int shift) { return _resize_field (field >> shift, width, 8); } static inline uint32_t _field_to_8_undither (uint32_t field, int width, int shift, int dither_adjustment) { return _adjust_field (_field_to_8 (field, width, shift), - dither_adjustment>>width); } /* Given an 8-bit value, convert it to a field of 'width', shift it up * to 'shift, and return it. */ static inline uint32_t _field_from_8 (uint32_t field, int width, int shift) { return _resize_field (field, 8, width) << shift; } static inline uint32_t _field_from_8_dither (uint32_t field, int width, int shift, int8_t dither_adjustment) { return _field_from_8 (_adjust_field (field, dither_adjustment>>width), width, shift); } static inline uint32_t _pseudocolor_from_rgb888_dither (cairo_xlib_visual_info_t *visual_info, uint32_t r, uint32_t g, uint32_t b, int8_t dither_adjustment) { if (r == g && g == b) { dither_adjustment /= RAMP_SIZE; return visual_info->gray8_to_pseudocolor[_adjust_field (r, dither_adjustment)]; } else { dither_adjustment = visual_info->dither8_to_cube[dither_adjustment+128]; return visual_info->cube_to_pseudocolor[visual_info->field8_to_cube[_adjust_field (r, dither_adjustment)]] [visual_info->field8_to_cube[_adjust_field (g, dither_adjustment)]] [visual_info->field8_to_cube[_adjust_field (b, dither_adjustment)]]; } } static inline uint32_t _pseudocolor_to_rgb888 (cairo_xlib_visual_info_t *visual_info, uint32_t pixel) { uint32_t r, g, b; pixel &= 0xff; r = visual_info->colors[pixel].r; g = visual_info->colors[pixel].g; b = visual_info->colors[pixel].b; return (r << 16) | (g << 8) | (b ); } /* should range from -128 to 127 */ #define X 16 static const int8_t dither_pattern[4][4] = { {-8*X, +0*X, -6*X, +2*X}, {+4*X, -4*X, +6*X, -2*X}, {-5*X, +4*X, -7*X, +1*X}, {+7*X, -1*X, +5*X, -3*X} }; #undef X static cairo_status_t _get_image_surface (cairo_xlib_surface_t *surface, cairo_rectangle_int_t *interest_rect, cairo_image_surface_t **image_out, cairo_rectangle_int_t *image_rect) { cairo_int_status_t status; cairo_image_surface_t *image = NULL; XImage *ximage; cairo_rectangle_int_t extents; pixman_format_code_t pixman_format; cairo_format_masks_t xlib_masks; extents.x = 0; extents.y = 0; extents.width = surface->width; extents.height = surface->height; if (interest_rect) { if (! _cairo_rectangle_intersect (&extents, interest_rect)) { *image_out = NULL; return CAIRO_STATUS_SUCCESS; } } if (image_rect) *image_rect = extents; /* XXX: This should try to use the XShm extension if available */ if (surface->use_pixmap == 0) { cairo_xlib_error_func_t old_handler; old_handler = XSetErrorHandler (_noop_error_handler); ximage = XGetImage (surface->dpy, surface->drawable, extents.x, extents.y, extents.width, extents.height, AllPlanes, ZPixmap); XSetErrorHandler (old_handler); /* If we get an error, the surface must have been a window, * so retry with the safe code path. */ if (!ximage) surface->use_pixmap = CAIRO_ASSUME_PIXMAP; } else { surface->use_pixmap--; ximage = NULL; } if (ximage == NULL) { /* XGetImage from a window is dangerous because it can * produce errors if the window is unmapped or partially * outside the screen. We could check for errors and * retry, but to keep things simple, we just create a * temporary pixmap */ Pixmap pixmap; status = _cairo_xlib_surface_ensure_gc (surface, FALSE); if (unlikely (status)) return status; pixmap = XCreatePixmap (surface->dpy, surface->drawable, extents.width, extents.height, surface->depth); if (pixmap) { XCopyArea (surface->dpy, surface->drawable, pixmap, surface->gc, extents.x, extents.y, extents.width, extents.height, 0, 0); ximage = XGetImage (surface->dpy, pixmap, 0, 0, extents.width, extents.height, AllPlanes, ZPixmap); XFreePixmap (surface->dpy, pixmap); } if (ximage == NULL) return _cairo_error (CAIRO_STATUS_NO_MEMORY); _cairo_xlib_surface_maybe_put_gc (surface); } _swap_ximage_to_native (ximage); xlib_masks.bpp = ximage->bits_per_pixel; xlib_masks.alpha_mask = surface->a_mask; xlib_masks.red_mask = surface->r_mask; xlib_masks.green_mask = surface->g_mask; xlib_masks.blue_mask = surface->b_mask; /* We can't use pixman to simply write to image if: * (a) the pixels are not appropriately aligned, * (b) pixman does not the pixel format, or * (c) if the image is palettized and we need to convert. */ if (ximage->bitmap_unit == 32 && ximage->bitmap_pad == 32 && _pixman_format_from_masks (&xlib_masks, &pixman_format) && (surface->visual == NULL || surface->visual->class == TrueColor)) { image = (cairo_image_surface_t*) _cairo_image_surface_create_with_pixman_format ((unsigned char *) ximage->data, pixman_format, ximage->width, ximage->height, ximage->bytes_per_line); status = image->base.status; if (unlikely (status)) goto BAIL; /* Let the surface take ownership of the data */ _cairo_image_surface_assume_ownership_of_data (image); ximage->data = NULL; } else { /* The visual we are dealing with is not supported by the * standard pixman formats. So we must first convert the data * to a supported format. */ cairo_format_t format; unsigned char *data; uint32_t *row; uint32_t in_pixel, out_pixel; unsigned int rowstride; uint32_t a_mask=0, r_mask=0, g_mask=0, b_mask=0; int a_width=0, r_width=0, g_width=0, b_width=0; int a_shift=0, r_shift=0, g_shift=0, b_shift=0; int x, y, x0, y0, x_off, y_off; cairo_xlib_visual_info_t *visual_info; if (surface->visual == NULL || surface->visual->class == TrueColor) { cairo_bool_t has_alpha; cairo_bool_t has_color; has_alpha = surface->a_mask; has_color = (surface->r_mask || surface->g_mask || surface->b_mask); if (has_color) { if (has_alpha) { format = CAIRO_FORMAT_ARGB32; } else { format = CAIRO_FORMAT_RGB24; } } else { /* XXX: Using CAIRO_FORMAT_A8 here would be more * efficient, but would require slightly different code in * the image conversion to put the alpha channel values * into the right place. */ format = CAIRO_FORMAT_ARGB32; } a_mask = surface->a_mask; r_mask = surface->r_mask; g_mask = surface->g_mask; b_mask = surface->b_mask; _characterize_field (a_mask, &a_width, &a_shift); _characterize_field (r_mask, &r_width, &r_shift); _characterize_field (g_mask, &g_width, &g_shift); _characterize_field (b_mask, &b_width, &b_shift); } else { format = CAIRO_FORMAT_RGB24; status = _cairo_xlib_screen_get_visual_info (surface->screen_info, surface->visual, &visual_info); if (unlikely (status)) goto BAIL; } image = (cairo_image_surface_t *) cairo_image_surface_create (format, ximage->width, ximage->height); status = image->base.status; if (unlikely (status)) goto BAIL; data = cairo_image_surface_get_data (&image->base); rowstride = cairo_image_surface_get_stride (&image->base) >> 2; row = (uint32_t *) data; x0 = extents.x + surface->base.device_transform.x0; y0 = extents.y + surface->base.device_transform.y0; for (y = 0, y_off = y0 % ARRAY_LENGTH (dither_pattern); y < ximage->height; y++, y_off = (y_off+1) % ARRAY_LENGTH (dither_pattern)) { const int8_t *dither_row = dither_pattern[y_off]; for (x = 0, x_off = x0 % ARRAY_LENGTH (dither_pattern[0]); x < ximage->width; x++, x_off = (x_off+1) % ARRAY_LENGTH (dither_pattern[0])) { int dither_adjustment = dither_row[x_off]; in_pixel = XGetPixel (ximage, x, y); if (surface->visual == NULL || surface->visual->class == TrueColor) { out_pixel = ( _field_to_8 (in_pixel & a_mask, a_width, a_shift) << 24 | _field_to_8_undither (in_pixel & r_mask, r_width, r_shift, dither_adjustment) << 16 | _field_to_8_undither (in_pixel & g_mask, g_width, g_shift, dither_adjustment) << 8 | _field_to_8_undither (in_pixel & b_mask, b_width, b_shift, dither_adjustment)); } else { /* Undithering pseudocolor does not look better */ out_pixel = _pseudocolor_to_rgb888 (visual_info, in_pixel); } row[x] = out_pixel; } row += rowstride; } } BAIL: XDestroyImage (ximage); if (unlikely (status)) { if (image) { cairo_surface_destroy (&image->base); image = NULL; } } *image_out = image; return status; } static void _cairo_xlib_surface_ensure_src_picture (cairo_xlib_surface_t *surface) { if (!surface->src_picture) { XRenderPictureAttributes pa; int mask = 0; pa.subwindow_mode = IncludeInferiors; mask |= CPSubwindowMode; surface->src_picture = XRenderCreatePicture (surface->dpy, surface->drawable, surface->xrender_format, mask, &pa); } } static void _cairo_xlib_surface_set_picture_clip_rects (cairo_xlib_surface_t *surface) { if (surface->clip_region != NULL) { XRenderSetPictureClipRectangles (surface->dpy, surface->dst_picture, 0, 0, surface->clip_rects, surface->num_clip_rects); } else { XRenderPictureAttributes pa; pa.clip_mask = None; XRenderChangePicture (surface->dpy, surface->dst_picture, CPClipMask, &pa); } surface->clip_dirty &= ~CAIRO_XLIB_SURFACE_CLIP_DIRTY_PICTURE; } static void _cairo_xlib_surface_set_gc_clip_rects (cairo_xlib_surface_t *surface) { surface->gc_has_clip_rects = surface->clip_region != NULL; if (surface->clip_region != NULL) { XSetClipRectangles(surface->dpy, surface->gc, 0, 0, surface->clip_rects, surface->num_clip_rects, YXSorted); } else XSetClipMask (surface->dpy, surface->gc, None); surface->clip_dirty &= ~CAIRO_XLIB_SURFACE_CLIP_DIRTY_GC; } static void _cairo_xlib_surface_ensure_dst_picture (cairo_xlib_surface_t *surface) { if (!surface->dst_picture) { surface->dst_picture = XRenderCreatePicture (surface->dpy, surface->drawable, surface->xrender_format, 0, NULL); } if (surface->clip_dirty & CAIRO_XLIB_SURFACE_CLIP_DIRTY_PICTURE) _cairo_xlib_surface_set_picture_clip_rects (surface); } static cairo_status_t _cairo_xlib_surface_ensure_gc (cairo_xlib_surface_t *surface, cairo_bool_t set_clip) { if (surface->gc == NULL) { surface->gc = _cairo_xlib_screen_get_gc (surface->screen_info, surface->depth, surface->drawable, &surface->clip_dirty); if (unlikely (surface->gc == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); surface->gc_has_clip_rects = surface->clip_dirty & CAIRO_XLIB_SURFACE_CLIP_DIRTY_GC; } if (set_clip) { if (surface->clip_dirty & CAIRO_XLIB_SURFACE_CLIP_DIRTY_GC) _cairo_xlib_surface_set_gc_clip_rects (surface); } else { if (surface->gc_has_clip_rects) { surface->gc_has_clip_rects = FALSE; XSetClipMask (surface->dpy, surface->gc, None); surface->clip_dirty |= CAIRO_XLIB_SURFACE_CLIP_DIRTY_GC; } } return CAIRO_STATUS_SUCCESS; } static void _cairo_xlib_surface_maybe_put_gc (cairo_xlib_surface_t *surface) { /* return the GC back to the common pool if clean */ if (surface->gc_has_clip_rects) return; _cairo_xlib_screen_put_gc (surface->screen_info, surface->depth, surface->gc, FALSE); surface->gc = NULL; } static cairo_status_t _draw_image_surface (cairo_xlib_surface_t *surface, cairo_image_surface_t *image, int src_x, int src_y, int width, int height, int dst_x, int dst_y) { XImage ximage; cairo_format_masks_t image_masks; int native_byte_order = _native_byte_order_lsb () ? LSBFirst : MSBFirst; cairo_status_t status; cairo_bool_t own_data; _pixman_format_to_masks (image->pixman_format, &image_masks); ximage.width = image->width; ximage.height = image->height; ximage.format = ZPixmap; ximage.byte_order = native_byte_order; ximage.bitmap_unit = 32; /* always for libpixman */ ximage.bitmap_bit_order = native_byte_order; ximage.bitmap_pad = 32; /* always for libpixman */ ximage.depth = surface->depth; ximage.red_mask = surface->r_mask; ximage.green_mask = surface->g_mask; ximage.blue_mask = surface->b_mask; ximage.xoffset = 0; if ((image_masks.alpha_mask == surface->a_mask || surface->a_mask == 0) && (image_masks.red_mask == surface->r_mask || surface->r_mask == 0) && (image_masks.green_mask == surface->g_mask || surface->g_mask == 0) && (image_masks.blue_mask == surface->b_mask || surface->b_mask == 0)) { int ret; ximage.bits_per_pixel = image_masks.bpp; ximage.bytes_per_line = image->stride; ximage.data = (char *)image->data; own_data = FALSE; ret = XInitImage (&ximage); assert (ret != 0); } else { unsigned int stride, rowstride; int x, y, x0, y0, x_off, y_off; uint32_t in_pixel, out_pixel, *row; int i_a_width=0, i_r_width=0, i_g_width=0, i_b_width=0; int i_a_shift=0, i_r_shift=0, i_g_shift=0, i_b_shift=0; int o_a_width=0, o_r_width=0, o_g_width=0, o_b_width=0; int o_a_shift=0, o_r_shift=0, o_g_shift=0, o_b_shift=0; cairo_xlib_visual_info_t *visual_info = NULL; cairo_bool_t true_color; int ret; if (surface->depth > 16) { ximage.bits_per_pixel = 32; } else if (surface->depth > 8) { ximage.bits_per_pixel = 16; } else if (surface->depth > 1) { ximage.bits_per_pixel = 8; } else { ximage.bits_per_pixel = 1; } stride = CAIRO_STRIDE_FOR_WIDTH_BPP (ximage.width, ximage.bits_per_pixel); ximage.bytes_per_line = stride; ximage.data = _cairo_malloc_ab (stride, ximage.height); if (unlikely (ximage.data == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); own_data = TRUE; ret = XInitImage (&ximage); assert (ret != 0); _characterize_field (image_masks.alpha_mask, &i_a_width, &i_a_shift); _characterize_field (image_masks.red_mask , &i_r_width, &i_r_shift); _characterize_field (image_masks.green_mask, &i_g_width, &i_g_shift); _characterize_field (image_masks.blue_mask , &i_b_width, &i_b_shift); true_color = surface->visual == NULL || surface->visual->class == TrueColor; if (true_color) { _characterize_field (surface->a_mask, &o_a_width, &o_a_shift); _characterize_field (surface->r_mask, &o_r_width, &o_r_shift); _characterize_field (surface->g_mask, &o_g_width, &o_g_shift); _characterize_field (surface->b_mask, &o_b_width, &o_b_shift); } else { status = _cairo_xlib_screen_get_visual_info (surface->screen_info, surface->visual, &visual_info); if (unlikely (status)) goto BAIL; } rowstride = image->stride >> 2; row = (uint32_t *) image->data; x0 = dst_x + surface->base.device_transform.x0; y0 = dst_y + surface->base.device_transform.y0; for (y = 0, y_off = y0 % ARRAY_LENGTH (dither_pattern); y < ximage.height; y++, y_off = (y_off+1) % ARRAY_LENGTH (dither_pattern)) { const int8_t *dither_row = dither_pattern[y_off]; for (x = 0, x_off = x0 % ARRAY_LENGTH (dither_pattern[0]); x < ximage.width; x++, x_off = (x_off+1) % ARRAY_LENGTH (dither_pattern[0])) { int dither_adjustment = dither_row[x_off]; int a, r, g, b; if (image_masks.bpp <= 8) in_pixel = ((uint8_t*)row)[x]; else if (image_masks.bpp <= 16) in_pixel = ((uint16_t*)row)[x]; else in_pixel = row[x]; /* If the incoming image has no alpha channel, then the input * is opaque and the output should have the maximum alpha value. * For all other channels, their absence implies 0. */ if (image_masks.alpha_mask == 0x0) a = 0xff; else a = _field_to_8 (in_pixel & image_masks.alpha_mask, i_a_width, i_a_shift); r = _field_to_8 (in_pixel & image_masks.red_mask , i_r_width, i_r_shift); g = _field_to_8 (in_pixel & image_masks.green_mask, i_g_width, i_g_shift); b = _field_to_8 (in_pixel & image_masks.blue_mask , i_b_width, i_b_shift); if (true_color) { out_pixel = _field_from_8 (a, o_a_width, o_a_shift) | _field_from_8_dither (r, o_r_width, o_r_shift, dither_adjustment) | _field_from_8_dither (g, o_g_width, o_g_shift, dither_adjustment) | _field_from_8_dither (b, o_b_width, o_b_shift, dither_adjustment); } else { out_pixel = _pseudocolor_from_rgb888_dither (visual_info, r, g, b, dither_adjustment); } XPutPixel (&ximage, x, y, out_pixel); } row += rowstride; } } /* XXX set clip? */ status = _cairo_xlib_surface_ensure_gc (surface, FALSE); if (unlikely (status)) goto BAIL; XPutImage (surface->dpy, surface->drawable, surface->gc, &ximage, src_x, src_y, dst_x, dst_y, width, height); _cairo_xlib_surface_maybe_put_gc (surface); BAIL: if (own_data) free (ximage.data); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_xlib_surface_acquire_source_image (void *abstract_surface, cairo_image_surface_t **image_out, void **image_extra) { cairo_xlib_surface_t *surface = abstract_surface; cairo_image_surface_t *image; cairo_status_t status; _cairo_xlib_display_notify (surface->display); status = _get_image_surface (surface, NULL, &image, NULL); if (unlikely (status)) return status; *image_out = image; *image_extra = NULL; return CAIRO_STATUS_SUCCESS; } static cairo_surface_t * _cairo_xlib_surface_snapshot (void *abstract_surface) { cairo_xlib_surface_t *surface = abstract_surface; cairo_image_surface_t *image; cairo_status_t status; _cairo_xlib_display_notify (surface->display); status = _get_image_surface (surface, NULL, &image, NULL); if (unlikely (status)) return _cairo_surface_create_in_error (status); return &image->base; } static void _cairo_xlib_surface_release_source_image (void *abstract_surface, cairo_image_surface_t *image, void *image_extra) { cairo_surface_destroy (&image->base); } static cairo_status_t _cairo_xlib_surface_acquire_dest_image (void *abstract_surface, cairo_rectangle_int_t *interest_rect, cairo_image_surface_t **image_out, cairo_rectangle_int_t *image_rect_out, void **image_extra) { cairo_xlib_surface_t *surface = abstract_surface; cairo_image_surface_t *image; cairo_status_t status; _cairo_xlib_display_notify (surface->display); status = _get_image_surface (surface, interest_rect, &image, image_rect_out); if (unlikely (status)) return status; *image_out = image; *image_extra = NULL; return CAIRO_STATUS_SUCCESS; } static void _cairo_xlib_surface_release_dest_image (void *abstract_surface, cairo_rectangle_int_t *interest_rect, cairo_image_surface_t *image, cairo_rectangle_int_t *image_rect, void *image_extra) { cairo_xlib_surface_t *surface = abstract_surface; cairo_status_t status; status = _draw_image_surface (surface, image, 0, 0, image->width, image->height, image_rect->x, image_rect->y); status = _cairo_surface_set_error (&surface->base, status); cairo_surface_destroy (&image->base); } /* * Return whether two xlib surfaces share the same * screen. Both core and Render drawing require this * when using multiple drawables in an operation. */ static inline cairo_bool_t _cairo_xlib_surface_same_screen (cairo_xlib_surface_t *dst, cairo_xlib_surface_t *src) { return dst->screen_info == src->screen_info; } static cairo_status_t _cairo_xlib_surface_clone_similar (void *abstract_surface, cairo_surface_t *src, cairo_content_t content, int src_x, int src_y, int width, int height, int *clone_offset_x, int *clone_offset_y, cairo_surface_t **clone_out) { cairo_xlib_surface_t *surface = abstract_surface; cairo_xlib_surface_t *clone; cairo_status_t status; _cairo_xlib_display_notify (surface->display); if (src->backend == surface->base.backend ) { cairo_xlib_surface_t *xlib_src = (cairo_xlib_surface_t *)src; if (_cairo_xlib_surface_same_screen (surface, xlib_src)) { *clone_offset_x = 0; *clone_offset_y = 0; *clone_out = cairo_surface_reference (src); return CAIRO_STATUS_SUCCESS; } } else if (_cairo_surface_is_image (src)) { cairo_image_surface_t *image_src = (cairo_image_surface_t *)src; cairo_format_t format; if (width > XLIB_COORD_MAX || height > XLIB_COORD_MAX) return UNSUPPORTED ("roi too large for xlib"); format = image_src->format; if (format == CAIRO_FORMAT_INVALID || (_cairo_content_from_format (format) & ~content)) { format = _cairo_format_from_content (image_src->base.content & content); } clone = (cairo_xlib_surface_t *) _cairo_xlib_surface_create_similar_with_format (surface, format, width, height); if (clone == NULL) return UNSUPPORTED ("unhandled image format, no similar surface"); if (unlikely (clone->base.status)) return clone->base.status; status = _draw_image_surface (clone, image_src, src_x, src_y, width, height, 0, 0); if (unlikely (status)) { cairo_surface_destroy (&clone->base); return status; } *clone_offset_x = src_x; *clone_offset_y = src_y; *clone_out = &clone->base; return CAIRO_STATUS_SUCCESS; } return CAIRO_INT_STATUS_UNSUPPORTED; } static cairo_surface_t * _cairo_xlib_surface_create_solid_pattern_surface (void *abstract_surface, const cairo_solid_pattern_t *solid_pattern) { /* This function's only responsibility is to create a proper surface * for when XRender is not available. The proper surface is a xlib * surface (as opposed to image surface which is what create_similar * returns in those cases) and the size of the dithering pattern, not * 1x1. This surface can then be used in * _cairo_xlib_surface_solid_fill_rectangles() to do dithered "solid" * fills using core protocol */ cairo_xlib_surface_t *other = abstract_surface; cairo_image_surface_t *image; cairo_xlib_surface_t *surface = NULL; cairo_status_t status = CAIRO_STATUS_SUCCESS; int width = ARRAY_LENGTH (dither_pattern[0]); int height = ARRAY_LENGTH (dither_pattern); Pixmap pixmap = None; if (CAIRO_SURFACE_RENDER_HAS_COMPOSITE (other)) return NULL; image = (cairo_image_surface_t *) _cairo_image_surface_create_with_content (solid_pattern->content, width, height); status = image->base.status; if (unlikely (status)) goto BAIL; pixmap = XCreatePixmap (other->dpy, other->drawable, width, height, other->depth); surface = (cairo_xlib_surface_t *) _cairo_xlib_surface_create_internal (other->dpy, pixmap, other->screen, other->visual, other->xrender_format, width, height, other->depth); status = surface->base.status; if (unlikely (status)) goto BAIL; status = _cairo_surface_paint (&image->base, CAIRO_OPERATOR_SOURCE, &solid_pattern->base, NULL); if (unlikely (status)) goto BAIL; status = _draw_image_surface (surface, image, 0, 0, width, height, 0, 0); if (unlikely (status)) goto BAIL; BAIL: cairo_surface_destroy (&image->base); if (unlikely (status)) { if (pixmap != None) XFreePixmap (other->dpy, pixmap); cairo_surface_destroy (&surface->base); return _cairo_surface_create_in_error (status); } surface->owns_pixmap = TRUE; return &surface->base; } static cairo_bool_t _cairo_xlib_surface_can_repaint_solid_pattern_surface (void *abstract_surface, const cairo_solid_pattern_t *solid_pattern) { cairo_xlib_surface_t *other = abstract_surface; return CAIRO_SURFACE_RENDER_HAS_COMPOSITE (other); } static cairo_status_t _cairo_xlib_surface_set_matrix (cairo_xlib_surface_t *surface, cairo_matrix_t *matrix, double xc, double yc) { XTransform xtransform; /* Casting between pixman_transform_t and XTransform is safe because * they happen to be the exact same type. */ _cairo_matrix_to_pixman_matrix (matrix, (pixman_transform_t *) &xtransform, xc, yc); if (memcmp (&xtransform, &surface->xtransform, sizeof (XTransform)) == 0) return CAIRO_STATUS_SUCCESS; if (! CAIRO_SURFACE_RENDER_HAS_PICTURE_TRANSFORM (surface)) return UNSUPPORTED ("XRender does not support picture transforms"); XRenderSetPictureTransform (surface->dpy, surface->src_picture, &xtransform); surface->xtransform = xtransform; return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_xlib_surface_set_filter (cairo_xlib_surface_t *surface, cairo_filter_t filter) { const char *render_filter; if (surface->filter == filter) return CAIRO_STATUS_SUCCESS; if (!CAIRO_SURFACE_RENDER_HAS_FILTERS (surface)) { if (filter == CAIRO_FILTER_FAST || filter == CAIRO_FILTER_NEAREST) return CAIRO_STATUS_SUCCESS; return UNSUPPORTED ("XRender does not support filter"); } switch (filter) { case CAIRO_FILTER_FAST: render_filter = FilterFast; break; case CAIRO_FILTER_GOOD: render_filter = FilterGood; break; case CAIRO_FILTER_BEST: render_filter = FilterBest; break; case CAIRO_FILTER_NEAREST: render_filter = FilterNearest; break; case CAIRO_FILTER_BILINEAR: render_filter = FilterBilinear; break; case CAIRO_FILTER_GAUSSIAN: /* XXX: The GAUSSIAN value has no implementation in cairo * whatsoever, so it was really a mistake to have it in the * API. We could fix this by officially deprecating it, or * else inventing semantics and providing an actual * implementation for it. */ default: render_filter = FilterBest; break; } XRenderSetPictureFilter (surface->dpy, surface->src_picture, (char *) render_filter, NULL, 0); surface->filter = filter; return CAIRO_STATUS_SUCCESS; } static void _cairo_xlib_surface_set_repeat (cairo_xlib_surface_t *surface, int repeat) { XRenderPictureAttributes pa; unsigned long mask; if (surface->repeat == repeat) return; mask = CPRepeat; pa.repeat = repeat; XRenderChangePicture (surface->dpy, surface->src_picture, mask, &pa); surface->repeat = repeat; } static cairo_int_status_t _cairo_xlib_surface_set_attributes (cairo_xlib_surface_t *surface, cairo_surface_attributes_t *attributes, double xc, double yc) { cairo_int_status_t status; int repeat; _cairo_xlib_surface_ensure_src_picture (surface); status = _cairo_xlib_surface_set_matrix (surface, &attributes->matrix, xc, yc); if (unlikely (status)) return status; switch (attributes->extend) { case CAIRO_EXTEND_NONE: repeat = RepeatNone; break; case CAIRO_EXTEND_REPEAT: repeat = RepeatNormal; break; case CAIRO_EXTEND_REFLECT: if (surface->buggy_pad_reflect) return UNSUPPORTED ("buggy reflect"); repeat = RepeatReflect; break; case CAIRO_EXTEND_PAD: if (surface->buggy_pad_reflect) return UNSUPPORTED ("buggy reflect"); repeat = RepeatPad; break; default: ASSERT_NOT_REACHED; return CAIRO_INT_STATUS_UNSUPPORTED; } _cairo_xlib_surface_set_repeat (surface, repeat); status = _cairo_xlib_surface_set_filter (surface, attributes->filter); if (unlikely (status)) return status; return CAIRO_STATUS_SUCCESS; } /* Checks whether we can can directly draw from src to dst with * the core protocol: either with CopyArea or using src as a * a tile in a GC. */ static cairo_bool_t _surfaces_compatible (cairo_xlib_surface_t *dst, cairo_xlib_surface_t *src) { /* same screen */ if (! _cairo_xlib_surface_same_screen (dst, src)) return FALSE; /* same depth (for core) */ if (src->depth != dst->depth) return FALSE; /* if Render is supported, match picture formats */ if (src->xrender_format != dst->xrender_format) return FALSE; else if (src->xrender_format != NULL) return TRUE; /* Without Render, match visuals instead */ if (src->visual == dst->visual) return TRUE; return FALSE; } static cairo_bool_t _surface_has_alpha (cairo_xlib_surface_t *surface) { if (surface->xrender_format) { if (surface->xrender_format->type == PictTypeDirect && surface->xrender_format->direct.alphaMask != 0) return TRUE; else return FALSE; } else { /* In the no-render case, we never have alpha */ return FALSE; } } /* Returns true if the given operator and source-alpha combination * requires alpha compositing to complete. */ static cairo_bool_t _operator_needs_alpha_composite (cairo_operator_t op, cairo_bool_t destination_has_alpha, cairo_bool_t source_has_alpha) { if (op == CAIRO_OPERATOR_SOURCE || (! source_has_alpha && (op == CAIRO_OPERATOR_OVER || op == CAIRO_OPERATOR_ATOP || op == CAIRO_OPERATOR_IN))) return destination_has_alpha; return TRUE; } /* There is a bug in most older X servers with compositing using a * untransformed repeating source pattern when the source is in off-screen * video memory, and another with repeated transformed images using a * general transform matrix. When these bugs could be triggered, we need a * fallback: in the common case where we have no transformation and the * source and destination have the same format/visual, we can do the * operation using the core protocol for the first bug, otherwise, we need * a software fallback. * * We can also often optimize a compositing operation by calling XCopyArea * for some common cases where there is no alpha compositing to be done. * We figure that out here as well. */ typedef enum { DO_RENDER, /* use render */ DO_XCOPYAREA, /* core protocol XCopyArea optimization/fallback */ DO_XTILE, /* core protocol XSetTile optimization/fallback */ DO_UNSUPPORTED /* software fallback */ } composite_operation_t; /* Initial check for the render bugs; we need to recheck for the * offscreen-memory bug after we turn patterns into surfaces, since that * may introduce a repeating pattern for gradient patterns. We don't need * to check for the repeat+transform bug because gradient surfaces aren't * transformed. * * All we do here is reject cases where we *know* are going to * hit the bug and won't be able to use a core protocol fallback. */ static composite_operation_t _categorize_composite_operation (cairo_xlib_surface_t *dst, cairo_operator_t op, const cairo_pattern_t *src_pattern, cairo_bool_t have_mask) { if (!CAIRO_SURFACE_RENDER_SUPPORTS_OPERATOR (dst, op)) return DO_UNSUPPORTED; if (! dst->buggy_repeat) return DO_RENDER; if (src_pattern->type == CAIRO_PATTERN_TYPE_SURFACE) { cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *)src_pattern; if (_cairo_matrix_is_integer_translation (&src_pattern->matrix, NULL, NULL) && src_pattern->extend == CAIRO_EXTEND_REPEAT) { /* This is the case where we have the bug involving * untransformed repeating source patterns with off-screen * video memory; reject some cases where a core protocol * fallback is impossible. */ if (have_mask || !(op == CAIRO_OPERATOR_SOURCE || op == CAIRO_OPERATOR_OVER)) return DO_UNSUPPORTED; if (_cairo_surface_is_xlib (surface_pattern->surface)) { cairo_xlib_surface_t *src = (cairo_xlib_surface_t *)surface_pattern->surface; if (op == CAIRO_OPERATOR_OVER && _surface_has_alpha (src)) return DO_UNSUPPORTED; /* If these are on the same screen but otherwise incompatible, * make a copy as core drawing can't cross depths and doesn't * work right across visuals of the same depth */ if (_cairo_xlib_surface_same_screen (dst, src) && !_surfaces_compatible (dst, src)) return DO_UNSUPPORTED; } } /* Check for the other bug involving repeat patterns with general * transforms. */ if (!_cairo_matrix_is_integer_translation (&src_pattern->matrix, NULL, NULL) && src_pattern->extend == CAIRO_EXTEND_REPEAT) return DO_UNSUPPORTED; } return DO_RENDER; } /* Recheck for composite-repeat once we've turned patterns into Xlib surfaces * If we end up returning DO_UNSUPPORTED here, we're throwing away work we * did to turn gradients into a pattern, but most of the time we can handle * that case with core protocol fallback. * * Also check here if we can just use XCopyArea, instead of going through * Render. */ static composite_operation_t _recategorize_composite_operation (cairo_xlib_surface_t *dst, cairo_operator_t op, cairo_xlib_surface_t *src, cairo_surface_attributes_t *src_attr, cairo_bool_t have_mask) { cairo_bool_t is_integer_translation = _cairo_matrix_is_integer_translation (&src_attr->matrix, NULL, NULL); cairo_bool_t needs_alpha_composite; needs_alpha_composite = _operator_needs_alpha_composite (op, _surface_has_alpha (dst), _surface_has_alpha (src)); if (! have_mask && is_integer_translation && src_attr->extend == CAIRO_EXTEND_NONE && ! needs_alpha_composite && src->owns_pixmap && _surfaces_compatible (src, dst)) { return DO_XCOPYAREA; } if (dst->buggy_repeat && is_integer_translation && src_attr->extend == CAIRO_EXTEND_REPEAT && (src->width != 1 || src->height != 1)) { if (! have_mask && ! needs_alpha_composite && src->owns_pixmap && _surfaces_compatible (dst, src)) { return DO_XTILE; } return DO_UNSUPPORTED; } if (! CAIRO_SURFACE_RENDER_HAS_COMPOSITE (src)) return DO_UNSUPPORTED; if (! CAIRO_SURFACE_RENDER_HAS_COMPOSITE (dst)) return DO_UNSUPPORTED; return DO_RENDER; } static int _render_operator (cairo_operator_t op) { switch (op) { case CAIRO_OPERATOR_CLEAR: return PictOpClear; case CAIRO_OPERATOR_SOURCE: return PictOpSrc; case CAIRO_OPERATOR_OVER: return PictOpOver; case CAIRO_OPERATOR_IN: return PictOpIn; case CAIRO_OPERATOR_OUT: return PictOpOut; case CAIRO_OPERATOR_ATOP: return PictOpAtop; case CAIRO_OPERATOR_DEST: return PictOpDst; case CAIRO_OPERATOR_DEST_OVER: return PictOpOverReverse; case CAIRO_OPERATOR_DEST_IN: return PictOpInReverse; case CAIRO_OPERATOR_DEST_OUT: return PictOpOutReverse; case CAIRO_OPERATOR_DEST_ATOP: return PictOpAtopReverse; case CAIRO_OPERATOR_XOR: return PictOpXor; case CAIRO_OPERATOR_ADD: return PictOpAdd; case CAIRO_OPERATOR_SATURATE: return PictOpSaturate; #if RENDER_MAJOR > 0 || RENDER_MINOR >= 11 case CAIRO_OPERATOR_MULTIPLY: return PictOpMultiply; case CAIRO_OPERATOR_SCREEN: return PictOpScreen; case CAIRO_OPERATOR_OVERLAY: return PictOpOverlay; case CAIRO_OPERATOR_DARKEN: return PictOpDarken; case CAIRO_OPERATOR_LIGHTEN: return PictOpLighten; case CAIRO_OPERATOR_COLOR_DODGE: return PictOpColorDodge; case CAIRO_OPERATOR_COLOR_BURN: return PictOpColorBurn; case CAIRO_OPERATOR_HARD_LIGHT: return PictOpHardLight; case CAIRO_OPERATOR_SOFT_LIGHT: return PictOpSoftLight; case CAIRO_OPERATOR_DIFFERENCE: return PictOpDifference; case CAIRO_OPERATOR_EXCLUSION: return PictOpExclusion; case CAIRO_OPERATOR_HSL_HUE: return PictOpHSLHue; case CAIRO_OPERATOR_HSL_SATURATION: return PictOpHSLSaturation; case CAIRO_OPERATOR_HSL_COLOR: return PictOpHSLColor; case CAIRO_OPERATOR_HSL_LUMINOSITY: return PictOpHSLLuminosity; #else 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: /* silence the compiler */ #endif default: ASSERT_NOT_REACHED; return PictOpOver; } } static cairo_int_status_t _cairo_xlib_surface_acquire_pattern_surface (cairo_xlib_surface_t *dst, const cairo_pattern_t *pattern, cairo_content_t content, int x, int y, int width, int height, cairo_xlib_surface_t **surface_out, cairo_surface_attributes_t *attributes) { switch (pattern->type) { case CAIRO_PATTERN_TYPE_LINEAR: case CAIRO_PATTERN_TYPE_RADIAL: { cairo_gradient_pattern_t *gradient = (cairo_gradient_pattern_t *) pattern; cairo_matrix_t matrix = pattern->matrix; cairo_xlib_surface_t *surface; char buf[CAIRO_STACK_BUFFER_SIZE]; XFixed *stops; XRenderColor *colors; XRenderPictFormat *format; Picture picture; unsigned int i; if (dst->buggy_gradients) break; if (gradient->n_stops < 2) /* becomes a solid */ break; if (gradient->n_stops < sizeof (buf) / (sizeof (XFixed) + sizeof (XRenderColor))) { stops = (XFixed *) buf; } else { stops = _cairo_malloc_ab (gradient->n_stops, sizeof (XFixed) + sizeof (XRenderColor)); if (unlikely (stops == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); } colors = (XRenderColor *) (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; } #if 0 /* For some weird reason the X server is sometimes getting * CreateGradient requests with bad length. So far I've only seen * XRenderCreateLinearGradient request with 4 stops sometime end up * with length field matching 0 stops at the server side. I've * looked at the libXrender code and I can't see anything that * could cause this behavior. However, for some reason having a * XSync call here seems to avoid the issue so I'll keep it here * until it's solved. */ XSync (dst->dpy, False); #endif if (pattern->type == CAIRO_PATTERN_TYPE_LINEAR) { cairo_linear_pattern_t *linear = (cairo_linear_pattern_t *) pattern; XLinearGradient grad; cairo_fixed_t xdim, ydim; xdim = linear->p2.x - linear->p1.x; ydim = linear->p2.y - linear->p1.y; /* * Transform the matrix to avoid overflow when converting between * cairo_fixed_t and pixman_fixed_t (without incurring performance * loss when the transformation is unnecessary). * * XXX: Consider converting out-of-range co-ordinates and transforms. * Having a function to compute the required transformation to * "normalize" a given bounding box would be generally useful - * cf linear patterns, gradient patterns, surface patterns... */ #define PIXMAN_MAX_INT ((pixman_fixed_1 >> 1) - pixman_fixed_e) /* need to ensure deltas also fit */ if (_cairo_fixed_integer_ceil (xdim) > PIXMAN_MAX_INT || _cairo_fixed_integer_ceil (ydim) > PIXMAN_MAX_INT) { double sf; if (xdim > ydim) sf = PIXMAN_MAX_INT / _cairo_fixed_to_double (xdim); else sf = PIXMAN_MAX_INT / _cairo_fixed_to_double (ydim); grad.p1.x = _cairo_fixed_16_16_from_double (_cairo_fixed_to_double (linear->p1.x) * sf); grad.p1.y = _cairo_fixed_16_16_from_double (_cairo_fixed_to_double (linear->p1.y) * sf); grad.p2.x = _cairo_fixed_16_16_from_double (_cairo_fixed_to_double (linear->p2.x) * sf); grad.p2.y = _cairo_fixed_16_16_from_double (_cairo_fixed_to_double (linear->p2.y) * sf); cairo_matrix_scale (&matrix, sf, sf); } else { grad.p1.x = _cairo_fixed_to_16_16 (linear->p1.x); grad.p1.y = _cairo_fixed_to_16_16 (linear->p1.y); grad.p2.x = _cairo_fixed_to_16_16 (linear->p2.x); grad.p2.y = _cairo_fixed_to_16_16 (linear->p2.y); } picture = XRenderCreateLinearGradient (dst->dpy, &grad, stops, colors, gradient->n_stops); } else { cairo_radial_pattern_t *radial = (cairo_radial_pattern_t *) pattern; XRadialGradient grad; grad.inner.x = _cairo_fixed_to_16_16 (radial->c1.x); grad.inner.y = _cairo_fixed_to_16_16 (radial->c1.y); grad.inner.radius = _cairo_fixed_to_16_16 (radial->r1); grad.outer.x = _cairo_fixed_to_16_16 (radial->c2.x); grad.outer.y = _cairo_fixed_to_16_16 (radial->c2.y); grad.outer.radius = _cairo_fixed_to_16_16 (radial->r2); picture = XRenderCreateRadialGradient (dst->dpy, &grad, stops, colors, gradient->n_stops); } if (stops != (XFixed *) buf) free (stops); if (unlikely (picture == None)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); /* Wrap the remote Picture in an xlib surface. */ format = _cairo_xlib_display_get_xrender_format (dst->display, CAIRO_FORMAT_ARGB32); surface = (cairo_xlib_surface_t *) _cairo_xlib_surface_create_internal (dst->dpy, None, dst->screen, NULL, format, 0, 0, 32); if (unlikely (surface->base.status)) { XRenderFreePicture (dst->dpy, picture); return surface->base.status; } surface->src_picture = picture; attributes->matrix = matrix; attributes->extend = pattern->extend; attributes->filter = CAIRO_FILTER_NEAREST; attributes->x_offset = 0; attributes->y_offset = 0; *surface_out = surface; return CAIRO_STATUS_SUCCESS; } default: ASSERT_NOT_REACHED; case CAIRO_PATTERN_TYPE_SOLID: case CAIRO_PATTERN_TYPE_SURFACE: break; } return _cairo_pattern_acquire_surface (pattern, &dst->base, content, x, y, width, height, dst->buggy_pad_reflect ? CAIRO_PATTERN_ACQUIRE_NO_REFLECT : CAIRO_PATTERN_ACQUIRE_NONE, (cairo_surface_t **) surface_out, attributes); } static cairo_int_status_t _cairo_xlib_surface_acquire_pattern_surfaces (cairo_xlib_surface_t *dst, const cairo_pattern_t *src, const cairo_pattern_t *mask, cairo_content_t src_content, int src_x, int src_y, int mask_x, int mask_y, unsigned int width, unsigned int height, cairo_xlib_surface_t **src_out, cairo_xlib_surface_t **mask_out, cairo_surface_attributes_t *src_attr, cairo_surface_attributes_t *mask_attr) { if (! dst->buggy_gradients && (src->type == CAIRO_PATTERN_TYPE_LINEAR || src->type == CAIRO_PATTERN_TYPE_RADIAL || (mask && (mask->type == CAIRO_PATTERN_TYPE_LINEAR || mask->type == CAIRO_PATTERN_TYPE_RADIAL)))) { cairo_int_status_t status; status = _cairo_xlib_surface_acquire_pattern_surface (dst, src, src_content, src_x, src_y, width, height, src_out, src_attr); if (unlikely (status)) return status; if (mask) { status = _cairo_xlib_surface_acquire_pattern_surface (dst, mask, CAIRO_CONTENT_ALPHA, mask_x, mask_y, width, height, mask_out, mask_attr); if (unlikely (status)) { _cairo_pattern_release_surface (src, &(*src_out)->base, src_attr); return status; } } else { *mask_out = NULL; } return CAIRO_STATUS_SUCCESS; } return _cairo_pattern_acquire_surfaces (src, mask, &dst->base, src_content, src_x, src_y, mask_x, mask_y, width, height, dst->buggy_pad_reflect ? CAIRO_PATTERN_ACQUIRE_NO_REFLECT : CAIRO_PATTERN_ACQUIRE_NONE, (cairo_surface_t **) src_out, (cairo_surface_t **) mask_out, src_attr, mask_attr); } static cairo_int_status_t _cairo_xlib_surface_composite (cairo_operator_t op, const cairo_pattern_t *src_pattern, const cairo_pattern_t *mask_pattern, void *abstract_dst, int src_x, int src_y, int mask_x, int mask_y, int dst_x, int dst_y, unsigned int width, unsigned int height, cairo_region_t *clip_region) { cairo_surface_attributes_t src_attr, mask_attr; cairo_xlib_surface_t *dst = abstract_dst; cairo_xlib_surface_t *src; cairo_xlib_surface_t *mask; cairo_int_status_t status; composite_operation_t operation; int itx, ity; cairo_bool_t is_integer_translation; cairo_bool_t needs_alpha_composite; cairo_content_t src_content; if (mask_pattern != NULL && ! CAIRO_SURFACE_RENDER_HAS_COMPOSITE (dst)) return UNSUPPORTED ("no support for masks"); operation = _categorize_composite_operation (dst, op, src_pattern, mask_pattern != NULL); if (operation == DO_UNSUPPORTED) return UNSUPPORTED ("unsupported operation"); needs_alpha_composite = _operator_needs_alpha_composite (op, _surface_has_alpha (dst), ! _cairo_pattern_is_opaque (src_pattern)); src_content = CAIRO_CONTENT_COLOR_ALPHA; if (! needs_alpha_composite) src_content &= ~CAIRO_CONTENT_ALPHA; _cairo_xlib_display_notify (dst->display); status = _cairo_xlib_surface_acquire_pattern_surfaces (dst, src_pattern, mask_pattern, src_content, src_x, src_y, mask_x, mask_y, width, height, &src, &mask, &src_attr, &mask_attr); if (unlikely (status)) return status; /* check for fallback surfaces that we cannot handle ... */ assert (_cairo_surface_is_xlib (&src->base)); assert (mask == NULL || _cairo_surface_is_xlib (&mask->base)); if (mask != NULL && ! CAIRO_SURFACE_RENDER_HAS_COMPOSITE (mask)) { status = UNSUPPORTED ("unsupported mask"); goto BAIL; } operation = _recategorize_composite_operation (dst, op, src, &src_attr, mask_pattern != NULL); if (operation == DO_UNSUPPORTED) { status = UNSUPPORTED ("unsupported operation"); goto BAIL; } status = _cairo_xlib_surface_set_clip_region (dst, clip_region); if (unlikely (status)) goto BAIL; switch (operation) { case DO_RENDER: status = _cairo_xlib_surface_set_attributes (src, &src_attr, dst_x + width / 2., dst_y + height / 2.); if (unlikely (status)) goto BAIL; _cairo_xlib_surface_ensure_dst_picture (dst); if (mask) { status = _cairo_xlib_surface_set_attributes (mask, &mask_attr, dst_x + width / 2., dst_y + height/ 2.); if (unlikely (status)) goto BAIL; XRenderComposite (dst->dpy, _render_operator (op), src->src_picture, mask->src_picture, dst->dst_picture, src_x + src_attr.x_offset, src_y + src_attr.y_offset, mask_x + mask_attr.x_offset, mask_y + mask_attr.y_offset, dst_x, dst_y, width, height); } else { XRenderComposite (dst->dpy, _render_operator (op), src->src_picture, 0, dst->dst_picture, src_x + src_attr.x_offset, src_y + src_attr.y_offset, 0, 0, dst_x, dst_y, width, height); } break; case DO_XCOPYAREA: status = _cairo_xlib_surface_ensure_gc (dst, TRUE); if (unlikely (status)) goto BAIL; is_integer_translation = _cairo_matrix_is_integer_translation (&src_attr.matrix, &itx, &ity); /* This is a pre-condition for DO_XCOPYAREA. */ assert (is_integer_translation); XCopyArea (dst->dpy, src->drawable, dst->drawable, dst->gc, src_x + src_attr.x_offset + itx, src_y + src_attr.y_offset + ity, width, height, dst_x, dst_y); _cairo_xlib_surface_maybe_put_gc (dst); break; case DO_XTILE: /* This case is only used for bug fallbacks, though we also use it for * the case where we don't have the RENDER extension, by forcing * buggy_repeat to TRUE. * * We've checked that we have a repeating unscaled source in * _recategorize_composite_operation. */ status = _cairo_xlib_surface_ensure_gc (dst, TRUE); if (unlikely (status)) goto BAIL; is_integer_translation = _cairo_matrix_is_integer_translation (&src_attr.matrix, &itx, &ity); /* This is a pre-condition for DO_XTILE. */ assert (is_integer_translation); XSetTSOrigin (dst->dpy, dst->gc, - (itx + src_attr.x_offset), - (ity + src_attr.y_offset)); XSetTile (dst->dpy, dst->gc, src->drawable); XSetFillStyle (dst->dpy, dst->gc, FillTiled); XFillRectangle (dst->dpy, dst->drawable, dst->gc, dst_x, dst_y, width, height); _cairo_xlib_surface_maybe_put_gc (dst); break; case DO_UNSUPPORTED: default: ASSERT_NOT_REACHED; } if (!_cairo_operator_bounded_by_source (op)) status = _cairo_surface_composite_fixup_unbounded (&dst->base, &src_attr, src->width, src->height, mask ? &mask_attr : NULL, mask ? mask->width : 0, mask ? mask->height : 0, src_x, src_y, mask_x, mask_y, dst_x, dst_y, width, height, clip_region); BAIL: if (mask) _cairo_pattern_release_surface (mask_pattern, &mask->base, &mask_attr); _cairo_pattern_release_surface (src_pattern, &src->base, &src_attr); return status; } /* XXX move this out of core and into acquire_pattern_surface() above. */ static cairo_int_status_t _cairo_xlib_surface_solid_fill_rectangles (cairo_xlib_surface_t *surface, const cairo_color_t *color, cairo_rectangle_int_t *rects, int num_rects) { cairo_status_t status; cairo_solid_pattern_t solid; cairo_surface_t *solid_surface = NULL; cairo_surface_attributes_t attrs; int i; _cairo_pattern_init_solid (&solid, color, CAIRO_CONTENT_COLOR); status = _cairo_xlib_surface_ensure_gc (surface, FALSE); if (unlikely (status)) return status; status = _cairo_pattern_acquire_surface (&solid.base, &surface->base, CAIRO_CONTENT_COLOR_ALPHA, 0, 0, ARRAY_LENGTH (dither_pattern[0]), ARRAY_LENGTH (dither_pattern), CAIRO_PATTERN_ACQUIRE_NONE, &solid_surface, &attrs); if (unlikely (status)) return status; assert (_cairo_surface_is_xlib (solid_surface)); XSetTSOrigin (surface->dpy, surface->gc, - (surface->base.device_transform.x0 + attrs.x_offset), - (surface->base.device_transform.y0 + attrs.y_offset)); XSetTile (surface->dpy, surface->gc, ((cairo_xlib_surface_t *) solid_surface)->drawable); XSetFillStyle (surface->dpy, surface->gc, FillTiled); for (i = 0; i < num_rects; i++) { XFillRectangle (surface->dpy, surface->drawable, surface->gc, rects[i].x, rects[i].y, rects[i].width, rects[i].height); } _cairo_xlib_surface_maybe_put_gc (surface); _cairo_pattern_release_surface (&solid.base, solid_surface, &attrs); return CAIRO_STATUS_SUCCESS; } static cairo_int_status_t _cairo_xlib_surface_fill_rectangles (void *abstract_surface, cairo_operator_t op, const cairo_color_t *color, cairo_rectangle_int_t *rects, int num_rects) { cairo_xlib_surface_t *surface = abstract_surface; XRenderColor render_color; cairo_status_t status; int i; _cairo_xlib_display_notify (surface->display); if (!CAIRO_SURFACE_RENDER_SUPPORTS_OPERATOR (surface, op)) return CAIRO_INT_STATUS_UNSUPPORTED; if (!CAIRO_SURFACE_RENDER_HAS_FILL_RECTANGLES (surface)) { if (op == CAIRO_OPERATOR_CLEAR || ((op == CAIRO_OPERATOR_SOURCE || op == CAIRO_OPERATOR_OVER) && CAIRO_COLOR_IS_OPAQUE (color))) { return _cairo_xlib_surface_solid_fill_rectangles (surface, color, rects, num_rects); } return UNSUPPORTED ("no support for FillRectangles with this op"); } render_color.red = color->red_short; render_color.green = color->green_short; render_color.blue = color->blue_short; render_color.alpha = color->alpha_short; status = _cairo_xlib_surface_set_clip_region (surface, NULL); assert (status == CAIRO_STATUS_SUCCESS); _cairo_xlib_surface_ensure_dst_picture (surface); if (num_rects == 1) { /* Take advantage of the protocol compaction that libXrender performs * to amalgamate sequences of XRenderFillRectangle(). */ XRenderFillRectangle (surface->dpy, _render_operator (op), surface->dst_picture, &render_color, rects->x, rects->y, rects->width, rects->height); } else { XRectangle static_xrects[CAIRO_STACK_ARRAY_LENGTH (XRectangle)]; XRectangle *xrects = static_xrects; if (num_rects > ARRAY_LENGTH (static_xrects)) { xrects = _cairo_malloc_ab (num_rects, sizeof (XRectangle)); if (unlikely (xrects == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); } for (i = 0; i < num_rects; i++) { xrects[i].x = rects[i].x; xrects[i].y = rects[i].y; xrects[i].width = rects[i].width; xrects[i].height = rects[i].height; } XRenderFillRectangles (surface->dpy, _render_operator (op), surface->dst_picture, &render_color, xrects, num_rects); if (xrects != static_xrects) free (xrects); } return CAIRO_STATUS_SUCCESS; } /* Creates an A8 picture of size @width x @height, initialized with @color */ static Picture _create_a8_picture (cairo_xlib_surface_t *surface, XRenderColor *color, int width, int height, cairo_bool_t repeat) { XRenderPictureAttributes pa; unsigned long mask = 0; Pixmap pixmap; Picture picture; XRenderPictFormat *xrender_format; if (width > XLIB_COORD_MAX || height > XLIB_COORD_MAX) return None; xrender_format = _cairo_xlib_display_get_xrender_format (surface->display, CAIRO_FORMAT_A8); if (xrender_format == NULL) return None; pixmap = XCreatePixmap (surface->dpy, surface->drawable, width <= 0 ? 1 : width, height <= 0 ? 1 : height, 8); if (repeat) { pa.repeat = TRUE; mask = CPRepeat; } picture = XRenderCreatePicture (surface->dpy, pixmap, xrender_format, mask, &pa); XRenderFillRectangle (surface->dpy, PictOpSrc, picture, color, 0, 0, width, height); XFreePixmap (surface->dpy, pixmap); return picture; } /* Creates a temporary mask for the trapezoids covering the area * [@dst_x, @dst_y, @width, @height] of the destination surface. */ static Picture _create_trapezoid_mask (cairo_xlib_surface_t *dst, cairo_trapezoid_t *traps, int num_traps, int dst_x, int dst_y, int width, int height, XRenderPictFormat *pict_format) { XRenderColor transparent = { 0, 0, 0, 0 }; XRenderColor solid = { 0xffff, 0xffff, 0xffff, 0xffff }; Picture mask_picture, solid_picture; XTrapezoid *offset_traps; int i; /* This would be considerably simpler using XRenderAddTraps(), but since * we are only using this in the unbounded-operator case, we stick with * XRenderCompositeTrapezoids, which is available on older versions * of RENDER rather than conditionalizing. We should still hit an * optimization that avoids creating another intermediate surface on * the servers that have XRenderAddTraps(). */ mask_picture = _create_a8_picture (dst, &transparent, width, height, FALSE); if (mask_picture == None || num_traps == 0) return mask_picture; offset_traps = _cairo_malloc_ab (num_traps, sizeof (XTrapezoid)); if (!offset_traps) { XRenderFreePicture (dst->dpy, mask_picture); _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return None; } for (i = 0; i < num_traps; i++) { offset_traps[i].top = _cairo_fixed_to_16_16(traps[i].top) - 0x10000 * dst_y; offset_traps[i].bottom = _cairo_fixed_to_16_16(traps[i].bottom) - 0x10000 * dst_y; offset_traps[i].left.p1.x = _cairo_fixed_to_16_16(traps[i].left.p1.x) - 0x10000 * dst_x; offset_traps[i].left.p1.y = _cairo_fixed_to_16_16(traps[i].left.p1.y) - 0x10000 * dst_y; offset_traps[i].left.p2.x = _cairo_fixed_to_16_16(traps[i].left.p2.x) - 0x10000 * dst_x; offset_traps[i].left.p2.y = _cairo_fixed_to_16_16(traps[i].left.p2.y) - 0x10000 * dst_y; offset_traps[i].right.p1.x = _cairo_fixed_to_16_16(traps[i].right.p1.x) - 0x10000 * dst_x; offset_traps[i].right.p1.y = _cairo_fixed_to_16_16(traps[i].right.p1.y) - 0x10000 * dst_y; offset_traps[i].right.p2.x = _cairo_fixed_to_16_16(traps[i].right.p2.x) - 0x10000 * dst_x; offset_traps[i].right.p2.y = _cairo_fixed_to_16_16(traps[i].right.p2.y) - 0x10000 * dst_y; } solid_picture = _create_a8_picture (dst, &solid, width, height, TRUE); if (solid_picture == None) { XRenderFreePicture (dst->dpy, mask_picture); free (offset_traps); return None; } XRenderCompositeTrapezoids (dst->dpy, PictOpAdd, solid_picture, mask_picture, pict_format, 0, 0, offset_traps, num_traps); XRenderFreePicture (dst->dpy, solid_picture); free (offset_traps); return mask_picture; } static cairo_int_status_t _cairo_xlib_surface_composite_trapezoids (cairo_operator_t op, const cairo_pattern_t *pattern, void *abstract_dst, cairo_antialias_t antialias, int src_x, int src_y, int dst_x, int dst_y, unsigned int width, unsigned int height, cairo_trapezoid_t *traps, int num_traps, cairo_region_t *clip_region) { cairo_surface_attributes_t attributes; cairo_xlib_surface_t *dst = abstract_dst; cairo_xlib_surface_t *src; cairo_int_status_t status; composite_operation_t operation; int render_reference_x, render_reference_y; int render_src_x, render_src_y; XRenderPictFormat *pict_format; _cairo_xlib_display_notify (dst->display); if (! CAIRO_SURFACE_RENDER_HAS_TRAPEZOIDS (dst)) return UNSUPPORTED ("XRender does not support CompositeTrapezoids"); operation = _categorize_composite_operation (dst, op, pattern, TRUE); if (operation == DO_UNSUPPORTED) return UNSUPPORTED ("unsupported operation"); status = _cairo_xlib_surface_acquire_pattern_surface (dst, pattern, CAIRO_CONTENT_COLOR_ALPHA, src_x, src_y, width, height, &src, &attributes); if (unlikely (status)) return status; operation = _recategorize_composite_operation (dst, op, src, &attributes, TRUE); if (operation == DO_UNSUPPORTED) { status = UNSUPPORTED ("unsupported operation"); goto BAIL; } switch (antialias) { case CAIRO_ANTIALIAS_NONE: pict_format = _cairo_xlib_display_get_xrender_format (dst->display, CAIRO_FORMAT_A1); break; case CAIRO_ANTIALIAS_GRAY: case CAIRO_ANTIALIAS_SUBPIXEL: case CAIRO_ANTIALIAS_DEFAULT: default: pict_format = _cairo_xlib_display_get_xrender_format (dst->display, CAIRO_FORMAT_A8); break; } if (traps[0].left.p1.y < traps[0].left.p2.y) { render_reference_x = _cairo_fixed_integer_floor (traps[0].left.p1.x); render_reference_y = _cairo_fixed_integer_floor (traps[0].left.p1.y); } else { render_reference_x = _cairo_fixed_integer_floor (traps[0].left.p2.x); render_reference_y = _cairo_fixed_integer_floor (traps[0].left.p2.y); } render_src_x = src_x + render_reference_x - dst_x; render_src_y = src_y + render_reference_y - dst_y; status = _cairo_xlib_surface_set_clip_region (dst, clip_region); if (unlikely (status)) goto BAIL; _cairo_xlib_surface_ensure_dst_picture (dst); status = _cairo_xlib_surface_set_attributes (src, &attributes, dst_x + width / 2., dst_y + height / 2.); if (unlikely (status)) goto BAIL; if (!_cairo_operator_bounded_by_mask (op)) { /* XRenderCompositeTrapezoids() creates a mask only large enough for the * trapezoids themselves, but if the operator is unbounded, then we need * to actually composite all the way out to the bounds, so we create * the mask and composite ourselves. There actually would * be benefit to doing this in all cases, since RENDER implementations * will frequently create a too temporary big mask, ignoring destination * bounds and clip. (XRenderAddTraps() could be used to make creating * the mask somewhat cheaper.) */ Picture mask_picture = _create_trapezoid_mask (dst, traps, num_traps, dst_x, dst_y, width, height, pict_format); if (!mask_picture) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto BAIL; } XRenderComposite (dst->dpy, _render_operator (op), src->src_picture, mask_picture, dst->dst_picture, src_x + attributes.x_offset, src_y + attributes.y_offset, 0, 0, dst_x, dst_y, width, height); XRenderFreePicture (dst->dpy, mask_picture); status = _cairo_surface_composite_shape_fixup_unbounded (&dst->base, &attributes, src->width, src->height, width, height, src_x, src_y, 0, 0, dst_x, dst_y, width, height, clip_region); } else { XTrapezoid xtraps_stack[CAIRO_STACK_ARRAY_LENGTH (XTrapezoid)]; XTrapezoid *xtraps = xtraps_stack; int i; if (num_traps > ARRAY_LENGTH (xtraps_stack)) { xtraps = _cairo_malloc_ab (num_traps, sizeof (XTrapezoid)); if (unlikely (xtraps == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto BAIL; } } for (i = 0; i < num_traps; i++) { xtraps[i].top = _cairo_fixed_to_16_16(traps[i].top); xtraps[i].bottom = _cairo_fixed_to_16_16(traps[i].bottom); xtraps[i].left.p1.x = _cairo_fixed_to_16_16(traps[i].left.p1.x); xtraps[i].left.p1.y = _cairo_fixed_to_16_16(traps[i].left.p1.y); xtraps[i].left.p2.x = _cairo_fixed_to_16_16(traps[i].left.p2.x); xtraps[i].left.p2.y = _cairo_fixed_to_16_16(traps[i].left.p2.y); xtraps[i].right.p1.x = _cairo_fixed_to_16_16(traps[i].right.p1.x); xtraps[i].right.p1.y = _cairo_fixed_to_16_16(traps[i].right.p1.y); xtraps[i].right.p2.x = _cairo_fixed_to_16_16(traps[i].right.p2.x); xtraps[i].right.p2.y = _cairo_fixed_to_16_16(traps[i].right.p2.y); } XRenderCompositeTrapezoids (dst->dpy, _render_operator (op), src->src_picture, dst->dst_picture, pict_format, render_src_x + attributes.x_offset, render_src_y + attributes.y_offset, xtraps, num_traps); if (xtraps != xtraps_stack) free(xtraps); } BAIL: _cairo_pattern_release_surface (pattern, &src->base, &attributes); return status; } static cairo_bool_t _cairo_xlib_surface_get_extents (void *abstract_surface, cairo_rectangle_int_t *rectangle) { cairo_xlib_surface_t *surface = abstract_surface; rectangle->x = 0; rectangle->y = 0; rectangle->width = surface->width; rectangle->height = surface->height; return TRUE; } static void _cairo_xlib_surface_get_font_options (void *abstract_surface, cairo_font_options_t *options) { cairo_xlib_surface_t *surface = abstract_surface; *options = *_cairo_xlib_screen_get_font_options (surface->screen_info); } static void _cairo_xlib_surface_scaled_font_fini (cairo_scaled_font_t *scaled_font); static void _cairo_xlib_surface_scaled_glyph_fini (cairo_scaled_glyph_t *scaled_glyph, cairo_scaled_font_t *scaled_font); static cairo_bool_t _cairo_xlib_surface_is_similar (void *surface_a, void *surface_b, cairo_content_t content) { cairo_xlib_surface_t *a = surface_a; cairo_xlib_surface_t *b = surface_b; XRenderPictFormat *xrender_format = b->xrender_format; if (! _cairo_xlib_surface_same_screen (a, b)) return FALSE; /* now inspect the content to check that a is similar to b */ if (xrender_format == NULL && b->visual != NULL) xrender_format = XRenderFindVisualFormat (b->dpy, b->visual); if (xrender_format == NULL || _xrender_format_to_content (xrender_format) != content) { xrender_format = _cairo_xlib_display_get_xrender_format ( b->display, _cairo_format_from_content (content)); } return a->xrender_format == xrender_format; } static const cairo_surface_backend_t cairo_xlib_surface_backend = { CAIRO_SURFACE_TYPE_XLIB, _cairo_xlib_surface_create_similar, _cairo_xlib_surface_finish, _cairo_xlib_surface_acquire_source_image, _cairo_xlib_surface_release_source_image, _cairo_xlib_surface_acquire_dest_image, _cairo_xlib_surface_release_dest_image, _cairo_xlib_surface_clone_similar, _cairo_xlib_surface_composite, _cairo_xlib_surface_fill_rectangles, _cairo_xlib_surface_composite_trapezoids, NULL, /* create_span_renderer */ NULL, /* check_span_renderer */ NULL, /* copy_page */ NULL, /* show_page */ _cairo_xlib_surface_get_extents, NULL, /* old_show_glyphs */ _cairo_xlib_surface_get_font_options, NULL, /* flush */ NULL, /* mark_dirty_rectangle */ _cairo_xlib_surface_scaled_font_fini, _cairo_xlib_surface_scaled_glyph_fini, NULL, /* paint */ NULL, /* mask */ NULL, /* stroke */ NULL, /* fill */ _cairo_xlib_surface_show_glyphs, _cairo_xlib_surface_snapshot, _cairo_xlib_surface_is_similar, NULL, /* fill_stroke */ _cairo_xlib_surface_create_solid_pattern_surface, _cairo_xlib_surface_can_repaint_solid_pattern_surface }; /** * _cairo_surface_is_xlib: * @surface: a #cairo_surface_t * * Checks if a surface is a #cairo_xlib_surface_t * * Return value: True if the surface is an xlib surface **/ static cairo_bool_t _cairo_surface_is_xlib (cairo_surface_t *surface) { return surface->backend == &cairo_xlib_surface_backend; } /* callback from CloseDisplay */ static void _cairo_xlib_surface_detach_display (cairo_xlib_display_t *display, void *data) { cairo_xlib_surface_t *surface = cairo_container_of (data, cairo_xlib_surface_t, close_display_hook); Display *dpy; dpy = surface->dpy; surface->dpy = NULL; if (surface->dst_picture != None) { XRenderFreePicture (dpy, surface->dst_picture); surface->dst_picture = None; } if (surface->src_picture != None) { XRenderFreePicture (dpy, surface->src_picture); surface->src_picture = None; } if (surface->owns_pixmap) { XFreePixmap (dpy, surface->drawable); surface->drawable = None; surface->owns_pixmap = FALSE; } if (surface->gc != NULL) { XFreeGC (dpy, surface->gc); surface->gc = NULL; } } static cairo_surface_t * _cairo_xlib_surface_create_internal (Display *dpy, Drawable drawable, Screen *screen, Visual *visual, XRenderPictFormat *xrender_format, int width, int height, int depth) { cairo_xlib_surface_t *surface; cairo_xlib_display_t *display; cairo_xlib_screen_info_t *screen_info; cairo_status_t status; CAIRO_MUTEX_INITIALIZE (); if (xrender_format) { depth = xrender_format->depth; /* XXX find matching visual for core/dithering fallbacks? */ } else if (visual) { int j, k; /* This is ugly, but we have to walk over all visuals * for the display to find the correct depth. */ depth = 0; for (j = 0; j < screen->ndepths; j++) { Depth *d = &screen->depths[j]; for (k = 0; k < d->nvisuals; k++) { if (&d->visuals[k] == visual) { depth = d->depth; goto found; } } } found: ; } if (depth == 0) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_VISUAL)); status = _cairo_xlib_display_get (dpy, &display); if (unlikely (status)) return _cairo_surface_create_in_error (status); status = _cairo_xlib_screen_info_get (display, screen, &screen_info); if (unlikely (status)) { _cairo_xlib_display_destroy (display); return _cairo_surface_create_in_error (status); } surface = malloc (sizeof (cairo_xlib_surface_t)); if (unlikely (surface == NULL)) { _cairo_xlib_screen_info_destroy (screen_info); _cairo_xlib_display_destroy (display); return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); } /* initialize and hook into the CloseDisplay callback */ surface->close_display_hook.func = _cairo_xlib_surface_detach_display; _cairo_xlib_add_close_display_hook (display, &surface->close_display_hook); surface->render_major = display->render_major; surface->render_minor = display->render_minor; if (CAIRO_SURFACE_RENDER_HAS_CREATE_PICTURE (surface)) { if (!xrender_format) { if (visual) { xrender_format = XRenderFindVisualFormat (dpy, visual); } else if (depth == 1) { xrender_format = _cairo_xlib_display_get_xrender_format (display, CAIRO_FORMAT_A1); } } } else { xrender_format = NULL; } /* we cannot use XRender for this surface, so ensure we don't try */ if (xrender_format == NULL) { surface->render_major = -1; surface->render_minor = -1; } _cairo_surface_init (&surface->base, &cairo_xlib_surface_backend, _xrender_format_to_content (xrender_format)); surface->dpy = dpy; surface->display = display; surface->screen_info = screen_info; surface->gc = NULL; surface->drawable = drawable; surface->screen = screen; surface->owns_pixmap = FALSE; surface->use_pixmap = 0; surface->width = width; surface->height = height; surface->buggy_repeat = screen_info->display->buggy_repeat; if (! CAIRO_SURFACE_RENDER_HAS_FILL_RECTANGLES (surface)) { /* so we can use the XTile fallback */ surface->buggy_repeat = TRUE; } surface->buggy_pad_reflect = screen_info->display->buggy_pad_reflect; if (! CAIRO_SURFACE_RENDER_HAS_EXTENDED_REPEAT (surface)) surface->buggy_pad_reflect = TRUE; surface->buggy_gradients = screen_info->display->buggy_gradients; if (! CAIRO_SURFACE_RENDER_HAS_GRADIENTS (surface)) surface->buggy_gradients = TRUE; surface->dst_picture = None; surface->src_picture = None; surface->visual = visual; surface->xrender_format = xrender_format; surface->depth = depth; surface->filter = CAIRO_FILTER_NEAREST; surface->repeat = FALSE; surface->xtransform = identity; surface->clip_region = NULL; surface->gc_has_clip_rects = FALSE; surface->clip_rects = surface->embedded_clip_rects; surface->num_clip_rects = 0; surface->clip_dirty = 0; /* * Compute the pixel format masks from either a XrenderFormat or * else from a visual; failing that we assume the drawable is an * alpha-only pixmap as it could only have been created that way * through the cairo_xlib_surface_create_for_bitmap function. */ if (xrender_format) { surface->a_mask = (unsigned long) surface->xrender_format->direct.alphaMask << surface->xrender_format->direct.alpha; surface->r_mask = (unsigned long) surface->xrender_format->direct.redMask << surface->xrender_format->direct.red; surface->g_mask = (unsigned long) surface->xrender_format->direct.greenMask << surface->xrender_format->direct.green; surface->b_mask = (unsigned long) surface->xrender_format->direct.blueMask << surface->xrender_format->direct.blue; } else if (visual) { surface->a_mask = 0; surface->r_mask = visual->red_mask; surface->g_mask = visual->green_mask; surface->b_mask = visual->blue_mask; } else { if (depth < 32) surface->a_mask = (1 << depth) - 1; else surface->a_mask = 0xffffffff; surface->r_mask = 0; surface->g_mask = 0; surface->b_mask = 0; } return (cairo_surface_t *) surface; } static Screen * _cairo_xlib_screen_from_visual (Display *dpy, Visual *visual) { int s, d, v; for (s = 0; s < ScreenCount (dpy); s++) { Screen *screen; screen = ScreenOfDisplay (dpy, s); if (visual == DefaultVisualOfScreen (screen)) return screen; for (d = 0; d < screen->ndepths; d++) { Depth *depth; depth = &screen->depths[d]; for (v = 0; v < depth->nvisuals; v++) if (visual == &depth->visuals[v]) return screen; } } return NULL; } /** * cairo_xlib_surface_create: * @dpy: an X Display * @drawable: an X Drawable, (a Pixmap or a Window) * @visual: the visual to use for drawing to @drawable. The depth * of the visual must match the depth of the drawable. * Currently, only TrueColor visuals are fully supported. * @width: the current width of @drawable. * @height: the current height of @drawable. * * Creates an Xlib surface that draws to the given drawable. * The way that colors are represented in the drawable is specified * by the provided visual. * * Note: If @drawable is a Window, then the function * cairo_xlib_surface_set_size() must be called whenever the size of the * window changes. * * When @drawable is a Window containing child windows then drawing to * the created surface will be clipped by those child windows. When * the created surface is used as a source, the contents of the * children will be included. * * Return value: the newly created surface **/ cairo_surface_t * cairo_xlib_surface_create (Display *dpy, Drawable drawable, Visual *visual, int width, int height) { Screen *screen = _cairo_xlib_screen_from_visual (dpy, visual); if (screen == NULL) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_VISUAL)); return _cairo_xlib_surface_create_internal (dpy, drawable, screen, visual, NULL, width, height, 0); } slim_hidden_def (cairo_xlib_surface_create); /** * cairo_xlib_surface_create_for_bitmap: * @dpy: an X Display * @bitmap: an X Drawable, (a depth-1 Pixmap) * @screen: the X Screen associated with @bitmap * @width: the current width of @bitmap. * @height: the current height of @bitmap. * * Creates an Xlib surface that draws to the given bitmap. * This will be drawn to as a %CAIRO_FORMAT_A1 object. * * Return value: the newly created surface **/ cairo_surface_t * cairo_xlib_surface_create_for_bitmap (Display *dpy, Pixmap bitmap, Screen *screen, int width, int height) { return _cairo_xlib_surface_create_internal (dpy, bitmap, screen, NULL, NULL, width, height, 1); } #if CAIRO_HAS_XLIB_XRENDER_SURFACE /** * cairo_xlib_surface_create_with_xrender_format: * @dpy: an X Display * @drawable: an X Drawable, (a Pixmap or a Window) * @screen: the X Screen associated with @drawable * @format: the picture format to use for drawing to @drawable. The depth * of @format must match the depth of the drawable. * @width: the current width of @drawable. * @height: the current height of @drawable. * * Creates an Xlib surface that draws to the given drawable. * The way that colors are represented in the drawable is specified * by the provided picture format. * * Note: If @drawable is a Window, then the function * cairo_xlib_surface_set_size() must be called whenever the size of the * window changes. * * Return value: the newly created surface **/ cairo_surface_t * cairo_xlib_surface_create_with_xrender_format (Display *dpy, Drawable drawable, Screen *screen, XRenderPictFormat *format, int width, int height) { return _cairo_xlib_surface_create_internal (dpy, drawable, screen, NULL, format, width, height, 0); } slim_hidden_def (cairo_xlib_surface_create_with_xrender_format); /** * cairo_xlib_surface_get_xrender_format: * @surface: an xlib surface * * Gets the X Render picture format that @surface uses for rendering with the * X Render extension. If the surface was created by * cairo_xlib_surface_create_with_xrender_format() originally, the return * value is the format passed to that constructor. * * Return value: the XRenderPictFormat* associated with @surface, * or %NULL if the surface is not an xlib surface * or if the X Render extension is not available. * * Since: 1.6 **/ XRenderPictFormat * cairo_xlib_surface_get_xrender_format (cairo_surface_t *surface) { cairo_xlib_surface_t *xlib_surface = (cairo_xlib_surface_t *) surface; /* Throw an error for a non-xlib surface */ if (! _cairo_surface_is_xlib (surface)) { _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); return NULL; } return xlib_surface->xrender_format; } #endif /** * cairo_xlib_surface_set_size: * @surface: a #cairo_surface_t for the XLib backend * @width: the new width of the surface * @height: the new height of the surface * * Informs cairo of the new size of the X Drawable underlying the * surface. For a surface created for a Window (rather than a Pixmap), * this function must be called each time the size of the window * changes. (For a subwindow, you are normally resizing the window * yourself, but for a toplevel window, it is necessary to listen for * ConfigureNotify events.) * * A Pixmap can never change size, so it is never necessary to call * this function on a surface created for a Pixmap. **/ void cairo_xlib_surface_set_size (cairo_surface_t *abstract_surface, int width, int height) { cairo_xlib_surface_t *surface = (cairo_xlib_surface_t *) abstract_surface; cairo_status_t status; if (! _cairo_surface_is_xlib (abstract_surface)) { status = _cairo_surface_set_error (abstract_surface, CAIRO_STATUS_SURFACE_TYPE_MISMATCH); return; } surface->width = width; surface->height = height; } /** * cairo_xlib_surface_set_drawable: * @surface: a #cairo_surface_t for the XLib backend * @drawable: the new drawable for the surface * @width: the width of the new drawable * @height: the height of the new drawable * * Informs cairo of a new X Drawable underlying the * surface. The drawable must match the display, screen * and format of the existing drawable or the application * will get X protocol errors and will probably terminate. * No checks are done by this function to ensure this * compatibility. **/ void cairo_xlib_surface_set_drawable (cairo_surface_t *abstract_surface, Drawable drawable, int width, int height) { cairo_xlib_surface_t *surface = (cairo_xlib_surface_t *)abstract_surface; cairo_status_t status; if (! _cairo_surface_is_xlib (abstract_surface)) { status = _cairo_surface_set_error (abstract_surface, CAIRO_STATUS_SURFACE_TYPE_MISMATCH); return; } /* XXX: and what about this case? */ if (surface->owns_pixmap) return; if (surface->drawable != drawable) { if (surface->dst_picture != None) { status = _cairo_xlib_display_queue_resource ( surface->display, XRenderFreePicture, surface->dst_picture); if (unlikely (status)) { status = _cairo_surface_set_error (&surface->base, status); return; } surface->dst_picture = None; } if (surface->src_picture != None) { status = _cairo_xlib_display_queue_resource ( surface->display, XRenderFreePicture, surface->src_picture); if (unlikely (status)) { status = _cairo_surface_set_error (&surface->base, status); return; } surface->src_picture = None; } surface->drawable = drawable; } surface->width = width; surface->height = height; } /** * cairo_xlib_surface_get_display: * @surface: a #cairo_xlib_surface_t * * Get the X Display for the underlying X Drawable. * * Return value: the display. * * Since: 1.2 **/ Display * cairo_xlib_surface_get_display (cairo_surface_t *abstract_surface) { cairo_xlib_surface_t *surface = (cairo_xlib_surface_t *) abstract_surface; if (! _cairo_surface_is_xlib (abstract_surface)) { _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); return NULL; } return surface->dpy; } /** * cairo_xlib_surface_get_drawable: * @surface: a #cairo_xlib_surface_t * * Get the underlying X Drawable used for the surface. * * Return value: the drawable. * * Since: 1.2 **/ Drawable cairo_xlib_surface_get_drawable (cairo_surface_t *abstract_surface) { cairo_xlib_surface_t *surface = (cairo_xlib_surface_t *) abstract_surface; if (! _cairo_surface_is_xlib (abstract_surface)) { _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); return 0; } return surface->drawable; } /** * cairo_xlib_surface_get_screen: * @surface: a #cairo_xlib_surface_t * * Get the X Screen for the underlying X Drawable. * * Return value: the screen. * * Since: 1.2 **/ Screen * cairo_xlib_surface_get_screen (cairo_surface_t *abstract_surface) { cairo_xlib_surface_t *surface = (cairo_xlib_surface_t *) abstract_surface; if (! _cairo_surface_is_xlib (abstract_surface)) { _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); return NULL; } return surface->screen; } /** * cairo_xlib_surface_get_visual: * @surface: a #cairo_xlib_surface_t * * Get the X Visual used for underlying X Drawable. * * Return value: the visual. * * Since: 1.2 **/ Visual * cairo_xlib_surface_get_visual (cairo_surface_t *abstract_surface) { cairo_xlib_surface_t *surface = (cairo_xlib_surface_t *) abstract_surface; if (! _cairo_surface_is_xlib (abstract_surface)) { _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); return NULL; } return surface->visual; } /** * cairo_xlib_surface_get_depth: * @surface: a #cairo_xlib_surface_t * * Get the number of bits used to represent each pixel value. * * Return value: the depth of the surface in bits. * * Since: 1.2 **/ int cairo_xlib_surface_get_depth (cairo_surface_t *abstract_surface) { cairo_xlib_surface_t *surface = (cairo_xlib_surface_t *) abstract_surface; if (! _cairo_surface_is_xlib (abstract_surface)) { _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); return 0; } return surface->depth; } /** * cairo_xlib_surface_get_width: * @surface: a #cairo_xlib_surface_t * * Get the width of the X Drawable underlying the surface in pixels. * * Return value: the width of the surface in pixels. * * Since: 1.2 **/ int cairo_xlib_surface_get_width (cairo_surface_t *abstract_surface) { cairo_xlib_surface_t *surface = (cairo_xlib_surface_t *) abstract_surface; if (! _cairo_surface_is_xlib (abstract_surface)) { _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); return -1; } return surface->width; } /** * cairo_xlib_surface_get_height: * @surface: a #cairo_xlib_surface_t * * Get the height of the X Drawable underlying the surface in pixels. * * Return value: the height of the surface in pixels. * * Since: 1.2 **/ int cairo_xlib_surface_get_height (cairo_surface_t *abstract_surface) { cairo_xlib_surface_t *surface = (cairo_xlib_surface_t *) abstract_surface; if (! _cairo_surface_is_xlib (abstract_surface)) { _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); return -1; } return surface->height; } enum { GLYPHSET_INDEX_ARGB32, GLYPHSET_INDEX_A8, GLYPHSET_INDEX_A1, NUM_GLYPHSETS }; typedef struct _cairo_xlib_font_glyphset_free_glyphs { GlyphSet glyphset; int glyph_count; unsigned long glyph_indices[128]; } cairo_xlib_font_glyphset_free_glyphs_t; typedef struct _cairo_xlib_font_glyphset_info { GlyphSet glyphset; cairo_format_t format; XRenderPictFormat *xrender_format; cairo_xlib_font_glyphset_free_glyphs_t *pending_free_glyphs; } cairo_xlib_font_glyphset_info_t; typedef struct _cairo_xlib_surface_font_private { cairo_scaled_font_t *scaled_font; cairo_xlib_hook_t close_display_hook; cairo_xlib_display_t *display; cairo_xlib_font_glyphset_info_t glyphset_info[NUM_GLYPHSETS]; } cairo_xlib_surface_font_private_t; /* callback from CloseDisplay */ static void _cairo_xlib_surface_remove_scaled_font (cairo_xlib_display_t *display, void *data) { cairo_xlib_surface_font_private_t *font_private; cairo_scaled_font_t *scaled_font; font_private = cairo_container_of (data, cairo_xlib_surface_font_private_t, close_display_hook); scaled_font = font_private->scaled_font; CAIRO_MUTEX_LOCK (scaled_font->mutex); font_private = scaled_font->surface_private; scaled_font->surface_private = NULL; _cairo_scaled_font_reset_cache (scaled_font); CAIRO_MUTEX_UNLOCK (scaled_font->mutex); if (font_private != NULL) { Display *dpy; int i; dpy = display->display; for (i = 0; i < NUM_GLYPHSETS; i++) { cairo_xlib_font_glyphset_info_t *glyphset_info; glyphset_info = &font_private->glyphset_info[i]; if (glyphset_info->glyphset) XRenderFreeGlyphSet (dpy, glyphset_info->glyphset); if (glyphset_info->pending_free_glyphs != NULL) free (glyphset_info->pending_free_glyphs); } _cairo_xlib_display_destroy (font_private->display); free (font_private); } } static cairo_status_t _cairo_xlib_surface_font_init (Display *dpy, cairo_scaled_font_t *scaled_font) { cairo_xlib_surface_font_private_t *font_private; cairo_status_t status; int i; font_private = malloc (sizeof (cairo_xlib_surface_font_private_t)); if (unlikely (font_private == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); font_private->scaled_font = scaled_font; status = _cairo_xlib_display_get (dpy, &font_private->display); if (unlikely (status)) { free (font_private); return status; } /* initialize and hook into the CloseDisplay callback */ font_private->close_display_hook.func = _cairo_xlib_surface_remove_scaled_font; _cairo_xlib_add_close_display_hook (font_private->display, &font_private->close_display_hook); for (i = 0; i < NUM_GLYPHSETS; i++) { cairo_xlib_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 = NULL; glyphset_info->glyphset = None; glyphset_info->pending_free_glyphs = NULL; } scaled_font->surface_private = font_private; scaled_font->surface_backend = &cairo_xlib_surface_backend; return CAIRO_STATUS_SUCCESS; } static void _cairo_xlib_surface_scaled_font_fini (cairo_scaled_font_t *scaled_font) { cairo_xlib_surface_font_private_t *font_private; font_private = scaled_font->surface_private; if (font_private != NULL) { cairo_xlib_display_t *display; int i; display = font_private->display; _cairo_xlib_remove_close_display_hook (display, &font_private->close_display_hook); for (i = 0; i < NUM_GLYPHSETS; i++) { cairo_xlib_font_glyphset_info_t *glyphset_info; glyphset_info = &font_private->glyphset_info[i]; if (glyphset_info->pending_free_glyphs != NULL) free (glyphset_info->pending_free_glyphs); if (glyphset_info->glyphset) { cairo_status_t status; status = _cairo_xlib_display_queue_resource (display, XRenderFreeGlyphSet, glyphset_info->glyphset); (void) status; /* XXX cannot propagate failure */ } } _cairo_xlib_display_destroy (display); free (font_private); } } static void _cairo_xlib_render_free_glyphs (Display *dpy, cairo_xlib_font_glyphset_free_glyphs_t *to_free) { XRenderFreeGlyphs (dpy, to_free->glyphset, to_free->glyph_indices, to_free->glyph_count); } static cairo_xlib_font_glyphset_info_t * _cairo_xlib_scaled_glyph_get_glyphset_info (cairo_scaled_glyph_t *scaled_glyph) { return scaled_glyph->surface_private; } static void _cairo_xlib_scaled_glyph_set_glyphset_info (cairo_scaled_glyph_t *scaled_glyph, cairo_xlib_font_glyphset_info_t *glyphset_info) { scaled_glyph->surface_private = glyphset_info; } static void _cairo_xlib_surface_scaled_glyph_fini (cairo_scaled_glyph_t *scaled_glyph, cairo_scaled_font_t *scaled_font) { cairo_xlib_surface_font_private_t *font_private; cairo_xlib_font_glyphset_info_t *glyphset_info; if (scaled_font->finished) return; font_private = scaled_font->surface_private; glyphset_info = _cairo_xlib_scaled_glyph_get_glyphset_info (scaled_glyph); if (font_private != NULL && glyphset_info != NULL) { cairo_xlib_font_glyphset_free_glyphs_t *to_free; cairo_status_t status; to_free = glyphset_info->pending_free_glyphs; if (to_free != NULL && to_free->glyph_count == ARRAY_LENGTH (to_free->glyph_indices)) { status = _cairo_xlib_display_queue_work (font_private->display, (cairo_xlib_notify_func) _cairo_xlib_render_free_glyphs, to_free, free); /* XXX cannot propagate failure */ if (unlikely (status)) free (to_free); to_free = glyphset_info->pending_free_glyphs = NULL; } if (to_free == NULL) { to_free = malloc (sizeof (cairo_xlib_font_glyphset_free_glyphs_t)); if (unlikely (to_free == NULL)) { _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return; /* XXX cannot propagate failure */ } to_free->glyphset = glyphset_info->glyphset; to_free->glyph_count = 0; glyphset_info->pending_free_glyphs = to_free; } to_free->glyph_indices[to_free->glyph_count++] = _cairo_scaled_glyph_index (scaled_glyph); } } static cairo_bool_t _native_byte_order_lsb (void) { int x = 1; return *((char *) &x) == 1; } static cairo_xlib_font_glyphset_info_t * _cairo_xlib_scaled_font_get_glyphset_info_for_format (cairo_scaled_font_t *scaled_font, cairo_format_t format) { cairo_xlib_surface_font_private_t *font_private; cairo_xlib_font_glyphset_info_t *glyphset_info; int glyphset_index; switch (format) { default: case CAIRO_FORMAT_RGB24: case CAIRO_FORMAT_ARGB32: glyphset_index = GLYPHSET_INDEX_ARGB32; break; case CAIRO_FORMAT_A8: glyphset_index = GLYPHSET_INDEX_A8; break; case CAIRO_FORMAT_A1: glyphset_index = GLYPHSET_INDEX_A1; break; } font_private = scaled_font->surface_private; glyphset_info = &font_private->glyphset_info[glyphset_index]; if (glyphset_info->glyphset == None) { cairo_xlib_display_t *display = font_private->display; glyphset_info->xrender_format = _cairo_xlib_display_get_xrender_format (display, glyphset_info->format); glyphset_info->glyphset = XRenderCreateGlyphSet (display->display, glyphset_info->xrender_format); } return glyphset_info; } static cairo_bool_t _cairo_xlib_glyphset_info_has_pending_free_glyph ( cairo_xlib_font_glyphset_info_t *glyphset_info, unsigned long glyph_index) { if (glyphset_info->pending_free_glyphs != NULL) { cairo_xlib_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_xlib_font_glyphset_info_t * _cairo_xlib_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_xlib_surface_font_private_t *font_private; int i; font_private = scaled_font->surface_private; if (font_private == NULL) return NULL; if (surface != NULL) { switch (surface->format) { default: case CAIRO_FORMAT_RGB24: case CAIRO_FORMAT_ARGB32: i = GLYPHSET_INDEX_ARGB32; break; case CAIRO_FORMAT_A8: i = GLYPHSET_INDEX_A8; break; case CAIRO_FORMAT_A1: i = GLYPHSET_INDEX_A1; break; } if (_cairo_xlib_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_xlib_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_xlib_surface_add_glyph (Display *dpy, cairo_scaled_font_t *scaled_font, cairo_scaled_glyph_t **pscaled_glyph) { XGlyphInfo glyph_info; unsigned long glyph_index; unsigned char *data; cairo_status_t status = CAIRO_STATUS_SUCCESS; cairo_scaled_glyph_t *scaled_glyph = *pscaled_glyph; cairo_image_surface_t *glyph_surface = scaled_glyph->surface; cairo_bool_t already_had_glyph_surface; cairo_xlib_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_xlib_scaled_font_get_glyphset_info_for_pending_free_glyph (scaled_font, glyph_index, glyph_surface); if (glyphset_info != NULL) { _cairo_xlib_scaled_glyph_set_glyphset_info (scaled_glyph, glyphset_info); return CAIRO_STATUS_SUCCESS; } if (!glyph_surface) { status = _cairo_scaled_glyph_lookup (scaled_font, glyph_index, CAIRO_SCALED_GLYPH_INFO_METRICS | CAIRO_SCALED_GLYPH_INFO_SURFACE, pscaled_glyph); if (unlikely (status)) return status; scaled_glyph = *pscaled_glyph; glyph_surface = scaled_glyph->surface; already_had_glyph_surface = FALSE; } else { already_had_glyph_surface = TRUE; } if (scaled_font->surface_private == NULL) { status = _cairo_xlib_surface_font_init (dpy, scaled_font); if (unlikely (status)) return status; } glyphset_info = _cairo_xlib_scaled_font_get_glyphset_info_for_format (scaled_font, glyph_surface->format); /* XRenderAddGlyph does not handle a glyph surface larger than the extended maximum XRequest size. */ { int len = cairo_format_stride_for_width (glyphset_info->format, glyph_surface->width) * glyph_surface->height; int max_request_size = (XExtendedMaxRequestSize (dpy) ? XExtendedMaxRequestSize (dpy) : XMaxRequestSize (dpy)) * 4 - sz_xRenderAddGlyphsReq - sz_xGlyphInfo - 8; if (len >= max_request_size) return UNSUPPORTED ("glyph too large for XRequest"); } /* 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_t *cr; 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; cr = cairo_create (tmp_surface); cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR); cairo_paint (cr); status = cairo_status (cr); cairo_destroy (cr); 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 (unlikely (status)) goto BAIL; } /* 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) { cairo_t *cr; cairo_surface_t *tmp_surface; tmp_surface = cairo_image_surface_create (glyphset_info->format, glyph_surface->width, glyph_surface->height); status = tmp_surface->status; if (unlikely (status)) goto BAIL; tmp_surface->device_transform = glyph_surface->base.device_transform; tmp_surface->device_transform_inverse = glyph_surface->base.device_transform_inverse; cr = cairo_create (tmp_surface); cairo_set_source_surface (cr, &glyph_surface->base, 0, 0); cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE); cairo_paint (cr); status = cairo_status (cr); cairo_destroy (cr); glyph_surface = (cairo_image_surface_t *) tmp_surface; if (unlikely (status)) goto BAIL; } /* XXX: FRAGILE: We're ignore device_transform scaling here. A bug? */ glyph_info.x = _cairo_lround (glyph_surface->base.device_transform.x0); glyph_info.y = _cairo_lround (glyph_surface->base.device_transform.y0); glyph_info.width = glyph_surface->width; glyph_info.height = glyph_surface->height; glyph_info.xOff = scaled_glyph->x_advance; glyph_info.yOff = scaled_glyph->y_advance; data = glyph_surface->data; /* flip formats around */ switch (scaled_glyph->surface->format) { case CAIRO_FORMAT_A1: /* local bitmaps are always stored with bit == byte */ if (_native_byte_order_lsb() != (BitmapBitOrder (dpy) == LSBFirst)) { int c = glyph_surface->stride * glyph_surface->height; unsigned char *d; unsigned char *new, *n; new = malloc (c); if (!new) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto BAIL; } n = new; d = data; do { char b = *d++; b = ((b << 1) & 0xaa) | ((b >> 1) & 0x55); b = ((b << 2) & 0xcc) | ((b >> 2) & 0x33); b = ((b << 4) & 0xf0) | ((b >> 4) & 0x0f); *n++ = b; } while (--c); data = new; } break; case CAIRO_FORMAT_A8: break; case CAIRO_FORMAT_ARGB32: if (_native_byte_order_lsb() != (ImageByteOrder (dpy) == LSBFirst)) { 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++); } while (--c); data = (uint8_t *) new; } break; case CAIRO_FORMAT_RGB24: default: ASSERT_NOT_REACHED; break; } /* XXX assume X server wants pixman padding. Xft assumes this as well */ XRenderAddGlyphs (dpy, glyphset_info->glyphset, &glyph_index, &glyph_info, 1, (char *) data, glyph_surface->stride * glyph_surface->height); if (data != glyph_surface->data) free (data); _cairo_xlib_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 (eg. 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_xrender_composite_text_func_t) (Display *dpy, int op, Picture src, Picture dst, _Xconst XRenderPictFormat *maskFormat, int xSrc, int ySrc, int xDst, int yDst, _Xconst XGlyphElt8 *elts, int nelt); /* 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_xlib_glyph_t; /* compile-time assert that #cairo_xlib_glyph_t is the same size as #cairo_glyph_t */ COMPILE_TIME_ASSERT (sizeof (cairo_xlib_glyph_t) == sizeof (cairo_glyph_t)); /* 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_xlib_surface_emit_glyphs and _emit_glyph_chunks */ #define _start_new_glyph_elt(count, glyph) \ (((count) & 127) == 0 || (glyph)->i.x || (glyph)->i.y) static cairo_status_t _emit_glyphs_chunk (cairo_xlib_surface_t *dst, cairo_xlib_glyph_t *glyphs, int num_glyphs, cairo_scaled_font_t *scaled_font, cairo_operator_t op, cairo_xlib_surface_t *src, cairo_surface_attributes_t *attributes, /* info for this chunk */ int num_elts, int width, cairo_xlib_font_glyphset_info_t *glyphset_info) { /* Which XRenderCompositeText function to use */ cairo_xrender_composite_text_func_t composite_text_func; int size; /* Element buffer stuff */ XGlyphElt8 *elts; XGlyphElt8 stack_elts[CAIRO_STACK_ARRAY_LENGTH (XGlyphElt8)]; /* Reuse the input glyph array for output char generation */ char *char8 = (char *) glyphs; unsigned short *char16 = (unsigned short *) glyphs; unsigned int *char32 = (unsigned int *) glyphs; int i; int nelt; /* Element index */ int n; /* Num output glyphs in current element */ int j; /* Num output glyphs so far */ switch (width) { case 1: /* don't cast the 8-variant, to catch possible mismatches */ composite_text_func = XRenderCompositeText8; size = sizeof (char); break; case 2: composite_text_func = (cairo_xrender_composite_text_func_t) XRenderCompositeText16; size = sizeof (unsigned short); break; default: case 4: composite_text_func = (cairo_xrender_composite_text_func_t) XRenderCompositeText32; size = sizeof (unsigned int); } /* Allocate element array */ if (num_elts <= ARRAY_LENGTH (stack_elts)) { elts = stack_elts; } else { elts = _cairo_malloc_ab (num_elts, sizeof (XGlyphElt8)); if (unlikely (elts == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); } /* Fill them in */ nelt = 0; n = 0; j = 0; for (i = 0; i < num_glyphs; i++) { /* Start a new element for first output glyph, * or for any glyph that has unexpected position, * or if current element has too many glyphs. * * These same conditions are mirrored in _cairo_xlib_surface_emit_glyphs() */ if (_start_new_glyph_elt (j, &glyphs[i])) { if (j) { elts[nelt].nchars = n; nelt++; n = 0; } elts[nelt].chars = char8 + size * j; elts[nelt].glyphset = glyphset_info->glyphset; elts[nelt].xOff = glyphs[i].i.x; elts[nelt].yOff = glyphs[i].i.y; } switch (width) { case 1: char8 [j] = (char) glyphs[i].index; break; case 2: char16[j] = (unsigned short) glyphs[i].index; break; default: case 4: char32[j] = (unsigned int) glyphs[i].index; break; } n++; j++; } if (n) { elts[nelt].nchars = n; nelt++; n = 0; } /* Check that we agree with _cairo_xlib_surface_emit_glyphs() on the * expected number of xGlyphElts. */ assert (nelt == num_elts); composite_text_func (dst->dpy, _render_operator (op), src->src_picture, dst->dst_picture, glyphset_info->xrender_format, attributes->x_offset + elts[0].xOff, attributes->y_offset + elts[0].yOff, elts[0].xOff, elts[0].yOff, (XGlyphElt8 *) elts, nelt); if (elts != stack_elts) free (elts); return CAIRO_STATUS_SUCCESS; } /* sz_xGlyphtElt required alignment to a 32-bit boundary, so ensure we have * enough room for padding */ #define _cairo_sz_xGlyphElt (sz_xGlyphElt + 4) static cairo_status_t _cairo_xlib_surface_emit_glyphs (cairo_xlib_surface_t *dst, cairo_xlib_glyph_t *glyphs, int num_glyphs, cairo_scaled_font_t *scaled_font, cairo_operator_t op, cairo_xlib_surface_t *src, cairo_surface_attributes_t *attributes, int *remaining_glyphs) { int i; cairo_status_t status = CAIRO_STATUS_SUCCESS; cairo_scaled_glyph_t *scaled_glyph; cairo_fixed_t x = 0, y = 0; cairo_xlib_font_glyphset_info_t *glyphset_info = NULL, *this_glyphset_info; unsigned long max_index = 0; int width = 1; int num_elts = 0; int num_out_glyphs = 0; int max_request_size = XMaxRequestSize (dst->dpy) * 4 - MAX (sz_xRenderCompositeGlyphs8Req, MAX(sz_xRenderCompositeGlyphs16Req, sz_xRenderCompositeGlyphs32Req)); int request_size = 0; _cairo_xlib_surface_ensure_dst_picture (dst); _cairo_xlib_display_notify (dst->display); for (i = 0; i < num_glyphs; i++) { int this_x, this_y; int old_width; status = _cairo_scaled_glyph_lookup (scaled_font, glyphs[i].index, CAIRO_SCALED_GLYPH_INFO_METRICS, &scaled_glyph); if (unlikely (status)) return status; this_x = _cairo_lround (glyphs[i].d.x); this_y = _cairo_lround (glyphs[i].d.y); /* Glyph skipping: * * We skip any glyphs that have troublesome coordinates. We want * to make sure that (glyph2.x - (glyph1.x + glyph1.width)) fits in * a signed 16bit integer, otherwise it will overflow in the render * protocol. * To ensure this, we'll make sure that (glyph2.x - glyph1.x) fits in * a signed 15bit integer. The trivial option would be to allow * coordinates -8192..8192, but that's kinda dull. It probably will * take a decade or so to get monitors 8192x4096 or something. A * negative value of -8192 on the other hand, is absolutely useless. * Note that we do want to allow some negative positions. The glyph * may start off the screen but part of it make it to the screen. * Anyway, we will allow positions in the range -4096..122887. That * will buy us a few more years before this stops working. * * Update: upon seeing weird glyphs, we just return and let fallback * code do the job. */ if (((this_x+4096)|(this_y+4096))&~0x3fffu) break; /* Send unsent glyphs to the server */ if (_cairo_xlib_scaled_glyph_get_glyphset_info (scaled_glyph) == NULL) { status = _cairo_xlib_surface_add_glyph (dst->dpy, scaled_font, &scaled_glyph); if (unlikely (status)) { if (status == CAIRO_INT_STATUS_UNSUPPORTED) /* Break so we flush glyphs so far and let fallback code * handle the rest */ break; return status; } } this_glyphset_info = _cairo_xlib_scaled_glyph_get_glyphset_info (scaled_glyph); if (!glyphset_info) glyphset_info = this_glyphset_info; /* The invariant here is that we can always flush the glyphs * accumulated before this one, using old_width, and they * would fit in the request. */ old_width = width; /* Update max glyph index */ if (glyphs[i].index > max_index) { max_index = glyphs[i].index; if (max_index >= 65536) width = 4; else if (max_index >= 256) width = 2; if (width != old_width) request_size += (width - old_width) * num_out_glyphs; } /* If we will pass the max request size by adding this glyph, * flush current glyphs. Note that we account for a * possible element being added below. * * Also flush if changing glyphsets, as Xrender limits one mask * format per request, so we can either break up, or use a * wide-enough mask format. We do the former. One reason to * prefer the latter is the fact that Xserver ADDs all glyphs * to the mask first, and then composes that to final surface, * though it's not a big deal. */ if (request_size + width > max_request_size - _cairo_sz_xGlyphElt || (this_glyphset_info != glyphset_info)) { status = _emit_glyphs_chunk (dst, glyphs, i, scaled_font, op, src, attributes, num_elts, old_width, glyphset_info); if (unlikely (status)) return status; glyphs += i; num_glyphs -= i; i = 0; max_index = glyphs[i].index; width = max_index < 256 ? 1 : max_index < 65536 ? 2 : 4; request_size = 0; num_elts = 0; num_out_glyphs = 0; x = y = 0; glyphset_info = this_glyphset_info; } /* Convert absolute glyph position to relative-to-current-point * position */ glyphs[i].i.x = this_x - x; 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 (num_out_glyphs, &glyphs[i])) { num_elts++; request_size += _cairo_sz_xGlyphElt; } /* adjust current-position */ x = this_x + scaled_glyph->x_advance; y = this_y + scaled_glyph->y_advance; num_out_glyphs++; request_size += width; } if (num_elts) { status = _emit_glyphs_chunk (dst, glyphs, i, scaled_font, op, src, attributes, num_elts, width, glyphset_info); } *remaining_glyphs = num_glyphs - i; if (*remaining_glyphs != 0 && status == CAIRO_STATUS_SUCCESS) status = CAIRO_INT_STATUS_UNSUPPORTED; return status; } static cairo_bool_t _cairo_xlib_surface_owns_font (cairo_xlib_surface_t *dst, cairo_scaled_font_t *scaled_font) { cairo_xlib_surface_font_private_t *font_private; font_private = scaled_font->surface_private; if ((scaled_font->surface_backend != NULL && scaled_font->surface_backend != &cairo_xlib_surface_backend) || (font_private != NULL && font_private->display != dst->display)) { return FALSE; } return TRUE; } static cairo_int_status_t _cairo_xlib_surface_show_glyphs (void *abstract_dst, cairo_operator_t op, const cairo_pattern_t *src_pattern, cairo_glyph_t *glyphs, int num_glyphs, cairo_scaled_font_t *scaled_font, cairo_clip_t *clip, int *remaining_glyphs) { cairo_int_status_t status = CAIRO_STATUS_SUCCESS; cairo_xlib_surface_t *dst = (cairo_xlib_surface_t*) abstract_dst; composite_operation_t operation; cairo_surface_attributes_t attributes; cairo_xlib_surface_t *src = NULL; cairo_region_t *clip_region = NULL; cairo_solid_pattern_t solid_pattern; if (! CAIRO_SURFACE_RENDER_HAS_COMPOSITE_TEXT (dst)) return UNSUPPORTED ("XRender does not support CompositeText"); /* Just let unbounded operators go through the fallback code * instead of trying to do the fixups here */ if (! _cairo_operator_bounded_by_mask (op)) return UNSUPPORTED ("unsupported unbounded op"); /* Render <= 0.10 seems to have a bug with PictOpSrc and glyphs -- * the solid source seems to be multiplied by the glyph mask, and * then the entire thing is copied to the destination surface, * including the fully transparent "background" of the rectangular * glyph surface. */ if (op == CAIRO_OPERATOR_SOURCE && ! CAIRO_SURFACE_RENDER_AT_LEAST(dst, 0, 11)) { return UNSUPPORTED ("known bug in Render"); } /* We can only use our code if we either have no clip or * have a real native clip region set. If we're using * fallback clip masking, we have to go through the full * fallback path. */ if (clip != NULL) { status = _cairo_clip_get_region (clip, &clip_region); assert (status != CAIRO_INT_STATUS_NOTHING_TO_DO); if (status) return status; } operation = _categorize_composite_operation (dst, op, src_pattern, TRUE); if (operation == DO_UNSUPPORTED) return UNSUPPORTED ("unsupported op"); if (! _cairo_xlib_surface_owns_font (dst, scaled_font)) return UNSUPPORTED ("unowned font"); status = _cairo_xlib_surface_set_clip_region (dst, clip_region); if (unlikely (status)) return status; /* After passing all those tests, we're now committed to rendering * these glyphs or to fail trying. We first upload any glyphs to * the X server that it doesn't have already, then we draw * them. */ /* PictOpClear doesn't seem to work with CompositeText; it seems to ignore * the mask (the glyphs). This code below was executed as a side effect * of going through the _clip_and_composite fallback code for old_show_glyphs, * so PictOpClear was never used with CompositeText before. */ if (op == CAIRO_OPERATOR_CLEAR) { _cairo_pattern_init_solid (&solid_pattern, CAIRO_COLOR_WHITE, CAIRO_CONTENT_COLOR); src_pattern = &solid_pattern.base; op = CAIRO_OPERATOR_DEST_OUT; } if (src_pattern->type == CAIRO_PATTERN_TYPE_SOLID) { status = _cairo_pattern_acquire_surface (src_pattern, &dst->base, CAIRO_CONTENT_COLOR_ALPHA, 0, 0, 1, 1, CAIRO_PATTERN_ACQUIRE_NONE, (cairo_surface_t **) &src, &attributes); if (unlikely (status)) goto BAIL0; } else { cairo_rectangle_int_t glyph_extents; status = _cairo_scaled_font_glyph_device_extents (scaled_font, glyphs, num_glyphs, &glyph_extents, NULL); if (unlikely (status)) goto BAIL0; if (clip != NULL) { if (! _cairo_rectangle_intersect (&glyph_extents, _cairo_clip_get_extents (clip))) { goto BAIL0; } } status = _cairo_xlib_surface_acquire_pattern_surface (dst, src_pattern, dst->base.content, glyph_extents.x, glyph_extents.y, glyph_extents.width, glyph_extents.height, &src, &attributes); if (unlikely (status)) goto BAIL0; } operation = _recategorize_composite_operation (dst, op, src, &attributes, TRUE); if (operation == DO_UNSUPPORTED) { status = UNSUPPORTED ("unsupported op"); goto BAIL1; } status = _cairo_xlib_surface_set_attributes (src, &attributes, 0, 0); if (unlikely (status)) goto BAIL1; _cairo_scaled_font_freeze_cache (scaled_font); if (_cairo_xlib_surface_owns_font (dst, scaled_font)) { status = _cairo_xlib_surface_emit_glyphs (dst, (cairo_xlib_glyph_t *) glyphs, num_glyphs, scaled_font, op, src, &attributes, remaining_glyphs); } else { status = UNSUPPORTED ("unowned font"); } _cairo_scaled_font_thaw_cache (scaled_font); BAIL1: if (src) _cairo_pattern_release_surface (src_pattern, &src->base, &attributes); BAIL0: _cairo_xlib_display_notify (dst->display); return status; }