]> www.fi.muni.cz Git - evince.git/commitdiff
Use a dynamic pixbuf cache size based on document page size
authorCarlos Garcia Campos <carlosgc@gnome.org>
Mon, 31 May 2010 15:57:33 +0000 (17:57 +0200)
committerCarlos Garcia Campos <carlosgc@gnome.org>
Mon, 31 May 2010 16:57:59 +0000 (18:57 +0200)
Instead of using a static number of pages to cache, we use a size in
bytes, and the number of pages that will be cached depends on the
current zoom level. It allows us caching more pages for lower scale
factors and increase zoom level by caching fewer pages. See bug #303365.

cut-n-paste/zoom-control/ephy-zoom.h
libview/ev-pixbuf-cache.c
libview/ev-pixbuf-cache.h
libview/ev-view-private.h
libview/ev-view.c
libview/ev-view.h
shell/ev-window.c

index 293880a200f05191d5816e9235924f07bf2f4792..bf01f0d7e638a3b05c079dd230e82e80c3a78048 100644 (file)
@@ -57,7 +57,11 @@ zoom_levels[] =
        { N_("175%"), 1.6817928304 },
        { N_("200%"), 2.0 },
        { N_("300%"), 2.8284271247 },
-       { N_("400%"), 4.0 }
+       { N_("400%"), 4.0 },
+       { N_("800%"), 8.0 },
+       { N_("1600%"), 16.0 },
+       { N_("3200%"), 32.0 },
+       { N_("6400%"), 64.0 }
 };
 static const guint n_zoom_levels = G_N_ELEMENTS (zoom_levels);
 
index 8658711f793273dd37bdfcde9c7556cc9138d383..530f084cbf0c445d4d446a99f471fb34472f7729 100644 (file)
@@ -37,10 +37,13 @@ struct _EvPixbufCache
        /* We keep a link to our containing view just for style information. */
        GtkWidget *view;
        EvDocument *document;
+       EvDocumentModel *model;
        int start_page;
        int end_page;
        gboolean inverted_colors;
 
+       gsize max_size;
+
        /* preload_cache_size is the number of pages prior to the current
         * visible area that we cache.  It's normally 1, but could be 2 in the
         * case of twin pages.
@@ -89,18 +92,15 @@ static gboolean      new_selection_surface_needed(EvPixbufCache      *pixbuf_cac
 #define PAGE_CACHE_LEN(pixbuf_cache) \
        ((pixbuf_cache->end_page - pixbuf_cache->start_page) + 1)
 
+#define MAX_PRELOADED_PAGES 3
+
 G_DEFINE_TYPE (EvPixbufCache, ev_pixbuf_cache, G_TYPE_OBJECT)
 
 static void
 ev_pixbuf_cache_init (EvPixbufCache *pixbuf_cache)
 {
-       pixbuf_cache->start_page = 0;
-       pixbuf_cache->end_page = 0;
-       pixbuf_cache->job_list = g_new0 (CacheJobInfo, PAGE_CACHE_LEN (pixbuf_cache));
-
-       pixbuf_cache->preload_cache_size = 2;
-       pixbuf_cache->prev_job = g_new0 (CacheJobInfo, pixbuf_cache->preload_cache_size);
-       pixbuf_cache->next_job = g_new0 (CacheJobInfo, pixbuf_cache->preload_cache_size);
+       pixbuf_cache->start_page = -1;
+       pixbuf_cache->end_page = -1;
 }
 
 static void
@@ -135,6 +135,8 @@ ev_pixbuf_cache_finalize (GObject *object)
        g_free (pixbuf_cache->job_list);
        g_free (pixbuf_cache->next_job);
 
+       g_object_unref (pixbuf_cache->model);
+
        G_OBJECT_CLASS (ev_pixbuf_cache_parent_class)->finalize (object);
 }
 
@@ -195,19 +197,34 @@ ev_pixbuf_cache_dispose (GObject *object)
 
 
 EvPixbufCache *
-ev_pixbuf_cache_new (GtkWidget  *view,
-                    EvDocument *document)
+ev_pixbuf_cache_new (GtkWidget       *view,
+                    EvDocumentModel *model,
+                    gsize            max_size)
 {
        EvPixbufCache *pixbuf_cache;
 
        pixbuf_cache = (EvPixbufCache *) g_object_new (EV_TYPE_PIXBUF_CACHE, NULL);
        /* This is a backlink, so we don't ref this */ 
        pixbuf_cache->view = view;
-       pixbuf_cache->document = document;
+       pixbuf_cache->model = g_object_ref (model);
+       pixbuf_cache->document = ev_document_model_get_document (model);
+       pixbuf_cache->max_size = max_size;
 
        return pixbuf_cache;
 }
 
+void
+ev_pixbuf_cache_set_max_size (EvPixbufCache *pixbuf_cache,
+                             gsize          max_size)
+{
+       if (pixbuf_cache->max_size == max_size)
+               return;
+
+       if (pixbuf_cache->max_size > max_size)
+               ev_pixbuf_cache_clear (pixbuf_cache);
+       pixbuf_cache->max_size = max_size;
+}
+
 static void
 copy_job_to_job_info (EvJobRender   *job_render,
                      CacheJobInfo  *job_info,
@@ -313,6 +330,7 @@ move_one_job (CacheJobInfo  *job_info,
              CacheJobInfo  *new_job_list,
              CacheJobInfo  *new_prev_job,
              CacheJobInfo  *new_next_job,
+             int            new_preload_cache_size,
              int            start_page,
              int            end_page,
              gint           priority)
@@ -321,25 +339,25 @@ move_one_job (CacheJobInfo  *job_info,
        int page_offset;
        gint new_priority;
 
-       if (page < (start_page - pixbuf_cache->preload_cache_size) ||
-           page > (end_page + pixbuf_cache->preload_cache_size)) {
+       if (page < (start_page - new_preload_cache_size) ||
+           page > (end_page + new_preload_cache_size)) {
                dispose_cache_job_info (job_info, pixbuf_cache);
                return;
        }
 
        /* find the target page to copy it over to. */
        if (page < start_page) {
-               page_offset = (page - (start_page - pixbuf_cache->preload_cache_size));
+               page_offset = (page - (start_page - new_preload_cache_size));
 
                g_assert (page_offset >= 0 &&
-                         page_offset < pixbuf_cache->preload_cache_size);
+                         page_offset < new_preload_cache_size);
                target_page = new_prev_job + page_offset;
                new_priority = EV_JOB_PRIORITY_LOW;
        } else if (page > end_page) {
                page_offset = (page - (end_page + 1));
 
                g_assert (page_offset >= 0 &&
-                         page_offset < pixbuf_cache->preload_cache_size);
+                         page_offset < new_preload_cache_size);
                target_page = new_next_job + page_offset;
                new_priority = EV_JOB_PRIORITY_LOW;
        } else {
@@ -360,23 +378,103 @@ move_one_job (CacheJobInfo  *job_info,
        }
 }
 
+static gsize
+ev_pixbuf_cache_get_page_size (EvPixbufCache *pixbuf_cache,
+                              gint           page_index,
+                              gdouble        scale,
+                              gint           rotation)
+{
+       gint width, height;
+
+       _get_page_size_for_scale_and_rotation (pixbuf_cache->document,
+                                              page_index, scale, rotation,
+                                              &width, &height);
+       return height * cairo_format_stride_for_width (CAIRO_FORMAT_RGB24, width);
+}
+
+static gint
+ev_pixbuf_cache_get_preload_size (EvPixbufCache *pixbuf_cache,
+                                 gint           start_page,
+                                 gint           end_page,
+                                 gdouble        scale,
+                                 gint           rotation)
+{
+       gsize range_size = 0;
+       gint  new_preload_cache_size = 0;
+       gint  i;
+       guint n_pages = ev_document_get_n_pages (pixbuf_cache->document);
+
+       /* Get the size of the current range */
+       for (i = start_page; i <= end_page; i++) {
+               range_size += ev_pixbuf_cache_get_page_size (pixbuf_cache, i, scale, rotation);
+       }
+
+       if (range_size >= pixbuf_cache->max_size)
+               return new_preload_cache_size;
+
+       i = 1;
+       while (((start_page - i > 0) || (end_page + i < n_pages)) &&
+              new_preload_cache_size < MAX_PRELOADED_PAGES) {
+               gsize    page_size;
+               gboolean updated = FALSE;
+
+               if (end_page + i < n_pages) {
+                       page_size = ev_pixbuf_cache_get_page_size (pixbuf_cache, end_page + i,
+                                                                  scale, rotation);
+                       if (page_size + range_size <= pixbuf_cache->max_size) {
+                               range_size += page_size;
+                               new_preload_cache_size++;
+                               updated = TRUE;
+                       } else {
+                               break;
+                       }
+               }
+
+               if (start_page - i > 0) {
+                       page_size = ev_pixbuf_cache_get_page_size (pixbuf_cache, start_page - i,
+                                                                  scale, rotation);
+                       if (page_size + range_size <= pixbuf_cache->max_size) {
+                               range_size += page_size;
+                               if (!updated)
+                                       new_preload_cache_size++;
+                       } else {
+                               break;
+                       }
+               }
+               i++;
+       }
+
+       return new_preload_cache_size;
+}
+
 static void
 ev_pixbuf_cache_update_range (EvPixbufCache *pixbuf_cache,
                              gint           start_page,
                              gint           end_page)
 {
        CacheJobInfo *new_job_list;
-       CacheJobInfo *new_prev_job;
-       CacheJobInfo *new_next_job;
-       int i, page;
-
+       CacheJobInfo *new_prev_job = NULL;
+       CacheJobInfo *new_next_job = NULL;
+       gint          new_preload_cache_size;
+       int           i, page;
+       gdouble       scale = ev_document_model_get_scale (pixbuf_cache->model);
+       gint          rotation = ev_document_model_get_rotation (pixbuf_cache->model);
+
+       new_preload_cache_size = ev_pixbuf_cache_get_preload_size (pixbuf_cache,
+                                                                  start_page,
+                                                                  end_page,
+                                                                  scale,
+                                                                  rotation);
        if (pixbuf_cache->start_page == start_page &&
-           pixbuf_cache->end_page == end_page)
+           pixbuf_cache->end_page == end_page &&
+           pixbuf_cache->preload_cache_size == new_preload_cache_size)
                return;
 
        new_job_list = g_new0 (CacheJobInfo, (end_page - start_page) + 1);
-       new_prev_job = g_new0 (CacheJobInfo, pixbuf_cache->preload_cache_size);
-       new_next_job = g_new0 (CacheJobInfo, pixbuf_cache->preload_cache_size);
+       if (new_preload_cache_size > 0) {
+               new_prev_job = g_new0 (CacheJobInfo, new_preload_cache_size);
+               new_next_job = g_new0 (CacheJobInfo, new_preload_cache_size);
+       }
 
        /* We go through each job in the old cache and either clear it or move
         * it to a new location. */
@@ -390,16 +488,18 @@ ev_pixbuf_cache_update_range (EvPixbufCache *pixbuf_cache,
                        move_one_job (pixbuf_cache->prev_job + i,
                                      pixbuf_cache, page,
                                      new_job_list, new_prev_job, new_next_job,
+                                     new_preload_cache_size,
                                      start_page, end_page, EV_JOB_PRIORITY_LOW);
                }
                page ++;
        }
 
        page = pixbuf_cache->start_page;
-       for (i = 0; i < PAGE_CACHE_LEN (pixbuf_cache); i++) {
+       for (i = 0; i < PAGE_CACHE_LEN (pixbuf_cache) && page >= 0; i++) {
                move_one_job (pixbuf_cache->job_list + i,
                              pixbuf_cache, page,
                              new_job_list, new_prev_job, new_next_job,
+                             new_preload_cache_size,
                              start_page, end_page, EV_JOB_PRIORITY_URGENT);
                page ++;
        }
@@ -411,6 +511,7 @@ ev_pixbuf_cache_update_range (EvPixbufCache *pixbuf_cache,
                        move_one_job (pixbuf_cache->next_job + i,
                                      pixbuf_cache, page,
                                      new_job_list, new_prev_job, new_next_job,
+                                     new_preload_cache_size,
                                      start_page, end_page, EV_JOB_PRIORITY_LOW);
                }
                page ++;
@@ -420,6 +521,8 @@ ev_pixbuf_cache_update_range (EvPixbufCache *pixbuf_cache,
        g_free (pixbuf_cache->prev_job);
        g_free (pixbuf_cache->next_job);
 
+       pixbuf_cache->preload_cache_size = new_preload_cache_size;
+
        pixbuf_cache->job_list = new_job_list;
        pixbuf_cache->prev_job = new_prev_job;
        pixbuf_cache->next_job = new_next_job;
@@ -550,6 +653,19 @@ add_job_if_needed (EvPixbufCache *pixbuf_cache,
            cairo_image_surface_get_height (job_info->surface) == height)
                return;
 
+       /* Free old surfaces for non visible pages */
+       if (priority == EV_JOB_PRIORITY_LOW) {
+               if (job_info->surface) {
+                       cairo_surface_destroy (job_info->surface);
+                       job_info->surface = NULL;
+               }
+
+               if (job_info->selection) {
+                       cairo_surface_destroy (job_info->selection);
+                       job_info->selection = NULL;
+               }
+       }
+
        add_job (pixbuf_cache, job_info, NULL,
                 width, height, page, rotation, scale,
                 priority);
index af7154687dee48778cdd8a179e6a94bf16452621..bdf6743ccf72cefc7ab8a7e5c425b66bfee5225c 100644 (file)
@@ -31,6 +31,7 @@
 #include <gtk/gtk.h>
 
 #include <evince-document.h>
+#include <evince-view.h>
 
 G_BEGIN_DECLS
 
@@ -55,7 +56,10 @@ typedef struct _EvPixbufCacheClass  EvPixbufCacheClass;
 
 GType          ev_pixbuf_cache_get_type             (void) G_GNUC_CONST;
 EvPixbufCache *ev_pixbuf_cache_new                  (GtkWidget     *view,
-                                                    EvDocument    *document);
+                                                    EvDocumentModel *model,
+                                                    gsize            max_size);
+void           ev_pixbuf_cache_set_max_size         (EvPixbufCache   *pixbuf_cache,
+                                                    gsize            max_size);
 void           ev_pixbuf_cache_set_page_range       (EvPixbufCache *pixbuf_cache,
                                                     gint           start_page,
                                                     gint           end_page,
index 01e260b8cc1584cbffe4e7166e8426ae98944e1f..0362bcd1901623cae4b0a9f1c3358bb6343bd604 100644 (file)
@@ -120,6 +120,7 @@ struct _EvView {
 
        EvDocumentModel *model;
        EvPixbufCache *pixbuf_cache;
+       gsize pixbuf_cache_size;
        EvPageCache *page_cache;
        EvHeightToPageCache *height_to_page_cache;
        EvViewCursor cursor;
index 7cc0f79e6962981eedd27361643c31b35cf7eed0..c26aabc5f86cc0018feeb24e648d6b271d5b52ca 100644 (file)
@@ -4554,7 +4554,7 @@ setup_caches (EvView *view)
        gboolean inverted_colors;
 
        view->height_to_page_cache = ev_view_get_height_to_page_cache (view);
-       view->pixbuf_cache = ev_pixbuf_cache_new (GTK_WIDGET (view), view->document);
+       view->pixbuf_cache = ev_pixbuf_cache_new (GTK_WIDGET (view), view->model, view->pixbuf_cache_size);
        view->page_cache = ev_page_cache_new (view->document);
        inverted_colors = ev_document_model_get_inverted_colors (view->model);
        ev_pixbuf_cache_set_inverted_colors (view->pixbuf_cache, inverted_colors);
@@ -4575,6 +4575,31 @@ clear_caches (EvView *view)
        }
 }
 
+/**
+ * ev_view_set_page_cache_size:
+ * @view:
+ * @cache_size:
+ *
+ * Sets the maximum size in bytes that will be used to cache
+ * rendered pages. Use 0 to disable caching rendered pages.
+ *
+ * Note that this limit doesn't affect the current visible page range,
+ * which will always be rendered. In order to limit the total memory used
+ * you have to use ev_document_model_set_max_scale() too.
+ *
+ */
+void
+ev_view_set_page_cache_size (EvView *view,
+                            gsize   cache_size)
+{
+       if (view->pixbuf_cache_size == cache_size)
+               return;
+
+       view->pixbuf_cache_size = cache_size;
+       if (view->pixbuf_cache)
+               ev_pixbuf_cache_set_max_size (view->pixbuf_cache, cache_size);
+}
+
 void
 ev_view_set_loading (EvView      *view,
                     gboolean      loading)
index 49f77e4aa5518ad1ef9746bb78270a6f7c82af0c..86e09db9aad2cb8f8a401dfc9478ea0aa8b4b5a1 100644 (file)
@@ -44,14 +44,16 @@ typedef enum {
        EV_VIEW_SELECTION_RECTANGLE,
 } EvViewSelectionMode;
 
-GType          ev_view_get_type          (void) G_GNUC_CONST;
-
-GtkWidget*     ev_view_new               (void);
-void           ev_view_set_model         (EvView         *view,
-                                          EvDocumentModel *model);
-void           ev_view_set_loading       (EvView         *view,
-                                          gboolean        loading);
-void            ev_view_reload            (EvView         *view);
+GType          ev_view_get_type            (void) G_GNUC_CONST;
+
+GtkWidget*     ev_view_new                 (void);
+void           ev_view_set_model           (EvView          *view,
+                                            EvDocumentModel *model);
+void           ev_view_set_loading         (EvView          *view,
+                                            gboolean         loading);
+void            ev_view_reload              (EvView          *view);
+void            ev_view_set_page_cache_size (EvView          *view,
+                                            gsize            cache_size);
 
 /* Clipboard */
 void           ev_view_copy              (EvView         *view);
index d4057b115bc97b05451917f10b254ff21db7b858..d73735629829f0586b7ad3f7f73cfe1cc5037a26 100644 (file)
@@ -34,6 +34,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
+#include <math.h>
 
 #include <glib/gstdio.h>
 #include <glib/gi18n.h>
@@ -243,7 +244,7 @@ struct _EvWindowPrivate {
 #define EV_TOOLBARS_FILENAME "evince-toolbar.xml"
 
 #define MIN_SCALE 0.05409
-#define MAX_SCALE 4.0
+#define PAGE_CACHE_SIZE 52428800 /* 50MB */
 
 #define MAX_RECENT_ITEM_LEN (40)
 
@@ -325,6 +326,7 @@ static void     ev_window_load_file_remote              (EvWindow         *ev_wi
 static void     ev_window_media_player_key_pressed      (EvWindow         *window,
                                                         const gchar      *key,
                                                         gpointer          user_data);
+static void     ev_window_update_max_min_scale          (EvWindow         *window);
 
 static guint ev_window_n_copies = 0;
 
@@ -1225,7 +1227,7 @@ ev_window_setup_document (EvWindow *ev_window)
        GtkAction *action;
 
        ev_window->priv->setup_document_idle = 0;
-       
+
        ev_window_refresh_window_thumbnail (ev_window);
 
        ev_window_set_page_mode (ev_window, PAGE_MODE_DOCUMENT);
@@ -1286,6 +1288,8 @@ ev_window_set_document (EvWindow *ev_window, EvDocument *document)
                g_object_unref (ev_window->priv->document);
        ev_window->priv->document = g_object_ref (document);
 
+       ev_window_update_max_min_scale (ev_window);
+
        ev_window_set_message_area (ev_window, NULL);
 
        if (ev_document_get_n_pages (document) <= 0) {
@@ -3731,23 +3735,47 @@ ev_window_setup_gtk_settings (EvWindow *window)
        g_free (menubar_accel_accel);
 }
 
+static void
+ev_window_update_max_min_scale (EvWindow *window)
+{
+       gdouble    dpi;
+       GtkAction *action;
+       gdouble    min_width, min_height;
+       gdouble    width, height;
+       gdouble    max_scale;
+       gint       rotation = ev_document_model_get_rotation (window->priv->model);
+
+       if (!window->priv->document)
+               return;
+
+       dpi = get_screen_dpi (window) / 72.0;
+
+       ev_document_get_min_page_size (window->priv->document, &min_width, &min_height);
+       width = (rotation == 0 || rotation == 180) ? min_width : min_height;
+       height = (rotation == 0 || rotation == 180) ? min_height : min_width;
+       max_scale = sqrt (PAGE_CACHE_SIZE / (width * dpi * 4 * height * dpi));
+
+       action = gtk_action_group_get_action (window->priv->action_group,
+                                             ZOOM_CONTROL_ACTION);
+       ephy_zoom_action_set_max_zoom_level (EPHY_ZOOM_ACTION (action), max_scale * dpi);
+
+       ev_document_model_set_min_scale (window->priv->model, MIN_SCALE * dpi);
+       ev_document_model_set_max_scale (window->priv->model, max_scale * dpi);
+}
+
 static void
 ev_window_screen_changed (GtkWidget *widget,
                          GdkScreen *old_screen)
 {
        EvWindow *window = EV_WINDOW (widget);
-       EvWindowPrivate *priv = window->priv;
        GdkScreen *screen;
-       gdouble dpi;
 
        screen = gtk_widget_get_screen (widget);
        if (screen == old_screen)
                return;
 
        ev_window_setup_gtk_settings (window);
-       dpi = get_screen_dpi (window);
-       ev_document_model_set_min_scale (priv->model, MIN_SCALE * dpi / 72.0);
-       ev_document_model_set_max_scale (priv->model, MAX_SCALE * dpi / 72.0);
+       ev_window_update_max_min_scale (window);
 
        if (GTK_WIDGET_CLASS (ev_window_parent_class)->screen_changed) {
                GTK_WIDGET_CLASS (ev_window_parent_class)->screen_changed (widget, old_screen);
@@ -4165,6 +4193,7 @@ ev_window_rotation_changed_cb (EvDocumentModel *model,
                ev_metadata_set_int (window->priv->metadata, "rotation",
                                     rotation);
 
+       ev_window_update_max_min_scale (window);
        ev_window_refresh_window_thumbnail (window);
 }
 
@@ -6130,7 +6159,6 @@ ev_window_init (EvWindow *ev_window)
        EggToolbarsModel *toolbars_model;
        GObject *mpkeys;
        gchar *ui_path;
-       gdouble dpi;
 
        g_signal_connect (ev_window, "configure_event",
                          G_CALLBACK (window_configure_event_cb), NULL);
@@ -6316,10 +6344,9 @@ ev_window_init (EvWindow *ev_window)
        gtk_widget_show (ev_window->priv->view_box);
 
        ev_window->priv->view = ev_view_new ();
+       ev_view_set_page_cache_size (EV_VIEW (ev_window->priv->view), PAGE_CACHE_SIZE);
        ev_view_set_model (EV_VIEW (ev_window->priv->view), ev_window->priv->model);
-       dpi = get_screen_dpi (ev_window);
-       ev_document_model_set_min_scale (ev_window->priv->model, MIN_SCALE * dpi / 72.0);
-       ev_document_model_set_max_scale (ev_window->priv->model, MAX_SCALE * dpi / 72.0);
+
        ev_window->priv->password_view = ev_password_view_new (GTK_WINDOW (ev_window));
        g_signal_connect_swapped (ev_window->priv->password_view,
                                  "unlock",