From b0c068f3cb3fb5643ddcc41c172157bb18c1b692 Mon Sep 17 00:00:00 2001 From: Lingzhu Xiang Date: Tue, 28 Oct 2014 03:44:26 -0400 Subject: [PATCH 1/4] Check memory allocation --- page-widget.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/page-widget.c b/page-widget.c index 4ed23c2..8ed2715 100644 --- a/page-widget.c +++ b/page-widget.c @@ -623,7 +623,14 @@ draw_thumbnail_image(cairo_surface_t* surface) cairo_surface_t *thumbnail; thumbnail = cairo_surface_create_similar(surface, CAIRO_CONTENT_COLOR, width, height); + if (thumbnail == NULL) { + return NULL; + } cairo_t *cr = cairo_create(thumbnail); + if (cr == NULL) { + cairo_surface_destroy(thumbnail); + return NULL; + } cairo_scale(cr, scale, scale); cairo_set_source_surface(cr, surface, 0, 0); From 757f3d82573d1e1b945f840c44d1eafbd36524c8 Mon Sep 17 00:00:00 2001 From: Lingzhu Xiang Date: Tue, 28 Oct 2014 03:46:20 -0400 Subject: [PATCH 2/4] Avoid large amount of scaling for pages at low zoom level --- page-widget.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/page-widget.c b/page-widget.c index 8ed2715..6339269 100644 --- a/page-widget.c +++ b/page-widget.c @@ -580,7 +580,7 @@ zathura_page_widget_redraw_canvas(ZathuraPage* pageview) } /* high enough but not causing noticable delay in scaling */ -#define THUMBNAIL_MAX_SIZE (8*1024*1024) +#define THUMBNAIL_MAX_SIZE (4*1024*1024) /* smaller than max to be replaced by actual renders */ #define THUMBNAIL_INITIAL_SIZE (THUMBNAIL_MAX_SIZE/4) /* small enough to make bilinear downscaling fast */ @@ -594,14 +594,16 @@ surface_small_enough(cairo_surface_t* surface, cairo_surface_t* old) const unsigned int width = cairo_image_surface_get_width(surface); const unsigned int height = cairo_image_surface_get_height(surface); - if (width * height > THUMBNAIL_MAX_SIZE) { + const size_t new_size = width * height; + if (new_size > THUMBNAIL_MAX_SIZE) { return false; } if (old != NULL) { const unsigned int width_old = cairo_image_surface_get_width(old); const unsigned int height_old = cairo_image_surface_get_height(old); - if (width * height < width_old * height_old) { + const size_t old_size = width_old * height_old; + if (new_size < old_size && new_size >= old_size * THUMBNAIL_MAX_SCALE * THUMBNAIL_MAX_SCALE) { return false; } } From 25e88a114aa7fc48f5847b09a5f82042c17950c9 Mon Sep 17 00:00:00 2001 From: Lingzhu Xiang Date: Tue, 28 Oct 2014 03:48:28 -0400 Subject: [PATCH 3/4] Penalize large render jobs during zooming All but the last jobs during zooming are aborted, so let smaller jobs go faster. --- page-widget.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/page-widget.c b/page-widget.c index 6339269..2365252 100644 --- a/page-widget.c +++ b/page-widget.c @@ -458,7 +458,10 @@ zathura_page_widget_draw(GtkWidget* widget, cairo_t* cairo) cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE); cairo_paint(cairo); cairo_restore(cairo); - zathura_render_request(priv->render_request, g_get_real_time()); + /* All but the last jobs requested here are aborted during zooming. + * Processing and aborting smaller jobs first improves responsiveness. */ + const gint64 penalty = pwidth * pheight; + zathura_render_request(priv->render_request, g_get_real_time() + penalty); return FALSE; } From f9b4a12208a596b91ae4ebbae2a6f18b7d43ee11 Mon Sep 17 00:00:00 2001 From: Lingzhu Xiang Date: Tue, 28 Oct 2014 18:11:52 -0400 Subject: [PATCH 4/4] Make thumbnail maximum size configurable --- config.c | 2 ++ doc/man/zathurarc.5.rst | 12 ++++++++++++ page-widget.c | 25 ++++++++++++++----------- zathura.h | 3 ++- 4 files changed, 30 insertions(+), 12 deletions(-) diff --git a/config.c b/config.c index 8d38287..25275af 100644 --- a/config.c +++ b/config.c @@ -162,6 +162,8 @@ config_load_default(zathura_t* zathura) girara_setting_add(gsession, "zoom-max", &int_value, INT, false, _("Zoom maximum"), NULL, NULL); int_value = ZATHURA_PAGE_CACHE_DEFAULT_SIZE; girara_setting_add(gsession, "page-cache-size", &int_value, INT, true, _("Maximum number of pages to keep in the cache"), NULL, NULL); + int_value = ZATHURA_PAGE_THUMBNAIL_DEFAULT_SIZE; + girara_setting_add(gsession, "page-thumbnail-size", &int_value, INT, true, _("Maximum size in pixels of thumbnails to keep in the cache"), NULL, NULL); int_value = 2000; girara_setting_add(gsession, "jumplist-size", &int_value, INT, false, _("Number of positions to remember in the jumplist"), cb_jumplist_change, NULL); diff --git a/doc/man/zathurarc.5.rst b/doc/man/zathurarc.5.rst index 938ca56..8ffd7c8 100644 --- a/doc/man/zathurarc.5.rst +++ b/doc/man/zathurarc.5.rst @@ -699,6 +699,18 @@ consuming a significant portion of the system memory. * Value type: Integer * Default value: 15 +page-thumbnail-size +^^^^^^^^^^^^^^^^^^^ +Defines the maximum size in pixels of the thumbnail that could be kept in the +thumbnail cache per page. The thumbnail is scaled for a quick preview during +zooming before the page is rendered. When the page is rendered, the result is +saved as the thumbnail only if the size is no more than this value. A larger +value increases quality but introduces longer delay in zooming and uses more +system memory. + +* Value type: Integer +* Default value: 4194304 (4M) + pages-per-row ^^^^^^^^^^^^^ Defines the number of pages that are rendered next to each other in a row. diff --git a/page-widget.c b/page-widget.c index 2365252..309f633 100644 --- a/page-widget.c +++ b/page-widget.c @@ -79,8 +79,8 @@ static void cb_menu_image_save(GtkMenuItem* item, ZathuraPage* page); static void cb_update_surface(ZathuraRenderRequest* request, cairo_surface_t* surface, void* data); static void cb_cache_added(ZathuraRenderRequest* request, void* data); static void cb_cache_invalidated(ZathuraRenderRequest* request, void* data); -static bool surface_small_enough(cairo_surface_t* surface, cairo_surface_t* old); -static cairo_surface_t *draw_thumbnail_image(cairo_surface_t* surface); +static bool surface_small_enough(cairo_surface_t* surface, size_t max_size, cairo_surface_t* old); +static cairo_surface_t *draw_thumbnail_image(cairo_surface_t* surface, size_t max_size); enum properties_e { PROP_0, @@ -582,15 +582,13 @@ zathura_page_widget_redraw_canvas(ZathuraPage* pageview) gtk_widget_queue_draw(widget); } -/* high enough but not causing noticable delay in scaling */ -#define THUMBNAIL_MAX_SIZE (4*1024*1024) /* smaller than max to be replaced by actual renders */ -#define THUMBNAIL_INITIAL_SIZE (THUMBNAIL_MAX_SIZE/4) +#define THUMBNAIL_INITIAL_SCALE 0.5 /* small enough to make bilinear downscaling fast */ #define THUMBNAIL_MAX_SCALE 0.5 static bool -surface_small_enough(cairo_surface_t* surface, cairo_surface_t* old) +surface_small_enough(cairo_surface_t* surface, size_t max_size, cairo_surface_t* old) { if (cairo_surface_get_type(surface) != CAIRO_SURFACE_TYPE_IMAGE) return true; @@ -598,7 +596,7 @@ surface_small_enough(cairo_surface_t* surface, cairo_surface_t* old) const unsigned int width = cairo_image_surface_get_width(surface); const unsigned int height = cairo_image_surface_get_height(surface); const size_t new_size = width * height; - if (new_size > THUMBNAIL_MAX_SIZE) { + if (new_size > max_size) { return false; } @@ -615,11 +613,11 @@ surface_small_enough(cairo_surface_t* surface, cairo_surface_t* old) } static cairo_surface_t * -draw_thumbnail_image(cairo_surface_t* surface) +draw_thumbnail_image(cairo_surface_t* surface, size_t max_size) { unsigned int width = cairo_image_surface_get_width(surface); unsigned int height = cairo_image_surface_get_height(surface); - double scale = sqrt((double)THUMBNAIL_INITIAL_SIZE / (width * height)); + double scale = sqrt((double)max_size / (width * height)) * THUMBNAIL_INITIAL_SCALE; if (scale > THUMBNAIL_MAX_SCALE) { scale = THUMBNAIL_MAX_SCALE; } @@ -651,6 +649,11 @@ void zathura_page_widget_update_surface(ZathuraPage* widget, cairo_surface_t* surface, bool keep_thumbnail) { zathura_page_widget_private_t* priv = ZATHURA_PAGE_GET_PRIVATE(widget); + int thumbnail_size = 0; + girara_setting_get(priv->zathura->ui.session, "page-thumbnail-size", &thumbnail_size); + if (thumbnail_size <= 0) { + thumbnail_size = ZATHURA_PAGE_THUMBNAIL_DEFAULT_SIZE; + } bool new_render = (priv->surface == NULL && priv->thumbnail == NULL); if (priv->surface != NULL) { @@ -661,14 +664,14 @@ zathura_page_widget_update_surface(ZathuraPage* widget, cairo_surface_t* surface priv->surface = surface; cairo_surface_reference(surface); - if (surface_small_enough(surface, priv->thumbnail)) { + if (surface_small_enough(surface, thumbnail_size, priv->thumbnail)) { if (priv->thumbnail != NULL) { cairo_surface_destroy(priv->thumbnail); } priv->thumbnail = surface; cairo_surface_reference(surface); } else if (new_render) { - priv->thumbnail = draw_thumbnail_image(surface); + priv->thumbnail = draw_thumbnail_image(surface, thumbnail_size); } } else if (!keep_thumbnail && priv->thumbnail != NULL) { cairo_surface_destroy(priv->thumbnail); diff --git a/zathura.h b/zathura.h index 66b5664..b7168cd 100644 --- a/zathura.h +++ b/zathura.h @@ -73,7 +73,8 @@ enum { /* cache constants */ enum { ZATHURA_PAGE_CACHE_DEFAULT_SIZE = 15, - ZATHURA_PAGE_CACHE_MAX_SIZE = 1024 + ZATHURA_PAGE_CACHE_MAX_SIZE = 1024, + ZATHURA_PAGE_THUMBNAIL_DEFAULT_SIZE = 4*1024*1024 }; /* forward declaration for types from database.h */