[frogr] Optimized usage of memory handling pixbufs internally.



commit 3a07cbc8a38629179670400bb595e78118332fb5
Author: Mario Sanchez Prada <msanchez igalia com>
Date:   Fri Jul 8 19:02:10 2011 +0200

    Optimized usage of memory handling pixbufs internally.
    
    So far (well, since the last commit), frogr loaded pixbufs from disk at
    the maximum resolution to be used (200x200) and then stored it to be
    used it both in the details window and the icon view (scaled down). This
    caused a terrible waste of memory because the icon view shows quite
    smaller thumbnails (140x140) and that meant that we would be storing
    in memory pixbufs bigger (60x60 bigger) than what we currently needed
    for most of the time just in case we wanted to display a picture in the
    details window *individually*.
    
    This patch makes frogr to store in memory the smallest pixbuf possible
    (140x140, for the icon view) and retrieves the bigger pixbufs needed for
    the details dialog in an on-demmand basis, saving quite a lot of memory.
    
    Some informal tests with and without this patch shows that, for 640
    high-res JPG pictures sizing 2-3MB each:
    
      - Without this patch: used ~120MB of memory (after finished loading)
      - With this patch: used ~60MB of memory
    
    So the improvement is quite big, I'd say

 src/frogr-details-dialog.c |  187 ++++++++++++++++++++++++++++++++++----------
 src/frogr-global-defs.h    |    9 ++
 src/frogr-main-view.c      |   10 +--
 src/frogr-picture-loader.c |   68 +----------------
 src/frogr-util.c           |   39 +++++++++-
 src/frogr-util.h           |    2 +-
 6 files changed, 198 insertions(+), 117 deletions(-)
---
diff --git a/src/frogr-details-dialog.c b/src/frogr-details-dialog.c
index a9c38c8..2c29d54 100644
--- a/src/frogr-details-dialog.c
+++ b/src/frogr-details-dialog.c
@@ -67,6 +67,7 @@ typedef struct _FrogrDetailsDialogPrivate {
   GtkWidget *picture_button;
   GtkWidget *picture_container;
   GtkWidget *mpictures_label;
+  GdkPixbuf *mpictures_pixbuf;
 
   GtkTreeModel *treemodel;
   GSList *pictures;
@@ -101,6 +102,14 @@ static gboolean _completion_match_selected_cb (GtkEntryCompletion *widget, GtkTr
 
 static void _update_ui (FrogrDetailsDialog *self);
 
+static void _load_picture_from_disk (FrogrDetailsDialog *self);
+
+static void _load_picture_from_disk_cb (GObject *object,
+                                        GAsyncResult *res,
+                                        gpointer data);
+
+static void _place_picture_in_dialog_and_show (FrogrDetailsDialog *self);
+
 static void _fill_dialog_with_data (FrogrDetailsDialog *self);
 
 static gboolean _validate_dialog_data (FrogrDetailsDialog *self);
@@ -171,6 +180,7 @@ _create_widgets (FrogrDetailsDialog *self)
   widget = gtk_label_new (NULL);
   gtk_box_pack_start (GTK_BOX (vbox), widget, FALSE, FALSE, 0);
   priv->mpictures_label = widget;
+  priv->mpictures_pixbuf = NULL;
 
   /* Visibility */
 
@@ -570,6 +580,133 @@ _update_ui (FrogrDetailsDialog *self)
 }
 
 static void
+_load_picture_from_disk (FrogrDetailsDialog *self)
+{
+  FrogrDetailsDialogPrivate *priv = FROGR_DETAILS_DIALOG_GET_PRIVATE (self);
+  guint n_pictures;
+
+  n_pictures = g_slist_length (priv->pictures);
+  if (n_pictures > 1)
+    {
+      gchar *mpictures_str = NULL;
+
+      /* Get the 'multiple pictures pixbuf' if not got yet (lazy approach) */
+      if (!priv->mpictures_pixbuf)
+        {
+          gchar *mpictures_full_path = NULL;
+
+          /* Set the image for editing multiple pictures */
+          mpictures_full_path = g_strdup_printf ("%s/" MPICTURES_IMAGE,
+                                                 frogr_util_get_app_data_dir ());
+          priv->mpictures_pixbuf = gdk_pixbuf_new_from_file (mpictures_full_path, NULL);
+          g_free (mpictures_full_path);
+        }
+
+      /* Just set the pixbuf in the image */
+      gtk_image_set_from_pixbuf (GTK_IMAGE (priv->picture_img), priv->mpictures_pixbuf);
+
+      /* Visually indicate how many pictures are being edited */
+      mpictures_str = g_strdup_printf (ngettext ("(%d Picture)", "(%d Pictures)", n_pictures), n_pictures);
+      gtk_label_set_text (GTK_LABEL (priv->mpictures_label), mpictures_str);
+      g_free (mpictures_str);
+
+      /* No need to spawn any async operation, show the dialog now */
+      _place_picture_in_dialog_and_show (self);
+    }
+  else
+    {
+      FrogrPicture *picture = NULL;
+      gchar *file_uri = NULL;
+      GFile *gfile = NULL;
+
+      picture = FROGR_PICTURE (priv->pictures->data);
+      file_uri = (gchar *)frogr_picture_get_fileuri (picture);
+      gfile = g_file_new_for_uri (file_uri);
+
+      /* Asynchronously load the picture */
+      g_file_load_contents_async (gfile, NULL, _load_picture_from_disk_cb, self);
+    }
+}
+
+static void
+_load_picture_from_disk_cb (GObject *object,
+                            GAsyncResult *res,
+                            gpointer data)
+{
+  FrogrDetailsDialog *self = FROGR_DETAILS_DIALOG (data);
+  FrogrDetailsDialogPrivate *priv = FROGR_DETAILS_DIALOG_GET_PRIVATE (self);
+  GFile *file = NULL;
+  GError *error = NULL;
+  gchar *contents = NULL;
+  gsize length = 0;
+
+  file = G_FILE (object);
+  if (g_file_load_contents_finish (file, res, &contents, &length, NULL, &error))
+    {
+      GdkPixbufLoader *pixbuf_loader = gdk_pixbuf_loader_new ();
+
+      if (gdk_pixbuf_loader_write (pixbuf_loader,
+                                   (const guchar *)contents,
+                                   length,
+                                   &error))
+        {
+          GdkPixbuf *pixbuf = NULL;
+          GdkPixbuf *s_pixbuf = NULL;
+
+          gdk_pixbuf_loader_close (pixbuf_loader, NULL);
+          pixbuf = gdk_pixbuf_loader_get_pixbuf (pixbuf_loader);
+
+          /* Get (scaled, and maybe rotated) pixbuf */
+          s_pixbuf = frogr_util_get_corrected_pixbuf (pixbuf, PICTURE_WIDTH, PICTURE_HEIGHT);
+
+          gtk_image_set_from_pixbuf (GTK_IMAGE (priv->picture_img), s_pixbuf);
+          g_object_unref (s_pixbuf);
+
+          /* Everything should be fine by now, show it */
+          _place_picture_in_dialog_and_show (self);
+        }
+    }
+  else
+    {
+      GtkWindow *parent_window = NULL;
+      gchar *error_msg = NULL;
+
+      parent_window = gtk_window_get_transient_for (GTK_WINDOW (self));
+      gtk_widget_destroy (GTK_WIDGET (self));
+
+      if (error)
+        {
+          error_msg = g_strdup (error->message);
+          g_error_free (error);
+        }
+      else
+        error_msg = g_strdup (_("An error happened trying to load the picture"));
+
+      frogr_util_show_error_dialog (parent_window, error_msg);
+      g_free (error_msg);
+    }
+}
+
+static void
+_place_picture_in_dialog_and_show (FrogrDetailsDialog *self)
+{
+  FrogrDetailsDialogPrivate *priv = FROGR_DETAILS_DIALOG_GET_PRIVATE (self);
+
+  gtk_button_set_image (GTK_BUTTON (priv->picture_button),
+                        priv->picture_img);
+
+  gtk_container_add (GTK_CONTAINER (priv->picture_container),
+                     priv->picture_button);
+
+  priv->picture_button_handler_id =
+    g_signal_connect (G_OBJECT (priv->picture_button), "clicked",
+                      G_CALLBACK (_on_picture_button_clicked),
+                      self);
+
+  gtk_widget_show_all (GTK_WIDGET (self));
+}
+
+static void
 _fill_dialog_with_data (FrogrDetailsDialog *self)
 {
   FrogrDetailsDialogPrivate *priv =
@@ -578,7 +715,6 @@ _fill_dialog_with_data (FrogrDetailsDialog *self)
   FrogrPicture *picture;
   GSList *item;
   GtkWidget *picture_widget;
-  guint n_pictures;
   gchar *title_val = NULL;
   gchar *desc_val = NULL;
   gchar *tags_val = NULL;
@@ -786,50 +922,15 @@ _fill_dialog_with_data (FrogrDetailsDialog *self)
                                    priv->picture_button_handler_id);
     }
 
-  n_pictures = g_slist_length (priv->pictures);
-  if (n_pictures > 1)
-    {
-      GdkPixbuf *pixbuf = NULL;
-      gchar *mpictures_str = NULL;
-      gchar *mpictures_full_path = NULL;
-
-      /* Set the image for editing multiple pictures */
-      mpictures_full_path = g_strdup_printf ("%s/" MPICTURES_IMAGE,
-					     frogr_util_get_app_data_dir ());
-      pixbuf = gdk_pixbuf_new_from_file (mpictures_full_path, NULL);
-      gtk_image_set_from_pixbuf (GTK_IMAGE (priv->picture_img), pixbuf);
-      g_object_unref (pixbuf);
-      g_free (mpictures_full_path);
-
-      /* Visually indicate how many pictures are being edited */
-      mpictures_str = g_strdup_printf (ngettext ("(%d Picture)", "(%d Pictures)", n_pictures), n_pictures);
-      gtk_label_set_text (GTK_LABEL (priv->mpictures_label), mpictures_str);
-      g_free (mpictures_str);
-    }
-  else
-    {
-      /* Set pixbuf scaled to the right size */
-      GdkPixbuf *pixbuf = frogr_picture_get_pixbuf (picture);
-      GdkPixbuf *s_pixbuf = frogr_util_get_scaled_pixbuf (pixbuf, PICTURE_WIDTH, PICTURE_HEIGHT);
-      gtk_image_set_from_pixbuf (GTK_IMAGE (priv->picture_img), s_pixbuf);
-      g_object_unref (s_pixbuf);
-    }
-
-  gtk_button_set_image (GTK_BUTTON (priv->picture_button),
-                        priv->picture_img);
-
-  gtk_container_add (GTK_CONTAINER (priv->picture_container),
-                     priv->picture_button);
-  priv->picture_button_handler_id =
-    g_signal_connect (G_OBJECT (priv->picture_button), "clicked",
-                      G_CALLBACK (_on_picture_button_clicked),
-                      self);
   /* Update UI */
   _update_ui (self);
 
   /* Initial widget to grab focus */
   gtk_widget_grab_focus (priv->title_entry);
   gtk_editable_set_position (GTK_EDITABLE (priv->title_entry), -1);
+
+  /* Load the picture from disk, asynchronously */
+  _load_picture_from_disk (self);
 }
 
 static gboolean
@@ -1093,6 +1194,12 @@ _frogr_details_dialog_dispose (GObject *object)
 {
   FrogrDetailsDialogPrivate *priv = FROGR_DETAILS_DIALOG_GET_PRIVATE (object);
 
+  if (priv->mpictures_pixbuf)
+    {
+      g_object_unref (priv->mpictures_pixbuf);
+      priv->mpictures_pixbuf = NULL;
+    }
+
   if (priv->pictures)
     {
       g_slist_foreach (priv->pictures, (GFunc)g_object_unref, NULL);
@@ -1192,6 +1299,4 @@ frogr_details_dialog_show (GtkWindow *parent, GSList *fpictures, GSList *tags)
 
   _fill_dialog_with_data (self);
   _populate_treemodel_with_tags (self, tags);
-
-  gtk_widget_show_all (GTK_WIDGET (self));
 }
diff --git a/src/frogr-global-defs.h b/src/frogr-global-defs.h
index 690551d..0386fe9 100644
--- a/src/frogr-global-defs.h
+++ b/src/frogr-global-defs.h
@@ -27,6 +27,15 @@
 #define APP_SHORTNAME PACKAGE
 #define APP_VERSION VERSION
 
+/* Max width and heigth for pictures in the icon view.
+ *
+ * These values will impact directly memory comsumption because it
+ * will be the size of the pixbuf that will remain in memory during
+ * the whole life of every FrogrPicture object.
+ */
+#define IV_THUMB_WIDTH 140
+#define IV_THUMB_HEIGHT 140
+
 #if DEBUG_ENABLED
 #define DEBUG(...) g_debug (__VA_ARGS__);
 #else
diff --git a/src/frogr-main-view.c b/src/frogr-main-view.c
index 2cd2ed1..048c968 100644
--- a/src/frogr-main-view.c
+++ b/src/frogr-main-view.c
@@ -50,9 +50,6 @@
 #define MINIMUM_WINDOW_WIDTH 840
 #define MINIMUM_WINDOW_HEIGHT 600
 
-#define ITEM_WIDTH 140
-#define ITEM_HEIGHT 140
-
 /* Path relative to the application data dir */
 #define GTKBUILDER_FILE "/gtkbuilder/frogr-main-view.xml"
 
@@ -772,24 +769,21 @@ _add_picture_to_ui (FrogrMainView *self, FrogrPicture *picture)
 {
   FrogrMainViewPrivate *priv = FROGR_MAIN_VIEW_GET_PRIVATE (self);
   GdkPixbuf *pixbuf = NULL;
-  GdkPixbuf *s_pixbuf = NULL;
   const gchar *fileuri = NULL;
   GtkTreeIter iter;
 
   /* Add to GtkIconView */
   fileuri = frogr_picture_get_fileuri (picture);
   pixbuf = frogr_picture_get_pixbuf (picture);
-  s_pixbuf = frogr_util_get_scaled_pixbuf (pixbuf, ITEM_WIDTH, ITEM_HEIGHT);
 
   gtk_list_store_append (GTK_LIST_STORE (priv->tree_model), &iter);
   gtk_list_store_set (GTK_LIST_STORE (priv->tree_model), &iter,
                       FILEURI_COL, fileuri,
-                      PIXBUF_COL, s_pixbuf,
+                      PIXBUF_COL, pixbuf,
                       FPICTURE_COL, picture,
                       -1);
 
   g_object_ref (picture);
-  g_object_unref (s_pixbuf);
 
   /* Reorder if needed */
   if (priv->sorting_criteria != SORT_AS_LOADED || priv->sorting_reversed)
@@ -1763,7 +1757,7 @@ frogr_main_view_init (FrogrMainView *self)
   gtk_icon_view_set_selection_mode (GTK_ICON_VIEW (icon_view),
                                     GTK_SELECTION_MULTIPLE);
   gtk_icon_view_set_columns (GTK_ICON_VIEW (icon_view), -1);
-  gtk_icon_view_set_item_width (GTK_ICON_VIEW (icon_view), ITEM_WIDTH);
+  gtk_icon_view_set_item_width (GTK_ICON_VIEW (icon_view), IV_THUMB_WIDTH);
   gtk_widget_set_has_tooltip (icon_view, TRUE);
 
   gtk_window_set_default_size (priv->window, MINIMUM_WINDOW_WIDTH, MINIMUM_WINDOW_HEIGHT);
diff --git a/src/frogr-picture-loader.c b/src/frogr-picture-loader.c
index 06e499e..600af57 100644
--- a/src/frogr-picture-loader.c
+++ b/src/frogr-picture-loader.c
@@ -25,6 +25,7 @@
 #include "frogr-global-defs.h"
 #include "frogr-main-view.h"
 #include "frogr-picture.h"
+#include "frogr-util.h"
 
 #include <config.h>
 #include <libexif/exif-byte-order.h>
@@ -36,9 +37,6 @@
 #include <glib/gi18n.h>
 #include <gio/gio.h>
 
-#define PICTURE_WIDTH 200
-#define PICTURE_HEIGHT 200
-
 #define FROGR_PICTURE_LOADER_GET_PRIVATE(object)                \
   (G_TYPE_INSTANCE_GET_PRIVATE ((object),                       \
                                 FROGR_TYPE_PICTURE_LOADER,      \
@@ -86,7 +84,6 @@ static const gchar *valid_mimetypes[] = {
 /* Prototypes */
 
 static void _update_status_and_progress (FrogrPictureLoader *self);
-static GdkPixbuf *_get_corrected_pixbuf (GdkPixbuf *pixbuf);
 static void _load_next_picture (FrogrPictureLoader *self);
 static void _load_next_picture_cb (GObject *object,
                                    GAsyncResult *res,
@@ -113,67 +110,6 @@ _update_status_and_progress (FrogrPictureLoader *self)
   g_free (status_text);
 }
 
-static GdkPixbuf *
-_get_corrected_pixbuf (GdkPixbuf *pixbuf)
-{
-  GdkPixbuf *scaled_pixbuf;
-  GdkPixbuf *rotated_pixbuf;
-  const gchar *orientation;
-  gint width;
-  gint height;
-  gint new_width;
-  gint new_height;
-
-  /* Look for the right side to reduce */
-  width = gdk_pixbuf_get_width (pixbuf);
-  height = gdk_pixbuf_get_height (pixbuf);
-  if (width > height)
-    {
-      new_width = PICTURE_WIDTH;
-      new_height = (float)new_width * height / width;
-    }
-  else
-    {
-      new_height = PICTURE_HEIGHT;
-      new_width = (float)new_height * width / height;
-    }
-
-  /* Scale the pixbuf to its best size */
-  scaled_pixbuf = gdk_pixbuf_scale_simple (pixbuf,
-                                           new_width, new_height,
-                                           GDK_INTERP_TILES);
-  /* Correct orientation if needed */
-  orientation = gdk_pixbuf_get_option (pixbuf, "orientation");
-
-  /* No orientation defined or 0 degrees rotation: we're done */
-  if (!orientation || !g_strcmp0 (orientation, "1"))
-    return scaled_pixbuf;
-
-  DEBUG ("File orientation for file: %s", orientation);
-  rotated_pixbuf = NULL;
-
-  /* Rotated 90 degrees */
-  if (!g_strcmp0 (orientation, "8"))
-    rotated_pixbuf = gdk_pixbuf_rotate_simple (scaled_pixbuf,
-                                               GDK_PIXBUF_ROTATE_COUNTERCLOCKWISE);
-  /* Rotated 180 degrees */
-  if (!g_strcmp0 (orientation, "3"))
-    rotated_pixbuf = gdk_pixbuf_rotate_simple (scaled_pixbuf,
-                                               GDK_PIXBUF_ROTATE_UPSIDEDOWN);
-  /* Rotated 270 degrees */
-  if (!g_strcmp0 (orientation, "6"))
-    rotated_pixbuf = gdk_pixbuf_rotate_simple (scaled_pixbuf,
-                                               GDK_PIXBUF_ROTATE_CLOCKWISE);
-  if (rotated_pixbuf)
-    {
-      g_object_unref (scaled_pixbuf);
-      return rotated_pixbuf;
-    }
-
-  /* No rotation was applied, return the scaled pixbuf */
-  return scaled_pixbuf;
-}
-
 static void
 _load_next_picture (FrogrPictureLoader *self)
 {
@@ -326,7 +262,7 @@ _load_next_picture_cb (GObject *object,
           pixbuf = gdk_pixbuf_loader_get_pixbuf (pixbuf_loader);
 
           /* Get (scaled, and maybe rotated) pixbuf */
-          s_pixbuf = _get_corrected_pixbuf (pixbuf);
+          s_pixbuf = frogr_util_get_corrected_pixbuf (pixbuf, IV_THUMB_WIDTH, IV_THUMB_HEIGHT);
 
           /* Build the FrogrPicture */
           fpicture = frogr_picture_new (file_uri,
diff --git a/src/frogr-util.c b/src/frogr-util.c
index dfdaaac..d8dafa8 100644
--- a/src/frogr-util.c
+++ b/src/frogr-util.c
@@ -24,6 +24,12 @@
 
 #include <glib/gi18n.h>
 #include <gtk/gtk.h>
+#include <libexif/exif-byte-order.h>
+#include <libexif/exif-data.h>
+#include <libexif/exif-entry.h>
+#include <libexif/exif-format.h>
+#include <libexif/exif-loader.h>
+#include <libexif/exif-tag.h>
 
 static gboolean
 _spawn_command (const gchar* cmd)
@@ -230,9 +236,11 @@ frogr_util_show_error_dialog (GtkWindow *parent, const gchar *message)
 }
 
 GdkPixbuf *
-frogr_util_get_scaled_pixbuf (GdkPixbuf *pixbuf, gint max_width, gint max_height)
+frogr_util_get_corrected_pixbuf (GdkPixbuf *pixbuf, gint max_width, gint max_height)
 {
   GdkPixbuf *scaled_pixbuf = NULL;
+  GdkPixbuf *rotated_pixbuf;
+  const gchar *orientation;
   gint width;
   gint height;
   gint new_width;
@@ -260,5 +268,34 @@ frogr_util_get_scaled_pixbuf (GdkPixbuf *pixbuf, gint max_width, gint max_height
                                            new_width, new_height,
                                            GDK_INTERP_TILES);
 
+  /* Correct orientation if needed */
+  orientation = gdk_pixbuf_get_option (pixbuf, "orientation");
+
+  /* No orientation defined or 0 degrees rotation: we're done */
+  if (!orientation || !g_strcmp0 (orientation, "1"))
+    return scaled_pixbuf;
+
+  DEBUG ("File orientation for file: %s", orientation);
+  rotated_pixbuf = NULL;
+
+  /* Rotated 90 degrees */
+  if (!g_strcmp0 (orientation, "8"))
+    rotated_pixbuf = gdk_pixbuf_rotate_simple (scaled_pixbuf,
+                                               GDK_PIXBUF_ROTATE_COUNTERCLOCKWISE);
+  /* Rotated 180 degrees */
+  if (!g_strcmp0 (orientation, "3"))
+    rotated_pixbuf = gdk_pixbuf_rotate_simple (scaled_pixbuf,
+                                               GDK_PIXBUF_ROTATE_UPSIDEDOWN);
+  /* Rotated 270 degrees */
+  if (!g_strcmp0 (orientation, "6"))
+    rotated_pixbuf = gdk_pixbuf_rotate_simple (scaled_pixbuf,
+                                               GDK_PIXBUF_ROTATE_CLOCKWISE);
+  if (rotated_pixbuf)
+    {
+      g_object_unref (scaled_pixbuf);
+      return rotated_pixbuf;
+    }
+
+  /* No rotation was applied, return the scaled pixbuf */
   return scaled_pixbuf;
 }
diff --git a/src/frogr-util.h b/src/frogr-util.h
index 10bd644..5c06962 100644
--- a/src/frogr-util.h
+++ b/src/frogr-util.h
@@ -39,7 +39,7 @@ void frogr_util_show_warning_dialog (GtkWindow *parent, const gchar *message);
 
 void frogr_util_show_error_dialog (GtkWindow *parent, const gchar *message);
 
-GdkPixbuf *frogr_util_get_scaled_pixbuf (GdkPixbuf *pixbuf, gint max_width, gint max_height);
+GdkPixbuf *frogr_util_get_corrected_pixbuf (GdkPixbuf *pixbuf, gint max_width, gint max_height);
 
 G_END_DECLS
 



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]