|
Packit |
a4058c |
/*
|
|
Packit |
a4058c |
* Copyright (C) 2016 Bastien Nocera <hadess@hadess.net>
|
|
Packit |
a4058c |
*
|
|
Packit |
a4058c |
* Authors: Bastien Nocera <hadess@hadess.net>
|
|
Packit |
a4058c |
*
|
|
Packit |
a4058c |
* This program is free software; you can redistribute it and/or modify
|
|
Packit |
a4058c |
* it under the terms of the GNU General Public License as published by
|
|
Packit |
a4058c |
* the Free Software Foundation; either version 2 of the License, or
|
|
Packit |
a4058c |
* (at your option) any later version.
|
|
Packit |
a4058c |
*
|
|
Packit |
a4058c |
* This program is distributed in the hope that it will be useful,
|
|
Packit |
a4058c |
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit |
a4058c |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
Packit |
a4058c |
* GNU General Public License for more details.
|
|
Packit |
a4058c |
*
|
|
Packit |
a4058c |
* You should have received a copy of the GNU General Public License
|
|
Packit |
a4058c |
* along with this program; if not, write to the Free Software
|
|
Packit |
a4058c |
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
Packit |
a4058c |
*
|
|
Packit |
a4058c |
*/
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
#include <string.h>
|
|
Packit |
a4058c |
#include <glib.h>
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
#include "gnome-thumbnailer-skeleton.h"
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
typedef struct {
|
|
Packit |
a4058c |
gint size;
|
|
Packit |
a4058c |
gint input_width;
|
|
Packit |
a4058c |
gint input_height;
|
|
Packit |
a4058c |
} SizePrepareContext;
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
#define LOAD_BUFFER_SIZE 65536
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
static void
|
|
Packit |
a4058c |
size_prepared_cb (GdkPixbufLoader *loader,
|
|
Packit |
a4058c |
int width,
|
|
Packit |
a4058c |
int height,
|
|
Packit |
a4058c |
gpointer data)
|
|
Packit |
a4058c |
{
|
|
Packit |
a4058c |
SizePrepareContext *info = data;
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
g_return_if_fail (width > 0 && height > 0);
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
info->input_width = width;
|
|
Packit |
a4058c |
info->input_height = height;
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
if (width < info->size && height < info->size) return;
|
|
Packit |
a4058c |
if (info->size <= 0) return;
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
if (height > width) {
|
|
Packit |
a4058c |
width = 0.5 + (double)width * (double)info->size / (double)height;
|
|
Packit |
a4058c |
height = info->size;
|
|
Packit |
a4058c |
} else {
|
|
Packit |
a4058c |
height = 0.5 + (double)height * (double)info->size / (double)width;
|
|
Packit |
a4058c |
width = info->size;
|
|
Packit |
a4058c |
}
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
gdk_pixbuf_loader_set_size (loader, width, height);
|
|
Packit |
a4058c |
}
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
static GdkPixbufLoader *
|
|
Packit |
a4058c |
create_loader (GFile *file,
|
|
Packit |
a4058c |
const guchar *data,
|
|
Packit |
a4058c |
gsize size)
|
|
Packit |
a4058c |
{
|
|
Packit |
a4058c |
GdkPixbufLoader *loader;
|
|
Packit |
a4058c |
GError *error = NULL;
|
|
Packit |
a4058c |
char *mime_type;
|
|
Packit |
a4058c |
char *filename;
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
loader = NULL;
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
/* need to specify the type here because the gdk_pixbuf_loader_write
|
|
Packit |
a4058c |
doesn't have access to the filename in order to correct detect
|
|
Packit |
a4058c |
the image type. */
|
|
Packit |
a4058c |
filename = g_file_get_basename (file);
|
|
Packit |
a4058c |
mime_type = g_content_type_guess (filename, data, size, NULL);
|
|
Packit |
a4058c |
g_free (filename);
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
if (mime_type != NULL) {
|
|
Packit |
a4058c |
loader = gdk_pixbuf_loader_new_with_mime_type (mime_type, &error);
|
|
Packit |
a4058c |
}
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
if (loader == NULL) {
|
|
Packit |
a4058c |
g_debug ("Unable to create loader for mime type %s: %s", mime_type,
|
|
Packit |
a4058c |
(error != NULL) ? error->message : "(null)");
|
|
Packit |
a4058c |
g_clear_error (&error);
|
|
Packit |
a4058c |
loader = gdk_pixbuf_loader_new ();
|
|
Packit |
a4058c |
}
|
|
Packit |
a4058c |
g_free (mime_type);
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
return loader;
|
|
Packit |
a4058c |
}
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
static GdkPixbuf *
|
|
Packit |
a4058c |
_gdk_pixbuf_new_from_uri_at_scale (const char *uri,
|
|
Packit |
a4058c |
gint size,
|
|
Packit |
a4058c |
GError **error)
|
|
Packit |
a4058c |
{
|
|
Packit |
a4058c |
gboolean result;
|
|
Packit |
a4058c |
guchar buffer[LOAD_BUFFER_SIZE];
|
|
Packit |
a4058c |
gssize bytes_read;
|
|
Packit |
a4058c |
GdkPixbufLoader *loader = NULL;
|
|
Packit |
a4058c |
GdkPixbuf *pixbuf;
|
|
Packit |
a4058c |
GdkPixbufAnimation *animation;
|
|
Packit |
a4058c |
GdkPixbufAnimationIter *iter;
|
|
Packit |
a4058c |
gboolean has_frame;
|
|
Packit |
a4058c |
SizePrepareContext info;
|
|
Packit |
a4058c |
GFile *file;
|
|
Packit |
a4058c |
GInputStream *input_stream;
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
g_return_val_if_fail (uri != NULL, NULL);
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
file = g_file_new_for_uri (uri);
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
input_stream = G_INPUT_STREAM (g_file_read (file, NULL, error));
|
|
Packit |
a4058c |
if (input_stream == NULL) {
|
|
Packit |
a4058c |
g_object_unref (file);
|
|
Packit |
a4058c |
return NULL;
|
|
Packit |
a4058c |
}
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
has_frame = FALSE;
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
result = FALSE;
|
|
Packit |
a4058c |
while (!has_frame) {
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
bytes_read = g_input_stream_read (input_stream,
|
|
Packit |
a4058c |
buffer,
|
|
Packit |
a4058c |
sizeof (buffer),
|
|
Packit |
a4058c |
NULL,
|
|
Packit |
a4058c |
error);
|
|
Packit |
a4058c |
if (bytes_read == -1) {
|
|
Packit |
a4058c |
break;
|
|
Packit |
a4058c |
}
|
|
Packit |
a4058c |
result = TRUE;
|
|
Packit |
a4058c |
if (bytes_read == 0) {
|
|
Packit |
a4058c |
break;
|
|
Packit |
a4058c |
}
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
if (loader == NULL) {
|
|
Packit |
a4058c |
loader = create_loader (file, buffer, bytes_read);
|
|
Packit |
a4058c |
if (1 <= size) {
|
|
Packit |
a4058c |
info.size = size;
|
|
Packit |
a4058c |
info.input_width = info.input_height = 0;
|
|
Packit |
a4058c |
g_signal_connect (loader, "size-prepared", G_CALLBACK (size_prepared_cb), &info;;
|
|
Packit |
a4058c |
}
|
|
Packit |
a4058c |
g_assert (loader != NULL);
|
|
Packit |
a4058c |
}
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
if (!gdk_pixbuf_loader_write (loader,
|
|
Packit |
a4058c |
(unsigned char *)buffer,
|
|
Packit |
a4058c |
bytes_read,
|
|
Packit |
a4058c |
error)) {
|
|
Packit |
a4058c |
result = FALSE;
|
|
Packit |
a4058c |
break;
|
|
Packit |
a4058c |
}
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
animation = gdk_pixbuf_loader_get_animation (loader);
|
|
Packit |
a4058c |
if (animation) {
|
|
Packit |
a4058c |
iter = gdk_pixbuf_animation_get_iter (animation, NULL);
|
|
Packit |
a4058c |
if (!gdk_pixbuf_animation_iter_on_currently_loading_frame (iter)) {
|
|
Packit |
a4058c |
has_frame = TRUE;
|
|
Packit |
a4058c |
}
|
|
Packit |
a4058c |
g_object_unref (iter);
|
|
Packit |
a4058c |
}
|
|
Packit |
a4058c |
}
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
if (loader == NULL) {
|
|
Packit |
a4058c |
/* This can happen if the above loop was exited due to the
|
|
Packit |
a4058c |
* g_input_stream_read() call failing. */
|
|
Packit |
a4058c |
result = FALSE;
|
|
Packit |
a4058c |
} else if (*error != NULL) {
|
|
Packit |
a4058c |
gdk_pixbuf_loader_close (loader, NULL);
|
|
Packit |
a4058c |
result = FALSE;
|
|
Packit |
a4058c |
} else if (gdk_pixbuf_loader_close (loader, error) == FALSE) {
|
|
Packit |
a4058c |
if (!g_error_matches (*error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_INCOMPLETE_ANIMATION))
|
|
Packit |
a4058c |
result = FALSE;
|
|
Packit |
a4058c |
else
|
|
Packit |
a4058c |
g_clear_error (error);
|
|
Packit |
a4058c |
}
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
if (!result) {
|
|
Packit |
a4058c |
g_clear_object (&loader);
|
|
Packit |
a4058c |
g_input_stream_close (input_stream, NULL, NULL);
|
|
Packit |
a4058c |
g_object_unref (input_stream);
|
|
Packit |
a4058c |
g_object_unref (file);
|
|
Packit |
a4058c |
return NULL;
|
|
Packit |
a4058c |
}
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
g_input_stream_close (input_stream, NULL, NULL);
|
|
Packit |
a4058c |
g_object_unref (input_stream);
|
|
Packit |
a4058c |
g_object_unref (file);
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
pixbuf = gdk_pixbuf_loader_get_pixbuf (loader);
|
|
Packit |
a4058c |
if (pixbuf != NULL) {
|
|
Packit |
a4058c |
g_object_ref (G_OBJECT (pixbuf));
|
|
Packit |
a4058c |
g_object_set_data (G_OBJECT (pixbuf), "gnome-original-width",
|
|
Packit |
a4058c |
GINT_TO_POINTER (info.input_width));
|
|
Packit |
a4058c |
g_object_set_data (G_OBJECT (pixbuf), "gnome-original-height",
|
|
Packit |
a4058c |
GINT_TO_POINTER (info.input_height));
|
|
Packit |
a4058c |
}
|
|
Packit |
a4058c |
g_object_unref (G_OBJECT (loader));
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
return pixbuf;
|
|
Packit |
a4058c |
}
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
GdkPixbuf *
|
|
Packit |
a4058c |
file_to_pixbuf (const char *path,
|
|
Packit |
a4058c |
guint destination_size,
|
|
Packit |
a4058c |
GError **error)
|
|
Packit |
a4058c |
{
|
|
Packit |
a4058c |
GdkPixbuf *pixbuf, *tmp_pixbuf;
|
|
Packit |
a4058c |
GFile *file;
|
|
Packit |
a4058c |
char *uri;
|
|
Packit |
a4058c |
int original_width, original_height;
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
file = g_file_new_for_path (path);
|
|
Packit |
a4058c |
uri = g_file_get_uri (file);
|
|
Packit |
a4058c |
pixbuf = _gdk_pixbuf_new_from_uri_at_scale (uri, destination_size, error);
|
|
Packit |
a4058c |
if (pixbuf == NULL)
|
|
Packit |
a4058c |
return NULL;
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
tmp_pixbuf = gdk_pixbuf_apply_embedded_orientation (pixbuf);
|
|
Packit |
a4058c |
gdk_pixbuf_copy_options (pixbuf, tmp_pixbuf);
|
|
Packit |
a4058c |
gdk_pixbuf_remove_option (tmp_pixbuf, "orientation");
|
|
Packit |
a4058c |
g_object_unref (pixbuf);
|
|
Packit |
a4058c |
pixbuf = tmp_pixbuf;
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
original_width = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (pixbuf),
|
|
Packit |
a4058c |
"gnome-original-width"));
|
|
Packit |
a4058c |
original_height = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (pixbuf),
|
|
Packit |
a4058c |
"gnome-original-height"));
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
if (original_width > 0 && original_height > 0) {
|
|
Packit |
a4058c |
char *tmp;
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
tmp = g_strdup_printf ("%d", original_width);
|
|
Packit |
a4058c |
gdk_pixbuf_set_option (pixbuf, "tEXt::Thumb::Image::Width", tmp);
|
|
Packit |
a4058c |
g_free (tmp);
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
tmp = g_strdup_printf ("%d", original_height);
|
|
Packit |
a4058c |
gdk_pixbuf_set_option (pixbuf, "tEXt::Thumb::Image::Height", tmp);
|
|
Packit |
a4058c |
g_free (tmp);
|
|
Packit |
a4058c |
}
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
return pixbuf;
|
|
Packit |
a4058c |
}
|