diff options
author | Owen Taylor <otaylor@redhat.com> | 2002-01-04 05:58:01 +0000 |
---|---|---|
committer | Owen Taylor <otaylor@src.gnome.org> | 2002-01-04 05:58:01 +0000 |
commit | d12c9702a4428cdf83e6d0dc37b0b9e69fb19f80 (patch) | |
tree | f9d643f1b7ec3efb015346a76f4b412a9e1cd70c /gdk/x11/gdkdrawable-x11.c | |
parent | a755adc58d6bc5ebb1a9c0ffa4825f44307420cf (diff) | |
download | gtk+-d12c9702a4428cdf83e6d0dc37b0b9e69fb19f80.tar.gz |
Private function to tell if we have RENDER extension.
Thu Jan 3 22:18:15 2002 Owen Taylor <otaylor@redhat.com>
* gdk/x11/gdkdrawable-x11.c gdk/x11/gdkprivate-x11.h
(_gdk_x11_have_render): Private function to tell if we have RENDER
extension.
* gdk/x11/gdkgc-x11.c (_gdk_x11_gc_get_fg_picture): Return
None if we don't have RENDER extension.
* gdk/x11/gdkpango-x11.c (gdk_pango_context_get): Don't
use Xft unless we have render extension.
* gdk/x11/gdkdrawable-x11.c (gdk_x11_drawable_get_picture):
Handle missing render extension.
* gdk/gdkdraw.c gdk/gdkdrawable.h gdk/gdkpixmap.c gdk/gdkwindow.c
gdk/gdkinternals.h: Add a private copy_to_image() virtual function
to the GdkDrawable vtable that extends get_image() to allow
copying onto existing images. Make the default implementation of
get_image() use this so that backends don't have to implement
both. Add private wrapper _gdk_drawable_copy_to_image().
* gdk/x11/gdkimage-x11.c gdk/x11/gdkprivate-x11.c
gdk/x11/gdkdrawable-x11.c (_gdk_x11_copy_to_image): Implement
copy_to_image() semantics, speed up by using ShmPixmaps and
XCopyArea when possible, XFlush() after ungrabbing the server,
generally redo the logic once again.
* gdk/gdkinternals.h gdk/x11/gdkimage-x11.c
_gdk_windowing_bits_per_depth(): Function to convert from depth to
bits-per-pixel. (We assume only one bpp per depth - X requires
this.)
* gdk/gdkinternals.h gdk/gdkrgb.c gdk/gdkimage.c: Move the GdkRGB
scratch image code into a generic _gdk_image_get_scratch() chunk
of code that we can use other places we need scratch images.
* gdk/gdkimage.c gdk/x11/gdkimage.c gdk/gdkinternals.h:
Add _gdk_image_new_for_depth() as the backend
to _gdk_image_new() to allowing creating images with
a depth and no visual.
* gdk/gdkpixbuf-drawable.c: Fix so that getting
parts of images not at 0,0 actually works.
* gdk/gdkdrawable.h gdk/gdkinternals.h gdk/gdkdraw.c
gdk/gdkwindow.c gdk/gdkpixmap.c gdk/gdkpixbuf-render.c:
- Add a new GdkDrawableClass vfunc _draw_pixbuf, and
_gdk_draw_pixbuf() [ will be made public later ], to allow
backends to accelerate drawing pixbufs.
- Move the implementation of gdk_pixbuf_render_to_drawable_alpha()
to be the default implementation.
- Update docs for gdk_pixbuf_render_to_drawable_alpha().
- Optimize the default implementation by using
_gdk_image_copy_to_pixmap() and scratch shared images, and
special casing the compositing.
* gdk/x11/gdkdrawable-x11.c: Accelerate _gdk_draw_pixbuf()
with alpha using the RENDER extension.
* gdk/gdkpixbuf-drawable.c (gdk_pixbuf_get_from_drawable):
Optimize by _gdk_image_copy_to_pixmap() and scratch images.
* tests/testrgb.c: Add test for speed of alpha composition,
reduce the number of iterations since alpha composition
can be a bit slow.
* gdk/x11/gdkimage-x11.c gdk/gdkprivate-x11.h (_gdk_x11_image_get_shm_pixmap):
Private function to get a ShmPixmap for an image, if possible.
Diffstat (limited to 'gdk/x11/gdkdrawable-x11.c')
-rw-r--r-- | gdk/x11/gdkdrawable-x11.c | 567 |
1 files changed, 565 insertions, 2 deletions
diff --git a/gdk/x11/gdkdrawable-x11.c b/gdk/x11/gdkdrawable-x11.c index 0ca8d12201..96f6b05f8a 100644 --- a/gdk/x11/gdkdrawable-x11.c +++ b/gdk/x11/gdkdrawable-x11.c @@ -35,6 +35,7 @@ #endif #include <stdlib.h> +#include <string.h> /* for memcpy() */ #if defined (HAVE_IPC_H) && defined (HAVE_SHM_H) && defined (HAVE_XSHM_H) #define USE_SHM @@ -119,6 +120,20 @@ static void gdk_x11_draw_image (GdkDrawable *drawable, gint ydest, gint width, gint height); +#ifdef HAVE_XFT +static void gdk_x11_draw_pixbuf (GdkDrawable *drawable, + GdkGC *gc, + GdkPixbuf *pixbuf, + gint src_x, + gint src_y, + gint dest_x, + gint dest_y, + gint width, + gint height, + GdkRgbDither dither, + gint x_dither, + gint y_dither); +#endif /* HAVE_XFT */ static void gdk_x11_set_colormap (GdkDrawable *drawable, GdkColormap *colormap); @@ -185,6 +200,9 @@ gdk_drawable_impl_x11_class_init (GdkDrawableImplX11Class *klass) drawable_class->draw_lines = gdk_x11_draw_lines; drawable_class->draw_glyphs = gdk_x11_draw_glyphs; drawable_class->draw_image = gdk_x11_draw_image; +#ifdef HAVE_XFT + drawable_class->_draw_pixbuf = gdk_x11_draw_pixbuf; +#endif /* HAVE_XFT */ drawable_class->set_colormap = gdk_x11_set_colormap; drawable_class->get_colormap = gdk_x11_get_colormap; @@ -192,7 +210,7 @@ gdk_drawable_impl_x11_class_init (GdkDrawableImplX11Class *klass) drawable_class->get_depth = gdk_x11_get_depth; drawable_class->get_visual = gdk_x11_get_visual; - drawable_class->get_image = _gdk_x11_get_image; + drawable_class->_copy_to_image = _gdk_x11_copy_to_image; } static void @@ -204,6 +222,17 @@ gdk_drawable_impl_x11_finalize (GObject *object) } #ifdef HAVE_XFT +gboolean +_gdk_x11_have_render (void) +{ + /* This check is cheap, but if we have to do version checks, we will + * need to cache the result since version checks are round-trip + */ + int event_base, error_base; + + return XRenderQueryExtension (gdk_display, &event_base, &error_base); +} + static Picture gdk_x11_drawable_get_picture (GdkDrawable *drawable) { @@ -225,7 +254,8 @@ gdk_x11_drawable_get_picture (GdkDrawable *drawable) } format = XRenderFindVisualFormat (impl->xdisplay, GDK_VISUAL_XVISUAL (visual)); - impl->picture = XRenderCreatePicture (impl->xdisplay, impl->xid, format, 0, NULL); + if (format) + impl->picture = XRenderCreatePicture (impl->xdisplay, impl->xid, format, 0, NULL); } return impl->picture; @@ -752,3 +782,536 @@ gdk_x11_drawable_get_xid (GdkDrawable *drawable) return ((GdkDrawableImplX11 *)impl)->xid; } + +/* Code for accelerated alpha compositing using the RENDER extension. + * It's a bit long because there are lots of possibilities for + * what's the fastest depending on the available picture formats, + * whether we can used shared pixmaps, etc. + */ +#ifdef HAVE_XFT +typedef enum { + FORMAT_NONE, + FORMAT_EXACT_MASK, + FORMAT_ARGB_MASK, + FORMAT_ARGB +} FormatType; + +static FormatType +select_format (Display *xdisplay, + XRenderPictFormat **format, + XRenderPictFormat **mask) +{ + XRenderPictFormat pf; + + +/* Look for a 32-bit xRGB and Axxx formats that exactly match the + * in memory data format. We can use them as pixmap and mask + * to deal with non-premultiplied data. + */ + + pf.type = PictTypeDirect; + pf.depth = 32; + pf.direct.redMask = 0xff; + pf.direct.greenMask = 0xff; + pf.direct.blueMask = 0xff; + + pf.direct.alphaMask = 0; + if (ImageByteOrder (xdisplay) == LSBFirst) + { + /* ABGR */ + pf.direct.red = 0; + pf.direct.green = 8; + pf.direct.blue = 16; + } + else + { + /* RGBA */ + pf.direct.red = 24; + pf.direct.green = 16; + pf.direct.blue = 8; + } + + *format = XRenderFindFormat (xdisplay, + (PictFormatType | PictFormatDepth | + PictFormatRedMask | PictFormatRed | + PictFormatGreenMask | PictFormatGreen | + PictFormatBlueMask | PictFormatBlue | + PictFormatAlphaMask), + &pf, + 0); + + pf.direct.alphaMask = 0xff; + if (ImageByteOrder (xdisplay) == LSBFirst) + { + /* ABGR */ + pf.direct.alpha = 24; + } + else + { + pf.direct.alpha = 0; + } + + *mask = XRenderFindFormat (xdisplay, + (PictFormatType | PictFormatDepth | + PictFormatAlphaMask | PictFormatAlpha), + &pf, + 0); + + if (*format && *mask) + return FORMAT_EXACT_MASK; + + /* OK, that failed, now look for xRGB and Axxx formats in + * RENDER's preferred order + */ + pf.direct.alphaMask = 0; + /* ARGB */ + pf.direct.red = 16; + pf.direct.green = 8; + pf.direct.blue = 0; + + *format = XRenderFindFormat (xdisplay, + (PictFormatType | PictFormatDepth | + PictFormatRedMask | PictFormatRed | + PictFormatGreenMask | PictFormatGreen | + PictFormatBlueMask | PictFormatBlue | + PictFormatAlphaMask), + &pf, + 0); + + pf.direct.alphaMask = 0xff; + pf.direct.alpha = 24; + + *mask = XRenderFindFormat (xdisplay, + (PictFormatType | PictFormatDepth | + PictFormatAlphaMask | PictFormatAlpha), + &pf, + 0); + + if (*format && *mask) + return FORMAT_ARGB_MASK; + + /* Finally, if neither of the above worked, fall back to + * looking for combined ARGB -- we'll premultiply ourselves. + */ + + pf.type = PictTypeDirect; + pf.depth = 32; + pf.direct.red = 16; + pf.direct.green = 8; + pf.direct.blue = 0; + pf.direct.alphaMask = 0xff; + pf.direct.alpha = 24; + + *format = XRenderFindFormat (xdisplay, + (PictFormatType | PictFormatDepth | + PictFormatRedMask | PictFormatRed | + PictFormatGreenMask | PictFormatGreen | + PictFormatBlueMask | PictFormatBlue | + PictFormatAlphaMask | PictFormatAlpha), + &pf, + 0); + *mask = NULL; + + if (*format) + return FORMAT_ARGB; + + return FORMAT_NONE; +} + +#if 0 +static void +list_formats (XRenderPictFormat *pf) +{ + gint i; + + for (i=0 ;; i++) + { + XRenderPictFormat *pf = XRenderFindFormat (impl->xdisplay, 0, NULL, i); + if (pf) + { + g_print ("%2d R-%#06x/%#06x G-%#06x/%#06x B-%#06x/%#06x A-%#06x/%#06x\n", + pf->depth, + pf->direct.red, + pf->direct.redMask, + pf->direct.green, + pf->direct.greenMask, + pf->direct.blue, + pf->direct.blueMask, + pf->direct.alpha, + pf->direct.alphaMask); + } + else + break; + } +} +#endif + +static void +convert_to_format (guchar *src_buf, + gint src_rowstride, + guchar *dest_buf, + gint dest_rowstride, + FormatType dest_format, + GdkByteOrder dest_byteorder, + gint width, + gint height) +{ + gint i; + + if (dest_format == FORMAT_EXACT_MASK && + src_rowstride == dest_rowstride) + { + memcpy (dest_buf, src_buf, height * src_rowstride); + return; + } + + for (i=0; i < height; i++) + { + switch (dest_format) + { + case FORMAT_EXACT_MASK: + { + memcpy (dest_buf + i * dest_rowstride, + src_buf + i * src_rowstride, + width * 4); + break; + } + case FORMAT_ARGB_MASK: + { + guint *p = (guint *)(src_buf + i * src_rowstride); + guint *q = (guint *)(dest_buf + i * dest_rowstride); + guint *end = p + width; + +#if G_BYTE_ORDER == G_LITTLE_ENDIAN + if (dest_byteorder == GDK_LSB_FIRST) + { + /* ABGR => ARGB */ + + while (p < end) + { + *q = ( (*p & 0xff00ff00) | + ((*p & 0x000000ff) << 16) | + ((*p & 0x00ff0000) >> 16)); + q++; + p++; + } + } + else + { + /* ABGR => BGRA */ + + while (p < end) + { + *q = (((*p & 0xff000000) >> 24) | + ((*p & 0x00ffffff) << 8)); + q++; + p++; + } + } +#else /* G_BYTE_ORDER == G_BIG_ENDIAN */ + if (dest_byteorder == GDK_LSB_FIRST) + { + /* RGBA => BGRA */ + + while (p < end) + { + *q = ( (*p & 0x00ff00ff) | + ((*p & 0x0000ff00) << 16) | + ((*p & 0xff000000) >> 16)); + q++; + p++; + } + } + else + { + /* RGBA => ARGB */ + + while (p < end) + { + *q = (((*p & 0xff000000) >> 24) | + ((*p & 0x00ffffff) << 8)); + q++; + p++; + } + } +#endif /* G_BYTE_ORDER*/ + break; + } + case FORMAT_ARGB: + { + guchar *p = (src_buf + i * src_rowstride); + guchar *q = (dest_buf + i * dest_rowstride); + guchar *end = p + 4 * width; + guchar a; + guint t; + +#define MULT(d,c,a) G_STMT_START { t = c * a; d = ((t >> 8) + t) >> 8; } G_STMT_END + + if (dest_byteorder == GDK_LSB_FIRST) + { + while (p < end) + { + a = p[3]; + MULT(q[0], p[2], a); + MULT(q[1], p[1], a); + MULT(q[2], p[0], a); + q[3] = a; + p += 4; + q += 4; + } + } + else + { + while (p < end) + { + a = p[3]; + q[0] = a; + MULT(q[1], p[0], a); + MULT(q[2], p[1], a); + MULT(q[3], p[2], a); + p += 4; + q += 4; + } + } +#undef MULT + break; + } + case FORMAT_NONE: + g_assert_not_reached (); + break; + } + } +} + +static void +draw_with_images (GdkDrawable *drawable, + GdkGC *gc, + FormatType format_type, + XRenderPictFormat *format, + XRenderPictFormat *mask_format, + guchar *src_rgb, + gint src_rowstride, + gint dest_x, + gint dest_y, + gint width, + gint height) +{ + Display *xdisplay = GDK_DRAWABLE_IMPL_X11 (drawable)->xdisplay; + GdkImage *image; + GdkPixmap *pix; + GdkGC *pix_gc; + Picture pict; + Picture dest_pict; + Picture mask = None; + gint x0, y0; + + pix = gdk_pixmap_new (NULL, width, height, 32); + pict = XRenderCreatePicture (xdisplay, + GDK_PIXMAP_XID (pix), + format, 0, NULL); + if (mask_format) + mask = XRenderCreatePicture (xdisplay, + GDK_PIXMAP_XID (pix), + mask_format, 0, NULL); + + dest_pict = gdk_x11_drawable_get_picture (drawable); + + pix_gc = gdk_gc_new (pix); + + for (y0 = 0; y0 < height; y0 += GDK_SCRATCH_IMAGE_HEIGHT) + { + gint height1 = MIN (height - y0, GDK_SCRATCH_IMAGE_HEIGHT); + for (x0 = 0; x0 < width; x0 += GDK_SCRATCH_IMAGE_WIDTH) + { + gint xs0, ys0; + + gint width1 = MIN (width - x0, GDK_SCRATCH_IMAGE_WIDTH); + + image = _gdk_image_get_scratch (width1, height1, 32, &xs0, &ys0); + + convert_to_format (src_rgb + y0 * src_rowstride + 4 * x0, src_rowstride, + image->mem + ys0 * image->bpl + 4 * xs0, image->bpl, + format_type, image->byte_order, + width1, height1); + + gdk_draw_image (pix, pix_gc, + image, xs0, ys0, x0, y0, width1, height1); + } + } + + XRenderComposite (xdisplay, PictOpOver, pict, mask, dest_pict, + 0, 0, 0, 0, dest_x, dest_y, width, height); + + XRenderFreePicture (xdisplay, pict); + if (mask) + XRenderFreePicture (xdisplay, mask); + + g_object_unref (pix); + g_object_unref (pix_gc); +} + +typedef struct _ShmPixmapInfo ShmPixmapInfo; + +struct _ShmPixmapInfo +{ + GdkImage *image; + Pixmap pix; + Picture pict; + Picture mask; +}; + +/* Returns FALSE if we can't get a shm pixmap */ +static gboolean +get_shm_pixmap_for_image (Display *xdisplay, + GdkImage *image, + XRenderPictFormat *format, + XRenderPictFormat *mask_format, + Pixmap *pix, + Picture *pict, + Picture *mask) +{ + ShmPixmapInfo *info; + + if (image->type != GDK_IMAGE_SHARED) + return FALSE; + + info = g_object_get_data (G_OBJECT (image), "gdk-x11-shm-pixmap"); + if (!info) + { + *pix = _gdk_x11_image_get_shm_pixmap (image); + + if (!*pix) + return FALSE; + + info = g_new (ShmPixmapInfo, 1); + info->pix = *pix; + + info->pict = XRenderCreatePicture (xdisplay, info->pix, + format, 0, NULL); + if (mask_format) + info->mask = XRenderCreatePicture (xdisplay, info->pix, + mask_format, 0, NULL); + else + info->mask = None; + + g_object_set_data (G_OBJECT (image), "gdk-x11-shm-pixmap", info); + } + + *pix = info->pix; + *pict = info->pict; + *mask = info->mask; + + return TRUE; +} + +#ifdef USE_SHM +/* Returns FALSE if drawing with ShmPixmaps is not possible */ +static gboolean +draw_with_pixmaps (GdkDrawable *drawable, + GdkGC *gc, + FormatType format_type, + XRenderPictFormat *format, + XRenderPictFormat *mask_format, + guchar *src_rgb, + gint src_rowstride, + gint dest_x, + gint dest_y, + gint width, + gint height) +{ + Display *xdisplay = GDK_DRAWABLE_IMPL_X11 (drawable)->xdisplay; + GdkImage *image; + Pixmap pix; + Picture pict; + Picture dest_pict; + Picture mask = None; + gint x0, y0; + + dest_pict = gdk_x11_drawable_get_picture (drawable); + + for (y0 = 0; y0 < height; y0 += GDK_SCRATCH_IMAGE_HEIGHT) + { + gint height1 = MIN (height - y0, GDK_SCRATCH_IMAGE_HEIGHT); + for (x0 = 0; x0 < width; x0 += GDK_SCRATCH_IMAGE_WIDTH) + { + gint xs0, ys0; + + gint width1 = MIN (width - x0, GDK_SCRATCH_IMAGE_WIDTH); + + image = _gdk_image_get_scratch (width1, height1, 32, &xs0, &ys0); + if (!get_shm_pixmap_for_image (xdisplay, image, format, mask_format, &pix, &pict, &mask)) + return FALSE; + + convert_to_format (src_rgb + y0 * src_rowstride + 4 * x0, src_rowstride, + image->mem + ys0 * image->bpl + 4 * xs0, image->bpl, + format_type, image->byte_order, + width1, height1); + + XRenderComposite (xdisplay, PictOpOver, pict, mask, dest_pict, + xs0, ys0, xs0, ys0, x0 + dest_x, y0 + dest_y, + width1, height1); + } + } + + return TRUE; +} +#endif + +static void +gdk_x11_draw_pixbuf (GdkDrawable *drawable, + GdkGC *gc, + GdkPixbuf *pixbuf, + gint src_x, + gint src_y, + gint dest_x, + gint dest_y, + gint width, + gint height, + GdkRgbDither dither, + gint x_dither, + gint y_dither) +{ + Display *xdisplay = GDK_DRAWABLE_IMPL_X11 (drawable)->xdisplay; + FormatType format_type; + XRenderPictFormat *format, *mask_format; + gint rowstride; +#ifdef USE_SHM + gboolean use_pixmaps = TRUE; +#endif /* USE_SHM */ + + format_type = select_format (xdisplay, &format, &mask_format); + + if (format_type == FORMAT_NONE || + !gdk_pixbuf_get_has_alpha (pixbuf) || + (dither == GDK_RGB_DITHER_MAX && gdk_drawable_get_depth (drawable) != 24)) + { + GdkDrawable *wrapper = GDK_DRAWABLE_IMPL_X11 (drawable)->wrapper; + GDK_DRAWABLE_CLASS (parent_class)->_draw_pixbuf (wrapper, gc, pixbuf, + src_x, src_y, dest_x, dest_y, + width, height, + dither, x_dither, y_dither); + return; + } + + rowstride = gdk_pixbuf_get_rowstride (pixbuf); + +#ifdef USE_SHM + if (use_pixmaps) + { + if (!draw_with_pixmaps (drawable, gc, + format_type, format, mask_format, + gdk_pixbuf_get_pixels (pixbuf) + src_y * rowstride + src_x * 4, + rowstride, + dest_x, dest_y, width, height)) + use_pixmaps = FALSE; + } + + if (!use_pixmaps) +#endif /* USE_SHM */ + draw_with_images (drawable, gc, + format_type, format, mask_format, + gdk_pixbuf_get_pixels (pixbuf) + src_y * rowstride + src_x * 4, + rowstride, + dest_x, dest_y, width, height); +} +#endif /* HAVE_XFT */ |