X-Git-Url: https://www.fi.muni.cz/~kas/git//home/kas/public_html/git/?a=blobdiff_plain;f=libview%2Fev-pixbuf-cache.c;h=367f70d781fd52490186d3a09b354ee8a26e4c81;hb=e769474337c9a6ffdaed2327056e8de2f7ca9ee4;hp=8cca5cdd7d751a21e0f4d35b7aced257decfe857;hpb=3a8589a7c5e0394df456074048845cddd04bc43d;p=evince.git diff --git a/libview/ev-pixbuf-cache.c b/libview/ev-pixbuf-cache.c index 8cca5cdd..367f70d7 100644 --- a/libview/ev-pixbuf-cache.c +++ b/libview/ev-pixbuf-cache.c @@ -1,20 +1,15 @@ #include #include "ev-pixbuf-cache.h" #include "ev-job-scheduler.h" -#include "ev-mapping.h" -#include "ev-document-forms.h" -#include "ev-document-images.h" -#include "ev-document-annotations.h" #include "ev-view-private.h" typedef struct _CacheJobInfo { EvJob *job; - EvRenderContext *rc; gboolean page_ready; /* Region of the page that needs to be drawn */ - GdkRegion *region; + cairo_region_t *region; /* Data we get from rendering */ cairo_surface_t *surface; @@ -28,7 +23,7 @@ typedef struct _CacheJobInfo gboolean points_set; cairo_surface_t *selection; - GdkRegion *selection_region; + cairo_region_t *selection_region; } CacheJobInfo; struct _EvPixbufCache @@ -38,15 +33,20 @@ 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. */ int preload_cache_size; + guint job_list_len; + CacheJobInfo *prev_job; CacheJobInfo *job_list; CacheJobInfo *next_job; @@ -90,18 +90,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 @@ -132,9 +129,23 @@ ev_pixbuf_cache_finalize (GObject *object) pixbuf_cache = EV_PIXBUF_CACHE (object); - g_free (pixbuf_cache->prev_job); - g_free (pixbuf_cache->job_list); - g_free (pixbuf_cache->next_job); + if (pixbuf_cache->job_list) { + g_slice_free1 (sizeof (CacheJobInfo) * pixbuf_cache->job_list_len, + pixbuf_cache->job_list); + pixbuf_cache->job_list = NULL; + } + if (pixbuf_cache->prev_job) { + g_slice_free1 (sizeof (CacheJobInfo) * pixbuf_cache->preload_cache_size, + pixbuf_cache->prev_job); + pixbuf_cache->prev_job = NULL; + } + if (pixbuf_cache->next_job) { + g_slice_free1 (sizeof (CacheJobInfo) * pixbuf_cache->preload_cache_size, + pixbuf_cache->next_job); + pixbuf_cache->next_job = NULL; + } + + g_object_unref (pixbuf_cache->model); G_OBJECT_CLASS (ev_pixbuf_cache_parent_class)->finalize (object); } @@ -159,7 +170,7 @@ dispose_cache_job_info (CacheJobInfo *job_info, job_info->surface = NULL; } if (job_info->region) { - gdk_region_destroy (job_info->region); + cairo_region_destroy (job_info->region); job_info->region = NULL; } if (job_info->selection) { @@ -167,13 +178,9 @@ dispose_cache_job_info (CacheJobInfo *job_info, job_info->selection = NULL; } if (job_info->selection_region) { - gdk_region_destroy (job_info->selection_region); + cairo_region_destroy (job_info->selection_region); job_info->selection_region = NULL; } - if (job_info->rc) { - g_object_unref (G_OBJECT (job_info->rc)); - job_info->rc = NULL; - } job_info->points_set = FALSE; } @@ -200,34 +207,39 @@ 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, EvPixbufCache *pixbuf_cache) { - if (job_info->rc == NULL) { - job_info->rc = ev_render_context_new (job_render->ev_page, - job_render->rotation, - job_render->scale); - } else { - ev_render_context_set_page (job_info->rc, job_render->ev_page); - ev_render_context_set_rotation (job_info->rc, job_render->rotation); - ev_render_context_set_scale (job_info->rc, job_render->scale); - } - if (job_info->surface) { cairo_surface_destroy (job_info->surface); } @@ -243,12 +255,12 @@ copy_job_to_job_info (EvJobRender *job_render, job_info->selection = NULL; } if (job_info->selection_region) { - gdk_region_destroy (job_info->selection_region); + cairo_region_destroy (job_info->selection_region); job_info->selection_region = NULL; } job_info->selection_points = job_render->selection_points; - job_info->selection_region = gdk_region_copy (job_render->selection_region); + job_info->selection_region = cairo_region_reference (job_render->selection_region); job_info->selection = cairo_surface_reference (job_render->selection); g_assert (job_info->selection_points.x1 >= 0); job_info->points_set = TRUE; @@ -328,6 +340,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) @@ -336,25 +349,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 { @@ -375,23 +388,105 @@ 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) + gint end_page, + guint rotation, + gdouble scale) { 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; + guint new_job_list_len; + int i, page; + + 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); + new_job_list_len = (end_page - start_page) + 1; + new_job_list = g_slice_alloc0 (sizeof (CacheJobInfo) * new_job_list_len); + if (new_preload_cache_size > 0) { + new_prev_job = g_slice_alloc0 (sizeof (CacheJobInfo) * new_preload_cache_size); + new_next_job = g_slice_alloc0 (sizeof (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. */ @@ -405,16 +500,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 ++; } @@ -426,14 +523,27 @@ 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 ++; } - g_free (pixbuf_cache->job_list); - g_free (pixbuf_cache->prev_job); - g_free (pixbuf_cache->next_job); + if (pixbuf_cache->job_list) { + g_slice_free1 (sizeof (CacheJobInfo) * pixbuf_cache->job_list_len, + pixbuf_cache->job_list); + } + if (pixbuf_cache->prev_job) { + g_slice_free1 (sizeof (CacheJobInfo) * pixbuf_cache->preload_cache_size, + pixbuf_cache->prev_job); + } + if (pixbuf_cache->next_job) { + g_slice_free1 (sizeof (CacheJobInfo) * pixbuf_cache->preload_cache_size, + pixbuf_cache->next_job); + } + + pixbuf_cache->preload_cache_size = new_preload_cache_size; + pixbuf_cache->job_list_len = new_job_list_len; pixbuf_cache->job_list = new_job_list; pixbuf_cache->prev_job = new_prev_job; @@ -494,31 +604,33 @@ ev_pixbuf_cache_clear_job_sizes (EvPixbufCache *pixbuf_cache, static void get_selection_colors (GtkWidget *widget, GdkColor **text, GdkColor **base) { - if (GTK_WIDGET_HAS_FOCUS (widget)) { - *text = &widget->style->text [GTK_STATE_SELECTED]; - *base = &widget->style->base [GTK_STATE_SELECTED]; - } else { - *text = &widget->style->text [GTK_STATE_ACTIVE]; - *base = &widget->style->base [GTK_STATE_ACTIVE]; - } + GtkStyle *style = gtk_widget_get_style (widget); + + if (gtk_widget_has_focus (widget)) { + *text = &style->text [GTK_STATE_SELECTED]; + *base = &style->base [GTK_STATE_SELECTED]; + } else { + *text = &style->text [GTK_STATE_ACTIVE]; + *base = &style->base [GTK_STATE_ACTIVE]; + } } static void -add_job (EvPixbufCache *pixbuf_cache, - CacheJobInfo *job_info, - GdkRegion *region, - gint width, - gint height, - gint page, - gint rotation, - gfloat scale, - EvJobPriority priority) +add_job (EvPixbufCache *pixbuf_cache, + CacheJobInfo *job_info, + cairo_region_t *region, + gint width, + gint height, + gint page, + gint rotation, + gfloat scale, + EvJobPriority priority) { job_info->page_ready = FALSE; if (job_info->region) - gdk_region_destroy (job_info->region); - job_info->region = region ? gdk_region_copy (region) : NULL; + cairo_region_destroy (job_info->region); + job_info->region = region ? cairo_region_reference (region) : NULL; job_info->job = ev_job_render_new (pixbuf_cache->document, page, rotation, scale, @@ -563,6 +675,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); @@ -610,10 +735,11 @@ void ev_pixbuf_cache_set_page_range (EvPixbufCache *pixbuf_cache, gint start_page, gint end_page, - gint rotation, - gfloat scale, GList *selection_list) { + gdouble scale = ev_document_model_get_scale (pixbuf_cache->model); + gint rotation = ev_document_model_get_rotation (pixbuf_cache->model); + g_return_if_fail (EV_IS_PIXBUF_CACHE (pixbuf_cache)); g_return_if_fail (start_page >= 0 && start_page < ev_document_get_n_pages (pixbuf_cache->document)); @@ -622,7 +748,7 @@ ev_pixbuf_cache_set_page_range (EvPixbufCache *pixbuf_cache, /* First, resize the page_range as needed. We cull old pages * mercilessly. */ - ev_pixbuf_cache_update_range (pixbuf_cache, start_page, end_page); + ev_pixbuf_cache_update_range (pixbuf_cache, start_page, end_page, rotation, scale); /* Then, we update the current jobs to see if any of them are the wrong * size, we remove them if we need to. */ @@ -651,11 +777,11 @@ ev_pixbuf_cache_set_inverted_colors (EvPixbufCache *pixbuf_cache, CacheJobInfo *job_info; job_info = pixbuf_cache->prev_job + i; - if (job_info->surface) + if (job_info && job_info->surface) ev_document_misc_invert_surface (job_info->surface); job_info = pixbuf_cache->next_job + i; - if (job_info->surface) + if (job_info && job_info->surface) ev_document_misc_invert_surface (job_info->surface); } @@ -663,7 +789,7 @@ ev_pixbuf_cache_set_inverted_colors (EvPixbufCache *pixbuf_cache, CacheJobInfo *job_info; job_info = pixbuf_cache->job_list + i; - if (job_info->surface) + if (job_info && job_info->surface) ev_document_misc_invert_surface (job_info->surface); } } @@ -697,12 +823,12 @@ new_selection_surface_needed (EvPixbufCache *pixbuf_cache, gint page, gfloat scale) { - if (job_info->selection && job_info->rc) { + if (job_info->selection) { gint width, height; gint selection_width, selection_height; _get_page_size_for_scale_and_rotation (pixbuf_cache->document, - page, scale, job_info->rc->rotation, + page, scale, 0, &width, &height); selection_width = cairo_image_surface_get_width (job_info->selection); @@ -739,6 +865,9 @@ ev_pixbuf_cache_clear (EvPixbufCache *pixbuf_cache) { int i; + if (!pixbuf_cache->job_list) + return; + for (i = 0; i < pixbuf_cache->preload_cache_size; i++) { dispose_cache_job_info (pixbuf_cache->prev_job + i, pixbuf_cache); dispose_cache_job_info (pixbuf_cache->next_job + i, pixbuf_cache); @@ -755,6 +884,9 @@ ev_pixbuf_cache_style_changed (EvPixbufCache *pixbuf_cache) { gint i; + if (!pixbuf_cache->job_list) + return; + /* FIXME: doesn't update running jobs. */ for (i = 0; i < pixbuf_cache->preload_cache_size; i++) { CacheJobInfo *job_info; @@ -784,10 +916,10 @@ ev_pixbuf_cache_style_changed (EvPixbufCache *pixbuf_cache) } cairo_surface_t * -ev_pixbuf_cache_get_selection_surface (EvPixbufCache *pixbuf_cache, - gint page, - gfloat scale, - GdkRegion **region) +ev_pixbuf_cache_get_selection_surface (EvPixbufCache *pixbuf_cache, + gint page, + gfloat scale, + cairo_region_t **region) { CacheJobInfo *job_info; @@ -803,17 +935,6 @@ ev_pixbuf_cache_get_selection_surface (EvPixbufCache *pixbuf_cache, if (!job_info->points_set) return NULL; - /* Create new render context if needed (selection + fast scrolling) */ - if (job_info->rc == NULL) { - EvPage *ev_page; - ev_page = ev_document_get_page (pixbuf_cache->document, page); - job_info->rc = ev_render_context_new (ev_page, 0, scale); - g_object_unref (ev_page); - } - - /* Update the rc */ - ev_render_context_set_scale (job_info->rc, scale); - /* If we have a running job, we just return what we have under the * assumption that it'll be updated later and we can scale it as need * be */ @@ -832,6 +953,8 @@ ev_pixbuf_cache_get_selection_surface (EvPixbufCache *pixbuf_cache, if (ev_rect_cmp (&(job_info->target_points), &(job_info->selection_points))) { EvRectangle *old_points; GdkColor *text, *base; + EvRenderContext *rc; + EvPage *ev_page; /* we need to get a new selection pixbuf */ ev_document_doc_mutex_lock (); @@ -843,12 +966,15 @@ ev_pixbuf_cache_get_selection_surface (EvPixbufCache *pixbuf_cache, old_points = &(job_info->selection_points); } + ev_page = ev_document_get_page (pixbuf_cache->document, page); + rc = ev_render_context_new (ev_page, 0, scale); + g_object_unref (ev_page); + if (job_info->selection_region) - gdk_region_destroy (job_info->selection_region); + cairo_region_destroy (job_info->selection_region); job_info->selection_region = ev_selection_get_selection_region (EV_SELECTION (pixbuf_cache->document), - job_info->rc, - job_info->selection_style, + rc, job_info->selection_style, &(job_info->target_points)); gtk_widget_ensure_style (pixbuf_cache->view); @@ -856,12 +982,13 @@ ev_pixbuf_cache_get_selection_surface (EvPixbufCache *pixbuf_cache, get_selection_colors (pixbuf_cache->view, &text, &base); ev_selection_render_selection (EV_SELECTION (pixbuf_cache->document), - job_info->rc, &(job_info->selection), + rc, &(job_info->selection), &(job_info->target_points), old_points, job_info->selection_style, text, base); job_info->selection_points = job_info->target_points; + g_object_unref (rc); ev_document_doc_mutex_unlock (); } if (region) @@ -1000,7 +1127,7 @@ ev_pixbuf_cache_get_selection_list (EvPixbufCache *pixbuf_cache) selection->page = page; selection->rect = pixbuf_cache->prev_job[i].selection_points; if (pixbuf_cache->prev_job[i].selection_region) - selection->covered_region = gdk_region_copy (pixbuf_cache->prev_job[i].selection_region); + selection->covered_region = cairo_region_reference (pixbuf_cache->prev_job[i].selection_region); retval = g_list_append (retval, selection); } @@ -1014,7 +1141,7 @@ ev_pixbuf_cache_get_selection_list (EvPixbufCache *pixbuf_cache) selection->page = page; selection->rect = pixbuf_cache->job_list[i].selection_points; if (pixbuf_cache->job_list[i].selection_region) - selection->covered_region = gdk_region_copy (pixbuf_cache->job_list[i].selection_region); + selection->covered_region = cairo_region_reference (pixbuf_cache->job_list[i].selection_region); retval = g_list_append (retval, selection); } @@ -1030,7 +1157,7 @@ ev_pixbuf_cache_get_selection_list (EvPixbufCache *pixbuf_cache) selection->page = page; selection->rect = pixbuf_cache->next_job[i].selection_points; if (pixbuf_cache->next_job[i].selection_region) - selection->covered_region = gdk_region_copy (pixbuf_cache->next_job[i].selection_region); + selection->covered_region = cairo_region_reference (pixbuf_cache->next_job[i].selection_region); retval = g_list_append (retval, selection); } @@ -1041,11 +1168,11 @@ ev_pixbuf_cache_get_selection_list (EvPixbufCache *pixbuf_cache) } void -ev_pixbuf_cache_reload_page (EvPixbufCache *pixbuf_cache, - GdkRegion *region, - gint page, - gint rotation, - gdouble scale) +ev_pixbuf_cache_reload_page (EvPixbufCache *pixbuf_cache, + cairo_region_t *region, + gint page, + gint rotation, + gdouble scale) { CacheJobInfo *job_info; gint width, height;