gtk2/gdk/gdkimage.c
Owen Taylor d12c9702a4 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.
2002-01-04 05:58:01 +00:00

403 lines
11 KiB
C

/* GDK - The GIMP Drawing Kit
* Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
/*
* Modified by the GTK+ Team and others 1997-2000. See the AUTHORS
* file for a list of people on the GTK+ Team. See the ChangeLog
* files for a list of changes. These files are distributed with
* GTK+ at ftp://ftp.gtk.org/pub/gtk/.
*/
#include <stdlib.h>
#include <sys/types.h>
#include "gdk.h" /* For gdk_flush() */
#include "gdkimage.h"
#include "gdkprivate.h"
#include "gdkinternals.h" /* For scratch_image code */
/**
* gdk_image_ref:
* @image: a #GdkImage
*
* Deprecated function; use g_object_ref() instead.
*
* Return value: the image
**/
GdkImage *
gdk_image_ref (GdkImage *image)
{
return (GdkImage *) g_object_ref (G_OBJECT (image));
}
/**
* gdk_image_unref:
* @image: a #GdkImage
*
* Deprecated function; use g_object_unref() instead.
*
**/
void
gdk_image_unref (GdkImage *image)
{
g_return_if_fail (GDK_IS_IMAGE (image));
g_object_unref (G_OBJECT (image));
}
/**
* gdk_image_get:
* @drawable: a #GdkDrawable
* @x: x coordinate in @window
* @y: y coordinate in @window
* @width: width of area in @window
* @height: height of area in @window
*
* This is a deprecated wrapper for gdk_drawable_get_image();
* gdk_drawable_get_image() should be used instead. Or even better: in
* most cases gdk_pixbuf_get_from_drawable() is the most convenient
* choice.
*
* Return value: a new #GdkImage or %NULL
**/
GdkImage*
gdk_image_get (GdkWindow *drawable,
gint x,
gint y,
gint width,
gint height)
{
g_return_val_if_fail (GDK_IS_DRAWABLE (drawable), NULL);
g_return_val_if_fail (x >= 0, NULL);
g_return_val_if_fail (y >= 0, NULL);
g_return_val_if_fail (width >= 0, NULL);
g_return_val_if_fail (height >= 0, NULL);
return gdk_drawable_get_image (drawable, x, y, width, height);
}
/**
* gdk_image_set_colormap:
* @image: a #GdkImage
* @colormap: a #GdkColormap
*
* Sets the colormap for the image to the given colormap. Normally
* there's no need to use this function, images are created with the
* correct colormap if you get the image from a drawable. If you
* create the image from scratch, use the colormap of the drawable you
* intend to render the image to.
**/
void
gdk_image_set_colormap (GdkImage *image,
GdkColormap *colormap)
{
g_return_if_fail (GDK_IS_IMAGE (image));
g_return_if_fail (GDK_IS_COLORMAP (colormap));
if (image->colormap != colormap)
{
if (image->colormap)
g_object_unref (G_OBJECT (image->colormap));
image->colormap = colormap;
g_object_ref (G_OBJECT (image->colormap));
}
}
/**
* gdk_image_get_colormap:
* @image: a #GdkImage
*
* Retrieves the colormap for a given image, if it exists. An image
* will have a colormap if the drawable from which it was created has
* a colormap, or if a colormap was set explicitely with
* gdk_image_set_colormap().
*
* Return value: colormap for the image
**/
GdkColormap *
gdk_image_get_colormap (GdkImage *image)
{
g_return_val_if_fail (GDK_IS_IMAGE (image), NULL);
return image->colormap;
}
/* We have N_REGION GDK_SCRATCH_IMAGE_WIDTH x GDK_SCRATCH_IMAGE_HEIGHT regions divided
* up between n_images different images. possible_n_images gives
* various divisors of N_REGIONS. The reason for allowing this
* flexibility is that we want to create as few images as possible,
* but we want to deal with the abberant systems that have a SHMMAX
* limit less than
*
* GDK_SCRATCH_IMAGE_WIDTH * GDK_SCRATCH_IMAGE_HEIGHT * N_REGIONS * 4 (384k)
*
* (Are there any such?)
*/
#define N_REGIONS 6
static const int possible_n_images[] = { 1, 2, 3, 6 };
/* We allocate one GdkScratchImageInfo structure for each
* depth where we are allocating scratch images. (Future: one
* per depth, per display)
*/
typedef struct _GdkScratchImageInfo GdkScratchImageInfo;
struct _GdkScratchImageInfo {
gint depth;
gint n_images;
GdkImage *static_image[N_REGIONS];
gint static_image_idx;
/* In order to optimize filling fractions, we simultaneously fill in up
* to three regions of size GDK_SCRATCH_IMAGE_WIDTH * GDK_SCRATCH_IMAGE_HEIGHT: one
* for images that are taller than GDK_SCRATCH_IMAGE_HEIGHT / 2, and must
* be tiled horizontally. One for images that are wider than
* GDK_SCRATCH_IMAGE_WIDTH / 2 and must be tiled vertically, and a third
* for images smaller than GDK_SCRATCH_IMAGE_HEIGHT / 2 x GDK_SCRATCH_IMAGE_WIDTH x 2
* that we tile in horizontal rows.
*/
gint horiz_idx;
gint horiz_y;
gint vert_idx;
gint vert_x;
/* tile_y1 and tile_y2 define the horizontal band into
* which we are tiling images. tile_x is the x extent to
* which that is filled
*/
gint tile_idx;
gint tile_x;
gint tile_y1;
gint tile_y2;
};
static GSList *scratch_image_infos = NULL;
static gboolean
allocate_scratch_images (GdkScratchImageInfo *info,
gint n_images,
gboolean shared)
{
gint i;
for (i = 0; i < n_images; i++)
{
info->static_image[i] = _gdk_image_new_for_depth (shared ? GDK_IMAGE_SHARED : GDK_IMAGE_NORMAL,
NULL,
GDK_SCRATCH_IMAGE_WIDTH * (N_REGIONS / n_images), GDK_SCRATCH_IMAGE_HEIGHT,
info->depth);
if (!info->static_image[i])
{
gint j;
for (j = 0; j < i; j++)
gdk_image_unref (info->static_image[i]);
return FALSE;
}
}
return TRUE;
}
GdkScratchImageInfo *
scratch_image_info_for_depth (gint depth)
{
GSList *tmp_list;
GdkScratchImageInfo *image_info;
gint i;
tmp_list = scratch_image_infos;
while (tmp_list)
{
image_info = tmp_list->data;
if (image_info->depth == depth)
return image_info;
tmp_list = tmp_list->next;
}
image_info = g_new (GdkScratchImageInfo, 1);
image_info->depth = depth;
/* Try to allocate as few possible shared images */
for (i=0; i < G_N_ELEMENTS (possible_n_images); i++)
{
if (allocate_scratch_images (image_info, possible_n_images[i], TRUE))
{
image_info->n_images = possible_n_images[i];
break;
}
}
/* If that fails, just allocate N_REGIONS normal images */
if (i == G_N_ELEMENTS (possible_n_images))
{
allocate_scratch_images (image_info, N_REGIONS, FALSE);
image_info->n_images = N_REGIONS;
}
image_info->static_image_idx = 0;
image_info->horiz_y = GDK_SCRATCH_IMAGE_HEIGHT;
image_info->vert_x = GDK_SCRATCH_IMAGE_WIDTH;
image_info->tile_x = GDK_SCRATCH_IMAGE_WIDTH;
image_info->tile_y1 = image_info->tile_y2 = GDK_SCRATCH_IMAGE_HEIGHT;
scratch_image_infos = g_slist_prepend (scratch_image_infos, image_info);
return image_info;
}
/* Defining NO_FLUSH can cause inconsistent screen updates, but is useful
for performance evaluation. */
#undef NO_FLUSH
#ifdef VERBOSE
static gint sincelast;
#endif
static gint
alloc_scratch_image (GdkScratchImageInfo *image_info)
{
if (image_info->static_image_idx == N_REGIONS)
{
#ifndef NO_FLUSH
gdk_flush ();
#endif
#ifdef VERBOSE
g_print ("flush, %d puts since last flush\n", sincelast);
sincelast = 0;
#endif
image_info->static_image_idx = 0;
/* Mark all regions that we might be filling in as completely
* full, to force new tiles to be allocated for subsequent
* images
*/
image_info->horiz_y = GDK_SCRATCH_IMAGE_HEIGHT;
image_info->vert_x = GDK_SCRATCH_IMAGE_WIDTH;
image_info->tile_x = GDK_SCRATCH_IMAGE_WIDTH;
image_info->tile_y1 = image_info->tile_y2 = GDK_SCRATCH_IMAGE_HEIGHT;
}
return image_info->static_image_idx++;
}
/**
* _gdk_image_get_scratch:
* @width: desired width
* @height: desired height
* @depth: depth of image
* @x: X location within returned image of scratch image
* @y: Y location within returned image of scratch image
*
* Allocates an image of size width/height, up to a maximum
* of GDK_SCRATCH_IMAGE_WIDTHxGDK_SCRATCH_IMAGE_HEIGHT
*
* Return value: a scratch image. This must be used by a
* call to gdk_image_put() before any other calls to
* _gdk_image_get_scratch()
**/
GdkImage *
_gdk_image_get_scratch (gint width,
gint height,
gint depth,
gint *x,
gint *y)
{
GdkScratchImageInfo *image_info;
GdkImage *image;
gint idx;
image_info = scratch_image_info_for_depth (depth);
if (width >= (GDK_SCRATCH_IMAGE_WIDTH >> 1))
{
if (height >= (GDK_SCRATCH_IMAGE_HEIGHT >> 1))
{
idx = alloc_scratch_image (image_info);
*x = 0;
*y = 0;
}
else
{
if (height + image_info->horiz_y > GDK_SCRATCH_IMAGE_HEIGHT)
{
image_info->horiz_idx = alloc_scratch_image (image_info);
image_info->horiz_y = 0;
}
idx = image_info->horiz_idx;
*x = 0;
*y = image_info->horiz_y;
image_info->horiz_y += height;
}
}
else
{
if (height >= (GDK_SCRATCH_IMAGE_HEIGHT >> 1))
{
if (width + image_info->vert_x > GDK_SCRATCH_IMAGE_WIDTH)
{
image_info->vert_idx = alloc_scratch_image (image_info);
image_info->vert_x = 0;
}
idx = image_info->vert_idx;
*x = image_info->vert_x;
*y = 0;
/* using 3 and -4 would be slightly more efficient on 32-bit machines
with > 1bpp displays */
image_info->vert_x += (width + 7) & -8;
}
else
{
if (width + image_info->tile_x > GDK_SCRATCH_IMAGE_WIDTH)
{
image_info->tile_y1 = image_info->tile_y2;
image_info->tile_x = 0;
}
if (height + image_info->tile_y1 > GDK_SCRATCH_IMAGE_HEIGHT)
{
image_info->tile_idx = alloc_scratch_image (image_info);
image_info->tile_x = 0;
image_info->tile_y1 = 0;
image_info->tile_y2 = 0;
}
if (height + image_info->tile_y1 > image_info->tile_y2)
image_info->tile_y2 = height + image_info->tile_y1;
idx = image_info->tile_idx;
*x = image_info->tile_x;
*y = image_info->tile_y1;
image_info->tile_x += (width + 7) & -8;
}
}
image = image_info->static_image[idx * image_info->n_images / N_REGIONS];
*x += GDK_SCRATCH_IMAGE_WIDTH * (idx % (N_REGIONS / image_info->n_images));
#ifdef VERBOSE
g_print ("index %d, x %d, y %d (%d x %d)\n", idx, *x, *y, width, height);
sincelast++;
#endif
return image;
}