render.c 27.6 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 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186

  free(priv->page_cache.cache);
  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 601 602 603 604 605 606
  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
607 608
  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;
609 610

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

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
  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);
    }
  }

644 645 646 647
  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) {
648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663
      /* 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;
        }
      }

664 665 666 667 668 669 670 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
      /* Careful. data color components blue, green, red. */
      const double rgb[3] = {
        (double) data[2] / 256.,
        (double) data[1] / 256.,
        (double) data[0] / 256.
      };

      /* 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 */
702 703 704
        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));
705 706 707 708
      }
    }
  }

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

716 717 718 719
#undef rgb1
#undef rgb2
}

720
static bool
721
render(render_job_t* job, ZathuraRenderRequest* request, ZathuraRenderer* renderer)
Moritz Lipp's avatar
Moritz Lipp committed
722
{
723 724 725
  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
726

Moritz Lipp's avatar
Moritz Lipp committed
727 728 729
  /* create cairo surface */
  unsigned int page_width  = 0;
  unsigned int page_height = 0;
730 731

  zathura_document_t* document = zathura_page_get_document(page);
732 733
  const double height = zathura_page_get_height(page);
  const double width = zathura_page_get_width(page);
734 735

  const double real_scale = page_calc_height_width(document, height, width,
736 737
                                                   &page_height, &page_width,
                                                   false);
738

Moritz Lipp's avatar
Moritz Lipp committed
739

740 741
  cairo_surface_t* surface = cairo_image_surface_create(CAIRO_FORMAT_RGB24,
      page_width, page_height);
Moritz Lipp's avatar
Moritz Lipp committed
742
  if (surface == NULL) {
Moritz Lipp's avatar
Moritz Lipp committed
743 744 745
    return false;
  }

Moritz Lipp's avatar
Moritz Lipp committed
746 747 748 749 750
  cairo_t* cairo = cairo_create(surface);
  if (cairo == NULL) {
    cairo_surface_destroy(surface);
    return false;
  }
Moritz Lipp's avatar
Moritz Lipp committed
751

Moritz Lipp's avatar
Moritz Lipp committed
752 753 754 755 756 757
  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
758

759 760
  if (fabs(real_scale - 1.0f) > FLT_EPSILON) {
    cairo_scale(cairo, real_scale, real_scale);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
761 762
  }

763
  zathura_renderer_lock(renderer);
764
  const int err = zathura_page_render(page, cairo, false);
765
  zathura_renderer_unlock(renderer);
Moritz Lipp's avatar
Moritz Lipp committed
766 767
  cairo_restore(cairo);
  cairo_destroy(cairo);
768 769 770 771
  if (err != ZATHURA_ERROR_OK) {
    cairo_surface_destroy(surface);
    return false;
  }
Moritz Lipp's avatar
Moritz Lipp committed
772

773
  /* before recoloring, check if we've been aborted */
774
  if (priv->about_to_close == true || job->aborted == true) {
775
    girara_debug("Rendering of page %d aborted",
776
                 zathura_page_get_index(request_priv->page) + 1);
777
    remove_job_and_free(job);
778 779 780 781
    cairo_surface_destroy(surface);
    return true;
  }

Moritz Lipp's avatar
Moritz Lipp committed
782
  /* recolor */
783
  if (priv->recolor.enabled == true) {
784
    recolor(priv, page, page_width, page_height, surface);
Moritz Lipp's avatar
Moritz Lipp committed
785 786
  }

787
  emit_completed_signal_t* ecs = g_try_malloc0(sizeof(emit_completed_signal_t));
788
  if (ecs == NULL) {
789
    cairo_surface_destroy(surface);
790 791 792
    return false;
  }

793
  ecs->job     = job;
794
  ecs->surface = cairo_surface_reference(surface);
795

796 797
  /* 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
798

799 800
  cairo_surface_destroy(surface);

Moritz Lipp's avatar
Moritz Lipp committed
801 802
  return true;
}
803

804 805 806
static void
render_job(void* data, void* user_data)
{
807 808
  render_job_t* job = data;
  ZathuraRenderRequest* request = job->request;
809 810 811 812 813
  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);
814
  if (priv->about_to_close == true || job->aborted == true) {
815
    /* back out early */
816
    remove_job_and_free(job);
817 818 819
    return;
  }

820
  request_private_t* request_priv = REQUEST_GET_PRIVATE(request);
821 822
  girara_debug("Rendering page %d ...",
      zathura_page_get_index(request_priv->page) + 1);
823
  if (render(job, request, renderer) != true) {
824 825
    girara_error("Rendering failed (page %d)\n",
        zathura_page_get_index(request_priv->page) + 1);
826
    remove_job_and_free(job);
827 828 829 830
  }
}


831
void
Moritz Lipp's avatar
Moritz Lipp committed
832
render_all(zathura_t* zathura)
833
{
834
  if (zathura == NULL || zathura->document == NULL) {
835 836 837 838
    return;
  }

  /* unmark all pages */
839
  const unsigned int number_of_pages = zathura_document_get_number_of_pages(zathura->document);
840
  for (unsigned int page_id = 0; page_id < number_of_pages; page_id++) {
841 842
    zathura_page_t* page = zathura_document_get_page(zathura->document,
        page_id);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
843
    unsigned int page_height = 0, page_width = 0;
844 845
    const double height = zathura_page_get_height(page);
    const double width = zathura_page_get_width(page);
846
    page_calc_height_width(zathura->document, height, width, &page_height, &page_width, true);
847

848
    GtkWidget* widget = zathura_page_get_widget(zathura, page);
Moritz Lipp's avatar
Moritz Lipp committed
849 850
    gtk_widget_set_size_request(widget, page_width, page_height);
    gtk_widget_queue_resize(widget);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
851
  }
Moritz Lipp's avatar
Moritz Lipp committed
852
}
853 854

static gint
855
render_thread_sort(gconstpointer a, gconstpointer b, gpointer UNUSED(data))
856
{
857
  if (a == NULL || b == NULL) {
858 859 860
    return 0;
  }

861 862 863 864 865 866
  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);

867 868
    return priv_a->last_view_time < priv_b->last_view_time ? -1 :
        (priv_a->last_view_time > priv_b->last_view_time ? 1 : 0);
869 870
  }

871
  /* sort aborted entries earlier so that the are thrown out of the queue */
872
  return job_a->aborted ? 1 : -1;
873
}
874 875 876

/* cache functions */

877 878
static bool
page_cache_is_cached(ZathuraRenderer* renderer, unsigned int page_index)
879 880 881 882 883 884 885 886 887 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
{
  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;
919
  ZathuraRenderRequest* request = NULL;
920
  for (size_t i = 0; i < priv->page_cache.size; ++i) {
921
    ZathuraRenderRequest* tmp_request = girara_list_find(priv->requests,
922
        find_request_by_page_index, &priv->page_cache.cache[i]);
923 924
    g_return_val_if_fail(tmp_request != NULL, -1);
    request_private_t* request_priv = REQUEST_GET_PRIVATE(tmp_request);
925 926 927 928

    if (request_priv->last_view_time < lru_view_time) {
      lru_view_time = request_priv->last_view_time;
      lru_index = i;
929
      request = tmp_request;
930 931 932 933 934 935
    }
  }

  request_private_t* request_priv = REQUEST_GET_PRIVATE(request);

  /* emit the signal */
936
  g_signal_emit(request, request_signals[REQUEST_CACHE_INVALIDATED], 0);
937 938 939 940 941 942 943 944 945 946 947
  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)
{
948
  g_return_val_if_fail(ZATHURA_IS_RENDERER(renderer) && result != NULL, false);
949

950
  private_t* priv = GET_PRIVATE(renderer);
951 952 953 954 955
  *result = priv->page_cache.num_cached_pages == priv->page_cache.size;

  return true;
}

956
static void
957 958
page_cache_invalidate_all(ZathuraRenderer* renderer)
{
959
  g_return_if_fail(ZATHURA_IS_RENDERER(renderer));
960 961 962 963 964 965 966 967 968

  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
969 970
zathura_renderer_page_cache_add(ZathuraRenderer* renderer,
    unsigned int page_index)
971
{
972 973
  g_return_if_fail(ZATHURA_IS_RENDERER(renderer));
  if (page_cache_is_cached(renderer, page_index) == true) {
974 975 976 977 978 979 980 981
    return;
  }

  private_t* priv = GET_PRIVATE(renderer);
  bool full = false;
  if (page_cache_is_full(renderer, &full) == false) {
    return;
  } else if (full == true) {
982
    const ssize_t idx = page_cache_lru_invalidate(renderer);
983 984 985 986 987 988 989
    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);
990 991 992 993
  } 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);
994 995
  }

996 997 998 999
  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);
1000
}