Blame thumbnailer/gdk-pixbuf-thumbnailer.c

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
}