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