mirror of
https://gitlab.gnome.org/GNOME/gtk.git
synced 2024-11-06 00:30:08 +00:00
d12c9702a4
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.
403 lines
11 KiB
C
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;
|
|
}
|