diff options
Diffstat (limited to 'src/cairo-xlib-surface.c')
-rw-r--r-- | src/cairo-xlib-surface.c | 1909 |
1 files changed, 1130 insertions, 779 deletions
diff --git a/src/cairo-xlib-surface.c b/src/cairo-xlib-surface.c index 67b4ca6..81f2ec3 100644 --- a/src/cairo-xlib-surface.c +++ b/src/cairo-xlib-surface.c @@ -39,25 +39,78 @@ * Karl Tomlinson <karlt+@karlt.net>, 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 <X11/Xutil.h> /* 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 + +#if DEBUG +#include <X11/Xlibint.h> +static void CAIRO_PRINTF_FORMAT (2, 3) +_x_bread_crumb (Display *dpy, + const char *fmt, + ...) +{ + xReq *req; + char buf[2048]; + unsigned int len, len_dwords; + va_list ap; + + va_start (ap, fmt); + len = vsnprintf (buf, sizeof (buf), fmt, ap); + va_end (ap); + + buf[len++] = '\0'; + while (len & 3) + buf[len++] = '\0'; + + LockDisplay (dpy); + GetEmptyReq (NoOperation, req); + + len_dwords = len >> 2; + SetReqLen (req, len_dwords, len_dwords); + Data (dpy, buf, len); + + UnlockDisplay (dpy); + SyncHandle (); +} +#define X_DEBUG(x) _x_bread_crumb x +#else +#define X_DEBUG(x) +#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, +_cairo_xlib_surface_create_internal (cairo_xlib_screen_t *screen, Drawable drawable, - Screen *screen, Visual *visual, XRenderPictFormat *xrender_format, int width, @@ -65,7 +118,10 @@ _cairo_xlib_surface_create_internal (Display *dpy, int depth); static cairo_status_t -_cairo_xlib_surface_ensure_gc (cairo_xlib_surface_t *surface); +_cairo_xlib_surface_get_gc (cairo_xlib_surface_t *surface, GC *gc); + +static void +_cairo_xlib_surface_put_gc (cairo_xlib_surface_t *surface, GC gc); static void _cairo_xlib_surface_ensure_src_picture (cairo_xlib_surface_t *surface); @@ -86,8 +142,12 @@ _cairo_xlib_surface_show_glyphs (void *abstract_dst, cairo_glyph_t *glyphs, int num_glyphs, cairo_scaled_font_t *scaled_font, - int *remaining_glyphs, - cairo_rectangle_int_t *extents); + 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, @@ -124,51 +184,70 @@ static const XTransform identity = { { #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) -static cairo_surface_t * -_cairo_xlib_surface_create_similar_with_format (void *abstract_src, - cairo_format_t format, - int width, - int height) +#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) + +#define CAIRO_SURFACE_RENDER_SUPPORTS_OPERATOR(surface, op) \ + ((op) <= CAIRO_OPERATOR_SATURATE || \ + (CAIRO_SURFACE_RENDER_HAS_PDF_OPERATORS(surface) && \ + (op) <= CAIRO_OPERATOR_HSL_LUMINOSITY)) + +static cairo_status_t +_cairo_xlib_surface_set_clip_region (cairo_xlib_surface_t *surface, + cairo_region_t *region) { - cairo_xlib_surface_t *src = abstract_src; - Display *dpy = src->dpy; - Pixmap pix; - cairo_xlib_surface_t *surface; - XRenderPictFormat *xrender_format; + cairo_bool_t had_clip_rects = surface->clip_region != NULL; - assert (width <= XLIB_COORD_MAX && height <= XLIB_COORD_MAX); + if (had_clip_rects == FALSE && region == NULL) + return CAIRO_STATUS_SUCCESS; - /* 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; + if (surface->clip_region == region) + return CAIRO_STATUS_SUCCESS; - xrender_format = _cairo_xlib_display_get_xrender_format (src->display, - format); - if (xrender_format == NULL) - return NULL; + if (cairo_region_equal (surface->clip_region, region)) + return CAIRO_STATUS_SUCCESS; - pix = XCreatePixmap (dpy, src->drawable, - width <= 0 ? 1 : width, height <= 0 ? 1 : height, - xrender_format->depth); + cairo_region_destroy (surface->clip_region); + surface->clip_region = cairo_region_reference (region); - surface = (cairo_xlib_surface_t *) - _cairo_xlib_surface_create_internal (dpy, pix, - src->screen, NULL, - xrender_format, - width, height, - xrender_format->depth); - if (surface->base.status) { - XFreePixmap (dpy, pix); - return &surface->base; + if (surface->clip_rects != surface->embedded_clip_rects) { + free (surface->clip_rects); + surface->clip_rects = surface->embedded_clip_rects; } + surface->num_clip_rects = 0; - surface->owns_pixmap = TRUE; + if (region != NULL) { + XRectangle *rects = NULL; + int n_rects, i; - return &surface->base; + 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_content_t @@ -203,48 +282,79 @@ _cairo_xlib_surface_create_similar (void *abstract_src, int height) { cairo_xlib_surface_t *src = abstract_src; - XRenderPictFormat *xrender_format = src->xrender_format; + 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 (! CAIRO_SURFACE_RENDER_HAS_CREATE_PICTURE (src)) + return NULL; - /* Start by examining the surface's XRenderFormat, or if it - * doesn't have one, then look one up through its visual (in the - * case of a bitmap, it won't even have that). */ - if (xrender_format == NULL && src->visual != NULL) - xrender_format = XRenderFindVisualFormat (src->dpy, src->visual); + _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. */ - if (xrender_format == NULL || - _xrender_format_to_content (xrender_format) != content) + xrender_format = src->xrender_format; + if ((xrender_format != NULL && + _xrender_format_to_content (xrender_format) == content) || + (xrender_format = + _cairo_xlib_display_get_xrender_format (src->display, + _cairo_format_from_content (content)))) { - return _cairo_xlib_surface_create_similar_with_format (abstract_src, - _cairo_format_from_content (content), - width, height); + Visual *visual; + + /* 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); + + visual = NULL; + if (xrender_format == src->xrender_format) + visual = src->visual; + + surface = (cairo_xlib_surface_t *) + _cairo_xlib_surface_create_internal (src->screen, pix, + visual, + xrender_format, + width, height, + xrender_format->depth); + } + else + { +#ifdef DEBUG_FORCE_FALLBACKS + Screen *screen = src->screen->screen; + int depth; + + /* No compatabile XRenderFormat, see if we can make an ordinary pixmap, + * so that we can still accelerate blits with XCopyArea(). */ + if (content != CAIRO_CONTENT_COLOR) + return NULL; + + depth = DefaultDepthOfScreen (screen); + + pix = XCreatePixmap (src->dpy, RootWindowOfScreen (screen), + width <= 0 ? 1 : width, height <= 0 ? 1 : height, + depth); + + surface = (cairo_xlib_surface_t *) + _cairo_xlib_surface_create_internal (src->screen, pix, + DefaultVisualOfScreen (screen), + NULL, + width, height, depth); +#else + /* No compatabile XRenderFormat, just say no. */ + return NULL; +#endif } - /* 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 (surface->base.status != CAIRO_STATUS_SUCCESS) { + if (unlikely (surface->base.status)) { XFreePixmap (src->dpy, pix); return &surface->base; } @@ -261,6 +371,8 @@ _cairo_xlib_surface_finish (void *abstract_surface) cairo_xlib_display_t *display = surface->display; cairo_status_t status = CAIRO_STATUS_SUCCESS; + X_DEBUG ((surface->dpy, "finish (drawable=%x)", (unsigned int) surface->drawable)); + if (surface->owns_pixmap) { cairo_status_t status2; @@ -268,9 +380,7 @@ _cairo_xlib_surface_finish (void *abstract_surface) status2 = _cairo_xlib_display_queue_resource (display, XRenderFreePicture, surface->dst_picture); - if (status2 == CAIRO_STATUS_SUCCESS) - surface->dst_picture = None; - else if (status == CAIRO_STATUS_SUCCESS) + if (status == CAIRO_STATUS_SUCCESS) status = status2; } @@ -278,19 +388,14 @@ _cairo_xlib_surface_finish (void *abstract_surface) status2 = _cairo_xlib_display_queue_resource (display, XRenderFreePicture, surface->src_picture); - if (status2 == CAIRO_STATUS_SUCCESS) - surface->src_picture = None; - else if (status == CAIRO_STATUS_SUCCESS) + if (status == CAIRO_STATUS_SUCCESS) status = status2; } status2 = _cairo_xlib_display_queue_resource (display, (cairo_xlib_notify_resource_func) XFreePixmap, surface->drawable); - if (status2 == CAIRO_STATUS_SUCCESS) { - surface->owns_pixmap = FALSE; - surface->drawable = None; - } else if (status == CAIRO_STATUS_SUCCESS) + if (status == CAIRO_STATUS_SUCCESS) status = status2; } else { if (surface->dst_picture != None) @@ -300,30 +405,18 @@ _cairo_xlib_surface_finish (void *abstract_surface) XRenderFreePicture (surface->dpy, surface->src_picture); } - if (surface->gc != NULL) { - cairo_status_t status2; - status2 = _cairo_xlib_screen_put_gc (surface->screen_info, - surface->depth, - surface->gc, - surface->gc_has_clip_rects); - surface->gc = NULL; - if (status == CAIRO_STATUS_SUCCESS) - status = status2; - } - 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) { + if (surface->dpy != NULL) { _cairo_xlib_remove_close_display_hook (surface->display, &surface->close_display_hook); - _cairo_xlib_display_destroy (surface->display); + surface->dpy = NULL; } - surface->dpy = NULL; + _cairo_xlib_screen_destroy (surface->screen); + + cairo_region_destroy (surface->clip_region); return status; } @@ -658,9 +751,7 @@ _get_image_surface (cairo_xlib_surface_t *surface, ximage = NULL; } - if (!ximage) - { - + 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 @@ -668,8 +759,9 @@ _get_image_surface (cairo_xlib_surface_t *surface, * temporary pixmap */ Pixmap pixmap; + GC gc; - status = _cairo_xlib_surface_ensure_gc (surface); + status = _cairo_xlib_surface_get_gc (surface, &gc); if (unlikely (status)) return status; @@ -678,7 +770,7 @@ _get_image_surface (cairo_xlib_surface_t *surface, extents.width, extents.height, surface->depth); if (pixmap) { - XCopyArea (surface->dpy, surface->drawable, pixmap, surface->gc, + XCopyArea (surface->dpy, surface->drawable, pixmap, gc, extents.x, extents.y, extents.width, extents.height, 0, 0); @@ -691,9 +783,12 @@ _get_image_surface (cairo_xlib_surface_t *surface, XFreePixmap (surface->dpy, pixmap); } + + _cairo_xlib_surface_put_gc (surface, gc); + + if (ximage == NULL) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); } - if (!ximage) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); _swap_ximage_to_native (ximage); @@ -703,10 +798,14 @@ _get_image_surface (cairo_xlib_surface_t *surface, xlib_masks.green_mask = surface->g_mask; xlib_masks.blue_mask = surface->b_mask; - if (_pixman_format_from_masks (&xlib_masks, &pixman_format) && - xlib_masks.bpp >= 24 && - ximage->bitmap_unit == 32 && - ximage->bitmap_pad == 32) + /* 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, @@ -735,7 +834,7 @@ _get_image_surface (cairo_xlib_surface_t *surface, 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; + cairo_xlib_visual_info_t *visual_info = NULL; if (surface->visual == NULL || surface->visual->class == TrueColor) { cairo_bool_t has_alpha; @@ -773,7 +872,7 @@ _get_image_surface (cairo_xlib_surface_t *surface, } else { format = CAIRO_FORMAT_RGB24; - status = _cairo_xlib_screen_get_visual_info (surface->screen_info, + status = _cairo_xlib_screen_get_visual_info (surface->screen, surface->visual, &visual_info); if (unlikely (status)) @@ -801,7 +900,7 @@ _get_image_surface (cairo_xlib_surface_t *surface, int dither_adjustment = dither_row[x_off]; in_pixel = XGetPixel (ximage, x, y); - if (surface->visual == NULL || surface->visual->class == TrueColor) { + if (visual_info == NULL) { 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 | @@ -833,8 +932,7 @@ _get_image_surface (cairo_xlib_surface_t *surface, static void _cairo_xlib_surface_ensure_src_picture (cairo_xlib_surface_t *surface) { - if (!surface->src_picture) - { + if (!surface->src_picture) { XRenderPictureAttributes pa; int mask = 0; @@ -851,7 +949,7 @@ _cairo_xlib_surface_ensure_src_picture (cairo_xlib_surface_t *surface) static void _cairo_xlib_surface_set_picture_clip_rects (cairo_xlib_surface_t *surface) { - if (surface->have_clip_rects) { + if (surface->clip_region != NULL) { XRenderSetPictureClipRectangles (surface->dpy, surface->dst_picture, 0, 0, surface->clip_rects, @@ -867,21 +965,6 @@ _cairo_xlib_surface_set_picture_clip_rects (cairo_xlib_surface_t *surface) } static void -_cairo_xlib_surface_set_gc_clip_rects (cairo_xlib_surface_t *surface) -{ - surface->gc_has_clip_rects = surface->have_clip_rects; - if (surface->have_clip_rects) { - 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) { @@ -896,29 +979,25 @@ _cairo_xlib_surface_ensure_dst_picture (cairo_xlib_surface_t *surface) } static cairo_status_t -_cairo_xlib_surface_ensure_gc (cairo_xlib_surface_t *surface) +_cairo_xlib_surface_get_gc (cairo_xlib_surface_t *surface, GC *gc) { - XGCValues gcv; - - if (surface->gc == NULL) { - surface->gc = _cairo_xlib_screen_get_gc (surface->screen_info, - surface->depth, - &surface->clip_dirty); - if (surface->gc == NULL) { - gcv.graphics_exposures = False; - surface->gc = XCreateGC (surface->dpy, surface->drawable, - GCGraphicsExposures, &gcv); - if (unlikely (surface->gc == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - } - - if (surface->clip_dirty & CAIRO_XLIB_SURFACE_CLIP_DIRTY_GC) - _cairo_xlib_surface_set_gc_clip_rects (surface); + *gc = _cairo_xlib_screen_get_gc (surface->screen, + surface->depth, + surface->drawable); + if (unlikely (*gc == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); return CAIRO_STATUS_SUCCESS; } +static void +_cairo_xlib_surface_put_gc (cairo_xlib_surface_t *surface, GC gc) +{ + _cairo_xlib_screen_put_gc (surface->screen, + surface->depth, + gc); +} + static cairo_status_t _draw_image_surface (cairo_xlib_surface_t *surface, cairo_image_surface_t *image, @@ -932,10 +1011,10 @@ _draw_image_surface (cairo_xlib_surface_t *surface, XImage ximage; cairo_format_masks_t image_masks; int native_byte_order = _native_byte_order_lsb () ? LSBFirst : MSBFirst; + pixman_image_t *pixman_image = NULL; cairo_status_t status; cairo_bool_t own_data; - - _pixman_format_to_masks (image->pixman_format, &image_masks); + GC gc; ximage.width = image->width; ximage.height = image->height; @@ -950,10 +1029,48 @@ _draw_image_surface (cairo_xlib_surface_t *surface, 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)) + if (!_pixman_format_to_masks (image->pixman_format, &image_masks)) + { + pixman_format_code_t intermediate_format; + int ret; + + image_masks.alpha_mask = surface->a_mask; + image_masks.red_mask = surface->r_mask; + image_masks.green_mask = surface->g_mask; + image_masks.blue_mask = surface->b_mask; + image_masks.bpp = surface->depth; + ret = _pixman_format_from_masks (&image_masks, &intermediate_format); + assert (ret); + + pixman_image = pixman_image_create_bits (intermediate_format, + image->width, + image->height, + NULL, + 0); + if (pixman_image == NULL) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + pixman_image_composite (PIXMAN_OP_SRC, + image->pixman_image, + NULL, + pixman_image, + 0, 0, + 0, 0, + 0, 0, + image->width, image->height); + + ximage.bits_per_pixel = image_masks.bpp; + ximage.data = (char *) pixman_image_get_data (pixman_image); + ximage.bytes_per_line = pixman_image_get_stride (pixman_image); + own_data = FALSE; + + ret = XInitImage (&ximage); + assert (ret != 0); + } + else 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; @@ -964,7 +1081,9 @@ _draw_image_surface (cairo_xlib_surface_t *surface, ret = XInitImage (&ximage); assert (ret != 0); - } else { + } + else + { unsigned int stride, rowstride; int x, y, x0, y0, x_off, y_off; uint32_t in_pixel, out_pixel, *row; @@ -976,15 +1095,14 @@ _draw_image_surface (cairo_xlib_surface_t *surface, cairo_bool_t true_color; int ret; - if (surface->depth > 16) { + if (surface->depth > 16) ximage.bits_per_pixel = 32; - } else if (surface->depth > 8) { + else if (surface->depth > 8) ximage.bits_per_pixel = 16; - } else if (surface->depth > 1) { + else if (surface->depth > 1) ximage.bits_per_pixel = 8; - } else { + else ximage.bits_per_pixel = 1; - } stride = CAIRO_STRIDE_FOR_WIDTH_BPP (ximage.width, ximage.bits_per_pixel); ximage.bytes_per_line = stride; @@ -1010,7 +1128,7 @@ _draw_image_surface (cairo_xlib_surface_t *surface, _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, + status = _cairo_xlib_screen_get_visual_info (surface->screen, surface->visual, &visual_info); if (unlikely (status)) @@ -1038,6 +1156,16 @@ _draw_image_surface (cairo_xlib_surface_t *surface, in_pixel = ((uint8_t*)row)[x]; else if (image_masks.bpp <= 16) in_pixel = ((uint16_t*)row)[x]; + else if (image_masks.bpp <= 24) +#ifdef WORDS_BIGENDIAN + in_pixel = ((uint8_t*)row)[3 * x] << 16 | + ((uint8_t*)row)[3 * x + 1] << 8 | + ((uint8_t*)row)[3 * x + 2]; +#else + in_pixel = ((uint8_t*)row)[3 * x] | + ((uint8_t*)row)[3 * x + 1] << 8 | + ((uint8_t*)row)[3 * x + 2] << 16; +#endif else in_pixel = row[x]; @@ -1069,19 +1197,23 @@ _draw_image_surface (cairo_xlib_surface_t *surface, } } - status = _cairo_xlib_surface_ensure_gc (surface); + status = _cairo_xlib_surface_get_gc (surface, &gc); if (unlikely (status)) goto BAIL; - XPutImage(surface->dpy, surface->drawable, surface->gc, - &ximage, src_x, src_y, dst_x, dst_y, - width, height); + XPutImage (surface->dpy, surface->drawable, gc, + &ximage, src_x, src_y, dst_x, dst_y, + width, height); + + _cairo_xlib_surface_put_gc (surface, gc); BAIL: if (own_data) free (ximage.data); + if (pixman_image) + pixman_image_unref (pixman_image); - return status; + return CAIRO_STATUS_SUCCESS; } static cairo_status_t @@ -1175,11 +1307,11 @@ _cairo_xlib_surface_release_dest_image (void *abstract_surfac * screen. Both core and Render drawing require this * when using multiple drawables in an operation. */ -static cairo_bool_t +static inline cairo_bool_t _cairo_xlib_surface_same_screen (cairo_xlib_surface_t *dst, cairo_xlib_surface_t *src) { - return dst->dpy == src->dpy && dst->screen == src->screen; + return dst->screen == src->screen; } static cairo_status_t @@ -1212,25 +1344,18 @@ _cairo_xlib_surface_clone_similar (void *abstract_surface, } } 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 CAIRO_INT_STATUS_UNSUPPORTED; + 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); + _cairo_xlib_surface_create_similar (surface, + image_src->base.content & content, + width, height); if (clone == NULL) - return CAIRO_INT_STATUS_UNSUPPORTED; + return UNSUPPORTED ("unhandled image format, no similar surface"); - if (clone->base.status) + if (unlikely (clone->base.status)) return clone->base.status; status = _draw_image_surface (clone, image_src, @@ -1277,9 +1402,6 @@ _cairo_xlib_surface_create_solid_pattern_surface (void *abstrac if (CAIRO_SURFACE_RENDER_HAS_COMPOSITE (other)) return NULL; - if (width > XLIB_COORD_MAX || height > XLIB_COORD_MAX) - return NULL; - image = (cairo_image_surface_t *) _cairo_image_surface_create_with_content (solid_pattern->content, width, height); @@ -1293,9 +1415,9 @@ _cairo_xlib_surface_create_solid_pattern_surface (void *abstrac other->depth); surface = (cairo_xlib_surface_t *) - _cairo_xlib_surface_create_internal (other->dpy, + _cairo_xlib_surface_create_internal (other->screen, pixmap, - other->screen, other->visual, + other->visual, other->xrender_format, width, height, other->depth); @@ -1305,7 +1427,8 @@ _cairo_xlib_surface_create_solid_pattern_surface (void *abstrac status = _cairo_surface_paint (&image->base, CAIRO_OPERATOR_SOURCE, - &solid_pattern->base, NULL); + &solid_pattern->base, + NULL); if (unlikely (status)) goto BAIL; @@ -1339,7 +1462,6 @@ _cairo_xlib_surface_can_repaint_solid_pattern_surface (void *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, @@ -1348,9 +1470,6 @@ _cairo_xlib_surface_set_matrix (cairo_xlib_surface_t *surface, { XTransform xtransform; - if (!surface->src_picture) - return CAIRO_STATUS_SUCCESS; - /* Casting between pixman_transform_t and XTransform is safe because * they happen to be the exact same type. */ @@ -1361,8 +1480,8 @@ _cairo_xlib_surface_set_matrix (cairo_xlib_surface_t *surface, if (memcmp (&xtransform, &surface->xtransform, sizeof (XTransform)) == 0) return CAIRO_STATUS_SUCCESS; - if (!CAIRO_SURFACE_RENDER_HAS_PICTURE_TRANSFORM (surface)) - return CAIRO_INT_STATUS_UNSUPPORTED; + 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; @@ -1376,18 +1495,14 @@ _cairo_xlib_surface_set_filter (cairo_xlib_surface_t *surface, { const char *render_filter; - if (!surface->src_picture) - return CAIRO_STATUS_SUCCESS; - if (surface->filter == filter) return CAIRO_STATUS_SUCCESS; - if (!CAIRO_SURFACE_RENDER_HAS_FILTERS (surface)) - { + if (!CAIRO_SURFACE_RENDER_HAS_FILTERS (surface)) { if (filter == CAIRO_FILTER_FAST || filter == CAIRO_FILTER_NEAREST) return CAIRO_STATUS_SUCCESS; - return CAIRO_INT_STATUS_UNSUPPORTED; + return UNSUPPORTED ("XRender does not support filter"); } switch (filter) { @@ -1424,23 +1539,48 @@ _cairo_xlib_surface_set_filter (cairo_xlib_surface_t *surface, return CAIRO_STATUS_SUCCESS; } -static void -_cairo_xlib_surface_set_repeat (cairo_xlib_surface_t *surface, int repeat) +static cairo_status_t +_cairo_xlib_surface_set_repeat (cairo_xlib_surface_t *surface, + cairo_extend_t extend) { XRenderPictureAttributes pa; unsigned long mask; + int repeat; - if (!surface->src_picture) - return; + if (surface->extend == extend) + return CAIRO_STATUS_SUCCESS; - if (surface->repeat == repeat) - return; + switch (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 pad"); + + repeat = RepeatPad; + break; + default: + ASSERT_NOT_REACHED; + return CAIRO_INT_STATUS_UNSUPPORTED; + } mask = CPRepeat; pa.repeat = repeat; XRenderChangePicture (surface->dpy, surface->src_picture, mask, &pa); - surface->repeat = repeat; + surface->extend = extend; + + return CAIRO_STATUS_SUCCESS; } static cairo_int_status_t @@ -1458,26 +1598,9 @@ _cairo_xlib_surface_set_attributes (cairo_xlib_surface_t *surface, if (unlikely (status)) return status; - switch (attributes->extend) { - case CAIRO_EXTEND_NONE: - _cairo_xlib_surface_set_repeat (surface, RepeatNone); - break; - case CAIRO_EXTEND_REPEAT: - _cairo_xlib_surface_set_repeat (surface, RepeatNormal); - break; - case CAIRO_EXTEND_REFLECT: - if (surface->buggy_pad_reflect) - return CAIRO_INT_STATUS_UNSUPPORTED; - _cairo_xlib_surface_set_repeat (surface, RepeatReflect); - break; - case CAIRO_EXTEND_PAD: - if (surface->buggy_pad_reflect) - return CAIRO_INT_STATUS_UNSUPPORTED; - _cairo_xlib_surface_set_repeat (surface, RepeatPad); - break; - default: - return CAIRO_INT_STATUS_UNSUPPORTED; - } + status = _cairo_xlib_surface_set_repeat (surface, attributes->extend); + if (unlikely (status)) + return status; status = _cairo_xlib_surface_set_filter (surface, attributes->filter); if (unlikely (status)) @@ -1495,7 +1618,7 @@ _surfaces_compatible (cairo_xlib_surface_t *dst, cairo_xlib_surface_t *src) { /* same screen */ - if (!_cairo_xlib_surface_same_screen (dst, src)) + if (! _cairo_xlib_surface_same_screen (dst, src)) return FALSE; /* same depth (for core) */ @@ -1525,7 +1648,6 @@ _surface_has_alpha (cairo_xlib_surface_t *surface) else return FALSE; } else { - /* In the no-render case, we never have alpha */ return FALSE; } @@ -1585,27 +1707,38 @@ _categorize_composite_operation (cairo_xlib_surface_t *dst, cairo_bool_t have_mask) { - if (!dst->buggy_repeat) + 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) + if (src_pattern->type != CAIRO_PATTERN_TYPE_SOLID && + src_pattern->extend == CAIRO_EXTEND_REPEAT) { - cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *)src_pattern; + /* Check for the bug with repeat patterns nad general transforms. */ + if (! _cairo_matrix_is_integer_translation (&src_pattern->matrix, + NULL, NULL)) + { + return DO_UNSUPPORTED; + } - if (_cairo_matrix_is_integer_translation (&src_pattern->matrix, NULL, NULL) && - src_pattern->extend == CAIRO_EXTEND_REPEAT) + if (have_mask || + !(op == CAIRO_OPERATOR_SOURCE || op == CAIRO_OPERATOR_OVER)) { + return DO_UNSUPPORTED; + } + + if (src_pattern->type == CAIRO_PATTERN_TYPE_SURFACE) { + cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *) src_pattern; + /* 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; + cairo_xlib_surface_t *src = (cairo_xlib_surface_t *) surface_pattern->surface; if (op == CAIRO_OPERATOR_OVER && _surface_has_alpha (src)) return DO_UNSUPPORTED; @@ -1616,15 +1749,11 @@ _categorize_composite_operation (cairo_xlib_surface_t *dst, */ 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; @@ -1645,41 +1774,24 @@ _recategorize_composite_operation (cairo_xlib_surface_t *dst, 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; - - if (! _cairo_surface_is_xlib (&src->base)) - return DO_UNSUPPORTED; - - needs_alpha_composite = - _operator_needs_alpha_composite (op, - _surface_has_alpha (dst), - _surface_has_alpha (src)); - + /* Can we use the core protocol? */ if (! have_mask && - is_integer_translation && - src_attr->extend == CAIRO_EXTEND_NONE && - ! needs_alpha_composite && - _surfaces_compatible (src, dst)) + src->owns_pixmap && + src->depth == dst->depth && + _cairo_matrix_is_integer_translation (&src_attr->matrix, NULL, NULL) && + ! _operator_needs_alpha_composite (op, + _surface_has_alpha (dst), + _surface_has_alpha (src))) { - return DO_XCOPYAREA; - } + if (src_attr->extend == CAIRO_EXTEND_NONE) + 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 && - _surfaces_compatible (dst, src)) - { + if (dst->buggy_repeat && src_attr->extend == CAIRO_EXTEND_REPEAT) return DO_XTILE; - } + } + if (dst->buggy_repeat && src_attr->extend == CAIRO_EXTEND_REPEAT) return DO_UNSUPPORTED; - } if (! CAIRO_SURFACE_RENDER_HAS_COMPOSITE (src)) return DO_UNSUPPORTED; @@ -1725,12 +1837,293 @@ _render_operator (cairo_operator_t op) return PictOpAdd; case CAIRO_OPERATOR_SATURATE: return PictOpSaturate; + + 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; + 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->screen, None, + 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, @@ -1742,7 +2135,8 @@ _cairo_xlib_surface_composite (cairo_operator_t op, int dst_x, int dst_y, unsigned int width, - unsigned int height) + unsigned int height, + cairo_region_t *clip_region) { cairo_surface_attributes_t src_attr, mask_attr; cairo_xlib_surface_t *dst = abstract_dst; @@ -1754,13 +2148,17 @@ _cairo_xlib_surface_composite (cairo_operator_t op, cairo_bool_t is_integer_translation; cairo_bool_t needs_alpha_composite; cairo_content_t src_content; + GC gc; - _cairo_xlib_display_notify (dst->display); + 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 CAIRO_INT_STATUS_UNSUPPORTED; + return UNSUPPORTED ("unsupported operation"); + + X_DEBUG ((dst->dpy, "composite (dst=%x)", (unsigned int) dst->drawable)); needs_alpha_composite = _operator_needs_alpha_composite (op, @@ -1770,38 +2168,33 @@ _cairo_xlib_surface_composite (cairo_operator_t op, if (! needs_alpha_composite) src_content &= ~CAIRO_CONTENT_ALPHA; - status = _cairo_pattern_acquire_surfaces (src_pattern, mask_pattern, - &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, - (cairo_surface_t **) &mask, - &src_attr, &mask_attr); + _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 ... */ - if (!_cairo_surface_is_xlib (&src->base)) { - status = CAIRO_INT_STATUS_UNSUPPORTED; - goto BAIL; - } - if (mask != NULL && - (! _cairo_surface_is_xlib (&mask->base) || - ! CAIRO_SURFACE_RENDER_HAS_COMPOSITE (dst))) - { - status = CAIRO_INT_STATUS_UNSUPPORTED; + 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 = CAIRO_INT_STATUS_UNSUPPORTED; + status = UNSUPPORTED ("unsupported operation"); goto BAIL; } @@ -1814,6 +2207,10 @@ _cairo_xlib_surface_composite (cairo_operator_t op, if (unlikely (status)) goto BAIL; + status = _cairo_xlib_surface_set_clip_region (dst, clip_region); + if (unlikely (status)) + goto BAIL; + _cairo_xlib_surface_ensure_dst_picture (dst); if (mask) { status = _cairo_xlib_surface_set_attributes (mask, &mask_attr, @@ -1849,23 +2246,40 @@ _cairo_xlib_surface_composite (cairo_operator_t op, break; case DO_XCOPYAREA: - status = _cairo_xlib_surface_ensure_gc (dst); + status = _cairo_xlib_surface_get_gc (dst, &gc); if (unlikely (status)) goto BAIL; - is_integer_translation = _cairo_matrix_is_integer_translation (&src_attr.matrix, - &itx, &ity); + 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); + if (clip_region == NULL) { + XCopyArea (dst->dpy, src->drawable, dst->drawable, gc, + src_x + src_attr.x_offset + itx, + src_y + src_attr.y_offset + ity, + width, height, + dst_x, dst_y); + } else { + int n, num_rects; + + src_x += src_attr.x_offset + itx - dst_x; + src_y += src_attr.y_offset + ity - dst_y; + + num_rects = cairo_region_num_rectangles (clip_region); + for (n = 0; n < num_rects; n++) { + cairo_rectangle_int_t rect; + + cairo_region_get_rectangle (clip_region, n, &rect); + XCopyArea (dst->dpy, src->drawable, dst->drawable, gc, + rect.x + src_x, rect.y + src_y, + rect.width, rect.height, + rect.x, rect.y); + } + } + + _cairo_xlib_surface_put_gc (dst, gc); break; case DO_XTILE: @@ -1877,21 +2291,36 @@ _cairo_xlib_surface_composite (cairo_operator_t op, * _recategorize_composite_operation. */ - status = _cairo_xlib_surface_ensure_gc (dst); + status = _cairo_xlib_surface_get_gc (dst, &gc); if (unlikely (status)) goto BAIL; - is_integer_translation = _cairo_matrix_is_integer_translation (&src_attr.matrix, - &itx, &ity); + + 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, + XSetTSOrigin (dst->dpy, gc, - (itx + src_attr.x_offset), - (ity + src_attr.y_offset)); - XSetTile (dst->dpy, dst->gc, src->drawable); - XSetFillStyle (dst->dpy, dst->gc, FillTiled); + XSetTile (dst->dpy, gc, src->drawable); + + if (clip_region == NULL) { + XFillRectangle (dst->dpy, dst->drawable, gc, + dst_x, dst_y, width, height); + } else { + int n, num_rects; + + num_rects = cairo_region_num_rectangles (clip_region); + for (n = 0; n < num_rects; n++) { + cairo_rectangle_int_t rect; - XFillRectangle (dst->dpy, dst->drawable, dst->gc, - dst_x, dst_y, width, height); + cairo_region_get_rectangle (clip_region, n, &rect); + XFillRectangle (dst->dpy, dst->drawable, gc, + rect.x, rect.y, rect.width, rect.height); + } + } + + _cairo_xlib_surface_put_gc (dst, gc); break; case DO_UNSUPPORTED: @@ -1907,7 +2336,8 @@ _cairo_xlib_surface_composite (cairo_operator_t op, mask ? mask->height : 0, src_x, src_y, mask_x, mask_y, - dst_x, dst_y, width, height); + dst_x, dst_y, width, height, + clip_region); BAIL: if (mask) @@ -1918,6 +2348,7 @@ _cairo_xlib_surface_composite (cairo_operator_t op, 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, @@ -1928,14 +2359,17 @@ _cairo_xlib_surface_solid_fill_rectangles (cairo_xlib_surface_t *surface, cairo_solid_pattern_t solid; cairo_surface_t *solid_surface = NULL; cairo_surface_attributes_t attrs; + GC gc; int i; _cairo_pattern_init_solid (&solid, color, CAIRO_CONTENT_COLOR); - status = _cairo_xlib_surface_ensure_gc (surface); + status = _cairo_xlib_surface_get_gc (surface, &gc); if (unlikely (status)) return status; + X_DEBUG ((surface->dpy, "solid_fill_rectangles (dst=%x)", (unsigned int) surface->drawable)); + status = _cairo_pattern_acquire_surface (&solid.base, &surface->base, CAIRO_CONTENT_COLOR_ALPHA, 0, 0, @@ -1944,31 +2378,30 @@ _cairo_xlib_surface_solid_fill_rectangles (cairo_xlib_surface_t *surface, CAIRO_PATTERN_ACQUIRE_NONE, &solid_surface, &attrs); - if (unlikely (status)) - return status; - - if (! _cairo_surface_is_xlib (solid_surface)) { - status = CAIRO_INT_STATUS_UNSUPPORTED; - goto BAIL; + if (unlikely (status)) { + _cairo_xlib_surface_put_gc (surface, gc); + return status; } - XSetTSOrigin (surface->dpy, surface->gc, + assert (_cairo_surface_is_xlib (solid_surface)); + + XSetTSOrigin (surface->dpy, gc, - (surface->base.device_transform.x0 + attrs.x_offset), - (surface->base.device_transform.y0 + attrs.y_offset)); - XSetTile (surface->dpy, surface->gc, + XSetTile (surface->dpy, 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, + XFillRectangle (surface->dpy, surface->drawable, gc, rects[i].x, rects[i].y, rects[i].width, rects[i].height); } - BAIL: + _cairo_xlib_surface_put_gc (surface, gc); + _cairo_pattern_release_surface (&solid.base, solid_surface, &attrs); - return status; + return CAIRO_STATUS_SUCCESS; } static cairo_int_status_t @@ -1980,11 +2413,15 @@ _cairo_xlib_surface_fill_rectangles (void *abstract_surface, { 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_HAS_FILL_RECTANGLES (surface)) { + 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))) @@ -1993,14 +2430,19 @@ _cairo_xlib_surface_fill_rectangles (void *abstract_surface, rects, num_rects); } - return CAIRO_INT_STATUS_UNSUPPORTED; + return UNSUPPORTED ("no support for FillRectangles with this op"); } + X_DEBUG ((surface->dpy, "fill_rectangles (dst=%x)", (unsigned int) surface->drawable)); + 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 @@ -2043,117 +2485,41 @@ _cairo_xlib_surface_fill_rectangles (void *abstract_surface, 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; - } +#define CAIRO_FIXED_16_16_MIN -32768 +#define CAIRO_FIXED_16_16_MAX 32767 - 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; +static cairo_bool_t +_line_exceeds_16_16 (const cairo_line_t *line) +{ + return + line->p1.x < CAIRO_FIXED_16_16_MIN || + line->p1.x > CAIRO_FIXED_16_16_MAX || + line->p2.x < CAIRO_FIXED_16_16_MIN || + line->p2.x > CAIRO_FIXED_16_16_MAX || + line->p1.y < CAIRO_FIXED_16_16_MIN || + line->p1.y > CAIRO_FIXED_16_16_MAX || + line->p2.y < CAIRO_FIXED_16_16_MIN || + line->p2.y > CAIRO_FIXED_16_16_MAX; } -/* 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) +static void +_project_line_x_onto_16_16 (const cairo_line_t *line, + cairo_fixed_t top, + cairo_fixed_t bottom, + XLineFixed *out) { - 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; - } + cairo_point_double_t p1, p2; + double m; - 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; - } + p1.x = _cairo_fixed_to_double (line->p1.x); + p1.y = _cairo_fixed_to_double (line->p1.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; - } + p2.x = _cairo_fixed_to_double (line->p2.x); + p2.y = _cairo_fixed_to_double (line->p2.y); - 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; + m = (p2.x - p1.x) / (p2.y - p1.y); + out->p1.x = _cairo_fixed_16_16_from_double (p1.x + m * _cairo_fixed_to_double (top - line->p1.y)); + out->p2.x = _cairo_fixed_16_16_from_double (p1.x + m * _cairo_fixed_to_double (bottom - line->p1.y)); } static cairo_int_status_t @@ -2168,7 +2534,8 @@ _cairo_xlib_surface_composite_trapezoids (cairo_operator_t op, unsigned int width, unsigned int height, cairo_trapezoid_t *traps, - int num_traps) + int num_traps, + cairo_region_t *clip_region) { cairo_surface_attributes_t attributes; cairo_xlib_surface_t *dst = abstract_dst; @@ -2178,31 +2545,34 @@ _cairo_xlib_surface_composite_trapezoids (cairo_operator_t op, int render_reference_x, render_reference_y; int render_src_x, render_src_y; XRenderPictFormat *pict_format; + XTrapezoid xtraps_stack[CAIRO_STACK_ARRAY_LENGTH (XTrapezoid)]; + XTrapezoid *xtraps = xtraps_stack; + int i; _cairo_xlib_display_notify (dst->display); - if (!CAIRO_SURFACE_RENDER_HAS_TRAPEZOIDS (dst)) - return CAIRO_INT_STATUS_UNSUPPORTED; + 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 CAIRO_INT_STATUS_UNSUPPORTED; + return UNSUPPORTED ("unsupported operation"); - status = _cairo_pattern_acquire_surface (pattern, &dst->base, - CAIRO_CONTENT_COLOR_ALPHA, - src_x, src_y, width, height, - dst->buggy_pad_reflect ? - CAIRO_PATTERN_ACQUIRE_NO_REFLECT : - CAIRO_PATTERN_ACQUIRE_NONE, - (cairo_surface_t **) &src, - &attributes); + X_DEBUG ((dst->dpy, "composite_trapezoids (dst=%x)", (unsigned int) dst->drawable)); + + 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 = CAIRO_INT_STATUS_UNSUPPORTED; + status = UNSUPPORTED ("unsupported operation"); goto BAIL; } @@ -2233,204 +2603,108 @@ _cairo_xlib_surface_composite_trapezoids (cairo_operator_t op, 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) { + 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; } - - 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); - - } 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_region_t * -_surface_maybe_clip_region (cairo_xlib_surface_t *surface, - cairo_region_t *clip, - cairo_region_t *bounded) -{ - cairo_rectangle_int_t rect; - - cairo_region_get_extents (clip, &rect); - if (rect.x >= 0 && - rect.y >= 0 && - rect.x + rect.width <= surface->width && - rect.y + rect.height <= surface->height) - { - return clip; - } - - rect.x = rect.y = 0; - rect.width = surface->width; - rect.height = surface->height; - _cairo_region_init_rectangle (bounded, &rect); - - bounded->status = cairo_region_intersect (bounded, clip); - - return bounded; -} - -static cairo_int_status_t -_cairo_xlib_surface_set_clip_region (void *abstract_surface, - cairo_region_t *region) -{ - cairo_xlib_surface_t *surface = abstract_surface; - cairo_bool_t had_clip_rects = surface->have_clip_rects; - - if (had_clip_rects == FALSE && region == NULL) - return CAIRO_STATUS_SUCCESS; - - if (surface->clip_rects != surface->embedded_clip_rects) { - free (surface->clip_rects); - surface->clip_rects = surface->embedded_clip_rects; - } - - surface->have_clip_rects = FALSE; - surface->num_clip_rects = 0; - - if (region != NULL) { - XRectangle *rects = NULL; - int n_rects, i; - cairo_region_t bounded; + for (i = 0; i < num_traps; i++) { + /* top/bottom will be clamped to surface bounds */ + xtraps[i].top = _cairo_fixed_to_16_16(traps[i].top); + xtraps[i].bottom = _cairo_fixed_to_16_16(traps[i].bottom); - /* Intersect the region with the bounds of the surface. This - * is necessary so we don't wrap around when we convert cairo's - * 32 bit region into 16 bit rectangles. + /* However, all the other coordinates will have been left untouched so + * as not to introduce numerical error. Recompute them if they + * exceed the 16.16 limits. */ - region = _surface_maybe_clip_region (surface, region, &bounded); - if (unlikely (region->status)) - return region->status; - - 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)) { - if (unlikely (region == &bounded)) - _cairo_region_fini (&bounded); - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } + if (unlikely (_line_exceeds_16_16 (&traps[i].left))) { + _project_line_x_onto_16_16 (&traps[i].left, + traps[i].top, + traps[i].bottom, + &xtraps[i].left); + xtraps[i].left.p1.y = xtraps[i].top; + xtraps[i].left.p2.y = xtraps[i].bottom; } else { - rects = surface->embedded_clip_rects; + 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); } - 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; + if (unlikely (_line_exceeds_16_16 (&traps[i].right))) { + _project_line_x_onto_16_16 (&traps[i].right, + traps[i].top, + traps[i].bottom, + &xtraps[i].right); + xtraps[i].right.p1.y = xtraps[i].top; + xtraps[i].right.p2.y = xtraps[i].bottom; + } else { + xtraps[i].right.p1.x = _cairo_fixed_to_16_16(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); } + } - if (unlikely (region == &bounded)) - _cairo_region_fini (&bounded); + 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); - surface->have_clip_rects = TRUE; - surface->clip_rects = rects; - surface->num_clip_rects = n_rects; + if (xtraps != xtraps_stack) + free (xtraps); - /* Discard the trivial clip rectangle that covers the entire surface */ - if (n_rects == 1 && - rects[0].x == 0 && - rects[0].y == 0 && - rects[0].width == surface->width && - rects[0].height == surface->height) - { - surface->have_clip_rects = FALSE; - surface->num_clip_rects = 0; + if (! _cairo_operator_bounded_by_mask (op)) { + cairo_traps_t _traps; + cairo_box_t box; + cairo_rectangle_int_t extents; - if (! had_clip_rects) - goto DONE; - } + /* 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. + */ + /* XXX: update the interface to pass composite rects */ + _traps.traps = traps; + _traps.num_traps = num_traps; + _cairo_traps_extents (&_traps, &box); + _cairo_box_round_to_rectangle (&box, &extents); + + status = _cairo_surface_composite_shape_fixup_unbounded (&dst->base, + &attributes, + src->width, src->height, + extents.width, extents.height, + src_x, src_y, + -extents.x + dst_x, -extents.y + dst_y, + dst_x, dst_y, + width, height, + clip_region); } - surface->clip_dirty = CAIRO_XLIB_SURFACE_CLIP_DIRTY_ALL; - DONE: + BAIL: + _cairo_pattern_release_surface (pattern, &src->base, &attributes); - return CAIRO_STATUS_SUCCESS; + return status; } -static cairo_int_status_t +static cairo_bool_t _cairo_xlib_surface_get_extents (void *abstract_surface, cairo_rectangle_int_t *rectangle) { @@ -2442,7 +2716,7 @@ _cairo_xlib_surface_get_extents (void *abstract_surface, rectangle->width = surface->width; rectangle->height = surface->height; - return CAIRO_STATUS_SUCCESS; + return TRUE; } static void @@ -2451,7 +2725,7 @@ _cairo_xlib_surface_get_font_options (void *abstract_surface, { cairo_xlib_surface_t *surface = abstract_surface; - *options = *_cairo_xlib_screen_get_font_options (surface->screen_info); + *options = *_cairo_xlib_screen_get_font_options (surface->screen); } static void @@ -2470,7 +2744,7 @@ _cairo_xlib_surface_is_similar (void *surface_a, cairo_xlib_surface_t *b = surface_b; XRenderPictFormat *xrender_format = b->xrender_format; - if (!_cairo_xlib_surface_same_screen (a, b)) + if (! _cairo_xlib_surface_same_screen (a, b)) return FALSE; /* now inspect the content to check that a is similar to b */ @@ -2489,19 +2763,6 @@ _cairo_xlib_surface_is_similar (void *surface_a, return a->xrender_format == xrender_format; } -static cairo_status_t -_cairo_xlib_surface_reset (void *abstract_surface) -{ - cairo_xlib_surface_t *surface = abstract_surface; - cairo_status_t status; - - status = _cairo_xlib_surface_set_clip_region (surface, NULL); - if (unlikely (status)) - return status; - - return CAIRO_STATUS_SUCCESS; -} - static const cairo_surface_backend_t cairo_xlib_surface_backend = { CAIRO_SURFACE_TYPE_XLIB, _cairo_xlib_surface_create_similar, @@ -2518,8 +2779,6 @@ static const cairo_surface_backend_t cairo_xlib_surface_backend = { NULL, /* check_span_renderer */ NULL, /* copy_page */ NULL, /* show_page */ - _cairo_xlib_surface_set_clip_region, - NULL, /* intersect_clip_path */ _cairo_xlib_surface_get_extents, NULL, /* old_show_glyphs */ _cairo_xlib_surface_get_font_options, @@ -2535,10 +2794,10 @@ static const cairo_surface_backend_t cairo_xlib_surface_backend = { _cairo_xlib_surface_show_glyphs, _cairo_xlib_surface_snapshot, - _cairo_xlib_surface_is_similar, - _cairo_xlib_surface_reset, + NULL, /* fill_stroke */ + _cairo_xlib_surface_create_solid_pattern_surface, _cairo_xlib_surface_can_repaint_solid_pattern_surface }; @@ -2569,6 +2828,8 @@ _cairo_xlib_surface_detach_display (cairo_xlib_display_t *display, void *data) dpy = surface->dpy; surface->dpy = NULL; + X_DEBUG ((dpy, "detach (drawable=%x)", (unsigned int) surface->drawable)); + if (surface->dst_picture != None) { XRenderFreePicture (dpy, surface->dst_picture); surface->dst_picture = None; @@ -2584,96 +2845,83 @@ _cairo_xlib_surface_detach_display (cairo_xlib_display_t *display, void *data) 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_create_internal (cairo_xlib_screen_t *screen, + Drawable drawable, + 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; + if (depth == 0) { + if (xrender_format) { + depth = xrender_format->depth; - /* XXX find matching visual for core/dithering fallbacks? */ - } else if (visual) { - int j, k; + /* XXX find matching visual for core/dithering fallbacks? */ + } else if (visual) { + Screen *scr = screen->screen; - /* 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; + if (visual == DefaultVisualOfScreen (scr)) { + depth = DefaultDepthOfScreen (scr); + } else { + 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 < scr->ndepths; j++) { + Depth *d = &scr->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)); + 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); +found: + ; } surface = malloc (sizeof (cairo_xlib_surface_t)); - if (unlikely (surface == NULL)) { - _cairo_xlib_screen_info_destroy (screen_info); - _cairo_xlib_display_destroy (display); + if (unlikely (surface == NULL)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); - } + + surface->dpy = _cairo_xlib_display_get_dpy (screen->display); /* 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); + _cairo_xlib_add_close_display_hook (screen->display, + &surface->close_display_hook); - surface->render_major = display->render_major; - surface->render_minor = display->render_minor; + _cairo_xlib_display_get_xrender_version (screen->display, + &surface->render_major, + &surface->render_minor); if (CAIRO_SURFACE_RENDER_HAS_CREATE_PICTURE (surface)) { if (!xrender_format) { if (visual) { - xrender_format = XRenderFindVisualFormat (dpy, visual); + xrender_format = XRenderFindVisualFormat (surface->dpy, visual); } else if (depth == 1) { xrender_format = - _cairo_xlib_display_get_xrender_format (display, + _cairo_xlib_display_get_xrender_format (screen->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) { + /* we cannot use XRender for this surface, so ensure we don't try */ surface->render_major = -1; surface->render_minor = -1; } @@ -2681,24 +2929,28 @@ _cairo_xlib_surface_create_internal (Display *dpy, _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->screen = _cairo_xlib_screen_reference (screen); + surface->display = screen->display; - 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)) { + surface->buggy_repeat = ! _cairo_xlib_display_has_repeat (surface->display); + 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; + + surface->buggy_pad_reflect = ! _cairo_xlib_display_has_reflect (surface->display); + if (! CAIRO_SURFACE_RENDER_HAS_EXTENDED_REPEAT (surface)) + surface->buggy_pad_reflect = TRUE; + + surface->buggy_gradients = ! _cairo_xlib_display_has_gradients (surface->display); + if (! CAIRO_SURFACE_RENDER_HAS_GRADIENTS (surface)) + surface->buggy_gradients = TRUE; surface->dst_picture = None; surface->src_picture = None; @@ -2707,11 +2959,10 @@ _cairo_xlib_surface_create_internal (Display *dpy, surface->xrender_format = xrender_format; surface->depth = depth; surface->filter = CAIRO_FILTER_NEAREST; - surface->repeat = FALSE; + surface->extend = CAIRO_EXTEND_NONE; surface->xtransform = identity; - surface->have_clip_rects = FALSE; - surface->gc_has_clip_rects = FALSE; + surface->clip_region = NULL; surface->clip_rects = surface->embedded_clip_rects; surface->num_clip_rects = 0; surface->clip_dirty = 0; @@ -2750,29 +3001,31 @@ _cairo_xlib_surface_create_internal (Display *dpy, surface->b_mask = 0; } - return (cairo_surface_t *) surface; + return &surface->base; } static Screen * _cairo_xlib_screen_from_visual (Display *dpy, Visual *visual) { - int s; - int d; - int v; - Screen *screen; - Depth *depth; + 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; } @@ -2808,14 +3061,34 @@ cairo_xlib_surface_create (Display *dpy, int width, int height) { - Screen *screen = _cairo_xlib_screen_from_visual (dpy, visual); + Screen *scr; + cairo_xlib_screen_t *screen; + cairo_surface_t *surface; + cairo_status_t status; - if (screen == NULL) + if (width > XLIB_COORD_MAX || height > XLIB_COORD_MAX) { + /* you're lying, and you know it! */ + return _cairo_surface_create_in_error (CAIRO_STATUS_INVALID_SIZE); + } + + scr = _cairo_xlib_screen_from_visual (dpy, visual); + if (scr == 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); + status = _cairo_xlib_screen_get (dpy, scr, &screen); + if (unlikely (status)) + return _cairo_surface_create_in_error (status); + + X_DEBUG ((dpy, "create (drawable=%x)", (unsigned int) drawable)); + + surface = _cairo_xlib_surface_create_internal (screen, drawable, + visual, NULL, + width, height, 0); + _cairo_xlib_screen_destroy (screen); + + return surface; } +slim_hidden_def (cairo_xlib_surface_create); /** * cairo_xlib_surface_create_for_bitmap: @@ -2833,12 +3106,29 @@ cairo_xlib_surface_create (Display *dpy, cairo_surface_t * cairo_xlib_surface_create_for_bitmap (Display *dpy, Pixmap bitmap, - Screen *screen, + Screen *scr, int width, int height) { - return _cairo_xlib_surface_create_internal (dpy, bitmap, screen, - NULL, NULL, width, height, 1); + cairo_xlib_screen_t *screen; + cairo_surface_t *surface; + cairo_status_t status; + + if (width > XLIB_COORD_MAX || height > XLIB_COORD_MAX) + return _cairo_surface_create_in_error (CAIRO_STATUS_INVALID_SIZE); + + status = _cairo_xlib_screen_get (dpy, scr, &screen); + if (unlikely (status)) + return _cairo_surface_create_in_error (status); + + X_DEBUG ((dpy, "create_for_bitmap (drawable=%x)", (unsigned int) bitmap)); + + surface = _cairo_xlib_surface_create_internal (screen, bitmap, + NULL, NULL, + width, height, 1); + _cairo_xlib_screen_destroy (screen); + + return surface; } #if CAIRO_HAS_XLIB_XRENDER_SURFACE @@ -2865,14 +3155,32 @@ cairo_xlib_surface_create_for_bitmap (Display *dpy, cairo_surface_t * cairo_xlib_surface_create_with_xrender_format (Display *dpy, Drawable drawable, - Screen *screen, + Screen *scr, XRenderPictFormat *format, int width, int height) { - return _cairo_xlib_surface_create_internal (dpy, drawable, screen, - NULL, format, width, height, 0); + cairo_xlib_screen_t *screen; + cairo_surface_t *surface; + cairo_status_t status; + + if (width > XLIB_COORD_MAX || height > XLIB_COORD_MAX) + return _cairo_surface_create_in_error (CAIRO_STATUS_INVALID_SIZE); + + status = _cairo_xlib_screen_get (dpy, scr, &screen); + if (unlikely (status)) + return _cairo_surface_create_in_error (status); + + X_DEBUG ((dpy, "create_with_xrender_format (drawable=%x)", (unsigned int) drawable)); + + surface = _cairo_xlib_surface_create_internal (screen, drawable, + NULL, format, + width, height, 0); + _cairo_xlib_screen_destroy (screen); + + return surface; } +slim_hidden_def (cairo_xlib_surface_create_with_xrender_format); /** * cairo_xlib_surface_get_xrender_format: @@ -2934,6 +3242,12 @@ cairo_xlib_surface_set_size (cairo_surface_t *abstract_surface, return; } + if (width > XLIB_COORD_MAX || height > XLIB_COORD_MAX) { + status = _cairo_surface_set_error (abstract_surface, + CAIRO_STATUS_INVALID_SIZE); + return; + } + surface->width = width; surface->height = height; } @@ -2966,11 +3280,19 @@ cairo_xlib_surface_set_drawable (cairo_surface_t *abstract_surface, return; } + if (width > XLIB_COORD_MAX || height > XLIB_COORD_MAX) { + status = _cairo_surface_set_error (abstract_surface, + CAIRO_STATUS_INVALID_SIZE); + return; + } + /* XXX: and what about this case? */ if (surface->owns_pixmap) return; if (surface->drawable != drawable) { + X_DEBUG ((surface->dpy, "set_drawable (drawable=%x)", (unsigned int) drawable)); + if (surface->dst_picture != None) { status = _cairo_xlib_display_queue_resource ( surface->display, @@ -3069,7 +3391,7 @@ cairo_xlib_surface_get_screen (cairo_surface_t *abstract_surface) return NULL; } - return surface->screen; + return surface->screen->screen; } /** @@ -3135,7 +3457,7 @@ cairo_xlib_surface_get_width (cairo_surface_t *abstract_surface) if (! _cairo_surface_is_xlib (abstract_surface)) { _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); - return -1; + return 0; } return surface->width; @@ -3158,7 +3480,7 @@ cairo_xlib_surface_get_height (cairo_surface_t *abstract_surface) if (! _cairo_surface_is_xlib (abstract_surface)) { _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); - return -1; + return 0; } return surface->height; @@ -3215,7 +3537,7 @@ _cairo_xlib_surface_remove_scaled_font (cairo_xlib_display_t *display, Display *dpy; int i; - dpy = display->display; + dpy = _cairo_xlib_display_get_dpy (display); for (i = 0; i < NUM_GLYPHSETS; i++) { cairo_xlib_font_glyphset_info_t *glyphset_info; @@ -3417,8 +3739,8 @@ _cairo_xlib_scaled_font_get_glyphset_info_for_format (cairo_scaled_font_t *scale glyphset_info->xrender_format = _cairo_xlib_display_get_xrender_format (display, glyphset_info->format); - glyphset_info->glyphset = XRenderCreateGlyphSet (display->display, - glyphset_info->xrender_format); + glyphset_info->glyphset = XRenderCreateGlyphSet (_cairo_xlib_display_get_dpy (display), + glyphset_info->xrender_format); } return glyphset_info; @@ -3545,14 +3867,13 @@ _cairo_xlib_surface_add_glyph (Display *dpy, sz_xGlyphInfo - 8; if (len >= max_request_size) - return CAIRO_INT_STATUS_UNSUPPORTED; + 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); @@ -3560,19 +3881,10 @@ _cairo_xlib_surface_add_glyph (Display *dpy, 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 @@ -3580,7 +3892,7 @@ _cairo_xlib_surface_add_glyph (Display *dpy, * format. */ if (glyph_surface->format != glyphset_info->format) { - cairo_t *cr; + cairo_surface_pattern_t pattern; cairo_surface_t *tmp_surface; tmp_surface = cairo_image_surface_create (glyphset_info->format, @@ -3593,12 +3905,11 @@ _cairo_xlib_surface_add_glyph (Display *dpy, 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); + _cairo_pattern_init_for_surface (&pattern, &glyph_surface->base); + status = _cairo_surface_paint (tmp_surface, + CAIRO_OPERATOR_SOURCE, &pattern.base, + NULL); + _cairo_pattern_fini (&pattern.base); glyph_surface = (cairo_image_surface_t *) tmp_surface; @@ -3830,7 +4141,6 @@ _emit_glyphs_chunk (cairo_xlib_surface_t *dst, if (n) { elts[nelt].nchars = n; nelt++; - n = 0; } /* Check that we agree with _cairo_xlib_surface_emit_glyphs() on the @@ -3896,7 +4206,7 @@ _cairo_xlib_surface_emit_glyphs (cairo_xlib_surface_t *dst, glyphs[i].index, CAIRO_SCALED_GLYPH_INFO_METRICS, &scaled_glyph); - if (status != CAIRO_STATUS_SUCCESS) + if (unlikely (status)) return status; this_x = _cairo_lround (glyphs[i].d.x); @@ -3976,7 +4286,7 @@ _cairo_xlib_surface_emit_glyphs (cairo_xlib_surface_t *dst, status = _emit_glyphs_chunk (dst, glyphs, i, scaled_font, op, src, attributes, num_elts, old_width, glyphset_info); - if (status != CAIRO_STATUS_SUCCESS) + if (unlikely (status)) return status; glyphs += i; @@ -4015,13 +4325,14 @@ _cairo_xlib_surface_emit_glyphs (cairo_xlib_surface_t *dst, request_size += width; } - if (num_elts) + 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 && status == CAIRO_STATUS_SUCCESS) + if (*remaining_glyphs != 0 && status == CAIRO_STATUS_SUCCESS) status = CAIRO_INT_STATUS_UNSUPPORTED; return status; @@ -4051,8 +4362,8 @@ _cairo_xlib_surface_show_glyphs (void *abstract_dst, cairo_glyph_t *glyphs, int num_glyphs, cairo_scaled_font_t *scaled_font, - int *remaining_glyphs, - cairo_rectangle_int_t *extents) + 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; @@ -4060,16 +4371,17 @@ _cairo_xlib_surface_show_glyphs (void *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 CAIRO_INT_STATUS_UNSUPPORTED; + 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 CAIRO_INT_STATUS_UNSUPPORTED; + 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 @@ -4077,25 +4389,58 @@ _cairo_xlib_surface_show_glyphs (void *abstract_dst, * including the fully transparent "background" of the rectangular * glyph surface. */ if (op == CAIRO_OPERATOR_SOURCE && - !CAIRO_SURFACE_RENDER_AT_LEAST(dst, 0, 11)) - return CAIRO_INT_STATUS_UNSUPPORTED; + ! 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 (dst->base.clip && - (dst->base.clip->mode != CAIRO_CLIP_MODE_REGION || - dst->base.clip->surface != NULL)) - return CAIRO_INT_STATUS_UNSUPPORTED; + if (clip != NULL) { + status = _cairo_clip_get_region (clip, &clip_region); + assert (status != CAIRO_INT_STATUS_NOTHING_TO_DO); + if (status) + return status; + } operation = _categorize_composite_operation (dst, op, src_pattern, TRUE); if (operation == DO_UNSUPPORTED) - return CAIRO_INT_STATUS_UNSUPPORTED; + return UNSUPPORTED ("unsupported op"); if (! _cairo_xlib_surface_owns_font (dst, scaled_font)) - return CAIRO_INT_STATUS_UNSUPPORTED; + return UNSUPPORTED ("unowned font"); + + X_DEBUG ((dst->dpy, "show_glyphs (dst=%x)", (unsigned int) dst->drawable)); + + if (clip_region != NULL && + cairo_region_num_rectangles (clip_region) == 1) + { + cairo_rectangle_int_t glyph_extents; + const cairo_rectangle_int_t *clip_extents; + + /* Can we do without the clip? + * Around 50% of the time the clip is redundant (firefox). + */ + _cairo_scaled_font_glyph_approximate_extents (scaled_font, + glyphs, num_glyphs, + &glyph_extents); + + clip_extents = &clip->path->extents; + if (clip_extents->x <= glyph_extents.x && + clip_extents->y <= glyph_extents.y && + clip_extents->x + clip_extents->width >= glyph_extents.x + glyph_extents.width && + clip_extents->y + clip_extents->height >= glyph_extents.y + glyph_extents.height) + { + clip_region = NULL; + } + } + + 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 @@ -4130,19 +4475,26 @@ _cairo_xlib_surface_show_glyphs (void *abstract_dst, status = _cairo_scaled_font_glyph_device_extents (scaled_font, glyphs, num_glyphs, - &glyph_extents); + &glyph_extents, + NULL); if (unlikely (status)) goto BAIL0; - status = _cairo_pattern_acquire_surface (src_pattern, &dst->base, - CAIRO_CONTENT_COLOR_ALPHA, - glyph_extents.x, glyph_extents.y, - glyph_extents.width, glyph_extents.height, - dst->buggy_pad_reflect ? - CAIRO_PATTERN_ACQUIRE_NO_REFLECT : - CAIRO_PATTERN_ACQUIRE_NONE, - (cairo_surface_t **) &src, - &attributes); + 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; } @@ -4150,7 +4502,7 @@ _cairo_xlib_surface_show_glyphs (void *abstract_dst, operation = _recategorize_composite_operation (dst, op, src, &attributes, TRUE); if (operation == DO_UNSUPPORTED) { - status = CAIRO_INT_STATUS_UNSUPPORTED; + status = UNSUPPORTED ("unsupported op"); goto BAIL1; } @@ -4168,15 +4520,14 @@ _cairo_xlib_surface_show_glyphs (void *abstract_dst, src, &attributes, remaining_glyphs); - } else - status = CAIRO_INT_STATUS_UNSUPPORTED; + } else { + status = UNSUPPORTED ("unowned font"); + } _cairo_scaled_font_thaw_cache (scaled_font); BAIL1: if (src) _cairo_pattern_release_surface (src_pattern, &src->base, &attributes); - if (src_pattern == &solid_pattern.base) - _cairo_pattern_fini (&solid_pattern.base); BAIL0: _cairo_xlib_display_notify (dst->display); |