]> www.fi.muni.cz Git - evince.git/blob - libview/ev-pixbuf-cache.c
74b8674f293685a68f006d165b6c29add76254f5
[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         GtkStyle *style = gtk_widget_get_style (widget);
608
609         if (gtk_widget_has_focus (widget)) {
610                 *text = &style->text [GTK_STATE_SELECTED];
611                 *base = &style->base [GTK_STATE_SELECTED];
612         } else {
613                 *text = &style->text [GTK_STATE_ACTIVE];
614                 *base = &style->base [GTK_STATE_ACTIVE];
615         }
616 }
617
618 static void
619 add_job (EvPixbufCache  *pixbuf_cache,
620          CacheJobInfo   *job_info,
621          cairo_region_t *region,
622          gint            width,
623          gint            height,
624          gint            page,
625          gint            rotation,
626          gfloat          scale,
627          EvJobPriority   priority)
628 {
629         job_info->page_ready = FALSE;
630
631         if (job_info->region)
632                 cairo_region_destroy (job_info->region);
633         job_info->region = region ? cairo_region_reference (region) : NULL;
634
635         job_info->job = ev_job_render_new (pixbuf_cache->document,
636                                            page, rotation, scale,
637                                            width, height);
638
639         if (new_selection_surface_needed (pixbuf_cache, job_info, page, scale)) {
640                 GdkColor *text, *base;
641
642                 gtk_widget_ensure_style (pixbuf_cache->view);
643                 get_selection_colors (pixbuf_cache->view, &text, &base);
644                 ev_job_render_set_selection_info (EV_JOB_RENDER (job_info->job), 
645                                                   &(job_info->target_points),
646                                                   job_info->selection_style,
647                                                   text, base);
648         }
649
650         g_signal_connect (job_info->job, "finished",
651                           G_CALLBACK (job_finished_cb),
652                           pixbuf_cache);
653         ev_job_scheduler_push_job (job_info->job, priority);
654 }
655
656 static void
657 add_job_if_needed (EvPixbufCache *pixbuf_cache,
658                    CacheJobInfo  *job_info,
659                    gint           page,
660                    gint           rotation,
661                    gfloat         scale,
662                    EvJobPriority  priority)
663 {
664         gint width, height;
665
666         if (job_info->job)
667                 return;
668
669         _get_page_size_for_scale_and_rotation (pixbuf_cache->document,
670                                                page, scale, rotation,
671                                                &width, &height);
672
673         if (job_info->surface &&
674             cairo_image_surface_get_width (job_info->surface) == width &&
675             cairo_image_surface_get_height (job_info->surface) == height)
676                 return;
677
678         /* Free old surfaces for non visible pages */
679         if (priority == EV_JOB_PRIORITY_LOW) {
680                 if (job_info->surface) {
681                         cairo_surface_destroy (job_info->surface);
682                         job_info->surface = NULL;
683                 }
684
685                 if (job_info->selection) {
686                         cairo_surface_destroy (job_info->selection);
687                         job_info->selection = NULL;
688                 }
689         }
690
691         add_job (pixbuf_cache, job_info, NULL,
692                  width, height, page, rotation, scale,
693                  priority);
694 }
695
696 static void
697 ev_pixbuf_cache_add_jobs_if_needed (EvPixbufCache *pixbuf_cache,
698                                     gint           rotation,
699                                     gfloat         scale)
700 {
701         CacheJobInfo *job_info;
702         int page;
703         int i;
704
705         for (i = 0; i < PAGE_CACHE_LEN (pixbuf_cache); i++) {
706                 job_info = (pixbuf_cache->job_list + i);
707                 page = pixbuf_cache->start_page + i;
708
709                 add_job_if_needed (pixbuf_cache, job_info,
710                                    page, rotation, scale,
711                                    EV_JOB_PRIORITY_URGENT);
712         }
713
714         for (i = FIRST_VISIBLE_PREV(pixbuf_cache); i < pixbuf_cache->preload_cache_size; i++) {
715                 job_info = (pixbuf_cache->prev_job + i);
716                 page = pixbuf_cache->start_page - pixbuf_cache->preload_cache_size + i;
717
718                 add_job_if_needed (pixbuf_cache, job_info,
719                                    page, rotation, scale,
720                                    EV_JOB_PRIORITY_LOW);
721         }
722
723         for (i = 0; i < VISIBLE_NEXT_LEN(pixbuf_cache); i++) {
724                 job_info = (pixbuf_cache->next_job + i);
725                 page = pixbuf_cache->end_page + 1 + i;
726
727                 add_job_if_needed (pixbuf_cache, job_info,
728                                    page, rotation, scale,
729                                    EV_JOB_PRIORITY_LOW);
730         }
731
732 }
733
734 void
735 ev_pixbuf_cache_set_page_range (EvPixbufCache  *pixbuf_cache,
736                                 gint            start_page,
737                                 gint            end_page,
738                                 GList          *selection_list)
739 {
740         gdouble scale = ev_document_model_get_scale (pixbuf_cache->model);
741         gint    rotation = ev_document_model_get_rotation (pixbuf_cache->model);
742
743         g_return_if_fail (EV_IS_PIXBUF_CACHE (pixbuf_cache));
744
745         g_return_if_fail (start_page >= 0 && start_page < ev_document_get_n_pages (pixbuf_cache->document));
746         g_return_if_fail (end_page >= 0 && end_page < ev_document_get_n_pages (pixbuf_cache->document));
747         g_return_if_fail (end_page >= start_page);
748
749         /* First, resize the page_range as needed.  We cull old pages
750          * mercilessly. */
751         ev_pixbuf_cache_update_range (pixbuf_cache, start_page, end_page, rotation, scale);
752
753         /* Then, we update the current jobs to see if any of them are the wrong
754          * size, we remove them if we need to. */
755         ev_pixbuf_cache_clear_job_sizes (pixbuf_cache, scale);
756
757         /* Next, we update the target selection for our pages */
758         ev_pixbuf_cache_set_selection_list (pixbuf_cache, selection_list);
759
760         /* Finally, we add the new jobs for all the sizes that don't have a
761          * pixbuf */
762         ev_pixbuf_cache_add_jobs_if_needed (pixbuf_cache, rotation, scale);
763 }
764
765 void
766 ev_pixbuf_cache_set_inverted_colors (EvPixbufCache *pixbuf_cache,
767                                      gboolean       inverted_colors)
768 {
769         gint i;
770
771         if (pixbuf_cache->inverted_colors == inverted_colors)
772                 return;
773
774         pixbuf_cache->inverted_colors = inverted_colors;
775
776         for (i = 0; i < pixbuf_cache->preload_cache_size; i++) {
777                 CacheJobInfo *job_info;
778
779                 job_info = pixbuf_cache->prev_job + i;
780                 if (job_info->surface)
781                         ev_document_misc_invert_surface (job_info->surface);
782
783                 job_info = pixbuf_cache->next_job + i;
784                 if (job_info->surface)
785                         ev_document_misc_invert_surface (job_info->surface);
786         }
787
788         for (i = 0; i < PAGE_CACHE_LEN (pixbuf_cache); i++) {
789                 CacheJobInfo *job_info;
790
791                 job_info = pixbuf_cache->job_list + i;
792                 if (job_info->surface)
793                         ev_document_misc_invert_surface (job_info->surface);
794         }
795 }
796
797 cairo_surface_t *
798 ev_pixbuf_cache_get_surface (EvPixbufCache *pixbuf_cache,
799                              gint           page)
800 {
801         CacheJobInfo *job_info;
802
803         job_info = find_job_cache (pixbuf_cache, page);
804         if (job_info == NULL)
805                 return NULL;
806
807         if (job_info->page_ready)
808                 return job_info->surface;
809
810         /* We don't need to wait for the idle to handle the callback */
811         if (job_info->job &&
812             EV_JOB_RENDER (job_info->job)->page_ready) {
813                 copy_job_to_job_info (EV_JOB_RENDER (job_info->job), job_info, pixbuf_cache);
814                 g_signal_emit (pixbuf_cache, signals[JOB_FINISHED], 0, job_info->region);
815         }
816
817         return job_info->surface;
818 }
819
820 static gboolean
821 new_selection_surface_needed (EvPixbufCache *pixbuf_cache,
822                               CacheJobInfo  *job_info,
823                               gint           page,
824                               gfloat         scale)
825 {
826         if (job_info->selection) {
827                 gint width, height;
828                 gint selection_width, selection_height;
829
830                 _get_page_size_for_scale_and_rotation (pixbuf_cache->document,
831                                                        page, scale, 0,
832                                                        &width, &height);
833
834                 selection_width = cairo_image_surface_get_width (job_info->selection);
835                 selection_height = cairo_image_surface_get_height (job_info->selection);
836                 
837                 if (width != selection_width || height != selection_height)
838                         return TRUE;
839         } else {
840                 if (job_info->points_set)
841                         return TRUE;
842         }
843         
844         return FALSE;
845 }
846
847 static void
848 clear_selection_if_needed (EvPixbufCache *pixbuf_cache,
849                            CacheJobInfo  *job_info,
850                            gint           page,
851                            gfloat         scale)
852 {
853         if (new_selection_surface_needed (pixbuf_cache, job_info, page, scale)) {
854                 if (job_info->selection)
855                         cairo_surface_destroy (job_info->selection);
856                 job_info->selection = NULL;
857                 job_info->selection_points.x1 = -1;
858         }
859 }
860
861 /* Clears the cache of jobs and pixbufs.
862  */
863 void
864 ev_pixbuf_cache_clear (EvPixbufCache *pixbuf_cache)
865 {
866         int i;
867
868         if (!pixbuf_cache->job_list)
869                 return;
870
871         for (i = 0; i < pixbuf_cache->preload_cache_size; i++) {
872                 dispose_cache_job_info (pixbuf_cache->prev_job + i, pixbuf_cache);
873                 dispose_cache_job_info (pixbuf_cache->next_job + i, pixbuf_cache);
874         }
875
876         for (i = 0; i < PAGE_CACHE_LEN (pixbuf_cache); i++) {
877                 dispose_cache_job_info (pixbuf_cache->job_list + i, pixbuf_cache);
878         }
879 }
880
881
882 void
883 ev_pixbuf_cache_style_changed (EvPixbufCache *pixbuf_cache)
884 {
885         gint i;
886
887         if (!pixbuf_cache->job_list)
888                 return;
889
890         /* FIXME: doesn't update running jobs. */
891         for (i = 0; i < pixbuf_cache->preload_cache_size; i++) {
892                 CacheJobInfo *job_info;
893
894                 job_info = pixbuf_cache->prev_job + i;
895                 if (job_info->selection) {
896                         cairo_surface_destroy (job_info->selection);
897                         job_info->selection = NULL;
898                 }
899
900                 job_info = pixbuf_cache->next_job + i;
901                 if (job_info->selection) {
902                         cairo_surface_destroy (job_info->selection);
903                         job_info->selection = NULL;
904                 }
905         }
906
907         for (i = 0; i < PAGE_CACHE_LEN (pixbuf_cache); i++) {
908                 CacheJobInfo *job_info;
909
910                 job_info = pixbuf_cache->job_list + i;
911                 if (job_info->selection) {
912                         cairo_surface_destroy (job_info->selection);
913                         job_info->selection = NULL;
914                 }
915         }
916 }
917
918 cairo_surface_t *
919 ev_pixbuf_cache_get_selection_surface (EvPixbufCache   *pixbuf_cache,
920                                        gint             page,
921                                        gfloat           scale,
922                                        cairo_region_t **region)
923 {
924         CacheJobInfo *job_info;
925
926         /* the document does not implement the selection interface */
927         if (!EV_IS_SELECTION (pixbuf_cache->document))
928                 return NULL;
929
930         job_info = find_job_cache (pixbuf_cache, page);
931         if (job_info == NULL)
932                 return NULL;
933
934         /* No selection on this page */
935         if (!job_info->points_set)
936                 return NULL;
937
938         /* If we have a running job, we just return what we have under the
939          * assumption that it'll be updated later and we can scale it as need
940          * be */
941         if (job_info->job && EV_JOB_RENDER (job_info->job)->include_selection)
942                 return job_info->selection;
943
944         /* Now, lets see if we need to resize the image.  If we do, we clear the
945          * old one. */
946         clear_selection_if_needed (pixbuf_cache, job_info, page, scale);
947
948         /* Finally, we see if the two scales are the same, and get a new pixbuf
949          * if needed.  We do this synchronously for now.  At some point, we
950          * _should_ be able to get rid of the doc_mutex, so the synchronicity
951          * doesn't kill us.  Rendering a few glyphs should really be fast.
952          */
953         if (ev_rect_cmp (&(job_info->target_points), &(job_info->selection_points))) {
954                 EvRectangle *old_points;
955                 GdkColor *text, *base;
956                 EvRenderContext *rc;
957                 EvPage *ev_page;
958
959                 /* we need to get a new selection pixbuf */
960                 ev_document_doc_mutex_lock ();
961                 if (job_info->selection_points.x1 < 0) {
962                         g_assert (job_info->selection == NULL);
963                         old_points = NULL;
964                 } else {
965                         g_assert (job_info->selection != NULL);
966                         old_points = &(job_info->selection_points);
967                 }
968
969                 ev_page = ev_document_get_page (pixbuf_cache->document, page);
970                 rc = ev_render_context_new (ev_page, 0, scale);
971                 g_object_unref (ev_page);
972
973                 if (job_info->selection_region)
974                         cairo_region_destroy (job_info->selection_region);
975                 job_info->selection_region =
976                         ev_selection_get_selection_region (EV_SELECTION (pixbuf_cache->document),
977                                                            rc, job_info->selection_style,
978                                                            &(job_info->target_points));
979
980                 gtk_widget_ensure_style (pixbuf_cache->view);
981
982                 get_selection_colors (pixbuf_cache->view, &text, &base);
983
984                 ev_selection_render_selection (EV_SELECTION (pixbuf_cache->document),
985                                                rc, &(job_info->selection),
986                                                &(job_info->target_points),
987                                                old_points,
988                                                job_info->selection_style,
989                                                text, base);
990                 job_info->selection_points = job_info->target_points;
991                 g_object_unref (rc);
992                 ev_document_doc_mutex_unlock ();
993         }
994         if (region)
995                 *region = job_info->selection_region;
996         return job_info->selection;
997 }
998
999 static void
1000 update_job_selection (CacheJobInfo    *job_info,
1001                       EvViewSelection *selection)
1002 {
1003         job_info->points_set = TRUE;            
1004         job_info->target_points = selection->rect;
1005         job_info->selection_style = selection->style;
1006 }
1007
1008 static void
1009 clear_job_selection (CacheJobInfo *job_info)
1010 {
1011         job_info->points_set = FALSE;
1012         job_info->selection_points.x1 = -1;
1013
1014         if (job_info->selection) {
1015                 cairo_surface_destroy (job_info->selection);
1016                 job_info->selection = NULL;
1017         }
1018 }
1019
1020 /* This function will reset the selection on pages that no longer have them, and
1021  * will update the target_selection on those that need it.  It will _not_ free
1022  * the previous selection_list -- that's up to caller to do.
1023  */
1024 void
1025 ev_pixbuf_cache_set_selection_list (EvPixbufCache *pixbuf_cache,
1026                                     GList         *selection_list)
1027 {
1028         EvViewSelection *selection;
1029         GList *list = selection_list;
1030         int page;
1031         int i;
1032
1033         g_return_if_fail (EV_IS_PIXBUF_CACHE (pixbuf_cache));
1034
1035         if (!EV_IS_SELECTION (pixbuf_cache->document))
1036                 return;
1037
1038         /* We check each area to see what needs updating, and what needs freeing; */
1039         page = pixbuf_cache->start_page - pixbuf_cache->preload_cache_size;
1040         for (i = 0; i < pixbuf_cache->preload_cache_size; i++) {
1041                 if (page < 0) {
1042                         page ++;
1043                         continue;
1044                 }
1045
1046                 selection = NULL;
1047                 while (list) {
1048                         if (((EvViewSelection *)list->data)->page == page) {
1049                                 selection = list->data;
1050                                 break;
1051                         } else if (((EvViewSelection *)list->data)->page > page) 
1052                                 break;
1053                         list = list->next;
1054                 }
1055
1056                 if (selection)
1057                         update_job_selection (pixbuf_cache->prev_job + i, selection);
1058                 else
1059                         clear_job_selection (pixbuf_cache->prev_job + i);
1060                 page ++;
1061         }
1062
1063         page = pixbuf_cache->start_page;
1064         for (i = 0; i < PAGE_CACHE_LEN (pixbuf_cache); i++) {
1065                 selection = NULL;
1066                 while (list) {
1067                         if (((EvViewSelection *)list->data)->page == page) {
1068                                 selection = list->data;
1069                                 break;
1070                         } else if (((EvViewSelection *)list->data)->page > page) 
1071                                 break;
1072                         list = list->next;
1073                 }
1074
1075                 if (selection)
1076                         update_job_selection (pixbuf_cache->job_list + i, selection);
1077                 else
1078                         clear_job_selection (pixbuf_cache->job_list + i);
1079                 page ++;
1080         }
1081
1082         for (i = 0; i < pixbuf_cache->preload_cache_size; i++) {
1083                 if (page >= ev_document_get_n_pages (pixbuf_cache->document))
1084                         break;
1085
1086                 selection = NULL;
1087                 while (list) {
1088                         if (((EvViewSelection *)list->data)->page == page) {
1089                                 selection = list->data;
1090                                 break;
1091                         } else if (((EvViewSelection *)list->data)->page > page) 
1092                                 break;
1093                         list = list->next;
1094                 }
1095
1096                 if (selection)
1097                         update_job_selection (pixbuf_cache->next_job + i, selection);
1098                 else
1099                         clear_job_selection (pixbuf_cache->next_job + i);
1100                 page ++;
1101         }
1102 }
1103
1104
1105 /* Returns what the pixbuf cache thinks is */
1106
1107 GList *
1108 ev_pixbuf_cache_get_selection_list (EvPixbufCache *pixbuf_cache)
1109 {
1110         EvViewSelection *selection;
1111         GList *retval = NULL;
1112         int page;
1113         int i;
1114
1115         g_return_val_if_fail (EV_IS_PIXBUF_CACHE (pixbuf_cache), NULL);
1116
1117         /* We check each area to see what needs updating, and what needs freeing; */
1118         page = pixbuf_cache->start_page - pixbuf_cache->preload_cache_size;
1119         for (i = 0; i < pixbuf_cache->preload_cache_size; i++) {
1120                 if (page < 0) {
1121                         page ++;
1122                         continue;
1123                 }
1124
1125                 if (pixbuf_cache->prev_job[i].selection_points.x1 != -1) {
1126                         selection = g_new0 (EvViewSelection, 1);
1127                         selection->page = page;
1128                         selection->rect = pixbuf_cache->prev_job[i].selection_points;
1129                         if (pixbuf_cache->prev_job[i].selection_region)
1130                                 selection->covered_region = cairo_region_reference (pixbuf_cache->prev_job[i].selection_region);
1131                         retval = g_list_append (retval, selection);
1132                 }
1133                 
1134                 page ++;
1135         }
1136
1137         page = pixbuf_cache->start_page;
1138         for (i = 0; i < PAGE_CACHE_LEN (pixbuf_cache); i++) {
1139                 if (pixbuf_cache->job_list[i].selection_points.x1 != -1) {
1140                         selection = g_new0 (EvViewSelection, 1);
1141                         selection->page = page;
1142                         selection->rect = pixbuf_cache->job_list[i].selection_points;
1143                         if (pixbuf_cache->job_list[i].selection_region)
1144                                 selection->covered_region = cairo_region_reference (pixbuf_cache->job_list[i].selection_region);
1145                         retval = g_list_append (retval, selection);
1146                 }
1147                 
1148                 page ++;
1149         }
1150
1151         for (i = 0; i < pixbuf_cache->preload_cache_size; i++) {
1152                 if (page >= ev_document_get_n_pages (pixbuf_cache->document))
1153                         break;
1154
1155                 if (pixbuf_cache->next_job[i].selection_points.x1 != -1) {
1156                         selection = g_new0 (EvViewSelection, 1);
1157                         selection->page = page;
1158                         selection->rect = pixbuf_cache->next_job[i].selection_points;
1159                         if (pixbuf_cache->next_job[i].selection_region)
1160                                 selection->covered_region = cairo_region_reference (pixbuf_cache->next_job[i].selection_region);
1161                         retval = g_list_append (retval, selection);
1162                 }
1163                 
1164                 page ++;
1165         }
1166
1167         return retval;
1168 }
1169
1170 void
1171 ev_pixbuf_cache_reload_page (EvPixbufCache  *pixbuf_cache,
1172                              cairo_region_t *region,
1173                              gint            page,
1174                              gint            rotation,
1175                              gdouble         scale)
1176 {
1177         CacheJobInfo *job_info;
1178         gint width, height;
1179
1180         job_info = find_job_cache (pixbuf_cache, page);
1181         if (job_info == NULL)
1182                 return;
1183
1184         _get_page_size_for_scale_and_rotation (pixbuf_cache->document,
1185                                                page, scale, rotation,
1186                                                &width, &height);
1187         add_job (pixbuf_cache, job_info, region,
1188                  width, height, page, rotation, scale,
1189                  EV_JOB_PRIORITY_URGENT);
1190 }
1191
1192