1 #include "ev-pixbuf-cache.h"
2 #include "ev-job-queue.h"
5 typedef struct _CacheJobInfo
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
24 int preload_cache_size;
25 CacheJobInfo *prev_job;
26 CacheJobInfo *job_list;
27 CacheJobInfo *next_job;
30 struct _EvPixbufCacheClass
32 GObjectClass parent_class;
34 void (* job_finished) (EvPixbufCache *pixbuf_cache);
44 static guint signals[N_SIGNALS] = {0, };
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,
54 static void copy_job_to_job_info (EvJobRender *job_render,
55 CacheJobInfo *job_info,
56 EvPixbufCache *pixbuf_cache);
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)
67 G_DEFINE_TYPE (EvPixbufCache, ev_pixbuf_cache, G_TYPE_OBJECT)
70 ev_pixbuf_cache_init (EvPixbufCache *pixbuf_cache)
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));
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);
82 ev_pixbuf_cache_class_init (EvPixbufCacheClass *class)
84 GObjectClass *object_class;
86 object_class = G_OBJECT_CLASS (class);
88 object_class->finalize = ev_pixbuf_cache_finalize;
89 object_class->dispose = ev_pixbuf_cache_dispose;
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),
96 g_cclosure_marshal_VOID__VOID,
101 ev_pixbuf_cache_finalize (GObject *object)
103 EvPixbufCache *pixbuf_cache;
105 pixbuf_cache = EV_PIXBUF_CACHE (object);
107 g_free (pixbuf_cache->prev_job);
108 g_free (pixbuf_cache->job_list);
109 g_free (pixbuf_cache->next_job);
113 dispose_cache_job_info (CacheJobInfo *job_info,
116 if (job_info == NULL)
119 g_signal_handlers_disconnect_by_func (job_info->job,
120 G_CALLBACK (job_finished_cb),
122 g_object_unref (G_OBJECT (job_info->job));
123 job_info->job = NULL;
125 if (job_info->pixbuf) {
126 g_object_unref (G_OBJECT (job_info->pixbuf));
127 job_info->pixbuf = NULL;
129 if (job_info->link_mapping) {
130 ev_link_mapping_free (job_info->link_mapping);
131 job_info->link_mapping = NULL;
136 ev_pixbuf_cache_dispose (GObject *object)
138 EvPixbufCache *pixbuf_cache;
141 pixbuf_cache = EV_PIXBUF_CACHE (object);
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);
148 for (i = 0; i < PAGE_CACHE_LEN (pixbuf_cache); i++) {
149 dispose_cache_job_info (pixbuf_cache->job_list + i, pixbuf_cache);
155 ev_pixbuf_cache_new (EvDocument *document)
157 EvPixbufCache *pixbuf_cache;
159 pixbuf_cache = (EvPixbufCache *) g_object_new (EV_TYPE_PIXBUF_CACHE, NULL);
160 pixbuf_cache->document = document;
166 job_finished_cb (EvJob *job,
167 EvPixbufCache *pixbuf_cache)
169 CacheJobInfo *job_info;
170 EvJobRender *job_render = EV_JOB_RENDER (job);
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);
180 job_info = find_job_cache (pixbuf_cache, job_render->page);
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;
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;
193 if (job_info->job == job)
194 job_info->job = NULL;
195 g_object_unref (job);
197 g_signal_emit (pixbuf_cache, signals[JOB_FINISHED], 0);
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.
204 check_job_size_and_unref (CacheJobInfo *job_info,
205 EvPageCache *page_cache,
213 if (job_info->job == NULL)
216 ev_page_cache_get_size (page_cache,
217 EV_JOB_RENDER (job_info->job)->page,
221 if (width == EV_JOB_RENDER (job_info->job)->target_width &&
222 height == EV_JOB_RENDER (job_info->job)->target_height)
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);
231 job_info->job = NULL;
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.
238 move_one_job (CacheJobInfo *job_info,
239 EvPixbufCache *pixbuf_cache,
241 CacheJobInfo *new_job_list,
242 CacheJobInfo *new_prev_job,
243 CacheJobInfo *new_next_job,
246 EvJobPriority priority)
248 CacheJobInfo *target_page = NULL;
250 EvJobPriority new_priority;
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);
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));
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));
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;
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;
281 *target_page = *job_info;
282 job_info->job = NULL;
283 job_info->pixbuf = NULL;
284 job_info->link_mapping = NULL;
286 if (new_priority != priority && target_page->job) {
287 ev_job_queue_update_job (target_page->job, new_priority);
294 ev_pixbuf_cache_update_range (EvPixbufCache *pixbuf_cache,
298 CacheJobInfo *new_job_list;
299 CacheJobInfo *new_prev_job;
300 CacheJobInfo *new_next_job;
301 EvPageCache *page_cache;
304 if (pixbuf_cache->start_page == start_page &&
305 pixbuf_cache->end_page == end_page)
308 page_cache = ev_document_get_page_cache (pixbuf_cache->document);
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);
314 /* We go through each job in the old cache and either clear it or move
315 * it to a new location. */
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++) {
321 dispose_cache_job_info (pixbuf_cache->prev_job + i, pixbuf_cache);
323 move_one_job (pixbuf_cache->prev_job + i,
325 new_job_list, new_prev_job, new_next_job,
326 start_page, end_page, EV_JOB_PRIORITY_LOW);
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,
335 new_job_list, new_prev_job, new_next_job,
336 start_page, end_page, EV_JOB_PRIORITY_HIGH);
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);
344 move_one_job (pixbuf_cache->next_job + i,
346 new_job_list, new_prev_job, new_next_job,
347 start_page, end_page, EV_JOB_PRIORITY_LOW);
352 g_free (pixbuf_cache->job_list);
353 g_free (pixbuf_cache->prev_job);
354 g_free (pixbuf_cache->next_job);
356 pixbuf_cache->job_list = new_job_list;
357 pixbuf_cache->prev_job = new_prev_job;
358 pixbuf_cache->next_job = new_next_job;
360 pixbuf_cache->start_page = start_page;
361 pixbuf_cache->end_page = end_page;
365 copy_job_to_job_info (EvJobRender *job_render,
366 CacheJobInfo *job_info,
367 EvPixbufCache *pixbuf_cache)
371 pixbuf = g_object_ref (job_render->pixbuf);
373 dispose_cache_job_info (job_info, pixbuf_cache);
375 job_info->pixbuf = pixbuf;
376 if (job_render->link_mapping)
377 job_info->link_mapping = job_render->link_mapping;
380 static CacheJobInfo *
381 find_job_cache (EvPixbufCache *pixbuf_cache,
386 if (page < (pixbuf_cache->start_page - pixbuf_cache->preload_cache_size) ||
387 page > (pixbuf_cache->end_page + pixbuf_cache->preload_cache_size))
390 if (page < pixbuf_cache->start_page) {
391 page_offset = (page - (pixbuf_cache->start_page - pixbuf_cache->preload_cache_size));
393 g_assert (page_offset >= 0 &&
394 page_offset < pixbuf_cache->preload_cache_size);
395 return pixbuf_cache->prev_job + page_offset;
398 if (page > pixbuf_cache->end_page) {
399 page_offset = (page - (pixbuf_cache->end_page + 1));
401 g_assert (page_offset >= 0 &&
402 page_offset < pixbuf_cache->preload_cache_size);
403 return pixbuf_cache->next_job + page_offset;
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;
413 ev_pixbuf_cache_clear_job_sizes (EvPixbufCache *pixbuf_cache,
416 EvPageCache *page_cache;
419 page_cache = ev_document_get_page_cache (pixbuf_cache->document);
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);
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);
431 #define FIRST_VISABLE_PREV(pixbuf_cache) \
432 (MAX (0, pixbuf_cache->preload_cache_size + 1 - pixbuf_cache->start_page))
435 add_job_if_needed (EvPixbufCache *pixbuf_cache,
436 CacheJobInfo *job_info,
437 EvPageCache *page_cache,
440 EvJobPriority priority)
447 ev_page_cache_get_size (page_cache,
451 if (job_info->pixbuf &&
452 gdk_pixbuf_get_width (job_info->pixbuf) == width &&
453 gdk_pixbuf_get_height (job_info->pixbuf) == height)
456 /* make a new job now */
457 job_info->job = ev_job_render_new (pixbuf_cache->document,
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);
467 ev_pixbuf_cache_add_jobs_if_needed (EvPixbufCache *pixbuf_cache,
470 EvPageCache *page_cache;
471 CacheJobInfo *job_info;
475 page_cache = ev_document_get_page_cache (pixbuf_cache->document);
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;
481 add_job_if_needed (pixbuf_cache, job_info,
482 page_cache, page, scale,
483 EV_JOB_PRIORITY_HIGH);
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;
490 add_job_if_needed (pixbuf_cache, job_info,
491 page_cache, page, scale,
492 EV_JOB_PRIORITY_LOW);
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;
499 add_job_if_needed (pixbuf_cache, job_info,
500 page_cache, page, scale,
501 EV_JOB_PRIORITY_LOW);
507 ev_pixbuf_cache_set_page_range (EvPixbufCache *pixbuf_cache,
512 EvPageCache *page_cache;
514 g_return_if_fail (EV_IS_PIXBUF_CACHE (pixbuf_cache));
516 page_cache = ev_document_get_page_cache (pixbuf_cache->document);
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);
522 /* First, resize the page_range as needed. We cull old pages
524 ev_pixbuf_cache_update_range (pixbuf_cache, start_page, end_page);
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);
530 /* Finally, we add the new jobs for all the sizes that don't have a
532 ev_pixbuf_cache_add_jobs_if_needed (pixbuf_cache, scale);
536 ev_pixbuf_cache_get_pixbuf (EvPixbufCache *pixbuf_cache,
539 CacheJobInfo *job_info;
541 job_info = find_job_cache (pixbuf_cache, page);
542 if (job_info == NULL)
545 /* We don't need to wait for the idle to handle the callback */
547 EV_JOB (job_info->job)->finished) {
548 copy_job_to_job_info (EV_JOB_RENDER (job_info->job), job_info, pixbuf_cache);
551 return job_info->pixbuf;
555 ev_pixbuf_cache_get_link_mapping (EvPixbufCache *pixbuf_cache,
558 CacheJobInfo *job_info;
560 job_info = find_job_cache (pixbuf_cache, page);
561 if (job_info == NULL)
564 /* We don't need to wait for the idle to handle the callback */
566 EV_JOB (job_info->job)->finished) {
567 copy_job_to_job_info (EV_JOB_RENDER (job_info->job), job_info, pixbuf_cache);
570 return job_info->link_mapping;