/* cairo - a vector graphics library with display and print output * * Copyright © 2002 University of Southern California * * 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 */ #include "cairoint.h" #include "cairo-xcb.h" #include "cairo-xcb-xrender.h" #define AllPlanes ((unsigned long)~0L) static XCBRenderPICTFORMAT format_from_visual(XCBConnection *c, XCBVISUALID visual) { static const XCBRenderPICTFORMAT nil = { 0 }; XCBRenderQueryPictFormatsRep *r; XCBRenderPICTSCREENIter si; XCBRenderPICTDEPTHIter di; XCBRenderPICTVISUALIter vi; r = XCBRenderQueryPictFormatsReply(c, XCBRenderQueryPictFormats(c), 0); if(!r) return nil; for(si = XCBRenderQueryPictFormatsScreensIter(r); si.rem; XCBRenderPICTSCREENNext(&si)) for(di = XCBRenderPICTSCREENDepthsIter(si.data); di.rem; XCBRenderPICTDEPTHNext(&di)) for(vi = XCBRenderPICTDEPTHVisualsIter(di.data); vi.rem; XCBRenderPICTVISUALNext(&vi)) if(vi.data->visual.id == visual.id) { XCBRenderPICTFORMAT ret = vi.data->format; free(r); return ret; } return nil; } /* XXX: Why is this ridiculously complex compared to the equivalent * function in cairo-xlib-surface.c */ static XCBRenderPICTFORMINFO _format_from_cairo(XCBConnection *c, cairo_format_t fmt) { XCBRenderPICTFORMINFO ret = {{ 0 }}; struct tmpl_t { XCBRenderDIRECTFORMAT direct; CARD8 depth; }; static const struct tmpl_t templates[] = { /* CAIRO_FORMAT_ARGB32 */ { { 16, 0xff, 8, 0xff, 0, 0xff, 24, 0xff }, 32 }, /* CAIRO_FORMAT_RGB24 */ { { 16, 0xff, 8, 0xff, 0, 0xff, 0, 0x00 }, 24 }, /* CAIRO_FORMAT_A8 */ { { 0, 0x00, 0, 0x00, 0, 0x00, 0, 0xff }, 8 }, /* CAIRO_FORMAT_A1 */ { { 0, 0x00, 0, 0x00, 0, 0x00, 0, 0x01 }, 1 }, }; const struct tmpl_t *tmpl; XCBRenderQueryPictFormatsRep *r; XCBRenderPICTFORMINFOIter fi; if(fmt < 0 || fmt >= (sizeof(templates) / sizeof(*templates))) return ret; tmpl = templates + fmt; r = XCBRenderQueryPictFormatsReply(c, XCBRenderQueryPictFormats(c), 0); if(!r) return ret; for(fi = XCBRenderQueryPictFormatsFormatsIter(r); fi.rem; XCBRenderPICTFORMINFONext(&fi)) { const XCBRenderDIRECTFORMAT *t, *f; if(fi.data->type != XCBRenderPictTypeDirect) continue; if(fi.data->depth != tmpl->depth) continue; t = &tmpl->direct; f = &fi.data->direct; if(t->red_mask && (t->red_mask != f->red_mask || t->red_shift != f->red_shift)) continue; if(t->green_mask && (t->green_mask != f->green_mask || t->green_shift != f->green_shift)) continue; if(t->blue_mask && (t->blue_mask != f->blue_mask || t->blue_shift != f->blue_shift)) continue; if(t->alpha_mask && (t->alpha_mask != f->alpha_mask || t->alpha_shift != f->alpha_shift)) continue; ret = *fi.data; } free(r); return ret; } /* * Instead of taking two round trips for each blending request, * assume that if a particular drawable fails GetImage that it will * fail for a "while"; use temporary pixmaps to avoid the errors */ #define CAIRO_ASSUME_PIXMAP 20 typedef struct cairo_xcb_surface { cairo_surface_t base; XCBConnection *dpy; XCBGCONTEXT gc; XCBDRAWABLE drawable; int owns_pixmap; XCBVISUALTYPE *visual; int use_pixmap; int render_major; int render_minor; int width; int height; int depth; XCBRenderPICTURE picture; XCBRenderPICTFORMINFO format; int has_format; } cairo_xcb_surface_t; #define CAIRO_SURFACE_RENDER_AT_LEAST(surface, major, minor) \ (((surface)->render_major > major) || \ (((surface)->render_major == major) && ((surface)->render_minor >= minor))) #define CAIRO_SURFACE_RENDER_HAS_CREATE_PICTURE(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 0) #define CAIRO_SURFACE_RENDER_HAS_COMPOSITE(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 0) #define CAIRO_SURFACE_RENDER_HAS_FILL_RECTANGLE(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 1) #define CAIRO_SURFACE_RENDER_HAS_FILL_RECTANGLES(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 1) #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) static void _cairo_xcb_surface_ensure_gc (cairo_xcb_surface_t *surface); static int _CAIRO_FORMAT_DEPTH (cairo_format_t format) { switch (format) { case CAIRO_FORMAT_A1: return 1; case CAIRO_FORMAT_A8: return 8; case CAIRO_FORMAT_RGB24: return 24; case CAIRO_FORMAT_ARGB32: default: return 32; } } static cairo_surface_t * _cairo_xcb_surface_create_similar (void *abstract_src, cairo_content_t content, int width, int height) { cairo_xcb_surface_t *src = abstract_src; XCBConnection *dpy = src->dpy; XCBDRAWABLE d; cairo_xcb_surface_t *surface; cairo_format_t format = _cairo_format_from_content (content); XCBRenderPICTFORMINFO xrender_format = _format_from_cairo (dpy, format); /* As a good first approximation, if the display doesn't have COMPOSITE, * we're better off using image surfaces for all temporary operations */ if (!CAIRO_SURFACE_RENDER_HAS_COMPOSITE (src)) { return cairo_image_surface_create (format, width, height); } d.pixmap = XCBPIXMAPNew (dpy); XCBCreatePixmap (dpy, _CAIRO_FORMAT_DEPTH (format), d.pixmap, src->drawable, width <= 0 ? 1 : width, height <= 0 ? 1 : height); surface = (cairo_xcb_surface_t *) cairo_xcb_surface_create_with_xrender_format (dpy, d, &xrender_format, width, height); if (surface->base.status) { _cairo_error (CAIRO_STATUS_NO_MEMORY); return (cairo_surface_t*) &_cairo_surface_nil; } surface->owns_pixmap = TRUE; return &surface->base; } static cairo_status_t _cairo_xcb_surface_finish (void *abstract_surface) { cairo_xcb_surface_t *surface = abstract_surface; if (surface->picture.xid) XCBRenderFreePicture (surface->dpy, surface->picture); if (surface->owns_pixmap) XCBFreePixmap (surface->dpy, surface->drawable.pixmap); if (surface->gc.xid) XCBFreeGC (surface->dpy, surface->gc); surface->dpy = NULL; return CAIRO_STATUS_SUCCESS; } static int _bits_per_pixel(XCBConnection *c, int depth) { XCBFORMAT *fmt = XCBConnSetupSuccessRepPixmapFormats(XCBGetSetup(c)); XCBFORMAT *fmtend = fmt + XCBConnSetupSuccessRepPixmapFormatsLength(XCBGetSetup(c)); for(; fmt != fmtend; ++fmt) if(fmt->depth == depth) return fmt->bits_per_pixel; if(depth <= 4) return 4; if(depth <= 8) return 8; if(depth <= 16) return 16; return 32; } static int _bytes_per_line(XCBConnection *c, int width, int bpp) { int bitmap_pad = XCBGetSetup(c)->bitmap_format_scanline_pad; return ((bpp * width + bitmap_pad - 1) & -bitmap_pad) >> 3; } static cairo_bool_t _CAIRO_MASK_FORMAT (cairo_format_masks_t *masks, cairo_format_t *format) { switch (masks->bpp) { case 32: if (masks->alpha_mask == 0xff000000 && masks->red_mask == 0x00ff0000 && masks->green_mask == 0x0000ff00 && masks->blue_mask == 0x000000ff) { *format = CAIRO_FORMAT_ARGB32; return 1; } if (masks->alpha_mask == 0x00000000 && masks->red_mask == 0x00ff0000 && masks->green_mask == 0x0000ff00 && masks->blue_mask == 0x000000ff) { *format = CAIRO_FORMAT_RGB24; return 1; } break; case 8: if (masks->alpha_mask == 0xff) { *format = CAIRO_FORMAT_A8; return 1; } break; case 1: if (masks->alpha_mask == 0x1) { *format = CAIRO_FORMAT_A1; return 1; } break; } return 0; } static cairo_status_t _get_image_surface (cairo_xcb_surface_t *surface, cairo_rectangle_t *interest_rect, cairo_image_surface_t **image_out, cairo_rectangle_t *image_rect) { cairo_image_surface_t *image; XCBGetImageRep *imagerep; int bpp, bytes_per_line; int x1, y1, x2, y2; unsigned char *data; cairo_format_t format; cairo_format_masks_t masks; x1 = 0; y1 = 0; x2 = surface->width; y2 = surface->height; if (interest_rect) { cairo_rectangle_t rect; rect.x = interest_rect->x; rect.y = interest_rect->y; rect.width = interest_rect->width; rect.height = interest_rect->height; if (rect.x > x1) x1 = rect.x; if (rect.y > y1) y1 = rect.y; if (rect.x + rect.width < x2) x2 = rect.x + rect.width; if (rect.y + rect.height < y2) y2 = rect.y + rect.height; if (x1 >= x2 || y1 >= y2) { *image_out = NULL; return CAIRO_STATUS_SUCCESS; } } if (image_rect) { image_rect->x = x1; image_rect->y = y1; image_rect->width = x2 - x1; image_rect->height = y2 - y1; } /* XXX: This should try to use the XShm extension if available */ if (surface->use_pixmap == 0) { XCBGenericError *error; imagerep = XCBGetImageReply(surface->dpy, XCBGetImage(surface->dpy, ZPixmap, surface->drawable, x1, y1, x2 - x1, y2 - y1, AllPlanes), &error); /* If we get an error, the surface must have been a window, * so retry with the safe code path. */ if (error) surface->use_pixmap = CAIRO_ASSUME_PIXMAP; } else { surface->use_pixmap--; imagerep = NULL; } if (!imagerep) { /* XCBGetImage 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 */ XCBDRAWABLE drawable; drawable.pixmap = XCBPIXMAPNew (surface->dpy); XCBCreatePixmap (surface->dpy, surface->depth, drawable.pixmap, surface->drawable, x2 - x1, y2 - y1); _cairo_xcb_surface_ensure_gc (surface); XCBCopyArea (surface->dpy, surface->drawable, drawable, surface->gc, x1, y1, 0, 0, x2 - x1, y2 - y1); imagerep = XCBGetImageReply(surface->dpy, XCBGetImage(surface->dpy, ZPixmap, drawable, x1, y1, x2 - x1, y2 - y1, AllPlanes), 0); XCBFreePixmap (surface->dpy, drawable.pixmap); } if (!imagerep) return CAIRO_STATUS_NO_MEMORY; bpp = _bits_per_pixel(surface->dpy, imagerep->depth); bytes_per_line = _bytes_per_line(surface->dpy, surface->width, bpp); data = malloc (bytes_per_line * surface->height); if (data == NULL) { free (imagerep); return CAIRO_STATUS_NO_MEMORY; } memcpy (data, XCBGetImageData (imagerep), bytes_per_line * surface->height); free (imagerep); /* * Compute the pixel format masks from either an XCBVISUALTYPE or * a XCBRenderPCTFORMINFO, failing we assume the drawable is an * alpha-only pixmap as it could only have been created that way * through the cairo_xlib_surface_create_for_bitmap function. */ if (surface->visual) { masks.bpp = bpp; masks.alpha_mask = 0; masks.red_mask = surface->visual->red_mask; masks.green_mask = surface->visual->green_mask; masks.blue_mask = surface->visual->blue_mask; } else if (surface->has_format) { masks.bpp = bpp; masks.red_mask = (unsigned long)surface->format.direct.red_mask << surface->format.direct.red_shift; masks.green_mask = (unsigned long)surface->format.direct.green_mask << surface->format.direct.green_shift; masks.blue_mask = (unsigned long)surface->format.direct.blue_mask << surface->format.direct.blue_shift; masks.alpha_mask = (unsigned long)surface->format.direct.alpha_mask << surface->format.direct.alpha_shift; } else { masks.bpp = bpp; masks.red_mask = 0; masks.green_mask = 0; masks.blue_mask = 0; if (surface->depth < 32) masks.alpha_mask = (1 << surface->depth) - 1; else masks.alpha_mask = 0xffffffff; } /* * Prefer to use a standard pixman format instead of the * general masks case. */ if (_CAIRO_MASK_FORMAT (&masks, &format)) { image = (cairo_image_surface_t *) cairo_image_surface_create_for_data (data, format, x2 - x1, y2 - y1, bytes_per_line); if (image->base.status) goto FAIL; } else { /* * XXX This can't work. We must convert the data to one of the * supported pixman formats. Pixman needs another function * which takes data in an arbitrary format and converts it * to something supported by that library. */ image = (cairo_image_surface_t *) _cairo_image_surface_create_with_masks (data, &masks, x2 - x1, y2 - y1, bytes_per_line); if (image->base.status) goto FAIL; } /* Let the surface take ownership of the data */ _cairo_image_surface_assume_ownership_of_data (image); *image_out = image; return CAIRO_STATUS_SUCCESS; FAIL: free (data); return CAIRO_STATUS_NO_MEMORY; } static void _cairo_xcb_surface_ensure_gc (cairo_xcb_surface_t *surface) { if (surface->gc.xid) return; surface->gc = XCBGCONTEXTNew(surface->dpy); XCBCreateGC (surface->dpy, surface->gc, surface->drawable, 0, 0); } static cairo_status_t _draw_image_surface (cairo_xcb_surface_t *surface, cairo_image_surface_t *image, int dst_x, int dst_y) { int bpp, data_len; _cairo_xcb_surface_ensure_gc (surface); bpp = _bits_per_pixel(surface->dpy, image->depth); data_len = _bytes_per_line(surface->dpy, image->width, bpp) * image->height; XCBPutImage(surface->dpy, ZPixmap, surface->drawable, surface->gc, image->width, image->height, dst_x, dst_y, /* left_pad */ 0, image->depth, data_len, image->data); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_xcb_surface_acquire_source_image (void *abstract_surface, cairo_image_surface_t **image_out, void **image_extra) { cairo_xcb_surface_t *surface = abstract_surface; cairo_image_surface_t *image; cairo_status_t status; status = _get_image_surface (surface, NULL, &image, NULL); if (status) return status; *image_out = image; *image_extra = NULL; return CAIRO_STATUS_SUCCESS; } static void _cairo_xcb_surface_release_source_image (void *abstract_surface, cairo_image_surface_t *image, void *image_extra) { cairo_surface_destroy (&image->base); } static cairo_status_t _cairo_xcb_surface_acquire_dest_image (void *abstract_surface, cairo_rectangle_t *interest_rect, cairo_image_surface_t **image_out, cairo_rectangle_t *image_rect_out, void **image_extra) { cairo_xcb_surface_t *surface = abstract_surface; cairo_image_surface_t *image; cairo_status_t status; status = _get_image_surface (surface, interest_rect, &image, image_rect_out); if (status) return status; *image_out = image; *image_extra = NULL; return CAIRO_STATUS_SUCCESS; } static void _cairo_xcb_surface_release_dest_image (void *abstract_surface, cairo_rectangle_t *interest_rect, cairo_image_surface_t *image, cairo_rectangle_t *image_rect, void *image_extra) { cairo_xcb_surface_t *surface = abstract_surface; /* ignore errors */ _draw_image_surface (surface, image, image_rect->x, image_rect->y); cairo_surface_destroy (&image->base); } static cairo_status_t _cairo_xcb_surface_clone_similar (void *abstract_surface, cairo_surface_t *src, cairo_surface_t **clone_out) { cairo_xcb_surface_t *surface = abstract_surface; cairo_xcb_surface_t *clone; if (src->backend == surface->base.backend ) { cairo_xcb_surface_t *xcb_src = (cairo_xcb_surface_t *)src; if (xcb_src->dpy == surface->dpy) { *clone_out = src; cairo_surface_reference (src); return CAIRO_STATUS_SUCCESS; } } else if (_cairo_surface_is_image (src)) { cairo_image_surface_t *image_src = (cairo_image_surface_t *)src; cairo_content_t content = _cairo_content_from_format (image_src->format); if (surface->base.status) return surface->base.status; clone = (cairo_xcb_surface_t *) _cairo_xcb_surface_create_similar (surface, content, image_src->width, image_src->height); if (clone->base.status) return CAIRO_STATUS_NO_MEMORY; _draw_image_surface (clone, image_src, 0, 0); *clone_out = &clone->base; return CAIRO_STATUS_SUCCESS; } return CAIRO_INT_STATUS_UNSUPPORTED; } static cairo_status_t _cairo_xcb_surface_set_matrix (cairo_xcb_surface_t *surface, cairo_matrix_t *matrix) { XCBRenderTRANSFORM xtransform; if (!surface->picture.xid) return CAIRO_STATUS_SUCCESS; xtransform.matrix11 = _cairo_fixed_from_double (matrix->xx); xtransform.matrix12 = _cairo_fixed_from_double (matrix->xy); xtransform.matrix13 = _cairo_fixed_from_double (matrix->x0); xtransform.matrix21 = _cairo_fixed_from_double (matrix->yx); xtransform.matrix22 = _cairo_fixed_from_double (matrix->yy); xtransform.matrix23 = _cairo_fixed_from_double (matrix->y0); xtransform.matrix31 = 0; xtransform.matrix32 = 0; xtransform.matrix33 = _cairo_fixed_from_double (1); if (!CAIRO_SURFACE_RENDER_HAS_PICTURE_TRANSFORM (surface)) { static const XCBRenderTRANSFORM identity = { 1 << 16, 0x00000, 0x00000, 0x00000, 1 << 16, 0x00000, 0x00000, 0x00000, 1 << 16 }; if (memcmp (&xtransform, &identity, sizeof (XCBRenderTRANSFORM)) == 0) return CAIRO_STATUS_SUCCESS; return CAIRO_INT_STATUS_UNSUPPORTED; } XCBRenderSetPictureTransform (surface->dpy, surface->picture, xtransform); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_xcb_surface_set_filter (cairo_xcb_surface_t *surface, cairo_filter_t filter) { char *render_filter; if (!surface->picture.xid) 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 CAIRO_INT_STATUS_UNSUPPORTED; } switch (filter) { case CAIRO_FILTER_FAST: render_filter = "fast"; break; case CAIRO_FILTER_GOOD: render_filter = "good"; break; case CAIRO_FILTER_BEST: render_filter = "best"; break; case CAIRO_FILTER_NEAREST: render_filter = "nearest"; break; case CAIRO_FILTER_BILINEAR: render_filter = "bilinear"; break; default: render_filter = "best"; break; } XCBRenderSetPictureFilter(surface->dpy, surface->picture, strlen(render_filter), render_filter, 0, NULL); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_xcb_surface_set_repeat (cairo_xcb_surface_t *surface, int repeat) { CARD32 mask = XCBRenderCPRepeat; CARD32 pa[] = { repeat }; if (!surface->picture.xid) return CAIRO_STATUS_SUCCESS; XCBRenderChangePicture (surface->dpy, surface->picture, mask, pa); return CAIRO_STATUS_SUCCESS; } static cairo_int_status_t _cairo_xcb_surface_set_attributes (cairo_xcb_surface_t *surface, cairo_surface_attributes_t *attributes) { cairo_int_status_t status; status = _cairo_xcb_surface_set_matrix (surface, &attributes->matrix); if (status) return status; switch (attributes->extend) { case CAIRO_EXTEND_NONE: _cairo_xcb_surface_set_repeat (surface, 0); break; case CAIRO_EXTEND_REPEAT: _cairo_xcb_surface_set_repeat (surface, 1); break; case CAIRO_EXTEND_REFLECT: return CAIRO_INT_STATUS_UNSUPPORTED; } status = _cairo_xcb_surface_set_filter (surface, attributes->filter); if (status) return status; return CAIRO_STATUS_SUCCESS; } static int _render_operator (cairo_operator_t operator) { switch (operator) { case CAIRO_OPERATOR_CLEAR: return XCBRenderPictOpClear; case CAIRO_OPERATOR_SOURCE: return XCBRenderPictOpSrc; case CAIRO_OPERATOR_DEST: return XCBRenderPictOpDst; case CAIRO_OPERATOR_OVER: return XCBRenderPictOpOver; case CAIRO_OPERATOR_DEST_OVER: return XCBRenderPictOpOverReverse; case CAIRO_OPERATOR_IN: return XCBRenderPictOpIn; case CAIRO_OPERATOR_DEST_IN: return XCBRenderPictOpInReverse; case CAIRO_OPERATOR_OUT: return XCBRenderPictOpOut; case CAIRO_OPERATOR_DEST_OUT: return XCBRenderPictOpOutReverse; case CAIRO_OPERATOR_ATOP: return XCBRenderPictOpAtop; case CAIRO_OPERATOR_DEST_ATOP: return XCBRenderPictOpAtopReverse; case CAIRO_OPERATOR_XOR: return XCBRenderPictOpXor; case CAIRO_OPERATOR_ADD: return XCBRenderPictOpAdd; case CAIRO_OPERATOR_SATURATE: return XCBRenderPictOpSaturate; default: return XCBRenderPictOpOver; } } static cairo_int_status_t _cairo_xcb_surface_composite (cairo_operator_t operator, cairo_pattern_t *src_pattern, 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_surface_attributes_t src_attr, mask_attr; cairo_xcb_surface_t *dst = abstract_dst; cairo_xcb_surface_t *src; cairo_xcb_surface_t *mask; cairo_int_status_t status; if (!CAIRO_SURFACE_RENDER_HAS_COMPOSITE (dst)) return CAIRO_INT_STATUS_UNSUPPORTED; status = _cairo_pattern_acquire_surfaces (src_pattern, mask_pattern, &dst->base, src_x, src_y, mask_x, mask_y, width, height, (cairo_surface_t **) &src, (cairo_surface_t **) &mask, &src_attr, &mask_attr); if (status) return status; status = _cairo_xcb_surface_set_attributes (src, &src_attr); if (status == CAIRO_STATUS_SUCCESS) { if (mask) { status = _cairo_xcb_surface_set_attributes (mask, &mask_attr); if (status == CAIRO_STATUS_SUCCESS) XCBRenderComposite (dst->dpy, _render_operator (operator), src->picture, mask->picture, dst->picture, src_x + src_attr.x_offset, src_y + src_attr.y_offset, mask_x + mask_attr.x_offset, mask_y + mask_attr.y_offset, dst_x, dst_y, width, height); } else { static XCBRenderPICTURE maskpict = { 0 }; XCBRenderComposite (dst->dpy, _render_operator (operator), src->picture, maskpict, dst->picture, src_x + src_attr.x_offset, src_y + src_attr.y_offset, 0, 0, dst_x, dst_y, width, height); } } if (mask) _cairo_pattern_release_surface (mask_pattern, &mask->base, &mask_attr); _cairo_pattern_release_surface (src_pattern, &src->base, &src_attr); return status; } static cairo_int_status_t _cairo_xcb_surface_fill_rectangles (void *abstract_surface, cairo_operator_t operator, const cairo_color_t *color, cairo_rectangle_t *rects, int num_rects) { cairo_xcb_surface_t *surface = abstract_surface; XCBRenderCOLOR render_color; if (!CAIRO_SURFACE_RENDER_HAS_FILL_RECTANGLE (surface)) return CAIRO_INT_STATUS_UNSUPPORTED; render_color.red = color->red_short; render_color.green = color->green_short; render_color.blue = color->blue_short; render_color.alpha = color->alpha_short; /* XXX: This XCBRECTANGLE cast is evil... it needs to go away somehow. */ XCBRenderFillRectangles (surface->dpy, _render_operator (operator), surface->picture, render_color, num_rects, (XCBRECTANGLE *) rects); return CAIRO_STATUS_SUCCESS; } static cairo_int_status_t _cairo_xcb_surface_composite_trapezoids (cairo_operator_t operator, cairo_pattern_t *pattern, void *abstract_dst, 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_surface_attributes_t attributes; cairo_xcb_surface_t *dst = abstract_dst; cairo_xcb_surface_t *src; cairo_int_status_t status; int render_reference_x, render_reference_y; int render_src_x, render_src_y; XCBRenderPICTFORMINFO render_format; if (!CAIRO_SURFACE_RENDER_HAS_TRAPEZOIDS (dst)) return CAIRO_INT_STATUS_UNSUPPORTED; status = _cairo_pattern_acquire_surface (pattern, &dst->base, src_x, src_y, width, height, (cairo_surface_t **) &src, &attributes); if (status) return status; if (traps[0].left.p1.y < traps[0].left.p2.y) { render_reference_x = _cairo_fixed_integer_floor (traps[0].left.p1.x); render_reference_y = _cairo_fixed_integer_floor (traps[0].left.p1.y); } else { render_reference_x = _cairo_fixed_integer_floor (traps[0].left.p2.x); render_reference_y = _cairo_fixed_integer_floor (traps[0].left.p2.y); } render_src_x = src_x + render_reference_x - dst_x; render_src_y = src_y + render_reference_y - dst_y; /* XXX: The XTrapezoid cast is evil and needs to go away somehow. */ /* XXX: _format_from_cairo is slow. should cache something. */ render_format = _format_from_cairo (dst->dpy, CAIRO_FORMAT_A8), status = _cairo_xcb_surface_set_attributes (src, &attributes); if (status == CAIRO_STATUS_SUCCESS) XCBRenderTrapezoids (dst->dpy, _render_operator (operator), src->picture, dst->picture, render_format.id, render_src_x + attributes.x_offset, render_src_y + attributes.y_offset, num_traps, (XCBRenderTRAP *) traps); _cairo_pattern_release_surface (pattern, &src->base, &attributes); return status; } static cairo_int_status_t _cairo_xcb_surface_get_extents (void *abstract_surface, cairo_rectangle_t *rectangle) { cairo_xcb_surface_t *surface = abstract_surface; rectangle->x = 0; rectangle->y = 0; rectangle->width = surface->width; rectangle->height = surface->height; return CAIRO_STATUS_SUCCESS; } static const cairo_surface_backend_t cairo_xcb_surface_backend = { _cairo_xcb_surface_create_similar, _cairo_xcb_surface_finish, _cairo_xcb_surface_acquire_source_image, _cairo_xcb_surface_release_source_image, _cairo_xcb_surface_acquire_dest_image, _cairo_xcb_surface_release_dest_image, _cairo_xcb_surface_clone_similar, _cairo_xcb_surface_composite, _cairo_xcb_surface_fill_rectangles, _cairo_xcb_surface_composite_trapezoids, NULL, /* copy_page */ NULL, /* show_page */ NULL, /* _cairo_xcb_surface_set_clip_region */ NULL, /* intersect_clip_path */ _cairo_xcb_surface_get_extents, NULL /* show_glyphs */ }; /** * _cairo_surface_is_xcb: * @surface: a #cairo_surface_t * * Checks if a surface is a #cairo_xcb_surface_t * * Return value: True if the surface is an xcb surface **/ static cairo_bool_t _cairo_surface_is_xcb (cairo_surface_t *surface) { return surface->backend == &cairo_xcb_surface_backend; } static void query_render_version (XCBConnection *c, cairo_xcb_surface_t *surface) { XCBRenderQueryVersionRep *r; surface->render_major = -1; surface->render_minor = -1; if (!XCBRenderInit(c)) return; r = XCBRenderQueryVersionReply(c, XCBRenderQueryVersion(c, 0, 6), 0); if (!r) return; surface->render_major = r->major_version; surface->render_minor = r->minor_version; free(r); } static cairo_surface_t * _cairo_xcb_surface_create_internal (XCBConnection *dpy, XCBDRAWABLE drawable, XCBVISUALTYPE *visual, XCBRenderPICTFORMINFO *format, int width, int height, int depth) { cairo_xcb_surface_t *surface; surface = malloc (sizeof (cairo_xcb_surface_t)); if (surface == NULL) { _cairo_error (CAIRO_STATUS_NO_MEMORY); return (cairo_surface_t*) &_cairo_surface_nil; } _cairo_surface_init (&surface->base, &cairo_xcb_surface_backend); surface->dpy = dpy; surface->gc.xid = 0; surface->drawable = drawable; surface->owns_pixmap = FALSE; surface->visual = visual; if (format) { surface->format = *format; surface->has_format = 1; } else { surface->format.id.xid = 0; surface->has_format = 0; } surface->use_pixmap = 0; surface->width = width; surface->height = height; surface->depth = depth; if (format) { surface->depth = format->depth; } else if (visual) { XCBSCREENIter roots; XCBDEPTHIter depths; XCBVISUALTYPEIter visuals; /* This is ugly, but we have to walk over all visuals * for the display to find the depth. */ roots = XCBConnSetupSuccessRepRootsIter(XCBGetSetup(surface->dpy)); for(; roots.rem; XCBSCREENNext(&roots)) { depths = XCBSCREENAllowedDepthsIter(roots.data); for(; depths.rem; XCBDEPTHNext(&depths)) { visuals = XCBDEPTHVisualsIter(depths.data); for(; visuals.rem; XCBVISUALTYPENext(&visuals)) { if(visuals.data->visual_id.id == visual->visual_id.id) { surface->depth = depths.data->depth; goto found; } } } } found: ; } query_render_version(dpy, surface); surface->picture.xid = 0; if (CAIRO_SURFACE_RENDER_HAS_CREATE_PICTURE (surface)) { XCBRenderPICTFORMAT pict_format = {0}; XCBRenderPICTFORMINFO format_info; surface->picture = XCBRenderPICTURENew(dpy); if (!format) { if (visual) { pict_format = format_from_visual (dpy, visual->visual_id); } else if (depth == 1) { format_info = _format_from_cairo (dpy, CAIRO_FORMAT_A1); pict_format = format_info.id; } XCBRenderCreatePicture (dpy, surface->picture, drawable, pict_format, 0, NULL); } else { XCBRenderCreatePicture (dpy, surface->picture, drawable, format->id, 0, NULL); } } return (cairo_surface_t *) surface; } /** * cairo_xcb_surface_create: * @c: an XCB connection * @drawable: an XCB drawable * @visual: the visual to use for drawing to @drawable. The depth * of the visual must match the depth of the drawable. * Currently, only TrueColor visuals are fully supported. * @width: the current width of @drawable. * @height: the current height of @drawable. * * Creates an XCB surface that draws to the given drawable. * The way that colors are represented in the drawable is specified * by the provided visual. * * NOTE: If @drawable is a window, then the function * cairo_xcb_surface_set_size must be called whenever the size of the * window changes. * * Return value: the newly created surface **/ cairo_surface_t * cairo_xcb_surface_create (XCBConnection *c, XCBDRAWABLE drawable, XCBVISUALTYPE *visual, int width, int height) { return _cairo_xcb_surface_create_internal (c, drawable, visual, NULL, width, height, 0); } /** * cairo_xcb_surface_create_for_bitmap: * @c: an XCB connection * @bitmap: an XCB Pixmap (a depth-1 pixmap) * @width: the current width of @bitmap * @height: the current height of @bitmap * * Creates an XCB surface that draws to the given bitmap. * This will be drawn to as a CAIRO_FORMAT_A1 object. * * Return value: the newly created surface **/ cairo_surface_t * cairo_xcb_surface_create_for_bitmap (XCBConnection *c, XCBPIXMAP bitmap, int width, int height) { XCBDRAWABLE drawable; drawable.pixmap = bitmap; return _cairo_xcb_surface_create_internal (c, drawable, NULL, NULL, width, height, 1); } /** * cairo_xcb_surface_create_with_xrender_format: * @c: an XCB connection * @drawable: an XCB drawable * @format: the picture format to use for drawing to @drawable. The * depth of @format mush match the depth of the drawable. * @width: the current width of @drawable * @height: the current height of @drawable * * Creates an XCB surface that draws to the given drawable. * The way that colors are represented in the drawable is specified * by the provided picture format. * * NOTE: If @drawable is a Window, then the function * cairo_xlib_surface_set_size must be called whenever the size of the * window changes. * * Return value: the newly created surface. **/ cairo_surface_t * cairo_xcb_surface_create_with_xrender_format (XCBConnection *c, XCBDRAWABLE drawable, XCBRenderPICTFORMINFO *format, int width, int height) { return _cairo_xcb_surface_create_internal (c, drawable, NULL, format, width, height, 0); } /** * cairo_xcb_surface_set_size: * @surface: a #cairo_surface_t for the XCB backend * @width: the new width of the surface * @height: the new height of the surface * * Informs cairo of the new size of the XCB drawable underlying the * surface. For a surface created for a window (rather than a pixmap), * this function must be called each time the size of the window * changes. (For a subwindow, you are normally resizing the window * yourself, but for a toplevel window, it is necessary to listen for * ConfigureNotify events.) * * A pixmap can never change size, so it is never necessary to call * this function on a surface created for a pixmap. **/ void cairo_xcb_surface_set_size (cairo_surface_t *surface, int width, int height) { cairo_xcb_surface_t *xcb_surface = (cairo_xcb_surface_t *)surface; /* XXX: How do we want to handle this error case? */ if (! _cairo_surface_is_xcb (surface)) return; xcb_surface->width = width; xcb_surface->height = height; }