gtk2/gdk/gdkimage.c
Matthias Clasen 50a72eda2d Seal gdk
Add G_SEAL annotation for struct members, and add accessors for
the (useful) fields. Patch based on work by Garrett Regier,
see bug #592580.
2010-05-25 12:01:04 -04:00

553 lines
13 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 "config.h"
#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 */
#include "gdkalias.h"
/**
* 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 (image->colormap);
image->colormap = colormap;
g_object_ref (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;
}
/**
* gdk_image_get_image_type:
* @image: a #GdkImage
*
* Determines the type of a given image.
*
* Return value: the #GdkImageType of the image
*
* Since: 2.22
**/
GdkImageType
gdk_image_get_image_type (GdkImage *image)
{
g_return_val_if_fail (GDK_IS_IMAGE (image), 0);
return image->type;
}
/**
* gdk_image_get_visual:
* @image: a #GdkImage
*
* Determines the visual that was used to create the image.
*
* Return value: a #GdkVisual
*
* Since: 2.22
**/
GdkVisual *
gdk_image_get_visual (GdkImage *image)
{
g_return_val_if_fail (GDK_IS_IMAGE (image), NULL);
return image->visual;
}
/**
* gdk_image_get_byte_order:
* @image: a #GdkImage
*
* Determines the byte order of the image.
*
* Return value: a #GdkVisual
*
* Since: 2.22
**/
GdkByteOrder
gdk_image_get_byte_order (GdkImage *image)
{
g_return_val_if_fail (GDK_IS_IMAGE (image), 0);
return image->byte_order;
}
/**
* gdk_image_get_width:
* @image: a #GdkImage
*
* Determines the width of the image.
*
* Return value: the width
*
* Since: 2.22
**/
gint
gdk_image_get_width (GdkImage *image)
{
g_return_val_if_fail (GDK_IS_IMAGE (image), 0);
return image->width;
}
/**
* gdk_image_get_height:
* @image: a #GdkImage
*
* Determines the height of the image.
*
* Return value: the height
*
* Since: 2.22
**/
gint
gdk_image_get_height (GdkImage *image)
{
g_return_val_if_fail (GDK_IS_IMAGE (image), 0);
return image->height;
}
/**
* gdk_image_get_depth:
* @image: a #GdkImage
*
* Determines the depth of the image.
*
* Return value: the depth
*
* Since: 2.22
**/
guint16
gdk_image_get_depth (GdkImage *image)
{
g_return_val_if_fail (GDK_IS_IMAGE (image), 0);
return image->depth;
}
/**
* gdk_image_get_bytes_per_pixel:
* @image: a #GdkImage
*
* Determines the number of bytes per pixel of the image.
*
* Return value: the bytes per pixel
*
* Since: 2.22
**/
guint16
gdk_image_get_bytes_per_pixel (GdkImage *image)
{
g_return_val_if_fail (GDK_IS_IMAGE (image), 0);
return image->bpp;
}
/**
* gdk_image_get_bytes_per_line:
* @image: a #GdkImage
*
* Determines the number of bytes per line of the image.
*
* Return value: the bytes per line
*
* Since: 2.22
**/
guint16
gdk_image_get_bytes_per_line (GdkImage *image)
{
g_return_val_if_fail (GDK_IS_IMAGE (image), 0);
return image->bpl;
}
/**
* gdk_image_get_bits_per_pixel:
* @image: a #GdkImage
*
* Determines the number of bits per pixel of the image.
*
* Return value: the bits per pixel
*
* Since: 2.22
**/
guint16
gdk_image_get_bits_per_pixel (GdkImage *image)
{
g_return_val_if_fail (GDK_IS_IMAGE (image), 0);
return image->bits_per_pixel;
}
/* 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;
GdkScreen *screen;
};
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 (info->screen,
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++)
g_object_unref (info->static_image[j]);
return FALSE;
}
}
return TRUE;
}
static void
scratch_image_info_display_closed (GdkDisplay *display,
gboolean is_error,
GdkScratchImageInfo *image_info)
{
gint i;
g_signal_handlers_disconnect_by_func (display,
scratch_image_info_display_closed,
image_info);
scratch_image_infos = g_slist_remove (scratch_image_infos, image_info);
for (i = 0; i < image_info->n_images; i++)
g_object_unref (image_info->static_image[i]);
g_free (image_info);
}
static GdkScratchImageInfo *
scratch_image_info_for_depth (GdkScreen *screen,
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 && image_info->screen == screen)
return image_info;
tmp_list = tmp_list->next;
}
image_info = g_new (GdkScratchImageInfo, 1);
image_info->depth = depth;
image_info->screen = screen;
g_signal_connect (gdk_screen_get_display (screen), "closed",
G_CALLBACK (scratch_image_info_display_closed),
image_info);
/* 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:
* @screen: a #GdkScreen
* @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 that is
* suitable to use on @screen.
*
* 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 (GdkScreen *screen,
gint width,
gint height,
gint depth,
gint *x,
gint *y)
{
GdkScratchImageInfo *image_info;
GdkImage *image;
gint idx;
g_return_val_if_fail (GDK_IS_SCREEN (screen), NULL);
image_info = scratch_image_info_for_depth (screen, 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;
}
GdkImage*
gdk_image_new (GdkImageType type,
GdkVisual *visual,
gint width,
gint height)
{
return _gdk_image_new_for_depth (gdk_visual_get_screen (visual), type,
visual, width, height, -1);
}
#define __GDK_IMAGE_C__
#include "gdkaliasdef.c"