1
0
mirror of https://gitlab.gnome.org/GNOME/evince synced 2024-07-05 00:59:07 +00:00

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.
This commit is contained in:
Carlos Garcia Campos 2010-05-31 17:57:33 +02:00
parent 51261c0750
commit d375c36972
7 changed files with 222 additions and 43 deletions

View 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);

View 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);

View 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,

View 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;

View 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)

View File

@ -44,14 +44,16 @@ typedef enum {
EV_VIEW_SELECTION_RECTANGLE,
} EvViewSelectionMode;
GType ev_view_get_type (void) G_GNUC_CONST;
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);
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);

View 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",