render.c 25.2 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 46
  struct {
    bool enabled;
    bool hue;

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 70
  girara_list_t* active_jobs;
  mutex 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 105 106
}

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

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

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

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

  priv->requests = girara_list_new();
}

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

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

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

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

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

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

  return ret;
150 151 152
}

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

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

  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);
  }
186 187 188 189 190 191
}

/* init, new and free for ZathuraRenderRequest */

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

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

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

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

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;
258
  }
259 260 261 262 263 264

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

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

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

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

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

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

296 297 298 299 300
  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
301

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

/* renderer methods */
306

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

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

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

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

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

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

339 340
void
zathura_renderer_set_recolor_colors(ZathuraRenderer* renderer,
341
    const GdkRGBA* light, const GdkRGBA* dark)
342 343 344 345 346
{
  g_return_if_fail(ZATHURA_IS_RENDERER(renderer));

  private_t* priv = GET_PRIVATE(renderer);
  if (light != NULL) {
347
    memcpy(&priv->recolor.light, light, sizeof(GdkRGBA));
348 349
  }
  if (dark != NULL) {
350
    memcpy(&priv->recolor.dark, dark, sizeof(GdkRGBA));
351 352 353 354 355 356 357 358 359 360
  }
}

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) {
361 362
    GdkRGBA color;
    gdk_rgba_parse(&color, dark);
363 364 365
    zathura_renderer_set_recolor_colors(renderer, NULL, &color);
  }
  if (light != NULL) {
366 367
    GdkRGBA color;
    gdk_rgba_parse(&color, light);
368 369 370 371 372 373
    zathura_renderer_set_recolor_colors(renderer, &color, NULL);
  }
}

void
zathura_renderer_get_recolor_colors(ZathuraRenderer* renderer,
374
    GdkRGBA* light, GdkRGBA* dark)
375 376 377 378
{
  g_return_if_fail(ZATHURA_IS_RENDERER(renderer));

  private_t* priv = GET_PRIVATE(renderer);
379
  if (light != NULL) {
380
    memcpy(light, &priv->recolor.light, sizeof(GdkRGBA));
381 382
  }
  if (dark != NULL) {
383
    memcpy(dark, &priv->recolor.dark, sizeof(GdkRGBA));
384
  }
385 386 387 388 389 390 391 392 393 394
}

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
395

396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415
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
416
zathura_render_request(ZathuraRenderRequest* request, gint64 last_view_time)
417 418 419 420
{
  g_return_if_fail(ZATHURA_IS_RENDER_REQUEST(request));

  request_private_t* request_priv = REQUEST_GET_PRIVATE(request);
421 422 423 424 425
  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)
426
    if (job->aborted == false) {
427 428 429 430 431 432
      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) {
433
    request_priv->last_view_time = last_view_time;
434

435 436 437 438 439
    render_job_t* job = g_try_malloc0(sizeof(render_job_t));
    if (job == NULL) {
      return;
    }

440 441 442 443
    job->request = g_object_ref(request);
    job->aborted = false;
    girara_list_append(request_priv->active_jobs, job);

444
    private_t* priv = GET_PRIVATE(request_priv->renderer);
445
    g_thread_pool_push(priv->pool, job, NULL);
Moritz Lipp's avatar
Moritz Lipp committed
446
  }
447 448

  mutex_unlock(&request_priv->jobs_mutex);
449
}
450

451 452 453 454 455 456
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);
457 458 459 460 461
  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
462 463
}

464 465 466 467 468 469 470 471
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();
}
472

Sebastian Ramacher's avatar
Sebastian Ramacher committed
473 474
/* render job */

475 476 477 478 479 480 481 482 483 484 485 486 487
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);
}

488
typedef struct emit_completed_signal_s
Moritz Lipp's avatar
Moritz Lipp committed
489
{
490
  render_job_t* job;
491 492
  cairo_surface_t* surface;
} emit_completed_signal_t;
493

494 495 496 497
static gboolean
emit_completed_signal(void* data)
{
  emit_completed_signal_t* ecs = data;
498 499 500
  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
501

502
  if (priv->about_to_close == false && job->aborted == false) {
503
    /* emit the signal */
504 505
    girara_debug("Emitting signal for page %d",
        zathura_page_get_index(request_priv->page) + 1);
506
    g_signal_emit(job->request, request_signals[REQUEST_COMPLETED], 0, ecs->surface);
507 508 509
  } else {
    girara_debug("Rendering of page %d aborted",
        zathura_page_get_index(request_priv->page) + 1);
510
  }
511
  /* mark the request as done */
512
  remove_job_and_free(job);
513 514 515 516 517 518

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

  return FALSE;
Moritz Lipp's avatar
Moritz Lipp committed
519 520
}

521 522 523
/* 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
524
static double
525
colorumax(const double* h, double l, double l1, double l2)
526 527 528 529 530
{
  if (h[0] == 0 && h[1] == 0 && h[2] == 0) {
    return 0;
  }

531 532 533
  const double lv = (l - l1)/(l2 - l1);    /* Remap l to the whole interval 0,1 */
  double u = 1000000;
  double v = u;
534 535
  for (int k = 0; k < 3; k++) {
    if (h[k] > 0) {
536 537
      const double uu = fabs((1-l)/h[k]);
      const double vv = fabs((1-lv)/h[k]);
538

Sebastian Ramacher's avatar
Sebastian Ramacher committed
539 540 541 542 543 544
      if (uu < u) {
        u = uu;
      }
      if (vv < v) {
        v = vv;
      }
545
    } else if (h[k] < 0) {
546 547
      const double uu = fabs(l/h[k]);
      const double vv = fabs(lv/h[k]);
548

Sebastian Ramacher's avatar
Sebastian Ramacher committed
549 550 551 552 553 554
      if (uu < u) {
        u = uu;
      }
      if (vv < v) {
        v = vv;
      }
555 556 557 558
    }
  }

  /* rescale v according to the length of the interval [l1, l2] */
Sebastian Ramacher's avatar
Sebastian Ramacher committed
559
  v = fabs(l2 - l1) * v;
560 561 562 563 564

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

565 566 567 568 569 570
static void
recolor(private_t* priv, unsigned int page_width, unsigned int page_height,
    cairo_surface_t* surface)
{
  /* uses a representation of a rgb color as follows:
     - a lightness scalar (between 0,1), which is a weighted average of r, g, b,
571 572 573 574
     - 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.
575 576 577 578 579 580 581 582 583 584
  */

  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
585 586
  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;
587 588

  const double rgb_diff[] = {
589 590 591
    rgb2.red - rgb1.red,
    rgb2.green - rgb1.green,
    rgb2.blue - rgb1.blue
592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635
  };

  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) {
      /* 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 */
636 637 638
        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));
639 640 641 642 643 644 645 646
      }
    }
  }

#undef rgb1
#undef rgb2
}

647
static bool
648
render(render_job_t* job, ZathuraRenderRequest* request, ZathuraRenderer* renderer)
Moritz Lipp's avatar
Moritz Lipp committed
649
{
650 651 652
  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
653

Moritz Lipp's avatar
Moritz Lipp committed
654 655 656
  /* create cairo surface */
  unsigned int page_width  = 0;
  unsigned int page_height = 0;
657 658

  zathura_document_t* document = zathura_page_get_document(page);
659 660
  const double height = zathura_page_get_height(page);
  const double width = zathura_page_get_width(page);
661 662

  const double real_scale = page_calc_height_width(document, height, width,
663 664
                                                   &page_height, &page_width,
                                                   false);
665

Moritz Lipp's avatar
Moritz Lipp committed
666

667 668
  cairo_surface_t* surface = cairo_image_surface_create(CAIRO_FORMAT_RGB24,
      page_width, page_height);
Moritz Lipp's avatar
Moritz Lipp committed
669
  if (surface == NULL) {
Moritz Lipp's avatar
Moritz Lipp committed
670 671 672
    return false;
  }

Moritz Lipp's avatar
Moritz Lipp committed
673 674 675 676 677
  cairo_t* cairo = cairo_create(surface);
  if (cairo == NULL) {
    cairo_surface_destroy(surface);
    return false;
  }
Moritz Lipp's avatar
Moritz Lipp committed
678

Moritz Lipp's avatar
Moritz Lipp committed
679 680 681 682 683 684
  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
685

686 687
  if (fabs(real_scale - 1.0f) > FLT_EPSILON) {
    cairo_scale(cairo, real_scale, real_scale);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
688 689
  }

690
  zathura_renderer_lock(renderer);
691
  const int err = zathura_page_render(page, cairo, false);
692
  zathura_renderer_unlock(renderer);
Moritz Lipp's avatar
Moritz Lipp committed
693 694
  cairo_restore(cairo);
  cairo_destroy(cairo);
695 696 697 698
  if (err != ZATHURA_ERROR_OK) {
    cairo_surface_destroy(surface);
    return false;
  }
Moritz Lipp's avatar
Moritz Lipp committed
699

700
  /* before recoloring, check if we've been aborted */
701
  if (priv->about_to_close == true || job->aborted == true) {
702
    girara_debug("Rendering of page %d aborted",
703
                 zathura_page_get_index(request_priv->page) + 1);
704
    remove_job_and_free(job);
705 706 707 708
    cairo_surface_destroy(surface);
    return true;
  }

Moritz Lipp's avatar
Moritz Lipp committed
709
  /* recolor */
710
  if (priv->recolor.enabled == true) {
711
    recolor(priv, page_width, page_height, surface);
Moritz Lipp's avatar
Moritz Lipp committed
712 713
  }

714
  emit_completed_signal_t* ecs = g_try_malloc0(sizeof(emit_completed_signal_t));
715
  if (ecs == NULL) {
716
    cairo_surface_destroy(surface);
717 718 719
    return false;
  }

720
  ecs->job     = job;
721
  ecs->surface = cairo_surface_reference(surface);
722

723 724
  /* 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
725

726 727
  cairo_surface_destroy(surface);

Moritz Lipp's avatar
Moritz Lipp committed
728 729
  return true;
}
730

731 732 733
static void
render_job(void* data, void* user_data)
{
734 735
  render_job_t* job = data;
  ZathuraRenderRequest* request = job->request;
736 737 738 739 740
  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);
741
  if (priv->about_to_close == true || job->aborted == true) {
742
    /* back out early */
743
    remove_job_and_free(job);
744 745 746
    return;
  }

747
  request_private_t* request_priv = REQUEST_GET_PRIVATE(request);
748 749
  girara_debug("Rendering page %d ...",
      zathura_page_get_index(request_priv->page) + 1);
750
  if (render(job, request, renderer) != true) {
751 752
    girara_error("Rendering failed (page %d)\n",
        zathura_page_get_index(request_priv->page) + 1);
753
    remove_job_and_free(job);
754 755 756 757
  }
}


758
void
Moritz Lipp's avatar
Moritz Lipp committed
759
render_all(zathura_t* zathura)
760
{
761
  if (zathura == NULL || zathura->document == NULL) {
762 763 764 765
    return;
  }

  /* unmark all pages */
766
  const unsigned int number_of_pages = zathura_document_get_number_of_pages(zathura->document);
767
  for (unsigned int page_id = 0; page_id < number_of_pages; page_id++) {
768 769
    zathura_page_t* page = zathura_document_get_page(zathura->document,
        page_id);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
770
    unsigned int page_height = 0, page_width = 0;
771 772
    const double height = zathura_page_get_height(page);
    const double width = zathura_page_get_width(page);
773
    page_calc_height_width(zathura->document, height, width, &page_height, &page_width, true);
774

775
    GtkWidget* widget = zathura_page_get_widget(zathura, page);
Moritz Lipp's avatar
Moritz Lipp committed
776 777
    gtk_widget_set_size_request(widget, page_width, page_height);
    gtk_widget_queue_resize(widget);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
778
  }
Moritz Lipp's avatar
Moritz Lipp committed
779
}
780 781

static gint
782
render_thread_sort(gconstpointer a, gconstpointer b, gpointer UNUSED(data))
783
{
784
  if (a == NULL || b == NULL) {
785 786 787
    return 0;
  }

788 789 790 791 792 793
  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);

794 795
    return priv_a->last_view_time < priv_b->last_view_time ? -1 :
        (priv_a->last_view_time > priv_b->last_view_time ? 1 : 0);
796 797
  }

798
  /* sort aborted entries earlier so that the are thrown out of the queue */
799
  return job_a->aborted ? 1 : -1;
800
}
801 802 803

/* cache functions */

804 805
static bool
page_cache_is_cached(ZathuraRenderer* renderer, unsigned int page_index)
806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845
{
  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;
846
  ZathuraRenderRequest* request = NULL;
847
  for (size_t i = 0; i < priv->page_cache.size; ++i) {
848
    ZathuraRenderRequest* tmp_request = girara_list_find(priv->requests,
849
        find_request_by_page_index, &priv->page_cache.cache[i]);
850 851
    g_return_val_if_fail(tmp_request != NULL, -1);
    request_private_t* request_priv = REQUEST_GET_PRIVATE(tmp_request);
852 853 854 855

    if (request_priv->last_view_time < lru_view_time) {
      lru_view_time = request_priv->last_view_time;
      lru_index = i;
856
      request = tmp_request;
857 858 859 860 861 862
    }
  }

  request_private_t* request_priv = REQUEST_GET_PRIVATE(request);

  /* emit the signal */
863
  g_signal_emit(request, request_signals[REQUEST_CACHE_INVALIDATED], 0);
864 865 866 867 868 869 870 871 872 873 874
  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)
{
875
  g_return_val_if_fail(ZATHURA_IS_RENDERER(renderer) && result != NULL, false);
876

877
  private_t* priv = GET_PRIVATE(renderer);
878 879 880 881 882
  *result = priv->page_cache.num_cached_pages == priv->page_cache.size;

  return true;
}

883
static void
884 885
page_cache_invalidate_all(ZathuraRenderer* renderer)
{
886
  g_return_if_fail(ZATHURA_IS_RENDERER(renderer));
887 888 889 890 891 892 893 894 895

  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
896 897
zathura_renderer_page_cache_add(ZathuraRenderer* renderer,
    unsigned int page_index)
898
{
899 900
  g_return_if_fail(ZATHURA_IS_RENDERER(renderer));
  if (page_cache_is_cached(renderer, page_index) == true) {
901 902 903 904 905 906 907 908
    return;
  }

  private_t* priv = GET_PRIVATE(renderer);
  bool full = false;
  if (page_cache_is_full(renderer, &full) == false) {
    return;
  } else if (full == true) {
909
    const ssize_t idx = page_cache_lru_invalidate(renderer);
910 911 912 913 914 915 916
    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);
917 918 919 920
  } 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);
921 922
  }

923 924 925 926
  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);
927
}