diff options
author | Carl Worth <cworth@cworth.org> | 2005-05-17 06:05:13 +0000 |
---|---|---|
committer | Carl Worth <cworth@cworth.org> | 2005-05-17 06:05:13 +0000 |
commit | 0c05b23b3165ec6908c28f56b3446cf43dff44a2 (patch) | |
tree | 88a37f1ff47b08ea10968a8c09e41f92f209b40d /src | |
parent | 84bc5a32d6b3ad366698c14a01f7a4898f1cd25c (diff) |
Rework of cairo_xlib_surface create functions by Keith Packard:
Add cairo_xlib_surface_create_with_render_format.
Reduce Xlib constructors down to two simple forms. Add width, height to constructors and eliminate any synchronous size queries from the implementation.
Update to match new cairo_xlib_surface_t create functions.
Diffstat (limited to 'src')
-rw-r--r-- | src/cairo-xlib-surface.c | 453 | ||||
-rw-r--r-- | src/cairo-xlib-xrender.h | 62 | ||||
-rw-r--r-- | src/cairo-xlib.h | 15 |
3 files changed, 309 insertions, 221 deletions
diff --git a/src/cairo-xlib-surface.c b/src/cairo-xlib-surface.c index d4e38692..e4d65e0e 100644 --- a/src/cairo-xlib-surface.c +++ b/src/cairo-xlib-surface.c @@ -36,6 +36,7 @@ #include "cairoint.h" #include "cairo-xlib.h" +#include "cairo-xlib-xrender.h" #include "cairo-xlib-test.h" #include <X11/extensions/Xrender.h> @@ -45,14 +46,16 @@ typedef int (*cairo_xlib_error_func_t) (Display *display, typedef struct _cairo_xlib_surface cairo_xlib_surface_t; -typedef enum { - CAIRO_XLIB_UNKNOWN, - CAIRO_XLIB_PIXMAP, - CAIRO_XLIB_WINDOW -} cairo_xlib_drawable_type_t; - static void _cairo_xlib_surface_ensure_gc (cairo_xlib_surface_t *surface); +/* + * 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 + struct _cairo_xlib_surface { cairo_surface_t base; @@ -61,17 +64,18 @@ struct _cairo_xlib_surface { Drawable drawable; cairo_bool_t owns_pixmap; Visual *visual; - cairo_format_t format; + + int use_pixmap; int render_major; int render_minor; - cairo_xlib_drawable_type_t type; int width; int height; int depth; Picture picture; + XRenderPictFormat *format; }; #define CAIRO_SURFACE_RENDER_AT_LEAST(surface, major, minor) \ @@ -132,6 +136,24 @@ _CAIRO_FORMAT_DEPTH (cairo_format_t format) } } +static XRenderPictFormat * +_CAIRO_FORMAT_XRENDER_FORMAT(Display *dpy, cairo_format_t format) +{ + int pict_format; + switch (format) { + case CAIRO_FORMAT_A1: + pict_format = PictStandardA1; break; + case CAIRO_FORMAT_A8: + pict_format = PictStandardA8; break; + case CAIRO_FORMAT_RGB24: + pict_format = PictStandardRGB24; break; + case CAIRO_FORMAT_ARGB32: + default: + pict_format = PictStandardARGB32; break; + } + return XRenderFindStandardFormat (dpy, pict_format); +} + static cairo_surface_t * _cairo_xlib_surface_create_similar (void *abstract_src, cairo_format_t format, @@ -144,6 +166,9 @@ _cairo_xlib_surface_create_similar (void *abstract_src, int scr; Pixmap pix; cairo_xlib_surface_t *surface; + int depth = _CAIRO_FORMAT_DEPTH (format); + XRenderPictFormat *xrender_format = _CAIRO_FORMAT_XRENDER_FORMAT (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 @@ -156,11 +181,12 @@ _cairo_xlib_surface_create_similar (void *abstract_src, pix = XCreatePixmap (dpy, DefaultRootWindow (dpy), width <= 0 ? 1 : width, height <= 0 ? 1 : height, - _CAIRO_FORMAT_DEPTH (format)); + depth); surface = (cairo_xlib_surface_t *) - cairo_xlib_surface_create (dpy, pix, format); - cairo_xlib_surface_set_size (&surface->base, width, height); + cairo_xlib_surface_create_with_xrender_format (dpy, pix, + xrender_format, + width, height); surface->owns_pixmap = TRUE; @@ -192,80 +218,44 @@ _noop_error_handler (Display *display, return False; /* return value is ignored */ } -static void -_cairo_xlib_surface_get_size (cairo_xlib_surface_t *surface, - int *width, - int *height) +static cairo_bool_t +_CAIRO_MASK_FORMAT (cairo_format_masks_t *masks, cairo_format_t *format) { - unsigned int width_u, height_u; - Window root_ignore; - int x_ignore, y_ignore; - unsigned int bwidth_ignore, depth_ignore; - - if (surface->width >= 0 && surface->height >= 0) { - *width = surface->width; - *height = surface->height; - return; - } - - /* The first time we get the size for a drawable, we - * also want to determine whether it was a window or - * a pixmap. By calling XGetWindowAttributes(), we'll - * get: - * - * A BadWindow error if the drawable was a pixmap - * The size if the drawable was a window - * - * In the first case, we retry with XGetGeometry(). We - * could reduce all cases to a single round-trip with - * custom Xlib async code, but since this code is never - * hit when we have RENDER, or when the application calls - * cairo_xlib_surface_set_size(), it's probably not - * worth it. - */ - retry: - if (surface->type == CAIRO_XLIB_UNKNOWN) { - cairo_xlib_error_func_t old_handler; - XWindowAttributes xwa; - int result; - - old_handler = XSetErrorHandler (_noop_error_handler); - - result = XGetWindowAttributes (surface->dpy, - surface->drawable, - &xwa); - - XSetErrorHandler (old_handler); - - if (result) { - surface->type = CAIRO_XLIB_WINDOW; - - width_u = xwa.width; - height_u = xwa.height; - } else { - surface->type = CAIRO_XLIB_PIXMAP; - goto retry; + switch (masks->bpp) { + case 32: + if (masks->alpha_mask == 0xff000000 && + masks->red_mask == 0x00ff0000 && + masks->green_mask == 0x0000ff00 && + masks->blue_mask == 0x000000ff) + { + *format = CAIRO_FORMAT_ARGB32; + return True; } - - } else { - - XGetGeometry (surface->dpy, - surface->drawable, - &root_ignore, &x_ignore, &y_ignore, - &width_u, &height_u, - &bwidth_ignore, &depth_ignore); - - if (surface->type == CAIRO_XLIB_PIXMAP) { - /* The size of a pixmap can't change, so we store - * the information to avoid having to get it again - */ - surface->width = width_u; - surface->height = height_u; + if (masks->alpha_mask == 0x00000000 && + masks->red_mask == 0x00ff0000 && + masks->green_mask == 0x0000ff00 && + masks->blue_mask == 0x000000ff) + { + *format = CAIRO_FORMAT_RGB24; + return True; + } + break; + case 8: + if (masks->alpha_mask == 0xff) + { + *format = CAIRO_FORMAT_A8; + return True; + } + break; + case 1: + if (masks->alpha_mask == 0x1) + { + *format = CAIRO_FORMAT_A1; + return True; } + break; } - - *width = width_u; - *height = height_u; + return False; } static cairo_status_t @@ -277,11 +267,13 @@ _get_image_surface (cairo_xlib_surface_t *surface, cairo_image_surface_t *image; XImage *ximage; int x1, y1, x2, y2; + cairo_format_masks_t masks; + cairo_format_t format; x1 = 0; y1 = 0; - - _cairo_xlib_surface_get_size (surface, &x2, &y2); + x2 = surface->width; + y2 = surface->height; if (interest_rect) { cairo_rectangle_t rect; @@ -315,8 +307,34 @@ _get_image_surface (cairo_xlib_surface_t *surface, /* XXX: This should try to use the XShm extension if available */ - retry: - if (surface->type == CAIRO_XLIB_WINDOW) { + if (surface->use_pixmap == 0) + { + cairo_xlib_error_func_t old_handler; + + old_handler = XSetErrorHandler (_noop_error_handler); + + ximage = XGetImage (surface->dpy, + surface->drawable, + x1, y1, + x2 - x1, y2 - y1, + AllPlanes, ZPixmap); + + XSetErrorHandler (old_handler); + + /* If we get an error, the surface must have been a window, + * so retry with the safe code path. + */ + if (!ximage) + surface->use_pixmap = CAIRO_ASSUME_PIXMAP; + } + else + { + surface->use_pixmap--; + ximage = 0; + } + + if (!ximage) + { /* XGetImage from a window is dangerous because it can * produce errors if the window is unmapped or partially @@ -340,34 +358,11 @@ _get_image_surface (cairo_xlib_surface_t *surface, AllPlanes, ZPixmap); XFreePixmap (surface->dpy, pixmap); - - } else { - cairo_xlib_error_func_t old_handler; - - old_handler = XSetErrorHandler (_noop_error_handler); - - ximage = XGetImage (surface->dpy, - surface->drawable, - x1, y1, - x2 - x1, y2 - y1, - AllPlanes, ZPixmap); - - XSetErrorHandler (old_handler); - - /* If we get an error, the surface must have been a window, - * so retry with the safe code path. - */ - if (!ximage) { - surface->type = CAIRO_XLIB_WINDOW; - goto retry; - } } - + if (!ximage) + return CAIRO_STATUS_NO_MEMORY; + if (surface->visual) { - cairo_format_masks_t masks; - - /* XXX: Add support here for pictures with external alpha? */ - masks.bpp = ximage->bits_per_pixel; masks.alpha_mask = 0; masks.red_mask = surface->visual->red_mask; @@ -379,13 +374,40 @@ _get_image_surface (cairo_xlib_surface_t *surface, ximage->width, ximage->height, ximage->bytes_per_line); + } else if (surface->format) { + masks.bpp = ximage->bits_per_pixel; + masks.red_mask = surface->format->direct.redMask << surface->format->direct.red; + masks.green_mask = surface->format->direct.greenMask << surface->format->direct.green; + masks.blue_mask = surface->format->direct.blueMask << surface->format->direct.blue; + masks.alpha_mask = surface->format->direct.alphaMask << surface->format->direct.alpha; } else { - image = (cairo_image_surface_t *) - cairo_image_surface_create_for_data ((unsigned char*) ximage->data, - surface->format, - ximage->width, - ximage->height, - ximage->bytes_per_line); + masks.bpp = ximage->bits_per_pixel; + masks.red_mask = 0; + masks.green_mask = 0; + masks.blue_mask = 0; + masks.alpha_mask = (1 << surface->depth) - 1; + if (!masks.alpha_mask) + masks.alpha_mask = 0xffffffff; + } + + if (_CAIRO_MASK_FORMAT (&masks, &format)) + { + image = (cairo_image_surface_t *) cairo_image_surface_create_for_data ((unsigned char *) ximage->data, + format, + ximage->width, + ximage->height, + ximage->bytes_per_line); + } + else + { + /* XXX This can't work. We must convert the data to one of the + * supported pixman formats + */ + image = _cairo_image_surface_create_with_masks ((unsigned char *) ximage->data, + &masks, + ximage->width, + ximage->height, + ximage->bytes_per_line); } /* Let the surface take ownership of the data */ @@ -423,6 +445,7 @@ _draw_image_surface (cairo_xlib_surface_t *surface, XImage *ximage; unsigned bitmap_pad; + /* XXX this is wrong */ if (image->depth > 16) bitmap_pad = 32; else if (image->depth > 8) @@ -754,10 +777,8 @@ _cairo_xlib_surface_composite (cairo_operator_t operator, return status; status = _cairo_xlib_surface_set_attributes (src, &src_attr); - if (CAIRO_OK (status)) - { - if (mask) - { + if (CAIRO_OK (status)) { + if (mask) { status = _cairo_xlib_surface_set_attributes (mask, &mask_attr); if (CAIRO_OK (status)) XRenderComposite (dst->dpy, @@ -771,9 +792,7 @@ _cairo_xlib_surface_composite (cairo_operator_t operator, mask_y + mask_attr.y_offset, dst_x, dst_y, width, height); - } - else - { + } else { XRenderComposite (dst->dpy, _render_operator (operator), src->picture, @@ -937,16 +956,12 @@ _cairo_xlib_surface_get_extents (void *abstract_surface, cairo_rectangle_t *rectangle) { cairo_xlib_surface_t *surface = abstract_surface; - int width, height; rectangle->x = 0; rectangle->y = 0; - _cairo_xlib_surface_get_size (surface, - &width, &height); - - rectangle->width = width; - rectangle->height = height; + rectangle->width = surface->width; + rectangle->height = surface->height; return CAIRO_STATUS_SUCCESS; } @@ -1001,7 +1016,10 @@ static cairo_surface_t * _cairo_xlib_surface_create_internal (Display *dpy, Drawable drawable, Visual *visual, - cairo_format_t format) + XRenderPictFormat *format, + int width, + int height, + int depth) { cairo_xlib_surface_t *surface; @@ -1011,21 +1029,23 @@ _cairo_xlib_surface_create_internal (Display *dpy, _cairo_surface_init (&surface->base, &cairo_xlib_surface_backend); - surface->type = CAIRO_XLIB_UNKNOWN; - surface->dpy = dpy; surface->gc = NULL; surface->drawable = drawable; surface->owns_pixmap = FALSE; - surface->width = -1; - surface->height = -1; + surface->visual = visual; + surface->format = format; + surface->use_pixmap = 0; + surface->width = width; + surface->height = height; + surface->depth = depth; - if (visual) { + if (format) { + surface->depth = format->depth; + } else if (visual) { int i, j, k; - surface->format = (cairo_format_t)-1; - /* This is ugly, but we have to walk over all visuals * for the display to find the depth. */ @@ -1042,13 +1062,7 @@ _cairo_xlib_surface_create_internal (Display *dpy, } } found: - - surface->visual = visual; - - } else { - surface->format = format; - surface->depth = _CAIRO_FORMAT_DEPTH (format); - surface->visual = NULL; + ; } if (cairo_xlib_render_disabled || @@ -1057,39 +1071,21 @@ _cairo_xlib_surface_create_internal (Display *dpy, surface->render_minor = -1; } - if (CAIRO_SURFACE_RENDER_HAS_CREATE_PICTURE (surface)) { - XRenderPictFormat *render_format; + surface->picture = None; - if (visual) { - render_format = XRenderFindVisualFormat (dpy, visual); - - } else { - - int render_standard; - - switch (format) { - case CAIRO_FORMAT_A1: - render_standard = PictStandardA1; - break; - case CAIRO_FORMAT_A8: - render_standard = PictStandardA8; - break; - case CAIRO_FORMAT_RGB24: - render_standard = PictStandardRGB24; - break; - case CAIRO_FORMAT_ARGB32: - default: - render_standard = PictStandardARGB32; - break; - } + if (CAIRO_SURFACE_RENDER_HAS_CREATE_PICTURE (surface)) { - render_format = XRenderFindStandardFormat (dpy, render_standard); + if (!format) { + if (visual) { + format = XRenderFindVisualFormat (dpy, visual); + } else if (depth == 1) + format = XRenderFindStandardFormat (dpy, PictStandardA1); } - surface->picture = XRenderCreatePicture (dpy, drawable, - render_format, 0, NULL); - } else - surface->picture = None; + if (format) + surface->picture = XRenderCreatePicture (dpy, drawable, + format, 0, NULL); + } return (cairo_surface_t *) surface; } @@ -1098,59 +1094,87 @@ _cairo_xlib_surface_create_internal (Display *dpy, * cairo_xlib_surface_create: * @dpy: an X Display * @drawable: an X Drawable, (a Pixmap or a Window) - * @format: a standard cairo pixel data format. The depth (number of - * of bits used) for the format must match the depth of - * @pixmap. + * @visual: the visual to use for drawing to @drawable. The depth + * of the visual must match the depth of the drawable. + * Currently, only TrueColor visuals are fully supported. + * @width: the current width of @drawable. + * @height: the current height of @drawable. * * Creates an Xlib surface that draws to the given drawable. - * The way that colors are represented in the pixmap is specified - * by giving one of cairo's standard pixel data formats. + * The way that colors are represented in the drawable is specified + * by the provided visual. + * + * NOTE: If @drawable is a Window, then the function + * cairo_xlib_surface_set_size must be called whenever the size of the + * window changes. * - * For maximum efficiency, if you know the size of the drawable, - * you should call cairo_xlib_surface_set_size(). - * * Return value: the newly created surface **/ cairo_surface_t * -cairo_xlib_surface_create (Display *dpy, - Drawable drawable, - cairo_format_t format) +cairo_xlib_surface_create (Display *dpy, + Drawable drawable, + Visual *visual, + int width, + int height) { return _cairo_xlib_surface_create_internal (dpy, drawable, - NULL, format); + visual, NULL, width, height, 0); } /** - * cairo_xlib_surface_create_with_visual: + * cairo_xlib_surface_create_for_bitmap: * @dpy: an X Display - * @drawable: an X Drawable, (a Pixmap or a Window) - * @visual: the visual to use for drawing to @drawable. The depth - * of the visual must match the depth of the drawable. - * Currently, only TrueColor visuals are fully supported. + * @bitmap: an X Drawable, (a depth-1 Pixmap) + * @width: the current width of @bitmap. + * @height: the current height of @bitmap. * + * Creates an Xlib surface that draws to the given bitmap. + * This will be drawn to as a CAIRO_FORMAT_A1 object. + * + * NOTE: If @drawable is a Window, then the function + * cairo_xlib_surface_set_size must be called whenever the size of the + * window changes. + * + * Return value: the newly created surface + **/ +cairo_surface_t * +cairo_xlib_surface_create_for_bitmap (Display *dpy, + Pixmap bitmap, + int width, + int height) +{ + return _cairo_xlib_surface_create_internal (dpy, bitmap, + NULL, NULL, width, height, 1); +} + +/** + * cairo_xlib_surface_create_with_xrender_format: + * @dpy: an X Display + * @drawable: an X Drawable, (a Pixmap or a Window) + * @format: the picture format to use for drawing to @drawable. The depth + * of @format must match the depth of the drawable. + * @width: the current width of @drawable. + * @height: the current height of @drawable. + * * Creates an Xlib surface that draws to the given drawable. * The way that colors are represented in the drawable is specified - * by an X visual. - * - * Normally, you would use this function instead of - * cairo_xlib_surface_create() when you are double-buffering by - * using cairo to draw to a pixmap and then XCopyArea() to copy the - * results to a window. In that case, @visual is the visual of the - * window. + * 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. * - * For maximum efficiency, if you know the size of the drawable, - * you should call cairo_xlib_surface_set_size(). - * * Return value: the newly created surface **/ cairo_surface_t * -cairo_xlib_surface_create_with_visual (Display *dpy, - Drawable drawable, - Visual *visual) +cairo_xlib_surface_create_with_xrender_format (Display *dpy, + Drawable drawable, + XRenderPictFormat *format, + int width, + int height) { return _cairo_xlib_surface_create_internal (dpy, drawable, - visual, - (cairo_format_t)-1); + NULL, format, width, height, 0); } /** @@ -1159,16 +1183,15 @@ cairo_xlib_surface_create_with_visual (Display *dpy, * @width: the new width of the surface * @height: the new height of the surface * - * Informs cairo of the size of the X drawable underlying the - * surface. This allows cairo to avoid querying the server for the - * size, which can be a significant performance bottleneck. + * Informs cairo of the new size of the X Drawable underlying the + * surface. For a surface created for a Window (rather than a Pixmap), + * this function must be called each time the size of the window + * changes. (For a subwindow, you are normally resizing the window + * yourself, but for a toplevel window, it is necessary to listen for + * ConfigureNotify events.) * - * For a surface created for a pixmap, it is only necessary to call - * this function once, since pixmaps have a fixed size. For a surface - * created for a window, you should call this function each time the - * window changes size. (For a subwindow, you are normally resizing - * the window yourself, but for a toplevel window, it is necessary - * to listen for ConfigureNotify events.) + * A Pixmap can never change size, so it is never necessary to call + * this function on a surface created for a Pixmap. **/ void cairo_xlib_surface_set_size (cairo_surface_t *surface, diff --git a/src/cairo-xlib-xrender.h b/src/cairo-xlib-xrender.h new file mode 100644 index 00000000..08a8624e --- /dev/null +++ b/src/cairo-xlib-xrender.h @@ -0,0 +1,62 @@ +/* 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 <cworth@cworth.org> + */ + +#ifndef CAIRO_XLIB_XRENDER_H +#define CAIRO_XLIB_XRENDER_H + +#include <cairo.h> + +#if CAIRO_HAS_XLIB_SURFACE + +#include <X11/extensions/Xrender.h> + +CAIRO_BEGIN_DECLS + +cairo_surface_t * +cairo_xlib_surface_create_with_xrender_format (Display *dpy, + Drawable drawable, + XRenderPictFormat *format, + int width, + int height); + + +CAIRO_END_DECLS + +#else /* CAIRO_HAS_XLIB_SURFACE */ +# error Cairo was not compiled with support for the xlib backend +#endif /* CAIRO_HAS_XLIB_SURFACE */ + +#endif /* CAIRO_XLIB_XRENDER_H */ diff --git a/src/cairo-xlib.h b/src/cairo-xlib.h index 7c71c4e3..3fe285fd 100644 --- a/src/cairo-xlib.h +++ b/src/cairo-xlib.h @@ -46,14 +46,17 @@ CAIRO_BEGIN_DECLS cairo_surface_t * -cairo_xlib_surface_create (Display *dpy, - Drawable drawable, - cairo_format_t format); +cairo_xlib_surface_create (Display *dpy, + Drawable drawable, + Visual *visual, + int width, + int height); cairo_surface_t * -cairo_xlib_surface_create_with_visual (Display *dpy, - Drawable drawable, - Visual *visual); +cairo_xlib_surface_create_for_bitmap (Display *dpy, + Pixmap bitmap, + int width, + int height); void cairo_xlib_surface_set_size (cairo_surface_t *surface, |