1 #include "ev-pixbuf-cache.h"
2 #include "ev-job-queue.h"
3 #include "ev-page-cache.h"
6 typedef struct _CacheJobInfo
21 /* preload_cache_size is the number of pages prior to the current
22 * visible area that we cache. It's normally 1, but could be 2 in the
25 int preload_cache_size;
26 CacheJobInfo *prev_job;
27 CacheJobInfo *job_list;
28 CacheJobInfo *next_job;
31 struct _EvPixbufCacheClass
33 GObjectClass parent_class;
35 void (* job_finished) (EvPixbufCache *pixbuf_cache);
45 static guint signals[N_SIGNALS] = {0, };
47 static void ev_pixbuf_cache_init (EvPixbufCache *pixbuf_cache);
48 static void ev_pixbuf_cache_class_init (EvPixbufCacheClass *pixbuf_cache);
49 static void ev_pixbuf_cache_finalize (GObject *object);
50 static void ev_pixbuf_cache_dispose (GObject *object);
51 static void job_finished_cb (EvJob *job,
52 EvPixbufCache *pixbuf_cache);
53 static CacheJobInfo *find_job_cache (EvPixbufCache *pixbuf_cache,
55 static void copy_job_to_job_info (EvJobRender *job_render,
56 CacheJobInfo *job_info,
57 EvPixbufCache *pixbuf_cache);
60 /* These are used for iterating through the prev and next arrays */
61 #define FIRST_VISABLE_PREV(pixbuf_cache) \
62 (MAX (0, pixbuf_cache->preload_cache_size + 1 - pixbuf_cache->start_page))
63 #define VISIBLE_NEXT_LEN(pixbuf_cache, page_cache) \
64 (MIN(pixbuf_cache->preload_cache_size, ev_page_cache_get_n_pages (page_cache) - (1 + pixbuf_cache->end_page)))
65 #define PAGE_CACHE_LEN(pixbuf_cache) \
66 ((pixbuf_cache->end_page - pixbuf_cache->start_page) + 1)
68 G_DEFINE_TYPE (EvPixbufCache, ev_pixbuf_cache, G_TYPE_OBJECT)
71 ev_pixbuf_cache_init (EvPixbufCache *pixbuf_cache)
73 pixbuf_cache->start_page = 0;
74 pixbuf_cache->end_page = 0;
75 pixbuf_cache->job_list = g_new0 (CacheJobInfo, PAGE_CACHE_LEN (pixbuf_cache));
77 pixbuf_cache->preload_cache_size = 2;
78 pixbuf_cache->prev_job = g_new0 (CacheJobInfo, pixbuf_cache->preload_cache_size);
79 pixbuf_cache->next_job = g_new0 (CacheJobInfo, pixbuf_cache->preload_cache_size);
83 ev_pixbuf_cache_class_init (EvPixbufCacheClass *class)
85 GObjectClass *object_class;
87 object_class = G_OBJECT_CLASS (class);
89 object_class->finalize = ev_pixbuf_cache_finalize;
90 object_class->dispose = ev_pixbuf_cache_dispose;
92 signals[JOB_FINISHED] = g_signal_new ("job-finished",
93 G_OBJECT_CLASS_TYPE (object_class),
94 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
95 G_STRUCT_OFFSET (EvPixbufCacheClass, job_finished),
97 g_cclosure_marshal_VOID__VOID,
102 ev_pixbuf_cache_finalize (GObject *object)
104 EvPixbufCache *pixbuf_cache;
106 pixbuf_cache = EV_PIXBUF_CACHE (object);
108 g_free (pixbuf_cache->prev_job);
109 g_free (pixbuf_cache->job_list);
110 g_free (pixbuf_cache->next_job);
114 dispose_cache_job_info (CacheJobInfo *job_info,
117 if (job_info == NULL)
120 g_signal_handlers_disconnect_by_func (job_info->job,
121 G_CALLBACK (job_finished_cb),
123 ev_job_queue_remove_job (job_info->job);
124 g_object_unref (G_OBJECT (job_info->job));
125 job_info->job = NULL;
127 if (job_info->pixbuf) {
128 g_object_unref (G_OBJECT (job_info->pixbuf));
129 job_info->pixbuf = NULL;
131 if (job_info->link_mapping) {
132 ev_link_mapping_free (job_info->link_mapping);
133 job_info->link_mapping = NULL;
138 ev_pixbuf_cache_dispose (GObject *object)
140 EvPixbufCache *pixbuf_cache;
143 pixbuf_cache = EV_PIXBUF_CACHE (object);
145 for (i = 0; i < pixbuf_cache->preload_cache_size; i++) {
146 dispose_cache_job_info (pixbuf_cache->prev_job + i, pixbuf_cache);
147 dispose_cache_job_info (pixbuf_cache->next_job + i, pixbuf_cache);
150 for (i = 0; i < PAGE_CACHE_LEN (pixbuf_cache); i++) {
151 dispose_cache_job_info (pixbuf_cache->job_list + i, pixbuf_cache);
157 ev_pixbuf_cache_new (EvDocument *document)
159 EvPixbufCache *pixbuf_cache;
161 pixbuf_cache = (EvPixbufCache *) g_object_new (EV_TYPE_PIXBUF_CACHE, NULL);
162 pixbuf_cache->document = document;
168 job_finished_cb (EvJob *job,
169 EvPixbufCache *pixbuf_cache)
171 CacheJobInfo *job_info;
172 EvJobRender *job_render = EV_JOB_RENDER (job);
175 /* If the job is outside of our interest, we silently discard it */
176 if ((job_render->page < (pixbuf_cache->start_page - pixbuf_cache->preload_cache_size)) ||
177 (job_render->page > (pixbuf_cache->end_page + pixbuf_cache->preload_cache_size))) {
178 g_object_unref (job);
182 job_info = find_job_cache (pixbuf_cache, job_render->page);
184 pixbuf = g_object_ref (job_render->pixbuf);
185 if (job_info->pixbuf)
186 g_object_unref (job_info->pixbuf);
187 job_info->pixbuf = pixbuf;
189 if (job_render->link_mapping) {
190 if (job_info->link_mapping)
191 ev_link_mapping_free (job_info->link_mapping);
192 job_info->link_mapping = job_render->link_mapping;
195 if (job_info->job == job)
196 job_info->job = NULL;
197 g_object_unref (job);
199 g_signal_emit (pixbuf_cache, signals[JOB_FINISHED], 0);
202 /* This checks a job to see if the job would generate the right sized pixbuf
203 * given a scale. If it won't, it removes the job and clears it to NULL.
206 check_job_size_and_unref (CacheJobInfo *job_info,
207 EvPageCache *page_cache,
215 if (job_info->job == NULL)
218 ev_page_cache_get_size (page_cache,
219 EV_JOB_RENDER (job_info->job)->page,
223 if (width == EV_JOB_RENDER (job_info->job)->target_width &&
224 height == EV_JOB_RENDER (job_info->job)->target_height)
227 /* Try to remove the job. If we can't, then the thread has already
228 * picked it up and we are going get a signal when it's done. If we
229 * can, then the job is fully dead and will never rnu.. */
230 if (ev_job_queue_remove_job (job_info->job))
231 g_object_unref (job_info->job);
233 job_info->job = NULL;
236 /* Do all function that copies a job from an older cache to it's position in the
237 * new cache. It clears the old job if it doesn't have a place.
240 move_one_job (CacheJobInfo *job_info,
241 EvPixbufCache *pixbuf_cache,
243 CacheJobInfo *new_job_list,
244 CacheJobInfo *new_prev_job,
245 CacheJobInfo *new_next_job,
248 EvJobPriority priority)
250 CacheJobInfo *target_page = NULL;
252 EvJobPriority new_priority;
254 if (page < (start_page - pixbuf_cache->preload_cache_size) ||
255 page > (end_page + pixbuf_cache->preload_cache_size)) {
256 dispose_cache_job_info (job_info, pixbuf_cache);
260 /* find the target page to copy it over to. */
261 if (page < start_page) {
262 page_offset = (page - (start_page - pixbuf_cache->preload_cache_size));
264 g_assert (page_offset >= 0 &&
265 page_offset < pixbuf_cache->preload_cache_size);
266 target_page = new_prev_job + page_offset;
267 new_priority = EV_JOB_PRIORITY_LOW;
268 } else if (page > end_page) {
269 page_offset = (page - (end_page + 1));
271 g_assert (page_offset >= 0 &&
272 page_offset < pixbuf_cache->preload_cache_size);
273 target_page = new_next_job + page_offset;
274 new_priority = EV_JOB_PRIORITY_LOW;
276 page_offset = page - start_page;
277 g_assert (page_offset >= 0 &&
278 page_offset <= ((end_page - start_page) + 1));
279 new_priority = EV_JOB_PRIORITY_HIGH;
280 target_page = new_job_list + page_offset;
283 *target_page = *job_info;
284 job_info->job = NULL;
285 job_info->pixbuf = NULL;
286 job_info->link_mapping = NULL;
288 if (new_priority != priority && target_page->job) {
289 ev_job_queue_update_job (target_page->job, new_priority);
296 ev_pixbuf_cache_update_range (EvPixbufCache *pixbuf_cache,
300 CacheJobInfo *new_job_list;
301 CacheJobInfo *new_prev_job;
302 CacheJobInfo *new_next_job;
303 EvPageCache *page_cache;
306 if (pixbuf_cache->start_page == start_page &&
307 pixbuf_cache->end_page == end_page)
310 page_cache = ev_page_cache_get (pixbuf_cache->document);
312 new_job_list = g_new0 (CacheJobInfo, (end_page - start_page) + 1);
313 new_prev_job = g_new0 (CacheJobInfo, pixbuf_cache->preload_cache_size);
314 new_next_job = g_new0 (CacheJobInfo, pixbuf_cache->preload_cache_size);
316 /* We go through each job in the old cache and either clear it or move
317 * it to a new location. */
319 /* Start with the prev cache. */
320 page = pixbuf_cache->start_page - pixbuf_cache->preload_cache_size;
321 for (i = 0; i < pixbuf_cache->preload_cache_size; i++) {
323 dispose_cache_job_info (pixbuf_cache->prev_job + i, pixbuf_cache);
325 move_one_job (pixbuf_cache->prev_job + i,
327 new_job_list, new_prev_job, new_next_job,
328 start_page, end_page, EV_JOB_PRIORITY_LOW);
333 page = pixbuf_cache->start_page;
334 for (i = 0; i < PAGE_CACHE_LEN (pixbuf_cache); i++) {
335 move_one_job (pixbuf_cache->job_list + i,
337 new_job_list, new_prev_job, new_next_job,
338 start_page, end_page, EV_JOB_PRIORITY_HIGH);
342 for (i = 0; i < pixbuf_cache->preload_cache_size; i++) {
343 if (page >= ev_page_cache_get_n_pages (page_cache)) {
344 dispose_cache_job_info (pixbuf_cache->next_job + i, pixbuf_cache);
346 move_one_job (pixbuf_cache->next_job + i,
348 new_job_list, new_prev_job, new_next_job,
349 start_page, end_page, EV_JOB_PRIORITY_LOW);
354 g_free (pixbuf_cache->job_list);
355 g_free (pixbuf_cache->prev_job);
356 g_free (pixbuf_cache->next_job);
358 pixbuf_cache->job_list = new_job_list;
359 pixbuf_cache->prev_job = new_prev_job;
360 pixbuf_cache->next_job = new_next_job;
362 pixbuf_cache->start_page = start_page;
363 pixbuf_cache->end_page = end_page;
367 copy_job_to_job_info (EvJobRender *job_render,
368 CacheJobInfo *job_info,
369 EvPixbufCache *pixbuf_cache)
373 pixbuf = g_object_ref (job_render->pixbuf);
375 dispose_cache_job_info (job_info, pixbuf_cache);
377 job_info->pixbuf = pixbuf;
378 if (job_render->link_mapping)
379 job_info->link_mapping = job_render->link_mapping;
382 static CacheJobInfo *
383 find_job_cache (EvPixbufCache *pixbuf_cache,
388 if (page < (pixbuf_cache->start_page - pixbuf_cache->preload_cache_size) ||
389 page > (pixbuf_cache->end_page + pixbuf_cache->preload_cache_size))
392 if (page < pixbuf_cache->start_page) {
393 page_offset = (page - (pixbuf_cache->start_page - pixbuf_cache->preload_cache_size));
395 g_assert (page_offset >= 0 &&
396 page_offset < pixbuf_cache->preload_cache_size);
397 return pixbuf_cache->prev_job + page_offset;
400 if (page > pixbuf_cache->end_page) {
401 page_offset = (page - (pixbuf_cache->end_page + 1));
403 g_assert (page_offset >= 0 &&
404 page_offset < pixbuf_cache->preload_cache_size);
405 return pixbuf_cache->next_job + page_offset;
408 page_offset = page - pixbuf_cache->start_page;
409 g_assert (page_offset >= 0 &&
410 page_offset <= PAGE_CACHE_LEN(pixbuf_cache));
411 return pixbuf_cache->job_list + page_offset;
415 ev_pixbuf_cache_clear_job_sizes (EvPixbufCache *pixbuf_cache,
418 EvPageCache *page_cache;
421 page_cache = ev_page_cache_get (pixbuf_cache->document);
423 for (i = 0; i < PAGE_CACHE_LEN (pixbuf_cache); i++) {
424 check_job_size_and_unref (pixbuf_cache->job_list + i, page_cache, scale);
427 for (i = 0; i < pixbuf_cache->preload_cache_size; i++) {
428 check_job_size_and_unref (pixbuf_cache->prev_job + i, page_cache, scale);
429 check_job_size_and_unref (pixbuf_cache->next_job + i, page_cache, scale);
433 #define FIRST_VISABLE_PREV(pixbuf_cache) \
434 (MAX (0, pixbuf_cache->preload_cache_size + 1 - pixbuf_cache->start_page))
437 add_job_if_needed (EvPixbufCache *pixbuf_cache,
438 CacheJobInfo *job_info,
439 EvPageCache *page_cache,
442 EvJobPriority priority)
449 ev_page_cache_get_size (page_cache,
453 if (job_info->pixbuf &&
454 gdk_pixbuf_get_width (job_info->pixbuf) == width &&
455 gdk_pixbuf_get_height (job_info->pixbuf) == height)
458 /* make a new job now */
459 job_info->job = ev_job_render_new (pixbuf_cache->document,
462 (job_info->link_mapping == NULL)?TRUE:FALSE);
463 ev_job_queue_add_job (job_info->job, priority);
464 g_signal_connect (job_info->job, "finished", G_CALLBACK (job_finished_cb), pixbuf_cache);
469 ev_pixbuf_cache_add_jobs_if_needed (EvPixbufCache *pixbuf_cache,
472 EvPageCache *page_cache;
473 CacheJobInfo *job_info;
477 page_cache = ev_page_cache_get (pixbuf_cache->document);
479 for (i = 0; i < PAGE_CACHE_LEN (pixbuf_cache); i++) {
480 job_info = (pixbuf_cache->job_list + i);
481 page = pixbuf_cache->start_page + i;
483 add_job_if_needed (pixbuf_cache, job_info,
484 page_cache, page, scale,
485 EV_JOB_PRIORITY_HIGH);
488 for (i = FIRST_VISABLE_PREV(pixbuf_cache); i < pixbuf_cache->preload_cache_size; i++) {
489 job_info = (pixbuf_cache->prev_job + i);
490 page = pixbuf_cache->start_page - pixbuf_cache->preload_cache_size + i;
492 add_job_if_needed (pixbuf_cache, job_info,
493 page_cache, page, scale,
494 EV_JOB_PRIORITY_LOW);
497 for (i = 0; i < VISIBLE_NEXT_LEN(pixbuf_cache, page_cache); i++) {
498 job_info = (pixbuf_cache->next_job + i);
499 page = pixbuf_cache->end_page + 1 + i;
501 add_job_if_needed (pixbuf_cache, job_info,
502 page_cache, page, scale,
503 EV_JOB_PRIORITY_LOW);
509 ev_pixbuf_cache_set_page_range (EvPixbufCache *pixbuf_cache,
514 EvPageCache *page_cache;
516 g_return_if_fail (EV_IS_PIXBUF_CACHE (pixbuf_cache));
518 page_cache = ev_page_cache_get (pixbuf_cache->document);
520 g_return_if_fail (start_page >= 0 && start_page < ev_page_cache_get_n_pages (page_cache));
521 g_return_if_fail (end_page >= 0 && end_page < ev_page_cache_get_n_pages (page_cache));
522 g_return_if_fail (end_page >= start_page);
524 /* First, resize the page_range as needed. We cull old pages
526 ev_pixbuf_cache_update_range (pixbuf_cache, start_page, end_page);
528 /* Then, we update the current jobs to see if any of them are the wrong
529 * size, we remove them if we need to. */
530 ev_pixbuf_cache_clear_job_sizes (pixbuf_cache, scale);
532 /* Finally, we add the new jobs for all the sizes that don't have a
534 ev_pixbuf_cache_add_jobs_if_needed (pixbuf_cache, scale);
538 ev_pixbuf_cache_get_pixbuf (EvPixbufCache *pixbuf_cache,
541 CacheJobInfo *job_info;
543 job_info = find_job_cache (pixbuf_cache, page);
544 if (job_info == NULL)
547 /* We don't need to wait for the idle to handle the callback */
549 EV_JOB (job_info->job)->finished) {
550 copy_job_to_job_info (EV_JOB_RENDER (job_info->job), job_info, pixbuf_cache);
553 return job_info->pixbuf;
557 ev_pixbuf_cache_get_link_mapping (EvPixbufCache *pixbuf_cache,
560 CacheJobInfo *job_info;
562 job_info = find_job_cache (pixbuf_cache, page);
563 if (job_info == NULL)
566 /* We don't need to wait for the idle to handle the callback */
568 EV_JOB (job_info->job)->finished) {
569 copy_job_to_job_info (EV_JOB_RENDER (job_info->job), job_info, pixbuf_cache);
572 return job_info->link_mapping;