summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/pdf_bridge.c64
1 files changed, 61 insertions, 3 deletions
diff --git a/src/pdf_bridge.c b/src/pdf_bridge.c
index 0fcf74c4..b880cd21 100644
--- a/src/pdf_bridge.c
+++ b/src/pdf_bridge.c
@@ -14,21 +14,28 @@ struct pardes_pdf_document {
fz_document *doc;
int page_count;
- /* One UI thread owns a document. Page switches invalidate this pair. */
+ /* One UI thread owns a document. Page switches invalidate this cache. */
int cached_page_number;
fz_page *cached_page;
fz_stext_page *cached_text;
+ fz_display_list *cached_display_list;
fz_rect cached_bounds;
+
+ /* Promote only a page which is rendered twice without changing pages. */
+ int display_list_candidate_page_number;
};
static void
pardes_pdf_drop_cached_page(pardes_pdf_document *document)
{
+ fz_drop_display_list(document->ctx, document->cached_display_list);
fz_drop_stext_page(document->ctx, document->cached_text);
fz_drop_page(document->ctx, document->cached_page);
+ document->cached_display_list = NULL;
document->cached_text = NULL;
document->cached_page = NULL;
document->cached_page_number = -1;
+ document->display_list_candidate_page_number = -1;
}
static int
@@ -88,6 +95,45 @@ pardes_pdf_cache_page(
document->cached_bounds = bounds;
}
+/*
+ * Build only after one successful direct render of the still-cached page.
+ * This keeps cold renders and sequential document passes on their original
+ * path instead of recording a list which the next page would immediately
+ * evict. The temporary list is committed only after its device has closed;
+ * any partially recorded list is dropped before the exception propagates.
+ */
+static void
+pardes_pdf_cache_display_list(pardes_pdf_document *document)
+{
+ fz_context *ctx = document->ctx;
+ fz_display_list *list = NULL;
+ fz_device *device = NULL;
+
+ if (document->cached_display_list != NULL)
+ return;
+
+ fz_var(list);
+ fz_var(device);
+ fz_try(ctx)
+ {
+ list = fz_new_display_list(ctx, document->cached_bounds);
+ device = fz_new_list_device(ctx, list);
+ fz_run_page(ctx, document->cached_page, device, fz_identity, NULL);
+ fz_close_device(ctx, device);
+ }
+ fz_always(ctx)
+ {
+ fz_drop_device(ctx, device);
+ }
+ fz_catch(ctx)
+ {
+ fz_drop_display_list(ctx, list);
+ fz_rethrow(ctx);
+ }
+
+ document->cached_display_list = list;
+}
+
static float
pardes_pdf_clamp_unit(float value)
{
@@ -187,6 +233,7 @@ pardes_pdf_open(const char *path, int *page_count)
if (state == NULL)
return NULL;
state->cached_page_number = -1;
+ state->display_list_candidate_page_number = -1;
ctx = fz_new_context(NULL, NULL, PARDES_PDF_STORE_LIMIT_BYTES);
if (ctx == NULL) {
@@ -344,8 +391,16 @@ pardes_pdf_render_with_highlights(
* premultiplied transparent samples, while Pardes consumes straight
* RGBA after the Zig-side RGB expansion.
*/
- pixmap = fz_new_pixmap_from_page(
- ctx, document->cached_page, ctm, fz_device_rgb(ctx), 0);
+ if (document->cached_display_list != NULL ||
+ document->display_list_candidate_page_number == page_number) {
+ pardes_pdf_cache_display_list(document);
+ pixmap = fz_new_pixmap_from_display_list(
+ ctx, document->cached_display_list, ctm,
+ fz_device_rgb(ctx), 0);
+ } else {
+ pixmap = fz_new_pixmap_from_page(
+ ctx, document->cached_page, ctm, fz_device_rgb(ctx), 0);
+ }
if (fz_pixmap_width(ctx, pixmap) < 1 ||
fz_pixmap_height(ctx, pixmap) < 1)
fz_throw(ctx, FZ_ERROR_FORMAT, "PDF page rendered empty");
@@ -398,6 +453,9 @@ pardes_pdf_render_with_highlights(
return PARDES_PDF_ERROR;
}
+ if (document->cached_display_list == NULL)
+ document->display_list_candidate_page_number = page_number;
+
out->handle = pixmap;
out->samples = fz_pixmap_samples(ctx, pixmap);
out->width = fz_pixmap_width(ctx, pixmap);