1 #include "ev-pixbuf-cache.h"
2 #include "ev-job-queue.h"
5 typedef struct _CacheJobInfo
19 /* preload_cache_size is the number of pages prior to the current
20 * visible area that we cache. It's normally 1, but could be 2 in the
23 int preload_cache_size;
24 CacheJobInfo *prev_job;
25 CacheJobInfo *job_list;
26 CacheJobInfo *next_job;
29 struct _EvPixbufCacheClass
31 GObjectClass parent_class;
33 void (* job_finished) (EvPixbufCache *pixbuf_cache);
43 static guint signals[N_SIGNALS] = {0, };
45 static void ev_pixbuf_cache_init (EvPixbufCache *pixbuf_cache);
46 static void ev_pixbuf_cache_class_init (EvPixbufCacheClass *pixbuf_cache);
47 static void ev_pixbuf_cache_finalize (GObject *object);
48 static void ev_pixbuf_cache_dispose (GObject *object);
49 static void job_finished_cb (EvJob *job,
50 EvPixbufCache *pixbuf_cache);
51 static CacheJobInfo *find_job_cache (EvPixbufCache *pixbuf_cache,
56 /* These are used for iterating through the prev and next arrays */
57 #define FIRST_VISABLE_PREV(pixbuf_cache) \
58 (MAX (0, pixbuf_cache->preload_cache_size + 1 - pixbuf_cache->start_page))
59 #define VISIBLE_NEXT_LEN(pixbuf_cache, page_cache) \
60 (MIN(pixbuf_cache->preload_cache_size, ev_page_cache_get_n_pages (page_cache) - pixbuf_cache->end_page))
61 #define PAGE_CACHE_LEN(pixbuf_cache) \
62 ((pixbuf_cache->end_page - pixbuf_cache->start_page) + 1)
64 G_DEFINE_TYPE (EvPixbufCache, ev_pixbuf_cache, G_TYPE_OBJECT)
67 ev_pixbuf_cache_init (EvPixbufCache *pixbuf_cache)
69 pixbuf_cache->start_page = 1;
70 pixbuf_cache->end_page = 1;
71 pixbuf_cache->job_list = g_new0 (CacheJobInfo, PAGE_CACHE_LEN (pixbuf_cache));
73 pixbuf_cache->preload_cache_size = 1;
74 pixbuf_cache->prev_job = g_new0 (CacheJobInfo, pixbuf_cache->preload_cache_size);
75 pixbuf_cache->next_job = g_new0 (CacheJobInfo, pixbuf_cache->preload_cache_size);
79 ev_pixbuf_cache_class_init (EvPixbufCacheClass *class)
81 GObjectClass *object_class;
83 object_class = G_OBJECT_CLASS (class);
85 object_class->finalize = ev_pixbuf_cache_finalize;
86 object_class->dispose = ev_pixbuf_cache_dispose;
88 signals[JOB_FINISHED] = g_signal_new ("job-finished",
89 G_OBJECT_CLASS_TYPE (object_class),
90 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
91 G_STRUCT_OFFSET (EvPixbufCacheClass, job_finished),
93 g_cclosure_marshal_VOID__VOID,
98 ev_pixbuf_cache_finalize (GObject *object)
100 EvPixbufCache *pixbuf_cache;
102 pixbuf_cache = EV_PIXBUF_CACHE (object);
104 g_free (pixbuf_cache->prev_job);
105 g_free (pixbuf_cache->job_list);
106 g_free (pixbuf_cache->next_job);
110 dispose_cache_job_info (CacheJobInfo *job_info,
113 if (job_info == NULL)
116 g_signal_handlers_disconnect_by_func (job_info->job,
117 G_CALLBACK (job_finished_cb),
119 g_object_unref (G_OBJECT (job_info->job));
120 job_info->job = NULL;
122 if (job_info->pixbuf) {
123 g_object_unref (G_OBJECT (job_info->pixbuf));
124 job_info->pixbuf = NULL;
129 ev_pixbuf_cache_dispose (GObject *object)
131 EvPixbufCache *pixbuf_cache;
134 pixbuf_cache = EV_PIXBUF_CACHE (object);
136 for (i = 0; i < pixbuf_cache->preload_cache_size; i++) {
137 dispose_cache_job_info (pixbuf_cache->prev_job + i, pixbuf_cache);
138 dispose_cache_job_info (pixbuf_cache->next_job + i, pixbuf_cache);
141 for (i = 0; i < PAGE_CACHE_LEN (pixbuf_cache); i++) {
142 dispose_cache_job_info (pixbuf_cache->job_list + i, pixbuf_cache);
148 ev_pixbuf_cache_new (EvDocument *document)
150 EvPixbufCache *pixbuf_cache;
152 pixbuf_cache = (EvPixbufCache *) g_object_new (EV_TYPE_PIXBUF_CACHE, NULL);
153 pixbuf_cache->document = document;
159 job_finished_cb (EvJob *job,
160 EvPixbufCache *pixbuf_cache)
162 CacheJobInfo *job_info;
163 EvJobRender *job_render = EV_JOB_RENDER (job);
166 /* If the job is outside of our interest, we silently discard it */
167 if ((job_render->page < (pixbuf_cache->start_page - pixbuf_cache->preload_cache_size)) ||
168 (job_render->page > (pixbuf_cache->end_page + pixbuf_cache->preload_cache_size))) {
169 g_object_unref (job);
173 job_info = find_job_cache (pixbuf_cache, job_render->page);
175 pixbuf = g_object_ref (job_render->pixbuf);
176 if (job_info->pixbuf)
177 g_object_unref (job_info->pixbuf);
178 job_info->pixbuf = pixbuf;
180 if (job_info->job == job)
181 job_info->job = NULL;
182 g_object_unref (job);
184 g_signal_emit (pixbuf_cache, signals[JOB_FINISHED], 0);
187 /* This checks a job to see if the job would generate the right sized pixbuf
188 * given a scale. If it won't, it removes the job and clears it to NULL.
191 check_job_size_and_unref (CacheJobInfo *job_info,
192 EvPageCache *page_cache,
200 if (job_info->job == NULL)
203 ev_page_cache_get_size (page_cache,
204 EV_JOB_RENDER (job_info->job)->page,
208 if (width == EV_JOB_RENDER (job_info->job)->target_width &&
209 height == EV_JOB_RENDER (job_info->job)->target_height)
212 /* Try to remove the job. If we can't, then the thread has already
213 * picked it up and we are going get a signal when it's done. If we
214 * can, then the job is fully dead and will never rnu.. */
215 if (ev_job_queue_remove_job (job_info->job))
216 g_object_unref (job_info->job);
218 job_info->job = NULL;
221 /* Do all function that copies a job from an older cache to it's position in the
222 * new cache. It clears the old job if it doesn't have a place.
225 move_one_job (CacheJobInfo *job_info,
226 EvPixbufCache *pixbuf_cache,
228 CacheJobInfo *new_job_list,
229 CacheJobInfo *new_prev_job,
230 CacheJobInfo *new_next_job,
233 EvJobPriority priority)
235 CacheJobInfo *target_page = NULL;
237 EvJobPriority new_priority;
239 if (page < (start_page - pixbuf_cache->preload_cache_size) ||
240 page > (end_page + pixbuf_cache->preload_cache_size)) {
241 dispose_cache_job_info (job_info, pixbuf_cache);
245 /* find the target page to copy it over to. */
246 if (page < start_page) {
247 page_offset = (page - (start_page - pixbuf_cache->preload_cache_size));
249 g_assert (page_offset >= 0 &&
250 page_offset < pixbuf_cache->preload_cache_size);
251 target_page = new_prev_job + page_offset;
252 new_priority = EV_JOB_PRIORITY_LOW;
253 } else if (page > end_page) {
254 page_offset = (page - (end_page + 1));
256 g_assert (page_offset >= 0 &&
257 page_offset < pixbuf_cache->preload_cache_size);
258 target_page = new_next_job + page_offset;
259 new_priority = EV_JOB_PRIORITY_LOW;
261 page_offset = page - start_page;
262 g_assert (page_offset >= 0 &&
263 page_offset <= ((end_page - start_page) + 1));
264 new_priority = EV_JOB_PRIORITY_HIGH;
265 target_page = new_job_list + page_offset;
268 *target_page = *job_info;
269 job_info->job = NULL;
270 job_info->pixbuf = NULL;
272 if (new_priority != priority && target_page->job) {
273 g_print ("FIXME: update priority \n");
280 ev_pixbuf_cache_update_range (EvPixbufCache *pixbuf_cache,
284 CacheJobInfo *new_job_list;
285 CacheJobInfo *new_prev_job;
286 CacheJobInfo *new_next_job;
287 EvPageCache *page_cache;
290 if (pixbuf_cache->start_page == start_page &&
291 pixbuf_cache->end_page == end_page)
294 page_cache = ev_document_get_page_cache (pixbuf_cache->document);
296 new_job_list = g_new0 (CacheJobInfo, (end_page - start_page) + 1);
297 new_prev_job = g_new0 (CacheJobInfo, pixbuf_cache->preload_cache_size);
298 new_next_job = g_new0 (CacheJobInfo, pixbuf_cache->preload_cache_size);
300 /* We go through each job in the old cache and either clear it or move
301 * it to a new location. */
303 /* Start with the prev cache. */
304 page = pixbuf_cache->start_page - pixbuf_cache->preload_cache_size;
305 for (i = 0; i < pixbuf_cache->preload_cache_size; i++) {
307 dispose_cache_job_info (pixbuf_cache->prev_job + i, pixbuf_cache);
309 move_one_job (pixbuf_cache->prev_job + i,
311 new_job_list, new_prev_job, new_next_job,
312 start_page, end_page, EV_JOB_PRIORITY_LOW);
317 page = pixbuf_cache->start_page;
318 for (i = 0; i < PAGE_CACHE_LEN (pixbuf_cache); i++) {
319 move_one_job (pixbuf_cache->job_list + i,
321 new_job_list, new_prev_job, new_next_job,
322 start_page, end_page, EV_JOB_PRIORITY_HIGH);
326 for (i = 0; i < pixbuf_cache->preload_cache_size; i++) {
327 if (page > ev_page_cache_get_n_pages (page_cache)) {
328 dispose_cache_job_info (pixbuf_cache->next_job + i, pixbuf_cache);
330 move_one_job (pixbuf_cache->next_job + i,
332 new_job_list, new_prev_job, new_next_job,
333 start_page, end_page, EV_JOB_PRIORITY_LOW);
338 g_free (pixbuf_cache->job_list);
339 g_free (pixbuf_cache->prev_job);
340 g_free (pixbuf_cache->next_job);
342 pixbuf_cache->job_list = new_job_list;
343 pixbuf_cache->prev_job = new_prev_job;
344 pixbuf_cache->next_job = new_next_job;
346 pixbuf_cache->start_page = start_page;
347 pixbuf_cache->end_page = end_page;
350 static CacheJobInfo *
351 find_job_cache (EvPixbufCache *pixbuf_cache,
356 if (page < (pixbuf_cache->start_page - pixbuf_cache->preload_cache_size) ||
357 page > (pixbuf_cache->end_page + pixbuf_cache->preload_cache_size))
360 if (page < pixbuf_cache->start_page) {
361 page_offset = (page - (pixbuf_cache->start_page - pixbuf_cache->preload_cache_size));
363 g_assert (page_offset >= 0 &&
364 page_offset < pixbuf_cache->preload_cache_size);
365 return pixbuf_cache->prev_job + page_offset;
368 if (page > pixbuf_cache->end_page) {
369 page_offset = (page - (pixbuf_cache->end_page + 1));
371 g_assert (page_offset >= 0 &&
372 page_offset < pixbuf_cache->preload_cache_size);
373 return pixbuf_cache->next_job + page_offset;
376 page_offset = page - pixbuf_cache->start_page;
377 g_assert (page_offset >= 0 &&
378 page_offset <= PAGE_CACHE_LEN(pixbuf_cache));
379 return pixbuf_cache->job_list + page_offset;
383 ev_pixbuf_cache_clear_job_sizes (EvPixbufCache *pixbuf_cache,
386 EvPageCache *page_cache;
389 page_cache = ev_document_get_page_cache (pixbuf_cache->document);
391 for (i = 0; i < PAGE_CACHE_LEN (pixbuf_cache); i++) {
392 check_job_size_and_unref (pixbuf_cache->job_list + i, page_cache, scale);
395 for (i = 0; i < pixbuf_cache->preload_cache_size; i++) {
396 check_job_size_and_unref (pixbuf_cache->prev_job + i, page_cache, scale);
397 check_job_size_and_unref (pixbuf_cache->next_job + i, page_cache, scale);
401 #define FIRST_VISABLE_PREV(pixbuf_cache) \
402 (MAX (0, pixbuf_cache->preload_cache_size + 1 - pixbuf_cache->start_page))
405 add_job_if_needed (EvPixbufCache *pixbuf_cache,
406 CacheJobInfo *job_info,
407 EvPageCache *page_cache,
410 EvJobPriority priority)
417 ev_page_cache_get_size (page_cache,
421 if (job_info->pixbuf &&
422 gdk_pixbuf_get_width (job_info->pixbuf) == width &&
423 gdk_pixbuf_get_height (job_info->pixbuf) == height)
426 /* make a new job now */
427 job_info->job = ev_job_render_new (pixbuf_cache->document,
430 ev_job_queue_add_job (job_info->job, priority);
431 g_signal_connect (job_info->job, "finished", G_CALLBACK (job_finished_cb), pixbuf_cache);
436 ev_pixbuf_cache_add_jobs_if_needed (EvPixbufCache *pixbuf_cache,
439 EvPageCache *page_cache;
440 CacheJobInfo *job_info;
444 page_cache = ev_document_get_page_cache (pixbuf_cache->document);
446 for (i = 0; i < PAGE_CACHE_LEN (pixbuf_cache); i++) {
447 job_info = (pixbuf_cache->job_list + i);
448 page = pixbuf_cache->start_page + i;
450 add_job_if_needed (pixbuf_cache, job_info,
451 page_cache, page, scale,
452 EV_JOB_PRIORITY_HIGH);
455 for (i = FIRST_VISABLE_PREV(pixbuf_cache); i < pixbuf_cache->preload_cache_size; i++) {
456 job_info = (pixbuf_cache->prev_job + i);
457 page = pixbuf_cache->start_page - pixbuf_cache->preload_cache_size + i;
459 add_job_if_needed (pixbuf_cache, job_info,
460 page_cache, page, scale,
461 EV_JOB_PRIORITY_LOW);
464 for (i = 0; i < VISIBLE_NEXT_LEN(pixbuf_cache, page_cache); i++) {
465 job_info = (pixbuf_cache->next_job + i);
466 page = pixbuf_cache->end_page + 1 + i;
468 add_job_if_needed (pixbuf_cache, job_info,
469 page_cache, page, scale,
470 EV_JOB_PRIORITY_LOW);
476 ev_pixbuf_cache_set_page_range (EvPixbufCache *pixbuf_cache,
481 EvPageCache *page_cache;
483 g_return_if_fail (EV_IS_PIXBUF_CACHE (pixbuf_cache));
485 page_cache = ev_document_get_page_cache (pixbuf_cache->document);
487 g_return_if_fail (start_page > 0 && start_page <= ev_page_cache_get_n_pages (page_cache));
488 g_return_if_fail (end_page > 0 && end_page <= ev_page_cache_get_n_pages (page_cache));
489 g_return_if_fail (end_page >= start_page);
491 /* First, resize the page_range as needed. We cull old pages
493 ev_pixbuf_cache_update_range (pixbuf_cache, start_page, end_page);
495 /* Then, we update the current jobs to see if any of them are the wrong
496 * size, we remove them if we need to. */
497 ev_pixbuf_cache_clear_job_sizes (pixbuf_cache, scale);
499 /* Finally, we add the new jobs for all the sizes that don't have a
501 ev_pixbuf_cache_add_jobs_if_needed (pixbuf_cache, scale);
505 ev_pixbuf_cache_get_pixbuf (EvPixbufCache *pixbuf_cache,
508 CacheJobInfo *job_info;
510 job_info = find_job_cache (pixbuf_cache, page);
511 if (job_info == NULL)
514 /* We don't need to wait for the idle to handle the callback */
516 EV_JOB (job_info->job)->finished) {
519 pixbuf = g_object_ref (EV_JOB_RENDER (job_info->job)->pixbuf);
520 dispose_cache_job_info (job_info, pixbuf_cache);
521 job_info->pixbuf = pixbuf;
524 return job_info->pixbuf;