]> www.fi.muni.cz Git - evince.git/blob - shell/ev-pixbuf-cache.c
Remove pixbuf backend for now
[evince.git] / shell / ev-pixbuf-cache.c
1 #include "ev-pixbuf-cache.h"
2 #include "ev-job-queue.h"
3
4
5 typedef struct _CacheJobInfo
6 {
7         EvJob *job;
8         GdkPixbuf *pixbuf;
9         GList *link_mapping;
10 } CacheJobInfo;
11
12 struct _EvPixbufCache
13 {
14         GObject parent;
15
16         EvDocument  *document;
17         int start_page;
18         int end_page;
19
20         /* preload_cache_size is the number of pages prior to the current
21          * visible area that we cache.  It's normally 1, but could be 2 in the
22          * case of twin pages.
23          */
24         int preload_cache_size;
25         CacheJobInfo *prev_job;
26         CacheJobInfo *job_list;
27         CacheJobInfo *next_job;
28 };
29
30 struct _EvPixbufCacheClass
31 {
32         GObjectClass parent_class;
33
34         void (* job_finished) (EvPixbufCache *pixbuf_cache);
35 };
36
37
38 enum
39 {
40         JOB_FINISHED,
41         N_SIGNALS,
42 };
43
44 static guint signals[N_SIGNALS] = {0, };
45
46 static void          ev_pixbuf_cache_init       (EvPixbufCache      *pixbuf_cache);
47 static void          ev_pixbuf_cache_class_init (EvPixbufCacheClass *pixbuf_cache);
48 static void          ev_pixbuf_cache_finalize   (GObject            *object);
49 static void          ev_pixbuf_cache_dispose    (GObject            *object);
50 static void          job_finished_cb            (EvJob              *job,
51                                                  EvPixbufCache      *pixbuf_cache);
52 static CacheJobInfo *find_job_cache             (EvPixbufCache      *pixbuf_cache,
53                                                  int                 page);
54 static void          copy_job_to_job_info       (EvJobRender        *job_render,
55                                                  CacheJobInfo       *job_info,
56                                                  EvPixbufCache      *pixbuf_cache);
57
58
59 /* These are used for iterating through the prev and next arrays */
60 #define FIRST_VISABLE_PREV(pixbuf_cache) \
61         (MAX (0, pixbuf_cache->preload_cache_size + 1 - pixbuf_cache->start_page))
62 #define VISIBLE_NEXT_LEN(pixbuf_cache, page_cache) \
63         (MIN(pixbuf_cache->preload_cache_size, ev_page_cache_get_n_pages (page_cache) - (1 + pixbuf_cache->end_page)))
64 #define PAGE_CACHE_LEN(pixbuf_cache) \
65         ((pixbuf_cache->end_page - pixbuf_cache->start_page) + 1)
66
67 G_DEFINE_TYPE (EvPixbufCache, ev_pixbuf_cache, G_TYPE_OBJECT)
68
69 static void
70 ev_pixbuf_cache_init (EvPixbufCache *pixbuf_cache)
71 {
72         pixbuf_cache->start_page = 0;
73         pixbuf_cache->end_page = 0;
74         pixbuf_cache->job_list = g_new0 (CacheJobInfo, PAGE_CACHE_LEN (pixbuf_cache));
75
76         pixbuf_cache->preload_cache_size = 1;
77         pixbuf_cache->prev_job = g_new0 (CacheJobInfo, pixbuf_cache->preload_cache_size);
78         pixbuf_cache->next_job = g_new0 (CacheJobInfo, pixbuf_cache->preload_cache_size);
79 }
80
81 static void
82 ev_pixbuf_cache_class_init (EvPixbufCacheClass *class)
83 {
84         GObjectClass *object_class;
85
86         object_class = G_OBJECT_CLASS (class);
87
88         object_class->finalize = ev_pixbuf_cache_finalize;
89         object_class->dispose = ev_pixbuf_cache_dispose;
90
91         signals[JOB_FINISHED] = g_signal_new ("job-finished",
92                                             G_OBJECT_CLASS_TYPE (object_class),
93                                             G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
94                                             G_STRUCT_OFFSET (EvPixbufCacheClass, job_finished),
95                                             NULL, NULL,
96                                             g_cclosure_marshal_VOID__VOID,
97                                             G_TYPE_NONE, 0);
98 }
99
100 static void
101 ev_pixbuf_cache_finalize (GObject *object)
102 {
103         EvPixbufCache *pixbuf_cache;
104
105         pixbuf_cache = EV_PIXBUF_CACHE (object);
106
107         g_free (pixbuf_cache->prev_job);
108         g_free (pixbuf_cache->job_list);
109         g_free (pixbuf_cache->next_job);
110 }
111
112 static void
113 dispose_cache_job_info (CacheJobInfo *job_info,
114                         gpointer      data)
115 {
116         if (job_info == NULL)
117                 return;
118         if (job_info->job) {
119                 g_signal_handlers_disconnect_by_func (job_info->job,
120                                                       G_CALLBACK (job_finished_cb),
121                                                       data);
122                 g_object_unref (G_OBJECT (job_info->job));
123                 job_info->job = NULL;
124         }
125         if (job_info->pixbuf) {
126                 g_object_unref (G_OBJECT (job_info->pixbuf));
127                 job_info->pixbuf = NULL;
128         }
129         if (job_info->link_mapping) {
130                 ev_link_mapping_free (job_info->link_mapping);
131                 job_info->link_mapping = NULL;
132         }
133 }
134
135 static void
136 ev_pixbuf_cache_dispose (GObject *object)
137 {
138         EvPixbufCache *pixbuf_cache;
139         int i;
140
141         pixbuf_cache = EV_PIXBUF_CACHE (object);
142
143         for (i = 0; i < pixbuf_cache->preload_cache_size; i++) {
144                 dispose_cache_job_info (pixbuf_cache->prev_job + i, pixbuf_cache);
145                 dispose_cache_job_info (pixbuf_cache->next_job + i, pixbuf_cache);
146         }
147
148         for (i = 0; i < PAGE_CACHE_LEN (pixbuf_cache); i++) {
149                 dispose_cache_job_info (pixbuf_cache->job_list + i, pixbuf_cache);
150         }
151 }
152
153
154 EvPixbufCache *
155 ev_pixbuf_cache_new (EvDocument *document)
156 {
157         EvPixbufCache *pixbuf_cache;
158
159         pixbuf_cache = (EvPixbufCache *) g_object_new (EV_TYPE_PIXBUF_CACHE, NULL);
160         pixbuf_cache->document = document;
161
162         return pixbuf_cache;
163 }
164
165 static void
166 job_finished_cb (EvJob         *job,
167                  EvPixbufCache *pixbuf_cache)
168 {
169         CacheJobInfo *job_info;
170         EvJobRender *job_render = EV_JOB_RENDER (job);
171         GdkPixbuf *pixbuf;
172
173         /* If the job is outside of our interest, we silently discard it */
174         if ((job_render->page < (pixbuf_cache->start_page - pixbuf_cache->preload_cache_size)) ||
175             (job_render->page > (pixbuf_cache->end_page + pixbuf_cache->preload_cache_size))) {
176                 g_object_unref (job);
177                 return;
178         }
179         
180         job_info = find_job_cache (pixbuf_cache, job_render->page);
181
182         pixbuf = g_object_ref (job_render->pixbuf);
183         if (job_info->pixbuf)
184                 g_object_unref (job_info->pixbuf);
185         job_info->pixbuf = pixbuf;
186
187         if (job_render->link_mapping) {
188                 if (job_info->link_mapping)
189                         ev_link_mapping_free (job_info->link_mapping);
190                 job_info->link_mapping = job_render->link_mapping;
191         }
192         
193         if (job_info->job == job)
194                 job_info->job = NULL;
195         g_object_unref (job);
196
197         g_signal_emit (pixbuf_cache, signals[JOB_FINISHED], 0);
198 }
199
200 /* This checks a job to see if the job would generate the right sized pixbuf
201  * given a scale.  If it won't, it removes the job and clears it to NULL.
202  */
203 static void
204 check_job_size_and_unref (CacheJobInfo *job_info,
205                           EvPageCache  *page_cache,
206                           gfloat        scale)
207 {
208         gint width;
209         gint height;
210
211         g_assert (job_info);
212
213         if (job_info->job == NULL)
214                 return;
215
216         ev_page_cache_get_size (page_cache,
217                                 EV_JOB_RENDER (job_info->job)->page,
218                                 scale,
219                                 &width, &height);
220                                 
221         if (width == EV_JOB_RENDER (job_info->job)->target_width &&
222             height == EV_JOB_RENDER (job_info->job)->target_height)
223                 return;
224
225         /* Try to remove the job.  If we can't, then the thread has already
226          * picked it up and we are going get a signal when it's done.  If we
227          * can, then the job is fully dead and will never rnu.. */
228         if (ev_job_queue_remove_job (job_info->job))
229                 g_object_unref (job_info->job);
230
231         job_info->job = NULL;
232 }
233
234 /* Do all function that copies a job from an older cache to it's position in the
235  * new cache.  It clears the old job if it doesn't have a place.
236  */
237 static void
238 move_one_job (CacheJobInfo  *job_info,
239               EvPixbufCache *pixbuf_cache,
240               int            page,
241               CacheJobInfo  *new_job_list,
242               CacheJobInfo  *new_prev_job,
243               CacheJobInfo  *new_next_job,
244               int            start_page,
245               int            end_page,
246               EvJobPriority  priority)
247 {
248         CacheJobInfo *target_page = NULL;
249         int page_offset;
250         EvJobPriority new_priority;
251
252         if (page < (start_page - pixbuf_cache->preload_cache_size) ||
253             page > (end_page + pixbuf_cache->preload_cache_size)) {
254                 dispose_cache_job_info (job_info, pixbuf_cache);
255                 return;
256         }
257
258         /* find the target page to copy it over to. */
259         if (page < start_page) {
260                 page_offset = (page - (start_page - pixbuf_cache->preload_cache_size));
261
262                 g_assert (page_offset >= 0 &&
263                           page_offset < pixbuf_cache->preload_cache_size);
264                 target_page = new_prev_job + page_offset;
265                 new_priority = EV_JOB_PRIORITY_LOW;
266         } else if (page > end_page) {
267                 page_offset = (page - (end_page + 1));
268
269                 g_assert (page_offset >= 0 &&
270                           page_offset < pixbuf_cache->preload_cache_size);
271                 target_page = new_next_job + page_offset;
272                 new_priority = EV_JOB_PRIORITY_LOW;
273         } else {
274                 page_offset = page - start_page;
275                 g_assert (page_offset >= 0 &&
276                           page_offset <= ((end_page - start_page) + 1));
277                 new_priority = EV_JOB_PRIORITY_HIGH;
278                 target_page = new_job_list + page_offset;
279         }
280
281         *target_page = *job_info;
282         job_info->job = NULL;
283         job_info->pixbuf = NULL;
284         job_info->link_mapping = NULL;
285
286         if (new_priority != priority && target_page->job) {
287                 g_print ("FIXME: update priority \n");
288         }
289 }
290
291
292
293 static void
294 ev_pixbuf_cache_update_range (EvPixbufCache *pixbuf_cache,
295                               gint           start_page,
296                               gint           end_page)
297 {
298         CacheJobInfo *new_job_list;
299         CacheJobInfo *new_prev_job;
300         CacheJobInfo *new_next_job;
301         EvPageCache *page_cache;
302         int i, page;
303
304         if (pixbuf_cache->start_page == start_page &&
305             pixbuf_cache->end_page == end_page)
306                 return;
307
308         page_cache = ev_document_get_page_cache (pixbuf_cache->document);
309
310         new_job_list = g_new0 (CacheJobInfo, (end_page - start_page) + 1);
311         new_prev_job = g_new0 (CacheJobInfo, pixbuf_cache->preload_cache_size);
312         new_next_job = g_new0 (CacheJobInfo, pixbuf_cache->preload_cache_size);
313
314         /* We go through each job in the old cache and either clear it or move
315          * it to a new location. */
316
317         /* Start with the prev cache. */
318         page = pixbuf_cache->start_page - pixbuf_cache->preload_cache_size;
319         for (i = 0; i < pixbuf_cache->preload_cache_size; i++) {
320                 if (page < 0) {
321                         dispose_cache_job_info (pixbuf_cache->prev_job + i, pixbuf_cache);
322                 } else {
323                         move_one_job (pixbuf_cache->prev_job + i,
324                                       pixbuf_cache, page,
325                                       new_job_list, new_prev_job, new_next_job,
326                                       start_page, end_page, EV_JOB_PRIORITY_LOW);
327                 }
328                 page ++;
329         }
330
331         page = pixbuf_cache->start_page;
332         for (i = 0; i < PAGE_CACHE_LEN (pixbuf_cache); i++) {
333                 move_one_job (pixbuf_cache->job_list + i,
334                               pixbuf_cache, page,
335                               new_job_list, new_prev_job, new_next_job,
336                               start_page, end_page, EV_JOB_PRIORITY_HIGH);
337                 page++;
338         }
339
340         for (i = 0; i < pixbuf_cache->preload_cache_size; i++) {
341                 if (page >= ev_page_cache_get_n_pages (page_cache)) {
342                         dispose_cache_job_info (pixbuf_cache->next_job + i, pixbuf_cache);
343                 } else {
344                         move_one_job (pixbuf_cache->next_job + i,
345                                       pixbuf_cache, page,
346                                       new_job_list, new_prev_job, new_next_job,
347                                       start_page, end_page, EV_JOB_PRIORITY_LOW);
348                 }
349                 page ++;
350         }
351
352         g_free (pixbuf_cache->job_list);
353         g_free (pixbuf_cache->prev_job);
354         g_free (pixbuf_cache->next_job);
355
356         pixbuf_cache->job_list = new_job_list;
357         pixbuf_cache->prev_job = new_prev_job;
358         pixbuf_cache->next_job = new_next_job;
359
360         pixbuf_cache->start_page = start_page;
361         pixbuf_cache->end_page = end_page;
362 }
363
364 static void
365 copy_job_to_job_info (EvJobRender   *job_render,
366                       CacheJobInfo  *job_info,
367                       EvPixbufCache *pixbuf_cache)
368 {
369         GdkPixbuf *pixbuf;
370
371         pixbuf = g_object_ref (job_render->pixbuf);
372
373         dispose_cache_job_info (job_info, pixbuf_cache);
374
375         job_info->pixbuf = pixbuf;
376         if (job_render->link_mapping)
377                 job_info->link_mapping = job_render->link_mapping;
378 }
379
380 static CacheJobInfo *
381 find_job_cache (EvPixbufCache *pixbuf_cache,
382                 int            page)
383 {
384         int page_offset;
385
386         if (page < (pixbuf_cache->start_page - pixbuf_cache->preload_cache_size) ||
387             page > (pixbuf_cache->end_page + pixbuf_cache->preload_cache_size))
388                 return NULL;
389
390         if (page < pixbuf_cache->start_page) {
391                 page_offset = (page - (pixbuf_cache->start_page - pixbuf_cache->preload_cache_size));
392
393                 g_assert (page_offset >= 0 &&
394                           page_offset < pixbuf_cache->preload_cache_size);
395                 return pixbuf_cache->prev_job + page_offset;
396         }
397
398         if (page > pixbuf_cache->end_page) {
399                 page_offset = (page - (pixbuf_cache->end_page + 1));
400
401                 g_assert (page_offset >= 0 &&
402                           page_offset < pixbuf_cache->preload_cache_size);
403                 return pixbuf_cache->next_job + page_offset;
404         }
405
406         page_offset = page - pixbuf_cache->start_page;
407         g_assert (page_offset >= 0 &&
408                   page_offset <= PAGE_CACHE_LEN(pixbuf_cache));
409         return pixbuf_cache->job_list + page_offset;
410 }
411
412 static void
413 ev_pixbuf_cache_clear_job_sizes (EvPixbufCache *pixbuf_cache,
414                                  gfloat         scale)
415 {
416         EvPageCache *page_cache;
417         int i;
418
419         page_cache = ev_document_get_page_cache (pixbuf_cache->document);
420
421         for (i = 0; i < PAGE_CACHE_LEN (pixbuf_cache); i++) {
422                 check_job_size_and_unref (pixbuf_cache->job_list + i, page_cache, scale);
423         }
424
425         for (i = 0; i < pixbuf_cache->preload_cache_size; i++) {
426                 check_job_size_and_unref (pixbuf_cache->prev_job + i, page_cache, scale);
427                 check_job_size_and_unref (pixbuf_cache->next_job + i, page_cache, scale);
428         }
429 }
430
431 #define FIRST_VISABLE_PREV(pixbuf_cache) \
432         (MAX (0, pixbuf_cache->preload_cache_size + 1 - pixbuf_cache->start_page))
433
434 static void
435 add_job_if_needed (EvPixbufCache *pixbuf_cache,
436                    CacheJobInfo  *job_info,
437                    EvPageCache   *page_cache,
438                    gint           page,
439                    gfloat         scale,
440                    EvJobPriority  priority)
441 {
442         int width, height;
443
444         if (job_info->job)
445                 return;
446
447         ev_page_cache_get_size (page_cache,
448                                 page, scale,
449                                 &width, &height);
450
451         if (job_info->pixbuf &&
452             gdk_pixbuf_get_width (job_info->pixbuf) == width &&
453             gdk_pixbuf_get_height (job_info->pixbuf) == height)
454                 return;
455
456         /* make a new job now */
457         job_info->job = ev_job_render_new (pixbuf_cache->document,
458                                            page, scale,
459                                            width, height,
460                                            (job_info->link_mapping == NULL)?TRUE:FALSE);
461         ev_job_queue_add_job (job_info->job, priority);
462         g_signal_connect (job_info->job, "finished", G_CALLBACK (job_finished_cb), pixbuf_cache);
463 }
464
465
466 static void
467 ev_pixbuf_cache_add_jobs_if_needed (EvPixbufCache *pixbuf_cache,
468                                     gfloat         scale)
469 {
470         EvPageCache *page_cache;
471         CacheJobInfo *job_info;
472         int page;
473         int i;
474
475         page_cache = ev_document_get_page_cache (pixbuf_cache->document);
476
477         for (i = 0; i < PAGE_CACHE_LEN (pixbuf_cache); i++) {
478                 job_info = (pixbuf_cache->job_list + i);
479                 page = pixbuf_cache->start_page + i;
480
481                 add_job_if_needed (pixbuf_cache, job_info,
482                                    page_cache, page, scale,
483                                    EV_JOB_PRIORITY_HIGH);
484         }
485
486         for (i = FIRST_VISABLE_PREV(pixbuf_cache); i < pixbuf_cache->preload_cache_size; i++) {
487                 job_info = (pixbuf_cache->prev_job + i);
488                 page = pixbuf_cache->start_page - pixbuf_cache->preload_cache_size + i;
489
490                 add_job_if_needed (pixbuf_cache, job_info,
491                                    page_cache, page, scale,
492                                    EV_JOB_PRIORITY_LOW);
493         }
494
495         for (i = 0; i < VISIBLE_NEXT_LEN(pixbuf_cache, page_cache); i++) {
496                 job_info = (pixbuf_cache->next_job + i);
497                 page = pixbuf_cache->end_page + 1 + i;
498
499                 add_job_if_needed (pixbuf_cache, job_info,
500                                    page_cache, page, scale,
501                                    EV_JOB_PRIORITY_LOW);
502         }
503
504 }
505
506 void
507 ev_pixbuf_cache_set_page_range (EvPixbufCache *pixbuf_cache,
508                                 gint           start_page,
509                                 gint           end_page,
510                                 gfloat         scale)
511 {
512         EvPageCache *page_cache;
513
514         g_return_if_fail (EV_IS_PIXBUF_CACHE (pixbuf_cache));
515
516         page_cache = ev_document_get_page_cache (pixbuf_cache->document);
517
518         g_return_if_fail (start_page >= 0 && start_page < ev_page_cache_get_n_pages (page_cache));
519         g_return_if_fail (end_page >= 0 && end_page < ev_page_cache_get_n_pages (page_cache));
520         g_return_if_fail (end_page >= start_page);
521
522         /* First, resize the page_range as needed.  We cull old pages
523          * mercilessly. */
524         ev_pixbuf_cache_update_range (pixbuf_cache, start_page, end_page);
525
526         /* Then, we update the current jobs to see if any of them are the wrong
527          * size, we remove them if we need to. */
528         ev_pixbuf_cache_clear_job_sizes (pixbuf_cache, scale);
529
530         /* Finally, we add the new jobs for all the sizes that don't have a
531          * pixbuf */
532         ev_pixbuf_cache_add_jobs_if_needed (pixbuf_cache, scale);
533 }
534
535 GdkPixbuf *
536 ev_pixbuf_cache_get_pixbuf (EvPixbufCache *pixbuf_cache,
537                             gint           page)
538 {
539         CacheJobInfo *job_info;
540
541         job_info = find_job_cache (pixbuf_cache, page);
542         if (job_info == NULL)
543                 return NULL;
544
545         /* We don't need to wait for the idle to handle the callback */
546         if (job_info->job &&
547             EV_JOB (job_info->job)->finished) {
548                 copy_job_to_job_info (EV_JOB_RENDER (job_info->job), job_info, pixbuf_cache);
549         }
550
551         return job_info->pixbuf;
552 }
553
554 GList *
555 ev_pixbuf_cache_get_link_mapping (EvPixbufCache *pixbuf_cache,
556                                   gint           page)
557 {
558         CacheJobInfo *job_info;
559
560         job_info = find_job_cache (pixbuf_cache, page);
561         if (job_info == NULL)
562                 return NULL;
563
564         /* We don't need to wait for the idle to handle the callback */
565         if (job_info->job &&
566             EV_JOB (job_info->job)->finished) {
567                 copy_job_to_job_info (EV_JOB_RENDER (job_info->job), job_info, pixbuf_cache);
568         }
569         
570         return job_info->link_mapping;
571 }