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