diff options
author | Bastien Nocera <hadess@hadess.net> | 2016-06-26 12:18:28 +0200 |
---|---|---|
committer | Bastien Nocera <hadess@hadess.net> | 2016-12-12 16:40:14 +0100 |
commit | b69fde6f4a709f8c4fa3087941cee5b4c4169a8d (patch) | |
tree | 2caeea6c6cd87ccf3ee48983405547b6b5c90a08 | |
parent | 1b96f045e82328d6fd6e0fa4f52165b725fb672d (diff) | |
download | gnome-desktop-b69fde6f4a709f8c4fa3087941cee5b4c4169a8d.tar.gz |
thumbnail: Always use external gdk-pixbuf thumbnailer
Instead of special-casing gdk-pixbuf-supported image formats, use an
external thumbnailer. This will give us the ability to:
- cancel running thumbnail operations
- avoid memory leaks, buffer overflows, double-frees, etc. in the
image loaders having an impact on the application
- limit resource usage when thumbnailing
https://bugzilla.gnome.org/show_bug.cgi?id=768064
-rw-r--r-- | configure.ac | 2 | ||||
-rw-r--r-- | libgnome-desktop/gnome-desktop-thumbnail.c | 351 |
2 files changed, 4 insertions, 349 deletions
diff --git a/configure.ac b/configure.ac index 2f3382d1..a4df4d35 100644 --- a/configure.ac +++ b/configure.ac @@ -103,7 +103,7 @@ GLIB_TESTS dnl If you add a version number here, you *must* add an AC_SUBST line for dnl it too, or it will never make it into the spec file! -GDK_PIXBUF_REQUIRED=2.33.0 +GDK_PIXBUF_REQUIRED=2.36.1 GTK_REQUIRED=3.3.6 GLIB_REQUIRED=2.44.0 XRANDR_REQUIRED=1.3 diff --git a/libgnome-desktop/gnome-desktop-thumbnail.c b/libgnome-desktop/gnome-desktop-thumbnail.c index 39463092..0605a76d 100644 --- a/libgnome-desktop/gnome-desktop-thumbnail.c +++ b/libgnome-desktop/gnome-desktop-thumbnail.c @@ -180,16 +180,6 @@ G_DEFINE_TYPE (GnomeDesktopThumbnailFactory, #define GNOME_DESKTOP_THUMBNAIL_FACTORY_GET_PRIVATE(object) \ (G_TYPE_INSTANCE_GET_PRIVATE ((object), GNOME_DESKTOP_TYPE_THUMBNAIL_FACTORY, GnomeDesktopThumbnailFactoryPrivate)) -typedef struct { - gint width; - gint height; - gint input_width; - gint input_height; - gboolean preserve_aspect_ratio; -} SizePrepareContext; - -#define LOAD_BUFFER_SIZE 4096 - #define THUMBNAILER_ENTRY_GROUP "Thumbnailer Entry" #define THUMBNAILER_EXTENSION ".thumbnailer" @@ -358,229 +348,6 @@ get_thumbnailers_dirs (void) return g_once (&once_init, init_thumbnailers_dirs, NULL); } -static void -size_prepared_cb (GdkPixbufLoader *loader, - int width, - int height, - gpointer data) -{ - SizePrepareContext *info = data; - - g_return_if_fail (width > 0 && height > 0); - - info->input_width = width; - info->input_height = height; - - if (width < info->width && height < info->height) return; - - if (info->preserve_aspect_ratio && - (info->width > 0 || info->height > 0)) { - if (info->width < 0) - { - width = width * (double)info->height/(double)height; - height = info->height; - } - else if (info->height < 0) - { - height = height * (double)info->width/(double)width; - width = info->width; - } - else if ((double)height * (double)info->width > - (double)width * (double)info->height) { - width = 0.5 + (double)width * (double)info->height / (double)height; - height = info->height; - } else { - height = 0.5 + (double)height * (double)info->width / (double)width; - width = info->width; - } - } else { - if (info->width > 0) - width = info->width; - if (info->height > 0) - height = info->height; - } - - gdk_pixbuf_loader_set_size (loader, width, height); -} - -static GdkPixbufLoader * -create_loader (GFile *file, - const guchar *data, - gsize size) -{ - GdkPixbufLoader *loader; - GError *error = NULL; - char *mime_type; - char *filename; - - loader = NULL; - - /* need to specify the type here because the gdk_pixbuf_loader_write - doesn't have access to the filename in order to correct detect - the image type. */ - filename = g_file_get_basename (file); - mime_type = g_content_type_guess (filename, data, size, NULL); - g_free (filename); - - if (mime_type != NULL) { - loader = gdk_pixbuf_loader_new_with_mime_type (mime_type, &error); - } - - if (loader == NULL) { - g_debug ("Unable to create loader for mime type %s: %s", mime_type, error->message); - g_clear_error (&error); - loader = gdk_pixbuf_loader_new (); - } - g_free (mime_type); - - return loader; -} - -static GdkPixbuf * -_gdk_pixbuf_new_from_uri_at_scale (const char *uri, - gint width, - gint height, - gboolean preserve_aspect_ratio) -{ - gboolean result; - guchar buffer[LOAD_BUFFER_SIZE]; - gssize bytes_read; - GdkPixbufLoader *loader = NULL; - GdkPixbuf *pixbuf; - GdkPixbufAnimation *animation; - GdkPixbufAnimationIter *iter; - gboolean has_frame; - SizePrepareContext info; - GFile *file; - GFileInfo *file_info; - GInputStream *input_stream; - GError *error = NULL; - - g_return_val_if_fail (uri != NULL, NULL); - - input_stream = NULL; - - file = g_file_new_for_uri (uri); - - /* First see if we can get an input stream via preview::icon */ - file_info = g_file_query_info (file, - G_FILE_ATTRIBUTE_PREVIEW_ICON, - G_FILE_QUERY_INFO_NONE, - NULL, /* GCancellable */ - NULL); /* return location for GError */ - if (file_info != NULL) { - GObject *object; - - object = g_file_info_get_attribute_object (file_info, - G_FILE_ATTRIBUTE_PREVIEW_ICON); - if (object != NULL && G_IS_LOADABLE_ICON (object)) { - input_stream = g_loadable_icon_load (G_LOADABLE_ICON (object), - 0, /* size */ - NULL, /* return location for type */ - NULL, /* GCancellable */ - NULL); /* return location for GError */ - } - g_object_unref (file_info); - } - - if (input_stream == NULL) { - input_stream = G_INPUT_STREAM (g_file_read (file, NULL, &error)); - if (input_stream == NULL) { - g_warning ("Unable to create an input stream for %s: %s", uri, error->message); - g_clear_error (&error); - g_object_unref (file); - return NULL; - } - } - - has_frame = FALSE; - - result = FALSE; - while (!has_frame) { - - bytes_read = g_input_stream_read (input_stream, - buffer, - sizeof (buffer), - NULL, - &error); - if (bytes_read == -1) { - g_warning ("Error reading from %s: %s", uri, error->message); - g_clear_error (&error); - break; - } - result = TRUE; - if (bytes_read == 0) { - break; - } - - if (loader == NULL) { - loader = create_loader (file, buffer, bytes_read); - if (1 <= width || 1 <= height) { - info.width = width; - info.height = height; - info.input_width = info.input_height = 0; - info.preserve_aspect_ratio = preserve_aspect_ratio; - g_signal_connect (loader, "size-prepared", G_CALLBACK (size_prepared_cb), &info); - } - g_assert (loader != NULL); - } - - if (!gdk_pixbuf_loader_write (loader, - (unsigned char *)buffer, - bytes_read, - &error)) { - g_warning ("Error creating thumbnail for %s: %s", uri, error->message); - g_clear_error (&error); - result = FALSE; - break; - } - - animation = gdk_pixbuf_loader_get_animation (loader); - if (animation) { - iter = gdk_pixbuf_animation_get_iter (animation, NULL); - if (!gdk_pixbuf_animation_iter_on_currently_loading_frame (iter)) { - has_frame = TRUE; - } - g_object_unref (iter); - } - } - - if (loader == NULL) { - /* This can happen if the above loop was exited due to the - * g_input_stream_read() call failing. */ - result = FALSE; - } else if (gdk_pixbuf_loader_close (loader, &error) == FALSE && - !g_error_matches (error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_INCOMPLETE_ANIMATION)) { - g_warning ("Error creating thumbnail for %s: %s", uri, error->message); - result = FALSE; - } - g_clear_error (&error); - - if (!result) { - g_clear_object (&loader); - g_input_stream_close (input_stream, NULL, NULL); - g_object_unref (input_stream); - g_object_unref (file); - return NULL; - } - - g_input_stream_close (input_stream, NULL, NULL); - g_object_unref (input_stream); - g_object_unref (file); - - pixbuf = gdk_pixbuf_loader_get_pixbuf (loader); - if (pixbuf != NULL) { - g_object_ref (G_OBJECT (pixbuf)); - g_object_set_data (G_OBJECT (pixbuf), "gnome-original-width", - GINT_TO_POINTER (info.input_width)); - g_object_set_data (G_OBJECT (pixbuf), "gnome-original-height", - GINT_TO_POINTER (info.input_height)); - } - g_object_unref (G_OBJECT (loader)); - - return pixbuf; -} - /* These should be called with the lock held */ static void gnome_desktop_thumbnail_factory_register_mime_types (GnomeDesktopThumbnailFactory *factory, @@ -638,7 +405,6 @@ remove_thumbnailer_from_mime_type_map (gchar *key, return (strcmp (value->path, path) == 0); } - static void update_or_create_thumbnailer (GnomeDesktopThumbnailFactory *factory, const gchar *path) @@ -1161,54 +927,6 @@ gnome_desktop_thumbnail_factory_has_valid_failed_thumbnail (GnomeDesktopThumbnai return TRUE; } -static gboolean -mimetype_supported_by_gdk_pixbuf (const char *mime_type) -{ - guint i; - static gsize formats_hash = 0; - gchar *key; - gboolean result; - - if (g_once_init_enter (&formats_hash)) { - GSList *formats, *list; - GHashTable *hash; - - hash = g_hash_table_new_full (g_str_hash, - (GEqualFunc) g_content_type_equals, - g_free, NULL); - - formats = gdk_pixbuf_get_formats (); - list = formats; - - while (list) { - GdkPixbufFormat *format = list->data; - gchar **mime_types; - - mime_types = gdk_pixbuf_format_get_mime_types (format); - - for (i = 0; mime_types[i] != NULL; i++) - g_hash_table_insert (hash, - (gpointer) g_content_type_from_mime_type (mime_types[i]), - GUINT_TO_POINTER (1)); - - g_strfreev (mime_types); - list = list->next; - } - g_slist_free (formats); - - g_once_init_leave (&formats_hash, (gsize) hash); - } - - key = g_content_type_from_mime_type (mime_type); - if (g_hash_table_lookup ((void*)formats_hash, key)) - result = TRUE; - else - result = FALSE; - g_free (key); - - return result; -} - /** * gnome_desktop_thumbnail_factory_can_thumbnail: * @factory: a #GnomeDesktopThumbnailFactory @@ -1252,7 +970,7 @@ gnome_desktop_thumbnail_factory_can_thumbnail (GnomeDesktopThumbnailFactory *fac } g_mutex_unlock (&factory->priv->lock); - if (have_script || mimetype_supported_by_gdk_pixbuf (mime_type)) + if (have_script) { return !gnome_desktop_thumbnail_factory_has_valid_failed_thumbnail (factory, uri, @@ -1351,13 +1069,9 @@ gnome_desktop_thumbnail_factory_generate_thumbnail (GnomeDesktopThumbnailFactory const char *uri, const char *mime_type) { - GdkPixbuf *pixbuf, *scaled, *tmp_pixbuf; + GdkPixbuf *pixbuf; char *script, *expanded_script; - int width, height, size; - int original_width = 0; - int original_height = 0; - char dimension[12]; - double scale; + int size; int exit_status; char *tmpname; @@ -1411,65 +1125,6 @@ gnome_desktop_thumbnail_factory_generate_thumbnail (GnomeDesktopThumbnailFactory g_free (script); } - /* Fall back to gdk-pixbuf */ - if (pixbuf == NULL) - { - pixbuf = _gdk_pixbuf_new_from_uri_at_scale (uri, size, size, TRUE); - - if (pixbuf != NULL) - { - original_width = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (pixbuf), - "gnome-original-width")); - original_height = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (pixbuf), - "gnome-original-height")); - } - } - - if (pixbuf == NULL) - return NULL; - - /* The pixbuf loader may attach an "orientation" option to the pixbuf, - if the tiff or exif jpeg file had an orientation tag. Rotate/flip - the pixbuf as specified by this tag, if present. */ - tmp_pixbuf = gdk_pixbuf_apply_embedded_orientation (pixbuf); - g_object_unref (pixbuf); - pixbuf = tmp_pixbuf; - - width = gdk_pixbuf_get_width (pixbuf); - height = gdk_pixbuf_get_height (pixbuf); - - if (width > size || height > size) - { - const gchar *orig_width, *orig_height; - scale = (double)size / MAX (width, height); - - scaled = gnome_desktop_thumbnail_scale_down_pixbuf (pixbuf, - floor (width * scale + 0.5), - floor (height * scale + 0.5)); - - orig_width = gdk_pixbuf_get_option (pixbuf, "tEXt::Thumb::Image::Width"); - orig_height = gdk_pixbuf_get_option (pixbuf, "tEXt::Thumb::Image::Height"); - - if (orig_width != NULL) { - gdk_pixbuf_set_option (scaled, "tEXt::Thumb::Image::Width", orig_width); - } - if (orig_height != NULL) { - gdk_pixbuf_set_option (scaled, "tEXt::Thumb::Image::Height", orig_height); - } - - g_object_unref (pixbuf); - pixbuf = scaled; - } - - if (original_width > 0) { - g_snprintf (dimension, sizeof (dimension), "%i", original_width); - gdk_pixbuf_set_option (pixbuf, "tEXt::Thumb::Image::Width", dimension); - } - if (original_height > 0) { - g_snprintf (dimension, sizeof (dimension), "%i", original_height); - gdk_pixbuf_set_option (pixbuf, "tEXt::Thumb::Image::Height", dimension); - } - return pixbuf; } |