Hello,

This incomplete patch implements LCD subpixel antialiasing in mupdf
plugin by rendering a 3x wider (or taller) pixmap and then filtering
it. Credit for the general approach to subpixel-precise downsampling
goes to imageworsener. Credit for the filtering method goes to
Freetype. All four (horizontal/vertical rgb/bgr) LCD subpixel layouts
work.

The patch is incomplete as it implements the guts, but for now simply
hardcodes subpixel layout as horizontal RGB (most popular one).  I
don't know what would be a good way to communicate layout information
to pdf_page_render from the viewer. It would need to account for
rotation. Filter weights are also hardcoded.

This patch needs my previous "deduplication" patches. Together with
those it's +99 lines of code.

Filtering is not gamma-correct, but may look good enough for a first step.

Alexander
From 3faefb44ff139bb7d08617cb5581cbfd42f4e18d Mon Sep 17 00:00:00 2001
From: Alexander Monakov <amona...@ispras.ru>
Date: Thu, 31 Oct 2013 12:43:11 +0400
Subject: [PATCH 5/5] Implement LCD subpixel antialiasing

---
 pdf.c | 142 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------
 1 file changed, 130 insertions(+), 12 deletions(-)

diff --git a/pdf.c b/pdf.c
index 791751b..a724d6a 100644
--- a/pdf.c
+++ b/pdf.c
@@ -452,6 +452,118 @@ error_ret:
   return NULL;
 }
 
+static void
+buffer_blit_row(unsigned char* dst, int dst_ncmpt,
+               unsigned char* src, int src_ncmpt,
+               int width)
+{
+  for (; width; width--) {
+    dst[0] = src[0];
+    dst[1] = src[1];
+    dst[2] = src[2];
+    dst += dst_ncmpt;
+    src += src_ncmpt;
+  }
+}
+
+static void
+buffer_blit(unsigned char* dst, int dst_ncmpt, int dst_stride,
+           unsigned char* src, int src_ncmpt, int src_stride,
+           int width, int height)
+{
+  for (unsigned int y = 0; y < height; y++) {
+    buffer_blit_row(dst, dst_ncmpt, src, src_ncmpt, width);
+    dst += dst_stride;
+    src += src_stride;
+  }
+}
+
+enum {SUBPIXEL_HRGB, SUBPIXEL_HBGR, SUBPIXEL_VRGB, SUBPIXEL_VBGR};
+
+/* 5-tap filter from Freetype, see ftlcdfil.h.  */
+static inline int
+filter(int x0, int x1, int x2, int x3, int x4)
+{
+  int FIR_WA = 0x30, FIR_WC = 0x20;
+  int w0 = FIR_WA - FIR_WC, w1 = FIR_WA + FIR_WC, w2 = FIR_WA * 2;
+  int W = w0 + w1 + w2 + w1 + w0;
+
+  return (x0 * w0 + x1 * w1 + x2 * w2 + x3 * w1 + x4 * w0 + W / 2) / W;
+}
+
+/* Shrink a bitmap by 3x horizontally with subpixel precision.  For pixel
+   data, BGR layout is assumed.  SUBPIX_ORDER is physical layout.  */
+static void
+buffer_subpix_x(unsigned char* dst, int dst_ncmpt, int dst_stride,
+               unsigned char* src, int src_ncmpt, int src_stride,
+               int width, int height, int subpix_order)
+{
+  int n = src_ncmpt;
+  int blue_offset = (subpix_order == SUBPIXEL_HBGR) ? -n : n;
+  src += n;
+
+  for (unsigned int y = 0; y < height; y++) {
+    unsigned char* d = dst;
+    unsigned char* s = src;
+    /* Do not bother filtering the first and the last columns.  */
+    {
+      d[0] = s[0];
+      d[1] = s[1];
+      d[2] = s[2];
+      s += src_ncmpt * 3;
+      d += dst_ncmpt;
+    }
+    for (unsigned int x = 1; x < width - 1; x++) {
+      s += blue_offset;
+      d[0] = filter(s[-n * 2], s[-n], s[0], s[n], s[n * 2]);
+      s += -blue_offset + 1;
+      d[1] = filter(s[-n * 2], s[-n], s[0], s[n], s[n * 2]);
+      s += -blue_offset + 1;
+      d[2] = filter(s[-n * 2], s[-n], s[0], s[n], s[n * 2]);
+      s += blue_offset - 2 + src_ncmpt * 3;
+      d += dst_ncmpt;
+    }
+    {
+      d[0] = s[0];
+      d[1] = s[1];
+      d[2] = s[2];
+    }
+    dst += dst_stride;
+    src += src_stride;
+  }
+}
+
+static void
+buffer_subpix_y(unsigned char* dst, int dst_ncmpt, int dst_stride,
+               unsigned char* src, int src_ncmpt, int src_stride,
+               int width, int height, int subpix_order)
+{
+  int n = src_stride;
+  int blue_offset = (subpix_order == SUBPIXEL_VBGR) ? -n : n;
+  src += n;
+
+  buffer_blit_row(dst, dst_ncmpt, src, src_ncmpt, width);
+  dst += dst_stride;
+  src += src_stride * 3;
+  for (unsigned int y = 1; y < height - 1; y++) {
+    unsigned char* d = dst;
+    unsigned char* s = src;
+    for (unsigned int x = 0; x < width; x++) {
+      s += blue_offset;
+      d[0] = filter(s[-n * 2], s[-n], s[0], s[n], s[n * 2]);
+      s += -blue_offset + 1;
+      d[1] = filter(s[-n * 2], s[-n], s[0], s[n], s[n * 2]);
+      s += -blue_offset + 1;
+      d[2] = filter(s[-n * 2], s[-n], s[0], s[n], s[n * 2]);
+      s += blue_offset - 2 + src_ncmpt;
+      d += dst_ncmpt;
+    }
+    dst += dst_stride;
+    src += src_stride * 3;
+  }
+  buffer_blit_row(dst, dst_ncmpt, src, src_ncmpt, width);
+}
+
 static zathura_error_t
 pdf_page_render_to_buffer(mupdf_document_t* mupdf_document, mupdf_page_t* 
mupdf_page,
                          unsigned char* image, int rowstride, int components,
@@ -461,9 +573,17 @@ pdf_page_render_to_buffer(mupdf_document_t* 
mupdf_document, mupdf_page_t* mupdf_
   fz_display_list* display_list = fz_new_display_list(mupdf_page->ctx);
   fz_device* device             = fz_new_list_device(mupdf_page->ctx, 
display_list);
 
+  int subpixx = 1, subpixy = 1;
+  int subpix_order = SUBPIXEL_HRGB;
+  if (subpix_order == SUBPIXEL_HRGB || subpix_order == SUBPIXEL_HBGR) {
+    subpixx = 3;
+  } else if (subpix_order == SUBPIXEL_VRGB || subpix_order == SUBPIXEL_VBGR) {
+    subpixy = 3;
+  }
+
   fz_try (mupdf_document->ctx) {
     fz_matrix m;
-    fz_scale(&m, scalex, scaley);
+    fz_scale(&m, scalex * subpixx, scaley * subpixy);
     fz_run_page(mupdf_document->document, mupdf_page->page, device, &m, NULL);
   } fz_catch (mupdf_document->ctx) {
     return ZATHURA_ERROR_UNKNOWN;
@@ -471,10 +591,10 @@ pdf_page_render_to_buffer(mupdf_document_t* 
mupdf_document, mupdf_page_t* mupdf_
 
   fz_free_device(device);
 
-  fz_irect irect = { .x1 = page_width, .y1 = page_height };
-  fz_rect rect = { .x1 = page_width, .y1 = page_height };
+  fz_irect irect = { .x1 = page_width * subpixx, .y1 = page_height * subpixy };
+  fz_rect rect = { .x1 = page_width * subpixx, .y1 = page_height * subpixy };
 
-  fz_colorspace* colorspace = fz_device_rgb(mupdf_document->ctx);
+  fz_colorspace* colorspace = fz_device_bgr(mupdf_document->ctx);
   fz_pixmap* pixmap = fz_new_pixmap_with_bbox(mupdf_page->ctx, colorspace, 
&irect);
   fz_clear_pixmap_with_value(mupdf_page->ctx, pixmap, 0xFF);
 
@@ -484,14 +604,12 @@ pdf_page_render_to_buffer(mupdf_document_t* 
mupdf_document, mupdf_page_t* mupdf_
 
   unsigned char* s = fz_pixmap_samples(mupdf_page->ctx, pixmap);
   unsigned int n   = fz_pixmap_components(mupdf_page->ctx, pixmap);
-  for (unsigned int y = 0; y < fz_pixmap_height(mupdf_page->ctx, pixmap); y++) 
{
-    for (unsigned int x = 0; x < fz_pixmap_width(mupdf_page->ctx, pixmap); 
x++) {
-      guchar* p = image + y * rowstride + x * components;
-      p[0] = s[2];
-      p[1] = s[1];
-      p[2] = s[0];
-      s += n;
-    }
+  if (subpixx == 3) {
+    buffer_subpix_x(image, components, rowstride, s, n, n * page_width * 3, 
page_width, page_height, subpix_order);
+  } else if (subpixy == 3) {
+    buffer_subpix_y(image, components, rowstride, s, n, n * page_width, 
page_width, page_height, subpix_order);
+  } else {
+    buffer_blit(image, components, rowstride, s, n, n * page_width, 
page_width, page_height);
   }
 
   fz_drop_pixmap(mupdf_page->ctx, pixmap);
-- 
1.8.3.2

_______________________________________________
zathura mailing list
zathura@lists.pwmt.org
http://lists.pwmt.org/mailman/listinfo/zathura

Reply via email to