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
#include "glib-compat.h"
8

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

17 18 19 20 21
/* 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  */
22
static void renderer_finalize(GObject* object);
23
/* private methods for ZathuraRenderRequest */
24
static void render_request_dispose(GObject* object);
25
static void render_request_finalize(GObject* object);
26

27
static void render_job(void* data, void* user_data);
28
static gint render_thread_sort(gconstpointer a, gconstpointer b, gpointer data);
29 30 31 32
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);

33 34 35

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

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

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

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

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

/* private data for ZathuraRenderRequest */
typedef struct request_private_s {
  ZathuraRenderer* renderer;
  zathura_page_t* page;
69
  gint64 last_view_time;
70 71
  girara_list_t* active_jobs;
  mutex jobs_mutex;
72 73 74 75 76 77 78 79
} 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))

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

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

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

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

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);
  mutex_init(&priv->mutex);

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

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

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

  priv->requests = girara_list_new();
}

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

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

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

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

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

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

  return ret;
152 153 154
}

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

  zathura_renderer_stop(renderer);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
161
  if (priv->pool != NULL) {
162
    g_thread_pool_free(priv->pool, TRUE, TRUE);
Moritz Lipp's avatar
Moritz Lipp committed
163
  }
164
  mutex_free(&(priv->mutex));
165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187

  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);
  }
188 189 190 191 192 193
}

/* init, new and free for ZathuraRenderRequest */

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

199 200 201 202 203 204 205 206 207 208
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);
209
  object_class->dispose      = render_request_dispose;
210
  object_class->finalize     = render_request_finalize;
211 212 213 214 215 216 217 218 219 220 221

  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);
222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241

  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);
242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259
}

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;
260
  }
261 262 263 264 265 266

  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;
267 268
  priv->active_jobs = girara_list_new();
  mutex_init(&priv->jobs_mutex);
269

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

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

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

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

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

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

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

/* renderer methods */
308

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

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

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

322 323 324 325 326 327 328 329 330
  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
331 332 333
}

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

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

341 342 343 344 345 346 347 348 349 350 351 352 353 354 355
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;
}
356 357
void
zathura_renderer_set_recolor_colors(ZathuraRenderer* renderer,
358
    const GdkRGBA* light, const GdkRGBA* dark)
359 360 361 362 363
{
  g_return_if_fail(ZATHURA_IS_RENDERER(renderer));

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

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) {
378 379
    GdkRGBA color;
    gdk_rgba_parse(&color, dark);
380 381 382
    zathura_renderer_set_recolor_colors(renderer, NULL, &color);
  }
  if (light != NULL) {
383 384
    GdkRGBA color;
    gdk_rgba_parse(&color, light);
385 386 387 388 389 390
    zathura_renderer_set_recolor_colors(renderer, &color, NULL);
  }
}

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

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

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

  private_t* priv = GET_PRIVATE(renderer);
  mutex_lock(&priv->mutex);
}
Moritz Lipp's avatar
Moritz Lipp committed
412

413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432
void
zathura_renderer_unlock(ZathuraRenderer* renderer)
{
  g_return_if_fail(ZATHURA_IS_RENDERER(renderer));

  private_t* priv = GET_PRIVATE(renderer);
  mutex_unlock(&priv->mutex);
}

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


/* ZathuraRenderRequest methods */

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

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

  bool unfinished_jobs = false;
  /* check if there are any active jobs left */
  GIRARA_LIST_FOREACH(request_priv->active_jobs, render_job_t*, iter, job)
443
    if (job->aborted == false) {
444 445 446 447 448 449
      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) {
450
    request_priv->last_view_time = last_view_time;
451

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

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

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

  mutex_unlock(&request_priv->jobs_mutex);
466
}
467

468 469 470 471 472 473
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);
474 475 476 477 478
  mutex_lock(&request_priv->jobs_mutex);
  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);
  mutex_unlock(&request_priv->jobs_mutex);
Moritz Lipp's avatar
Moritz Lipp committed
479 480
}

481 482 483 484 485 486 487 488
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();
}
489

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

492 493 494 495 496 497 498 499 500 501 502 503 504
static void
remove_job_and_free(render_job_t* job)
{
  request_private_t* request_priv = REQUEST_GET_PRIVATE(job->request);

  mutex_lock(&request_priv->jobs_mutex);
  girara_list_remove(request_priv->active_jobs, job);
  mutex_unlock(&request_priv->jobs_mutex);

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

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

511 512 513 514
static gboolean
emit_completed_signal(void* data)
{
  emit_completed_signal_t* ecs = data;
515 516 517
  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
518

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

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

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

538 539 540
/* 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
541
static double
542
colorumax(const double* h, double l, double l1, double l2)
543 544 545 546 547
{
  if (h[0] == 0 && h[1] == 0 && h[2] == 0) {
    return 0;
  }

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

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

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

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

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

582
static void
583 584
recolor(private_t* priv, zathura_page_t* page, unsigned int page_width, 
        unsigned int page_height, cairo_surface_t* surface)
585 586 587
{
  /* uses a representation of a rgb color as follows:
     - a lightness scalar (between 0,1), which is a weighted average of r, g, b,
588 589 590 591
     - 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.
592 593
  */

594 595 596 597 598 599
  /* 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.
   */

600 601 602 603 604 605 606 607
  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
608 609
  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;
610 611

  const double rgb_diff[] = {
612 613 614
    rgb2.red - rgb1.red,
    rgb2.green - rgb1.green,
    rgb2.blue - rgb1.blue
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 644
  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);
    }
  }

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

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

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

717 718 719 720
#undef rgb1
#undef rgb2
}

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

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

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

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

Moritz Lipp's avatar
Moritz Lipp committed
740

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

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

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

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

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

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

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

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

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

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

800 801
  cairo_surface_destroy(surface);

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

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

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


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

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

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

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

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

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

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

/* cache functions */

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

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

  request_private_t* request_priv = REQUEST_GET_PRIVATE(request);

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

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

  return true;
}

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

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

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

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