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

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

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

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

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

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

32 33 34

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  priv->requests = girara_list_new();
}

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

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

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

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

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

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

  return ret;
151 152 153
}

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

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

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

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

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

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

/* init, new and free for ZathuraRenderRequest */

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

/* renderer methods */
307

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

414 415 416 417 418 419
void
zathura_renderer_unlock(ZathuraRenderer* renderer)
{
  g_return_if_fail(ZATHURA_IS_RENDERER(renderer));

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

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


/* ZathuraRenderRequest methods */

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

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

  bool unfinished_jobs = false;
  /* check if there are any active jobs left */
Sebastian Ramacher's avatar
Sebastian Ramacher committed
443
  GIRARA_LIST_FOREACH_BODY(request_priv->active_jobs, render_job_t*, job,
444
    if (job->aborted == false) {
445
      unfinished_jobs = true;
Sebastian Ramacher's avatar
Sebastian Ramacher committed
446
      break;
447
    }
Sebastian Ramacher's avatar
Sebastian Ramacher committed
448
  );
449 450 451

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

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

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

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

467
  g_mutex_unlock(&request_priv->jobs_mutex);
468
}
469

470 471 472 473 474 475
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);
476
  g_mutex_lock(&request_priv->jobs_mutex);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
477
  GIRARA_LIST_FOREACH_BODY(request_priv->active_jobs, render_job_t*, job,
478
    job->aborted = true;
Sebastian Ramacher's avatar
Sebastian Ramacher committed
479
  );
480
  g_mutex_unlock(&request_priv->jobs_mutex);
Moritz Lipp's avatar
Moritz Lipp committed
481 482
}

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

Sebastian Ramacher's avatar
Sebastian Ramacher committed
492 493
/* render job */

494 495 496 497 498
static void
remove_job_and_free(render_job_t* job)
{
  request_private_t* request_priv = REQUEST_GET_PRIVATE(job->request);

499
  g_mutex_lock(&request_priv->jobs_mutex);
500
  girara_list_remove(request_priv->active_jobs, job);
501
  g_mutex_unlock(&request_priv->jobs_mutex);
502 503 504 505 506

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

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

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

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

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

  return FALSE;
Moritz Lipp's avatar
Moritz Lipp committed
538 539
}

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

550
  const double lv = (l - l1) / (l2 - l1);    /* Remap l to the whole interval [0,1] */
551 552
  double u = DBL_MAX;
  double v = DBL_MAX;
Sebastian Ramacher's avatar
Sebastian Ramacher committed
553
  for (unsigned int k = 0; k < 3; ++k) {
554
    if (h[k] > DBL_EPSILON) {
Sebastian Ramacher's avatar
Sebastian Ramacher committed
555 556
      u = fmin(fabs((1-l)/h[k]), u);
      v = fmin(fabs((1-lv)/h[k]), v);
557
    } else if (h[k] < -DBL_EPSILON) {
Sebastian Ramacher's avatar
Sebastian Ramacher committed
558 559
      u = fmin(fabs(l/h[k]), u);
      v = fmin(fabs(lv/h[k]), v);
560 561 562 563
    }
  }

  /* rescale v according to the length of the interval [l1, l2] */
Sebastian Ramacher's avatar
Sebastian Ramacher committed
564
  v = fabs(l2 - l1) * v;
565 566 567 568 569

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

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

582 583
  /* TODO: split handling of image handling off
   * Ideally we would create a mask surface for the location of the images and
Sebastian Ramacher's avatar
Sebastian Ramacher committed
584
   * we would blit the recolored and unmodified surfaces together to get the
585 586 587
   * same effect.
   */

588 589
  cairo_surface_flush(surface);

590 591 592 593 594 595
  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};

596 597 598
  const GdkRGBA rgb1 = priv->recolor.dark;
  const GdkRGBA rgb2 = priv->recolor.light;

599 600
  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;
601 602

  const double rgb_diff[] = {
603 604 605
    rgb2.red - rgb1.red,
    rgb2.green - rgb1.green,
    rgb2.blue - rgb1.blue
606 607
  };

608 609 610 611 612 613 614 615 616 617 618 619
  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;
620
      girara_warning("Failed to retrieve images.");
621 622 623 624
    }

    if (found_images == true) {
      /* Get images bounding boxes */
Sebastian Ramacher's avatar
Sebastian Ramacher committed
625
      GIRARA_LIST_FOREACH_BODY(images, zathura_image_t*, image_it,
626 627 628 629 630 631
        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);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
632
      );
633 634 635
    }
  }

636 637 638 639
  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) {
640 641 642
      /* 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;
Sebastian Ramacher's avatar
Sebastian Ramacher committed
643
        GIRARA_LIST_FOREACH_BODY(rectangles, zathura_rectangle_t*, rect_it,
644 645 646 647 648
          if (rect_it->x1 <= x && rect_it->x2 >= x &&
              rect_it->y1 <= y && rect_it->y2 >= y) {
            inside_image = true;
            break;
          }
Sebastian Ramacher's avatar
Sebastian Ramacher committed
649
        );
650 651 652 653 654 655
        /* If it's inside and image don't recolor */
        if (inside_image == true) {
          continue;
        }
      }

656 657
      /* Careful. data color components blue, green, red. */
      const double rgb[3] = {
Sebastian Ramacher's avatar
Sebastian Ramacher committed
658 659 660
        data[2] / 255.,
        data[1] / 255.,
        data[0] / 255.
661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676
      };

      /* 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 */
677 678
        const double u = colorumax(h, l, 0, 1);
        const double s = fabs(u) > DBL_EPSILON ? 1.0 / u : 0.0;
679 680 681 682 683

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

684 685 686 687
        const double su = s * colorumax(h, l, l1, l2);
        data[2] = (unsigned char)round(255.*(l + su * h[0]));
        data[1] = (unsigned char)round(255.*(l + su * h[1]));
        data[0] = (unsigned char)round(255.*(l + su * h[2]));
688 689 690
      } else {
        /* linear interpolation between dark and light with color ligtness as
         * a parameter */
691 692 693
        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));
694 695 696 697
      }
    }
  }

698 699 700 701 702 703 704
  if (images != NULL) {
    girara_list_free(images);
  }
  if (rectangles != NULL) {
    girara_list_free(rectangles);
  }

705
  cairo_surface_mark_dirty(surface);
706 707
}

708
static bool
709
render(render_job_t* job, ZathuraRenderRequest* request, ZathuraRenderer* renderer)
Moritz Lipp's avatar
Moritz Lipp committed
710
{
711 712 713
  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
714

Moritz Lipp's avatar
Moritz Lipp committed
715 716 717
  /* create cairo surface */
  unsigned int page_width  = 0;
  unsigned int page_height = 0;
718

719
  /* page size in points */
720
  zathura_document_t* document = zathura_page_get_document(page);
721 722
  const double height = zathura_page_get_height(page);
  const double width = zathura_page_get_width(page);
723

724
  /* page size in user pixels based on document zoom: if PPI information is
725 726
   * correct, 100% zoom will result in 72 documents points per inch of screen
   * (i.e. document size on screen matching the physical paper size). */
727
  const double real_scale = page_calc_height_width(document, height, width,
728 729
                                                   &page_height, &page_width,
                                                   false);
730

Sebastian Ramacher's avatar
Sebastian Ramacher committed
731
  const zathura_device_factors_t device_factors = zathura_document_get_device_factors(document);
732 733
  page_width *= device_factors.x;
  page_height *= device_factors.y;
Moritz Lipp's avatar
Moritz Lipp committed
734

735 736
  cairo_surface_t* surface = cairo_image_surface_create(CAIRO_FORMAT_RGB24,
      page_width, page_height);
Moritz Lipp's avatar
Moritz Lipp committed
737
  if (surface == NULL) {
Moritz Lipp's avatar
Moritz Lipp committed
738 739
    return false;
  }
740

741
  cairo_surface_set_device_scale(surface, device_factors.x, device_factors.y);
742

743 744 745 746
  if (cairo_surface_status(surface) != CAIRO_STATUS_SUCCESS) {
    cairo_surface_destroy(surface);
    return false;
  }
Moritz Lipp's avatar
Moritz Lipp committed
747

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

Moritz Lipp's avatar
Moritz Lipp committed
754 755
  cairo_save(cairo);
  cairo_set_source_rgb(cairo, 1, 1, 1);
756
  cairo_paint(cairo);
Moritz Lipp's avatar
Moritz Lipp committed
757 758
  cairo_restore(cairo);
  cairo_save(cairo);
Moritz Lipp's avatar
Moritz Lipp committed
759

760
  /* apply scale (used by e.g. Poppler as pixels per point) */
761 762
  if (fabs(real_scale - 1.0f) > FLT_EPSILON) {
    cairo_scale(cairo, real_scale, real_scale);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
763 764
  }

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

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

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

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

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

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

801 802
  cairo_surface_destroy(surface);

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

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

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


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

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

Sebastian Ramacher's avatar
Sebastian Ramacher committed
850
    girara_debug("Queuing resize for page %u to %u x %u (%0.2f x %0.2f).", page_id, page_width, page_height, width, height);
851
    GtkWidget* widget = zathura_page_get_widget(zathura, page);
852 853 854 855
    if (widget != NULL) {
      gtk_widget_set_size_request(widget, page_width, page_height);
      gtk_widget_queue_resize(widget);
    }
Sebastian Ramacher's avatar
Sebastian Ramacher committed
856
  }
Moritz Lipp's avatar
Moritz Lipp committed
857
}
858 859

static gint
860
render_thread_sort(gconstpointer a, gconstpointer b, gpointer UNUSED(data))
861
{
862
  if (a == NULL || b == NULL) {
863 864 865
    return 0;
  }

866 867 868 869 870 871
  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);

872 873
    return priv_a->last_view_time < priv_b->last_view_time ? -1 :
        (priv_a->last_view_time > priv_b->last_view_time ? 1 : 0);
874 875
  }

Sebastian Ramacher's avatar
Sebastian Ramacher committed
876
  /* sort aborted entries earlier so that they are thrown out of the queue */
877
  return job_a->aborted ? 1 : -1;
878
}
879 880 881

/* cache functions */

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

    if (request_priv->last_view_time < lru_view_time) {
      lru_view_time = request_priv->last_view_time;
      lru_index = i;
934
      request = tmp_request;
935 936 937 938 939 940
    }
  }

  request_private_t* request_priv = REQUEST_GET_PRIVATE(request);

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

955
  private_t* priv = GET_PRIVATE(renderer);
956 957 958 959 960
  *result = priv->page_cache.num_cached_pages == priv->page_cache.size;

  return true;
}

961
static void
962 963
page_cache_invalidate_all(ZathuraRenderer* renderer)
{
964
  g_return_if_fail(ZATHURA_IS_RENDERER(renderer));
965 966 967 968 969 970 971 972 973

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

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

1001 1002 1003 1004
  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);
1005
}