[cairo] [PATCH] image: Use convolution filters for sample reconstruction when downscaling

spitzak at gmail.com spitzak at gmail.com
Tue Jun 10 13:14:04 PDT 2014


From: Bill Spitzak <spitzak at gmail.com>

Differences from previous patch:

1. Avoids buggy pixman BOX filters
2. One of "sampling" or "reconstruction" is always IMPULSE
3. When filtering is not used it uses the same pixman one-pass filter as before

Also I had to switch xlib to the fallback to see the results in my test program.
This must be fixed to instead detect if filtering will be used and fallback
only in these cases. Likley the xcb backend and maybe others need the same fix.
---
 src/cairo-image-source.c |   59 ++++++++++++++++++++++++++++++++++++++++++----
 src/cairo-xlib-display.c |    2 +-
 2 files changed, 56 insertions(+), 5 deletions(-)

diff --git a/src/cairo-image-source.c b/src/cairo-image-source.c
index c5bd228..fb1fb5c 100644
--- a/src/cairo-image-source.c
+++ b/src/cairo-image-source.c
@@ -526,6 +526,22 @@ _pixel_to_solid (cairo_image_surface_t *image, int x, int y)
     }
 }
 
+/* Number of samples determined by eye examining output of transforms */
+static int
+_samples (double scale)
+{
+    if (scale < 1/32.0) return 7;
+    else if (scale < 1/16.0) return 6;
+    else if (scale < 1/8.0) return 5;
+    else if (scale < 1/4.0) return 4;
+    else if (scale < 1/2.0) return 3;
+    else if (scale <= 1) return 2;
+    else if (scale < 2.5) return 4;
+    else if (scale < 3.6) return 3;
+    else if (scale < 8) return 2;
+    else return 1;
+}
+
 static cairo_bool_t
 _pixman_image_set_properties (pixman_image_t *pixman_image,
 			      const cairo_pattern_t *pattern,
@@ -555,16 +571,30 @@ _pixman_image_set_properties (pixman_image_t *pixman_image,
     else
     {
 	pixman_filter_t pixman_filter;
+	double scale_x, scale_y;
+
+	/* Compute scale factors as the length of basis vectors
+	 * transformed by the pattern matrix. These scale factors are
+	 * from user to pattern space, and as such they are greater
+	 * than 1.0 for downscaling and less than 1.0 for
+	 * upscaling. */
+	scale_x = hypot (pattern->matrix.xx, pattern->matrix.yx);
+	scale_y = hypot (pattern->matrix.yx, pattern->matrix.yy);
 
 	switch (pattern->filter) {
 	case CAIRO_FILTER_FAST:
 	    pixman_filter = PIXMAN_FILTER_FAST;
 	    break;
 	case CAIRO_FILTER_GOOD:
+	default:
 	    pixman_filter = PIXMAN_FILTER_GOOD;
+	    if (scale_x > 1.35 || scale_y > 1.35)
+		pixman_filter = PIXMAN_FILTER_SEPARABLE_CONVOLUTION;
 	    break;
 	case CAIRO_FILTER_BEST:
 	    pixman_filter = PIXMAN_FILTER_BEST;
+	    if (scale_x > 1.04 || scale_y > 1.04)
+		pixman_filter = PIXMAN_FILTER_SEPARABLE_CONVOLUTION;
 	    break;
 	case CAIRO_FILTER_NEAREST:
 	    pixman_filter = PIXMAN_FILTER_NEAREST;
@@ -577,12 +607,33 @@ _pixman_image_set_properties (pixman_image_t *pixman_image,
 	     * whatsoever, so it was really a mistake to have it in the
 	     * API. We could fix this by officially deprecating it, or
 	     * else inventing semantics and providing an actual
-	     * implementation for it. */
-	default:
-	    pixman_filter = PIXMAN_FILTER_BEST;
+	     * implementation for it. For now used as a test of
+	     * always using the kernel*/
+	    pixman_filter = PIXMAN_FILTER_SEPARABLE_CONVOLUTION;
 	}
 
-	pixman_image_set_filter (pixman_image, pixman_filter, NULL, 0);
+	if (pixman_filter == PIXMAN_FILTER_SEPARABLE_CONVOLUTION) {
+	    int n_params;
+	    pixman_fixed_t *params;
+	    n_params = 0;
+	    params = pixman_filter_create_separable_convolution
+		(&n_params,
+		 scale_x * 65536.0 + 0.5,
+		 scale_y * 65536.0 + 0.5,
+		 scale_x <= 1 ? PIXMAN_KERNEL_LINEAR : PIXMAN_KERNEL_IMPULSE,
+		 scale_y <= 1 ? PIXMAN_KERNEL_LINEAR : PIXMAN_KERNEL_IMPULSE,
+		 scale_x <= 1 ? PIXMAN_KERNEL_IMPULSE : PIXMAN_KERNEL_CUBIC,
+		 scale_y <= 1 ? PIXMAN_KERNEL_IMPULSE : PIXMAN_KERNEL_CUBIC,
+		 _samples(scale_x),
+		 _samples(scale_y));
+
+	    pixman_image_set_filter (pixman_image, pixman_filter,
+				     params, n_params);
+
+	    free (params);
+	} else {
+	    pixman_image_set_filter (pixman_image, pixman_filter, NULL, 0);
+	}
     }
 
     {
diff --git a/src/cairo-xlib-display.c b/src/cairo-xlib-display.c
index c7708c5..a073d08 100644
--- a/src/cairo-xlib-display.c
+++ b/src/cairo-xlib-display.c
@@ -151,7 +151,7 @@ static const cairo_device_backend_t _cairo_xlib_device_backend = {
 
 static void _cairo_xlib_display_select_compositor (cairo_xlib_display_t *display)
 {
-#if 1
+#if 0
     if (display->render_major > 0 || display->render_minor >= 4)
 	display->compositor = _cairo_xlib_traps_compositor_get ();
     else if (display->render_major > 0 || display->render_minor >= 0)
-- 
1.7.9.5



More information about the cairo mailing list