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