Evince
Evince is a document viewer capable of displaying multiple and single page document formats like PDF and Postscript.
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
ev-pixbuf-cache.c
Go to the documentation of this file.
1 #include <config.h>
2 #include "ev-pixbuf-cache.h"
3 #include "ev-job-scheduler.h"
4 #include "ev-view-private.h"
5 
6 typedef enum {
10 
11 typedef struct _CacheJobInfo
12 {
14  gboolean page_ready;
15 
16  /* Region of the page that needs to be drawn */
17  cairo_region_t *region;
18 
19  /* Data we get from rendering */
20  cairo_surface_t *surface;
21 
22  /* Device scale factor of target widget */
24 
25  /* Selection data.
26  * Selection_points are the coordinates encapsulated in selection.
27  * target_points is the target selection size. */
30  gboolean points_set;
31 
32  cairo_surface_t *selection;
33  gdouble selection_scale;
35 
36  cairo_region_t *selection_region;
39 } CacheJobInfo;
40 
42 {
43  GObject parent;
44 
45  /* We keep a link to our containing view just for style information. */
46  GtkWidget *view;
50  int end_page;
52  gboolean inverted_colors;
53 
54  gsize max_size;
55 
56  /* preload_cache_size is the number of pages prior to the current
57  * visible area that we cache. It's normally 1, but could be 2 in the
58  * case of twin pages.
59  */
61  guint job_list_len;
62 
66 };
67 
69 {
70  GObjectClass parent_class;
71 
72  void (* job_finished) (EvPixbufCache *pixbuf_cache);
73 };
74 
75 
76 enum
77 {
80 };
81 
82 static guint signals[N_SIGNALS] = {0, };
83 
84 static void ev_pixbuf_cache_init (EvPixbufCache *pixbuf_cache);
85 static void ev_pixbuf_cache_class_init (EvPixbufCacheClass *pixbuf_cache);
86 static void ev_pixbuf_cache_finalize (GObject *object);
87 static void ev_pixbuf_cache_dispose (GObject *object);
88 static void job_finished_cb (EvJob *job,
89  EvPixbufCache *pixbuf_cache);
90 static CacheJobInfo *find_job_cache (EvPixbufCache *pixbuf_cache,
91  int page);
92 static gboolean new_selection_surface_needed(EvPixbufCache *pixbuf_cache,
93  CacheJobInfo *job_info,
94  gint page,
95  gfloat scale);
96 
97 
98 /* These are used for iterating through the prev and next arrays */
99 #define FIRST_VISIBLE_PREV(pixbuf_cache) \
100  (MAX (0, pixbuf_cache->preload_cache_size - pixbuf_cache->start_page))
101 #define VISIBLE_NEXT_LEN(pixbuf_cache) \
102  (MIN(pixbuf_cache->preload_cache_size, ev_document_get_n_pages (pixbuf_cache->document) - (1 + pixbuf_cache->end_page)))
103 #define PAGE_CACHE_LEN(pixbuf_cache) \
104  ((pixbuf_cache->end_page - pixbuf_cache->start_page) + 1)
105 
106 #define MAX_PRELOADED_PAGES 3
107 
108 G_DEFINE_TYPE (EvPixbufCache, ev_pixbuf_cache, G_TYPE_OBJECT)
109 
110 static void
112 {
113  pixbuf_cache->start_page = -1;
114  pixbuf_cache->end_page = -1;
115 }
116 
117 static void
119 {
120  GObjectClass *object_class;
121 
122  object_class = G_OBJECT_CLASS (class);
123 
124  object_class->finalize = ev_pixbuf_cache_finalize;
125  object_class->dispose = ev_pixbuf_cache_dispose;
126 
128  g_signal_new ("job-finished",
129  G_OBJECT_CLASS_TYPE (object_class),
130  G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
131  G_STRUCT_OFFSET (EvPixbufCacheClass, job_finished),
132  NULL, NULL,
133  g_cclosure_marshal_VOID__POINTER,
134  G_TYPE_NONE, 1,
135  G_TYPE_POINTER);
136 }
137 
138 static void
139 ev_pixbuf_cache_finalize (GObject *object)
140 {
141  EvPixbufCache *pixbuf_cache;
142 
143  pixbuf_cache = EV_PIXBUF_CACHE (object);
144 
145  if (pixbuf_cache->job_list) {
146  g_slice_free1 (sizeof (CacheJobInfo) * pixbuf_cache->job_list_len,
147  pixbuf_cache->job_list);
148  pixbuf_cache->job_list = NULL;
149  }
150  if (pixbuf_cache->prev_job) {
151  g_slice_free1 (sizeof (CacheJobInfo) * pixbuf_cache->preload_cache_size,
152  pixbuf_cache->prev_job);
153  pixbuf_cache->prev_job = NULL;
154  }
155  if (pixbuf_cache->next_job) {
156  g_slice_free1 (sizeof (CacheJobInfo) * pixbuf_cache->preload_cache_size,
157  pixbuf_cache->next_job);
158  pixbuf_cache->next_job = NULL;
159  }
160 
161  g_object_unref (pixbuf_cache->model);
162 
163  G_OBJECT_CLASS (ev_pixbuf_cache_parent_class)->finalize (object);
164 }
165 
166 static void
168  gpointer data)
169 {
170  g_signal_handlers_disconnect_by_func (job_info->job,
171  G_CALLBACK (job_finished_cb),
172  data);
173  ev_job_cancel (job_info->job);
174  g_object_unref (job_info->job);
175  job_info->job = NULL;
176 }
177 
178 static void
180  gpointer data)
181 {
182  if (job_info == NULL)
183  return;
184 
185  if (job_info->job)
186  end_job (job_info, data);
187 
188  if (job_info->surface) {
189  cairo_surface_destroy (job_info->surface);
190  job_info->surface = NULL;
191  }
192  if (job_info->region) {
193  cairo_region_destroy (job_info->region);
194  job_info->region = NULL;
195  }
196  if (job_info->selection) {
197  cairo_surface_destroy (job_info->selection);
198  job_info->selection = NULL;
199  }
200  if (job_info->selection_region) {
201  cairo_region_destroy (job_info->selection_region);
202  job_info->selection_region = NULL;
203  }
204 
205  job_info->points_set = FALSE;
206 }
207 
208 static void
209 ev_pixbuf_cache_dispose (GObject *object)
210 {
211  EvPixbufCache *pixbuf_cache;
212  int i;
213 
214  pixbuf_cache = EV_PIXBUF_CACHE (object);
215 
216  for (i = 0; i < pixbuf_cache->preload_cache_size; i++) {
217  dispose_cache_job_info (pixbuf_cache->prev_job + i, pixbuf_cache);
218  dispose_cache_job_info (pixbuf_cache->next_job + i, pixbuf_cache);
219  }
220 
221  for (i = 0; i < PAGE_CACHE_LEN (pixbuf_cache); i++) {
222  dispose_cache_job_info (pixbuf_cache->job_list + i, pixbuf_cache);
223  }
224 
225  G_OBJECT_CLASS (ev_pixbuf_cache_parent_class)->dispose (object);
226 }
227 
228 
230 ev_pixbuf_cache_new (GtkWidget *view,
231  EvDocumentModel *model,
232  gsize max_size)
233 {
234  EvPixbufCache *pixbuf_cache;
235 
236  pixbuf_cache = (EvPixbufCache *) g_object_new (EV_TYPE_PIXBUF_CACHE, NULL);
237  /* This is a backlink, so we don't ref this */
238  pixbuf_cache->view = view;
239  pixbuf_cache->model = g_object_ref (model);
240  pixbuf_cache->document = ev_document_model_get_document (model);
241  pixbuf_cache->max_size = max_size;
242 
243  return pixbuf_cache;
244 }
245 
246 void
248  gsize max_size)
249 {
250  if (pixbuf_cache->max_size == max_size)
251  return;
252 
253  if (pixbuf_cache->max_size > max_size)
254  ev_pixbuf_cache_clear (pixbuf_cache);
255  pixbuf_cache->max_size = max_size;
256 }
257 
258 static int
260 {
261 #ifdef HAVE_HIDPI_SUPPORT
262  return gtk_widget_get_scale_factor (pixbuf_cache->view);
263 #else
264  return 1;
265 #endif
266 }
267 
268 static void
269 set_device_scale_on_surface (cairo_surface_t *surface,
270  int device_scale)
271 {
272 #ifdef HAVE_HIDPI_SUPPORT
273  cairo_surface_set_device_scale (surface, device_scale, device_scale);
274 #else
275  g_return_if_fail (device_scale == 1);
276 #endif
277 }
278 
279 static void
281  CacheJobInfo *job_info,
282  EvPixbufCache *pixbuf_cache)
283 {
284  if (job_info->surface) {
285  cairo_surface_destroy (job_info->surface);
286  }
287  job_info->surface = cairo_surface_reference (job_render->surface);
288  set_device_scale_on_surface (job_info->surface, job_info->device_scale);
289  if (pixbuf_cache->inverted_colors) {
291  }
292 
293  job_info->points_set = FALSE;
294  if (job_render->include_selection) {
295  if (job_info->selection) {
296  cairo_surface_destroy (job_info->selection);
297  job_info->selection = NULL;
298  }
299  if (job_info->selection_region) {
300  cairo_region_destroy (job_info->selection_region);
301  job_info->selection_region = NULL;
302  }
303 
304  job_info->selection_points = job_render->selection_points;
305  job_info->selection = cairo_surface_reference (job_render->selection);
306  if (job_info->selection)
307  set_device_scale_on_surface (job_info->selection, job_info->device_scale);
308  job_info->selection_scale = job_render->scale * job_info->device_scale;
309  g_assert (job_info->selection_points.x1 >= 0);
310 
311  job_info->selection_region_points = job_render->selection_points;
312  job_info->selection_region = cairo_region_reference (job_render->selection_region);
313  job_info->selection_region_scale = job_render->scale;
314 
315  job_info->points_set = TRUE;
316  }
317 
318  if (job_info->job)
319  end_job (job_info, pixbuf_cache);
320 
321  job_info->page_ready = TRUE;
322 }
323 
324 static void
326  EvPixbufCache *pixbuf_cache)
327 {
328  CacheJobInfo *job_info;
329  EvJobRender *job_render = EV_JOB_RENDER (job);
330 
331  /* If the job is outside of our interest, we silently discard it */
332  if ((job_render->page < (pixbuf_cache->start_page - pixbuf_cache->preload_cache_size)) ||
333  (job_render->page > (pixbuf_cache->end_page + pixbuf_cache->preload_cache_size))) {
334  g_object_unref (job);
335  return;
336  }
337 
338  job_info = find_job_cache (pixbuf_cache, job_render->page);
339 
340  if (ev_job_is_failed (job)) {
341  job_info->job = NULL;
342  g_object_unref (job);
343  return;
344  }
345 
346  copy_job_to_job_info (job_render, job_info, pixbuf_cache);
347  g_signal_emit (pixbuf_cache, signals[JOB_FINISHED], 0, job_info->region);
348 }
349 
350 /* This checks a job to see if the job would generate the right sized pixbuf
351  * given a scale. If it won't, it removes the job and clears it to NULL.
352  */
353 static void
355  CacheJobInfo *job_info,
356  gfloat scale)
357 {
358  gint width, height;
359  gint device_scale;
360 
361  g_assert (job_info);
362 
363  if (job_info->job == NULL)
364  return;
365 
366  device_scale = get_device_scale (pixbuf_cache);
367  if (job_info->device_scale == device_scale) {
368  _get_page_size_for_scale_and_rotation (job_info->job->document,
369  EV_JOB_RENDER (job_info->job)->page,
370  scale,
371  EV_JOB_RENDER (job_info->job)->rotation,
372  &width, &height);
373  if (width * device_scale == EV_JOB_RENDER (job_info->job)->target_width &&
374  height * device_scale == EV_JOB_RENDER (job_info->job)->target_height)
375  return;
376  }
377 
378  end_job (job_info, pixbuf_cache);
379 }
380 
381 /* Do all function that copies a job from an older cache to it's position in the
382  * new cache. It clears the old job if it doesn't have a place.
383  */
384 static void
386  EvPixbufCache *pixbuf_cache,
387  int page,
388  CacheJobInfo *new_job_list,
389  CacheJobInfo *new_prev_job,
390  CacheJobInfo *new_next_job,
391  int new_preload_cache_size,
392  int start_page,
393  int end_page,
394  gint priority)
395 {
396  CacheJobInfo *target_page = NULL;
397  int page_offset;
398  gint new_priority;
399 
400  if (page < (start_page - new_preload_cache_size) ||
401  page > (end_page + new_preload_cache_size)) {
402  dispose_cache_job_info (job_info, pixbuf_cache);
403  return;
404  }
405 
406  /* find the target page to copy it over to. */
407  if (page < start_page) {
408  page_offset = (page - (start_page - new_preload_cache_size));
409 
410  g_assert (page_offset >= 0 &&
411  page_offset < new_preload_cache_size);
412  target_page = new_prev_job + page_offset;
413  new_priority = EV_JOB_PRIORITY_LOW;
414  } else if (page > end_page) {
415  page_offset = (page - (end_page + 1));
416 
417  g_assert (page_offset >= 0 &&
418  page_offset < new_preload_cache_size);
419  target_page = new_next_job + page_offset;
420  new_priority = EV_JOB_PRIORITY_LOW;
421  } else {
422  page_offset = page - start_page;
423  g_assert (page_offset >= 0 &&
424  page_offset <= ((end_page - start_page) + 1));
425  new_priority = EV_JOB_PRIORITY_URGENT;
426  target_page = new_job_list + page_offset;
427  }
428 
429  *target_page = *job_info;
430  job_info->job = NULL;
431  job_info->region = NULL;
432  job_info->surface = NULL;
433 
434  if (new_priority != priority && target_page->job) {
435  ev_job_scheduler_update_job (target_page->job, new_priority);
436  }
437 }
438 
439 static gsize
441  gint page_index,
442  gdouble scale,
443  gint rotation)
444 {
445  gint width, height;
446 
448  page_index, scale, rotation,
449  &width, &height);
450  return height * cairo_format_stride_for_width (CAIRO_FORMAT_RGB24, width);
451 }
452 
453 static gint
455  gint start_page,
456  gint end_page,
457  gdouble scale,
458  gint rotation)
459 {
460  gsize range_size = 0;
461  gint new_preload_cache_size = 0;
462  gint i;
463  guint n_pages = ev_document_get_n_pages (pixbuf_cache->document);
464 
465  /* Get the size of the current range */
466  for (i = start_page; i <= end_page; i++) {
467  range_size += ev_pixbuf_cache_get_page_size (pixbuf_cache, i, scale, rotation);
468  }
469 
470  if (range_size >= pixbuf_cache->max_size)
471  return new_preload_cache_size;
472 
473  i = 1;
474  while (((start_page - i > 0) || (end_page + i < n_pages)) &&
475  new_preload_cache_size < MAX_PRELOADED_PAGES) {
476  gsize page_size;
477  gboolean updated = FALSE;
478 
479  if (end_page + i < n_pages) {
480  page_size = ev_pixbuf_cache_get_page_size (pixbuf_cache, end_page + i,
481  scale, rotation);
482  if (page_size + range_size <= pixbuf_cache->max_size) {
483  range_size += page_size;
484  new_preload_cache_size++;
485  updated = TRUE;
486  } else {
487  break;
488  }
489  }
490 
491  if (start_page - i > 0) {
492  page_size = ev_pixbuf_cache_get_page_size (pixbuf_cache, start_page - i,
493  scale, rotation);
494  if (page_size + range_size <= pixbuf_cache->max_size) {
495  range_size += page_size;
496  if (!updated)
497  new_preload_cache_size++;
498  } else {
499  break;
500  }
501  }
502  i++;
503  }
504 
505  return new_preload_cache_size;
506 }
507 
508 static void
510  gint start_page,
511  gint end_page,
512  guint rotation,
513  gdouble scale)
514 {
515  CacheJobInfo *new_job_list;
516  CacheJobInfo *new_prev_job = NULL;
517  CacheJobInfo *new_next_job = NULL;
518  gint new_preload_cache_size;
519  guint new_job_list_len;
520  int i, page;
521 
522  new_preload_cache_size = ev_pixbuf_cache_get_preload_size (pixbuf_cache,
523  start_page,
524  end_page,
525  scale,
526  rotation);
527  if (pixbuf_cache->start_page == start_page &&
528  pixbuf_cache->end_page == end_page &&
529  pixbuf_cache->preload_cache_size == new_preload_cache_size)
530  return;
531 
532  new_job_list_len = (end_page - start_page) + 1;
533  new_job_list = g_slice_alloc0 (sizeof (CacheJobInfo) * new_job_list_len);
534  if (new_preload_cache_size > 0) {
535  new_prev_job = g_slice_alloc0 (sizeof (CacheJobInfo) * new_preload_cache_size);
536  new_next_job = g_slice_alloc0 (sizeof (CacheJobInfo) * new_preload_cache_size);
537  }
538 
539  /* We go through each job in the old cache and either clear it or move
540  * it to a new location. */
541 
542  /* Start with the prev cache. */
543  page = pixbuf_cache->start_page - pixbuf_cache->preload_cache_size;
544  for (i = 0; i < pixbuf_cache->preload_cache_size; i++) {
545  if (page < 0) {
546  dispose_cache_job_info (pixbuf_cache->prev_job + i, pixbuf_cache);
547  } else {
548  move_one_job (pixbuf_cache->prev_job + i,
549  pixbuf_cache, page,
550  new_job_list, new_prev_job, new_next_job,
551  new_preload_cache_size,
552  start_page, end_page, EV_JOB_PRIORITY_LOW);
553  }
554  page ++;
555  }
556 
557  page = pixbuf_cache->start_page;
558  for (i = 0; i < PAGE_CACHE_LEN (pixbuf_cache) && page >= 0; i++) {
559  move_one_job (pixbuf_cache->job_list + i,
560  pixbuf_cache, page,
561  new_job_list, new_prev_job, new_next_job,
562  new_preload_cache_size,
563  start_page, end_page, EV_JOB_PRIORITY_URGENT);
564  page ++;
565  }
566 
567  for (i = 0; i < pixbuf_cache->preload_cache_size; i++) {
568  if (page >= ev_document_get_n_pages (pixbuf_cache->document)) {
569  dispose_cache_job_info (pixbuf_cache->next_job + i, pixbuf_cache);
570  } else {
571  move_one_job (pixbuf_cache->next_job + i,
572  pixbuf_cache, page,
573  new_job_list, new_prev_job, new_next_job,
574  new_preload_cache_size,
575  start_page, end_page, EV_JOB_PRIORITY_LOW);
576  }
577  page ++;
578  }
579 
580  if (pixbuf_cache->job_list) {
581  g_slice_free1 (sizeof (CacheJobInfo) * pixbuf_cache->job_list_len,
582  pixbuf_cache->job_list);
583  }
584  if (pixbuf_cache->prev_job) {
585  g_slice_free1 (sizeof (CacheJobInfo) * pixbuf_cache->preload_cache_size,
586  pixbuf_cache->prev_job);
587  }
588  if (pixbuf_cache->next_job) {
589  g_slice_free1 (sizeof (CacheJobInfo) * pixbuf_cache->preload_cache_size,
590  pixbuf_cache->next_job);
591  }
592 
593  pixbuf_cache->preload_cache_size = new_preload_cache_size;
594  pixbuf_cache->job_list_len = new_job_list_len;
595 
596  pixbuf_cache->job_list = new_job_list;
597  pixbuf_cache->prev_job = new_prev_job;
598  pixbuf_cache->next_job = new_next_job;
599 
600  pixbuf_cache->start_page = start_page;
601  pixbuf_cache->end_page = end_page;
602 }
603 
604 static CacheJobInfo *
606  int page)
607 {
608  int page_offset;
609 
610  if (page < (pixbuf_cache->start_page - pixbuf_cache->preload_cache_size) ||
611  page > (pixbuf_cache->end_page + pixbuf_cache->preload_cache_size))
612  return NULL;
613 
614  if (page < pixbuf_cache->start_page) {
615  page_offset = (page - (pixbuf_cache->start_page - pixbuf_cache->preload_cache_size));
616 
617  g_assert (page_offset >= 0 &&
618  page_offset < pixbuf_cache->preload_cache_size);
619  return pixbuf_cache->prev_job + page_offset;
620  }
621 
622  if (page > pixbuf_cache->end_page) {
623  page_offset = (page - (pixbuf_cache->end_page + 1));
624 
625  g_assert (page_offset >= 0 &&
626  page_offset < pixbuf_cache->preload_cache_size);
627  return pixbuf_cache->next_job + page_offset;
628  }
629 
630  page_offset = page - pixbuf_cache->start_page;
631  g_assert (page_offset >= 0 &&
632  page_offset <= PAGE_CACHE_LEN(pixbuf_cache));
633  return pixbuf_cache->job_list + page_offset;
634 }
635 
636 static void
638  gfloat scale)
639 {
640  int i;
641 
642  for (i = 0; i < PAGE_CACHE_LEN (pixbuf_cache); i++) {
643  check_job_size_and_unref (pixbuf_cache, pixbuf_cache->job_list + i, scale);
644  }
645 
646  for (i = 0; i < pixbuf_cache->preload_cache_size; i++) {
647  check_job_size_and_unref (pixbuf_cache, pixbuf_cache->prev_job + i, scale);
648  check_job_size_and_unref (pixbuf_cache, pixbuf_cache->next_job + i, scale);
649  }
650 }
651 
652 static void
653 get_selection_colors (EvView *view, GdkColor *text, GdkColor *base)
654 {
655  GdkRGBA fg, bg;
656 
657  _ev_view_get_selection_colors (view, &bg, &fg);
658 
659  text->pixel = 0;
660  text->red = CLAMP ((guint) (fg.red * 65535), 0, 65535);
661  text->green = CLAMP ((guint) (fg.green * 65535), 0, 65535);
662  text->blue = CLAMP ((guint) (fg.blue * 65535), 0, 65535);
663 
664  base->pixel = 0;
665  base->red = CLAMP ((guint) (bg.red * 65535), 0, 65535);
666  base->green = CLAMP ((guint) (bg.green * 65535), 0, 65535);
667  base->blue = CLAMP ((guint) (bg.blue * 65535), 0, 65535);
668 }
669 
670 static void
671 add_job (EvPixbufCache *pixbuf_cache,
672  CacheJobInfo *job_info,
673  cairo_region_t *region,
674  gint width,
675  gint height,
676  gint page,
677  gint rotation,
678  gfloat scale,
679  EvJobPriority priority)
680 {
681  job_info->device_scale = get_device_scale (pixbuf_cache);
682  job_info->page_ready = FALSE;
683 
684  if (job_info->region)
685  cairo_region_destroy (job_info->region);
686  job_info->region = region ? cairo_region_reference (region) : NULL;
687 
688  if (job_info->job)
689  end_job (job_info, pixbuf_cache);
690 
691  job_info->job = ev_job_render_new (pixbuf_cache->document,
692  page, rotation,
693  scale * job_info->device_scale,
694  width * job_info->device_scale,
695  height * job_info->device_scale);
696 
697  if (new_selection_surface_needed (pixbuf_cache, job_info, page, scale)) {
698  GdkColor text, base;
699 
700  get_selection_colors (EV_VIEW (pixbuf_cache->view), &text, &base);
702  &(job_info->target_points),
703  job_info->selection_style,
704  &text, &base);
705  }
706 
707  g_signal_connect (job_info->job, "finished",
708  G_CALLBACK (job_finished_cb),
709  pixbuf_cache);
710  ev_job_scheduler_push_job (job_info->job, priority);
711 }
712 
713 static void
715  CacheJobInfo *job_info,
716  gint page,
717  gint rotation,
718  gfloat scale,
719  EvJobPriority priority)
720 {
721  gint device_scale = get_device_scale (pixbuf_cache);
722  gint width, height;
723 
724  if (job_info->job)
725  return;
726 
728  page, scale, rotation,
729  &width, &height);
730 
731  if (job_info->surface &&
732  job_info->device_scale == device_scale &&
733  cairo_image_surface_get_width (job_info->surface) == width * device_scale &&
734  cairo_image_surface_get_height (job_info->surface) == height * device_scale)
735  return;
736 
737  /* Free old surfaces for non visible pages */
738  if (priority == EV_JOB_PRIORITY_LOW) {
739  if (job_info->surface) {
740  cairo_surface_destroy (job_info->surface);
741  job_info->surface = NULL;
742  }
743 
744  if (job_info->selection) {
745  cairo_surface_destroy (job_info->selection);
746  job_info->selection = NULL;
747  }
748  }
749 
750  add_job (pixbuf_cache, job_info, NULL,
751  width, height, page, rotation, scale,
752  priority);
753 }
754 
755 static void
757  gint rotation,
758  gfloat scale)
759 {
760  CacheJobInfo *job_info;
761  int page;
762  int i;
763 
764  for (i = pixbuf_cache->preload_cache_size - 1; i >= FIRST_VISIBLE_PREV(pixbuf_cache); i--) {
765  job_info = (pixbuf_cache->prev_job + i);
766  page = pixbuf_cache->start_page - pixbuf_cache->preload_cache_size + i;
767 
768  add_job_if_needed (pixbuf_cache, job_info,
769  page, rotation, scale,
771  }
772 }
773 
774 static void
776  gint rotation,
777  gfloat scale)
778 {
779  CacheJobInfo *job_info;
780  int page;
781  int i;
782 
783  for (i = 0; i < VISIBLE_NEXT_LEN(pixbuf_cache); i++) {
784  job_info = (pixbuf_cache->next_job + i);
785  page = pixbuf_cache->end_page + 1 + i;
786 
787  add_job_if_needed (pixbuf_cache, job_info,
788  page, rotation, scale,
790  }
791 }
792 
793 static void
795  gint rotation,
796  gfloat scale)
797 {
798  CacheJobInfo *job_info;
799  int page;
800  int i;
801 
802  for (i = 0; i < PAGE_CACHE_LEN (pixbuf_cache); i++) {
803  job_info = (pixbuf_cache->job_list + i);
804  page = pixbuf_cache->start_page + i;
805 
806  add_job_if_needed (pixbuf_cache, job_info,
807  page, rotation, scale,
809  }
810 
811  if (pixbuf_cache->scroll_direction == SCROLL_DIRECTION_UP) {
812  add_prev_jobs_if_needed (pixbuf_cache, rotation, scale);
813  add_next_jobs_if_needed (pixbuf_cache, rotation, scale);
814  } else {
815  add_next_jobs_if_needed (pixbuf_cache, rotation, scale);
816  add_prev_jobs_if_needed (pixbuf_cache, rotation, scale);
817  }
818 }
819 
820 static ScrollDirection
822  gint start_page,
823  gint end_page)
824 {
825  if (start_page < pixbuf_cache->start_page)
826  return SCROLL_DIRECTION_UP;
827 
828  if (end_page > pixbuf_cache->end_page)
829  return SCROLL_DIRECTION_DOWN;
830 
831  if (start_page > pixbuf_cache->start_page)
832  return SCROLL_DIRECTION_DOWN;
833 
834  if (end_page < pixbuf_cache->end_page)
835  return SCROLL_DIRECTION_UP;
836 
837  return pixbuf_cache->scroll_direction;
838 }
839 
840 void
842  gint start_page,
843  gint end_page,
844  GList *selection_list)
845 {
846  gdouble scale = ev_document_model_get_scale (pixbuf_cache->model);
847  gint rotation = ev_document_model_get_rotation (pixbuf_cache->model);
848 
849  g_return_if_fail (EV_IS_PIXBUF_CACHE (pixbuf_cache));
850 
851  g_return_if_fail (start_page >= 0 && start_page < ev_document_get_n_pages (pixbuf_cache->document));
852  g_return_if_fail (end_page >= 0 && end_page < ev_document_get_n_pages (pixbuf_cache->document));
853  g_return_if_fail (end_page >= start_page);
854 
855  pixbuf_cache->scroll_direction = ev_pixbuf_cache_get_scroll_direction (pixbuf_cache, start_page, end_page);
856 
857  /* First, resize the page_range as needed. We cull old pages
858  * mercilessly. */
859  ev_pixbuf_cache_update_range (pixbuf_cache, start_page, end_page, rotation, scale);
860 
861  /* Then, we update the current jobs to see if any of them are the wrong
862  * size, we remove them if we need to. */
863  ev_pixbuf_cache_clear_job_sizes (pixbuf_cache, scale);
864 
865  /* Next, we update the target selection for our pages */
866  ev_pixbuf_cache_set_selection_list (pixbuf_cache, selection_list);
867 
868  /* Finally, we add the new jobs for all the sizes that don't have a
869  * pixbuf */
870  ev_pixbuf_cache_add_jobs_if_needed (pixbuf_cache, rotation, scale);
871 }
872 
873 void
875  gboolean inverted_colors)
876 {
877  gint i;
878 
879  if (pixbuf_cache->inverted_colors == inverted_colors)
880  return;
881 
882  pixbuf_cache->inverted_colors = inverted_colors;
883 
884  for (i = 0; i < pixbuf_cache->preload_cache_size; i++) {
885  CacheJobInfo *job_info;
886 
887  job_info = pixbuf_cache->prev_job + i;
888  if (job_info && job_info->surface)
890 
891  job_info = pixbuf_cache->next_job + i;
892  if (job_info && job_info->surface)
894  }
895 
896  for (i = 0; i < PAGE_CACHE_LEN (pixbuf_cache); i++) {
897  CacheJobInfo *job_info;
898 
899  job_info = pixbuf_cache->job_list + i;
900  if (job_info && job_info->surface)
902  }
903 }
904 
905 cairo_surface_t *
907  gint page)
908 {
909  CacheJobInfo *job_info;
910 
911  job_info = find_job_cache (pixbuf_cache, page);
912  if (job_info == NULL)
913  return NULL;
914 
915  if (job_info->page_ready)
916  return job_info->surface;
917 
918  /* We don't need to wait for the idle to handle the callback */
919  if (job_info->job &&
920  EV_JOB_RENDER (job_info->job)->page_ready) {
921  copy_job_to_job_info (EV_JOB_RENDER (job_info->job), job_info, pixbuf_cache);
922  g_signal_emit (pixbuf_cache, signals[JOB_FINISHED], 0, job_info->region);
923  }
924 
925  return job_info->surface;
926 }
927 
928 static gboolean
930  CacheJobInfo *job_info,
931  gint page,
932  gfloat scale)
933 {
934  if (job_info->selection)
935  return job_info->selection_scale != scale;
936  return job_info->points_set;
937 }
938 
939 static gboolean
941  CacheJobInfo *job_info,
942  gint page,
943  gfloat scale)
944 {
945  if (job_info->selection_region)
946  return job_info->selection_region_scale != scale;
947  return job_info->points_set;
948 }
949 
950 static void
952  CacheJobInfo *job_info,
953  gint page,
954  gfloat scale)
955 {
956  if (new_selection_surface_needed (pixbuf_cache, job_info, page, scale)) {
957  if (job_info->selection)
958  cairo_surface_destroy (job_info->selection);
959  job_info->selection = NULL;
960  job_info->selection_points.x1 = -1;
961  }
962 }
963 
964 static void
966  CacheJobInfo *job_info,
967  gint page,
968  gfloat scale)
969 {
970  if (new_selection_region_needed (pixbuf_cache, job_info, page, scale)) {
971  if (job_info->selection_region)
972  cairo_region_destroy (job_info->selection_region);
973  job_info->selection_region = NULL;
974  job_info->selection_region_points.x1 = -1;
975  }
976 }
977 
978 /* Clears the cache of jobs and pixbufs.
979  */
980 void
982 {
983  int i;
984 
985  if (!pixbuf_cache->job_list)
986  return;
987 
988  for (i = 0; i < pixbuf_cache->preload_cache_size; i++) {
989  dispose_cache_job_info (pixbuf_cache->prev_job + i, pixbuf_cache);
990  dispose_cache_job_info (pixbuf_cache->next_job + i, pixbuf_cache);
991  }
992 
993  for (i = 0; i < PAGE_CACHE_LEN (pixbuf_cache); i++) {
994  dispose_cache_job_info (pixbuf_cache->job_list + i, pixbuf_cache);
995  }
996 }
997 
998 
999 void
1001 {
1002  gint i;
1003 
1004  if (!pixbuf_cache->job_list)
1005  return;
1006 
1007  /* FIXME: doesn't update running jobs. */
1008  for (i = 0; i < pixbuf_cache->preload_cache_size; i++) {
1009  CacheJobInfo *job_info;
1010 
1011  job_info = pixbuf_cache->prev_job + i;
1012  if (job_info->selection) {
1013  cairo_surface_destroy (job_info->selection);
1014  job_info->selection = NULL;
1015  job_info->selection_points.x1 = -1;
1016  }
1017 
1018  job_info = pixbuf_cache->next_job + i;
1019  if (job_info->selection) {
1020  cairo_surface_destroy (job_info->selection);
1021  job_info->selection = NULL;
1022  job_info->selection_points.x1 = -1;
1023  }
1024  }
1025 
1026  for (i = 0; i < PAGE_CACHE_LEN (pixbuf_cache); i++) {
1027  CacheJobInfo *job_info;
1028 
1029  job_info = pixbuf_cache->job_list + i;
1030  if (job_info->selection) {
1031  cairo_surface_destroy (job_info->selection);
1032  job_info->selection = NULL;
1033  job_info->selection_points.x1 = -1;
1034  }
1035  }
1036 }
1037 
1038 cairo_surface_t *
1040  gint page,
1041  gfloat scale)
1042 {
1043  CacheJobInfo *job_info;
1044 
1045  /* the document does not implement the selection interface */
1046  if (!EV_IS_SELECTION (pixbuf_cache->document))
1047  return NULL;
1048 
1049  job_info = find_job_cache (pixbuf_cache, page);
1050  if (job_info == NULL)
1051  return NULL;
1052 
1053  /* No selection on this page */
1054  if (!job_info->points_set)
1055  return NULL;
1056 
1057  /* If we have a running job, we just return what we have under the
1058  * assumption that it'll be updated later and we can scale it as need
1059  * be */
1060  if (job_info->job && EV_JOB_RENDER (job_info->job)->include_selection)
1061  return job_info->selection;
1062 
1063  /* Now, lets see if we need to resize the image. If we do, we clear the
1064  * old one. */
1065  clear_selection_surface_if_needed (pixbuf_cache, job_info, page, scale);
1066 
1067  /* Finally, we see if the two scales are the same, and get a new pixbuf
1068  * if needed. We do this synchronously for now. At some point, we
1069  * _should_ be able to get rid of the doc_mutex, so the synchronicity
1070  * doesn't kill us. Rendering a few glyphs should really be fast.
1071  */
1072  if (ev_rect_cmp (&(job_info->target_points), &(job_info->selection_points))) {
1073  EvRectangle *old_points;
1074  GdkColor text, base;
1075  EvRenderContext *rc;
1076  EvPage *ev_page;
1077  gint width, height;
1078 
1079  /* we need to get a new selection pixbuf */
1081  if (job_info->selection_points.x1 < 0) {
1082  g_assert (job_info->selection == NULL);
1083  old_points = NULL;
1084  } else {
1085  old_points = &(job_info->selection_points);
1086  }
1087 
1088  ev_page = ev_document_get_page (pixbuf_cache->document, page);
1090  page,
1091  scale * job_info->device_scale,
1092  0, &width, &height);
1093 
1094  rc = ev_render_context_new (ev_page, 0, scale * job_info->device_scale);
1095  ev_render_context_set_target_size (rc, width, height);
1096  g_object_unref (ev_page);
1097 
1098  get_selection_colors (EV_VIEW (pixbuf_cache->view), &text, &base);
1100  rc, &(job_info->selection),
1101  &(job_info->target_points),
1102  old_points,
1103  job_info->selection_style,
1104  &text, &base);
1105  if (job_info->selection)
1106  set_device_scale_on_surface (job_info->selection, job_info->device_scale);
1107  job_info->selection_points = job_info->target_points;
1108  job_info->selection_scale = scale * job_info->device_scale;
1109  g_object_unref (rc);
1111  }
1112  return job_info->selection;
1113 }
1114 
1115 cairo_region_t *
1117  gint page,
1118  gfloat scale)
1119 {
1120  CacheJobInfo *job_info;
1121 
1122  /* the document does not implement the selection interface */
1123  if (!EV_IS_SELECTION (pixbuf_cache->document))
1124  return NULL;
1125 
1126  job_info = find_job_cache (pixbuf_cache, page);
1127  if (job_info == NULL)
1128  return NULL;
1129 
1130  /* No selection on this page */
1131  if (!job_info->points_set)
1132  return NULL;
1133 
1134  /* If we have a running job, we just return what we have under the
1135  * assumption that it'll be updated later and we can scale it as need
1136  * be */
1137  if (job_info->job && EV_JOB_RENDER (job_info->job)->include_selection)
1138  return job_info->selection_region && !cairo_region_is_empty(job_info->selection_region) ?
1139  job_info->selection_region : NULL;
1140 
1141  /* Now, lets see if we need to resize the region. If we do, we clear the
1142  * old one. */
1143  clear_selection_region_if_needed (pixbuf_cache, job_info, page, scale);
1144 
1145  /* Finally, we see if the two scales are the same, and get a new region
1146  * if needed.
1147  */
1148  if (ev_rect_cmp (&(job_info->target_points), &(job_info->selection_region_points))) {
1149  EvRenderContext *rc;
1150  EvPage *ev_page;
1151  gint width, height;
1152 
1154  ev_page = ev_document_get_page (pixbuf_cache->document, page);
1155 
1157  page, scale, 0,
1158  &width, &height);
1159 
1160  rc = ev_render_context_new (ev_page, 0, 0.);
1161  ev_render_context_set_target_size (rc, width, height);
1162  g_object_unref (ev_page);
1163 
1164  if (job_info->selection_region)
1165  cairo_region_destroy (job_info->selection_region);
1166  job_info->selection_region =
1168  rc, job_info->selection_style,
1169  &(job_info->target_points));
1170  job_info->selection_region_points = job_info->target_points;
1171  job_info->selection_region_scale = scale;
1172  g_object_unref (rc);
1174  }
1175  return job_info->selection_region && !cairo_region_is_empty(job_info->selection_region) ?
1176  job_info->selection_region : NULL;
1177 }
1178 
1179 static void
1181  EvViewSelection *selection)
1182 {
1183  job_info->points_set = TRUE;
1184  job_info->target_points = selection->rect;
1185  job_info->selection_style = selection->style;
1186 }
1187 
1188 static void
1190 {
1191  job_info->points_set = FALSE;
1192  job_info->selection_points.x1 = -1;
1193 
1194  if (job_info->selection) {
1195  cairo_surface_destroy (job_info->selection);
1196  job_info->selection = NULL;
1197  }
1198 
1199  if (job_info->selection_region) {
1200  cairo_region_destroy (job_info->selection_region);
1201  job_info->selection_region = NULL;
1202  }
1203 }
1204 
1205 /* This function will reset the selection on pages that no longer have them, and
1206  * will update the target_selection on those that need it. It will _not_ free
1207  * the previous selection_list -- that's up to caller to do.
1208  */
1209 void
1211  GList *selection_list)
1212 {
1213  EvViewSelection *selection;
1214  GList *list = selection_list;
1215  int page;
1216  int i;
1217 
1218  g_return_if_fail (EV_IS_PIXBUF_CACHE (pixbuf_cache));
1219 
1220  if (!EV_IS_SELECTION (pixbuf_cache->document))
1221  return;
1222 
1223  if (pixbuf_cache->start_page == -1 || pixbuf_cache->end_page == -1)
1224  return;
1225 
1226  /* We check each area to see what needs updating, and what needs freeing; */
1227  page = pixbuf_cache->start_page - pixbuf_cache->preload_cache_size;
1228  for (i = 0; i < pixbuf_cache->preload_cache_size; i++) {
1229  if (page < 0) {
1230  page ++;
1231  continue;
1232  }
1233 
1234  selection = NULL;
1235  while (list) {
1236  if (((EvViewSelection *)list->data)->page == page) {
1237  selection = list->data;
1238  break;
1239  } else if (((EvViewSelection *)list->data)->page > page)
1240  break;
1241  list = list->next;
1242  }
1243 
1244  if (selection)
1245  update_job_selection (pixbuf_cache->prev_job + i, selection);
1246  else
1247  clear_job_selection (pixbuf_cache->prev_job + i);
1248  page ++;
1249  }
1250 
1251  page = pixbuf_cache->start_page;
1252  for (i = 0; i < PAGE_CACHE_LEN (pixbuf_cache); i++) {
1253  selection = NULL;
1254  while (list) {
1255  if (((EvViewSelection *)list->data)->page == page) {
1256  selection = list->data;
1257  break;
1258  } else if (((EvViewSelection *)list->data)->page > page)
1259  break;
1260  list = list->next;
1261  }
1262 
1263  if (selection)
1264  update_job_selection (pixbuf_cache->job_list + i, selection);
1265  else
1266  clear_job_selection (pixbuf_cache->job_list + i);
1267  page ++;
1268  }
1269 
1270  for (i = 0; i < pixbuf_cache->preload_cache_size; i++) {
1271  if (page >= ev_document_get_n_pages (pixbuf_cache->document))
1272  break;
1273 
1274  selection = NULL;
1275  while (list) {
1276  if (((EvViewSelection *)list->data)->page == page) {
1277  selection = list->data;
1278  break;
1279  } else if (((EvViewSelection *)list->data)->page > page)
1280  break;
1281  list = list->next;
1282  }
1283 
1284  if (selection)
1285  update_job_selection (pixbuf_cache->next_job + i, selection);
1286  else
1287  clear_job_selection (pixbuf_cache->next_job + i);
1288  page ++;
1289  }
1290 }
1291 
1292 
1293 /* Returns what the pixbuf cache thinks is */
1294 
1295 GList *
1297 {
1298  EvViewSelection *selection;
1299  GList *retval = NULL;
1300  int page;
1301  int i;
1302 
1303  g_return_val_if_fail (EV_IS_PIXBUF_CACHE (pixbuf_cache), NULL);
1304 
1305  if (pixbuf_cache->start_page == -1 || pixbuf_cache->end_page == -1)
1306  return NULL;
1307 
1308  /* We check each area to see what needs updating, and what needs freeing; */
1309  page = pixbuf_cache->start_page - pixbuf_cache->preload_cache_size;
1310  for (i = 0; i < pixbuf_cache->preload_cache_size; i++) {
1311  if (page < 0) {
1312  page ++;
1313  continue;
1314  }
1315 
1316  if (pixbuf_cache->prev_job[i].selection_points.x1 != -1) {
1317  selection = g_slice_new0 (EvViewSelection);
1318  selection->page = page;
1319  selection->rect = pixbuf_cache->prev_job[i].selection_points;
1320  if (pixbuf_cache->prev_job[i].selection_region)
1321  selection->covered_region = cairo_region_reference (pixbuf_cache->prev_job[i].selection_region);
1322  retval = g_list_prepend (retval, selection);
1323  }
1324 
1325  page ++;
1326  }
1327 
1328  page = pixbuf_cache->start_page;
1329  for (i = 0; i < PAGE_CACHE_LEN (pixbuf_cache); i++) {
1330  if (pixbuf_cache->job_list[i].selection_points.x1 != -1) {
1331  selection = g_slice_new0 (EvViewSelection);
1332  selection->page = page;
1333  selection->rect = pixbuf_cache->job_list[i].selection_points;
1334  if (pixbuf_cache->job_list[i].selection_region)
1335  selection->covered_region = cairo_region_reference (pixbuf_cache->job_list[i].selection_region);
1336  retval = g_list_prepend (retval, selection);
1337  }
1338 
1339  page ++;
1340  }
1341 
1342  for (i = 0; i < pixbuf_cache->preload_cache_size; i++) {
1343  if (page >= ev_document_get_n_pages (pixbuf_cache->document))
1344  break;
1345 
1346  if (pixbuf_cache->next_job[i].selection_points.x1 != -1) {
1347  selection = g_slice_new0 (EvViewSelection);
1348  selection->page = page;
1349  selection->rect = pixbuf_cache->next_job[i].selection_points;
1350  if (pixbuf_cache->next_job[i].selection_region)
1351  selection->covered_region = cairo_region_reference (pixbuf_cache->next_job[i].selection_region);
1352  retval = g_list_prepend (retval, selection);
1353  }
1354 
1355  page ++;
1356  }
1357 
1358  return g_list_reverse (retval);
1359 }
1360 
1361 void
1363  cairo_region_t *region,
1364  gint page,
1365  gint rotation,
1366  gdouble scale)
1367 {
1368  CacheJobInfo *job_info;
1369  gint width, height;
1370 
1371  job_info = find_job_cache (pixbuf_cache, page);
1372  if (job_info == NULL)
1373  return;
1374 
1376  page, scale, rotation,
1377  &width, &height);
1378  add_job (pixbuf_cache, job_info, region,
1379  width, height, page, rotation, scale,
1381 }
1382 
1383