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 ev_job_queue_remove_job (job_info->job);
123 g_object_unref (G_OBJECT (job_info->job));
124 job_info->job = NULL;
126 if (job_info->pixbuf) {
127 g_object_unref (G_OBJECT (job_info->pixbuf));
128 job_info->pixbuf = NULL;
130 if (job_info->link_mapping) {
131 ev_link_mapping_free (job_info->link_mapping);
132 job_info->link_mapping = NULL;
137 ev_pixbuf_cache_dispose (GObject *object)
139 EvPixbufCache *pixbuf_cache;
142 pixbuf_cache = EV_PIXBUF_CACHE (object);
144 for (i = 0; i < pixbuf_cache->preload_cache_size; i++) {
145 dispose_cache_job_info (pixbuf_cache->prev_job + i, pixbuf_cache);
146 dispose_cache_job_info (pixbuf_cache->next_job + i, pixbuf_cache);
149 for (i = 0; i < PAGE_CACHE_LEN (pixbuf_cache); i++) {
150 dispose_cache_job_info (pixbuf_cache->job_list + i, pixbuf_cache);
156 ev_pixbuf_cache_new (EvDocument *document)
158 EvPixbufCache *pixbuf_cache;
160 pixbuf_cache = (EvPixbufCache *) g_object_new (EV_TYPE_PIXBUF_CACHE, NULL);
161 pixbuf_cache->document = document;
167 job_finished_cb (EvJob *job,
168 EvPixbufCache *pixbuf_cache)
170 CacheJobInfo *job_info;
171 EvJobRender *job_render = EV_JOB_RENDER (job);
174 /* If the job is outside of our interest, we silently discard it */
175 if ((job_render->page < (pixbuf_cache->start_page - pixbuf_cache->preload_cache_size)) ||
176 (job_render->page > (pixbuf_cache->end_page + pixbuf_cache->preload_cache_size))) {
177 g_object_unref (job);
181 job_info = find_job_cache (pixbuf_cache, job_render->page);
183 pixbuf = g_object_ref (job_render->pixbuf);
184 if (job_info->pixbuf)
185 g_object_unref (job_info->pixbuf);
186 job_info->pixbuf = pixbuf;
188 if (job_render->link_mapping) {
189 if (job_info->link_mapping)
190 ev_link_mapping_free (job_info->link_mapping);
191 job_info->link_mapping = job_render->link_mapping;
194 if (job_info->job == job)
195 job_info->job = NULL;
196 g_object_unref (job);
198 g_signal_emit (pixbuf_cache, signals[JOB_FINISHED], 0);
201 /* This checks a job to see if the job would generate the right sized pixbuf
202 * given a scale. If it won't, it removes the job and clears it to NULL.
205 check_job_size_and_unref (CacheJobInfo *job_info,
206 EvPageCache *page_cache,
214 if (job_info->job == NULL)
217 ev_page_cache_get_size (page_cache,
218 EV_JOB_RENDER (job_info->job)->page,
222 if (width == EV_JOB_RENDER (job_info->job)->target_width &&
223 height == EV_JOB_RENDER (job_info->job)->target_height)
226 /* Try to remove the job. If we can't, then the thread has already
227 * picked it up and we are going get a signal when it's done. If we
228 * can, then the job is fully dead and will never rnu.. */
229 if (ev_job_queue_remove_job (job_info->job))
230 g_object_unref (job_info->job);
232 job_info->job = NULL;
235 /* Do all function that copies a job from an older cache to it's position in the
236 * new cache. It clears the old job if it doesn't have a place.
239 move_one_job (CacheJobInfo *job_info,
240 EvPixbufCache *pixbuf_cache,
242 CacheJobInfo *new_job_list,
243 CacheJobInfo *new_prev_job,
244 CacheJobInfo *new_next_job,
247 EvJobPriority priority)
249 CacheJobInfo *target_page = NULL;
251 EvJobPriority new_priority;
253 if (page < (start_page - pixbuf_cache->preload_cache_size) ||
254 page > (end_page + pixbuf_cache->preload_cache_size)) {
255 dispose_cache_job_info (job_info, pixbuf_cache);
259 /* find the target page to copy it over to. */
260 if (page < start_page) {
261 page_offset = (page - (start_page - pixbuf_cache->preload_cache_size));
263 g_assert (page_offset >= 0 &&
264 page_offset < pixbuf_cache->preload_cache_size);
265 target_page = new_prev_job + page_offset;
266 new_priority = EV_JOB_PRIORITY_LOW;
267 } else if (page > end_page) {
268 page_offset = (page - (end_page + 1));
270 g_assert (page_offset >= 0 &&
271 page_offset < pixbuf_cache->preload_cache_size);
272 target_page = new_next_job + page_offset;
273 new_priority = EV_JOB_PRIORITY_LOW;
275 page_offset = page - start_page;
276 g_assert (page_offset >= 0 &&
277 page_offset <= ((end_page - start_page) + 1));
278 new_priority = EV_JOB_PRIORITY_HIGH;
279 target_page = new_job_list + page_offset;
282 *target_page = *job_info;
283 job_info->job = NULL;
284 job_info->pixbuf = NULL;
285 job_info->link_mapping = NULL;
287 if (new_priority != priority && target_page->job) {
288 ev_job_queue_update_job (target_page->job, new_priority);
295 ev_pixbuf_cache_update_range (EvPixbufCache *pixbuf_cache,
299 CacheJobInfo *new_job_list;
300 CacheJobInfo *new_prev_job;
301 CacheJobInfo *new_next_job;
302 EvPageCache *page_cache;
305 if (pixbuf_cache->start_page == start_page &&
306 pixbuf_cache->end_page == end_page)
309 page_cache = ev_document_get_page_cache (pixbuf_cache->document);
311 new_job_list = g_new0 (CacheJobInfo, (end_page - start_page) + 1);
312 new_prev_job = g_new0 (CacheJobInfo, pixbuf_cache->preload_cache_size);
313 new_next_job = g_new0 (CacheJobInfo, pixbuf_cache->preload_cache_size);
315 /* We go through each job in the old cache and either clear it or move
316 * it to a new location. */
318 /* Start with the prev cache. */
319 page = pixbuf_cache->start_page - pixbuf_cache->preload_cache_size;
320 for (i = 0; i < pixbuf_cache->preload_cache_size; i++) {
322 dispose_cache_job_info (pixbuf_cache->prev_job + i, pixbuf_cache);
324 move_one_job (pixbuf_cache->prev_job + i,
326 new_job_list, new_prev_job, new_next_job,
327 start_page, end_page, EV_JOB_PRIORITY_LOW);
332 page = pixbuf_cache->start_page;
333 for (i = 0; i < PAGE_CACHE_LEN (pixbuf_cache); i++) {
334 move_one_job (pixbuf_cache->job_list + i,
336 new_job_list, new_prev_job, new_next_job,
337 start_page, end_page, EV_JOB_PRIORITY_HIGH);
341 for (i = 0; i < pixbuf_cache->preload_cache_size; i++) {
342 if (page >= ev_page_cache_get_n_pages (page_cache)) {
343 dispose_cache_job_info (pixbuf_cache->next_job + i, pixbuf_cache);
345 move_one_job (pixbuf_cache->next_job + i,
347 new_job_list, new_prev_job, new_next_job,
348 start_page, end_page, EV_JOB_PRIORITY_LOW);
353 g_free (pixbuf_cache->job_list);
354 g_free (pixbuf_cache->prev_job);
355 g_free (pixbuf_cache->next_job);
357 pixbuf_cache->job_list = new_job_list;
358 pixbuf_cache->prev_job = new_prev_job;
359 pixbuf_cache->next_job = new_next_job;
361 pixbuf_cache->start_page = start_page;
362 pixbuf_cache->end_page = end_page;
366 copy_job_to_job_info (EvJobRender *job_render,
367 CacheJobInfo *job_info,
368 EvPixbufCache *pixbuf_cache)
372 pixbuf = g_object_ref (job_render->pixbuf);
374 dispose_cache_job_info (job_info, pixbuf_cache);
376 job_info->pixbuf = pixbuf;
377 if (job_render->link_mapping)
378 job_info->link_mapping = job_render->link_mapping;
381 static CacheJobInfo *
382 find_job_cache (EvPixbufCache *pixbuf_cache,
387 if (page < (pixbuf_cache->start_page - pixbuf_cache->preload_cache_size) ||
388 page > (pixbuf_cache->end_page + pixbuf_cache->preload_cache_size))
391 if (page < pixbuf_cache->start_page) {
392 page_offset = (page - (pixbuf_cache->start_page - pixbuf_cache->preload_cache_size));
394 g_assert (page_offset >= 0 &&
395 page_offset < pixbuf_cache->preload_cache_size);
396 return pixbuf_cache->prev_job + page_offset;
399 if (page > pixbuf_cache->end_page) {
400 page_offset = (page - (pixbuf_cache->end_page + 1));
402 g_assert (page_offset >= 0 &&
403 page_offset < pixbuf_cache->preload_cache_size);
404 return pixbuf_cache->next_job + page_offset;
407 page_offset = page - pixbuf_cache->start_page;
408 g_assert (page_offset >= 0 &&
409 page_offset <= PAGE_CACHE_LEN(pixbuf_cache));
410 return pixbuf_cache->job_list + page_offset;
414 ev_pixbuf_cache_clear_job_sizes (EvPixbufCache *pixbuf_cache,
417 EvPageCache *page_cache;
420 page_cache = ev_document_get_page_cache (pixbuf_cache->document);
422 for (i = 0; i < PAGE_CACHE_LEN (pixbuf_cache); i++) {
423 check_job_size_and_unref (pixbuf_cache->job_list + i, page_cache, scale);
426 for (i = 0; i < pixbuf_cache->preload_cache_size; i++) {
427 check_job_size_and_unref (pixbuf_cache->prev_job + i, page_cache, scale);
428 check_job_size_and_unref (pixbuf_cache->next_job + i, page_cache, scale);
432 #define FIRST_VISABLE_PREV(pixbuf_cache) \
433 (MAX (0, pixbuf_cache->preload_cache_size + 1 - pixbuf_cache->start_page))
436 add_job_if_needed (EvPixbufCache *pixbuf_cache,
437 CacheJobInfo *job_info,
438 EvPageCache *page_cache,
441 EvJobPriority priority)
448 ev_page_cache_get_size (page_cache,
452 if (job_info->pixbuf &&
453 gdk_pixbuf_get_width (job_info->pixbuf) == width &&
454 gdk_pixbuf_get_height (job_info->pixbuf) == height)
457 /* make a new job now */
458 job_info->job = ev_job_render_new (pixbuf_cache->document,
461 (job_info->link_mapping == NULL)?TRUE:FALSE);
462 ev_job_queue_add_job (job_info->job, priority);
463 g_signal_connect (job_info->job, "finished", G_CALLBACK (job_finished_cb), pixbuf_cache);
468 ev_pixbuf_cache_add_jobs_if_needed (EvPixbufCache *pixbuf_cache,
471 EvPageCache *page_cache;
472 CacheJobInfo *job_info;
476 page_cache = ev_document_get_page_cache (pixbuf_cache->document);
478 for (i = 0; i < PAGE_CACHE_LEN (pixbuf_cache); i++) {
479 job_info = (pixbuf_cache->job_list + i);
480 page = pixbuf_cache->start_page + i;
482 add_job_if_needed (pixbuf_cache, job_info,
483 page_cache, page, scale,
484 EV_JOB_PRIORITY_HIGH);
487 for (i = FIRST_VISABLE_PREV(pixbuf_cache); i < pixbuf_cache->preload_cache_size; i++) {
488 job_info = (pixbuf_cache->prev_job + i);
489 page = pixbuf_cache->start_page - pixbuf_cache->preload_cache_size + i;
491 add_job_if_needed (pixbuf_cache, job_info,
492 page_cache, page, scale,
493 EV_JOB_PRIORITY_LOW);
496 for (i = 0; i < VISIBLE_NEXT_LEN(pixbuf_cache, page_cache); i++) {
497 job_info = (pixbuf_cache->next_job + i);
498 page = pixbuf_cache->end_page + 1 + i;
500 add_job_if_needed (pixbuf_cache, job_info,
501 page_cache, page, scale,
502 EV_JOB_PRIORITY_LOW);
508 ev_pixbuf_cache_set_page_range (EvPixbufCache *pixbuf_cache,
513 EvPageCache *page_cache;
515 g_return_if_fail (EV_IS_PIXBUF_CACHE (pixbuf_cache));
517 page_cache = ev_document_get_page_cache (pixbuf_cache->document);
519 g_return_if_fail (start_page >= 0 && start_page < ev_page_cache_get_n_pages (page_cache));
520 g_return_if_fail (end_page >= 0 && end_page < ev_page_cache_get_n_pages (page_cache));
521 g_return_if_fail (end_page >= start_page);
523 /* First, resize the page_range as needed. We cull old pages
525 ev_pixbuf_cache_update_range (pixbuf_cache, start_page, end_page);
527 /* Then, we update the current jobs to see if any of them are the wrong
528 * size, we remove them if we need to. */
529 ev_pixbuf_cache_clear_job_sizes (pixbuf_cache, scale);
531 /* Finally, we add the new jobs for all the sizes that don't have a
533 ev_pixbuf_cache_add_jobs_if_needed (pixbuf_cache, scale);
537 ev_pixbuf_cache_get_pixbuf (EvPixbufCache *pixbuf_cache,
540 CacheJobInfo *job_info;
542 job_info = find_job_cache (pixbuf_cache, page);
543 if (job_info == NULL)
546 /* We don't need to wait for the idle to handle the callback */
548 EV_JOB (job_info->job)->finished) {
549 copy_job_to_job_info (EV_JOB_RENDER (job_info->job), job_info, pixbuf_cache);
552 return job_info->pixbuf;
556 ev_pixbuf_cache_get_link_mapping (EvPixbufCache *pixbuf_cache,
559 CacheJobInfo *job_info;
561 job_info = find_job_cache (pixbuf_cache, page);
562 if (job_info == NULL)
565 /* We don't need to wait for the idle to handle the callback */
567 EV_JOB (job_info->job)->finished) {
568 copy_job_to_job_info (EV_JOB_RENDER (job_info->job), job_info, pixbuf_cache);
571 return job_info->link_mapping;