From d12c9702a4428cdf83e6d0dc37b0b9e69fb19f80 Mon Sep 17 00:00:00 2001 From: Owen Taylor Date: Fri, 4 Jan 2002 05:58:01 +0000 Subject: Private function to tell if we have RENDER extension. Thu Jan 3 22:18:15 2002 Owen Taylor * 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. --- gdk/gdkdraw.c | 557 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 548 insertions(+), 9 deletions(-) (limited to 'gdk/gdkdraw.c') diff --git a/gdk/gdkdraw.c b/gdk/gdkdraw.c index e2d50121b3..c7882c5775 100644 --- a/gdk/gdkdraw.c +++ b/gdk/gdkdraw.c @@ -27,15 +27,34 @@ #include "gdkdrawable.h" #include "gdkinternals.h" #include "gdkwindow.h" - -static GdkDrawable* gdk_drawable_real_get_composite_drawable (GdkDrawable *drawable, - gint x, - gint y, - gint width, - gint height, - gint *composite_x_offset, - gint *composite_y_offset); -static GdkRegion * gdk_drawable_real_get_visible_region (GdkDrawable *drawable); +#include "gdk-pixbuf-private.h" +#include "gdkpixbuf.h" + +static GdkImage* gdk_drawable_real_get_image (GdkDrawable *drawable, + gint x, + gint y, + gint width, + gint height); +static GdkDrawable* gdk_drawable_real_get_composite_drawable (GdkDrawable *drawable, + gint x, + gint y, + gint width, + gint height, + gint *composite_x_offset, + gint *composite_y_offset); +static GdkRegion * gdk_drawable_real_get_visible_region (GdkDrawable *drawable); +static void gdk_drawable_real_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); static void gdk_drawable_class_init (GdkDrawableClass *klass); @@ -70,10 +89,12 @@ gdk_drawable_get_type (void) static void gdk_drawable_class_init (GdkDrawableClass *klass) { + klass->get_image = gdk_drawable_real_get_image; klass->get_composite_drawable = gdk_drawable_real_get_composite_drawable; /* Default implementation for clip and visible region is the same */ klass->get_clip_region = gdk_drawable_real_get_visible_region; klass->get_visible_region = gdk_drawable_real_get_visible_region; + klass->_draw_pixbuf = gdk_drawable_real_draw_pixbuf; } /* Manipulation of drawables @@ -529,6 +550,57 @@ gdk_draw_image (GdkDrawable *drawable, xdest, ydest, width, height); } +/** + * _gdk_draw_pixbuf: + * @drawable: Destination drawable. + * @gc: a #GdkGC, used for clipping, or %NULL + * @pixbuf: a #GdkPixbuf + * @src_x: Source X coordinate within pixbuf. + * @src_y: Source Y coordinates within pixbuf. + * @dest_x: Destination X coordinate within drawable. + * @dest_y: Destination Y coordinate within drawable. + * @width: Width of region to render, in pixels, or -1 to use pixbuf width. + * @height: Height of region to render, in pixels, or -1 to use pixbuf height. + * @dither: Dithering mode for GdkRGB. + * @x_dither: X offset for dither. + * @y_dither: Y offset for dither. + * + * Renders a rectangular portion of a pixbuf to a drawable. The destination + * drawable must have a colormap. All windows have a colormap, however, pixmaps + * only have colormap by default if they were created with a non-NULL window argument. + * Otherwise a colormap must be set on them with gdk_drawable_set_colormap. + * + * On older X servers, rendering pixbufs with an alpha channel involves round trips + * to the X server, and may be somewhat slow. + **/ +void +_gdk_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) +{ + g_return_if_fail (GDK_IS_DRAWABLE (drawable)); + g_return_if_fail (gc == NULL || GDK_IS_GC (gc)); + g_return_if_fail (GDK_IS_PIXBUF (pixbuf)); + + if (width == -1) + width = gdk_pixbuf_get_width (pixbuf); + if (height == -1) + height = gdk_pixbuf_get_height (pixbuf); + + GDK_DRAWABLE_GET_CLASS (drawable)->_draw_pixbuf (drawable, gc, pixbuf, + src_x, src_y, dest_x, dest_y, width, height, + dither, x_dither, y_dither); +} + void gdk_draw_points (GdkDrawable *drawable, GdkGC *gc, @@ -617,6 +689,80 @@ gdk_draw_glyphs (GdkDrawable *drawable, } +/** + * _gdk_drawable_copy_to_image: + * @drawable: a #GdkDrawable + * @image: a #GdkDrawable, or %NULL if a new @image should be created. + * @src_x: x coordinate on @drawable + * @src_y: y coordinate on @drawable + * @dest_x: x coordinate within @image. Must be 0 if @image is %NULL + * @dest_y: y coordinate within @image. Must be 0 if @image is %NULL + * @width: width of region to get + * @height: height or region to get + * + * Copies a portion of @drawable into the client side image structure + * @image. If @image is %NULL, creates a new image of size @width x @height + * and copies into that. See gdk_drawable_get_image() for further details. + * + * Return value: @image, or a new a #GdkImage containing the contents + of @drawable + **/ +GdkImage* +_gdk_drawable_copy_to_image (GdkDrawable *drawable, + GdkImage *image, + gint src_x, + gint src_y, + gint dest_x, + gint dest_y, + gint width, + gint height) +{ + GdkDrawable *composite; + gint composite_x_offset = 0; + gint composite_y_offset = 0; + GdkImage *retval; + GdkColormap *cmap; + + g_return_val_if_fail (GDK_IS_DRAWABLE (drawable), NULL); + g_return_val_if_fail (src_x >= 0, NULL); + g_return_val_if_fail (src_y >= 0, NULL); + + /* FIXME? Note race condition since we get the size then + * get the image, and the size may have changed. + */ + + if (width < 0 || height < 0) + gdk_drawable_get_size (drawable, + width < 0 ? &width : NULL, + height < 0 ? &height : NULL); + + composite = + GDK_DRAWABLE_GET_CLASS (drawable)->get_composite_drawable (drawable, + src_x, src_y, + width, height, + &composite_x_offset, + &composite_y_offset); + + retval = GDK_DRAWABLE_GET_CLASS (composite)->_copy_to_image (composite, + image, + src_x - composite_x_offset, + src_y - composite_y_offset, + dest_x, dest_y, + width, height); + + g_object_unref (G_OBJECT (composite)); + + if (!image && retval) + { + cmap = gdk_drawable_get_colormap (drawable); + + if (cmap) + gdk_image_set_colormap (retval, cmap); + } + + return retval; +} + /** * gdk_drawable_get_image: * @drawable: a #GdkDrawable @@ -705,6 +851,16 @@ gdk_drawable_get_image (GdkDrawable *drawable, return retval; } +static GdkImage* +gdk_drawable_real_get_image (GdkDrawable *drawable, + gint x, + gint y, + gint width, + gint height) +{ + return _gdk_drawable_copy_to_image (drawable, NULL, x, y, 0, 0, width, height); +} + static GdkDrawable* gdk_drawable_real_get_composite_drawable (GdkDrawable *drawable, gint x, @@ -776,3 +932,386 @@ gdk_drawable_real_get_visible_region (GdkDrawable *drawable) return gdk_region_rectangle (&rect); } + +static void +composite (guchar *src_buf, + gint src_rowstride, + guchar *dest_buf, + gint dest_rowstride, + gint width, + gint height) +{ + guchar *src = src_buf; + guchar *dest = dest_buf; + + while (height--) + { + gint twidth = width; + guchar *p = src; + guchar *q = dest; + + while (twidth--) + { + guchar a = p[3]; + guint t; + + t = a * p[0] + (255 - a) * q[0] + 0x80; + q[0] = (t + (t >> 8)) >> 8; + t = a * p[1] + (255 - a) * q[1] + 0x80; + q[1] = (t + (t >> 8)) >> 8; + t = a * p[2] + (255 - a) * q[2] + 0x80; + q[2] = (t + (t >> 8)) >> 8; + + p += 4; + q += 3; + } + + src += src_rowstride; + dest += dest_rowstride; + } +} + +static void +composite_0888 (guchar *src_buf, + gint src_rowstride, + guchar *dest_buf, + gint dest_rowstride, + GdkByteOrder dest_byte_order, + gint width, + gint height) +{ + guchar *src = src_buf; + guchar *dest = dest_buf; + + while (height--) + { + gint twidth = width; + guchar *p = src; + guchar *q = dest; + + if (dest_byte_order == GDK_LSB_FIRST) + { + while (twidth--) + { + guint t; + + t = p[3] * p[2] + (255 - p[3]) * q[0] + 0x80; + q[0] = (t + (t >> 8)) >> 8; + t = p[3] * p[1] + (255 - p[3]) * q[1] + 0x80; + q[1] = (t + (t >> 8)) >> 8; + t = p[3] * p[0] + (255 - p[3]) * q[2] + 0x80; + q[2] = (t + (t >> 8)) >> 8; + p += 4; + q += 4; + } + } + else + { + while (twidth--) + { + guint t; + + t = p[3] * p[0] + (255 - p[3]) * q[1] + 0x80; + q[1] = (t + (t >> 8)) >> 8; + t = p[3] * p[1] + (255 - p[3]) * q[2] + 0x80; + q[2] = (t + (t >> 8)) >> 8; + t = p[3] * p[2] + (255 - p[3]) * q[3] + 0x80; + q[3] = (t + (t >> 8)) >> 8; + p += 4; + q += 4; + } + } + + src += src_rowstride; + dest += dest_rowstride; + } +} + +static void +composite_565 (guchar *src_buf, + gint src_rowstride, + guchar *dest_buf, + gint dest_rowstride, + GdkByteOrder dest_byte_order, + gint width, + gint height) +{ + guchar *src = src_buf; + guchar *dest = dest_buf; + + while (height--) + { + gint twidth = width; + guchar *p = src; + gushort *q = (gushort *)dest; + + while (twidth--) + { + guchar a = p[3]; + guint tr, tg, tb; + guint tr1, tg1, tb1; + guint tmp = *q; + +#if 1 + /* This is fast, and corresponds to what composite() above does + * if we converted to 8-bit first. + */ + tr = (tmp & 0xf800); + tr1 = a * p[0] + (255 - a) * ((tr >> 8) + (tr >> 13)) + 0x80; + tg = (tmp & 0x07e0); + tg1 = a * p[1] + (255 - a) * ((tg >> 3) + (tg >> 9)) + 0x80; + tb = (tmp & 0x001f); + tb1 = a * p[2] + (255 - a) * ((tb << 3) + (tb >> 2)) + 0x80; + + *q = (((tr1 + (tr1 >> 8)) & 0xf800) | + (((tg1 + (tg1 >> 8)) & 0xfc00) >> 5) | + ((tb1 + (tb1 >> 8)) >> 11)); +#else + /* This version correspond to the result we get with XRENDER - + * a bit of precision is lost since we convert to 8 bit after premultiplying + * instead of at the end + */ + guint tr2, tg2, tb2; + guint tr3, tg3, tb3; + + tr = (tmp & 0xf800); + tr1 = (255 - a) * ((tr >> 8) + (tr >> 13)) + 0x80; + tr2 = a * p[0] + 0x80; + tr3 = ((tr1 + (tr1 >> 8)) >> 8) + ((tr2 + (tr2 >> 8)) >> 8); + + tg = (tmp & 0x07e0); + tg1 = (255 - a) * ((tg >> 3) + (tg >> 9)) + 0x80; + tg2 = a * p[0] + 0x80; + tg3 = ((tg1 + (tg1 >> 8)) >> 8) + ((tg2 + (tg2 >> 8)) >> 8); + + tb = (tmp & 0x001f); + tb1 = (255 - a) * ((tb << 3) + (tb >> 2)) + 0x80; + tb2 = a * p[0] + 0x80; + tb3 = ((tb1 + (tb1 >> 8)) >> 8) + ((tb2 + (tb2 >> 8)) >> 8); + + *q = (((tr3 & 0xf8) << 8) | + ((tg3 & 0xfc) << 3) | + ((tb3 >> 3))); +#endif + + p += 4; + q++; + } + + src += src_rowstride; + dest += dest_rowstride; + } +} + +static void +gdk_drawable_real_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) +{ + gboolean free_gc = FALSE; + GdkPixbuf *composited = NULL; + gint dwidth, dheight; + GdkRegion *clip; + GdkRegion *drect; + GdkRectangle tmp_rect; + + g_return_if_fail (GDK_IS_PIXBUF (pixbuf)); + g_return_if_fail (pixbuf->colorspace == GDK_COLORSPACE_RGB); + g_return_if_fail (pixbuf->n_channels == 3 || pixbuf->n_channels == 4); + g_return_if_fail (pixbuf->bits_per_sample == 8); + + g_return_if_fail (drawable != NULL); + + if (width == -1) + width = pixbuf->width; + if (height == -1) + height = pixbuf->height; + + g_return_if_fail (width >= 0 && height >= 0); + g_return_if_fail (src_x >= 0 && src_x + width <= pixbuf->width); + g_return_if_fail (src_y >= 0 && src_y + height <= pixbuf->height); + + /* Clip to the drawable; this is required for get_from_drawable() so + * can't be done implicitly + */ + + if (dest_x < 0) + { + src_x -= dest_x; + width += dest_x; + dest_x = 0; + } + + if (dest_y < 0) + { + src_y -= dest_y; + height += dest_y; + dest_y = 0; + } + + gdk_drawable_get_size (drawable, &dwidth, &dheight); + + if ((dest_x + width) > dwidth) + width = dwidth - dest_x; + + if ((dest_y + height) > dheight) + height = dheight - dest_y; + + if (width <= 0 || height <= 0) + return; + + /* Clip to the clip region; this avoids getting more + * image data from the server than we need to. + */ + + tmp_rect.x = dest_x; + tmp_rect.y = dest_y; + tmp_rect.width = width; + tmp_rect.height = height; + + drect = gdk_region_rectangle (&tmp_rect); + clip = gdk_drawable_get_clip_region (drawable); + + gdk_region_intersect (drect, clip); + + gdk_region_get_clipbox (drect, &tmp_rect); + + gdk_region_destroy (drect); + gdk_region_destroy (clip); + + if (tmp_rect.width == 0 || + tmp_rect.height == 0) + return; + + /* Actually draw */ + + if (!gc) + { + gc = gdk_gc_new (drawable); + free_gc = TRUE; + } + + if (pixbuf->has_alpha) + { + GdkVisual *visual = gdk_drawable_get_visual (drawable); + void (*composite_func) (guchar *src_buf, + gint src_rowstride, + guchar *dest_buf, + gint dest_rowstride, + GdkByteOrder dest_byte_order, + gint width, + gint height) = NULL; + + /* First we see if we have a visual-specific composition function that can composite + * the pixbuf data directly onto the image + */ + if (visual) + { + gint bits_per_pixel = _gdk_windowing_get_bits_for_depth (visual->depth); + + if (visual->byte_order == (G_BYTE_ORDER == G_BIG_ENDIAN ? GDK_MSB_FIRST : GDK_LSB_FIRST) && + visual->depth == 16 && + visual->red_mask == 0xf800 && + visual->green_mask == 0x07e0 && + visual->blue_mask == 0x001f) + composite_func = composite_565; + else if (visual->depth == 24 && bits_per_pixel == 32 && + visual->red_mask == 0xff0000 && + visual->green_mask == 0x00ff00 && + visual->blue_mask == 0x0000ff) + composite_func = composite_0888; + } + + /* We can't use our composite func if we are required to dither + */ + if (composite_func && !(dither == GDK_RGB_DITHER_MAX && visual->depth != 24)) + { + gint x0, y0; + 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); + + GdkImage *image = _gdk_image_get_scratch (width1, height1, gdk_drawable_get_depth (drawable), &xs0, &ys0); + + _gdk_drawable_copy_to_image (drawable, image, + dest_x + x0, dest_y + y0, + xs0, ys0, + width1, height1); + (*composite_func) (pixbuf->pixels + (src_y + y0) * pixbuf->rowstride + (src_x + x0) * 4, + pixbuf->rowstride, + image->mem + ys0 * image->bpl + xs0 * image->bpp, + image->bpl, + visual->byte_order, + width1, height1); + gdk_draw_image (drawable, gc, image, + xs0, ys0, + dest_x + x0, dest_y + y0, + width1, height1); + } + } + + goto out; + } + else + { + /* No special composition func, convert dest to 24 bit RGB data, composite against + * that, and convert back. + */ + composited = gdk_pixbuf_get_from_drawable (NULL, + drawable, + NULL, + dest_x, dest_y, + 0, 0, + width, height); + + if (composited) + composite (pixbuf->pixels + src_y * pixbuf->rowstride + src_x * 4, + pixbuf->rowstride, + composited->pixels, + composited->rowstride, + width, height); + } + } + + if (composited) + { + gdk_pixbuf_render_to_drawable (composited, + drawable, gc, + 0, 0, + dest_x, dest_y, + width, height, + dither, + x_dither, y_dither); + } + else + { + gdk_pixbuf_render_to_drawable (pixbuf, + drawable, gc, + src_x, src_y, + dest_x, dest_y, + width, height, + dither, + x_dither, y_dither); + } + + out: + if (composited) + g_object_unref (G_OBJECT (composited)); + + if (free_gc) + gdk_gc_unref (gc); +} -- cgit v1.2.1