]> www.fi.muni.cz Git - evince.git/blobdiff - libview/ev-pixbuf-cache.c
[dualscreen] fix crash on ctrl+w and fix control window closing
[evince.git] / libview / ev-pixbuf-cache.c
index df06705c51b2360e709e2373f53e14dc9498ee0a..98719a36e1ad213d9081d160c4a1b8cfbe7d04c5 100644 (file)
@@ -1,29 +1,19 @@
 #include <config.h>
 #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;
-       GList *link_mapping;
-       GList *image_mapping;
-       GList *form_field_mapping;
-       GList *annots_mapping;
-       GdkRegion *text_mapping;
-       
+
        /* Selection data. 
         * Selection_points are the coordinates encapsulated in selection.
         * target_points is the target selection size. */
@@ -33,7 +23,7 @@ typedef struct _CacheJobInfo
        gboolean         points_set;
        
        cairo_surface_t *selection;
-       GdkRegion *selection_region;
+       cairo_region_t  *selection_region;
 } CacheJobInfo;
 
 struct _EvPixbufCache
@@ -43,14 +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;
@@ -76,18 +72,10 @@ static void          ev_pixbuf_cache_init       (EvPixbufCache      *pixbuf_cach
 static void          ev_pixbuf_cache_class_init (EvPixbufCacheClass *pixbuf_cache);
 static void          ev_pixbuf_cache_finalize   (GObject            *object);
 static void          ev_pixbuf_cache_dispose    (GObject            *object);
-static void          job_page_ready_cb          (EvJob              *job,
-                                                EvPixbufCache      *pixbuf_cache);
 static void          job_finished_cb            (EvJob              *job,
                                                 EvPixbufCache      *pixbuf_cache);
 static CacheJobInfo *find_job_cache             (EvPixbufCache      *pixbuf_cache,
                                                 int                 page);
-static void          copy_job_to_job_info       (EvJobRender        *job_render,
-                                                CacheJobInfo       *job_info,
-                                                EvPixbufCache      *pixbuf_cache);
-static void          copy_job_page_and_selection_to_job_info (EvJobRender        *job_render,
-                                                             CacheJobInfo       *job_info,
-                                                             EvPixbufCache      *pixbuf_cache);
 static gboolean      new_selection_surface_needed(EvPixbufCache      *pixbuf_cache,
                                                  CacheJobInfo       *job_info,
                                                  gint                page,
@@ -102,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
@@ -144,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);
 }
@@ -157,10 +156,8 @@ dispose_cache_job_info (CacheJobInfo *job_info,
 {
        if (job_info == NULL)
                return;
+
        if (job_info->job) {
-               g_signal_handlers_disconnect_by_func (job_info->job,
-                                                     G_CALLBACK (job_page_ready_cb),
-                                                     data);
                g_signal_handlers_disconnect_by_func (job_info->job,
                                                      G_CALLBACK (job_finished_cb),
                                                      data);
@@ -173,41 +170,17 @@ 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->link_mapping) {
-               ev_mapping_list_free (job_info->link_mapping, g_object_unref);
-               job_info->link_mapping = NULL;
-       }
-       if (job_info->image_mapping) {
-               ev_mapping_list_free (job_info->image_mapping, g_object_unref);
-               job_info->image_mapping = NULL;
-       }
-       if (job_info->form_field_mapping) {
-               ev_mapping_list_free (job_info->form_field_mapping, g_object_unref);
-               job_info->form_field_mapping = NULL;
-       }
-       if (job_info->annots_mapping) {
-               ev_mapping_list_free (job_info->annots_mapping, g_object_unref);
-               job_info->annots_mapping = NULL;
-       }
-       if (job_info->text_mapping) {
-               gdk_region_destroy (job_info->text_mapping);
-               job_info->text_mapping = NULL;
-       }
        if (job_info->selection) {
                cairo_surface_destroy (job_info->selection);
                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;
 }
@@ -234,37 +207,75 @@ 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
-job_page_ready_cb (EvJob         *job,
-                  EvPixbufCache *pixbuf_cache)
+copy_job_to_job_info (EvJobRender   *job_render,
+                     CacheJobInfo  *job_info,
+                     EvPixbufCache *pixbuf_cache)
 {
-       CacheJobInfo *job_info;
-       EvJobRender *job_render = EV_JOB_RENDER (job);
+       if (job_info->surface) {
+               cairo_surface_destroy (job_info->surface);
+       }
+       job_info->surface = cairo_surface_reference (job_render->surface);
+       if (pixbuf_cache->inverted_colors) {
+               ev_document_misc_invert_surface (job_info->surface);
+       }
 
-       /* If the job is outside of our interest, we silently discard it */
-       if ((job_render->page < (pixbuf_cache->start_page - pixbuf_cache->preload_cache_size)) ||
-           (job_render->page > (pixbuf_cache->end_page + pixbuf_cache->preload_cache_size))) {
-               g_object_unref (job);
-               return;
+       job_info->points_set = FALSE;
+       if (job_render->include_selection) {
+               if (job_info->selection) {
+                       cairo_surface_destroy (job_info->selection);
+                       job_info->selection = NULL;
+               }
+               if (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 = 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;
        }
 
-       job_info = find_job_cache (pixbuf_cache, job_render->page);
+       if (job_info->job) {
+               g_signal_handlers_disconnect_by_func (job_info->job,
+                                                     G_CALLBACK (job_finished_cb),
+                                                     pixbuf_cache);
+               ev_job_cancel (job_info->job);
+               g_object_unref (job_info->job);
+               job_info->job = NULL;
+       }
 
-       copy_job_page_and_selection_to_job_info (job_render, job_info, pixbuf_cache);
-       g_signal_emit (pixbuf_cache, signals[JOB_FINISHED], 0, job_info->region);
+       job_info->page_ready = TRUE;
 }
 
 static void
@@ -282,7 +293,9 @@ job_finished_cb (EvJob         *job,
        }
 
        job_info = find_job_cache (pixbuf_cache, job_render->page);
+
        copy_job_to_job_info (job_render, job_info, pixbuf_cache);
+       g_signal_emit (pixbuf_cache, signals[JOB_FINISHED], 0, job_info->region);
 }
 
 /* This checks a job to see if the job would generate the right sized pixbuf
@@ -309,9 +322,6 @@ check_job_size_and_unref (EvPixbufCache *pixbuf_cache,
            height == EV_JOB_RENDER (job_info->job)->target_height)
                return;
 
-       g_signal_handlers_disconnect_by_func (job_info->job,
-                                             G_CALLBACK (job_page_ready_cb),
-                                             pixbuf_cache);
        g_signal_handlers_disconnect_by_func (job_info->job,
                                              G_CALLBACK (job_finished_cb),
                                              pixbuf_cache);
@@ -330,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)
@@ -338,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 {
@@ -371,33 +382,111 @@ move_one_job (CacheJobInfo  *job_info,
        job_info->job = NULL;
        job_info->region = NULL;
        job_info->surface = NULL;
-       job_info->link_mapping = NULL;
-       job_info->image_mapping = NULL;
-       job_info->form_field_mapping = NULL;
-       job_info->annots_mapping = NULL;
 
        if (new_priority != priority && target_page->job) {
                ev_job_scheduler_update_job (target_page->job, new_priority);
        }
 }
 
+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. */
@@ -411,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 ++;
        }
@@ -432,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;
@@ -449,105 +553,6 @@ ev_pixbuf_cache_update_range (EvPixbufCache *pixbuf_cache,
        pixbuf_cache->end_page = end_page;
 }
 
-static void
-copy_job_page_and_selection_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);
-       }
-       job_info->surface = cairo_surface_reference (job_render->surface);
-
-       job_info->points_set = FALSE;
-       if (job_render->flags & EV_RENDER_INCLUDE_SELECTION) {
-               if (job_info->selection) {
-                       cairo_surface_destroy (job_info->selection);
-                       job_info->selection = NULL;
-               }
-               if (job_info->selection_region) {
-                       gdk_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 = cairo_surface_reference (job_render->selection);
-               g_assert (job_info->selection_points.x1 >= 0);
-               job_info->points_set = TRUE;
-       }
-
-       if (job_info->job) {
-               g_signal_handlers_disconnect_by_func (job_info->job,
-                                                     G_CALLBACK (job_page_ready_cb),
-                                                     pixbuf_cache);
-       }
-
-       job_info->page_ready = TRUE;
-}
-
-static void
-copy_job_to_job_info (EvJobRender   *job_render,
-                     CacheJobInfo  *job_info,
-                     EvPixbufCache *pixbuf_cache)
-{
-       if (!job_info->page_ready) {
-               g_signal_emit (pixbuf_cache, signals[JOB_FINISHED], 0, job_info->region);
-               copy_job_page_and_selection_to_job_info (job_render,
-                                                        job_info,
-                                                        pixbuf_cache);
-       }
-       
-       if (job_render->flags & EV_RENDER_INCLUDE_LINKS) {
-               if (job_info->link_mapping)
-                       ev_mapping_list_free (job_info->link_mapping, g_object_unref);
-               job_info->link_mapping = job_render->link_mapping;
-       }
-
-       if (job_render->flags & EV_RENDER_INCLUDE_IMAGES) {
-               if (job_info->image_mapping)
-                       ev_mapping_list_free (job_info->image_mapping, g_object_unref);
-               job_info->image_mapping = job_render->image_mapping;
-       }
-
-       if (job_render->flags & EV_RENDER_INCLUDE_FORMS) {
-               if (job_info->form_field_mapping)
-                       ev_mapping_list_free (job_info->form_field_mapping, g_object_unref);
-               job_info->form_field_mapping = job_render->form_field_mapping;
-       }
-
-       if (job_render->flags & EV_RENDER_INCLUDE_ANNOTS) {
-               if (job_info->annots_mapping)
-                       ev_mapping_list_free (job_info->annots_mapping, g_object_unref);
-               job_info->annots_mapping = job_render->annots_mapping;
-       }
-
-       if (job_render->flags & EV_RENDER_INCLUDE_TEXT) {
-               if (job_info->text_mapping)
-                       gdk_region_destroy (job_info->text_mapping);
-               job_info->text_mapping = job_render->text_mapping;
-       }
-
-       if (job_info->job) {
-               g_signal_handlers_disconnect_by_func (job_info->job,
-                                                     G_CALLBACK (job_finished_cb),
-                                                     pixbuf_cache);
-               ev_job_cancel (job_info->job);
-               g_object_unref (job_info->job);
-               job_info->job = NULL;
-       }
-}
-
 static CacheJobInfo *
 find_job_cache (EvPixbufCache *pixbuf_cache,
                int            page)
@@ -597,67 +602,58 @@ ev_pixbuf_cache_clear_job_sizes (EvPixbufCache *pixbuf_cache,
 }
 
 static void
-get_selection_colors (GtkWidget *widget, GdkColor **text, GdkColor **base)
+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];
-    }
+       GtkStyleContext *context = gtk_widget_get_style_context (widget);
+        GtkStateFlags    state = 0;
+        GdkRGBA          fg, bg;
+
+        state |= gtk_widget_has_focus (widget) ? GTK_STATE_FLAG_SELECTED : GTK_STATE_FLAG_ACTIVE;
+
+        gtk_style_context_get_color (context, state, &fg);
+        text->pixel = 0;
+        text->red = CLAMP ((guint) (fg.red * 65535), 0, 65535);
+        text->green = CLAMP ((guint) (fg.green * 65535), 0, 65535);
+        text->blue = CLAMP ((guint) (fg.blue * 65535), 0, 65535);
+
+        gtk_style_context_get_background_color (context, state, &bg);
+        base->pixel = 0;
+        base->red = CLAMP ((guint) (bg.red * 65535), 0, 65535);
+        base->green = CLAMP ((guint) (bg.green * 65535), 0, 65535);
+        base->blue = CLAMP ((guint) (bg.blue * 65535), 0, 65535);
 }
 
 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)
 {
-       EvRenderFlags flags = 0;
-
        job_info->page_ready = FALSE;
-       
+
        if (job_info->region)
-               gdk_region_destroy (job_info->region);
-       job_info->region = region ? gdk_region_copy (region) : NULL;
-
-       /* Figure out what else we need for this job */
-       if (job_info->link_mapping == NULL)
-               flags |= EV_RENDER_INCLUDE_LINKS;
-       if (job_info->image_mapping == NULL)
-               flags |= EV_RENDER_INCLUDE_IMAGES;
-       if (job_info->form_field_mapping == NULL)
-               flags |= EV_RENDER_INCLUDE_FORMS;
-       if (job_info->annots_mapping == NULL)
-               flags |= EV_RENDER_INCLUDE_ANNOTS;
-       if (job_info->text_mapping == NULL)
-               flags |= EV_RENDER_INCLUDE_TEXT;
+               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,
-                                          width, height,
-                                          flags);
-       
+                                          width, height);
+
        if (new_selection_surface_needed (pixbuf_cache, job_info, page, scale)) {
-               GdkColor *text, *base;
+               GdkColor text, base;
 
-               gtk_widget_ensure_style (pixbuf_cache->view);
                get_selection_colors (pixbuf_cache->view, &text, &base);
                ev_job_render_set_selection_info (EV_JOB_RENDER (job_info->job), 
                                                  &(job_info->target_points),
                                                  job_info->selection_style,
-                                                 text, base);
+                                                 &text, &base);
        }
 
-       g_signal_connect (job_info->job, "page-ready",
-                         G_CALLBACK (job_page_ready_cb),
-                         pixbuf_cache);
        g_signal_connect (job_info->job, "finished",
                          G_CALLBACK (job_finished_cb),
                          pixbuf_cache);
@@ -686,6 +682,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);
@@ -733,10 +742,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));
@@ -745,7 +755,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. */
@@ -759,112 +769,59 @@ ev_pixbuf_cache_set_page_range (EvPixbufCache  *pixbuf_cache,
        ev_pixbuf_cache_add_jobs_if_needed (pixbuf_cache, rotation, scale);
 }
 
-cairo_surface_t *
-ev_pixbuf_cache_get_surface (EvPixbufCache *pixbuf_cache,
-                            gint           page)
+void
+ev_pixbuf_cache_set_inverted_colors (EvPixbufCache *pixbuf_cache,
+                                    gboolean       inverted_colors)
 {
-       CacheJobInfo *job_info;
-
-       job_info = find_job_cache (pixbuf_cache, page);
-       if (job_info == NULL)
-               return NULL;
+       gint i;
 
-       if (job_info->page_ready)
-               return job_info->surface;
-       
-       /* We don't need to wait for the idle to handle the callback */
-       if (job_info->job &&
-           EV_JOB_RENDER (job_info->job)->page_ready) {
-               copy_job_page_and_selection_to_job_info (EV_JOB_RENDER (job_info->job), job_info, pixbuf_cache);
-               g_signal_emit (pixbuf_cache, signals[JOB_FINISHED], 0, job_info->region);
-       }
+       if (pixbuf_cache->inverted_colors == inverted_colors)
+               return;
 
-       return job_info->surface;
-}
+       pixbuf_cache->inverted_colors = inverted_colors;
 
-GList *
-ev_pixbuf_cache_get_link_mapping (EvPixbufCache *pixbuf_cache,
-                                 gint           page)
-{
-       CacheJobInfo *job_info;
+       for (i = 0; i < pixbuf_cache->preload_cache_size; i++) {
+               CacheJobInfo *job_info;
 
-       job_info = find_job_cache (pixbuf_cache, page);
-       if (job_info == NULL)
-               return NULL;
+               job_info = pixbuf_cache->prev_job + i;
+               if (job_info && job_info->surface)
+                       ev_document_misc_invert_surface (job_info->surface);
 
-       /* We don't need to wait for the idle to handle the callback */
-       if (job_info->job &&
-           EV_JOB (job_info->job)->finished) {
-               copy_job_to_job_info (EV_JOB_RENDER (job_info->job), job_info, pixbuf_cache);
+               job_info = pixbuf_cache->next_job + i;
+               if (job_info && job_info->surface)
+                       ev_document_misc_invert_surface (job_info->surface);
        }
 
-       return job_info->link_mapping;
-}
-
-GList *
-ev_pixbuf_cache_get_image_mapping (EvPixbufCache *pixbuf_cache,
-                                  gint           page)
-{
-       CacheJobInfo *job_info;
-
-       if (!EV_IS_DOCUMENT_IMAGES (pixbuf_cache->document))
-               return NULL;
-       
-       job_info = find_job_cache (pixbuf_cache, page);
-       if (job_info == NULL)
-               return NULL;
+       for (i = 0; i < PAGE_CACHE_LEN (pixbuf_cache); i++) {
+               CacheJobInfo *job_info;
 
-       /* We don't need to wait for the idle to handle the callback */
-       if (job_info->job &&
-           EV_JOB (job_info->job)->finished) {
-               copy_job_to_job_info (EV_JOB_RENDER (job_info->job), job_info, pixbuf_cache);
+               job_info = pixbuf_cache->job_list + i;
+               if (job_info && job_info->surface)
+                       ev_document_misc_invert_surface (job_info->surface);
        }
-
-       return job_info->image_mapping;
 }
 
-GList *
-ev_pixbuf_cache_get_form_field_mapping (EvPixbufCache *pixbuf_cache,
-                                       gint           page)
+cairo_surface_t *
+ev_pixbuf_cache_get_surface (EvPixbufCache *pixbuf_cache,
+                            gint           page)
 {
        CacheJobInfo *job_info;
 
-       if (!EV_IS_DOCUMENT_FORMS (pixbuf_cache->document))
-               return NULL;
-       
        job_info = find_job_cache (pixbuf_cache, page);
        if (job_info == NULL)
                return NULL;
 
-       /* We don't need to wait for the idle to handle the callback */
-       if (job_info->job &&
-          EV_JOB (job_info->job)->finished) {
-               copy_job_to_job_info (EV_JOB_RENDER(job_info->job), job_info, pixbuf_cache);
-       }
-       
-       return job_info->form_field_mapping;
-}
-
-GList *
-ev_pixbuf_cache_get_annots_mapping (EvPixbufCache *pixbuf_cache,
-                                   gint           page)
-{
-       CacheJobInfo *job_info;
-
-       if (!EV_IS_DOCUMENT_ANNOTATIONS (pixbuf_cache->document))
-               return NULL;
-
-       job_info = find_job_cache (pixbuf_cache, page);
-       if (job_info == NULL)
-               return NULL;
+       if (job_info->page_ready)
+               return job_info->surface;
 
        /* We don't need to wait for the idle to handle the callback */
        if (job_info->job &&
-          EV_JOB (job_info->job)->finished) {
+           EV_JOB_RENDER (job_info->job)->page_ready) {
                copy_job_to_job_info (EV_JOB_RENDER (job_info->job), job_info, pixbuf_cache);
+               g_signal_emit (pixbuf_cache, signals[JOB_FINISHED], 0, job_info->region);
        }
 
-       return job_info->annots_mapping;
+       return job_info->surface;
 }
 
 static gboolean
@@ -873,12 +830,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);
@@ -908,25 +865,6 @@ clear_selection_if_needed (EvPixbufCache *pixbuf_cache,
        }
 }
 
-GdkRegion *
-ev_pixbuf_cache_get_text_mapping (EvPixbufCache *pixbuf_cache,
-                                 gint           page)
-{
-       CacheJobInfo *job_info;
-
-       job_info = find_job_cache (pixbuf_cache, page);
-       if (job_info == NULL)
-               return NULL;
-
-       /* We don't need to wait for the idle to handle the callback */
-       if (job_info->job &&
-           EV_JOB (job_info->job)->finished) {
-               copy_job_to_job_info (EV_JOB_RENDER (job_info->job), job_info, pixbuf_cache);
-       }
-       
-       return job_info->text_mapping;
-}
-
 /* Clears the cache of jobs and pixbufs.
  */
 void
@@ -934,6 +872,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);
@@ -950,6 +891,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;
@@ -979,10 +923,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;
 
@@ -998,21 +942,10 @@ 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 */
-       if (job_info->job && (EV_JOB_RENDER (job_info->job)->flags & EV_RENDER_INCLUDE_SELECTION))
+       if (job_info->job && EV_JOB_RENDER (job_info->job)->include_selection)
                return job_info->selection;
 
        /* Now, lets see if we need to resize the image.  If we do, we clear the
@@ -1026,7 +959,9 @@ 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;
+               GdkColor text, base;
+               EvRenderContext *rc;
+               EvPage *ev_page;
 
                /* we need to get a new selection pixbuf */
                ev_document_doc_mutex_lock ();
@@ -1038,25 +973,27 @@ 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);
-
                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);
+                                              &text, &base);
                job_info->selection_points = job_info->target_points;
+               g_object_unref (rc);
                ev_document_doc_mutex_unlock ();
        }
        if (region)
@@ -1103,6 +1040,9 @@ ev_pixbuf_cache_set_selection_list (EvPixbufCache *pixbuf_cache,
        if (!EV_IS_SELECTION (pixbuf_cache->document))
                return;
 
+        if (pixbuf_cache->start_page == -1 || pixbuf_cache->end_page == -1)
+                return;
+
        /* We check each area to see what needs updating, and what needs freeing; */
        page = pixbuf_cache->start_page - pixbuf_cache->preload_cache_size;
        for (i = 0; i < pixbuf_cache->preload_cache_size; i++) {
@@ -1182,6 +1122,9 @@ ev_pixbuf_cache_get_selection_list (EvPixbufCache *pixbuf_cache)
 
        g_return_val_if_fail (EV_IS_PIXBUF_CACHE (pixbuf_cache), NULL);
 
+        if (pixbuf_cache->start_page == -1 || pixbuf_cache->end_page == -1)
+                return NULL;
+
        /* We check each area to see what needs updating, and what needs freeing; */
        page = pixbuf_cache->start_page - pixbuf_cache->preload_cache_size;
        for (i = 0; i < pixbuf_cache->preload_cache_size; i++) {
@@ -1195,7 +1138,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);
                }
                
@@ -1209,7 +1152,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);
                }
                
@@ -1225,7 +1168,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);
                }
                
@@ -1236,11 +1179,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;