mirror of
https://gitlab.gnome.org/GNOME/nautilus
synced 2024-11-05 16:04:31 +00:00
0be1195b18
of the high integrity Nautilus hackers would approve it. * libnautilus-extensions/Makefile.am: * libnautilus-extensions/bug-5712-pr3-workaround--gdk-pixbuf-drawable.c: * libnautilus-extensions/bug-5712-pr3-workaround--gdk-pixbuf-private.h: * libnautilus-extensions/bug-5712-pr3-workaround--gdkimage.c: * libnautilus-extensions/nautilus-gdk-pixbuf-extensions.c: (nautilus_gdk_pixbuf_get_from_window_safe): Workaround for bug 5712. This is a temporary evil until GTK+ 1.2.9 is released.
1449 lines
41 KiB
C
1449 lines
41 KiB
C
/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
|
|
|
|
/* nautilus-gdk-pixbuf-extensions.c: Routines to augment what's in gdk-pixbuf.
|
|
|
|
Copyright (C) 2000 Eazel, Inc.
|
|
|
|
The Gnome Library is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU Library General Public License as
|
|
published by the Free Software Foundation; either version 2 of the
|
|
License, or (at your option) any later version.
|
|
|
|
The Gnome 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
|
|
Library General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Library General Public
|
|
License along with the Gnome Library; see the file COPYING.LIB. If not,
|
|
write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
|
Boston, MA 02111-1307, USA.
|
|
|
|
Authors: Darin Adler <darin@eazel.com>
|
|
Ramiro Estrugo <ramiro@eazel.com>
|
|
*/
|
|
|
|
#include <config.h>
|
|
#include "nautilus-gdk-pixbuf-extensions.h"
|
|
|
|
#include "nautilus-gdk-extensions.h"
|
|
#include "nautilus-glib-extensions.h"
|
|
#include "nautilus-string.h"
|
|
#include <gdk-pixbuf/gdk-pixbuf-loader.h>
|
|
#include <gdk/gdkx.h>
|
|
#include <gdk/gdkprivate.h>
|
|
#include <libgnomevfs/gnome-vfs-async-ops.h>
|
|
#include <libgnomevfs/gnome-vfs-ops.h>
|
|
#include <libgnomevfs/gnome-vfs-utils.h>
|
|
#include <math.h>
|
|
#include <png.h>
|
|
|
|
#define LOAD_BUFFER_SIZE 4096
|
|
|
|
struct NautilusPixbufLoadHandle {
|
|
GnomeVFSAsyncHandle *vfs_handle;
|
|
NautilusPixbufLoadCallback callback;
|
|
gpointer callback_data;
|
|
GdkPixbufLoader *loader;
|
|
char buffer[LOAD_BUFFER_SIZE];
|
|
};
|
|
|
|
static void file_opened_callback (GnomeVFSAsyncHandle *vfs_handle,
|
|
GnomeVFSResult result,
|
|
gpointer callback_data);
|
|
static void file_read_callback (GnomeVFSAsyncHandle *vfs_handle,
|
|
GnomeVFSResult result,
|
|
gpointer buffer,
|
|
GnomeVFSFileSize bytes_requested,
|
|
GnomeVFSFileSize bytes_read,
|
|
gpointer callback_data);
|
|
static void file_closed_callback (GnomeVFSAsyncHandle *handle,
|
|
GnomeVFSResult result,
|
|
gpointer callback_data);
|
|
static void load_done (NautilusPixbufLoadHandle *handle,
|
|
GnomeVFSResult result,
|
|
gboolean get_pixbuf);
|
|
|
|
/**
|
|
* nautilus_gdk_pixbuf_list_ref
|
|
* @pixbuf_list: A list of GdkPixbuf objects.
|
|
*
|
|
* Refs all the pixbufs.
|
|
**/
|
|
void
|
|
nautilus_gdk_pixbuf_list_ref (GList *pixbuf_list)
|
|
{
|
|
g_list_foreach (pixbuf_list, (GFunc) gdk_pixbuf_ref, NULL);
|
|
}
|
|
|
|
/**
|
|
* nautilus_gdk_pixbuf_list_free
|
|
* @pixbuf_list: A list of GdkPixbuf objects.
|
|
*
|
|
* Unrefs all the pixbufs, then frees the list.
|
|
**/
|
|
void
|
|
nautilus_gdk_pixbuf_list_free (GList *pixbuf_list)
|
|
{
|
|
nautilus_g_list_free_deep_custom (pixbuf_list, (GFunc) gdk_pixbuf_unref, NULL);
|
|
}
|
|
|
|
GdkPixbuf *
|
|
nautilus_gdk_pixbuf_load (const char *uri)
|
|
{
|
|
GnomeVFSResult result;
|
|
GnomeVFSHandle *handle;
|
|
char buffer[LOAD_BUFFER_SIZE];
|
|
GnomeVFSFileSize bytes_read;
|
|
GdkPixbufLoader *loader;
|
|
GdkPixbuf *pixbuf;
|
|
|
|
g_return_val_if_fail (uri != NULL, NULL);
|
|
|
|
result = gnome_vfs_open (&handle,
|
|
uri,
|
|
GNOME_VFS_OPEN_READ);
|
|
if (result != GNOME_VFS_OK) {
|
|
return NULL;
|
|
}
|
|
|
|
loader = gdk_pixbuf_loader_new ();
|
|
while (1) {
|
|
result = gnome_vfs_read (handle,
|
|
buffer,
|
|
sizeof (buffer),
|
|
&bytes_read);
|
|
if (result != GNOME_VFS_OK) {
|
|
break;
|
|
}
|
|
if (bytes_read == 0) {
|
|
break;
|
|
}
|
|
if (!gdk_pixbuf_loader_write (loader,
|
|
buffer,
|
|
bytes_read)) {
|
|
result = GNOME_VFS_ERROR_WRONG_FORMAT;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (result != GNOME_VFS_OK && result != GNOME_VFS_ERROR_EOF) {
|
|
gtk_object_unref (GTK_OBJECT (loader));
|
|
gnome_vfs_close (handle);
|
|
return NULL;
|
|
}
|
|
|
|
gnome_vfs_close (handle);
|
|
gdk_pixbuf_loader_close (loader);
|
|
|
|
pixbuf = gdk_pixbuf_loader_get_pixbuf (loader);
|
|
if (pixbuf != NULL) {
|
|
gdk_pixbuf_ref (pixbuf);
|
|
}
|
|
gtk_object_unref (GTK_OBJECT (loader));
|
|
|
|
return pixbuf;
|
|
}
|
|
|
|
NautilusPixbufLoadHandle *
|
|
nautilus_gdk_pixbuf_load_async (const char *uri,
|
|
NautilusPixbufLoadCallback callback,
|
|
gpointer callback_data)
|
|
{
|
|
NautilusPixbufLoadHandle *handle;
|
|
|
|
handle = g_new0 (NautilusPixbufLoadHandle, 1);
|
|
handle->callback = callback;
|
|
handle->callback_data = callback_data;
|
|
|
|
gnome_vfs_async_open (&handle->vfs_handle,
|
|
uri,
|
|
GNOME_VFS_OPEN_READ,
|
|
file_opened_callback,
|
|
handle);
|
|
|
|
return handle;
|
|
}
|
|
|
|
static void
|
|
file_opened_callback (GnomeVFSAsyncHandle *vfs_handle,
|
|
GnomeVFSResult result,
|
|
gpointer callback_data)
|
|
{
|
|
NautilusPixbufLoadHandle *handle;
|
|
|
|
handle = callback_data;
|
|
g_assert (handle->vfs_handle == vfs_handle);
|
|
|
|
if (result != GNOME_VFS_OK) {
|
|
handle->vfs_handle = NULL;
|
|
load_done (handle, result, FALSE);
|
|
return;
|
|
}
|
|
|
|
handle->loader = gdk_pixbuf_loader_new ();
|
|
|
|
gnome_vfs_async_read (handle->vfs_handle,
|
|
handle->buffer,
|
|
sizeof (handle->buffer),
|
|
file_read_callback,
|
|
handle);
|
|
}
|
|
|
|
static void
|
|
file_read_callback (GnomeVFSAsyncHandle *vfs_handle,
|
|
GnomeVFSResult result,
|
|
gpointer buffer,
|
|
GnomeVFSFileSize bytes_requested,
|
|
GnomeVFSFileSize bytes_read,
|
|
gpointer callback_data)
|
|
{
|
|
NautilusPixbufLoadHandle *handle;
|
|
|
|
handle = callback_data;
|
|
g_assert (handle->vfs_handle == vfs_handle);
|
|
g_assert (handle->buffer == buffer);
|
|
|
|
if (result == GNOME_VFS_OK && bytes_read != 0) {
|
|
if (!gdk_pixbuf_loader_write (handle->loader,
|
|
buffer,
|
|
bytes_read)) {
|
|
result = GNOME_VFS_ERROR_WRONG_FORMAT;
|
|
}
|
|
gnome_vfs_async_read (handle->vfs_handle,
|
|
handle->buffer,
|
|
sizeof (handle->buffer),
|
|
file_read_callback,
|
|
handle);
|
|
return;
|
|
}
|
|
|
|
load_done (handle, result, result == GNOME_VFS_OK || result == GNOME_VFS_ERROR_EOF);
|
|
}
|
|
|
|
static void
|
|
file_closed_callback (GnomeVFSAsyncHandle *handle,
|
|
GnomeVFSResult result,
|
|
gpointer callback_data)
|
|
{
|
|
g_assert (callback_data == NULL);
|
|
}
|
|
|
|
static void
|
|
free_pixbuf_load_handle (NautilusPixbufLoadHandle *handle)
|
|
{
|
|
if (handle->loader != NULL) {
|
|
gtk_object_unref (GTK_OBJECT (handle->loader));
|
|
}
|
|
g_free (handle);
|
|
}
|
|
|
|
static void
|
|
load_done (NautilusPixbufLoadHandle *handle, GnomeVFSResult result, gboolean get_pixbuf)
|
|
{
|
|
GdkPixbuf *pixbuf;
|
|
|
|
if (handle->loader != NULL) {
|
|
gdk_pixbuf_loader_close (handle->loader);
|
|
}
|
|
|
|
pixbuf = get_pixbuf ? gdk_pixbuf_loader_get_pixbuf (handle->loader) : NULL;
|
|
|
|
if (handle->vfs_handle != NULL) {
|
|
gnome_vfs_async_close (handle->vfs_handle, file_closed_callback, NULL);
|
|
}
|
|
|
|
handle->callback (result, pixbuf, handle->callback_data);
|
|
|
|
free_pixbuf_load_handle (handle);
|
|
}
|
|
|
|
void
|
|
nautilus_cancel_gdk_pixbuf_load (NautilusPixbufLoadHandle *handle)
|
|
{
|
|
if (handle == NULL) {
|
|
return;
|
|
}
|
|
if (handle->vfs_handle != NULL) {
|
|
gnome_vfs_async_cancel (handle->vfs_handle);
|
|
}
|
|
free_pixbuf_load_handle (handle);
|
|
}
|
|
|
|
/* return the average value of each component */
|
|
void
|
|
nautilus_gdk_pixbuf_average_value (GdkPixbuf *pixbuf, GdkColor *color)
|
|
{
|
|
uint red_total, green_total, blue_total, count;
|
|
int row, column;
|
|
int width, height;
|
|
int row_stride;
|
|
guchar *pixsrc, *original_pixels;
|
|
|
|
gboolean has_alpha;
|
|
|
|
red_total = 0;
|
|
green_total = 0;
|
|
blue_total = 0;
|
|
count = 0;
|
|
|
|
/* iterate through the pixbuf, counting up each component */
|
|
has_alpha = gdk_pixbuf_get_has_alpha (pixbuf);
|
|
width = gdk_pixbuf_get_width (pixbuf);
|
|
height = gdk_pixbuf_get_height (pixbuf);
|
|
row_stride = gdk_pixbuf_get_rowstride (pixbuf);
|
|
original_pixels = gdk_pixbuf_get_pixels (pixbuf);
|
|
|
|
for (row = 0; row < height; row++) {
|
|
pixsrc = original_pixels + (row * row_stride);
|
|
for (column = 0; column < width; column++) {
|
|
red_total += *pixsrc++;
|
|
green_total += *pixsrc++;
|
|
blue_total += *pixsrc++;
|
|
count += 1;
|
|
if (has_alpha) {
|
|
pixsrc++;
|
|
}
|
|
}
|
|
}
|
|
|
|
color->red = (red_total * 256) / count;
|
|
color->green = (green_total * 256) / count;
|
|
color->blue = (blue_total * 256) / count;
|
|
}
|
|
|
|
double
|
|
nautilus_gdk_scale_to_fit_factor (int width, int height,
|
|
int max_width, int max_height,
|
|
int *scaled_width, int *scaled_height)
|
|
{
|
|
double scale_factor;
|
|
|
|
scale_factor = MIN (max_width / (double) width, max_height / (double) height);
|
|
|
|
*scaled_width = floor (width * scale_factor + .5);
|
|
*scaled_height = floor (height * scale_factor + .5);
|
|
|
|
return scale_factor;
|
|
}
|
|
|
|
/* Returns a scaled copy of pixbuf, preserving aspect ratio. The copy will
|
|
* be scaled as large as possible without exceeding the specified width and height.
|
|
*/
|
|
GdkPixbuf *
|
|
nautilus_gdk_pixbuf_scale_to_fit (GdkPixbuf *pixbuf, int max_width, int max_height)
|
|
{
|
|
int scaled_width;
|
|
int scaled_height;
|
|
|
|
nautilus_gdk_scale_to_fit_factor (gdk_pixbuf_get_width(pixbuf), gdk_pixbuf_get_height(pixbuf),
|
|
max_width, max_height,
|
|
&scaled_width, &scaled_height);
|
|
|
|
return gdk_pixbuf_scale_simple (pixbuf, scaled_width, scaled_height, GDK_INTERP_BILINEAR);
|
|
}
|
|
|
|
/* Returns a copy of pixbuf scaled down, preserving aspect ratio, to fit
|
|
* within the specified width and height. If it already fits, a copy of
|
|
* the original, without scaling, is returned.
|
|
*/
|
|
GdkPixbuf *
|
|
nautilus_gdk_pixbuf_scale_down_to_fit (GdkPixbuf *pixbuf, int max_width, int max_height)
|
|
{
|
|
int scaled_width;
|
|
int scaled_height;
|
|
|
|
double scale_factor;
|
|
|
|
scale_factor = nautilus_gdk_scale_to_fit_factor (gdk_pixbuf_get_width(pixbuf), gdk_pixbuf_get_height(pixbuf),
|
|
max_width, max_height,
|
|
&scaled_width, &scaled_height);
|
|
|
|
if (scale_factor >= 1.0) {
|
|
return gdk_pixbuf_copy (pixbuf);
|
|
} else {
|
|
return gdk_pixbuf_scale_simple (pixbuf, scaled_width, scaled_height, GDK_INTERP_BILINEAR);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* nautilus_gdk_pixbuf_is_valid:
|
|
* @pixbuf: A GdkPixbuf
|
|
*
|
|
* Return value: A boolean indicating whether the given pixbuf is valid.
|
|
*
|
|
* A pixbuf is valid if:
|
|
*
|
|
* 1. It is non NULL
|
|
* 2. It is has non NULL pixel data.
|
|
* 3. It has width and height greater than 0.
|
|
*/
|
|
gboolean
|
|
nautilus_gdk_pixbuf_is_valid (const GdkPixbuf *pixbuf)
|
|
{
|
|
return ((pixbuf != NULL)
|
|
&& (gdk_pixbuf_get_pixels (pixbuf) != NULL)
|
|
&& (gdk_pixbuf_get_width (pixbuf) > 0)
|
|
&& (gdk_pixbuf_get_height (pixbuf) > 0));
|
|
}
|
|
|
|
/**
|
|
* nautilus_gdk_pixbuf_get_frame:
|
|
* @pixbuf: A GdkPixbuf
|
|
*
|
|
* Return value: A ArtIRect representing the dimensions of the
|
|
* pixbuf.
|
|
*
|
|
* This function is useful in code that uses libart rect
|
|
* intersection routines.
|
|
*/
|
|
ArtIRect
|
|
nautilus_gdk_pixbuf_get_frame (const GdkPixbuf *pixbuf)
|
|
{
|
|
ArtIRect frame;
|
|
|
|
g_return_val_if_fail (nautilus_gdk_pixbuf_is_valid (pixbuf), NAUTILUS_ART_IRECT_EMPTY);
|
|
|
|
frame.x0 = 0;
|
|
frame.y0 = 0;
|
|
frame.x1 = gdk_pixbuf_get_width (pixbuf);
|
|
frame.y1 = gdk_pixbuf_get_height (pixbuf);
|
|
|
|
return frame;
|
|
}
|
|
|
|
/**
|
|
* nautilus_gdk_pixbuf_fill_rectangle:
|
|
* @pixbuf: Target pixbuf to fill into.
|
|
* @area: Rectangle to fill.
|
|
* @color: The color to use.
|
|
*
|
|
* Fill the rectangle with the the given color.
|
|
* Use the given rectangle if not NULL.
|
|
* If rectangle is NULL, fill the whole pixbuf.
|
|
*/
|
|
void
|
|
nautilus_gdk_pixbuf_fill_rectangle_with_color (GdkPixbuf *pixbuf,
|
|
const ArtIRect *area,
|
|
guint32 color)
|
|
{
|
|
ArtIRect frame;
|
|
ArtIRect target;
|
|
guchar red;
|
|
guchar green;
|
|
guchar blue;
|
|
guchar alpha;
|
|
guchar *pixels;
|
|
gboolean has_alpha;
|
|
guint pixel_offset;
|
|
guint rowstride;
|
|
guchar *row_offset;
|
|
int x;
|
|
int y;
|
|
|
|
g_return_if_fail (nautilus_gdk_pixbuf_is_valid (pixbuf));
|
|
|
|
frame = nautilus_gdk_pixbuf_get_frame (pixbuf);
|
|
|
|
pixels = gdk_pixbuf_get_pixels (pixbuf);
|
|
rowstride = gdk_pixbuf_get_rowstride (pixbuf);
|
|
has_alpha = gdk_pixbuf_get_has_alpha (pixbuf);
|
|
pixel_offset = has_alpha ? 4 : 3;
|
|
|
|
/* If an area is given, make sure its clipped to the pixbuf frame */
|
|
if (area != NULL) {
|
|
|
|
art_irect_intersect (&target, area, &frame);
|
|
|
|
if (art_irect_empty (&target)) {
|
|
return;
|
|
}
|
|
/* If no area is given, then use the whole pixbuf frame */
|
|
} else {
|
|
target = frame;
|
|
}
|
|
|
|
red = NAUTILUS_RGBA_COLOR_GET_R (color);
|
|
green = NAUTILUS_RGBA_COLOR_GET_G (color);
|
|
blue = NAUTILUS_RGBA_COLOR_GET_B (color);
|
|
alpha = NAUTILUS_RGBA_COLOR_GET_A (color);
|
|
|
|
row_offset = pixels + target.y0 * rowstride;
|
|
|
|
for (y = target.y0; y < target.y1; y++) {
|
|
guchar *offset = row_offset + (target.x0 * pixel_offset);
|
|
|
|
for (x = target.x0; x < target.x1; x++) {
|
|
*(offset++) = red;
|
|
*(offset++) = green;
|
|
*(offset++) = blue;
|
|
|
|
if (has_alpha) {
|
|
*(offset++) = alpha;
|
|
}
|
|
|
|
}
|
|
|
|
row_offset += rowstride;
|
|
}
|
|
}
|
|
|
|
/* utility routine for saving a pixbuf to a png file.
|
|
* This was adapted from Iain Holmes' code in gnome-iconedit, and probably
|
|
* should be in a utility library, possibly in gdk-pixbuf itself.
|
|
*/
|
|
gboolean
|
|
nautilus_gdk_pixbuf_save_to_file (const GdkPixbuf *pixbuf,
|
|
const char *file_name)
|
|
{
|
|
FILE *handle;
|
|
char *buffer;
|
|
gboolean has_alpha;
|
|
int width, height, depth, rowstride;
|
|
guchar *pixels;
|
|
png_structp png_ptr;
|
|
png_infop info_ptr;
|
|
png_text text[2];
|
|
int i;
|
|
|
|
g_return_val_if_fail (pixbuf != NULL, FALSE);
|
|
g_return_val_if_fail (file_name != NULL, FALSE);
|
|
g_return_val_if_fail (file_name[0] != '\0', FALSE);
|
|
|
|
handle = fopen (file_name, "wb");
|
|
if (handle == NULL) {
|
|
return FALSE;
|
|
}
|
|
|
|
png_ptr = png_create_write_struct (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
|
|
if (png_ptr == NULL) {
|
|
fclose (handle);
|
|
return FALSE;
|
|
}
|
|
|
|
info_ptr = png_create_info_struct (png_ptr);
|
|
if (info_ptr == NULL) {
|
|
png_destroy_write_struct (&png_ptr, (png_infopp)NULL);
|
|
fclose (handle);
|
|
return FALSE;
|
|
}
|
|
|
|
if (setjmp (png_ptr->jmpbuf)) {
|
|
png_destroy_write_struct (&png_ptr, &info_ptr);
|
|
fclose (handle);
|
|
return FALSE;
|
|
}
|
|
|
|
png_init_io (png_ptr, handle);
|
|
|
|
has_alpha = gdk_pixbuf_get_has_alpha (pixbuf);
|
|
width = gdk_pixbuf_get_width (pixbuf);
|
|
height = gdk_pixbuf_get_height (pixbuf);
|
|
depth = gdk_pixbuf_get_bits_per_sample (pixbuf);
|
|
pixels = gdk_pixbuf_get_pixels (pixbuf);
|
|
rowstride = gdk_pixbuf_get_rowstride (pixbuf);
|
|
|
|
png_set_IHDR (png_ptr, info_ptr, width, height,
|
|
depth, PNG_COLOR_TYPE_RGB_ALPHA,
|
|
PNG_INTERLACE_NONE,
|
|
PNG_COMPRESSION_TYPE_DEFAULT,
|
|
PNG_FILTER_TYPE_DEFAULT);
|
|
|
|
/* Some text to go with the png image */
|
|
text[0].key = "Title";
|
|
text[0].text = (char *) file_name;
|
|
text[0].compression = PNG_TEXT_COMPRESSION_NONE;
|
|
text[1].key = "Software";
|
|
text[1].text = "Nautilus Thumbnail";
|
|
text[1].compression = PNG_TEXT_COMPRESSION_NONE;
|
|
png_set_text (png_ptr, info_ptr, text, 2);
|
|
|
|
/* Write header data */
|
|
png_write_info (png_ptr, info_ptr);
|
|
|
|
/* if there is no alpha in the data, allocate buffer to expand into */
|
|
if (has_alpha) {
|
|
buffer = NULL;
|
|
} else {
|
|
buffer = g_malloc(4 * width);
|
|
}
|
|
|
|
/* pump the raster data into libpng, one scan line at a time */
|
|
for (i = 0; i < height; i++) {
|
|
if (has_alpha) {
|
|
png_bytep row_pointer = pixels;
|
|
png_write_row (png_ptr, row_pointer);
|
|
} else {
|
|
/* expand RGB to RGBA using an opaque alpha value */
|
|
int x;
|
|
char *buffer_ptr = buffer;
|
|
char *source_ptr = pixels;
|
|
for (x = 0; x < width; x++) {
|
|
*buffer_ptr++ = *source_ptr++;
|
|
*buffer_ptr++ = *source_ptr++;
|
|
*buffer_ptr++ = *source_ptr++;
|
|
*buffer_ptr++ = 255;
|
|
}
|
|
png_write_row (png_ptr, (png_bytep) buffer);
|
|
}
|
|
pixels += rowstride;
|
|
}
|
|
|
|
png_write_end (png_ptr, info_ptr);
|
|
png_destroy_write_struct (&png_ptr, &info_ptr);
|
|
|
|
g_free (buffer);
|
|
|
|
fclose (handle);
|
|
return TRUE;
|
|
}
|
|
|
|
void
|
|
nautilus_gdk_pixbuf_ref_if_not_null (GdkPixbuf *pixbuf_or_null)
|
|
{
|
|
if (pixbuf_or_null != NULL) {
|
|
gdk_pixbuf_ref (pixbuf_or_null);
|
|
}
|
|
}
|
|
|
|
void
|
|
nautilus_gdk_pixbuf_unref_if_not_null (GdkPixbuf *pixbuf_or_null)
|
|
{
|
|
if (pixbuf_or_null != NULL) {
|
|
gdk_pixbuf_unref (pixbuf_or_null);
|
|
}
|
|
}
|
|
|
|
static ArtIRect
|
|
nautilus_gdk_window_get_frame (const GdkWindow *window)
|
|
{
|
|
ArtIRect frame;
|
|
|
|
g_return_val_if_fail (window != NULL, NAUTILUS_ART_IRECT_EMPTY);
|
|
|
|
frame.x0 = 0;
|
|
frame.y0 = 0;
|
|
gdk_window_get_size ((GdkWindow *) window, &frame.x1, &frame.y1);
|
|
|
|
return frame;
|
|
}
|
|
|
|
void
|
|
nautilus_gdk_pixbuf_draw_to_drawable (const GdkPixbuf *pixbuf,
|
|
GdkDrawable *drawable,
|
|
GdkGC *gc,
|
|
int source_x,
|
|
int source_y,
|
|
const ArtIRect *destination_area,
|
|
GdkRgbDither dither,
|
|
GdkPixbufAlphaMode alpha_compositing_mode,
|
|
int alpha_threshold)
|
|
{
|
|
ArtIRect frame;
|
|
ArtIRect target;
|
|
ArtIRect source;
|
|
ArtIRect destination_frame;
|
|
int target_width;
|
|
int target_height;
|
|
int source_width;
|
|
int source_height;
|
|
|
|
g_return_if_fail (nautilus_gdk_pixbuf_is_valid (pixbuf));
|
|
g_return_if_fail (drawable != NULL);
|
|
g_return_if_fail (gc != NULL);
|
|
g_return_if_fail (destination_area != NULL);
|
|
g_return_if_fail (destination_area->x1 > destination_area->x0);
|
|
g_return_if_fail (destination_area->y1 > destination_area->y0);
|
|
g_return_if_fail (alpha_threshold > NAUTILUS_OPACITY_FULLY_TRANSPARENT);
|
|
g_return_if_fail (alpha_threshold <= NAUTILUS_OPACITY_FULLY_OPAQUE);
|
|
g_return_if_fail (alpha_compositing_mode >= GDK_PIXBUF_ALPHA_BILEVEL);
|
|
g_return_if_fail (alpha_compositing_mode <= GDK_PIXBUF_ALPHA_FULL);
|
|
|
|
frame = nautilus_gdk_pixbuf_get_frame (pixbuf);
|
|
destination_frame = nautilus_gdk_window_get_frame (drawable);
|
|
|
|
g_return_if_fail (source_x >= 0);
|
|
g_return_if_fail (source_y >= 0);
|
|
g_return_if_fail (source_x < (frame.x1 - frame.x0));
|
|
g_return_if_fail (source_y < (frame.y1 - frame.y0));
|
|
|
|
/* Clip the destination area to the destination pixbuf frame */
|
|
art_irect_intersect (&target, destination_area, &destination_frame);
|
|
if (art_irect_empty (&target)) {
|
|
return;
|
|
}
|
|
|
|
/* Assign the source area */
|
|
nautilus_art_irect_assign (&source,
|
|
source_x,
|
|
source_y,
|
|
frame.x1 - frame.x0 - source_x,
|
|
frame.y1 - frame.y0 - source_y);
|
|
|
|
/* Adjust the target width if the source area is smaller than the
|
|
* source pixbuf frame */
|
|
target_width = target.x1 - target.x0;
|
|
target_height = target.y1 - target.y0;
|
|
source_width = source.x1 - source.x0;
|
|
source_height = source.y1 - source.y0;
|
|
|
|
target.x1 = target.x0 + MIN (target_width, source_width);
|
|
target.y1 = target.y0 + MIN (target_height, source_height);
|
|
|
|
if (gdk_pixbuf_get_has_alpha (pixbuf)) {
|
|
gdk_pixbuf_render_to_drawable_alpha ((GdkPixbuf *) pixbuf,
|
|
drawable,
|
|
source.x0,
|
|
source.y0,
|
|
target.x0,
|
|
target.y0,
|
|
target.x1 - target.x0,
|
|
target.y1 - target.y0,
|
|
alpha_compositing_mode,
|
|
alpha_threshold,
|
|
dither,
|
|
0,
|
|
0);
|
|
} else {
|
|
gdk_pixbuf_render_to_drawable ((GdkPixbuf *) pixbuf,
|
|
drawable,
|
|
gc,
|
|
source.x0,
|
|
source.y0,
|
|
target.x0,
|
|
target.y0,
|
|
target.x1 - target.x0,
|
|
target.y1 - target.y0,
|
|
dither,
|
|
0,
|
|
0);
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* nautilus_gdk_pixbuf_draw_to_pixbuf:
|
|
* @pixbuf: The source pixbuf to draw.
|
|
* @destination_pixbuf: The destination pixbuf.
|
|
* @source_x: The source pixbuf x coordiate to composite from.
|
|
* @source_y: The source pixbuf y coordiate to composite from.
|
|
* @destination_area: The destination area within the destination pixbuf.
|
|
* This area will be clipped if invalid in any way.
|
|
*
|
|
* Copy one pixbuf onto another another.. This function has some advantages
|
|
* over plain gdk_pixbuf_copy_area():
|
|
*
|
|
* Composition paramters (source coordinate, destination area) are
|
|
* given in a way that is consistent with the rest of the extensions
|
|
* in this file. That is, it matches the declaration of
|
|
* nautilus_gdk_pixbuf_draw_to_pixbuf_alpha() and
|
|
* nautilus_gdk_pixbuf_draw_to_drawable() very closely.
|
|
*
|
|
* All values are clipped to make sure they are valid.
|
|
*
|
|
*/
|
|
void
|
|
nautilus_gdk_pixbuf_draw_to_pixbuf (const GdkPixbuf *pixbuf,
|
|
GdkPixbuf *destination_pixbuf,
|
|
int source_x,
|
|
int source_y,
|
|
const ArtIRect *destination_area)
|
|
{
|
|
ArtIRect frame;
|
|
ArtIRect target;
|
|
ArtIRect source;
|
|
ArtIRect destination_frame;
|
|
int target_width;
|
|
int target_height;
|
|
int source_width;
|
|
int source_height;
|
|
|
|
g_return_if_fail (nautilus_gdk_pixbuf_is_valid (pixbuf));
|
|
g_return_if_fail (nautilus_gdk_pixbuf_is_valid (destination_pixbuf));
|
|
g_return_if_fail (destination_area != NULL);
|
|
g_return_if_fail (destination_area->x1 > destination_area->x0);
|
|
g_return_if_fail (destination_area->y1 > destination_area->y0);
|
|
|
|
frame = nautilus_gdk_pixbuf_get_frame (pixbuf);
|
|
destination_frame = nautilus_gdk_pixbuf_get_frame (destination_pixbuf);
|
|
|
|
g_return_if_fail (source_x >= 0);
|
|
g_return_if_fail (source_y >= 0);
|
|
g_return_if_fail (source_x < (frame.x1 - frame.x0));
|
|
g_return_if_fail (source_y < (frame.y1 - frame.y0));
|
|
|
|
/* Clip the destination area to the destination pixbuf frame */
|
|
art_irect_intersect (&target, destination_area, &destination_frame);
|
|
if (art_irect_empty (&target)) {
|
|
return;
|
|
}
|
|
|
|
/* Assign the source area */
|
|
nautilus_art_irect_assign (&source,
|
|
source_x,
|
|
source_y,
|
|
frame.x1 - frame.x0 - source_x,
|
|
frame.y1 - frame.y0 - source_y);
|
|
|
|
/* Adjust the target width if the source area is smaller than the
|
|
* source pixbuf frame */
|
|
target_width = target.x1 - target.x0;
|
|
target_height = target.y1 - target.y0;
|
|
source_width = source.x1 - source.x0;
|
|
source_height = source.y1 - source.y0;
|
|
|
|
target.x1 = target.x0 + MIN (target_width, source_width);
|
|
target.y1 = target.y0 + MIN (target_height, source_height);
|
|
|
|
gdk_pixbuf_copy_area (pixbuf,
|
|
source.x0,
|
|
source.y0,
|
|
target.x1 - target.x0,
|
|
target.y1 - target.y0,
|
|
destination_pixbuf,
|
|
target.x0,
|
|
target.y0);
|
|
}
|
|
|
|
/**
|
|
* nautilus_gdk_pixbuf_draw_to_pixbuf_alpha:
|
|
* @pixbuf: The source pixbuf to draw.
|
|
* @destination_pixbuf: The destination pixbuf.
|
|
* @source_x: The source pixbuf x coordiate to composite from.
|
|
* @source_y: The source pixbuf y coordiate to composite from.
|
|
* @destination_area: The destination area within the destination pixbuf.
|
|
* This area will be clipped if invalid in any way.
|
|
* @opacity: The opacity of the drawn tiles where 0 <= opacity <= 255.
|
|
* @interpolation_mode: The interpolation mode. See <gdk-pixbuf.h>
|
|
*
|
|
* Composite one pixbuf over another. This function has some advantages
|
|
* over plain gdk_pixbuf_composite():
|
|
*
|
|
* Composition paramters (source coordinate, destination area) are
|
|
* given in a way that is consistent with the rest of the extensions
|
|
* in this file. That is, it matches the declaration of
|
|
* nautilus_gdk_pixbuf_draw_to_pixbuf() and
|
|
* nautilus_gdk_pixbuf_draw_to_drawable() very closely.
|
|
*
|
|
* All values are clipped to make sure they are valid.
|
|
*
|
|
* Workaround a limitation in gdk_pixbuf_composite() that does not allow
|
|
* the source (x,y) to be greater than (0,0)
|
|
*
|
|
*/
|
|
void
|
|
nautilus_gdk_pixbuf_draw_to_pixbuf_alpha (const GdkPixbuf *pixbuf,
|
|
GdkPixbuf *destination_pixbuf,
|
|
int source_x,
|
|
int source_y,
|
|
const ArtIRect *destination_area,
|
|
int opacity,
|
|
GdkInterpType interpolation_mode)
|
|
{
|
|
ArtIRect frame;
|
|
ArtIRect target;
|
|
ArtIRect source;
|
|
ArtIRect destination_frame;
|
|
int target_width;
|
|
int target_height;
|
|
int source_width;
|
|
int source_height;
|
|
|
|
g_return_if_fail (nautilus_gdk_pixbuf_is_valid (pixbuf));
|
|
g_return_if_fail (nautilus_gdk_pixbuf_is_valid (destination_pixbuf));
|
|
g_return_if_fail (destination_area != NULL);
|
|
g_return_if_fail (destination_area->x1 > destination_area->x0);
|
|
g_return_if_fail (destination_area->y1 > destination_area->y0);
|
|
g_return_if_fail (opacity >= NAUTILUS_OPACITY_FULLY_TRANSPARENT);
|
|
g_return_if_fail (opacity <= NAUTILUS_OPACITY_FULLY_OPAQUE);
|
|
g_return_if_fail (interpolation_mode >= GDK_INTERP_NEAREST);
|
|
g_return_if_fail (interpolation_mode <= GDK_INTERP_HYPER);
|
|
|
|
frame = nautilus_gdk_pixbuf_get_frame (pixbuf);
|
|
destination_frame = nautilus_gdk_pixbuf_get_frame (destination_pixbuf);
|
|
|
|
g_return_if_fail (source_x >= 0);
|
|
g_return_if_fail (source_y >= 0);
|
|
g_return_if_fail (source_x < (frame.x1 - frame.x0));
|
|
g_return_if_fail (source_y < (frame.y1 - frame.y0));
|
|
|
|
/* Clip the destination area to the destination pixbuf frame */
|
|
art_irect_intersect (&target, destination_area, &destination_frame);
|
|
if (art_irect_empty (&target)) {
|
|
return;
|
|
}
|
|
|
|
/* Assign the source area */
|
|
nautilus_art_irect_assign (&source,
|
|
source_x,
|
|
source_y,
|
|
frame.x1 - frame.x0 - source_x,
|
|
frame.y1 - frame.y0 - source_y);
|
|
|
|
/* Adjust the target width if the source area is smaller than the
|
|
* source pixbuf frame */
|
|
target_width = target.x1 - target.x0;
|
|
target_height = target.y1 - target.y0;
|
|
source_width = source.x1 - source.x0;
|
|
source_height = source.y1 - source.y0;
|
|
|
|
target.x1 = target.x0 + MIN (target_width, source_width);
|
|
target.y1 = target.y0 + MIN (target_height, source_height);
|
|
|
|
/* If the source point is not (0,0), then we need to create a sub pixbuf
|
|
* with only the source area. This is needed to work around a limitation
|
|
* in gdk_pixbuf_composite() that requires the source area to be (0,0). */
|
|
if (source.x0 != 0 || source.y0 != 0) {
|
|
ArtIRect area;
|
|
int width;
|
|
int height;
|
|
|
|
width = frame.x1 - frame.x0 - source.x0;
|
|
height = frame.y1 - frame.y0 - source.y0;
|
|
|
|
area.x0 = source.x0;
|
|
area.y0 = source.y0;
|
|
area.x1 = area.x0 + width;
|
|
area.y1 = area.y0 + height;
|
|
|
|
pixbuf = nautilus_gdk_pixbuf_new_from_pixbuf_sub_area ((GdkPixbuf *) pixbuf, &area);
|
|
} else {
|
|
gdk_pixbuf_ref ((GdkPixbuf *) pixbuf);
|
|
}
|
|
|
|
gdk_pixbuf_composite (pixbuf,
|
|
destination_pixbuf,
|
|
target.x0,
|
|
target.y0,
|
|
target.x1 - target.x0,
|
|
target.y1 - target.y0,
|
|
target.x0,
|
|
target.y0,
|
|
1.0,
|
|
1.0,
|
|
interpolation_mode,
|
|
opacity);
|
|
|
|
gdk_pixbuf_unref ((GdkPixbuf *) pixbuf);
|
|
}
|
|
|
|
static void
|
|
pixbuf_destroy_callback (guchar *pixels,
|
|
gpointer callback_data)
|
|
{
|
|
g_return_if_fail (pixels != NULL);
|
|
g_return_if_fail (callback_data != NULL);
|
|
|
|
gdk_pixbuf_unref ((GdkPixbuf *) callback_data);
|
|
}
|
|
|
|
/**
|
|
* nautilus_gdk_pixbuf_new_from_pixbuf_sub_area:
|
|
* @pixbuf: The source pixbuf.
|
|
* @area: The area within the source pixbuf to use for the sub pixbuf.
|
|
* This area needs to be contained within the bounds of the
|
|
* source pixbuf, otherwise it will be clipped to that.
|
|
*
|
|
* Return value: A newly allocated pixbuf that shares the pixel data
|
|
* of the source pixbuf in order to represent a sub area.
|
|
*
|
|
* Create a pixbuf from a sub area of another pixbuf. The resulting pixbuf
|
|
* will share the pixel data of the source pixbuf. Memory bookeeping is
|
|
* all taken care for the caller. All you need to do is gdk_pixbuf_unref()
|
|
* the resulting pixbuf to properly free resources.
|
|
*/
|
|
GdkPixbuf *
|
|
nautilus_gdk_pixbuf_new_from_pixbuf_sub_area (GdkPixbuf *pixbuf,
|
|
const ArtIRect *area)
|
|
{
|
|
GdkPixbuf *sub_pixbuf;
|
|
ArtIRect frame;
|
|
ArtIRect target;
|
|
guchar *pixels;
|
|
|
|
g_return_val_if_fail (nautilus_gdk_pixbuf_is_valid (pixbuf), NULL);
|
|
g_return_val_if_fail (area != NULL, NULL);
|
|
g_return_val_if_fail (area->x1 > area->x0, NULL);
|
|
g_return_val_if_fail (area->y1 > area->y0, NULL);
|
|
|
|
frame = nautilus_gdk_pixbuf_get_frame (pixbuf);
|
|
|
|
art_irect_intersect (&target, area, &frame);
|
|
if (art_irect_empty (&target)) {
|
|
return NULL;
|
|
}
|
|
|
|
/* Since we are going to be sharing the given pixbuf's data, we need
|
|
* to ref it. It will be unreffed in the destroy function above */
|
|
gdk_pixbuf_ref (pixbuf);
|
|
|
|
/* Compute the offset into the pixel data */
|
|
pixels =
|
|
gdk_pixbuf_get_pixels (pixbuf)
|
|
+ (target.y0 * gdk_pixbuf_get_rowstride (pixbuf))
|
|
+ (target.x0 * (gdk_pixbuf_get_has_alpha (pixbuf) ? 4 : 3));
|
|
|
|
/* Make a pixbuf pretending its real estate is the sub area */
|
|
sub_pixbuf = gdk_pixbuf_new_from_data (pixels,
|
|
GDK_COLORSPACE_RGB,
|
|
gdk_pixbuf_get_has_alpha (pixbuf),
|
|
8,
|
|
target.x1 - target.x0,
|
|
target.y1 - target.y0,
|
|
gdk_pixbuf_get_rowstride (pixbuf),
|
|
pixbuf_destroy_callback,
|
|
pixbuf);
|
|
|
|
return sub_pixbuf;
|
|
}
|
|
|
|
/* The tile algorithm is identical whether the destination is
|
|
* a pixbuf or a drawable. So, we use a simple callback
|
|
* mechanism to share it regardless of the destination.
|
|
*/
|
|
typedef struct {
|
|
GdkPixbuf *destination_pixbuf;
|
|
int opacity;
|
|
GdkInterpType interpolation_mode;
|
|
} PixbufTileData;
|
|
|
|
typedef struct {
|
|
GdkDrawable *drawable;
|
|
GdkGC *gc;
|
|
GdkRgbDither dither;
|
|
GdkPixbufAlphaMode alpha_compositing_mode;
|
|
int alpha_threshold;
|
|
} DrawableTileData;
|
|
|
|
typedef void (* DrawPixbufTileCallback) (const GdkPixbuf *pixbuf,
|
|
int x,
|
|
int y,
|
|
const ArtIRect *destination_area,
|
|
gpointer callback_data);
|
|
|
|
/* The shared tiliing implementation */
|
|
static void
|
|
pixbuf_draw_tiled (const GdkPixbuf *pixbuf,
|
|
const ArtIRect *destination_frame,
|
|
const ArtIRect *destination_area,
|
|
int tile_width,
|
|
int tile_height,
|
|
int tile_origin_x,
|
|
int tile_origin_y,
|
|
DrawPixbufTileCallback callback,
|
|
gpointer callback_data)
|
|
{
|
|
ArtIRect target;
|
|
int x;
|
|
int y;
|
|
NautilusArtIPoint min_point;
|
|
NautilusArtIPoint max_point;
|
|
int num_left;
|
|
int num_above;
|
|
|
|
g_return_if_fail (pixbuf != NULL);
|
|
g_return_if_fail (destination_frame != NULL);
|
|
g_return_if_fail (tile_width > 0);
|
|
g_return_if_fail (tile_height > 0);
|
|
g_return_if_fail (tile_width <= gdk_pixbuf_get_width (pixbuf));
|
|
g_return_if_fail (tile_height <= gdk_pixbuf_get_height (pixbuf));
|
|
g_return_if_fail (callback != NULL);
|
|
|
|
if (destination_area != NULL) {
|
|
art_irect_intersect (&target, destination_area, destination_frame);
|
|
|
|
if (art_irect_empty (&target)) {
|
|
return;
|
|
}
|
|
} else {
|
|
target = *destination_frame;
|
|
}
|
|
|
|
/* The number of tiles left and above the target area */
|
|
num_left = (target.x0 - tile_origin_x) / tile_width;
|
|
num_above = (target.y0 - tile_origin_y) / tile_height;
|
|
|
|
nautilus_art_ipoint_assign (&min_point,
|
|
tile_origin_x - tile_width + (num_left * tile_width),
|
|
tile_origin_y - tile_height + (num_above * tile_height));
|
|
|
|
nautilus_art_ipoint_assign (&max_point,
|
|
(target.x1 + 2 * tile_width),
|
|
(target.y1 + 2 * tile_height));
|
|
|
|
for (y = min_point.y; y <= max_point.y; y += tile_height) {
|
|
for (x = min_point.x; x <= max_point.x; x += tile_width) {
|
|
ArtIRect current;
|
|
ArtIRect area;
|
|
|
|
nautilus_art_irect_assign (¤t, x, y, tile_width, tile_height);
|
|
|
|
/* FIXME: A potential speed improvement here would be to clip only the
|
|
* first and last rectangles, not the ones in between.
|
|
*/
|
|
art_irect_intersect (&area, &target, ¤t);
|
|
|
|
if (!art_irect_empty (&area)) {
|
|
g_assert (area.x0 >= x);
|
|
g_assert (area.y0 >= y);
|
|
|
|
(* callback) (pixbuf,
|
|
area.x0 - x,
|
|
area.y0 - y,
|
|
&area,
|
|
callback_data);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
draw_tile_to_pixbuf_callback (const GdkPixbuf *pixbuf,
|
|
int x,
|
|
int y,
|
|
const ArtIRect *area,
|
|
gpointer callback_data)
|
|
{
|
|
PixbufTileData *pixbuf_tile_data;
|
|
|
|
g_return_if_fail (pixbuf != NULL);
|
|
g_return_if_fail (area != NULL);
|
|
g_return_if_fail (callback_data != NULL);
|
|
|
|
pixbuf_tile_data = (PixbufTileData *) callback_data;
|
|
|
|
if (pixbuf_tile_data->opacity == NAUTILUS_OPACITY_FULLY_TRANSPARENT) {
|
|
nautilus_gdk_pixbuf_draw_to_pixbuf (pixbuf,
|
|
pixbuf_tile_data->destination_pixbuf,
|
|
x,
|
|
y,
|
|
area);
|
|
} else {
|
|
nautilus_gdk_pixbuf_draw_to_pixbuf_alpha (pixbuf,
|
|
pixbuf_tile_data->destination_pixbuf,
|
|
x,
|
|
y,
|
|
area,
|
|
pixbuf_tile_data->opacity,
|
|
pixbuf_tile_data->interpolation_mode);
|
|
}
|
|
}
|
|
|
|
static void
|
|
draw_tile_to_drawable_callback (const GdkPixbuf *pixbuf,
|
|
int x,
|
|
int y,
|
|
const ArtIRect *area,
|
|
gpointer callback_data)
|
|
{
|
|
DrawableTileData *drawable_tile_data;
|
|
|
|
g_return_if_fail (pixbuf != NULL);
|
|
g_return_if_fail (area != NULL);
|
|
g_return_if_fail (callback_data != NULL);
|
|
|
|
drawable_tile_data = (DrawableTileData *) callback_data;
|
|
|
|
nautilus_gdk_pixbuf_draw_to_drawable (pixbuf,
|
|
drawable_tile_data->drawable,
|
|
drawable_tile_data->gc,
|
|
x,
|
|
y,
|
|
area,
|
|
drawable_tile_data->dither,
|
|
drawable_tile_data->alpha_compositing_mode,
|
|
drawable_tile_data->alpha_threshold);
|
|
}
|
|
|
|
/**
|
|
* nautilus_gdk_pixbuf_draw_to_pixbuf_tiled:
|
|
* @pixbuf: Source tile pixbuf.
|
|
* @destination_pixbuf: Destination pixbuf.
|
|
* @destination_area: Area of the destination pixbuf to tile.
|
|
* @tile_width: Width of the tile. This can be less than width of the
|
|
* tile pixbuf, but not greater.
|
|
* @tile_height: Height of the tile. This can be less than width of the
|
|
* tile pixbuf, but not greater.
|
|
* @tile_origin_x: The x coordinate of the tile origin. Can be negative.
|
|
* @tile_origin_y: The y coordinate of the tile origin. Can be negative.
|
|
* @opacity: The opacity of the drawn tiles where 0 <= opacity <= 255.
|
|
* @interpolation_mode: The interpolation mode. See <gdk-pixbuf.h>
|
|
*
|
|
* Fill an area of a GdkPixbuf with a tile.
|
|
*/
|
|
void
|
|
nautilus_gdk_pixbuf_draw_to_pixbuf_tiled (const GdkPixbuf *pixbuf,
|
|
GdkPixbuf *destination_pixbuf,
|
|
const ArtIRect *destination_area,
|
|
int tile_width,
|
|
int tile_height,
|
|
int tile_origin_x,
|
|
int tile_origin_y,
|
|
int opacity,
|
|
GdkInterpType interpolation_mode)
|
|
{
|
|
PixbufTileData pixbuf_tile_data;
|
|
ArtIRect destination_frame;
|
|
|
|
g_return_if_fail (nautilus_gdk_pixbuf_is_valid (destination_pixbuf));
|
|
g_return_if_fail (nautilus_gdk_pixbuf_is_valid (pixbuf));
|
|
g_return_if_fail (tile_width > 0);
|
|
g_return_if_fail (tile_height > 0);
|
|
g_return_if_fail (tile_width <= gdk_pixbuf_get_width (pixbuf));
|
|
g_return_if_fail (tile_height <= gdk_pixbuf_get_height (pixbuf));
|
|
g_return_if_fail (opacity >= NAUTILUS_OPACITY_FULLY_TRANSPARENT);
|
|
g_return_if_fail (opacity <= NAUTILUS_OPACITY_FULLY_OPAQUE);
|
|
g_return_if_fail (interpolation_mode >= GDK_INTERP_NEAREST);
|
|
g_return_if_fail (interpolation_mode <= GDK_INTERP_HYPER);
|
|
|
|
destination_frame = nautilus_gdk_pixbuf_get_frame (destination_pixbuf);
|
|
|
|
pixbuf_tile_data.destination_pixbuf = destination_pixbuf;
|
|
pixbuf_tile_data.opacity = opacity;
|
|
pixbuf_tile_data.interpolation_mode = interpolation_mode;
|
|
|
|
pixbuf_draw_tiled (pixbuf,
|
|
&destination_frame,
|
|
destination_area,
|
|
tile_width,
|
|
tile_height,
|
|
tile_origin_x,
|
|
tile_origin_y,
|
|
draw_tile_to_pixbuf_callback,
|
|
&pixbuf_tile_data);
|
|
}
|
|
|
|
/**
|
|
* nautilus_gdk_pixbuf_draw_to_drawable_tiled:
|
|
* @pixbuf: Source tile pixbuf.
|
|
* @gc: GC for copy area operation.
|
|
* @drawable: Destination drawable.
|
|
* @destination_area: Area of the destination pixbuf to tile.
|
|
* @tile_width: Width of the tile. This can be less than width of the
|
|
* tile pixbuf, but not greater.
|
|
* @tile_height: Height of the tile. This can be less than width of the
|
|
* tile pixbuf, but not greater.
|
|
* @tile_origin_x: The x coordinate of the tile origin. Can be negative.
|
|
* @tile_origin_y: The y coordinate of the tile origin. Can be negative.
|
|
* @dither: Dither type to use (see <gdkrgb.h>)
|
|
* @dither: Dither type to use (see <gdkrgb.h>)
|
|
* @alpha_compositing_mode: The alpha compositing mode. See <gdk-pixbuf.h>
|
|
*
|
|
* Fill an area of a GdkDrawable with a tile.
|
|
*/
|
|
void
|
|
nautilus_gdk_pixbuf_draw_to_drawable_tiled (const GdkPixbuf *pixbuf,
|
|
GdkDrawable *drawable,
|
|
GdkGC *gc,
|
|
const ArtIRect *destination_area,
|
|
int tile_width,
|
|
int tile_height,
|
|
int tile_origin_x,
|
|
int tile_origin_y,
|
|
GdkRgbDither dither,
|
|
GdkPixbufAlphaMode alpha_compositing_mode,
|
|
int alpha_threshold)
|
|
{
|
|
DrawableTileData drawable_tile_data;
|
|
ArtIRect destination_frame;
|
|
|
|
g_return_if_fail (nautilus_gdk_pixbuf_is_valid (pixbuf));
|
|
g_return_if_fail (drawable != NULL);
|
|
g_return_if_fail (tile_width > 0);
|
|
g_return_if_fail (tile_height > 0);
|
|
g_return_if_fail (tile_width <= gdk_pixbuf_get_width (pixbuf));
|
|
g_return_if_fail (tile_height <= gdk_pixbuf_get_height (pixbuf));
|
|
g_return_if_fail (alpha_threshold > NAUTILUS_OPACITY_FULLY_TRANSPARENT);
|
|
g_return_if_fail (alpha_threshold <= NAUTILUS_OPACITY_FULLY_OPAQUE);
|
|
g_return_if_fail (alpha_compositing_mode >= GDK_PIXBUF_ALPHA_BILEVEL);
|
|
g_return_if_fail (alpha_compositing_mode <= GDK_PIXBUF_ALPHA_FULL);
|
|
|
|
destination_frame = nautilus_gdk_window_get_frame (drawable);
|
|
|
|
drawable_tile_data.drawable = drawable;
|
|
drawable_tile_data.gc = gc;
|
|
drawable_tile_data.dither = dither;
|
|
drawable_tile_data.alpha_compositing_mode = alpha_compositing_mode;
|
|
drawable_tile_data.alpha_threshold = alpha_threshold;
|
|
|
|
pixbuf_draw_tiled (pixbuf,
|
|
&destination_frame,
|
|
destination_area,
|
|
tile_width,
|
|
tile_height,
|
|
tile_origin_x,
|
|
tile_origin_y,
|
|
draw_tile_to_drawable_callback,
|
|
&drawable_tile_data);
|
|
}
|
|
|
|
/**
|
|
* nautilus_gdk_pixbuf_get_global_buffer:
|
|
* @minimum_width: The minimum width for the requested buffer.
|
|
* @minimum_height: The minimum height for the requested buffer.
|
|
*
|
|
* Return value: A GdkPixbuf that is at least as big as the passed in
|
|
* dimensions.
|
|
*
|
|
* Access a global buffer for temporary GdkPixbuf operations.
|
|
* The returned buffer will be at least as big as the passed in
|
|
* dimensions. The contents are not guaranteed to be anything at
|
|
* anytime. Also, it is not thread safe at all. */
|
|
|
|
static GdkPixbuf *global_buffer = NULL;
|
|
|
|
static void
|
|
destroy_global_buffer (void)
|
|
{
|
|
if (global_buffer != NULL) {
|
|
gdk_pixbuf_unref (global_buffer);
|
|
global_buffer = NULL;
|
|
}
|
|
}
|
|
|
|
GdkPixbuf *
|
|
nautilus_gdk_pixbuf_get_global_buffer (int minimum_width,
|
|
int minimum_height)
|
|
{
|
|
static gboolean at_exit_deallocator_installed = FALSE;
|
|
|
|
g_return_val_if_fail (minimum_width > 0, NULL);
|
|
g_return_val_if_fail (minimum_height > 0, NULL);
|
|
|
|
if (global_buffer != NULL) {
|
|
if (gdk_pixbuf_get_width (global_buffer) >= minimum_width
|
|
&& gdk_pixbuf_get_height (global_buffer) >= minimum_height) {
|
|
return global_buffer;
|
|
}
|
|
|
|
destroy_global_buffer ();
|
|
}
|
|
|
|
g_assert (global_buffer == NULL);
|
|
|
|
global_buffer = gdk_pixbuf_new (GDK_COLORSPACE_RGB, FALSE, 8,
|
|
minimum_width, minimum_height);
|
|
|
|
|
|
if (at_exit_deallocator_installed == FALSE) {
|
|
at_exit_deallocator_installed = TRUE;
|
|
g_atexit (destroy_global_buffer);
|
|
}
|
|
|
|
return global_buffer;
|
|
}
|
|
|
|
/* FIXME bugzilla.eazel.com 5813:
|
|
* As soon as gtk 1.2.9 is released, this hack needs to be exorcised.
|
|
*/
|
|
GdkPixbuf *
|
|
NAUTILUS_BUG_5712_PR3_WORKAROUND__gdk_pixbuf_get_from_drawable (GdkPixbuf *dest,
|
|
GdkDrawable *src, GdkColormap *cmap,
|
|
int src_x, int src_y,
|
|
int dest_x, int dest_y,
|
|
int width, int height);
|
|
|
|
/* Same as gdk_pixbuf_get_from_drawable() except it deals with
|
|
* race conditions and other evil things that can happen */
|
|
GdkPixbuf *
|
|
nautilus_gdk_pixbuf_get_from_window_safe (GdkWindow *window,
|
|
int x,
|
|
int y,
|
|
int width,
|
|
int height)
|
|
{
|
|
GdkWindowPrivate *window_private;
|
|
GdkPixbuf *pixbuf;
|
|
int error_code;
|
|
GdkWindowType save_window_type;
|
|
GdkColormap *colormap;
|
|
|
|
g_return_val_if_fail (window != NULL, NULL);
|
|
|
|
|
|
/* Push an error handler so that we can catch
|
|
* the very rare (but possible) case where
|
|
* the GetImage() request fails. See HACK2 below
|
|
* for a more complete excuse.
|
|
*/
|
|
gdk_error_trap_push ();
|
|
|
|
/* Save the window type and colormap. Otherwise
|
|
* GdkPixbuf will try to fetch them from a hacked
|
|
* window. See HACK1 below for reason why we
|
|
* hack the private window contents.
|
|
*/
|
|
save_window_type = gdk_window_get_type (window);
|
|
colormap = gdk_window_get_colormap (window);
|
|
|
|
/* HACK1:
|
|
*
|
|
* This horrible thing we do here is needed to
|
|
* prevent GdkPixbuf from doing geometry sanity
|
|
* checks on the window. By pretending it
|
|
* is a Pixmap, we fool GdkPixbuf into not doing
|
|
* these checks.
|
|
*
|
|
* Why ? The sanity checks that GdkPixbuf does
|
|
* are good most of the time, but not 100% of
|
|
* the time. This is because there is no guarantee
|
|
* that when the x server gets the GetImage request,
|
|
* the geometry of that window is what GdkPixbuf
|
|
* thought it was.
|
|
*
|
|
* For example, the window manager could have triggered
|
|
* a sequence of events causing the window to resize.
|
|
*/
|
|
window_private = (GdkWindowPrivate*) window;
|
|
window_private->window_type = GDK_WINDOW_PIXMAP;
|
|
|
|
pixbuf = NAUTILUS_BUG_5712_PR3_WORKAROUND__gdk_pixbuf_get_from_drawable (NULL,
|
|
window,
|
|
colormap,
|
|
x,
|
|
y,
|
|
0,
|
|
0,
|
|
width,
|
|
height);
|
|
|
|
/* Restore the window's guts */
|
|
window_private->window_type = save_window_type;
|
|
|
|
/* HACK2:
|
|
*
|
|
* Now we pop the error handler and see whether an error
|
|
* occured.
|
|
*
|
|
* It is very rare that an error might occur. The conditions
|
|
* under which it might are:
|
|
*
|
|
* 1) Race condition as described above in HACK1
|
|
*
|
|
* 2) Bogus coordinates and/or dimensions given to
|
|
* the GetImage() request - which GdkPixbuf cant
|
|
* safely sanity check against.
|
|
*
|
|
* So, if we get an error, we simply drop this request on
|
|
* the floor and return NULL. The caller needs to deal with
|
|
* the fact that their request couldnt be executed. Most of
|
|
* the time, all that is needed is to simply ignore it.
|
|
*/
|
|
error_code = gdk_error_trap_pop ();
|
|
|
|
if (error_code != 0) {
|
|
/* HACK3:
|
|
*
|
|
* The magical number "8" is the minor x error request
|
|
* code. That is the only error we are expecting.
|
|
*
|
|
* Otherwise we still return NULL, but the caller
|
|
* gets a critical.
|
|
*/
|
|
g_return_val_if_fail (error_code == 8, NULL);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
return pixbuf;
|
|
}
|