[evince] Use a dynamic pixbuf cache size based on document page size



commit d375c36972ff3a01b7979984b5a1043eb4c807b0
Author: Carlos Garcia Campos <carlosgc gnome org>
Date:   Mon May 31 17:57:33 2010 +0200

    Use a dynamic pixbuf cache size based on document page size
    
    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 |    6 +-
 libview/ev-pixbuf-cache.c            |  162 +++++++++++++++++++++++++++++-----
 libview/ev-pixbuf-cache.h            |    6 +-
 libview/ev-view-private.h            |    1 +
 libview/ev-view.c                    |   27 ++++++-
 libview/ev-view.h                    |   16 ++--
 shell/ev-window.c                    |   49 ++++++++---
 7 files changed, 223 insertions(+), 44 deletions(-)
---
diff --git a/cut-n-paste/zoom-control/ephy-zoom.h b/cut-n-paste/zoom-control/ephy-zoom.h
index 293880a..bf01f0d 100644
--- a/cut-n-paste/zoom-control/ephy-zoom.h
+++ b/cut-n-paste/zoom-control/ephy-zoom.h
@@ -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);
 
diff --git a/libview/ev-pixbuf-cache.c b/libview/ev-pixbuf-cache.c
index 8658711..530f084 100644
--- a/libview/ev-pixbuf-cache.c
+++ b/libview/ev-pixbuf-cache.c
@@ -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);
diff --git a/libview/ev-pixbuf-cache.h b/libview/ev-pixbuf-cache.h
index af71546..bdf6743 100644
--- a/libview/ev-pixbuf-cache.h
+++ b/libview/ev-pixbuf-cache.h
@@ -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,
diff --git a/libview/ev-view-private.h b/libview/ev-view-private.h
index 01e260b..0362bcd 100644
--- a/libview/ev-view-private.h
+++ b/libview/ev-view-private.h
@@ -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;
diff --git a/libview/ev-view.c b/libview/ev-view.c
index 7cc0f79..c26aabc 100644
--- a/libview/ev-view.c
+++ b/libview/ev-view.c
@@ -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)
diff --git a/libview/ev-view.h b/libview/ev-view.h
index 49f77e4..86e09db 100644
--- a/libview/ev-view.h
+++ b/libview/ev-view.h
@@ -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);
diff --git a/shell/ev-window.c b/shell/ev-window.c
index d4057b1..d737356 100644
--- a/shell/ev-window.c
+++ b/shell/ev-window.c
@@ -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) {
@@ -3732,22 +3736,46 @@ ev_window_setup_gtk_settings (EvWindow *window)
 }
 
 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",



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