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