]> www.fi.muni.cz Git - evince.git/blob - libview/ev-pixbuf-cache.c
[dualscreen] fix crash on ctrl+w and fix control window closing
[evince.git] / libview / ev-pixbuf-cache.c
1 #include <config.h>
2 #include "ev-pixbuf-cache.h"
3 #include "ev-job-scheduler.h"
4 #include "ev-view-private.h"
5
6 typedef struct _CacheJobInfo
7 {
8         EvJob *job;
9         gboolean page_ready;
10
11         /* Region of the page that needs to be drawn */
12         cairo_region_t  *region;
13
14         /* Data we get from rendering */
15         cairo_surface_t *surface;
16
17         /* Selection data. 
18          * Selection_points are the coordinates encapsulated in selection.
19          * target_points is the target selection size. */
20         EvRectangle      selection_points;
21         EvRectangle      target_points;
22         EvSelectionStyle selection_style;
23         gboolean         points_set;
24         
25         cairo_surface_t *selection;
26         cairo_region_t  *selection_region;
27 } CacheJobInfo;
28
29 struct _EvPixbufCache
30 {
31         GObject parent;
32
33         /* We keep a link to our containing view just for style information. */
34         GtkWidget *view;
35         EvDocument *document;
36         EvDocumentModel *model;
37         int start_page;
38         int end_page;
39         gboolean inverted_colors;
40
41         gsize max_size;
42
43         /* preload_cache_size is the number of pages prior to the current
44          * visible area that we cache.  It's normally 1, but could be 2 in the
45          * case of twin pages.
46          */
47         int preload_cache_size;
48         guint job_list_len;
49
50         CacheJobInfo *prev_job;
51         CacheJobInfo *job_list;
52         CacheJobInfo *next_job;
53 };
54
55 struct _EvPixbufCacheClass
56 {
57         GObjectClass parent_class;
58
59         void (* job_finished) (EvPixbufCache *pixbuf_cache);
60 };
61
62
63 enum
64 {
65         JOB_FINISHED,
66         N_SIGNALS,
67 };
68
69 static guint signals[N_SIGNALS] = {0, };
70
71 static void          ev_pixbuf_cache_init       (EvPixbufCache      *pixbuf_cache);
72 static void          ev_pixbuf_cache_class_init (EvPixbufCacheClass *pixbuf_cache);
73 static void          ev_pixbuf_cache_finalize   (GObject            *object);
74 static void          ev_pixbuf_cache_dispose    (GObject            *object);
75 static void          job_finished_cb            (EvJob              *job,
76                                                  EvPixbufCache      *pixbuf_cache);
77 static CacheJobInfo *find_job_cache             (EvPixbufCache      *pixbuf_cache,
78                                                  int                 page);
79 static gboolean      new_selection_surface_needed(EvPixbufCache      *pixbuf_cache,
80                                                   CacheJobInfo       *job_info,
81                                                   gint                page,
82                                                   gfloat              scale);
83
84
85 /* These are used for iterating through the prev and next arrays */
86 #define FIRST_VISIBLE_PREV(pixbuf_cache) \
87         (MAX (0, pixbuf_cache->preload_cache_size - pixbuf_cache->start_page))
88 #define VISIBLE_NEXT_LEN(pixbuf_cache) \
89         (MIN(pixbuf_cache->preload_cache_size, ev_document_get_n_pages (pixbuf_cache->document) - (1 + pixbuf_cache->end_page)))
90 #define PAGE_CACHE_LEN(pixbuf_cache) \
91         ((pixbuf_cache->end_page - pixbuf_cache->start_page) + 1)
92
93 #define MAX_PRELOADED_PAGES 3
94
95 G_DEFINE_TYPE (EvPixbufCache, ev_pixbuf_cache, G_TYPE_OBJECT)
96
97 static void
98 ev_pixbuf_cache_init (EvPixbufCache *pixbuf_cache)
99 {
100         pixbuf_cache->start_page = -1;
101         pixbuf_cache->end_page = -1;
102 }
103
104 static void
105 ev_pixbuf_cache_class_init (EvPixbufCacheClass *class)
106 {
107         GObjectClass *object_class;
108
109         object_class = G_OBJECT_CLASS (class);
110
111         object_class->finalize = ev_pixbuf_cache_finalize;
112         object_class->dispose = ev_pixbuf_cache_dispose;
113
114         signals[JOB_FINISHED] =
115                 g_signal_new ("job-finished",
116                               G_OBJECT_CLASS_TYPE (object_class),
117                               G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
118                               G_STRUCT_OFFSET (EvPixbufCacheClass, job_finished),
119                               NULL, NULL,
120                               g_cclosure_marshal_VOID__POINTER,
121                               G_TYPE_NONE, 1,
122                               G_TYPE_POINTER);
123 }
124
125 static void
126 ev_pixbuf_cache_finalize (GObject *object)
127 {
128         EvPixbufCache *pixbuf_cache;
129
130         pixbuf_cache = EV_PIXBUF_CACHE (object);
131
132         if (pixbuf_cache->job_list) {
133                 g_slice_free1 (sizeof (CacheJobInfo) * pixbuf_cache->job_list_len,
134                                pixbuf_cache->job_list);
135                 pixbuf_cache->job_list = NULL;
136         }
137         if (pixbuf_cache->prev_job) {
138                 g_slice_free1 (sizeof (CacheJobInfo) * pixbuf_cache->preload_cache_size,
139                                pixbuf_cache->prev_job);
140                 pixbuf_cache->prev_job = NULL;
141         }
142         if (pixbuf_cache->next_job) {
143                 g_slice_free1 (sizeof (CacheJobInfo) * pixbuf_cache->preload_cache_size,
144                                pixbuf_cache->next_job);
145                 pixbuf_cache->next_job = NULL;
146         }
147
148         g_object_unref (pixbuf_cache->model);
149
150         G_OBJECT_CLASS (ev_pixbuf_cache_parent_class)->finalize (object);
151 }
152
153 static void
154 dispose_cache_job_info (CacheJobInfo *job_info,
155                         gpointer      data)
156 {
157         if (job_info == NULL)
158                 return;
159
160         if (job_info->job) {
161                 g_signal_handlers_disconnect_by_func (job_info->job,
162                                                       G_CALLBACK (job_finished_cb),
163                                                       data);
164                 ev_job_cancel (job_info->job);
165                 g_object_unref (job_info->job);
166                 job_info->job = NULL;
167         }
168         if (job_info->surface) {
169                 cairo_surface_destroy (job_info->surface);
170                 job_info->surface = NULL;
171         }
172         if (job_info->region) {
173                 cairo_region_destroy (job_info->region);
174                 job_info->region = NULL;
175         }
176         if (job_info->selection) {
177                 cairo_surface_destroy (job_info->selection);
178                 job_info->selection = NULL;
179         }
180         if (job_info->selection_region) {
181                 cairo_region_destroy (job_info->selection_region);
182                 job_info->selection_region = NULL;
183         }
184
185         job_info->points_set = FALSE;
186 }
187
188 static void
189 ev_pixbuf_cache_dispose (GObject *object)
190 {
191         EvPixbufCache *pixbuf_cache;
192         int i;
193
194         pixbuf_cache = EV_PIXBUF_CACHE (object);
195
196         for (i = 0; i < pixbuf_cache->preload_cache_size; i++) {
197                 dispose_cache_job_info (pixbuf_cache->prev_job + i, pixbuf_cache);
198                 dispose_cache_job_info (pixbuf_cache->next_job + i, pixbuf_cache);
199         }
200
201         for (i = 0; i < PAGE_CACHE_LEN (pixbuf_cache); i++) {
202                 dispose_cache_job_info (pixbuf_cache->job_list + i, pixbuf_cache);
203         }
204
205         G_OBJECT_CLASS (ev_pixbuf_cache_parent_class)->dispose (object);
206 }
207
208
209 EvPixbufCache *
210 ev_pixbuf_cache_new (GtkWidget       *view,
211                      EvDocumentModel *model,
212                      gsize            max_size)
213 {
214         EvPixbufCache *pixbuf_cache;
215
216         pixbuf_cache = (EvPixbufCache *) g_object_new (EV_TYPE_PIXBUF_CACHE, NULL);
217         /* This is a backlink, so we don't ref this */ 
218         pixbuf_cache->view = view;
219         pixbuf_cache->model = g_object_ref (model);
220         pixbuf_cache->document = ev_document_model_get_document (model);
221         pixbuf_cache->max_size = max_size;
222
223         return pixbuf_cache;
224 }
225
226 void
227 ev_pixbuf_cache_set_max_size (EvPixbufCache *pixbuf_cache,
228                               gsize          max_size)
229 {
230         if (pixbuf_cache->max_size == max_size)
231                 return;
232
233         if (pixbuf_cache->max_size > max_size)
234                 ev_pixbuf_cache_clear (pixbuf_cache);
235         pixbuf_cache->max_size = max_size;
236 }
237
238 static void
239 copy_job_to_job_info (EvJobRender   *job_render,
240                       CacheJobInfo  *job_info,
241                       EvPixbufCache *pixbuf_cache)
242 {
243         if (job_info->surface) {
244                 cairo_surface_destroy (job_info->surface);
245         }
246         job_info->surface = cairo_surface_reference (job_render->surface);
247         if (pixbuf_cache->inverted_colors) {
248                 ev_document_misc_invert_surface (job_info->surface);
249         }
250
251         job_info->points_set = FALSE;
252         if (job_render->include_selection) {
253                 if (job_info->selection) {
254                         cairo_surface_destroy (job_info->selection);
255                         job_info->selection = NULL;
256                 }
257                 if (job_info->selection_region) {
258                         cairo_region_destroy (job_info->selection_region);
259                         job_info->selection_region = NULL;
260                 }
261
262                 job_info->selection_points = job_render->selection_points;
263                 job_info->selection_region = cairo_region_reference (job_render->selection_region);
264                 job_info->selection = cairo_surface_reference (job_render->selection);
265                 g_assert (job_info->selection_points.x1 >= 0);
266                 job_info->points_set = TRUE;
267         }
268
269         if (job_info->job) {
270                 g_signal_handlers_disconnect_by_func (job_info->job,
271                                                       G_CALLBACK (job_finished_cb),
272                                                       pixbuf_cache);
273                 ev_job_cancel (job_info->job);
274                 g_object_unref (job_info->job);
275                 job_info->job = NULL;
276         }
277
278         job_info->page_ready = TRUE;
279 }
280
281 static void
282 job_finished_cb (EvJob         *job,
283                  EvPixbufCache *pixbuf_cache)
284 {
285         CacheJobInfo *job_info;
286         EvJobRender *job_render = EV_JOB_RENDER (job);
287
288         /* If the job is outside of our interest, we silently discard it */
289         if ((job_render->page < (pixbuf_cache->start_page - pixbuf_cache->preload_cache_size)) ||
290             (job_render->page > (pixbuf_cache->end_page + pixbuf_cache->preload_cache_size))) {
291                 g_object_unref (job);
292                 return;
293         }
294
295         job_info = find_job_cache (pixbuf_cache, job_render->page);
296
297         copy_job_to_job_info (job_render, job_info, pixbuf_cache);
298         g_signal_emit (pixbuf_cache, signals[JOB_FINISHED], 0, job_info->region);
299 }
300
301 /* This checks a job to see if the job would generate the right sized pixbuf
302  * given a scale.  If it won't, it removes the job and clears it to NULL.
303  */
304 static void
305 check_job_size_and_unref (EvPixbufCache *pixbuf_cache,
306                           CacheJobInfo  *job_info,
307                           gfloat         scale)
308 {
309         gint width, height;
310
311         g_assert (job_info);
312
313         if (job_info->job == NULL)
314                 return;
315
316         _get_page_size_for_scale_and_rotation (job_info->job->document,
317                                                EV_JOB_RENDER (job_info->job)->page,
318                                                scale,
319                                                EV_JOB_RENDER (job_info->job)->rotation,
320                                                &width, &height);
321         if (width == EV_JOB_RENDER (job_info->job)->target_width &&
322             height == EV_JOB_RENDER (job_info->job)->target_height)
323                 return;
324
325         g_signal_handlers_disconnect_by_func (job_info->job,
326                                               G_CALLBACK (job_finished_cb),
327                                               pixbuf_cache);
328         ev_job_cancel (job_info->job);
329         g_object_unref (job_info->job);
330         job_info->job = NULL;
331 }
332
333 /* Do all function that copies a job from an older cache to it's position in the
334  * new cache.  It clears the old job if it doesn't have a place.
335  */
336 static void
337 move_one_job (CacheJobInfo  *job_info,
338               EvPixbufCache *pixbuf_cache,
339               int            page,
340               CacheJobInfo  *new_job_list,
341               CacheJobInfo  *new_prev_job,
342               CacheJobInfo  *new_next_job,
343               int            new_preload_cache_size,
344               int            start_page,
345               int            end_page,
346               gint           priority)
347 {
348         CacheJobInfo *target_page = NULL;
349         int page_offset;
350         gint new_priority;
351
352         if (page < (start_page - new_preload_cache_size) ||
353             page > (end_page + new_preload_cache_size)) {
354                 dispose_cache_job_info (job_info, pixbuf_cache);
355                 return;
356         }
357
358         /* find the target page to copy it over to. */
359         if (page < start_page) {
360                 page_offset = (page - (start_page - new_preload_cache_size));
361
362                 g_assert (page_offset >= 0 &&
363                           page_offset < new_preload_cache_size);
364                 target_page = new_prev_job + page_offset;
365                 new_priority = EV_JOB_PRIORITY_LOW;
366         } else if (page > end_page) {
367                 page_offset = (page - (end_page + 1));
368
369                 g_assert (page_offset >= 0 &&
370                           page_offset < new_preload_cache_size);
371                 target_page = new_next_job + page_offset;
372                 new_priority = EV_JOB_PRIORITY_LOW;
373         } else {
374                 page_offset = page - start_page;
375                 g_assert (page_offset >= 0 &&
376                           page_offset <= ((end_page - start_page) + 1));
377                 new_priority = EV_JOB_PRIORITY_URGENT;
378                 target_page = new_job_list + page_offset;
379         }
380
381         *target_page = *job_info;
382         job_info->job = NULL;
383         job_info->region = NULL;
384         job_info->surface = NULL;
385
386         if (new_priority != priority && target_page->job) {
387                 ev_job_scheduler_update_job (target_page->job, new_priority);
388         }
389 }
390
391 static gsize
392 ev_pixbuf_cache_get_page_size (EvPixbufCache *pixbuf_cache,
393                                gint           page_index,
394                                gdouble        scale,
395                                gint           rotation)
396 {
397         gint width, height;
398
399         _get_page_size_for_scale_and_rotation (pixbuf_cache->document,
400                                                page_index, scale, rotation,
401                                                &width, &height);
402         return height * cairo_format_stride_for_width (CAIRO_FORMAT_RGB24, width);
403 }
404
405 static gint
406 ev_pixbuf_cache_get_preload_size (EvPixbufCache *pixbuf_cache,
407                                   gint           start_page,
408                                   gint           end_page,
409                                   gdouble        scale,
410                                   gint           rotation)
411 {
412         gsize range_size = 0;
413         gint  new_preload_cache_size = 0;
414         gint  i;
415         guint n_pages = ev_document_get_n_pages (pixbuf_cache->document);
416
417         /* Get the size of the current range */
418         for (i = start_page; i <= end_page; i++) {
419                 range_size += ev_pixbuf_cache_get_page_size (pixbuf_cache, i, scale, rotation);
420         }
421
422         if (range_size >= pixbuf_cache->max_size)
423                 return new_preload_cache_size;
424
425         i = 1;
426         while (((start_page - i > 0) || (end_page + i < n_pages)) &&
427                new_preload_cache_size < MAX_PRELOADED_PAGES) {
428                 gsize    page_size;
429                 gboolean updated = FALSE;
430
431                 if (end_page + i < n_pages) {
432                         page_size = ev_pixbuf_cache_get_page_size (pixbuf_cache, end_page + i,
433                                                                    scale, rotation);
434                         if (page_size + range_size <= pixbuf_cache->max_size) {
435                                 range_size += page_size;
436                                 new_preload_cache_size++;
437                                 updated = TRUE;
438                         } else {
439                                 break;
440                         }
441                 }
442
443                 if (start_page - i > 0) {
444                         page_size = ev_pixbuf_cache_get_page_size (pixbuf_cache, start_page - i,
445                                                                    scale, rotation);
446                         if (page_size + range_size <= pixbuf_cache->max_size) {
447                                 range_size += page_size;
448                                 if (!updated)
449                                         new_preload_cache_size++;
450                         } else {
451                                 break;
452                         }
453                 }
454                 i++;
455         }
456
457         return new_preload_cache_size;
458 }
459
460 static void
461 ev_pixbuf_cache_update_range (EvPixbufCache *pixbuf_cache,
462                               gint           start_page,
463                               gint           end_page,
464                               guint          rotation,
465                               gdouble        scale)
466 {
467         CacheJobInfo *new_job_list;
468         CacheJobInfo *new_prev_job = NULL;
469         CacheJobInfo *new_next_job = NULL;
470         gint          new_preload_cache_size;
471         guint         new_job_list_len;
472         int           i, page;
473
474         new_preload_cache_size = ev_pixbuf_cache_get_preload_size (pixbuf_cache,
475                                                                    start_page,
476                                                                    end_page,
477                                                                    scale,
478                                                                    rotation);
479         if (pixbuf_cache->start_page == start_page &&
480             pixbuf_cache->end_page == end_page &&
481             pixbuf_cache->preload_cache_size == new_preload_cache_size)
482                 return;
483
484         new_job_list_len = (end_page - start_page) + 1;
485         new_job_list = g_slice_alloc0 (sizeof (CacheJobInfo) * new_job_list_len);
486         if (new_preload_cache_size > 0) {
487                 new_prev_job = g_slice_alloc0 (sizeof (CacheJobInfo) * new_preload_cache_size);
488                 new_next_job = g_slice_alloc0 (sizeof (CacheJobInfo) * new_preload_cache_size);
489         }
490
491         /* We go through each job in the old cache and either clear it or move
492          * it to a new location. */
493
494         /* Start with the prev cache. */
495         page = pixbuf_cache->start_page - pixbuf_cache->preload_cache_size;
496         for (i = 0; i < pixbuf_cache->preload_cache_size; i++) {
497                 if (page < 0) {
498                         dispose_cache_job_info (pixbuf_cache->prev_job + i, pixbuf_cache);
499                 } else {
500                         move_one_job (pixbuf_cache->prev_job + i,
501                                       pixbuf_cache, page,
502                                       new_job_list, new_prev_job, new_next_job,
503                                       new_preload_cache_size,
504                                       start_page, end_page, EV_JOB_PRIORITY_LOW);
505                 }
506                 page ++;
507         }
508
509         page = pixbuf_cache->start_page;
510         for (i = 0; i < PAGE_CACHE_LEN (pixbuf_cache) && page >= 0; i++) {
511                 move_one_job (pixbuf_cache->job_list + i,
512                               pixbuf_cache, page,
513                               new_job_list, new_prev_job, new_next_job,
514                               new_preload_cache_size,
515                               start_page, end_page, EV_JOB_PRIORITY_URGENT);
516                 page ++;
517         }
518
519         for (i = 0; i < pixbuf_cache->preload_cache_size; i++) {
520                 if (page >= ev_document_get_n_pages (pixbuf_cache->document)) {
521                         dispose_cache_job_info (pixbuf_cache->next_job + i, pixbuf_cache);
522                 } else {
523                         move_one_job (pixbuf_cache->next_job + i,
524                                       pixbuf_cache, page,
525                                       new_job_list, new_prev_job, new_next_job,
526                                       new_preload_cache_size,
527                                       start_page, end_page, EV_JOB_PRIORITY_LOW);
528                 }
529                 page ++;
530         }
531
532         if (pixbuf_cache->job_list) {
533                 g_slice_free1 (sizeof (CacheJobInfo) * pixbuf_cache->job_list_len,
534                                pixbuf_cache->job_list);
535         }
536         if (pixbuf_cache->prev_job) {
537                 g_slice_free1 (sizeof (CacheJobInfo) * pixbuf_cache->preload_cache_size,
538                                pixbuf_cache->prev_job);
539         }
540         if (pixbuf_cache->next_job) {
541                 g_slice_free1 (sizeof (CacheJobInfo) * pixbuf_cache->preload_cache_size,
542                                pixbuf_cache->next_job);
543         }
544
545         pixbuf_cache->preload_cache_size = new_preload_cache_size;
546         pixbuf_cache->job_list_len = new_job_list_len;
547
548         pixbuf_cache->job_list = new_job_list;
549         pixbuf_cache->prev_job = new_prev_job;
550         pixbuf_cache->next_job = new_next_job;
551
552         pixbuf_cache->start_page = start_page;
553         pixbuf_cache->end_page = end_page;
554 }
555
556 static CacheJobInfo *
557 find_job_cache (EvPixbufCache *pixbuf_cache,
558                 int            page)
559 {
560         int page_offset;
561
562         if (page < (pixbuf_cache->start_page - pixbuf_cache->preload_cache_size) ||
563             page > (pixbuf_cache->end_page + pixbuf_cache->preload_cache_size))
564                 return NULL;
565
566         if (page < pixbuf_cache->start_page) {
567                 page_offset = (page - (pixbuf_cache->start_page - pixbuf_cache->preload_cache_size));
568
569                 g_assert (page_offset >= 0 &&
570                           page_offset < pixbuf_cache->preload_cache_size);
571                 return pixbuf_cache->prev_job + page_offset;
572         }
573
574         if (page > pixbuf_cache->end_page) {
575                 page_offset = (page - (pixbuf_cache->end_page + 1));
576
577                 g_assert (page_offset >= 0 &&
578                           page_offset < pixbuf_cache->preload_cache_size);
579                 return pixbuf_cache->next_job + page_offset;
580         }
581
582         page_offset = page - pixbuf_cache->start_page;
583         g_assert (page_offset >= 0 &&
584                   page_offset <= PAGE_CACHE_LEN(pixbuf_cache));
585         return pixbuf_cache->job_list + page_offset;
586 }
587
588 static void
589 ev_pixbuf_cache_clear_job_sizes (EvPixbufCache *pixbuf_cache,
590                                  gfloat         scale)
591 {
592         int i;
593
594         for (i = 0; i < PAGE_CACHE_LEN (pixbuf_cache); i++) {
595                 check_job_size_and_unref (pixbuf_cache, pixbuf_cache->job_list + i, scale);
596         }
597
598         for (i = 0; i < pixbuf_cache->preload_cache_size; i++) {
599                 check_job_size_and_unref (pixbuf_cache, pixbuf_cache->prev_job + i, scale);
600                 check_job_size_and_unref (pixbuf_cache, pixbuf_cache->next_job + i, scale);
601         }
602 }
603
604 static void
605 get_selection_colors (GtkWidget *widget, GdkColor *text, GdkColor *base)
606 {
607         GtkStyleContext *context = gtk_widget_get_style_context (widget);
608         GtkStateFlags    state = 0;
609         GdkRGBA          fg, bg;
610
611         state |= gtk_widget_has_focus (widget) ? GTK_STATE_FLAG_SELECTED : GTK_STATE_FLAG_ACTIVE;
612
613         gtk_style_context_get_color (context, state, &fg);
614         text->pixel = 0;
615         text->red = CLAMP ((guint) (fg.red * 65535), 0, 65535);
616         text->green = CLAMP ((guint) (fg.green * 65535), 0, 65535);
617         text->blue = CLAMP ((guint) (fg.blue * 65535), 0, 65535);
618
619         gtk_style_context_get_background_color (context, state, &bg);
620         base->pixel = 0;
621         base->red = CLAMP ((guint) (bg.red * 65535), 0, 65535);
622         base->green = CLAMP ((guint) (bg.green * 65535), 0, 65535);
623         base->blue = CLAMP ((guint) (bg.blue * 65535), 0, 65535);
624 }
625
626 static void
627 add_job (EvPixbufCache  *pixbuf_cache,
628          CacheJobInfo   *job_info,
629          cairo_region_t *region,
630          gint            width,
631          gint            height,
632          gint            page,
633          gint            rotation,
634          gfloat          scale,
635          EvJobPriority   priority)
636 {
637         job_info->page_ready = FALSE;
638
639         if (job_info->region)
640                 cairo_region_destroy (job_info->region);
641         job_info->region = region ? cairo_region_reference (region) : NULL;
642
643         job_info->job = ev_job_render_new (pixbuf_cache->document,
644                                            page, rotation, scale,
645                                            width, height);
646
647         if (new_selection_surface_needed (pixbuf_cache, job_info, page, scale)) {
648                 GdkColor text, base;
649
650                 get_selection_colors (pixbuf_cache->view, &text, &base);
651                 ev_job_render_set_selection_info (EV_JOB_RENDER (job_info->job), 
652                                                   &(job_info->target_points),
653                                                   job_info->selection_style,
654                                                   &text, &base);
655         }
656
657         g_signal_connect (job_info->job, "finished",
658                           G_CALLBACK (job_finished_cb),
659                           pixbuf_cache);
660         ev_job_scheduler_push_job (job_info->job, priority);
661 }
662
663 static void
664 add_job_if_needed (EvPixbufCache *pixbuf_cache,
665                    CacheJobInfo  *job_info,
666                    gint           page,
667                    gint           rotation,
668                    gfloat         scale,
669                    EvJobPriority  priority)
670 {
671         gint width, height;
672
673         if (job_info->job)
674                 return;
675
676         _get_page_size_for_scale_and_rotation (pixbuf_cache->document,
677                                                page, scale, rotation,
678                                                &width, &height);
679
680         if (job_info->surface &&
681             cairo_image_surface_get_width (job_info->surface) == width &&
682             cairo_image_surface_get_height (job_info->surface) == height)
683                 return;
684
685         /* Free old surfaces for non visible pages */
686         if (priority == EV_JOB_PRIORITY_LOW) {
687                 if (job_info->surface) {
688                         cairo_surface_destroy (job_info->surface);
689                         job_info->surface = NULL;
690                 }
691
692                 if (job_info->selection) {
693                         cairo_surface_destroy (job_info->selection);
694                         job_info->selection = NULL;
695                 }
696         }
697
698         add_job (pixbuf_cache, job_info, NULL,
699                  width, height, page, rotation, scale,
700                  priority);
701 }
702
703 static void
704 ev_pixbuf_cache_add_jobs_if_needed (EvPixbufCache *pixbuf_cache,
705                                     gint           rotation,
706                                     gfloat         scale)
707 {
708         CacheJobInfo *job_info;
709         int page;
710         int i;
711
712         for (i = 0; i < PAGE_CACHE_LEN (pixbuf_cache); i++) {
713                 job_info = (pixbuf_cache->job_list + i);
714                 page = pixbuf_cache->start_page + i;
715
716                 add_job_if_needed (pixbuf_cache, job_info,
717                                    page, rotation, scale,
718                                    EV_JOB_PRIORITY_URGENT);
719         }
720
721         for (i = FIRST_VISIBLE_PREV(pixbuf_cache); i < pixbuf_cache->preload_cache_size; i++) {
722                 job_info = (pixbuf_cache->prev_job + i);
723                 page = pixbuf_cache->start_page - pixbuf_cache->preload_cache_size + i;
724
725                 add_job_if_needed (pixbuf_cache, job_info,
726                                    page, rotation, scale,
727                                    EV_JOB_PRIORITY_LOW);
728         }
729
730         for (i = 0; i < VISIBLE_NEXT_LEN(pixbuf_cache); i++) {
731                 job_info = (pixbuf_cache->next_job + i);
732                 page = pixbuf_cache->end_page + 1 + i;
733
734                 add_job_if_needed (pixbuf_cache, job_info,
735                                    page, rotation, scale,
736                                    EV_JOB_PRIORITY_LOW);
737         }
738
739 }
740
741 void
742 ev_pixbuf_cache_set_page_range (EvPixbufCache  *pixbuf_cache,
743                                 gint            start_page,
744                                 gint            end_page,
745                                 GList          *selection_list)
746 {
747         gdouble scale = ev_document_model_get_scale (pixbuf_cache->model);
748         gint    rotation = ev_document_model_get_rotation (pixbuf_cache->model);
749
750         g_return_if_fail (EV_IS_PIXBUF_CACHE (pixbuf_cache));
751
752         g_return_if_fail (start_page >= 0 && start_page < ev_document_get_n_pages (pixbuf_cache->document));
753         g_return_if_fail (end_page >= 0 && end_page < ev_document_get_n_pages (pixbuf_cache->document));
754         g_return_if_fail (end_page >= start_page);
755
756         /* First, resize the page_range as needed.  We cull old pages
757          * mercilessly. */
758         ev_pixbuf_cache_update_range (pixbuf_cache, start_page, end_page, rotation, scale);
759
760         /* Then, we update the current jobs to see if any of them are the wrong
761          * size, we remove them if we need to. */
762         ev_pixbuf_cache_clear_job_sizes (pixbuf_cache, scale);
763
764         /* Next, we update the target selection for our pages */
765         ev_pixbuf_cache_set_selection_list (pixbuf_cache, selection_list);
766
767         /* Finally, we add the new jobs for all the sizes that don't have a
768          * pixbuf */
769         ev_pixbuf_cache_add_jobs_if_needed (pixbuf_cache, rotation, scale);
770 }
771
772 void
773 ev_pixbuf_cache_set_inverted_colors (EvPixbufCache *pixbuf_cache,
774                                      gboolean       inverted_colors)
775 {
776         gint i;
777
778         if (pixbuf_cache->inverted_colors == inverted_colors)
779                 return;
780
781         pixbuf_cache->inverted_colors = inverted_colors;
782
783         for (i = 0; i < pixbuf_cache->preload_cache_size; i++) {
784                 CacheJobInfo *job_info;
785
786                 job_info = pixbuf_cache->prev_job + i;
787                 if (job_info && job_info->surface)
788                         ev_document_misc_invert_surface (job_info->surface);
789
790                 job_info = pixbuf_cache->next_job + i;
791                 if (job_info && job_info->surface)
792                         ev_document_misc_invert_surface (job_info->surface);
793         }
794
795         for (i = 0; i < PAGE_CACHE_LEN (pixbuf_cache); i++) {
796                 CacheJobInfo *job_info;
797
798                 job_info = pixbuf_cache->job_list + i;
799                 if (job_info && job_info->surface)
800                         ev_document_misc_invert_surface (job_info->surface);
801         }
802 }
803
804 cairo_surface_t *
805 ev_pixbuf_cache_get_surface (EvPixbufCache *pixbuf_cache,
806                              gint           page)
807 {
808         CacheJobInfo *job_info;
809
810         job_info = find_job_cache (pixbuf_cache, page);
811         if (job_info == NULL)
812                 return NULL;
813
814         if (job_info->page_ready)
815                 return job_info->surface;
816
817         /* We don't need to wait for the idle to handle the callback */
818         if (job_info->job &&
819             EV_JOB_RENDER (job_info->job)->page_ready) {
820                 copy_job_to_job_info (EV_JOB_RENDER (job_info->job), job_info, pixbuf_cache);
821                 g_signal_emit (pixbuf_cache, signals[JOB_FINISHED], 0, job_info->region);
822         }
823
824         return job_info->surface;
825 }
826
827 static gboolean
828 new_selection_surface_needed (EvPixbufCache *pixbuf_cache,
829                               CacheJobInfo  *job_info,
830                               gint           page,
831                               gfloat         scale)
832 {
833         if (job_info->selection) {
834                 gint width, height;
835                 gint selection_width, selection_height;
836
837                 _get_page_size_for_scale_and_rotation (pixbuf_cache->document,
838                                                        page, scale, 0,
839                                                        &width, &height);
840
841                 selection_width = cairo_image_surface_get_width (job_info->selection);
842                 selection_height = cairo_image_surface_get_height (job_info->selection);
843                 
844                 if (width != selection_width || height != selection_height)
845                         return TRUE;
846         } else {
847                 if (job_info->points_set)
848                         return TRUE;
849         }
850         
851         return FALSE;
852 }
853
854 static void
855 clear_selection_if_needed (EvPixbufCache *pixbuf_cache,
856                            CacheJobInfo  *job_info,
857                            gint           page,
858                            gfloat         scale)
859 {
860         if (new_selection_surface_needed (pixbuf_cache, job_info, page, scale)) {
861                 if (job_info->selection)
862                         cairo_surface_destroy (job_info->selection);
863                 job_info->selection = NULL;
864                 job_info->selection_points.x1 = -1;
865         }
866 }
867
868 /* Clears the cache of jobs and pixbufs.
869  */
870 void
871 ev_pixbuf_cache_clear (EvPixbufCache *pixbuf_cache)
872 {
873         int i;
874
875         if (!pixbuf_cache->job_list)
876                 return;
877
878         for (i = 0; i < pixbuf_cache->preload_cache_size; i++) {
879                 dispose_cache_job_info (pixbuf_cache->prev_job + i, pixbuf_cache);
880                 dispose_cache_job_info (pixbuf_cache->next_job + i, pixbuf_cache);
881         }
882
883         for (i = 0; i < PAGE_CACHE_LEN (pixbuf_cache); i++) {
884                 dispose_cache_job_info (pixbuf_cache->job_list + i, pixbuf_cache);
885         }
886 }
887
888
889 void
890 ev_pixbuf_cache_style_changed (EvPixbufCache *pixbuf_cache)
891 {
892         gint i;
893
894         if (!pixbuf_cache->job_list)
895                 return;
896
897         /* FIXME: doesn't update running jobs. */
898         for (i = 0; i < pixbuf_cache->preload_cache_size; i++) {
899                 CacheJobInfo *job_info;
900
901                 job_info = pixbuf_cache->prev_job + i;
902                 if (job_info->selection) {
903                         cairo_surface_destroy (job_info->selection);
904                         job_info->selection = NULL;
905                 }
906
907                 job_info = pixbuf_cache->next_job + i;
908                 if (job_info->selection) {
909                         cairo_surface_destroy (job_info->selection);
910                         job_info->selection = NULL;
911                 }
912         }
913
914         for (i = 0; i < PAGE_CACHE_LEN (pixbuf_cache); i++) {
915                 CacheJobInfo *job_info;
916
917                 job_info = pixbuf_cache->job_list + i;
918                 if (job_info->selection) {
919                         cairo_surface_destroy (job_info->selection);
920                         job_info->selection = NULL;
921                 }
922         }
923 }
924
925 cairo_surface_t *
926 ev_pixbuf_cache_get_selection_surface (EvPixbufCache   *pixbuf_cache,
927                                        gint             page,
928                                        gfloat           scale,
929                                        cairo_region_t **region)
930 {
931         CacheJobInfo *job_info;
932
933         /* the document does not implement the selection interface */
934         if (!EV_IS_SELECTION (pixbuf_cache->document))
935                 return NULL;
936
937         job_info = find_job_cache (pixbuf_cache, page);
938         if (job_info == NULL)
939                 return NULL;
940
941         /* No selection on this page */
942         if (!job_info->points_set)
943                 return NULL;
944
945         /* If we have a running job, we just return what we have under the
946          * assumption that it'll be updated later and we can scale it as need
947          * be */
948         if (job_info->job && EV_JOB_RENDER (job_info->job)->include_selection)
949                 return job_info->selection;
950
951         /* Now, lets see if we need to resize the image.  If we do, we clear the
952          * old one. */
953         clear_selection_if_needed (pixbuf_cache, job_info, page, scale);
954
955         /* Finally, we see if the two scales are the same, and get a new pixbuf
956          * if needed.  We do this synchronously for now.  At some point, we
957          * _should_ be able to get rid of the doc_mutex, so the synchronicity
958          * doesn't kill us.  Rendering a few glyphs should really be fast.
959          */
960         if (ev_rect_cmp (&(job_info->target_points), &(job_info->selection_points))) {
961                 EvRectangle *old_points;
962                 GdkColor text, base;
963                 EvRenderContext *rc;
964                 EvPage *ev_page;
965
966                 /* we need to get a new selection pixbuf */
967                 ev_document_doc_mutex_lock ();
968                 if (job_info->selection_points.x1 < 0) {
969                         g_assert (job_info->selection == NULL);
970                         old_points = NULL;
971                 } else {
972                         g_assert (job_info->selection != NULL);
973                         old_points = &(job_info->selection_points);
974                 }
975
976                 ev_page = ev_document_get_page (pixbuf_cache->document, page);
977                 rc = ev_render_context_new (ev_page, 0, scale);
978                 g_object_unref (ev_page);
979
980                 if (job_info->selection_region)
981                         cairo_region_destroy (job_info->selection_region);
982                 job_info->selection_region =
983                         ev_selection_get_selection_region (EV_SELECTION (pixbuf_cache->document),
984                                                            rc, job_info->selection_style,
985                                                            &(job_info->target_points));
986
987                 get_selection_colors (pixbuf_cache->view, &text, &base);
988
989                 ev_selection_render_selection (EV_SELECTION (pixbuf_cache->document),
990                                                rc, &(job_info->selection),
991                                                &(job_info->target_points),
992                                                old_points,
993                                                job_info->selection_style,
994                                                &text, &base);
995                 job_info->selection_points = job_info->target_points;
996                 g_object_unref (rc);
997                 ev_document_doc_mutex_unlock ();
998         }
999         if (region)
1000                 *region = job_info->selection_region;
1001         return job_info->selection;
1002 }
1003
1004 static void
1005 update_job_selection (CacheJobInfo    *job_info,
1006                       EvViewSelection *selection)
1007 {
1008         job_info->points_set = TRUE;            
1009         job_info->target_points = selection->rect;
1010         job_info->selection_style = selection->style;
1011 }
1012
1013 static void
1014 clear_job_selection (CacheJobInfo *job_info)
1015 {
1016         job_info->points_set = FALSE;
1017         job_info->selection_points.x1 = -1;
1018
1019         if (job_info->selection) {
1020                 cairo_surface_destroy (job_info->selection);
1021                 job_info->selection = NULL;
1022         }
1023 }
1024
1025 /* This function will reset the selection on pages that no longer have them, and
1026  * will update the target_selection on those that need it.  It will _not_ free
1027  * the previous selection_list -- that's up to caller to do.
1028  */
1029 void
1030 ev_pixbuf_cache_set_selection_list (EvPixbufCache *pixbuf_cache,
1031                                     GList         *selection_list)
1032 {
1033         EvViewSelection *selection;
1034         GList *list = selection_list;
1035         int page;
1036         int i;
1037
1038         g_return_if_fail (EV_IS_PIXBUF_CACHE (pixbuf_cache));
1039
1040         if (!EV_IS_SELECTION (pixbuf_cache->document))
1041                 return;
1042
1043         if (pixbuf_cache->start_page == -1 || pixbuf_cache->end_page == -1)
1044                 return;
1045
1046         /* We check each area to see what needs updating, and what needs freeing; */
1047         page = pixbuf_cache->start_page - pixbuf_cache->preload_cache_size;
1048         for (i = 0; i < pixbuf_cache->preload_cache_size; i++) {
1049                 if (page < 0) {
1050                         page ++;
1051                         continue;
1052                 }
1053
1054                 selection = NULL;
1055                 while (list) {
1056                         if (((EvViewSelection *)list->data)->page == page) {
1057                                 selection = list->data;
1058                                 break;
1059                         } else if (((EvViewSelection *)list->data)->page > page) 
1060                                 break;
1061                         list = list->next;
1062                 }
1063
1064                 if (selection)
1065                         update_job_selection (pixbuf_cache->prev_job + i, selection);
1066                 else
1067                         clear_job_selection (pixbuf_cache->prev_job + i);
1068                 page ++;
1069         }
1070
1071         page = pixbuf_cache->start_page;
1072         for (i = 0; i < PAGE_CACHE_LEN (pixbuf_cache); i++) {
1073                 selection = NULL;
1074                 while (list) {
1075                         if (((EvViewSelection *)list->data)->page == page) {
1076                                 selection = list->data;
1077                                 break;
1078                         } else if (((EvViewSelection *)list->data)->page > page) 
1079                                 break;
1080                         list = list->next;
1081                 }
1082
1083                 if (selection)
1084                         update_job_selection (pixbuf_cache->job_list + i, selection);
1085                 else
1086                         clear_job_selection (pixbuf_cache->job_list + i);
1087                 page ++;
1088         }
1089
1090         for (i = 0; i < pixbuf_cache->preload_cache_size; i++) {
1091                 if (page >= ev_document_get_n_pages (pixbuf_cache->document))
1092                         break;
1093
1094                 selection = NULL;
1095                 while (list) {
1096                         if (((EvViewSelection *)list->data)->page == page) {
1097                                 selection = list->data;
1098                                 break;
1099                         } else if (((EvViewSelection *)list->data)->page > page) 
1100                                 break;
1101                         list = list->next;
1102                 }
1103
1104                 if (selection)
1105                         update_job_selection (pixbuf_cache->next_job + i, selection);
1106                 else
1107                         clear_job_selection (pixbuf_cache->next_job + i);
1108                 page ++;
1109         }
1110 }
1111
1112
1113 /* Returns what the pixbuf cache thinks is */
1114
1115 GList *
1116 ev_pixbuf_cache_get_selection_list (EvPixbufCache *pixbuf_cache)
1117 {
1118         EvViewSelection *selection;
1119         GList *retval = NULL;
1120         int page;
1121         int i;
1122
1123         g_return_val_if_fail (EV_IS_PIXBUF_CACHE (pixbuf_cache), NULL);
1124
1125         if (pixbuf_cache->start_page == -1 || pixbuf_cache->end_page == -1)
1126                 return NULL;
1127
1128         /* We check each area to see what needs updating, and what needs freeing; */
1129         page = pixbuf_cache->start_page - pixbuf_cache->preload_cache_size;
1130         for (i = 0; i < pixbuf_cache->preload_cache_size; i++) {
1131                 if (page < 0) {
1132                         page ++;
1133                         continue;
1134                 }
1135
1136                 if (pixbuf_cache->prev_job[i].selection_points.x1 != -1) {
1137                         selection = g_new0 (EvViewSelection, 1);
1138                         selection->page = page;
1139                         selection->rect = pixbuf_cache->prev_job[i].selection_points;
1140                         if (pixbuf_cache->prev_job[i].selection_region)
1141                                 selection->covered_region = cairo_region_reference (pixbuf_cache->prev_job[i].selection_region);
1142                         retval = g_list_append (retval, selection);
1143                 }
1144                 
1145                 page ++;
1146         }
1147
1148         page = pixbuf_cache->start_page;
1149         for (i = 0; i < PAGE_CACHE_LEN (pixbuf_cache); i++) {
1150                 if (pixbuf_cache->job_list[i].selection_points.x1 != -1) {
1151                         selection = g_new0 (EvViewSelection, 1);
1152                         selection->page = page;
1153                         selection->rect = pixbuf_cache->job_list[i].selection_points;
1154                         if (pixbuf_cache->job_list[i].selection_region)
1155                                 selection->covered_region = cairo_region_reference (pixbuf_cache->job_list[i].selection_region);
1156                         retval = g_list_append (retval, selection);
1157                 }
1158                 
1159                 page ++;
1160         }
1161
1162         for (i = 0; i < pixbuf_cache->preload_cache_size; i++) {
1163                 if (page >= ev_document_get_n_pages (pixbuf_cache->document))
1164                         break;
1165
1166                 if (pixbuf_cache->next_job[i].selection_points.x1 != -1) {
1167                         selection = g_new0 (EvViewSelection, 1);
1168                         selection->page = page;
1169                         selection->rect = pixbuf_cache->next_job[i].selection_points;
1170                         if (pixbuf_cache->next_job[i].selection_region)
1171                                 selection->covered_region = cairo_region_reference (pixbuf_cache->next_job[i].selection_region);
1172                         retval = g_list_append (retval, selection);
1173                 }
1174                 
1175                 page ++;
1176         }
1177
1178         return retval;
1179 }
1180
1181 void
1182 ev_pixbuf_cache_reload_page (EvPixbufCache  *pixbuf_cache,
1183                              cairo_region_t *region,
1184                              gint            page,
1185                              gint            rotation,
1186                              gdouble         scale)
1187 {
1188         CacheJobInfo *job_info;
1189         gint width, height;
1190
1191         job_info = find_job_cache (pixbuf_cache, page);
1192         if (job_info == NULL)
1193                 return;
1194
1195         _get_page_size_for_scale_and_rotation (pixbuf_cache->document,
1196                                                page, scale, rotation,
1197                                                &width, &height);
1198         add_job (pixbuf_cache, job_info, region,
1199                  width, height, page, rotation, scale,
1200                  EV_JOB_PRIORITY_URGENT);
1201 }
1202
1203