PIKApp/app/core/pikaimagefile.c

1107 lines
34 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.

/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* pikaimagefile.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 "libpikabase/pikabase.h"
#include "libpikathumb/pikathumb.h"
#include "core-types.h"
#include "config/pikacoreconfig.h"
#include "gegl/pika-babl.h"
#include "gegl/pika-gegl-utils.h"
#include "pika.h"
#include "pikacontainer.h"
#include "pikacontext.h"
#include "pikaimage.h"
#include "pikaimage-metadata.h"
#include "pikaimagefile.h"
#include "pikapickable.h"
#include "pikaprogress.h"
#include "file/file-open.h"
#include "pika-intl.h"
enum
{
INFO_CHANGED,
LAST_SIGNAL
};
typedef struct _PikaImagefilePrivate PikaImagefilePrivate;
struct _PikaImagefilePrivate
{
Pika *pika;
GFile *file;
PikaThumbnail *thumbnail;
GIcon *icon;
GCancellable *icon_cancellable;
gchar *description;
gboolean static_desc;
};
#define GET_PRIVATE(imagefile) ((PikaImagefilePrivate *) pika_imagefile_get_instance_private ((PikaImagefile *) (imagefile)))
static void pika_imagefile_dispose (GObject *object);
static void pika_imagefile_finalize (GObject *object);
static void pika_imagefile_name_changed (PikaObject *object);
static GdkPixbuf * pika_imagefile_get_new_pixbuf (PikaViewable *viewable,
PikaContext *context,
gint width,
gint height);
static gchar * pika_imagefile_get_description (PikaViewable *viewable,
gchar **tooltip);
static void pika_imagefile_info_changed (PikaImagefile *imagefile);
static void pika_imagefile_notify_thumbnail (PikaImagefile *imagefile,
GParamSpec *pspec);
static void pika_imagefile_icon_callback (GObject *source_object,
GAsyncResult *result,
gpointer data);
static GdkPixbuf * pika_imagefile_load_thumb (PikaImagefile *imagefile,
gint width,
gint height);
static gboolean pika_imagefile_save_thumb (PikaImagefile *imagefile,
PikaImage *image,
gint size,
gboolean replace,
GError **error);
static void pika_thumbnail_set_info_from_image (PikaThumbnail *thumbnail,
const gchar *mime_type,
PikaImage *image);
static void pika_thumbnail_set_info (PikaThumbnail *thumbnail,
const gchar *mime_type,
gint width,
gint height,
const Babl *format,
gint num_layers);
G_DEFINE_TYPE_WITH_PRIVATE (PikaImagefile, pika_imagefile, PIKA_TYPE_VIEWABLE)
#define parent_class pika_imagefile_parent_class
static guint pika_imagefile_signals[LAST_SIGNAL] = { 0 };
static void
pika_imagefile_class_init (PikaImagefileClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
PikaObjectClass *pika_object_class = PIKA_OBJECT_CLASS (klass);
PikaViewableClass *viewable_class = PIKA_VIEWABLE_CLASS (klass);
gchar *creator;
pika_imagefile_signals[INFO_CHANGED] =
g_signal_new ("info-changed",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET (PikaImagefileClass, info_changed),
NULL, NULL, NULL,
G_TYPE_NONE, 0);
object_class->dispose = pika_imagefile_dispose;
object_class->finalize = pika_imagefile_finalize;
pika_object_class->name_changed = pika_imagefile_name_changed;
viewable_class->name_changed_signal = "info-changed";
viewable_class->get_new_pixbuf = pika_imagefile_get_new_pixbuf;
viewable_class->get_description = pika_imagefile_get_description;
g_type_class_ref (PIKA_TYPE_IMAGE_TYPE);
creator = g_strdup_printf ("pika-%d.%d",
PIKA_MAJOR_VERSION, PIKA_MINOR_VERSION);
pika_thumb_init (creator, NULL);
g_free (creator);
}
static void
pika_imagefile_init (PikaImagefile *imagefile)
{
PikaImagefilePrivate *private = GET_PRIVATE (imagefile);
private->thumbnail = pika_thumbnail_new ();
g_signal_connect_object (private->thumbnail, "notify",
G_CALLBACK (pika_imagefile_notify_thumbnail),
imagefile, G_CONNECT_SWAPPED);
}
static void
pika_imagefile_dispose (GObject *object)
{
PikaImagefilePrivate *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
pika_imagefile_finalize (GObject *object)
{
PikaImagefilePrivate *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
pika_imagefile_name_changed (PikaObject *object)
{
PikaImagefilePrivate *private = GET_PRIVATE (object);
if (PIKA_OBJECT_CLASS (parent_class)->name_changed)
PIKA_OBJECT_CLASS (parent_class)->name_changed (object);
pika_thumbnail_set_uri (private->thumbnail, pika_object_get_name (object));
g_clear_object (&private->file);
if (pika_object_get_name (object))
private->file = g_file_new_for_uri (pika_object_get_name (object));
}
static GdkPixbuf *
pika_imagefile_get_new_pixbuf (PikaViewable *viewable,
PikaContext *context,
gint width,
gint height)
{
PikaImagefile *imagefile = PIKA_IMAGEFILE (viewable);
if (! pika_object_get_name (imagefile))
return NULL;
return pika_imagefile_load_thumb (imagefile, width, height);
}
static gchar *
pika_imagefile_get_description (PikaViewable *viewable,
gchar **tooltip)
{
PikaImagefile *imagefile = PIKA_IMAGEFILE (viewable);
PikaImagefilePrivate *private = GET_PRIVATE (imagefile);
PikaThumbnail *thumbnail = private->thumbnail;
gchar *basename;
if (! private->file)
return NULL;
if (tooltip)
{
const gchar *name;
const gchar *desc;
name = pika_file_get_utf8_name (private->file);
desc = pika_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 (pika_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 */
PikaImagefile *
pika_imagefile_new (Pika *pika,
GFile *file)
{
PikaImagefile *imagefile;
g_return_val_if_fail (PIKA_IS_PIKA (pika), NULL);
g_return_val_if_fail (file == NULL || G_IS_FILE (file), NULL);
imagefile = g_object_new (PIKA_TYPE_IMAGEFILE, NULL);
GET_PRIVATE (imagefile)->pika = pika;
if (file)
{
pika_object_take_name (PIKA_OBJECT (imagefile), g_file_get_uri (file));
/* file member gets created by pika_imagefile_name_changed() */
}
return imagefile;
}
GFile *
pika_imagefile_get_file (PikaImagefile *imagefile)
{
g_return_val_if_fail (PIKA_IS_IMAGEFILE (imagefile), NULL);
return GET_PRIVATE (imagefile)->file;
}
void
pika_imagefile_set_file (PikaImagefile *imagefile,
GFile *file)
{
g_return_if_fail (PIKA_IS_IMAGEFILE (imagefile));
g_return_if_fail (file == NULL || G_IS_FILE (file));
if (GET_PRIVATE (imagefile)->file != file)
{
pika_object_take_name (PIKA_OBJECT (imagefile),
file ? g_file_get_uri (file) : NULL);
}
}
PikaThumbnail *
pika_imagefile_get_thumbnail (PikaImagefile *imagefile)
{
g_return_val_if_fail (PIKA_IS_IMAGEFILE (imagefile), NULL);
return GET_PRIVATE (imagefile)->thumbnail;
}
GIcon *
pika_imagefile_get_gicon (PikaImagefile *imagefile)
{
PikaImagefilePrivate *private;
g_return_val_if_fail (PIKA_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,
pika_imagefile_icon_callback,
imagefile);
}
return NULL;
}
void
pika_imagefile_set_mime_type (PikaImagefile *imagefile,
const gchar *mime_type)
{
g_return_if_fail (PIKA_IS_IMAGEFILE (imagefile));
g_object_set (GET_PRIVATE (imagefile)->thumbnail,
"image-mimetype", mime_type,
NULL);
}
void
pika_imagefile_update (PikaImagefile *imagefile)
{
PikaImagefilePrivate *private;
gchar *uri;
g_return_if_fail (PIKA_IS_IMAGEFILE (imagefile));
private = GET_PRIVATE (imagefile);
pika_viewable_invalidate_preview (PIKA_VIEWABLE (imagefile));
g_object_get (private->thumbnail,
"image-uri", &uri,
NULL);
if (uri)
{
PikaImagefile *documents_imagefile = (PikaImagefile *)
pika_container_get_child_by_name (private->pika->documents, uri);
if (documents_imagefile != imagefile &&
PIKA_IS_IMAGEFILE (documents_imagefile))
pika_viewable_invalidate_preview (PIKA_VIEWABLE (documents_imagefile));
g_free (uri);
}
}
gboolean
pika_imagefile_create_thumbnail (PikaImagefile *imagefile,
PikaContext *context,
PikaProgress *progress,
gint size,
gboolean replace,
GError **error)
{
PikaImagefilePrivate *private;
PikaThumbnail *thumbnail;
PikaThumbState image_state;
g_return_val_if_fail (PIKA_IS_IMAGEFILE (imagefile), FALSE);
g_return_val_if_fail (PIKA_IS_CONTEXT (context), FALSE);
g_return_val_if_fail (progress == NULL || PIKA_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;
pika_thumbnail_set_uri (thumbnail,
pika_object_get_name (imagefile));
image_state = pika_thumbnail_peek_image (thumbnail);
if (image_state == PIKA_THUMB_STATE_REMOTE ||
image_state >= PIKA_THUMB_STATE_EXISTS)
{
PikaImage *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->pika, context, progress,
private->file, size,
&mime_type, &width, &height,
&format, &num_layers, error);
if (image)
{
pika_thumbnail_set_info (private->thumbnail,
mime_type, width, height,
format, num_layers);
}
else
{
PikaPDBStatusType 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 = pika_image_metadata_load_thumbnail (private->pika, private->file, &width, &height, &format, error);
if (image)
{
pika_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->pika, context, progress,
private->file,
FALSE, NULL, PIKA_RUN_NONINTERACTIVE,
&status, &mime_type, error);
if (image)
pika_thumbnail_set_info_from_image (private->thumbnail,
mime_type, image);
}
}
if (image)
{
success = pika_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.
*/
pika_thumbnail_save_failure (thumbnail,
"PIKA " PIKA_VERSION,
error && *error ? NULL : error);
pika_imagefile_update (imagefile);
success = FALSE;
}
g_object_unref (imagefile);
if (! success)
{
g_object_set (thumbnail,
"thumb-state", PIKA_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
pika_imagefile_create_thumbnail_weak (PikaImagefile *imagefile,
PikaContext *context,
PikaProgress *progress,
gint size,
gboolean replace)
{
PikaImagefilePrivate *private;
PikaImagefile *local;
g_return_if_fail (PIKA_IS_IMAGEFILE (imagefile));
if (size < 1)
return;
private = GET_PRIVATE (imagefile);
if (! private->file)
return;
local = pika_imagefile_new (private->pika, private->file);
g_object_add_weak_pointer (G_OBJECT (imagefile), (gpointer) &imagefile);
if (! pika_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
* PIKA_THUMB_STATE_NOT_FOUND, which is less informative.
*/
g_object_set (private->thumbnail,
"thumb-state", PIKA_THUMB_STATE_FAILED,
NULL);
}
if (imagefile)
{
GFile *file = pika_imagefile_get_file (imagefile);
if (file && g_file_equal (file, pika_imagefile_get_file (local)))
{
pika_imagefile_update (imagefile);
}
g_object_remove_weak_pointer (G_OBJECT (imagefile),
(gpointer) &imagefile);
}
g_object_unref (local);
}
gboolean
pika_imagefile_check_thumbnail (PikaImagefile *imagefile)
{
PikaImagefilePrivate *private;
gint size;
g_return_val_if_fail (PIKA_IS_IMAGEFILE (imagefile), FALSE);
private = GET_PRIVATE (imagefile);
size = private->pika->config->thumbnail_size;
if (size > 0)
{
PikaThumbState state;
state = pika_thumbnail_check_thumb (private->thumbnail, size);
return (state == PIKA_THUMB_STATE_OK);
}
return TRUE;
}
gboolean
pika_imagefile_save_thumbnail (PikaImagefile *imagefile,
const gchar *mime_type,
PikaImage *image,
GError **error)
{
PikaImagefilePrivate *private;
gint size;
gboolean success = TRUE;
g_return_val_if_fail (PIKA_IS_IMAGEFILE (imagefile), FALSE);
g_return_val_if_fail (PIKA_IS_IMAGE (image), FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
private = GET_PRIVATE (imagefile);
size = private->pika->config->thumbnail_size;
if (size > 0)
{
pika_thumbnail_set_info_from_image (private->thumbnail,
mime_type, image);
success = pika_imagefile_save_thumb (imagefile,
image, size, FALSE,
error);
}
return success;
}
/* private functions */
static void
pika_imagefile_info_changed (PikaImagefile *imagefile)
{
PikaImagefilePrivate *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, pika_imagefile_signals[INFO_CHANGED], 0);
}
static void
pika_imagefile_notify_thumbnail (PikaImagefile *imagefile,
GParamSpec *pspec)
{
if (strcmp (pspec->name, "image-state") == 0 ||
strcmp (pspec->name, "thumb-state") == 0)
{
pika_imagefile_info_changed (imagefile);
}
}
static void
pika_imagefile_icon_callback (GObject *source_object,
GAsyncResult *result,
gpointer data)
{
PikaImagefile *imagefile;
PikaImagefilePrivate *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 PIKA_UNSTABLE
g_printerr ("%s: %s\n", G_STRFUNC, error->message);
#endif
g_clear_error (&error);
}
imagefile = PIKA_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)
pika_viewable_invalidate_preview (PIKA_VIEWABLE (imagefile));
}
const gchar *
pika_imagefile_get_desc_string (PikaImagefile *imagefile)
{
PikaImagefilePrivate *private;
PikaThumbnail *thumbnail;
g_return_val_if_fail (PIKA_IS_IMAGEFILE (imagefile), NULL);
private = GET_PRIVATE (imagefile);
if (private->description)
return (const gchar *) private->description;
thumbnail = private->thumbnail;
switch (thumbnail->image_state)
{
case PIKA_THUMB_STATE_UNKNOWN:
private->description = NULL;
private->static_desc = TRUE;
break;
case PIKA_THUMB_STATE_FOLDER:
private->description = (gchar *) _("Folder");
private->static_desc = TRUE;
break;
case PIKA_THUMB_STATE_SPECIAL:
private->description = (gchar *) _("Special File");
private->static_desc = TRUE;
break;
case PIKA_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 == PIKA_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 PIKA_THUMB_STATE_NOT_FOUND:
if (str->len > 0)
g_string_append_c (str, '\n');
g_string_append (str, _("Click to create preview"));
break;
case PIKA_THUMB_STATE_EXISTS:
if (str->len > 0)
g_string_append_c (str, '\n');
g_string_append (str, _("Loading preview..."));
break;
case PIKA_THUMB_STATE_OLD:
if (str->len > 0)
g_string_append_c (str, '\n');
g_string_append (str, _("Preview is out of date"));
break;
case PIKA_THUMB_STATE_FAILED:
if (str->len > 0)
g_string_append_c (str, '\n');
g_string_append (str, _("Cannot create preview"));
break;
case PIKA_THUMB_STATE_OK:
{
if (thumbnail->image_state == PIKA_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 *
pika_imagefile_load_thumb (PikaImagefile *imagefile,
gint width,
gint height)
{
PikaImagefilePrivate *private = GET_PRIVATE (imagefile);
PikaThumbnail *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 (pika_thumbnail_peek_thumb (thumbnail, size) < PIKA_THUMB_STATE_EXISTS)
return NULL;
if (thumbnail->image_state == PIKA_THUMB_STATE_NOT_FOUND)
return NULL;
pixbuf = pika_thumbnail_load_thumb (thumbnail, size, &error);
if (! pixbuf)
{
if (error)
{
pika_message (private->pika, NULL, PIKA_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);
pika_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, PIKA_CHECK_SIZE_SM,
0x66666666, 0x99999999);
g_object_unref (pixbuf);
pixbuf = tmp;
}
return pixbuf;
}
static gboolean
pika_imagefile_save_thumb (PikaImagefile *imagefile,
PikaImage *image,
gint size,
gboolean replace,
GError **error)
{
PikaImagefilePrivate *private = GET_PRIVATE (imagefile);
PikaThumbnail *thumbnail = private->thumbnail;
GdkPixbuf *pixbuf;
gint width, height;
gboolean success = FALSE;
if (size < 1)
return TRUE;
if (pika_image_get_width (image) <= size &&
pika_image_get_height (image) <= size)
{
width = pika_image_get_width (image);
height = pika_image_get_height (image);
size = MAX (width, height);
}
else
{
if (pika_image_get_width (image) < pika_image_get_height (image))
{
height = size;
width = MAX (1, (size * pika_image_get_width (image) /
pika_image_get_height (image)));
}
else
{
width = size;
height = MAX (1, (size * pika_image_get_height (image) /
pika_image_get_width (image)));
}
}
/* we need the projection constructed NOW, not some time later */
pika_pickable_flush (PIKA_PICKABLE (image));
pixbuf = pika_viewable_get_new_pixbuf (PIKA_VIEWABLE (image),
/* random context, unused */
pika_get_user_context (image->pika),
width, height);
/* when layer previews are disabled, we won't get a pixbuf */
if (! pixbuf)
return TRUE;
success = pika_thumbnail_save_thumb (thumbnail,
pixbuf,
"PIKA " PIKA_VERSION,
error);
g_object_unref (pixbuf);
if (success)
{
if (replace)
pika_thumbnail_delete_others (thumbnail, size);
else
pika_thumbnail_delete_failure (thumbnail);
pika_imagefile_update (imagefile);
}
return success;
}
static void
pika_thumbnail_set_info_from_image (PikaThumbnail *thumbnail,
const gchar *mime_type,
PikaImage *image)
{
const Babl *format;
/* peek the thumbnail to make sure that mtime and filesize are set */
pika_thumbnail_peek_image (thumbnail);
format = pika_image_get_layer_format (image,
pika_image_has_alpha (image));
g_object_set (thumbnail,
"image-mimetype", mime_type,
"image-width", pika_image_get_width (image),
"image-height", pika_image_get_height (image),
"image-type", pika_babl_format_get_description (format),
"image-num-layers", pika_image_get_n_layers (image),
NULL);
}
/**
* pika_thumbnail_set_info:
* @thumbnail: #PikaThumbnail 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
pika_thumbnail_set_info (PikaThumbnail *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 */
pika_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", pika_babl_format_get_description (format),
NULL);
if (num_layers != -1)
g_object_set (thumbnail,
"image-num-layers", num_layers,
NULL);
}