render.c 27.9 KB
Newer Older
Moritz Lipp's avatar
Moritz Lipp committed
1 2 3
/* See LICENSE file for license and copyright information */

#include <math.h>
4
#include <string.h>
5 6
#include <girara/datastructures.h>
#include <girara/utils.h>
7

Moritz Lipp's avatar
Moritz Lipp committed
8
#include "render.h"
9
#include "adjustment.h"
Moritz Lipp's avatar
Moritz Lipp committed
10
#include "zathura.h"
11
#include "document.h"
Moritz Lipp's avatar
Moritz Lipp committed
12
#include "page.h"
13
#include "page-widget.h"
14
#include "utils.h"
Moritz Lipp's avatar
Moritz Lipp committed
15

16 17 18 19 20
/* define the two types */
G_DEFINE_TYPE(ZathuraRenderer, zathura_renderer, G_TYPE_OBJECT)
G_DEFINE_TYPE(ZathuraRenderRequest, zathura_render_request, G_TYPE_OBJECT)

/* private methods for ZathuraRenderer  */
21
static void renderer_finalize(GObject* object);
22
/* private methods for ZathuraRenderRequest */
23
static void render_request_dispose(GObject* object);
24
static void render_request_finalize(GObject* object);
25

26
static void render_job(void* data, void* user_data);
27
static gint render_thread_sort(gconstpointer a, gconstpointer b, gpointer data);
28 29 30 31
static ssize_t page_cache_lru_invalidate(ZathuraRenderer* renderer);
static void page_cache_invalidate_all(ZathuraRenderer* renderer);
static bool page_cache_is_full(ZathuraRenderer* renderer, bool* result);

32 33 34

/* private data for ZathuraRenderer */
typedef struct private_s {
35
  GThreadPool* pool; /**< Pool of threads */
36
  GMutex mutex; /**< Render lock */
37 38
  volatile bool about_to_close; /**< Render thread is to be freed */

39 40 41
  /**
   * recolor information
   */
42 43 44
  struct {
    bool enabled;
    bool hue;
45
    bool reverse_video;
46

47 48
    GdkRGBA light;
    GdkRGBA dark;
49
  } recolor;
50 51 52 53 54 55 56 57 58 59 60 61

  /*
   * page cache
   */
  struct {
    int* cache;
    size_t size;
    size_t num_cached_pages;
  } page_cache;

  /* render requests */
  girara_list_t* requests;
62 63 64 65 66 67
} private_t;

/* private data for ZathuraRenderRequest */
typedef struct request_private_s {
  ZathuraRenderer* renderer;
  zathura_page_t* page;
68
  gint64 last_view_time;
69
  girara_list_t* active_jobs;
70
  GMutex jobs_mutex;
71 72 73 74 75 76 77 78
} request_private_t;

#define GET_PRIVATE(obj) \
  (G_TYPE_INSTANCE_GET_PRIVATE((obj), ZATHURA_TYPE_RENDERER, private_t))
#define REQUEST_GET_PRIVATE(obj) \
  (G_TYPE_INSTANCE_GET_PRIVATE((obj), ZATHURA_TYPE_RENDER_REQUEST, \
                               request_private_t))

79 80 81 82 83 84
/* job descritption for render thread */
typedef struct render_job_s {
  ZathuraRenderRequest* request;
  volatile bool aborted;
} render_job_t;

85
/* init, new and free for ZathuraRenderer */
86 87

static void
88
zathura_renderer_class_init(ZathuraRendererClass* class)
89
{
90 91 92 93 94
  /* add private members */
  g_type_class_add_private(class, sizeof(private_t));

  /* overwrite methods */
  GObjectClass* object_class = G_OBJECT_CLASS(class);
95
  object_class->finalize     = renderer_finalize;
96 97 98 99 100 101 102 103 104
}

static void
zathura_renderer_init(ZathuraRenderer* renderer)
{
  private_t* priv = GET_PRIVATE(renderer);
  priv->pool = g_thread_pool_new(render_job, renderer, 1, TRUE, NULL);
  priv->about_to_close = false;
  g_thread_pool_set_sort_function(priv->pool, render_thread_sort, NULL);
105
  g_mutex_init(&priv->mutex);
106

107
  /* recolor */
108 109
  priv->recolor.enabled = false;
  priv->recolor.hue = true;
110
  priv->recolor.reverse_video = false;
111

112 113 114 115 116
  /* page cache */
  priv->page_cache.size = 0;
  priv->page_cache.cache = NULL;
  priv->page_cache.num_cached_pages = 0;

117
  zathura_renderer_set_recolor_colors_str(renderer, "#000000", "#FFFFFF");
118 119 120 121

  priv->requests = girara_list_new();
}

122
static bool
123 124 125 126
page_cache_init(ZathuraRenderer* renderer, size_t cache_size)
{
  private_t* priv = GET_PRIVATE(renderer);

127 128
  priv->page_cache.size  = cache_size;
  priv->page_cache.cache = g_try_malloc0(cache_size * sizeof(int));
129 130 131 132
  if (priv->page_cache.cache == NULL) {
    return false;
  }

133
  page_cache_invalidate_all(renderer);
134
  return true;
135 136 137
}

ZathuraRenderer*
138
zathura_renderer_new(size_t cache_size)
139
{
140 141
  g_return_val_if_fail(cache_size > 0, NULL);

Sebastian Ramacher's avatar
Sebastian Ramacher committed
142
  GObject* obj = g_object_new(ZATHURA_TYPE_RENDERER, NULL);
143
  ZathuraRenderer* ret = ZATHURA_RENDERER(obj);
144 145

  if (page_cache_init(ret, cache_size) == false) {
146
    g_object_unref(obj);
147 148
    return NULL;
  }
149 150

  return ret;
151 152 153
}

static void
154
renderer_finalize(GObject* object)
155 156 157 158 159
{
  ZathuraRenderer* renderer = ZATHURA_RENDERER(object);
  private_t* priv = GET_PRIVATE(renderer);

  zathura_renderer_stop(renderer);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
160
  if (priv->pool != NULL) {
161
    g_thread_pool_free(priv->pool, TRUE, TRUE);
Moritz Lipp's avatar
Moritz Lipp committed
162
  }
Sebastian Ramacher's avatar
Sebastian Ramacher committed
163
  g_mutex_clear(&(priv->mutex));
164

165
  g_free(priv->page_cache.cache);
166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186
  girara_list_free(priv->requests);
}

/* (un)register requests at the renderer */

static void
renderer_unregister_request(ZathuraRenderer* renderer,
    ZathuraRenderRequest* request)
{
  private_t* priv = GET_PRIVATE(renderer);
  girara_list_remove(priv->requests, request);
}

static void
renderer_register_request(ZathuraRenderer* renderer,
    ZathuraRenderRequest* request)
{
  private_t* priv = GET_PRIVATE(renderer);
  if (girara_list_contains(priv->requests, request) == false) {
    girara_list_append(priv->requests, request);
  }
187 188 189 190 191 192
}

/* init, new and free for ZathuraRenderRequest */

enum {
  REQUEST_COMPLETED,
193 194
  REQUEST_CACHE_ADDED,
  REQUEST_CACHE_INVALIDATED,
195 196
  REQUEST_LAST_SIGNAL
};
Moritz Lipp's avatar
Moritz Lipp committed
197

198 199 200 201 202 203 204 205 206 207
static guint request_signals[REQUEST_LAST_SIGNAL] = { 0 };

static void
zathura_render_request_class_init(ZathuraRenderRequestClass* class)
{
  /* add private members */
  g_type_class_add_private(class, sizeof(request_private_t));

  /* overwrite methods */
  GObjectClass* object_class = G_OBJECT_CLASS(class);
208
  object_class->dispose      = render_request_dispose;
209
  object_class->finalize     = render_request_finalize;
210 211 212 213 214 215 216 217 218 219 220

  request_signals[REQUEST_COMPLETED] = g_signal_new("completed",
      ZATHURA_TYPE_RENDER_REQUEST,
      G_SIGNAL_RUN_LAST,
      0,
      NULL,
      NULL,
      g_cclosure_marshal_generic,
      G_TYPE_NONE,
      1,
      G_TYPE_POINTER);
221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240

  request_signals[REQUEST_CACHE_ADDED] = g_signal_new("cache-added",
      ZATHURA_TYPE_RENDER_REQUEST,
      G_SIGNAL_RUN_LAST,
      0,
      NULL,
      NULL,
      g_cclosure_marshal_generic,
      G_TYPE_NONE,
      0);

  request_signals[REQUEST_CACHE_INVALIDATED] = g_signal_new("cache-invalidated",
      ZATHURA_TYPE_RENDER_REQUEST,
      G_SIGNAL_RUN_LAST,
      0,
      NULL,
      NULL,
      g_cclosure_marshal_generic,
      G_TYPE_NONE,
      0);
241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258
}

static void
zathura_render_request_init(ZathuraRenderRequest* request)
{
  request_private_t* priv = REQUEST_GET_PRIVATE(request);
  priv->renderer = NULL;
  priv->page = NULL;
}

ZathuraRenderRequest*
zathura_render_request_new(ZathuraRenderer* renderer, zathura_page_t* page)
{
  g_return_val_if_fail(renderer != NULL && page != NULL, NULL);

  GObject* obj = g_object_new(ZATHURA_TYPE_RENDER_REQUEST, NULL);
  if (obj == NULL) {
    return NULL;
259
  }
260 261 262 263 264 265

  ZathuraRenderRequest* request = ZATHURA_RENDER_REQUEST(obj);
  request_private_t* priv = REQUEST_GET_PRIVATE(request);
  /* we want to make sure that renderer lives long enough */
  priv->renderer = g_object_ref(renderer);
  priv->page = page;
266
  priv->active_jobs = girara_list_new();
267
  g_mutex_init(&priv->jobs_mutex);
268

269 270 271
  /* register the request with the renderer */
  renderer_register_request(renderer, request);

272
  return request;
Moritz Lipp's avatar
Moritz Lipp committed
273 274
}

275
static void
276
render_request_dispose(GObject* object)
Moritz Lipp's avatar
Moritz Lipp committed
277
{
278 279
  ZathuraRenderRequest* request = ZATHURA_RENDER_REQUEST(object);
  request_private_t* priv = REQUEST_GET_PRIVATE(request);
Moritz Lipp's avatar
Moritz Lipp committed
280

281
  if (priv->renderer != NULL) {
282 283 284
    /* unregister the request */
    renderer_unregister_request(priv->renderer, request);
    /* release our private reference to the renderer */
285
    g_clear_object(&priv->renderer);
286
  }
287 288 289 290 291 292 293 294 295 296

  G_OBJECT_CLASS(zathura_render_request_parent_class)->dispose(object);
}

static void
render_request_finalize(GObject* object)
{
  ZathuraRenderRequest* request = ZATHURA_RENDER_REQUEST(object);
  request_private_t* priv = REQUEST_GET_PRIVATE(request);

297 298 299 300
  if (girara_list_size(priv->active_jobs) != 0) {
    girara_error("This should not happen!");
  }
  girara_list_free(priv->active_jobs);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
301
  g_mutex_clear(&priv->jobs_mutex);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
302

303
  G_OBJECT_CLASS(zathura_render_request_parent_class)->finalize(object);
304 305 306
}

/* renderer methods */
307

308 309 310 311
bool
zathura_renderer_recolor_enabled(ZathuraRenderer* renderer)
{
  g_return_val_if_fail(ZATHURA_IS_RENDERER(renderer), false);
312

313 314
  return GET_PRIVATE(renderer)->recolor.enabled;
}
Moritz Lipp's avatar
Moritz Lipp committed
315

316 317 318 319
void
zathura_renderer_enable_recolor(ZathuraRenderer* renderer, bool enable)
{
  g_return_if_fail(ZATHURA_IS_RENDERER(renderer));
Moritz Lipp's avatar
Moritz Lipp committed
320

321 322 323 324 325 326 327 328 329
  GET_PRIVATE(renderer)->recolor.enabled = enable;
}

bool
zathura_renderer_recolor_hue_enabled(ZathuraRenderer* renderer)
{
  g_return_val_if_fail(ZATHURA_IS_RENDERER(renderer), false);

  return GET_PRIVATE(renderer)->recolor.hue;
Moritz Lipp's avatar
Moritz Lipp committed
330 331 332
}

void
333
zathura_renderer_enable_recolor_hue(ZathuraRenderer* renderer, bool enable)
Moritz Lipp's avatar
Moritz Lipp committed
334
{
335 336 337 338 339
  g_return_if_fail(ZATHURA_IS_RENDERER(renderer));

  GET_PRIVATE(renderer)->recolor.hue = enable;
}

340 341 342 343 344 345 346 347 348 349 350 351 352 353 354
bool
zathura_renderer_recolor_reverse_video_enabled(ZathuraRenderer* renderer)
{
  g_return_val_if_fail(ZATHURA_IS_RENDERER(renderer), false);

  return GET_PRIVATE(renderer)->recolor.reverse_video;
}

void
zathura_renderer_enable_recolor_reverse_video(ZathuraRenderer* renderer, bool enable)
{
  g_return_if_fail(ZATHURA_IS_RENDERER(renderer));

  GET_PRIVATE(renderer)->recolor.reverse_video = enable;
}
355 356
void
zathura_renderer_set_recolor_colors(ZathuraRenderer* renderer,
357
    const GdkRGBA* light, const GdkRGBA* dark)
358 359 360 361 362
{
  g_return_if_fail(ZATHURA_IS_RENDERER(renderer));

  private_t* priv = GET_PRIVATE(renderer);
  if (light != NULL) {
363
    memcpy(&priv->recolor.light, light, sizeof(GdkRGBA));
364 365
  }
  if (dark != NULL) {
366
    memcpy(&priv->recolor.dark, dark, sizeof(GdkRGBA));
367 368 369 370 371 372 373 374 375 376
  }
}

void
zathura_renderer_set_recolor_colors_str(ZathuraRenderer* renderer,
    const char* light, const char* dark)
{
  g_return_if_fail(ZATHURA_IS_RENDERER(renderer));

  if (dark != NULL) {
377 378
    GdkRGBA color;
    gdk_rgba_parse(&color, dark);
379 380 381
    zathura_renderer_set_recolor_colors(renderer, NULL, &color);
  }
  if (light != NULL) {
382 383
    GdkRGBA color;
    gdk_rgba_parse(&color, light);
384 385 386 387 388 389
    zathura_renderer_set_recolor_colors(renderer, &color, NULL);
  }
}

void
zathura_renderer_get_recolor_colors(ZathuraRenderer* renderer,
390
    GdkRGBA* light, GdkRGBA* dark)
391 392 393 394
{
  g_return_if_fail(ZATHURA_IS_RENDERER(renderer));

  private_t* priv = GET_PRIVATE(renderer);
395
  if (light != NULL) {
396
    memcpy(light, &priv->recolor.light, sizeof(GdkRGBA));
397 398
  }
  if (dark != NULL) {
399
    memcpy(dark, &priv->recolor.dark, sizeof(GdkRGBA));
400
  }
401 402 403 404 405 406 407 408
}

void
zathura_renderer_lock(ZathuraRenderer* renderer)
{
  g_return_if_fail(ZATHURA_IS_RENDERER(renderer));

  private_t* priv = GET_PRIVATE(renderer);
409
  g_mutex_lock(&priv->mutex);
410
}
Moritz Lipp's avatar
Moritz Lipp committed
411

412 413 414 415 416 417
void
zathura_renderer_unlock(ZathuraRenderer* renderer)
{
  g_return_if_fail(ZATHURA_IS_RENDERER(renderer));

  private_t* priv = GET_PRIVATE(renderer);
418
  g_mutex_unlock(&priv->mutex);
419 420 421 422 423 424 425 426 427 428 429 430 431
}

void
zathura_renderer_stop(ZathuraRenderer* renderer)
{
  g_return_if_fail(ZATHURA_IS_RENDERER(renderer));
  GET_PRIVATE(renderer)->about_to_close = true;
}


/* ZathuraRenderRequest methods */

void
432
zathura_render_request(ZathuraRenderRequest* request, gint64 last_view_time)
433 434 435 436
{
  g_return_if_fail(ZATHURA_IS_RENDER_REQUEST(request));

  request_private_t* request_priv = REQUEST_GET_PRIVATE(request);
437
  g_mutex_lock(&request_priv->jobs_mutex);
438 439 440 441

  bool unfinished_jobs = false;
  /* check if there are any active jobs left */
  GIRARA_LIST_FOREACH(request_priv->active_jobs, render_job_t*, iter, job)
442
    if (job->aborted == false) {
443 444 445 446 447 448
      unfinished_jobs = true;
    }
  GIRARA_LIST_FOREACH_END(request_priv->active_jobs, render_job_t*, iter, job);

  /* only add a new job if there are no active ones left */
  if (unfinished_jobs == false) {
449
    request_priv->last_view_time = last_view_time;
450

451 452 453 454 455
    render_job_t* job = g_try_malloc0(sizeof(render_job_t));
    if (job == NULL) {
      return;
    }

456 457 458 459
    job->request = g_object_ref(request);
    job->aborted = false;
    girara_list_append(request_priv->active_jobs, job);

460
    private_t* priv = GET_PRIVATE(request_priv->renderer);
461
    g_thread_pool_push(priv->pool, job, NULL);
Moritz Lipp's avatar
Moritz Lipp committed
462
  }
463

464
  g_mutex_unlock(&request_priv->jobs_mutex);
465
}
466

467 468 469 470 471 472
void
zathura_render_request_abort(ZathuraRenderRequest* request)
{
  g_return_if_fail(ZATHURA_IS_RENDER_REQUEST(request));

  request_private_t* request_priv = REQUEST_GET_PRIVATE(request);
473
  g_mutex_lock(&request_priv->jobs_mutex);
474 475 476
  GIRARA_LIST_FOREACH(request_priv->active_jobs, render_job_t*, iter, job)
    job->aborted = true;
  GIRARA_LIST_FOREACH_END(request_priv->active_jobs, render_job_t*, iter, job);
477
  g_mutex_unlock(&request_priv->jobs_mutex);
Moritz Lipp's avatar
Moritz Lipp committed
478 479
}

480 481 482 483 484 485 486 487
void
zathura_render_request_update_view_time(ZathuraRenderRequest* request)
{
  g_return_if_fail(ZATHURA_IS_RENDER_REQUEST(request));

  request_private_t* request_priv = REQUEST_GET_PRIVATE(request);
  request_priv->last_view_time = g_get_real_time();
}
488

Sebastian Ramacher's avatar
Sebastian Ramacher committed
489 490
/* render job */

491 492 493 494 495
static void
remove_job_and_free(render_job_t* job)
{
  request_private_t* request_priv = REQUEST_GET_PRIVATE(job->request);

496
  g_mutex_lock(&request_priv->jobs_mutex);
497
  girara_list_remove(request_priv->active_jobs, job);
498
  g_mutex_unlock(&request_priv->jobs_mutex);
499 500 501 502 503

  g_object_unref(job->request);
  g_free(job);
}

504
typedef struct emit_completed_signal_s
Moritz Lipp's avatar
Moritz Lipp committed
505
{
506
  render_job_t* job;
507 508
  cairo_surface_t* surface;
} emit_completed_signal_t;
509

510 511 512 513
static gboolean
emit_completed_signal(void* data)
{
  emit_completed_signal_t* ecs = data;
514 515 516
  render_job_t* job = ecs->job;
  request_private_t* request_priv = REQUEST_GET_PRIVATE(job->request);
  private_t* priv = GET_PRIVATE(request_priv->renderer);
Moritz Lipp's avatar
Moritz Lipp committed
517

518
  if (priv->about_to_close == false && job->aborted == false) {
519
    /* emit the signal */
520 521
    girara_debug("Emitting signal for page %d",
        zathura_page_get_index(request_priv->page) + 1);
522
    g_signal_emit(job->request, request_signals[REQUEST_COMPLETED], 0, ecs->surface);
523 524 525
  } else {
    girara_debug("Rendering of page %d aborted",
        zathura_page_get_index(request_priv->page) + 1);
526
  }
527
  /* mark the request as done */
528
  remove_job_and_free(job);
529 530 531 532 533 534

  /* clean up the data */
  cairo_surface_destroy(ecs->surface);
  g_free(ecs);

  return FALSE;
Moritz Lipp's avatar
Moritz Lipp committed
535 536
}

537 538 539
/* Returns the maximum possible saturation for given h and l.
   Assumes that l is in the interval l1, l2 and corrects the value to
   force u=0 on l1 and l2 */
Sebastian Ramacher's avatar
Sebastian Ramacher committed
540
static double
541
colorumax(const double* h, double l, double l1, double l2)
542 543 544 545 546
{
  if (h[0] == 0 && h[1] == 0 && h[2] == 0) {
    return 0;
  }

547 548 549
  const double lv = (l - l1)/(l2 - l1);    /* Remap l to the whole interval 0,1 */
  double u = 1000000;
  double v = u;
550 551
  for (int k = 0; k < 3; k++) {
    if (h[k] > 0) {
552 553
      const double uu = fabs((1-l)/h[k]);
      const double vv = fabs((1-lv)/h[k]);
554

Sebastian Ramacher's avatar
Sebastian Ramacher committed
555 556 557 558 559 560
      if (uu < u) {
        u = uu;
      }
      if (vv < v) {
        v = vv;
      }
561
    } else if (h[k] < 0) {
562 563
      const double uu = fabs(l/h[k]);
      const double vv = fabs(lv/h[k]);
564

Sebastian Ramacher's avatar
Sebastian Ramacher committed
565 566 567 568 569 570
      if (uu < u) {
        u = uu;
      }
      if (vv < v) {
        v = vv;
      }
571 572 573 574
    }
  }

  /* rescale v according to the length of the interval [l1, l2] */
Sebastian Ramacher's avatar
Sebastian Ramacher committed
575
  v = fabs(l2 - l1) * v;
576 577 578 579 580

  /* forces the returned value to be 0 on l1 and l2, trying not to distort colors too much */
  return fmin(u, v);
}

581
static void
582 583
recolor(private_t* priv, zathura_page_t* page, unsigned int page_width, 
        unsigned int page_height, cairo_surface_t* surface)
584 585 586
{
  /* uses a representation of a rgb color as follows:
     - a lightness scalar (between 0,1), which is a weighted average of r, g, b,
587 588 589 590
     - a hue vector, which indicates a radian direction from the grey axis,
       inside the equal lightness plane.
     - a saturation scalar between 0,1. It is 0 when grey, 1 when the color is
       in the boundary of the rgb cube.
591 592
  */

593 594 595 596 597 598
  /* TODO: split handling of image handling off
   * Ideally we would create a mask surface for the location of the images and
   * we would blit the the recolored and unmodified surfaces together to get the
   * same effect.
   */

599 600
  cairo_surface_flush(surface);

601 602 603 604 605 606 607 608
  const int rowstride  = cairo_image_surface_get_stride(surface);
  unsigned char* image = cairo_image_surface_get_data(surface);

  /* RGB weights for computing lightness. Must sum to one */
  static const double a[] = {0.30, 0.59, 0.11};

#define rgb1 priv->recolor.dark
#define rgb2 priv->recolor.light
609 610
  const double l1 = a[0]*rgb1.red + a[1]*rgb1.green + a[2]*rgb1.blue;
  const double l2 = a[0]*rgb2.red + a[1]*rgb2.green + a[2]*rgb2.blue;
611 612

  const double rgb_diff[] = {
613 614 615
    rgb2.red - rgb1.red,
    rgb2.green - rgb1.green,
    rgb2.blue - rgb1.blue
616 617
  };

618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645
  girara_list_t* images     = NULL;
  girara_list_t* rectangles = NULL;
  bool found_images         = false;

  /* If in reverse video mode retrieve images */
  if (priv->recolor.reverse_video == true) {
    images = zathura_page_images_get(page, NULL);
    found_images = (images != NULL);

    rectangles = girara_list_new();
    if (rectangles == NULL) {
      found_images = false;
      girara_warning("Failed to retrieve images.\n");
    }

    if (found_images == true) {
      /* Get images bounding boxes */
      GIRARA_LIST_FOREACH(images, zathura_image_t*, iter, image_it)
        zathura_rectangle_t* rect = g_try_malloc(sizeof(zathura_rectangle_t));
        if (rect == NULL) {
          break;
        }
        *rect = recalc_rectangle(page, image_it->position);
        girara_list_append(rectangles, rect);
      GIRARA_LIST_FOREACH_END(images, zathura_image_t*, iter, image_it);
    }
  }

646 647 648 649
  for (unsigned int y = 0; y < page_height; y++) {
    unsigned char* data = image + y * rowstride;

    for (unsigned int x = 0; x < page_width; x++, data += 4) {
650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665
      /* Check if the pixel belongs to an image when in reverse video mode*/
      if (priv->recolor.reverse_video == true && found_images == true){
        bool inside_image = false;
        GIRARA_LIST_FOREACH(rectangles, zathura_rectangle_t*, iter, rect_it)
          if (rect_it->x1 <= x && rect_it->x2 >= x &&
              rect_it->y1 <= y && rect_it->y2 >= y) {
            inside_image = true;
            break;
          }
        GIRARA_LIST_FOREACH_END(rectangles, zathura_rectangle_t*, iter, rect_it);
        /* If it's inside and image don't recolor */
        if (inside_image == true) {
          continue;
        }
      }

666 667
      /* Careful. data color components blue, green, red. */
      const double rgb[3] = {
Sebastian Ramacher's avatar
Sebastian Ramacher committed
668 669 670
        data[2] / 255.,
        data[1] / 255.,
        data[0] / 255.
671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703
      };

      /* compute h, s, l data   */
      double l = a[0]*rgb[0] + a[1]*rgb[1] + a[2]*rgb[2];

      if (priv->recolor.hue == true) {
        /* adjusting lightness keeping hue of current color. white and black
         * go to grays of same ligtness as light and dark colors. */
        const double h[3] = {
          rgb[0] - l,
          rgb[1] - l,
          rgb[2] - l
        };

        /* u is the maximum possible saturation for given h and l. s is a
         * rescaled saturation between 0 and 1 */
        double u = colorumax(h, l, 0, 1);
        double s = 0;
        if (u != 0) {
          s = 1/u;
        }

        /* Interpolates lightness between light and dark colors. white goes to
         * light, and black goes to dark. */
        l = l * (l2 - l1) + l1;
        u = colorumax(h, l, l1, l2);

        data[2] = (unsigned char)round(255.*(l + s*u * h[0]));
        data[1] = (unsigned char)round(255.*(l + s*u * h[1]));
        data[0] = (unsigned char)round(255.*(l + s*u * h[2]));
      } else {
        /* linear interpolation between dark and light with color ligtness as
         * a parameter */
704 705 706
        data[2] = (unsigned char)round(255.*(l * rgb_diff[0] + rgb1.red));
        data[1] = (unsigned char)round(255.*(l * rgb_diff[1] + rgb1.green));
        data[0] = (unsigned char)round(255.*(l * rgb_diff[2] + rgb1.blue));
707 708 709 710
      }
    }
  }

711 712 713 714 715 716 717
  if (images != NULL) {
    girara_list_free(images);
  }
  if (rectangles != NULL) {
    girara_list_free(rectangles);
  }

718 719
  cairo_surface_mark_dirty(surface);

720 721 722 723
#undef rgb1
#undef rgb2
}

724
static bool
725
render(render_job_t* job, ZathuraRenderRequest* request, ZathuraRenderer* renderer)
Moritz Lipp's avatar
Moritz Lipp committed
726
{
727 728 729
  private_t* priv = GET_PRIVATE(renderer);
  request_private_t* request_priv = REQUEST_GET_PRIVATE(request);
  zathura_page_t* page = request_priv->page;
Moritz Lipp's avatar
Moritz Lipp committed
730

Moritz Lipp's avatar
Moritz Lipp committed
731 732 733
  /* create cairo surface */
  unsigned int page_width  = 0;
  unsigned int page_height = 0;
734 735

  zathura_document_t* document = zathura_page_get_document(page);
736 737
  const double height = zathura_page_get_height(page);
  const double width = zathura_page_get_width(page);
738 739

  const double real_scale = page_calc_height_width(document, height, width,
740 741
                                                   &page_height, &page_width,
                                                   false);
742

Moritz Lipp's avatar
Moritz Lipp committed
743

744 745
  cairo_surface_t* surface = cairo_image_surface_create(CAIRO_FORMAT_RGB24,
      page_width, page_height);
Moritz Lipp's avatar
Moritz Lipp committed
746
  if (surface == NULL) {
Moritz Lipp's avatar
Moritz Lipp committed
747 748
    return false;
  }
749 750 751 752
  if (cairo_surface_status(surface) != CAIRO_STATUS_SUCCESS) {
    cairo_surface_destroy(surface);
    return false;
  }
Moritz Lipp's avatar
Moritz Lipp committed
753

Moritz Lipp's avatar
Moritz Lipp committed
754 755 756 757 758
  cairo_t* cairo = cairo_create(surface);
  if (cairo == NULL) {
    cairo_surface_destroy(surface);
    return false;
  }
Moritz Lipp's avatar
Moritz Lipp committed
759

Moritz Lipp's avatar
Moritz Lipp committed
760 761 762 763 764 765
  cairo_save(cairo);
  cairo_set_source_rgb(cairo, 1, 1, 1);
  cairo_rectangle(cairo, 0, 0, page_width, page_height);
  cairo_fill(cairo);
  cairo_restore(cairo);
  cairo_save(cairo);
Moritz Lipp's avatar
Moritz Lipp committed
766

767 768
  if (fabs(real_scale - 1.0f) > FLT_EPSILON) {
    cairo_scale(cairo, real_scale, real_scale);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
769 770
  }

771
  zathura_renderer_lock(renderer);
772
  const int err = zathura_page_render(page, cairo, false);
773
  zathura_renderer_unlock(renderer);
Moritz Lipp's avatar
Moritz Lipp committed
774 775
  cairo_restore(cairo);
  cairo_destroy(cairo);
776 777 778 779
  if (err != ZATHURA_ERROR_OK) {
    cairo_surface_destroy(surface);
    return false;
  }
Moritz Lipp's avatar
Moritz Lipp committed
780

781
  /* before recoloring, check if we've been aborted */
782
  if (priv->about_to_close == true || job->aborted == true) {
783
    girara_debug("Rendering of page %d aborted",
784
                 zathura_page_get_index(request_priv->page) + 1);
785
    remove_job_and_free(job);
786 787 788 789
    cairo_surface_destroy(surface);
    return true;
  }

Moritz Lipp's avatar
Moritz Lipp committed
790
  /* recolor */
791
  if (priv->recolor.enabled == true) {
792
    recolor(priv, page, page_width, page_height, surface);
Moritz Lipp's avatar
Moritz Lipp committed
793 794
  }

795
  emit_completed_signal_t* ecs = g_try_malloc0(sizeof(emit_completed_signal_t));
796
  if (ecs == NULL) {
797
    cairo_surface_destroy(surface);
798 799 800
    return false;
  }

801
  ecs->job     = job;
802
  ecs->surface = cairo_surface_reference(surface);
803

804 805
  /* emit signal from the main context, i.e. the main thread */
  g_main_context_invoke(NULL, emit_completed_signal, ecs);
Moritz Lipp's avatar
Moritz Lipp committed
806

807 808
  cairo_surface_destroy(surface);

Moritz Lipp's avatar
Moritz Lipp committed
809 810
  return true;
}
811

812 813 814
static void
render_job(void* data, void* user_data)
{
815 816
  render_job_t* job = data;
  ZathuraRenderRequest* request = job->request;
817 818 819 820 821
  ZathuraRenderer* renderer = user_data;
  g_return_if_fail(ZATHURA_IS_RENDER_REQUEST(request));
  g_return_if_fail(ZATHURA_IS_RENDERER(renderer));

  private_t* priv = GET_PRIVATE(renderer);
822
  if (priv->about_to_close == true || job->aborted == true) {
823
    /* back out early */
824
    remove_job_and_free(job);
825 826 827
    return;
  }

828
  request_private_t* request_priv = REQUEST_GET_PRIVATE(request);
829 830
  girara_debug("Rendering page %d ...",
      zathura_page_get_index(request_priv->page) + 1);
831
  if (render(job, request, renderer) != true) {
832 833
    girara_error("Rendering failed (page %d)\n",
        zathura_page_get_index(request_priv->page) + 1);
834
    remove_job_and_free(job);
835 836 837 838
  }
}


839
void
Moritz Lipp's avatar
Moritz Lipp committed
840
render_all(zathura_t* zathura)
841
{
842
  if (zathura == NULL || zathura->document == NULL) {
843 844 845 846
    return;
  }

  /* unmark all pages */
847
  const unsigned int number_of_pages = zathura_document_get_number_of_pages(zathura->document);
Sebastian Ramacher's avatar
CS  
Sebastian Ramacher committed
848
  for (unsigned int page_id = 0; page_id < number_of_pages; ++page_id) {
849 850
    zathura_page_t* page = zathura_document_get_page(zathura->document,
        page_id);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
851
    unsigned int page_height = 0, page_width = 0;
852 853
    const double height = zathura_page_get_height(page);
    const double width = zathura_page_get_width(page);
854
    page_calc_height_width(zathura->document, height, width, &page_height, &page_width, true);
855

Sebastian Ramacher's avatar
Sebastian Ramacher committed
856
    girara_debug("Queuing resize for page %u to %u x %u (%f x %f).", page_id, page_width, page_height, width, height);
857
    GtkWidget* widget = zathura_page_get_widget(zathura, page);
Moritz Lipp's avatar
Moritz Lipp committed
858 859
    gtk_widget_set_size_request(widget, page_width, page_height);
    gtk_widget_queue_resize(widget);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
860
  }
Moritz Lipp's avatar
Moritz Lipp committed
861
}
862 863

static gint
864
render_thread_sort(gconstpointer a, gconstpointer b, gpointer UNUSED(data))
865
{
866
  if (a == NULL || b == NULL) {
867 868 869
    return 0;
  }

870 871 872 873 874 875
  const render_job_t* job_a = a;
  const render_job_t* job_b = b;
  if (job_a->aborted == job_b->aborted) {
    request_private_t* priv_a = REQUEST_GET_PRIVATE(job_a->request);
    request_private_t* priv_b = REQUEST_GET_PRIVATE(job_b->request);

876 877
    return priv_a->last_view_time < priv_b->last_view_time ? -1 :
        (priv_a->last_view_time > priv_b->last_view_time ? 1 : 0);
878 879
  }

880
  /* sort aborted entries earlier so that the are thrown out of the queue */
881
  return job_a->aborted ? 1 : -1;
882
}
883 884 885

/* cache functions */

886 887
static bool
page_cache_is_cached(ZathuraRenderer* renderer, unsigned int page_index)
888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927
{
  g_return_val_if_fail(renderer != NULL, false);
  private_t* priv = GET_PRIVATE(renderer);

  if (priv->page_cache.num_cached_pages != 0) {
    for (size_t i = 0; i < priv->page_cache.size; ++i) {
      if (priv->page_cache.cache[i] >= 0 &&
          page_index == (unsigned int)priv->page_cache.cache[i]) {
        girara_debug("Page %d is a cache hit", page_index + 1);
        return true;
      }
    }
  }

  girara_debug("Page %d is a cache miss", page_index + 1);
  return false;
}

static int
find_request_by_page_index(const void* req, const void* data)
{
  const ZathuraRenderRequest* request = req;
  const unsigned int page_index = *((const int*)data);

  request_private_t* priv = REQUEST_GET_PRIVATE(request);
  if (zathura_page_get_index(priv->page) == page_index) {
    return 0;
  }
  return 1;
}

static ssize_t
page_cache_lru_invalidate(ZathuraRenderer* renderer)
{
  g_return_val_if_fail(renderer != NULL, -1);
  private_t* priv = GET_PRIVATE(renderer);
  g_return_val_if_fail(priv->page_cache.size != 0, -1);

  ssize_t lru_index = 0;
  gint64 lru_view_time = G_MAXINT64;
928
  ZathuraRenderRequest* request = NULL;
929
  for (size_t i = 0; i < priv->page_cache.size; ++i) {
930
    ZathuraRenderRequest* tmp_request = girara_list_find(priv->requests,
931
        find_request_by_page_index, &priv->page_cache.cache[i]);
932 933
    g_return_val_if_fail(tmp_request != NULL, -1);
    request_private_t* request_priv = REQUEST_GET_PRIVATE(tmp_request);
934 935 936 937

    if (request_priv->last_view_time < lru_view_time) {
      lru_view_time = request_priv->last_view_time;
      lru_index = i;
938
      request = tmp_request;
939 940 941 942 943 944
    }
  }

  request_private_t* request_priv = REQUEST_GET_PRIVATE(request);

  /* emit the signal */
945
  g_signal_emit(request, request_signals[REQUEST_CACHE_INVALIDATED], 0);
946 947 948 949 950 951 952 953 954 955 956
  girara_debug("Invalidated page %d at cache index %zd",
      zathura_page_get_index(request_priv->page) + 1, lru_index);
  priv->page_cache.cache[lru_index] = -1;
  --priv->page_cache.num_cached_pages;

  return lru_index;
}

static bool
page_cache_is_full(ZathuraRenderer* renderer, bool* result)
{
957
  g_return_val_if_fail(ZATHURA_IS_RENDERER(renderer) && result != NULL, false);
958

959
  private_t* priv = GET_PRIVATE(renderer);
960 961 962 963 964
  *result = priv->page_cache.num_cached_pages == priv->page_cache.size;

  return true;
}

965
static void
966 967
page_cache_invalidate_all(ZathuraRenderer* renderer)
{
968
  g_return_if_fail(ZATHURA_IS_RENDERER(renderer));
969 970 971 972 973 974 975 976 977

  private_t* priv = GET_PRIVATE(renderer);
  for (size_t i = 0; i < priv->page_cache.size; ++i) {
    priv->page_cache.cache[i] = -1;
  }
  priv->page_cache.num_cached_pages = 0;
}

void
978 979
zathura_renderer_page_cache_add(ZathuraRenderer* renderer,
    unsigned int page_index)
980
{
981 982
  g_return_if_fail(ZATHURA_IS_RENDERER(renderer));
  if (page_cache_is_cached(renderer, page_index) == true) {
983 984 985 986 987 988 989 990
    return;
  }

  private_t* priv = GET_PRIVATE(renderer);
  bool full = false;
  if (page_cache_is_full(renderer, &full) == false) {
    return;
  } else if (full == true) {
991
    const ssize_t idx = page_cache_lru_invalidate(renderer);
992 993 994 995 996 997 998
    if (idx == -1) {
      return;
    }

    priv->page_cache.cache[idx] = page_index;
    ++priv->page_cache.num_cached_pages;
    girara_debug("Page %d is cached at cache index %zd", page_index + 1, idx);
999 1000 1001 1002
  } else {
    priv->page_cache.cache[priv->page_cache.num_cached_pages++] = page_index;
    girara_debug("Page %d is cached at cache index %zu", page_index + 1,
        priv->page_cache.num_cached_pages - 1);
1003 1004
  }

1005 1006 1007 1008
  ZathuraRenderRequest* request = girara_list_find(priv->requests,
        find_request_by_page_index, &page_index);
  g_return_if_fail(request != NULL);
  g_signal_emit(request, request_signals[REQUEST_CACHE_ADDED], 0);
1009
}