gimp/app/core/gimpimagefile.c

1103 lines
33 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* GIMP - The GNU Image Manipulation Program
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* gimpimagefile.c
*
* Copyright (C) 2001-2004 Sven Neumann <sven@gimp.org>
* Michael Natterer <mitch@gimp.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <string.h>
#include <gegl.h>
#include <gdk-pixbuf/gdk-pixbuf.h>
#include "libgimpbase/gimpbase.h"
#include "libgimpthumb/gimpthumb.h"
#include "core-types.h"
#include "config/gimpcoreconfig.h"
#include "gegl/gimp-babl.h"
#include "gegl/gimp-gegl-utils.h"
#include "gimp.h"
#include "gimpcontainer.h"
#include "gimpcontext.h"
#include "gimpimage.h"
#include "gimpimage-metadata.h"
#include "gimpimagefile.h"
#include "gimppickable.h"
#include "gimpprogress.h"
#include "file/file-open.h"
#include "gimp-intl.h"
enum
{
INFO_CHANGED,
LAST_SIGNAL
};
typedef struct _GimpImagefilePrivate GimpImagefilePrivate;
struct _GimpImagefilePrivate
{
Gimp *gimp;
GFile *file;
GimpThumbnail *thumbnail;
GIcon *icon;
GCancellable *icon_cancellable;
gchar *description;
gboolean static_desc;
};
#define GET_PRIVATE(imagefile) ((GimpImagefilePrivate *) gimp_imagefile_get_instance_private ((GimpImagefile *) (imagefile)))
static void gimp_imagefile_dispose (GObject *object);
static void gimp_imagefile_finalize (GObject *object);
static void gimp_imagefile_name_changed (GimpObject *object);
static GdkPixbuf * gimp_imagefile_get_new_pixbuf (GimpViewable *viewable,
GimpContext *context,
gint width,
gint height);
static gchar * gimp_imagefile_get_description (GimpViewable *viewable,
gchar **tooltip);
static void gimp_imagefile_info_changed (GimpImagefile *imagefile);
static void gimp_imagefile_notify_thumbnail (GimpImagefile *imagefile,
GParamSpec *pspec);
static void gimp_imagefile_icon_callback (GObject *source_object,
GAsyncResult *result,
gpointer data);
static GdkPixbuf * gimp_imagefile_load_thumb (GimpImagefile *imagefile,
gint width,
gint height);
static gboolean gimp_imagefile_save_thumb (GimpImagefile *imagefile,
GimpImage *image,
gint size,
gboolean replace,
GError **error);
static void gimp_thumbnail_set_info_from_image (GimpThumbnail *thumbnail,
const gchar *mime_type,
GimpImage *image);
static void gimp_thumbnail_set_info (GimpThumbnail *thumbnail,
const gchar *mime_type,
gint width,
gint height,
const Babl *format,
gint num_layers);
G_DEFINE_TYPE_WITH_PRIVATE (GimpImagefile, gimp_imagefile, GIMP_TYPE_VIEWABLE)
#define parent_class gimp_imagefile_parent_class
static guint gimp_imagefile_signals[LAST_SIGNAL] = { 0 };
static void
gimp_imagefile_class_init (GimpImagefileClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GimpObjectClass *gimp_object_class = GIMP_OBJECT_CLASS (klass);
GimpViewableClass *viewable_class = GIMP_VIEWABLE_CLASS (klass);
gchar *creator;
gimp_imagefile_signals[INFO_CHANGED] =
g_signal_new ("info-changed",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET (GimpImagefileClass, info_changed),
NULL, NULL, NULL,
G_TYPE_NONE, 0);
object_class->dispose = gimp_imagefile_dispose;
object_class->finalize = gimp_imagefile_finalize;
gimp_object_class->name_changed = gimp_imagefile_name_changed;
viewable_class->name_changed_signal = "info-changed";
viewable_class->get_new_pixbuf = gimp_imagefile_get_new_pixbuf;
viewable_class->get_description = gimp_imagefile_get_description;
g_type_class_ref (GIMP_TYPE_IMAGE_TYPE);
creator = g_strdup_printf ("gimp-%d.%d",
GIMP_MAJOR_VERSION, GIMP_MINOR_VERSION);
gimp_thumb_init (creator, NULL);
g_free (creator);
}
static void
gimp_imagefile_init (GimpImagefile *imagefile)
{
GimpImagefilePrivate *private = GET_PRIVATE (imagefile);
private->thumbnail = gimp_thumbnail_new ();
g_signal_connect_object (private->thumbnail, "notify",
G_CALLBACK (gimp_imagefile_notify_thumbnail),
imagefile, G_CONNECT_SWAPPED);
}
static void
gimp_imagefile_dispose (GObject *object)
{
GimpImagefilePrivate *private = GET_PRIVATE (object);
if (private->icon_cancellable)
{
g_cancellable_cancel (private->icon_cancellable);
g_clear_object (&private->icon_cancellable);
}
G_OBJECT_CLASS (parent_class)->dispose (object);
}
static void
gimp_imagefile_finalize (GObject *object)
{
GimpImagefilePrivate *private = GET_PRIVATE (object);
if (private->description)
{
if (! private->static_desc)
g_free (private->description);
private->description = NULL;
}
g_clear_object (&private->thumbnail);
g_clear_object (&private->icon);
g_clear_object (&private->file);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
gimp_imagefile_name_changed (GimpObject *object)
{
GimpImagefilePrivate *private = GET_PRIVATE (object);
if (GIMP_OBJECT_CLASS (parent_class)->name_changed)
GIMP_OBJECT_CLASS (parent_class)->name_changed (object);
gimp_thumbnail_set_uri (private->thumbnail, gimp_object_get_name (object));
g_clear_object (&private->file);
if (gimp_object_get_name (object))
private->file = g_file_new_for_uri (gimp_object_get_name (object));
}
static GdkPixbuf *
gimp_imagefile_get_new_pixbuf (GimpViewable *viewable,
GimpContext *context,
gint width,
gint height)
{
GimpImagefile *imagefile = GIMP_IMAGEFILE (viewable);
if (! gimp_object_get_name (imagefile))
return NULL;
return gimp_imagefile_load_thumb (imagefile, width, height);
}
static gchar *
gimp_imagefile_get_description (GimpViewable *viewable,
gchar **tooltip)
{
GimpImagefile *imagefile = GIMP_IMAGEFILE (viewable);
GimpImagefilePrivate *private = GET_PRIVATE (imagefile);
GimpThumbnail *thumbnail = private->thumbnail;
gchar *basename;
if (! private->file)
return NULL;
if (tooltip)
{
const gchar *name;
const gchar *desc;
name = gimp_file_get_utf8_name (private->file);
desc = gimp_imagefile_get_desc_string (imagefile);
if (desc)
*tooltip = g_strdup_printf ("%s\n%s", name, desc);
else
*tooltip = g_strdup (name);
}
basename = g_path_get_basename (gimp_file_get_utf8_name (private->file));
if (thumbnail->image_width > 0 && thumbnail->image_height > 0)
{
gchar *tmp = basename;
basename = g_strdup_printf ("%s (%d × %d)",
tmp,
thumbnail->image_width,
thumbnail->image_height);
g_free (tmp);
}
return basename;
}
/* public functions */
GimpImagefile *
gimp_imagefile_new (Gimp *gimp,
GFile *file)
{
GimpImagefile *imagefile;
g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL);
g_return_val_if_fail (file == NULL || G_IS_FILE (file), NULL);
imagefile = g_object_new (GIMP_TYPE_IMAGEFILE, NULL);
GET_PRIVATE (imagefile)->gimp = gimp;
if (file)
{
gimp_object_take_name (GIMP_OBJECT (imagefile), g_file_get_uri (file));
/* file member gets created by gimp_imagefile_name_changed() */
}
return imagefile;
}
GFile *
gimp_imagefile_get_file (GimpImagefile *imagefile)
{
g_return_val_if_fail (GIMP_IS_IMAGEFILE (imagefile), NULL);
return GET_PRIVATE (imagefile)->file;
}
void
gimp_imagefile_set_file (GimpImagefile *imagefile,
GFile *file)
{
g_return_if_fail (GIMP_IS_IMAGEFILE (imagefile));
g_return_if_fail (file == NULL || G_IS_FILE (file));
if (GET_PRIVATE (imagefile)->file != file)
{
gimp_object_take_name (GIMP_OBJECT (imagefile),
file ? g_file_get_uri (file) : NULL);
}
}
GimpThumbnail *
gimp_imagefile_get_thumbnail (GimpImagefile *imagefile)
{
g_return_val_if_fail (GIMP_IS_IMAGEFILE (imagefile), NULL);
return GET_PRIVATE (imagefile)->thumbnail;
}
GIcon *
gimp_imagefile_get_gicon (GimpImagefile *imagefile)
{
GimpImagefilePrivate *private;
g_return_val_if_fail (GIMP_IS_IMAGEFILE (imagefile), NULL);
private = GET_PRIVATE (imagefile);
if (private->icon)
return private->icon;
if (private->file && ! private->icon_cancellable)
{
private->icon_cancellable = g_cancellable_new ();
g_file_query_info_async (private->file, "standard::icon",
G_FILE_QUERY_INFO_NONE,
G_PRIORITY_DEFAULT,
private->icon_cancellable,
gimp_imagefile_icon_callback,
imagefile);
}
return NULL;
}
void
gimp_imagefile_set_mime_type (GimpImagefile *imagefile,
const gchar *mime_type)
{
g_return_if_fail (GIMP_IS_IMAGEFILE (imagefile));
g_object_set (GET_PRIVATE (imagefile)->thumbnail,
"image-mimetype", mime_type,
NULL);
}
void
gimp_imagefile_update (GimpImagefile *imagefile)
{
GimpImagefilePrivate *private;
gchar *uri;
g_return_if_fail (GIMP_IS_IMAGEFILE (imagefile));
private = GET_PRIVATE (imagefile);
gimp_viewable_invalidate_preview (GIMP_VIEWABLE (imagefile));
g_object_get (private->thumbnail,
"image-uri", &uri,
NULL);
if (uri)
{
GimpImagefile *documents_imagefile = (GimpImagefile *)
gimp_container_get_child_by_name (private->gimp->documents, uri);
if (documents_imagefile != imagefile &&
GIMP_IS_IMAGEFILE (documents_imagefile))
gimp_viewable_invalidate_preview (GIMP_VIEWABLE (documents_imagefile));
g_free (uri);
}
}
gboolean
gimp_imagefile_create_thumbnail (GimpImagefile *imagefile,
GimpContext *context,
GimpProgress *progress,
gint size,
gboolean replace,
GError **error)
{
GimpImagefilePrivate *private;
GimpThumbnail *thumbnail;
GimpThumbState image_state;
g_return_val_if_fail (GIMP_IS_IMAGEFILE (imagefile), FALSE);
g_return_val_if_fail (GIMP_IS_CONTEXT (context), FALSE);
g_return_val_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress), FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
/* thumbnailing is disabled, we successfully did nothing */
if (size < 1)
return TRUE;
private = GET_PRIVATE (imagefile);
thumbnail = private->thumbnail;
gimp_thumbnail_set_uri (thumbnail,
gimp_object_get_name (imagefile));
image_state = gimp_thumbnail_peek_image (thumbnail);
if (image_state == GIMP_THUMB_STATE_REMOTE ||
image_state >= GIMP_THUMB_STATE_EXISTS)
{
GimpImage *image;
gboolean success;
gint width = 0;
gint height = 0;
const gchar *mime_type = NULL;
const Babl *format = NULL;
gint num_layers = -1;
/* we only want to attempt thumbnailing on readable, regular files */
if (g_file_is_native (private->file))
{
GFileInfo *file_info;
gboolean regular;
gboolean readable;
file_info = g_file_query_info (private->file,
G_FILE_ATTRIBUTE_STANDARD_TYPE ","
G_FILE_ATTRIBUTE_ACCESS_CAN_READ,
G_FILE_QUERY_INFO_NONE,
NULL, NULL);
regular = (g_file_info_get_attribute_uint32 (file_info, G_FILE_ATTRIBUTE_STANDARD_TYPE) == G_FILE_TYPE_REGULAR);
readable = g_file_info_get_attribute_boolean (file_info,
G_FILE_ATTRIBUTE_ACCESS_CAN_READ);
g_object_unref (file_info);
if (! (regular && readable))
return TRUE;
}
g_object_ref (imagefile);
image = file_open_thumbnail (private->gimp, context, progress,
private->file, size,
&mime_type, &width, &height,
&format, &num_layers, error);
if (image)
{
gimp_thumbnail_set_info (private->thumbnail,
mime_type, width, height,
format, num_layers);
}
else
{
GimpPDBStatusType status;
if (error && *error)
{
g_printerr ("Info: Thumbnail load procedure failed: %s\n"
" Falling back to metadata or file load.\n",
(*error)->message);
g_clear_error (error);
}
image = gimp_image_metadata_load_thumbnail (private->gimp, private->file, &width, &height, &format, error);
if (image)
{
gimp_thumbnail_set_info (private->thumbnail,
mime_type, width, height,
format, 0);
}
else
{
if (error && *error)
{
g_printerr ("Info: metadata load failed: %s\n"
" Falling back to file load procedure.\n",
(*error)->message);
g_clear_error (error);
}
image = file_open_image (private->gimp, context, progress,
private->file,
FALSE, NULL, GIMP_RUN_NONINTERACTIVE,
&status, &mime_type, error);
if (image)
gimp_thumbnail_set_info_from_image (private->thumbnail,
mime_type, image);
}
}
if (image)
{
success = gimp_imagefile_save_thumb (imagefile,
image, size, replace,
error);
g_object_unref (image);
}
else
{
/* If the error object is already set (i.e. we have an error
* message for why the thumbnail creation failed), this is the
* error we want to return. Ignore any error from failed
* thumbnail saving.
*/
gimp_thumbnail_save_failure (thumbnail,
"GIMP " GIMP_VERSION,
error && *error ? NULL : error);
gimp_imagefile_update (imagefile);
success = FALSE;
}
g_object_unref (imagefile);
if (! success)
{
g_object_set (thumbnail,
"thumb-state", GIMP_THUMB_STATE_FAILED,
NULL);
}
return success;
}
return TRUE;
}
/* The weak version doesn't ref the imagefile but deals gracefully
* with an imagefile that is destroyed while the thumbnail is
* created. This allows one to use this function w/o the need to
* block the user interface.
*/
void
gimp_imagefile_create_thumbnail_weak (GimpImagefile *imagefile,
GimpContext *context,
GimpProgress *progress,
gint size,
gboolean replace)
{
GimpImagefilePrivate *private;
GimpImagefile *local;
g_return_if_fail (GIMP_IS_IMAGEFILE (imagefile));
if (size < 1)
return;
private = GET_PRIVATE (imagefile);
if (! private->file)
return;
local = gimp_imagefile_new (private->gimp, private->file);
g_object_add_weak_pointer (G_OBJECT (imagefile), (gpointer) &imagefile);
if (! gimp_imagefile_create_thumbnail (local, context, progress, size, replace,
NULL))
{
/* The weak version works on a local copy so the thumbnail
* status of the actual image is not properly updated in case of
* creation failure, thus it would end up in a generic
* GIMP_THUMB_STATE_NOT_FOUND, which is less informative.
*/
g_object_set (private->thumbnail,
"thumb-state", GIMP_THUMB_STATE_FAILED,
NULL);
}
if (imagefile)
{
GFile *file = gimp_imagefile_get_file (imagefile);
if (file && g_file_equal (file, gimp_imagefile_get_file (local)))
{
gimp_imagefile_update (imagefile);
}
g_object_remove_weak_pointer (G_OBJECT (imagefile),
(gpointer) &imagefile);
}
g_object_unref (local);
}
gboolean
gimp_imagefile_check_thumbnail (GimpImagefile *imagefile)
{
GimpImagefilePrivate *private;
gint size;
g_return_val_if_fail (GIMP_IS_IMAGEFILE (imagefile), FALSE);
private = GET_PRIVATE (imagefile);
size = private->gimp->config->thumbnail_size;
if (size > 0)
{
GimpThumbState state;
state = gimp_thumbnail_check_thumb (private->thumbnail, size);
return (state == GIMP_THUMB_STATE_OK);
}
return TRUE;
}
gboolean
gimp_imagefile_save_thumbnail (GimpImagefile *imagefile,
const gchar *mime_type,
GimpImage *image,
GError **error)
{
GimpImagefilePrivate *private;
gint size;
gboolean success = TRUE;
g_return_val_if_fail (GIMP_IS_IMAGEFILE (imagefile), FALSE);
g_return_val_if_fail (GIMP_IS_IMAGE (image), FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
private = GET_PRIVATE (imagefile);
size = private->gimp->config->thumbnail_size;
if (size > 0)
{
gimp_thumbnail_set_info_from_image (private->thumbnail,
mime_type, image);
success = gimp_imagefile_save_thumb (imagefile,
image, size, FALSE,
error);
}
return success;
}
/* private functions */
static void
gimp_imagefile_info_changed (GimpImagefile *imagefile)
{
GimpImagefilePrivate *private = GET_PRIVATE (imagefile);
if (private->description)
{
if (! private->static_desc)
g_free (private->description);
private->description = NULL;
}
g_clear_object (&private->icon);
g_signal_emit (imagefile, gimp_imagefile_signals[INFO_CHANGED], 0);
}
static void
gimp_imagefile_notify_thumbnail (GimpImagefile *imagefile,
GParamSpec *pspec)
{
if (strcmp (pspec->name, "image-state") == 0 ||
strcmp (pspec->name, "thumb-state") == 0)
{
gimp_imagefile_info_changed (imagefile);
}
}
static void
gimp_imagefile_icon_callback (GObject *source_object,
GAsyncResult *result,
gpointer data)
{
GimpImagefile *imagefile;
GimpImagefilePrivate *private;
GFile *file = G_FILE (source_object);
GError *error = NULL;
GFileInfo *file_info;
file_info = g_file_query_info_finish (file, result, &error);
if (error)
{
/* we were cancelled from dispose() and the imagefile is
* long gone, bail out
*/
if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
{
g_clear_error (&error);
return;
}
#ifdef GIMP_UNSTABLE
g_printerr ("%s: %s\n", G_STRFUNC, error->message);
#endif
g_clear_error (&error);
}
imagefile = GIMP_IMAGEFILE (data);
private = GET_PRIVATE (imagefile);
if (file_info)
{
private->icon = g_object_ref (G_ICON (g_file_info_get_attribute_object (file_info, G_FILE_ATTRIBUTE_STANDARD_ICON)));
g_object_unref (file_info);
}
g_clear_object (&private->icon_cancellable);
if (private->icon)
gimp_viewable_invalidate_preview (GIMP_VIEWABLE (imagefile));
}
const gchar *
gimp_imagefile_get_desc_string (GimpImagefile *imagefile)
{
GimpImagefilePrivate *private;
GimpThumbnail *thumbnail;
g_return_val_if_fail (GIMP_IS_IMAGEFILE (imagefile), NULL);
private = GET_PRIVATE (imagefile);
if (private->description)
return (const gchar *) private->description;
thumbnail = private->thumbnail;
switch (thumbnail->image_state)
{
case GIMP_THUMB_STATE_UNKNOWN:
private->description = NULL;
private->static_desc = TRUE;
break;
case GIMP_THUMB_STATE_FOLDER:
private->description = (gchar *) _("Folder");
private->static_desc = TRUE;
break;
case GIMP_THUMB_STATE_SPECIAL:
private->description = (gchar *) _("Special File");
private->static_desc = TRUE;
break;
case GIMP_THUMB_STATE_NOT_FOUND:
private->description =
(gchar *) g_strerror (thumbnail->image_not_found_errno);
private->static_desc = TRUE;
break;
default:
{
GString *str = g_string_new (NULL);
if (thumbnail->image_state == GIMP_THUMB_STATE_REMOTE)
{
g_string_append (str, _("Remote File"));
}
if (thumbnail->image_filesize > 0)
{
gchar *size = g_format_size (thumbnail->image_filesize);
if (str->len > 0)
g_string_append_c (str, '\n');
g_string_append (str, size);
g_free (size);
}
switch (thumbnail->thumb_state)
{
case GIMP_THUMB_STATE_NOT_FOUND:
if (str->len > 0)
g_string_append_c (str, '\n');
g_string_append (str, _("Click to create preview"));
break;
case GIMP_THUMB_STATE_EXISTS:
if (str->len > 0)
g_string_append_c (str, '\n');
g_string_append (str, _("Loading preview..."));
break;
case GIMP_THUMB_STATE_OLD:
if (str->len > 0)
g_string_append_c (str, '\n');
g_string_append (str, _("Preview is out of date"));
break;
case GIMP_THUMB_STATE_FAILED:
if (str->len > 0)
g_string_append_c (str, '\n');
g_string_append (str, _("Cannot create preview"));
break;
case GIMP_THUMB_STATE_OK:
{
if (thumbnail->image_state == GIMP_THUMB_STATE_REMOTE)
{
if (str->len > 0)
g_string_append_c (str, '\n');
g_string_append (str, _("(Preview may be out of date)"));
}
if (thumbnail->image_width > 0 && thumbnail->image_height > 0)
{
if (str->len > 0)
g_string_append_c (str, '\n');
g_string_append_printf (str,
ngettext ("%d × %d pixel",
"%d × %d pixels",
thumbnail->image_height),
thumbnail->image_width,
thumbnail->image_height);
}
if (thumbnail->image_type)
{
if (str->len > 0)
g_string_append_c (str, '\n');
g_string_append (str, gettext (thumbnail->image_type));
}
if (thumbnail->image_num_layers > 0)
{
if (thumbnail->image_type)
g_string_append_len (str, ", ", 2);
else if (str->len > 0)
g_string_append_c (str, '\n');
g_string_append_printf (str,
ngettext ("%d layer",
"%d layers",
thumbnail->image_num_layers),
thumbnail->image_num_layers);
}
}
break;
default:
break;
}
private->description = g_string_free (str, FALSE);
private->static_desc = FALSE;
}
}
return (const gchar *) private->description;
}
static GdkPixbuf *
gimp_imagefile_load_thumb (GimpImagefile *imagefile,
gint width,
gint height)
{
GimpImagefilePrivate *private = GET_PRIVATE (imagefile);
GimpThumbnail *thumbnail = private->thumbnail;
GdkPixbuf *pixbuf = NULL;
GError *error = NULL;
gint size = MAX (width, height);
gint pixbuf_width;
gint pixbuf_height;
gint preview_width;
gint preview_height;
if (gimp_thumbnail_peek_thumb (thumbnail, size) < GIMP_THUMB_STATE_EXISTS)
return NULL;
if (thumbnail->image_state == GIMP_THUMB_STATE_NOT_FOUND)
return NULL;
pixbuf = gimp_thumbnail_load_thumb (thumbnail, size, &error);
if (! pixbuf)
{
if (error)
{
gimp_message (private->gimp, NULL, GIMP_MESSAGE_ERROR,
_("Could not open thumbnail '%s': %s"),
thumbnail->thumb_filename, error->message);
g_clear_error (&error);
}
return NULL;
}
pixbuf_width = gdk_pixbuf_get_width (pixbuf);
pixbuf_height = gdk_pixbuf_get_height (pixbuf);
gimp_viewable_calc_preview_size (pixbuf_width,
pixbuf_height,
width,
height,
TRUE, 1.0, 1.0,
&preview_width,
&preview_height,
NULL);
if (preview_width < pixbuf_width || preview_height < pixbuf_height)
{
GdkPixbuf *scaled = gdk_pixbuf_scale_simple (pixbuf,
preview_width,
preview_height,
GDK_INTERP_BILINEAR);
g_object_unref (pixbuf);
pixbuf = scaled;
pixbuf_width = preview_width;
pixbuf_height = preview_height;
}
if (gdk_pixbuf_get_n_channels (pixbuf) != 3)
{
GdkPixbuf *tmp = gdk_pixbuf_new (GDK_COLORSPACE_RGB, FALSE, 8,
pixbuf_width, pixbuf_height);
gdk_pixbuf_composite_color (pixbuf, tmp,
0, 0, pixbuf_width, pixbuf_height,
0.0, 0.0, 1.0, 1.0,
GDK_INTERP_NEAREST, 255,
0, 0, GIMP_CHECK_SIZE_SM,
0x66666666, 0x99999999);
g_object_unref (pixbuf);
pixbuf = tmp;
}
return pixbuf;
}
static gboolean
gimp_imagefile_save_thumb (GimpImagefile *imagefile,
GimpImage *image,
gint size,
gboolean replace,
GError **error)
{
GimpImagefilePrivate *private = GET_PRIVATE (imagefile);
GimpThumbnail *thumbnail = private->thumbnail;
GdkPixbuf *pixbuf;
gint width, height;
gboolean success = FALSE;
if (size < 1)
return TRUE;
if (gimp_image_get_width (image) <= size &&
gimp_image_get_height (image) <= size)
{
width = gimp_image_get_width (image);
height = gimp_image_get_height (image);
size = MAX (width, height);
}
else
{
if (gimp_image_get_width (image) < gimp_image_get_height (image))
{
height = size;
width = MAX (1, (size * gimp_image_get_width (image) /
gimp_image_get_height (image)));
}
else
{
width = size;
height = MAX (1, (size * gimp_image_get_height (image) /
gimp_image_get_width (image)));
}
}
/* we need the projection constructed NOW, not some time later */
gimp_pickable_flush (GIMP_PICKABLE (image));
pixbuf = gimp_viewable_get_new_pixbuf (GIMP_VIEWABLE (image),
/* random context, unused */
gimp_get_user_context (image->gimp),
width, height);
/* when layer previews are disabled, we won't get a pixbuf */
if (! pixbuf)
return TRUE;
success = gimp_thumbnail_save_thumb (thumbnail,
pixbuf,
"GIMP " GIMP_VERSION,
error);
g_object_unref (pixbuf);
if (success)
{
if (replace)
gimp_thumbnail_delete_others (thumbnail, size);
else
gimp_thumbnail_delete_failure (thumbnail);
gimp_imagefile_update (imagefile);
}
return success;
}
static void
gimp_thumbnail_set_info_from_image (GimpThumbnail *thumbnail,
const gchar *mime_type,
GimpImage *image)
{
const Babl *format;
/* peek the thumbnail to make sure that mtime and filesize are set */
gimp_thumbnail_peek_image (thumbnail);
format = gimp_image_get_layer_format (image,
gimp_image_has_alpha (image));
g_object_set (thumbnail,
"image-mimetype", mime_type,
"image-width", gimp_image_get_width (image),
"image-height", gimp_image_get_height (image),
"image-type", gimp_babl_format_get_description (format),
"image-num-layers", gimp_image_get_n_layers (image),
NULL);
}
/**
* gimp_thumbnail_set_info:
* @thumbnail: #GimpThumbnail object
* @mime_type: MIME type of the image associated with this thumbnail
* @width: width of the image associated with this thumbnail
* @height: height of the image associated with this thumbnail
* @format: format of the image (or NULL if the type is not known)
* @num_layers: number of layers in the image
* (or -1 if the number of layers is not known)
*
* Set information about the image associated with the @thumbnail object.
*/
static void
gimp_thumbnail_set_info (GimpThumbnail *thumbnail,
const gchar *mime_type,
gint width,
gint height,
const Babl *format,
gint num_layers)
{
/* peek the thumbnail to make sure that mtime and filesize are set */
gimp_thumbnail_peek_image (thumbnail);
g_object_set (thumbnail,
"image-mimetype", mime_type,
"image-width", width,
"image-height", height,
NULL);
if (format)
g_object_set (thumbnail,
"image-type", gimp_babl_format_get_description (format),
NULL);
if (num_layers != -1)
g_object_set (thumbnail,
"image-num-layers", num_layers,
NULL);
}